pax_global_header00006660000000000000000000000064147413207710014520gustar00rootroot0000000000000052 comment=b87ad67adb2c557bd96e52a3221748a7ba028858 njs-0.8.9/000077500000000000000000000000001474132077100123305ustar00rootroot00000000000000njs-0.8.9/.github/000077500000000000000000000000001474132077100136705ustar00rootroot00000000000000njs-0.8.9/.github/ISSUE_TEMPLATE/000077500000000000000000000000001474132077100160535ustar00rootroot00000000000000njs-0.8.9/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000023641474132077100205520ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve title: "" labels: "bug" --- ### Describe the bug A clear and concise description of what the bug is. Before submitting a bug report, please check the following: - [ ] The bug is reproducible with the latest version of njs. - [ ] I minimized the code and NGINX configuration to the smallest possible to reproduce the issue. ### To reproduce Steps to reproduce the behavior: - JS script ```js // Your JS code here ``` or put the code in a [gist](https://gist.github.com/) and link it here. - NGINX configuration if applicable ``` # Your NGINX configuration here ``` or put the configuration in a [gist](https://gist.github.com/) and link it here. - NGINX logs if applicable ``` # Your NGINX logs here ``` or post the full log to a [gist](https://gist.github.com/) and link it here. - Output of the `nginx -V` command if applicable. - Exact steps to reproduce the behavior ### Expected behavior A clear and concise description of what you expected to happen. ### Your environment - Version of njs or specific commit - Version of NGINX if applicable - List of other enabled nginx modules if applicable - OS: [e.g. Ubuntu 20.04] ### Additional context Add any other context about the problem here. njs-0.8.9/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000010511474132077100215750ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for this project title: "" labels: "feature" --- ### Is your feature request related to a problem? Please describe A clear and concise description of what the problem is. ### Describe the solution you'd like A clear and concise description of what you want to happen. ### Describe alternatives you've considered A clear and concise description of any alternative solutions or features you've considered. ### Additional context Add any other context or screenshots about the feature request here. njs-0.8.9/.github/pull_request_template.md000066400000000000000000000012571474132077100206360ustar00rootroot00000000000000### Proposed changes Describe the use case and detail of the change. If this PR addresses an issue on GitHub, make sure to include a link to that issue using one of the [supported keywords](https://docs.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue) here in this description (not in the title of the PR). ### Checklist Before creating a PR, run through this checklist and mark each as complete: - [ ] I have read the [`CONTRIBUTING`](CONTRIBUTING.md) document - [ ] If applicable, I have added tests that prove my fix is effective or that my feature works - [ ] If applicable, I have checked that any relevant tests pass after adding my changes njs-0.8.9/.github/workflows/000077500000000000000000000000001474132077100157255ustar00rootroot00000000000000njs-0.8.9/.github/workflows/check-pr.yml000066400000000000000000000176601474132077100201560ustar00rootroot00000000000000name: check-pr on: pull_request: jobs: build: runs-on: [ ubuntu-24.04 ] steps: - name: checkout v4 uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Set the defaults and set up environment run: | echo NGINX_CONFIGURE_CMD="auto/configure --prefix=/tmp --with-http_ssl_module --with-http_realip_module --with-http_addition_module --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_stub_status_module --with-mail --with-mail_ssl_module --with-select_module --with-poll_module --with-http_auth_request_module --with-http_v2_module --with-http_slice_module --with-stream --with-stream_ssl_module --with-stream_ssl_preread_module --with-stream_realip_module --with-threads --with-cpp_test_module --with-compat --with-http_degradation_module --with-http_xslt_module --with-http_image_filter_module --with-http_perl_module --with-http_geoip_module --with-stream_geoip_module" >> $GITHUB_ENV export DEB_BUILD_MAINT_OPTIONS="hardening=+all" export DEB_CFLAGS_MAINT_APPEND="-fPIC" export DEB_LDFLAGS_MAINT_APPEND=""-Wl,--as-needed"" echo CC_OPT=$(dpkg-buildflags --get CFLAGS) >> $GITHUB_ENV echo LD_OPT=$(dpkg-buildflags --get LDFLAGS) >> $GITHUB_ENV echo MAKE_UTILITY=make >> $GITHUB_ENV - name: Install build dependencies run: | sudo apt-get update sudo apt-get install \ libssl-dev zlib1g-dev libpcre2-dev libxslt1-dev libgeoip-dev \ libgd-dev libxml2-dev libedit-dev libperl-dev libtest-harness-perl \ libgd-perl libgeoip-dev expect - name: Install x86 build dependencies run: | sudo dpkg --add-architecture i386 sudo apt-get update sudo apt-get install -y gcc-multilib libc6:i386 libpcre2-dev:i386 zlib1g-dev:i386 - name: Check out nginx run: | git clone https://github.com/nginx/nginx nginx-source - name: Check out nginx tests run: | git clone https://github.com/nginx/nginx-tests - name: Check out and build quickjs run: | git clone https://github.com/bellard/quickjs cd quickjs CFLAGS=$CC_OPT LDFLAGS=$LD_OPT $MAKE_UTILITY -j$(nproc) libquickjs.a - name: Configure and make njs run: | ./configure \ --cc-opt="$CC_OPT" \ --ld-opt="$LD_OPT" \ || cat build/autoconf.err $MAKE_UTILITY -j$(nproc) - name: Test njs run: | $MAKE_UTILITY test $MAKE_UTILITY clean - name: Configure and make njs, 32-bit run: | ./configure \ --cc-opt="$CC_OPT -m32" \ --ld-opt="$LD_OPT" \ || cat build/autoconf.err $MAKE_UTILITY -j$(nproc) - name: Test njs, 32-bit run: | $MAKE_UTILITY test $MAKE_UTILITY clean - name: Configure and make njs with quickjs run: | ./configure \ --with-quickjs \ --cc-opt="$CC_OPT -Iquickjs" \ --ld-opt="$LD_OPT -Lquickjs" \ || cat build/autoconf.err $MAKE_UTILITY -j$(nproc) - name: Test njs with quickjs run: | $MAKE_UTILITY test $MAKE_UTILITY clean - name: Configure and build nginx and njs modules run: | cd nginx-source $NGINX_CONFIGURE_CMD --with-cc-opt="$CC_OPT" --with-ld-opt="$LD_OPT" --add-dynamic-module=../nginx || cat objs/autoconf.err $MAKE_UTILITY -j$(nproc) modules $MAKE_UTILITY -j$(nproc) - name: Test njs modules run: | ulimit -c unlimited prove -v -j$(nproc) -Inginx-tests/lib --state=save nginx/t . || prove -v -Inginx-tests/lib --state=failed env: TEST_NGINX_BINARY: "${{ github.workspace }}/nginx-source/objs/nginx" TEST_NGINX_GLOBALS: "load_module ${{ github.workspace }}/nginx-source/objs/ngx_http_js_module.so; load_module ${{ github.workspace }}/nginx-source/objs/ngx_stream_js_module.so;" TEST_NGINX_VERBOSE: 1 - name: Create LSAN suppression file run: | cat << EOF > lsan_suppressions.txt leak:ngx_event_process_init EOF - name: Configure and build nginx and njs modules with quickjs, asan, static modules run: | cd nginx-source $NGINX_CONFIGURE_CMD --with-cc-opt="$CC_OPT -I${{ github.workspace }}/quickjs -fsanitize=address -DNJS_DEBUG_MEMORY -DNGX_DEBUG_PALLOC -DNGX_DEBUG_MALLOC" --with-ld-opt="$LD_OPT -L${{ github.workspace }}/quickjs -fsanitize=address" --add-module=../nginx || cat objs/autoconf.err $MAKE_UTILITY -j$(nproc) - name: Test njs modules, static modules run: | ulimit -c unlimited prove -v -j$(nproc) -Inginx-tests/lib --state=save nginx/t . || prove -v -Inginx-tests/lib --state=failed env: TEST_NGINX_BINARY: "${{ github.workspace }}/nginx-source/objs/nginx" TEST_NGINX_VERBOSE: 1 ASAN_OPTIONS: "detect_odr_violation=0:report_globals=0" LSAN_OPTIONS: "suppressions=${{ github.workspace }}/lsan_suppressions.txt" - name: Test njs modules (js_engine qjs), static modules run: | ulimit -c unlimited prove -v -j$(nproc) -Inginx-tests/lib --state=save nginx/t . || prove -v -Inginx-tests/lib --state=failed env: TEST_NGINX_BINARY: "${{ github.workspace }}/nginx-source/objs/nginx" TEST_NGINX_GLOBALS_HTTP: "js_engine qjs;" TEST_NGINX_GLOBALS_STREAM: "js_engine qjs;" TEST_NGINX_VERBOSE: 1 ASAN_OPTIONS: "detect_odr_violation=0:report_globals=0" LSAN_OPTIONS: "suppressions=${{ github.workspace }}/lsan_suppressions.txt" - name: Configure and build nginx and njs modules with quickjs, asan, dynamic modules run: | cd nginx-source $NGINX_CONFIGURE_CMD --with-debug --with-cc-opt="$CC_OPT -I${{ github.workspace }}/quickjs -fsanitize=address -DNJS_DEBUG_MEMORY -DNGX_DEBUG_PALLOC -DNGX_DEBUG_MALLOC" --with-ld-opt="$LD_OPT -L${{ github.workspace }}/quickjs -fsanitize=address" --add-dynamic-module=../nginx || cat objs/autoconf.err $MAKE_UTILITY -j$(nproc) modules $MAKE_UTILITY -j$(nproc) - name: Test njs modules, dynamic modules run: | ulimit -c unlimited prove -v -j$(nproc) -Inginx-tests/lib --state=save nginx/t . || prove -v -Inginx-tests/lib --state=failed env: TEST_NGINX_BINARY: "${{ github.workspace }}/nginx-source/objs/nginx" TEST_NGINX_GLOBALS: "load_module ${{ github.workspace }}/nginx-source/objs/ngx_http_js_module.so; load_module ${{ github.workspace }}/nginx-source/objs/ngx_stream_js_module.so;" TEST_NGINX_VERBOSE: 1 ASAN_OPTIONS: "detect_odr_violation=0:report_globals=0:fast_unwind_on_malloc=0" LSAN_OPTIONS: "suppressions=${{ github.workspace }}/lsan_suppressions.txt" - name: Test njs modules (js_engine qjs), dynamic modules run: | ulimit -c unlimited prove -v -j$(nproc) -Inginx-tests/lib --state=save nginx/t . || prove -v -Inginx-tests/lib --state=failed env: TEST_NGINX_BINARY: "${{ github.workspace }}/nginx-source/objs/nginx" TEST_NGINX_GLOBALS: "load_module ${{ github.workspace }}/nginx-source/objs/ngx_stream_js_module.so; load_module ${{ github.workspace }}/nginx-source/objs/ngx_http_js_module.so;" TEST_NGINX_GLOBALS_HTTP: "js_engine qjs;" TEST_NGINX_GLOBALS_STREAM: "js_engine qjs;" TEST_NGINX_VERBOSE: 1 ASAN_OPTIONS: "detect_odr_violation=0:report_globals=0:fast_unwind_on_malloc=0" LSAN_OPTIONS: "suppressions=${{ github.workspace }}/lsan_suppressions.txt" njs-0.8.9/.github/workflows/ci.yml000066400000000000000000000002421474132077100170410ustar00rootroot00000000000000name: CI on: workflow_dispatch: push: branches: - master jobs: buildbot: uses: nginx/ci-self-hosted/.github/workflows/njs-buildbot.yml@main njs-0.8.9/.gitignore000066400000000000000000000000621474132077100143160ustar00rootroot00000000000000node_modules/ package-lock.json /build/ /Makefile njs-0.8.9/.hgignore000066400000000000000000000000641474132077100141330ustar00rootroot00000000000000/node_modules/ package-lock.json ^build/ ^Makefile$ njs-0.8.9/.hgtags000066400000000000000000000062701474132077100136130ustar00rootroot00000000000000cdb8d20935ee96f1fa0c99d994d4b11cefcbc119 0.1.0 0039a747d25a3e08792c23c43b75768896724031 0.1.1 5b066b4db54c17dc0a9a72948474f36957462e87 0.1.2 360449773d51e7f451e5396e27021badc6b86085 0.1.3 508689c1fb94c23f6b24be087c1dc63b2f9e6654 0.1.4 9c813c2bb2acfd5b6e9d1e9b6699af928baea15a 0.1.5 44b524f7e313369cd062a387511ea6fdc427875f 0.1.6 15dc54100400f99c3ec044d8fb0175dd3d69adcb 0.1.7 a29f29d481125db6101ecdc23dc20187c143cdc9 0.1.8 5bd2833988222900f60ad9b330ebc44df3b30662 0.1.9 b1456ef3e002376d9d146a8a02acf6a4a21748e9 0.1.10 fc5df33f4e6b02a673daf3728ff690fb1e09b95e 0.1.11 c07b060396be3622ca97b037a86076b61b850847 0.1.12 d548b78eb881ca799aa6fc8ba459d076f7db5ac8 0.1.13 d89d06dc638e78f8635c0bfbcd02469ac1a08748 0.1.14 215ca47b9167d513fd58ac88de97659377e45275 0.1.15 ddd1b2c9c64b2d459e9c399554dfaadcaabcc364 0.2.0 2a0a59728b5f197379ca62a334a516fabd4ea392 0.2.1 4adbb035caa39dae58611a061d78bc974652231e 0.2.2 e83f41520613987542472275d49633a9aa5955d1 0.2.3 3e6c38f64bdbc53e783813541559034ed6890aee 0.2.4 3315f6aa6000ce6d2dbc74c73660becf4178a549 0.2.5 0709b8b4f11ebec95dd4f72d5bb38044682f77e6 0.2.6 4624ba4f6497a3d10fe1c0a6f45fb453579502f5 0.2.7 ee190d3ace005f8eb063d4763b578f44d3028c68 0.2.8 1935ab4643fdaec5b4a8c36070f4d2cb8e3799d7 0.3.0 ebfbdb8d8fe2f640d880359575657cb53e38328f 0.3.1 82101d50fff6e4c7a92c0542a3d6026ff7e462fb 0.3.2 c65a4be9867d434ca449a18d868305d5dcd5b91b 0.3.3 8eadbb3a7c7b7c3426f73adabfa251cd9d296752 0.3.4 b7fa83f27f1b64443b88c990e1851f59583b2182 0.3.5 7b302775917b72537e1abdbd5dc9d04e55a7d582 0.3.6 9e5ef927c7eaed003de3e5e4a16fa3eab08de7f7 0.3.7 1abb97e9d9dc07dcff2616d4c75132e6d189a6aa 0.3.8 fc4eeaaf0dfe7dc7d41232f1b643bd364f1efe82 0.3.9 6144aafa1472fbdf79bc9d1f858555938ee08452 0.4.0 9400790bf53843001f94c77b47bc99b05518af78 0.4.1 b409e86fd02a6f2cb3d741a41b6562471e1b66ef 0.4.2 1ada1061a040e5cd5ec55744bfa916dfc6744e4c 0.4.3 fdfd580b0dd617a884ed9287d98341ebef03ee9f 0.4.4 69f07c6151628880bf7d5ac28bd8287ce96d8a36 0.5.0 d355071f55ef4612d89db0ba72e7aaeaa99deef7 0.5.1 e5de01378b1a8ab0a94dd3a8c4c6bb7a235f4b9c 0.5.2 282b9412976ceee31eb12876f1499fe975e6f08c 0.5.3 742ebceef2b5d15febc093172fe6174e427b26c8 0.6.0 4adbe67b292af2adc0a6fde4ec6cb95dbba9470a 0.6.1 dfba7f61745c7454ffdd55303a793206d0a9a84a 0.6.2 8418bd4a4ce3114d57b4d75f913e8c4912bf4b5d 0.7.0 35aca5cc5ea7582b80947caa1e3f4a4fb8ee232d 0.7.1 3dd315b80bab10b6ac475ee25dd207d2eb759881 0.7.2 f15d039cf625fb92e061f21c9f28a788032a0faa 0.7.3 b5198f7f11a3b5e174f9e75a7bd50394fa354fb0 0.7.4 63c258c456ca018385b13f352faefdf25c7bd3bb 0.7.5 461dfb0bb60e531d361319f30993f29860c19f55 0.7.6 1592d46d9076aa832b2d37d50b90f5edfca67030 0.7.7 3308415d7de83c3c0c7c65405bec4836685a71de 0.7.8 5f705230a62ccae8718ef9b7a85b29dc569d8b5e 0.7.9 60a3ffe734b67add80bbc58448c3f6c9036fe844 0.7.10 0000000000000000000000000000000000000000 0.7.10 0000000000000000000000000000000000000000 0.7.10 0000000000000000000000000000000000000000 0.7.10 3a1b46d51f040f5e7b9b81c3b2b312a2d272f0a3 0.7.10 26dd3824b9f343e2768609c1b673f788e3a5e154 0.7.11 a1faa64d4972020413fd168e2b542bcc150819c0 0.7.12 0ed1952588ab1e0e1c18425fe7923b2b76f38a65 0.8.0 a52b49f9afcf410597dc6657ad39ae3dbbfeec56 0.8.1 45f81882c780a12e56be519cd3106c4fe5567a64 0.8.2 3aba7ee620807ad10bc34bff3677350fa8a3c3b2 0.8.3 11d956c1577c91037a2c122d4a9d818a9733562d 0.8.4 njs-0.8.9/CHANGES000066400000000000000000001771151474132077100133370ustar00rootroot00000000000000Changes with njs 0.8.9 14 Jan 2025 nginx modules: *) Bugfix: removed extra VM creation per server. Previously, when js_import was declared in http or stream blocks, an extra copy of the VM instance was created for each server block. This was not needed and consumed a lot of memory for configurations with many server blocks. This issue was introduced in 9b674412 (0.8.6) and was partially fixed for location blocks only in 685b64f0 (0.8.7). Core: *) Feature: added fs module for QuickJS engine. Changes with njs 0.8.8 10 Dec 2024 nginx modules: *) Feature: implemented shared dictionary for QuickJS engine. *) Improvement: js_preload_object is refactored. *) Bugfix: fixed limit rated output. *) Bugfix: optimized use of SSL contexts for js_fetch_trusted_certificate directive. Core: *) Feature: implemented process object for QuickJS engine. *) Feature: implemented process.kill() method. *) Bugfix: fixed tests with libxml2 2.13 and later. *) Bugfix: fixed promise resolving when Promise is inherited. *) Bugfix: fixed absolute scope in cloned VMs. Changes with njs 0.8.7 22 Oct 2024 nginx modules: *) Bugfix: eliminated unnecessary VM creation. Previously, njs consumed memory proportionally to the number of nginx locations. The issue was introduced in 9b674412 (0.8.6). *) Improvement: added strict syntax validation for js_body_filter. *) Improvement: improved error messages for module loading failures. Core: *) Feature: implemented fs.readlink() and friends. *) Improvement: implemented lazy stack symbolization. *) Bugfix: fixed heap-buffer-overflow in Buffer.prototype.indexOf(). The issue was introduced in 5d15a8d6 (0.8.6). *) Bugfix: fixed Buffer.prototype.lastIndexOf() when `from` is provided. Changes with njs 0.8.6 02 Oct 2024 nginx modules: *) Feature: introduced QuickJS engine. *) Feature: added optional nocache flag for js_set directive. Thanks to Thomas P. *) Feature: exposed capture group variables in HTTP module. Thanks to Thomas P. Core: *) Feature: added Buffer module for QuickJS engine. *) Bugfix: fixed handling of empty labelled statement in a function. *) Bugfix: fixed Function constructor handling when called without arguments. *) Bugfix: fixed Buffer.prototype.writeInt8() and friends. *) Bugfix: fixed Buffer.prototype.writeFloat() and friends. *) Bugfix: fixed Buffer.prototype.lastIndexOf(). *) Bugfix: fixed Buffer.prototype.write(). *) Bugfix: fixed maybe-uninitialized warnings in error creation. *) Bugfix: fixed 'ctx.codepoint' initialization in UTF-8 decoding. *) Bugfix: fixed 'length' initialization in Array.prototype.pop(). *) Bugfix: fixed handling of encode arg in fs.readdir() and fs.realpath(). Changes with njs 0.8.5 25 Jun 2024 nginx modules: *) Change: r.variables.var, r.requestText, r.responseText, s.variables.var, and the "data" argument of the s.on() callback with "upload" or "download" event types will now convert bytes invalid in UTF-8 encoding into the replacement character. When working with binary data, use r.rawVariables.var, r.requestBuffer, r.responseBuffer, s.rawVariables.var, and the "upstream" or "downstream" event type for s.on() instead. *) Feature: added timeout argument for shared dictionary methods add(), set() and incr(). *) Bugfix: fixed checking for duplicate js_set variables. *) Bugfix: fixed request Host header when the port is non-standard. *) Bugfix: fixed handling of a zero-length request body in ngx.fetch() and r.subrequest(). *) Bugfix: fixed heap-buffer-overflow in Headers.get(). *) Bugfix: fixed r.subrequest() error handling. Core: *) Feature: added zlib module for QuickJS engine. *) Bugfix: fixed zlib.inflate(). *) Bugfix: fixed String.prototype.replaceAll() with zero-length argument. *) Bugfix: fixed retval handling after an exception in Array.prototype.toSpliced(), Array.prototype.toReversed(), Array.prototype.toSorted(). *) Bugfix: fixed RegExp.prototype[@@replace]() with replacements containing "$'", "$\`" and strings with Unicode characters. *) Bugfix: fixed a one-byte overread in decodeURI() and decodeURIComponent(). *) Bugfix: fixed tracking of argument scope. *) Bugfix: fixed integer overflow in Date.parse(). Changes with njs 0.8.4 16 Apr 2024 nginx modules: *) Feature: allowing to set Server header for outgoing headers. *) Improvement: validating URI and args arguments in r.subrequest(). *) Improvement: checking for duplicate js_set variables. *) Bugfix: fixed clear() method of a shared dictionary without timeout introduced in 0.8.3. *) Bugfix: fixed r.send() with Buffer argument. Core: *) Feature: added QuickJS engine support in CLI. *) Bugfix: fixed atob() with non-padded base64 strings. Changes with njs 0.8.3 07 Feb 2024 nginx modules: *) Bugfix: fixed Headers.set(). *) Bugfix: fixed js_set with Buffer values. *) Bugfix: fixed clear() method of a shared dictionary when a timeout is not specified. *) Bugfix: fixed stub_status statistics when js_periodic is enabled. Core: *) Bugfix: fixed building with libxml2 2.12 and later. *) Bugfix: fixed Date constructor for overflows and with NaN values. *) Bugfix: fixed underflow in querystring.parse(). *) Bugfix: fixed potential buffer overread in String.prototype.match(). *) Bugfix: fixed parsing of for-in loops. *) Bugfix: fixed parsing of hexadecimal, octal, and binary literals with no digits. Changes with njs 0.8.2 24 Oct 2023 nginx modules: *) Feature: introduced console object. The following methods were introduced: error(), info(), log(), time(), timeEnd(), warn(). *) Bugfix: fixed HEAD response handling with large Content-Length in fetch API. *) Bugfix: fixed items() method for a shared dictionary. *) Bugfix: fixed delete() method for a shared dictionary. Core: *) Feature: extended "fs" module. Added existsSync(). *) Bugfix: fixed "xml" module. Fixed broken XML exception handling in parse() method. *) Bugfix: fixed RegExp.prototype.exec() with global regexp and unicode input. *) Bugfix: fixed return statement parsing with invalid expression. Changes with njs 0.8.1 12 Sep 2023 nginx modules: *) Feature: introduced js_periodic directive. The directive specifies a JS handler to run at regular intervals. *) Feature: implemented items() method for a shared dictionary. The method returns all the non-expired key-value pairs. *) Bugfix: fixed size() and keys() methods of a shared dictionary. *) Bugfix: fixed erroneous exception in r.internalRedirect() introduced in 0.8.0. Core: *) Bugfix: fixed incorrect order of keys in Object.getOwnPropertyNames(). Changes with njs 0.8.0 6 Jul 2023 nginx modules: *) Change: removed special treatment of forbidden headers in Fetch API introduced in 0.7.10. *) Change: removed deprecated since 0.5.0 r.requestBody and r.responseBody in HTTP module. *) Change: throwing an exception in r.internalRedirect() while filtering in HTTP module. *) Feature: introduced global nginx properties. ngx.build - an optional nginx build name, corresponds to --build=name argument of configure script, by default is "". ngx.conf_file_path - the file path to current nginx configuration file. ngx.error_log_path - the file path to current error log file. ngx.prefix - the directory that keeps server files. ngx.version - the nginx version as a string, for example: "1.25.0". ngx.version_number - the nginx version as a number, for example: 1025000. ngx.worker_id - corresponds to an nginx internal worker id. The value is between 0 and worker_processes - 1. *) Feature: introduced js_shared_dict_zone directive. The directive allows to declare a dictionary that is shared among the working processes. *) Improvement: added compile-time options to disable njs modules. For example to disable libxslt related code: NJS_LIBXSLT=NO ./configure .. --add-module=/path/to/njs/module *) Bugfix: fixed r.status setter when filtering in HTTP module. *) Bugfix: fixed setting of Location header in HTTP module. Core: *) Change: native methods are provided with retval argument. This change breaks compatibility with C extension for njs requiring to modify the code. *) Change: non-compliant deprecated String methods were removed. The following methods were removed: String.bytesFrom(), String.prototype.fromBytes(), String.prototype.fromUTF8(), String.prototype.toBytes(), String.prototype.toUTF8(), String.prototype.toString(encoding). *) Change: removed support for building with GNU readline. *) Feature: added Array.from(), Array.prototype.toSorted(), Array.prototype.toSpliced(), Array.prototype.toReversed(). *) Feature: added %TypedArray%.prototype.toSorted(), %TypedArray%.prototype.toSpliced(), %TypedArray%.prototype.toReversed(). *) Feature: added CryptoKey properties in WebCrypto. The following properties for CryptoKey were added: algorithm, extractable, type, usages. *) Bugfix: fixed retval of crypto.getRandomValues(). *) Bugfix: fixed evaluation of computed property names with function expressions. *) Bugfix: fixed implicit name for a function expression declared in arrays. *) Bugfix: fixed parsing of for-in loops. *) Bugfix: fixed Date.parse() with ISO-8601 format and UTC time offset. Changes with njs 0.7.12 10 Apr 2023 nginx modules: *) Bugfix: fixed Headers() constructor in Fetch API. Core: *) Feature: added Hash.copy() method in "crypto" module. *) Feature: added "zlib" module. *) Improvement: added support for export {name as default} statement. *) Bugfix: fixed Number constructor according to the spec. Changes with njs 0.7.11 9 Mar 2023 nginx modules: *) Bugfix: added missed linking with libxml2 for the dynamic module. The bug was introduced in 0.7.10. Core: *) Feature: added XMLNode API to modify XML documents. *) Change: removed XML_PARSE_DTDVALID during parsing of XML document due to security implications. The issue was introduced in 0.7.10. When XML_PARSE_DTDVALID is enabled, libxml2 parses and executes external entities present inside an XML document. *) Bugfix: fixed the detection of await in arguments. *) Bugfix: fixed Error() instance dumping when "name" prop is not primitive. *) Bugfix: fixed array instance with a getter property dumping. *) Bugfix: fixed njs_object_property() with NJS_WHITEOUT properties. *) Bugfix: fixed func instance dumping with "name" as getter. *) Bugfix: fixed attaching of a stack to an error object. *) Bugfix: fixed String.prototype.replace() with replacement containing "$'", "$`". Changes with njs 0.7.10 7 Feb 2023 nginx modules: *) Feature: added Request, Response and Headers ctors in Fetch API. *) Bugfix: fixed nginx logger callback for calls in master process. Core: *) Feature: added signal support in CLI. *) Feature: added "xml" module for working with XML documents. *) Feature: extended support for symmetric and asymmetric keys in WebCrypto. Most notably JWK format for importKey() was added. *) Feature: extended support for symmetric and asymmetric keys in WebCrypto. Most notably JWK format for importKey() was added. generateKey() and exportKey() were also implemented. *) Feature: added String.prototype.replaceAll(). *) Bugfix: fixed for(expr1; conditional syntax error handling. *) Bugfix: fixed Object.values() and Object.entries() with external objects. *) Bugfix: fixed RegExp.prototype[@@replace](). Changes with njs 0.7.9 17 Nov 2022 nginx modules: *) Bugfix: fixed Fetch Response prototype reinitialization. When at least one js_import directive was declared in both HTTP and Stream, ngx.fetch() returned inapproriate response in Stream. The bug was introduced in 0.7.7. Core: *) Bugfix: fixed String.prototype.replace(re) if re.exec() returns non-flat array. *) Bugfix: fixed Array.prototype.fill() when start object changes "this". *) Bugfix: fixed description for fs.mkdir() and fs.rmdir() methods. *) Bugfix: fixed %TypedArray%.prototype.set(s) when s element changes "this". *) Bugfix: fixed Array.prototype.splice(s, d) when d resizes "this" during evaluation. *) Bugfix: fixed for-in loop with left and right hand side expressions. Changes with njs 0.7.8 25 Oct 2022 nginx modules: *) Feature: added js_preload_object directive. *) Feature: added ngx.conf_prefix property. *) Feature: added s.sendUpstream() and s.sendDownstream() in stream module. *) Feature: added support for HEAD method in Fetch API. *) Improvement: improved async callback support for s.send() in stream module. Core: *) Feature: added "name" instance property for a function object. *) Feature: added njs.memoryStats object. *) Bugfix: fixed String.prototype.trimEnd() with unicode string. *) Bugfix: fixed Object.freeze() with fast arrays. *) Bugfix: fixed Object.defineProperty() with fast arrays. *) Bugfix: fixed async token as a property name of an object. *) Bugfix: fixed property set instruction when key modifies base binding. *) Bugfix: fixed complex assignments. *) Bugfix: fixed handling of unhandled promise rejection. *) Bugfix: fixed process.env when duplicate environ variables are present. *) Bugfix: fixed double declaration detection in modules. *) Bugfix: fixed bound function calls according to the spec. *) Bugfix: fixed break label for if statement. *) Bugfix: fixed labeled empty statements. Changes with njs 0.7.7 30 Aug 2022 nginx modules: *) Feature: the number of nginx configuration contexts where js directives can be specified is extended. HTTP: js_import, js_path, js_set and js_var are allowed in server and location contexts. js_content, js_body_filter and js_header_filter are allowed in 'if' context. Stream: js_import, js_path, js_set and js_var are allowed in server context. *) Feature: added r.internal property. *) Bugfix: fixed reading response body in fetch API. *) Bugfix: fixed "js_fetch_timeout" in stream module. *) Bugfix: fixed socket leak with 0 fetch timeout. Core: *) Feature: extended "fs" module. Added fs.openSync(), fs.promises.open(), fs.fstatSync(), fs.readSync(), fs.writeSync(). The following properties of FileHandle are implemented: fd, read(), stat(), write(), close(). *) Bugfix: fixed parseInt(), parseFloat(), Symbol.for() with no arguments. Changes with njs 0.7.6 19 Jul 2022 nginx modules: *) Feature: improved r.args object. Added support for multiple arguments with the same key. Added case sensitivity for keys. Keys and values are percent-decoded now. *) Bugfix: fixed r.headersOut setter for special headers. Core: *) Feature: added Symbol.for() and Symbol.keyfor(). *) Feature: added btoa() and atob() from WHATWG spec. *) Bugfix: fixed large non-decimal literals. *) Bugfix: fixed unicode argument trimming in parseInt(). *) Bugfix: fixed break instruction in a try-catch block. *) Bugfix: fixed async function declaration in CLI. Changes with njs 0.7.5 21 Jun 2022 nginx modules: *) Change: adapting to changes in nginx header structures. *) Bugfix: fixed r.headersOut special getters when value is absent. *) Change: returning undefined value instead of an empty string for Content-Type when the header is absent. Core: *) Bugfix: fixed catching of the exception thrown from an awaited function. *) Bugfix: fixed function value initialization. *) Bugfix: fixed interpreter when await fails. *) Bugfix: fixed typed-array constructor when source array is changed while iterating. *) Bugfix: fixed String.prototype.replace() with byte strings. *) Bugfix: fixed template literal from producing byte-strings. *) Bugfix: fixed array iterator with sparse arrays. *) Bugfix: fixed memory free while converting a flat array to a slow array. *) Bugfix: properly handling NJS_DECLINE in promise native functions. *) Bugfix: fixed working with an array-like object in Promise.all() and friends. Changes with njs 0.7.4 24 May 2022 nginx modules: *) Feature: added extended directive to configure Fetch API. The following directives were added: "js_fetch_timeout", "js_fetch_verify", "js_fetch_buffer_size", "js_fetch_max_response_buffer_size". *) Change: r.internalRedirect() now accepts escaped URIs. *) Bugfix: fixed Response parsing with more than 8 headers in Fetch API. Core: *) Feature: added njs.version_number property. *) Feature: added compatibility with BoringSSL for WebCrypto API. *) Bugfix: fixed Array.prototype.sort() when arr size is changed in a comparator. *) Bugfix: fixed Array.prototype.slice() with slow "this" argument. *) Bugfix: fixed aggregation methods of Promise ctor with array-like object. *) Bugfix: fixed Array.prototype.lastIndexOf() with unicode string as "this". *) Bugfix: fixed JSON.parse() when reviver function is provided. *) Bugfix: fixed Object.defineProperty() when a recursive descriptor is provided. *) Bugfix: fixed Array.prototype.fill() for typed-arrays. *) Bugfix: making function expression binding immutable according the specs. *) Bugfix: fixed redefinition of special props in Object.defineProperty(). Changes with njs 0.7.3 12 Apr 2022 Core: *) Feature: added support of module resolution callback. This feature allows a host environment to control how imported modules are loaded. *) Bugfix: fixed backtraces while traversing imported user modules. *) Bugfix: fixed Array.prototype.concat() when "this" is a slow array. *) Bugfix: fixed frame allocation from an awaited frame. *) Bugfix: fixed allocation of large array literals. *) Bugfix: fixed interpreter when "toString" conversion fails. Changes with njs 0.7.2 25 Jan 2022 Core: *) Bugfix: fixed Array.prototype.join() when array is changed while iterating. *) Bugfix: fixed Array.prototype.slice() when array is changed while iterating. *) Bugfix: fixed Array.prototype.concat() when array is changed while iterating. *) Bugfix: fixed Array.prototype.reverse() when array is changed while iterating. *) Bugfix: fixed Buffer.concat() with subarrays. Thanks to Sylvain Etienne. *) Bugfix: fixed type confusion bug while resolving promises. *) Bugfix: fixed Function.prototype.apply() with large array arguments. *) Bugfix: fixed recursive async function calls. *) Bugfix: fixed function redeclaration. The bug was introduced in 0.7.0. Changes with njs 0.7.1 28 Dec 2021 nginx modules: *) Change: the "js_include" directive deprecated since 0.4.0 was removed. *) Change: PCRE/PCRE2-specific code was moved to the modules. This ensures that njs uses the same RegExp library as nginx. Core: *) Feature: extended "fs" module. Added stat(), fstat() and friends. *) Change: default RegExp engine for CLI is switched to PCRE2. *) Bugfix: fixed decodeURI() and decodeURIComponent() with invalid byte strings. The bug was introduced in 0.4.3. *) Bugfix: fixed heap-use-after-free in await frame. The bug was introduced in 0.7.0. *) Bugfix: fixed WebCrypto sign() and verify() methods with OpenSSL 3.0. *) Bugfix: fixed exception throwing when RegExp match fails. The bug was introduced in 0.1.15. *) Bugfix: fixed catching of exception thrown in try block of async function. The bug was introduced in 0.7.0. *) Bugfix: fixed execution of async function in synchronous context. The bug was introduced in 0.7.0. *) Bugfix: fixed function redeclaration in CLI when interactive mode is on. The bug was introduced in 0.6.2. *) Bugfix: fixed typeof operator with DataView object. *) Bugfix: eliminated information leak in Buffer.from(). Changes with njs 0.7.0 19 Oct 2021 nginx modules: *) Feature: added HTTPS support for Fetch API. *) Feature: added setReturnValue() method. Core: *) Feature: introduced Async/Await implementation. *) Feature: added WebCrypto API implementation. *) Bugfix: fixed copying of closures for declared functions. The bug was introduced in 0.6.0. *) Bugfix: fixed unhandled promise rejection in handle events. *) Bugfix: fixed Response.headers getter in Fetch API. Changes with njs 0.6.2 31 Aug 2021 nginx modules: *) Bugfix: fixed CPU hog when js_filter is registered in both directions. Core: *) Feature: introduced AggregateError implementation. *) Feature: added remaining Promise constructor methods. The following methods were added: Promise.all(), Promise.allSettled(), Promise.any(), Promise.race(). *) Improvement: removed recursion from code generator. *) Bugfix: fixed rest parameter parsing without binding identifier. *) Bugfix: fixed resolve/reject callback for Promise.prototype.finally(). *) Bugfix: fixed %TypedArray%.prototype.join() with detached buffer. *) Bugfix: fixed memory leak in interactive shell. Changes with njs 0.6.1 29 Jun 2021 *) Bugfix: fixed RegExpBuiltinExec() with UTF-8 only regexps. The bug was introduced in 0.4.2. *) Bugfix: fixed parsing of export default declaration with non-assignment expressions. Thanks to Artem S. Povalyukhin. Changes with njs 0.6.0 15 Jun 2021 Core: *) Feature: added let and const declaration support. *) Feature: added RegExp.prototype[Symbol.split]. *) Feature: added sticky flag support for RegExp. *) Bugfix: fixed heap-buffer-overflow in String.prototype.lastIndexOf(). *) Bugfix: fixed RegExp.prototype.test() according to the specification. *) Bugfix: fixed String.prototype.split() according to the specification. *) Bugfix: fixed use-of-uninitialized-value while tracking rejected promises. *) Bugfix: fixed njs.dump() for objects with circular references. Changes with njs 0.5.3 30 Mar 2021 nginx modules: *) Feature: added the "js_var" directive. Changes with njs 0.5.2 09 Mar 2021 nginx modules: *) Feature: added the "js_body_filter" directive. *) Feature: introduced the "status" property for stream session object. Core: *) Feature: added njs.on('exit') callback support. *) Bugfix: fixed property descriptor reuse for not extensible objects. Thanks to Artem S. Povalyukhin. *) Bugfix: fixed Object.freeze() and friends according to the specification. Thanks to Artem S. Povalyukhin. *) Bugfix: fixed Function() in CLI mode. *) Bugfix: fixed for-in iteration of typed array values. Thanks to Artem S. Povalyukhin. Changes with njs 0.5.1 16 Feb 2021 nginx modules: *) Feature: introduced ngx.fetch() method implementing Fetch API. The following init options are supported: body, headers, buffer_size (nginx specific), max_response_body_size (nginx specific), method. The following properties and methods of Response object are implemented: arrayBuffer(), bodyUsed, json(), headers, ok, redirect, status, statusText, text(), type, url. The following properties and methods of Header object are implemented: get(), getAll(), has(). Notable limitations: only the http:// scheme is supported, redirects are not handled. In collaboration with 洪志道 (Hong Zhi Dao). *) Feature: added the "js_header_filter" directive. *) Bugfix: fixed processing buffered data in body filter in stream module. Core: *) Bugfix: fixed safe mode bypass in Function constructor. *) Bugfix: fixed Date.prototype.toISOString() with invalid date values. Changes with njs 0.5.0 01 Dec 2020 nginx modules: *) Feature: introduced global "ngx" object. The following methods were added: ngx.log(level, msg) The following properties were added: ngx.INFO, ngx.WARN, ngx.ERR. *) Feature: added support for Buffer object where string is expected. *) Feature: added Buffer version of existing properties. The following properties were added: r.requestBuffer (r.requestBody), r.responseBuffer (r.responseBody), r.rawVariables (r.variables), s.rawVariables (s.variables). The following events were added in stream module: upstream (upload), downstream (download). *) Improvement: added aliases to existing properties. The following properties were added: r.requestText (r.requestBody), r.responseText (r.responseBody). *) Improvement: throwing an exception in r.internalRedirect() for a subrequest. *) Bugfix: fixed promise r.subrequest() with error_page redirect. *) Bugfix: fixed promise events handling. Core: *) Feature: added TypeScript definitions for built-in modules. Thanks to Jakub Jirutka. *) Feature: tracking unhandled promise rejection. *) Feature: added initial iterator support. Thanks to Artem S. Povalyukhin. *) Improvement: TypeScript definitions are refactored. Thanks to Jakub Jirutka. *) Improvement: added forgotten support for Object.prototype.valueOf() in Buffer.from(). *) Bugfix: fixed heap-use-after-free in JSON.parse(). *) Bugfix: fixed heap-use-after-free in JSON.stringify(). *) Bugfix: fixed JSON.stringify() for arrays resizable via getters. *) Bugfix: fixed heap-buffer-overflow for RegExp.prototype[Symbol.replace]. *) Bugfix: fixed returned value for Buffer.prototype.write* functions. *) Bugfix: fixed querystring.stringify(). Thanks to Artem S. Povalyukhin. *) Bugfix: fixed the catch handler for Promise.prototype.finally(). *) Bugfix: fixed querystring.parse(). Changes with njs 0.4.4 29 Sep 2020 nginx modules: *) Bugfix: fixed location merge. *) Bugfix: fixed r.httpVersion for HTTP/2. Core: *) Feature: added support for numeric separators (ES12). *) Feature: added remaining methods for %TypedArray%.prototype. The following methods were added: every(), filter(), find(), findIndex(), forEach(), includes(), indexOf(), lastIndexOf(), map(), reduce(), reduceRight(), reverse(), some(). *) Feature: added %TypedArray% remaining methods. The following methods were added: from(), of(). *) Feature: added DataView object. *) Feature: added Buffer object implementation. *) Feature: added support for ArrayBuffer in TextDecoder.prototype.decode(). *) Feature: added support for Buffer object in "crypto" methods. *) Feature: added support for Buffer object in "fs" methods. *) Change: Hash.prototype.digest() and Hmac.prototype.digest() now return a Buffer instance instead of a byte string when encoding is not provided. *) Change: fs.readFile() and friends now return a Buffer instance instead of a byte string when encoding is not provided. *) Bugfix: fixed function "prototype" property handler while setting. *) Bugfix: fixed function "constructor" property handler while setting. *) Bugfix: fixed String.prototype.indexOf() for byte strings. *) Bugfix: fixed RegExpBuiltinExec() with a global flag and byte strings. *) Bugfix: fixed RegExp.prototype[Symbol.replace] when the replacement value is a function. *) Bugfix: fixed TextDecoder.prototype.decode() with non-zero TypedArray offset. Changes with njs 0.4.3 11 Aug 2020 Core: *) Feature: added Query String module. *) Feature: improved fs.mkdir() to support recursive directory creation. Thanks to Artem S. Povalyukhin. *) Feature: improved fs.rmdir() to support recursive directory removal. Thanks to Artem S. Povalyukhin. *) Feature: introduced UTF-8 decoder according to WHATWG encoding spec. *) Feature: added TextEncoder/TextDecoder implementation. *) Bugfix: fixed parsing return statement without semicolon. *) Bugfix: fixed njs_number_to_int32() for big-endian platforms. *) Bugfix: fixed unit test on big-endian platforms. *) Bugfix: fixed regexp-literals parsing with '=' characters. *) Bugfix: fixed pre/post increment/decrement in assignment operations. Changes with njs 0.4.2 07 Jul 2020 Core: *) Feature: added RegExp.prototype[Symbol.replace]. *) Feature: introduced line level backtrace. *) Feature: added %TypedArray%.prototype.sort(). *) Feature: extended "fs" module. Added mkdir(), readdir(), rmdir() and friends. Thanks to Artem S. Povalyukhin. *) Improvement: parser refactoring. *) Bugfix: fixed TypedScript API description for HTTP headers. *) Bugfix: fixed TypedScript API description for NjsByteString type. *) Bugfix: fixed String.prototype.repeat() according to the specification. *) Bugfix: fixed parsing of flags for regexp literals. *) Bugfix: fixed index generation for global objects in generator. *) Bugfix: fixed String.prototype.replace() according to the specification. *) Bugfix: fixed %TypedArray%.prototype.copyWithin() with nonzero byte offset. *) Bugfix: fixed Array.prototype.splice() for sparse arrays. *) Bugfix: fixed Array.prototype.reverse() for sparse arrays. *) Bugfix: fixed Array.prototype.sort() for sparse arrays. Changes with njs 0.4.1 19 May 2020 *) Feature: added support for multi-value headers in r.headersIn. *) Feature: introduced raw headers API. *) Feature: added TypedScript API description. Core: *) Bugfix: fixed Array.prototype.slice() for sparse arrays. Changes with njs 0.4.0 23 Apr 2020 nginx modules: *) Feature: added js_import directive. *) Feature: added support for multi-value headers in r.headersOut. *) Improvement: iteration over r.headersOut with special headers. *) Improvement: iteration over r.headersOut with duplicates. *) Change: r.responseBody property handler now returns "undefined" instead of throwing an exception if response body is not available. Core: *) Feature: added script arguments support in CLI. *) Feature: converting externals values to native js objects. *) Bugfix: fixed NULL-pointer dereference in "__proto__" property handler. *) Bugfix: fixed handling of no-newline at the end of the script. *) Bugfix: fixed RegExp() constructor with empty pattern and non-empty flags. *) Bugfix: fixed String.prototype.replace() when function returns non-string. *) Bugfix: fixed reading of pseudofiles in "fs". Changes with njs 0.3.9 03 Mar 2020 nginx modules: *) Feature: added detached mode for r.subrequest(). Responses to detached subrequests are ignored. Unlike ordinary subrequests, a detached subrequest can be created inside a variable handler. Core: *) Feature: added promises API for "fs" module. Thanks to Artem S. Povalyukhin. *) Feature: extended "fs" module. Added access(), symlink(), unlink(), realpath() and friends. Thanks to Artem S. Povalyukhin. *) Improvement: introduced memory-efficient ordinary arrays. *) Improvement: lexer refactoring. *) Bugfix: fixed matching of native functions in backtraces. *) Bugfix: fixed callback invocations in "fs" module. Thanks to Artem S. Povalyukhin. *) Bugfix: fixed Object.getOwnPropertySymbols(). *) Bugfix: fixed heap-buffer-overflow in njs_json_append_string(). *) Bugfix: fixed encodeURI() and decodeURI() according to the specification. *) Bugfix: fixed Number.prototype.toPrecision(). *) Bugfix: fixed handling of space argument in JSON.stringify(). *) Bugfix: fixed JSON.stringify() with Number() and String() objects. *) Bugfix: fixed Unicode Escaping in JSON.stringify() according to specification. *) Bugfix: fixed non-native module importing. Thanks to 洪志道 (Hong Zhi Dao). *) Bugfix: fixed njs.dump() with the Date() instance in a container. Changes with njs 0.3.8 21 Jan 2020 nginx modules: *) Feature: added Promise support for r.subrequest(). If callback is not provided r.subrequest() returns an ordinary Promise object that resolves to subrequest response object. *) Change: r.parent property handler now returns "undefined" instead of throwing exception if parent object is not available. Core: *) Feature: added Promise support. Implemented according to the specification without: Promise.all(), Promise.allSettled(), Promise.race(). *) Feature: added initial Typed-arrays support. Thanks to Tiago Natel de Moura. *) Feature: added ArrayBuffer support. Thanks to Tiago Natel de Moura. *) Feature: added initial Symbol support. Thanks to Artem S. Povalyukhin. *) Feature: added externals support for JSON.stringify(). *) Feature: added Object.is(). Thanks to Artem S. Povalyukhin. *) Feature: added Object.setPrototypeOf(). Thanks to Artem S. Povalyukhin. *) Feature: introduced nullish coalescing operator. Thanks to Valentin Bartenev. *) Bugfix: fixed Object.getPrototypeOf() according to the specification. *) Bugfix: fixed Object.prototype.valueOf() according to the specification. *) Bugfix: fixed JSON.stringify() with unprintable values and replacer function. *) Bugfix: fixed operator "in" according to the specification. *) Bugfix: fixed Object.defineProperties() according to the specification. *) Bugfix: fixed Object.create() according to the specification. Thanks to Artem S. Povalyukhin. *) Bugfix: fixed Number.prototype.toString(radix) when fast-math is enabled. *) Bugfix: fixed RegExp() instance properties. *) Bugfix: fixed import segfault. Thanks to 洪志道 (Hong Zhi Dao). Changes with njs 0.3.7 19 Nov 2019 nginx modules: *) Improvement: refactored iteration over external objects. Core: *) Feature: added Object.assign(). *) Feature: added Array.prototype.copyWithin(). *) Feature: added support for labels in console.time(). *) Change: removed console.help() from CLI. *) Improvement: moved constructors and top-level objects to global object. *) Improvement: arguments validation for configure script. *) Improvement: refactored JSON methods. *) Bugfix: fixed heap-buffer-overflow in njs_array_reverse_iterator() function. The following functions were affected: Array.prototype.lastIndexOf(), Array.prototype.reduceRight(). *) Bugfix: fixed [[Prototype]] slot of NativeErrors. *) Bugfix: fixed NativeError.prototype.message properties. *) Bugfix: added conversion of "this" value to object in Array.prototype functions. *) Bugfix: fixed iterator for Array.prototype.find() and Array.prototype.findIndex() functions. *) Bugfix: fixed Array.prototype.includes() and Array.prototype.join() with "undefined" argument. *) Bugfix: fixed "constructor" property of "Hash" and "Hmac" objects. *) Bugfix: fixed "__proto__" property of getters and setters. *) Bugfix: fixed "Date" object string formatting. *) Bugfix: fixed handling of NaN and -0 arguments in Math.min() and Math.max(). *) Bugfix: fixed Math.round() according to the specification. *) Bugfix: reimplemented "bound" functions according to the specification. Changes with njs 0.3.6 22 Oct 2019 nginx modules: *) Improvement: getting special headers from r.headersIn. Core: *) Feature: added new Function() support. *) Feature: added Number.prototype.toFixed(). *) Feature: added Number.prototype.toPrecision(). *) Feature: added Number.prototype.toExponential(). *) Improvement: making "prototype" property of function instances writable. *) Improvement: limiting recursion depth while compiling. *) Improvement: moving global functions to the global object. *) Bugfix: fixed prototype mutation for object literals. *) Bugfix: fixed heap-buffer-overflow while parsing regexp literals. *) Bugfix: fixed integer-overflow while parsing exponent of number literals. *) Bugfix: fixed parseFloat(). *) Bugfix: fixed Array.prototype functions according to the specification. The following functions were fixed: every, includes, indexOf, filter, find, findIndex, forEach, lastIndexOf, map, pop, push, reduce, reduceRight, shift, some, unshift. *) Bugfix: fixed handing of accessor descriptors in Object.freeze(). *) Bugfix: fixed String.prototype.replace() when first argument is not a string. *) Bugfix: fixed stack-use-after-scope in Array.prototype.map(). *) Bugfix: Date.prototype.toUTCString() format was aligned to ES9. *) Bugfix: fixed buffer overflow in Number.prototype.toString(radix). *) Bugfix: fixed Regexp.prototype.test() for regexps with backreferences. *) Bugfix: fixed Array.prototype.map() for objects with nonexistent values. *) Bugfix: fixed Array.prototype.pop() and shift() for sparse objects. *) Bugfix: fixed Date.UTC() according to the specification. *) Bugfix: fixed Date() constructor according to the specification. *) Bugfix: fixed type of Date.prototype. Thanks to Artem S. Povalyukhin. *) Bugfix: fixed Date.prototype.setTime(). Thanks to Artem S. Povalyukhin. *) Bugfix: fixed default number of arguments expected by built-in functions. *) Bugfix: fixed "caller" and "arguments" properties of a function instance. Thanks to Artem S. Povalyukhin. Changes with njs 0.3.5 15 Aug 2019 Core: *) Bugfix: fixed module importing using require(). The bug was introduced in 0.3.4. *) Bugfix: fixed [[SetPrototypeOf]]. Changes with njs 0.3.4 13 Aug 2019 Core: *) Feature: added Object shorthand methods and computed property names. Thanks to 洪志道 (Hong Zhi Dao) and Artem S. Povalyukhin. *) Feature: added getter/setter literal support. Thanks to 洪志道 (Hong Zhi Dao) and Artem S. Povalyukhin. *) Feature: added fs.renameSync(). *) Feature: added String.prototype.trimStart() and String.prototype.trimEnd(). *) Improvement: added memory-sanitizer support. *) Improvement: Unicode case tables updated to version 12.1. *) Improvement: added UTF8 validation for string literals. *) Bugfix: fixed reading files with zero size in fs.readFileSync(). *) Bugfix: extended the list of space separators in String.prototype.trim(). *) Bugfix: fixed using of uninitialized value in String.prototype.padStart(). *) Bugfix: fixed String.prototype.replace() for '$0' and '$&' replacement string. *) Bugfix: fixed String.prototype.replace() for byte strings with regex argument. *) Bugfix: fixed global match in String.prototype.replace() with regexp argument. *) Bugfix: fixed Array.prototype.slice() for primitive types. *) Bugfix: fixed heap-buffer-overflow while importing module. *) Bugfix: fixed UTF-8 character escaping. *) Bugfix: fixed Object.values() and Object.entries() for shared objects. *) Bugfix: fixed uninitialized memory access in String.prototype.match(). *) Bugfix: fixed String.prototype.match() for byte strings with regex argument. *) Bugfix: fixed Array.prototype.lastIndexOf() with undefined arguments. *) Bugfix: fixed String.prototype.substring() with empty substring. *) Bugfix: fixed invalid memory access in String.prototype.substring(). *) Bugfix: fixed String.fromCharCode() for code points > 65535 and NaN. *) Bugfix: fixed String.prototype.toLowerCase() and String.prototype.toUpperCase(). *) Bugfix: fixed Error() constructor with no arguments. *) Bugfix: fixed "in" operator for values with accessor descriptors. *) Bugfix: fixed Object.defineProperty() for non-boolean descriptor props. *) Bugfix: fixed Error.prototype.toString() with UTF8 string properties. *) Bugfix: fixed Error.prototype.toString() with non-string values for "name" and "message". Changes with njs 0.3.3 25 Jun 2019 nginx modules: *) Improvement: getting of special response headers in headersOut. *) Improvement: working with unknown methods in subrequest(). *) Improvement: added support for null as a second argument of r.subrequest(). *) Bugfix: fixed processing empty output chain in stream body filter. Core: *) Feature: added runtime support for property getter/setter. Thanks to 洪志道 (Hong Zhi Dao) and Artem S. Povalyukhin. *) Feature: added "process" global object. *) Feature: writable most of built-in properties and methods. *) Feature: added generic implementation of Array.prototype.fill(). *) Bugfix: fixed integer-overflow in String.prototype.concat(). *) Bugfix: fixed setting of object properties. *) Bugfix: fixed Array.prototype.toString(). *) Bugfix: fixed Date.prototype.toJSON(). *) Bugfix: fixed overwriting "constructor" property of built-in prototypes. *) Bugfix: fixed processing of invalid surrogate pairs in strings. *) Bugfix: fixed processing of invalid surrogate pairs in JSON strings. *) Bugfix: fixed heap-buffer-overflow in toUpperCase() and toLowerCase(). *) Bugfix: fixed escaping lone closing square brackets in RegExp() constructor. *) Bugfix: fixed String.prototype.toBytes() for ASCII strings. *) Bugfix: fixed handling zero byte characters inside RegExp pattern strings. *) Bugfix: fixed String.prototype.toBytes() for ASCII strings. *) Bugfix: fixed truth value of JSON numbers in JSON.parse(). *) Bugfix: fixed use-of-uninitialized-value in njs_string_replace_join(). *) Bugfix: fixed parseInt('-0'). Thanks to Artem S. Povalyukhin. Changes with njs 0.3.2 21 May 2019 Core: *) Feature: added support for template literals. Thanks to 洪志道 (Hong Zhi Dao) and Artem S. Povalyukhin. *) Feature: executing command from command line arguments. *) Feature: added support for RegExp "groups" object (ES9). *) Feature: added block scoped function definitions support. *) Feature: added support for building with GNU Readline library. *) Feature: made configurable "length", "name", and most of built-in methods. *) Feature: made all constructor properties configurable. *) Bugfix: fixed Regexp.prototype.exec() for Unicode-only regexps. *) Bugfix: fixed njs_vm_value_dump() for empty string values. *) Bugfix: fixed RegExp constructor for regexp value arguments. *) Bugfix: fixed walking over prototypes chain during iteration over an object. *) Bugfix: fixed overflow in Array.prototype.concat(). *) Bugfix: fixed length calculation for UTF-8 string with escape characters. *) Bugfix: fixed parsing surrogate pair presents as UTF-16 escape sequences. *) Bugfix: fixed processing asterisk quantifier for String.prototype.match(). *) Bugfix: fixed Date() constructor with one argument. *) Bugfix: fixed arrays expansion. *) Bugfix: fixed heap-buffer-overflow in String.prototype.replace(). *) Bugfix: fixed heap-buffer-overflow in String.prototype.lastIndexOf(). *) Bugfix: fixed regexp literals parsing with escaped backslash and backslash in square brackets. *) Bugfix: fixed regexp literals with lone closing brackets. *) Bugfix: fixed uninitialized-memory-access in Object.defineProperties(). *) Bugfix: fixed processing "*" quantifier for String.prototype.replace(). *) Bugfix: fixed Array.prototype.slice() for UTF8-invalid byte strings. *) Bugfix: fixed String.prototype.split() for UTF8-invalid byte strings. *) Bugfix: fixed handling of empty block statements. Changes with njs 0.3.1 16 Apr 2019 Core: *) Feature: added arrow functions support. Thanks to 洪志道 (Hong Zhi Dao) and Artem S. Povalyukhin. *) Feature: added Object.getOwnPropertyNames(). Thanks to Artem S. Povalyukhin. *) Feature: added Object.getOwnPropertyDescriptors(). Thanks to Artem S. Povalyukhin. *) Feature: making __proto__ accessor descriptor of Object instances mutable. *) Feature: added shebang support in CLI. *) Feature: added support for module mode execution in CLI. In module mode global this is unavailable. *) Bugfix: fixed editline detection. *) Bugfix: fixed Function.prototype.bind(). Thanks to 洪志道 (Hong Zhi Dao). *) Bugfix: fixed checking of duplication of parameters for functions. Thanks to 洪志道 (Hong Zhi Dao). *) Bugfix: fixed function declaration with the same name as a variable. Thanks to 洪志道 (Hong Zhi Dao). *) Improvement: code related to parsing of objects, variables and functions is refactored. Thanks to 洪志道 (Hong Zhi Dao). *) Improvement: console.log() improved for outputting large values. *) Improvement: console.log() improved for outputting strings in a compliant way (without escaping and quotes). *) Improvement: using ES6 version of ToInt32(), ToUint32(), ToLength(). Changes with njs 0.3.0 26 Mar 2019 nginx modules: *) Feature: added js_path directive. *) Change: returning undefined value instead of empty strings for absent properties in the following objects: r.args, r.headersIn, r.headersOut, r.variables, s.variables. *) Change: returning undefined value instead of throwing an exception for r.requestBody when request body is unavailable. *) Bugfix: fixed crash while iterating over r.args when a value is absent in a key-value pair. Core: *) Feature: added initial ES6 modules support. Default import and default export statements are supported. Thanks to 洪志道 (Hong Zhi Dao). *) Feature: added Object.prototype.propertyIsEnumerable(). *) Feature: reporting file name and function name in disassembler output. *) Bugfix: fixed function redeclarations in interactive shell. Thanks to 洪志道 (Hong Zhi Dao). *) Bugfix: fixed RegExp literals parsing. *) Bugfix: fixed setting length of UTF8 string in fs.readFileSync(). *) Bugfix: fixed nxt_file_dirname() for paths with no dir component. Changes with njs 0.2.8 26 Feb 2019 nginx modules: *) Change: properties of HTTP request deprecarted in 0.2.2 are removed. *) Feature: added support for delete operation in r.headersOut. *) Feature: added support for setting nginx variables. *) Bugfix: fixed r.subrequest() for empty body value. *) Improvement: setting special response headers in r.headersOut. Core: *) Feature: added labels support. *) Feature: added setImmediate() method. *) Feature: added support for shorthand property names for Object literals. *) Bugfix: fixed Function.prototype.bind(). *) Bugfix: fixed parsing of string literals containing newline characters. *) Bugfix: fixed line number in reporting variable reference errors. *) Bugfix: fixed creation of long UTF8 strings. *) Bugfix: fixed String.prototype.split() for unicode strings. *) Bugfix: fixed heap-buffer-overflow in String.prototype.split(). *) Bugfix: fixed Array.prototype.fill(). Thanks to Artem S. Povalyukhin. *) Improvement: code related to function invocation is refactored. Thanks to 洪志道 (Hong Zhi Dao). *) Improvement: code related to variables is refactored. Thanks to 洪志道 (Hong Zhi Dao). *) Improvement: parser is refactored. Thanks to 洪志道 (Hong Zhi Dao). *) Improvement: reporting filenames in exceptions. Changes with njs 0.2.7 25 Dec 2018 Core: *) Feature: rest parameters syntax (destructuring is not supported). Thanks to Alexander Pyshchev. *) Feature: added Object.entries() method. *) Feature: added Object.values() method. *) Improvement: code generator refactored and simplified. *) Bugfix: fixed automatic semicolon insertion. *) Bugfix: fixed assignment expression from compound assignment. *) Bugfix: fixed comparison of Byte and UTF8 strings. *) Bugfix: fixed type of iteration variable in for-in with array values. *) Bugfix: fixed building on paltforms without librt. *) Bugfix: miscellaneous additional bugs have been fixed. Changes with njs 0.2.6 27 Nov 2018 Core: *) Feature: making built-in prototypes mutable. *) Feature: making global object mutable. *) Feature: console.time() and console.timeEnd() methods. *) Feature: allowing variables and functions to be redeclared. *) Feature: extending Object.defineProperty() spec conformance. *) Feature: introduced quiet mode for CLI to handle simple expressions from stdin. *) Feature: introduced compact form of backtraces to handle stack overflows. *) Improvement: improved wording for various exceptions. *) Bugfix: fixed closure values handling. *) Bugfix: fixed equality operator for various value types. *) Bugfix: fixed handling of "this" keyword in various scopes. *) Bugfix: fixed handling non-object values in Object.keys(). *) Bugfix: fixed parsing of throw statement inside if statement. *) Bugfix: fixed parsing of newline after throw statement. *) Bugfix: fixed parsing of statements in if statement without newline. *) Bugfix: fixed size uint32_t overflow in njs_array_expand(). *) Bugfix: fixed typeof operator for object_value type. *) Bugfix: miscellaneous additional bugs have been fixed. Changes with njs 0.2.5 30 Oct 2018 nginx modules: *) Bugfix: fixed counting pending events in stream module. *) Bugfix: fixed s.off() in stream module. *) Bugfix: fixed processing of data chunks in js_filter in stream module. *) Bugfix: fixed http status and contentType getter in http module. *) Bugfix: fixed http response and parent getters in http module. Core: *) Feature: arguments object support. *) Feature: non-integer fractions support. *) Improvement: handling non-array values in Array.prototype.slice(). *) Bugfix: fixed Array.prototype.length setter. *) Bugfix: fixed njs_array_alloc() for length > 2**31. *) Bugfix: handling int overflow in njs_array_alloc() on 32bit archs. *) Bugfix: fixed code size mismatch error message. *) Bugfix: fixed delete operator in a loop. *) Bugfix: fixed Object.getOwnPropertyDescriptor() for complex object (inherited from Array and string values). *) Bugfix: fixed Object.prototype.hasOwnProperty() for non-object properties. *) Bugfix: miscellaneous additional bugs have been fixed. Changes with njs 0.2.4 18 Aug 2018 nginx modules: *) Change: stream module handlers are refactored. New methods and properties: s.on(), s.off(), s.allow(), s.done(), s.decline(), s.deny(). Removed properties of Stream object: s.OK, s.ABORT, s.AGAIN, s.DECLINED, s.ERROR (replaced with s.allow(), s.done([code]), s.deny()). s.buffer (for reading replaced with data argument of the corresponding callback, for writing use s.send()). s.fromUpstream (replaced with a callback for a corresponding event). s.eof (replaced with flags.last). Core: *) Feature: added Function.prototype.length. *) Feature: introduced sandboxing mode. *) Improvement: added exception strings where appropriate. *) Improvement: improved wording for primitive type conversion exception. *) Bugfix: throwing TypeError for attempts to change frozen properties. *) Bugfix: fixed Object.defineProperty() for existing properties. *) Bugfix: respecting the enumerable attribute while iterating by for in. *) Bugfix: respecting writable attribute for property handlers. *) Bugfix: fixed exception handling in arguments of a function. *) Bugfix: fixed Object.prototype.toString for different value types. *) Bugfix: fixed Object() constructor for object types arguments. *) Bugfix: fixed comparison of objects and strings. *) Bugfix: fixed String.slice() for undefined arguments. *) Bugfix: miscellaneous additional bugs have been fixed. Changes with njs 0.2.3 31 Jul 2018 nginx modules: *) Bugfix: making a subrequest from a Reply object caused a segmentation fault. *) Bugfix: getting the parent property of the main Request object caused a segmentation fault. Core: *) Feature: added the pretty string representation for values. *) Feature: correctly printing floating point numbers. *) Feature: correctly parsing floating point numbers. *) Feature: String.bytesFrom() method (decoding hex, base64, base64url into a byte string). *) Feature: String.padStart() and String.padEnd() methods. *) Feature: added support of binary literals. *) Improvement: added information about illegal token in number parsing. *) Improvement: allowed uppercased O in octal literal values. *) Improvement: added support for multiple arguments in console.log(). *) Bugfix: fixed applying call() to methods of external values. *) Bugfix: fixed addition operator applied to an object. *) Bugfix: fixed exception handling in njs_vm_value_to_ext_string(). *) Bugfix: fixed Number() with boolean, null and undefined arguments. *) Bugfix: fixed error handling of setting non-numeric Array.length. *) Bugfix: fixed autocompletion for global objects. *) Bugfix: miscellaneous additional bugs have been fixed. Changes with njs 0.2.2 19 Jun 2018 nginx modules: *) Change: merged HTTP Response and Reply into Request. New members of Request: req.status (res.status) req.parent (reply.parent) req.requestBody (req.body) req.responseBody (reply.body) req.headersIn (req.headers) req.headersOut (res.headers) req.sendHeader() (res.sendHeader()) req.send() (res.send()) req.finish() (res.finish()) req.return() (res.return()) Deprecated members of Request: req.body (use req.requestBody or req.responseBody) req.headers (use req.headersIn or req.headersOut) req.response Deprecated members of Response: res.contentLength (use req.headersOut['Content-Length']) res.contentType (use req.headersOut['Content-Type']) The deprecated properties will be removed in the following releases. *) Feature: HTTP internalRedirect() method. Core: *) Bugfix: fixed heap-buffer-overflow in crypto.createHmac(). Changes with njs 0.2.1 31 May 2018 nginx modules: *) Feature: HTTP request body getter. *) Improvement: moved njs vm to the main configuration. *) Improvement: improved logging for js_set and js_content directives. *) Improvement: setting status code to 500 by default in js_content handler. *) Improvement: added the debug for the returned status code in js_content. *) Bugfix: fixed error logging in js_include. Core: *) Feature: added array length setter. *) Improvement: public header cleanup. njscript.h is renamed to njs.h. *) Bugfix: fixed crypto update() method after digest() is called. *) Bugfix: fixed crypto.createHmac() for keys with size >= alg size and < 64. *) Bugfix: fixed JSON.stringify() for arrays with empty cells. *) Bugfix: fixed exception type for unsupported types in JSON.stringify(). *) Bugfix: fixed handling of undefined arguments of functions. *) Bugfix: fixed handling of missing arg of Object.getOwnPropertyDescriptor(). *) Bugfix: fixed handling of properties in Object.getOwnPropertyDescriptor(). *) Bugfix: fixed the writeable flag of Array.length property. *) Bugfix: fixed return value type of clearTimeout(). *) Bugfix: fixed njs_vm_external_bind(). *) Bugfix: miscellaneous additional bugs have been fixed. Changes with njs 0.2.0 3 Apr 2018 *) Feature: reporting njs version by CLI. *) Feature: textual description for type converting exceptions. *) Feature: setTimeout() and clearTimeout() methods. *) Feature: Byte string to hex, base64, base64url encodings. *) Feature: Node.js style crypto methods. *) Feature: HTTP and stream warn() and error() methods. *) Feature: HTTP subrequest() method. *) Feature: HTTP return() method. *) Bugfix: miscellaneous bugs have been fixed in the core and interactive shell. Changes with njs 0.1.15 20 Nov 2017 *) Feature: Error, EvalError, InternalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError objects. *) Feature: octal literals support. *) Feature: File system access fs.readFile(), fs.readFileSync(), fs.appendFile(), fs.appendFileSync(), fs.writeFile(), fs.writeFileSync() methods. *) Feature: nginx modules print backtrace on exception. *) Bugfix: miscellaneous bugs have been fixed. Changes with njs 0.1.14 09 Oct 2017 *) Feature: JSON object. *) Feature: object level completions in interactive shell. *) Feature: various configure improvements. *) Bugfix: miscellaneous bugs have been fixed in the core and interactive shell. Changes with njs 0.1.13 31 Aug 2017 *) Feature: console.log() and console.help() methods in interactive shell. *) Feature: interactive shell prints backtrace on exception. *) Feature: interactive shell by default if libedit is available. *) Bugfix: processing of large files from stdin in command line mode. *) Bugfix: improved editline detection. Changes with njs 0.1.12 08 Aug 2017 *) Feature: Interactive shell. *) Bugfix: in Object.isSealed(). Changes with njs 0.1.11 27 Jun 2017 *) Feature: Object.keys(), Object.prototype.hasOwnProperty() methods. *) Feature: Object.defineProperty(), Object.defineProperties(), Object.getOwnPropertyDescriptor() methods. *) Feature: Object.getPrototypeOf(), Object.prototype.isPrototypeOf() methods. *) Feature: Object.preventExtensions(), Object.isExtensible(), Object.freeze(), Object.isFrozen(), Object.seal(), Object.isSealed() methods. *) Feature: scientific notation literals support. *) Feature: hexadecimal literals support. *) Bugfix: processing of large array indexes. *) Bugfix: in parseInt() and Date.parse(). Changes with njs 0.1.10 04 Apr 2017 *) Feature: nested functions and function closures. *) Feature: Array.of(), Array.prototype.fill(), Array.prototype.find(), Array.prototype.findIndex() methods. *) Bugfix: miscellaneous bugs and segmentation faults have been fixed. Changes with njs 0.1.9 01 Feb 2017 *) Bugfix: global variables were not initialized when njs was used in nginx. Changes with njs 0.1.8 24 Jan 2017 *) Change: the "strict" mode is enforced, variables must be explicitly declared. *) Feature: "for" and "for-in" loops support variable declaration. *) Bugfix: global and function scopes have been fixed. *) Bugfix: now "for-in" loop does not discard the last value of property variable. *) Bugfix: miscellaneous bugs and segmentation faults have been fixed. Changes with njs 0.1.7 27 Dec 2016 *) Change: the "js_include" directive has been disabled at server and location levels. *) Feature: exponentiation operators. *) Bugfix: miscellaneous bugs and segmentation faults have been fixed. Changes with njs 0.1.6 13 Dec 2016 *) Change: the "js_set" directive has been disabled at server and location levels. *) Feature: ES6 Math methods. *) Bugfix: miscellaneous bugs and segmentation faults have been fixed. njs-0.8.9/CODE_OF_CONDUCT.md000066400000000000000000000121421474132077100151270ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: - Demonstrating empathy and kindness toward other people - Being respectful of differing opinions, viewpoints, and experiences - Giving and gracefully accepting constructive feedback - Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience - Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: - The use of sexualized language or imagery, and sexual attention or advances of any kind - Trolling, insulting or derogatory comments, and personal or political attacks - Public or private harassment - Publishing others' private information, such as a physical or email address, without their explicit permission - Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official email address, posting via an official social media account, or acting as an appointed representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at . All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. ## Enforcement Guidelines Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: ### 1. Correction **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. ### 2. Warning **Community Impact**: A violation through a single incident or series of actions. **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. ### 3. Temporary Ban **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within the community. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.1, available at . Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/inclusion). For answers to common questions about this code of conduct, see the FAQ at . Translations are available at . njs-0.8.9/CONTRIBUTING.md000066400000000000000000000066711474132077100145730ustar00rootroot00000000000000# Contributing guidelines The following is a set of guidelines for contributing to njs. We do appreciate that you are considering contributing! ## Table of contents - [Getting started](#getting-started) - [Ask a question](#ask-a-question) - [Contributing](#contributing) - [Git style guide](#git-style-guide) ## Getting started Check out the [Getting started](README.md#getting-started-with-nginx-javascript) and [njs examples](https://github.com/nginx/njs-examples) guides to get NGINX with njs up and running. ## Ask a question Please open an [issue](https://github.com/nginx/njs/issues/new) on GitHub with the label `question`. You can also ask a question on the NGINX mailing list, nginx@nginx.org (subscribe [here](https://mailman.nginx.org/mailman/listinfo/nginx)). ## Contributing ### Report a bug Please ensure the bug has not already been reported as [issue](https://github.com/nginx/njs/issues). If the bug is a potential security vulnerability, please report using our [security policy](SECURITY.md). To report a non-security bug, open an [issue](https://github.com/nginx/njs/issues/new) on GitHub with the label `bug`. Be sure to include a title and clear description, as much relevant information as possible, and a code sample or an executable test case showing the expected behavior that doesn't occur. ### Suggest a feature or enhancement To suggest a feature or enhancement, please create an [issue](https://github.com/nginx/njs/issues/new) on GitHub with the label `feature` or `enhancement` using the available feature request issue template. Please ensure the feature or enhancement has not already been suggested. > [!NOTE] > If you’d like to implement a new feature, please consider creating a > feature request issue first to start a discussion about the feature > before implementing it. ### Open a pull request Fork the repo, create a branch, implement your changes, add any relevant tests, submit a PR when your changes are tested and ready for review. Before submitting a PR, please read the NGINX code guidelines to learn more about coding conventions and style. - Try to make it clear why the suggested change is needed, and provide a use case, if possible. - Changes should be formatted according to the code style used by njs. njs mostly follows the [NGINX coding style](https://nginx.org/en/docs/dev/development_guide.html#code_style), with some minor differences. Sometimes, there is no clear rule; in such cases examine how existing njs sources are formatted and mimic this style. - Submitting changes implies granting project a permission to use it under an [BSD-2-Clause license](LICENSE). ## Git style guide - Keep a clean, concise and meaningful git commit history on your branch, rebasing locally and squashing before submitting a PR - In the subject line, use the past tense ("Added feature", not "Add feature"); also, use past tense to describe past scenarios, and present tense for current behavior - Limit the subject line to 67 characters, and the rest of the commit message to 80 characters - Use subject line prefixes for commits that affect a specific portion of the code; examples include "Tests:", "HTTP:", or "Core:". See the commit history to get an idea of the prefixes used. - Reference issues and PRs liberally after the subject line; if the commit remedies a GitHub issue, [name it](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) accordingly njs-0.8.9/LICENSE000066400000000000000000000030341474132077100133350ustar00rootroot00000000000000/* * Copyright (C) 2015-2019 NGINX, Inc. * Copyright (C) 2019-2025 F5, Inc. * Copyright (C) 2015-2021 Igor Sysoev * Copyright (C) 2017-2025 Dmitry Volyntsev * Copyright (C) 2019-2022 Alexander Borisov * Copyright (C) 2022-2025 Vadim Zhestikov * 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. */ njs-0.8.9/NGINX-js-1660x332.png000066400000000000000000002356361474132077100153640ustar00rootroot00000000000000PNG  IHDR|L pHYs  ~ IDATxy|W9z9J9㲈eUziͅ鯸VW= ha΀n00*-Bi)-mҴI&?*;Mi c|;}~ F X¾z """"""""""" """"""""""" """"""""""" """"""""""" """"""""""" """"""""""" """"""""""" """"""""""" """"""""""" """"""""""" """"""""""" """"""""""" """"""""""" """"""""""" """"""""""" """"""""""" """"""""""" """"""""""" """""""F`u0CDDDDDDDtIJJdڵ ΆFȈ'DK:""""""""]qqq0@ff& ɰb h4Xp>| """""""FAvv6222`4bMnn.h{nt>|%1b """"""""3 HJJBNNrss[=鐟1VLj,YUr'V]gt:v m&{cXh"FL& !""!"""""""NhZL&<0Z>핟Yfjl6#%%?1CDDDDDDD4i4dff߆hĢE֊-''Ɉl|""~ """"""",55fHNNn5V+UVA"""(88xi_ADDDDDDDDݣj*l6 DDDDDDDD\JJ f3V+f͚+sz|eZe˖`0 ;;âFhZmrfEEE8<`XPSS!Q;8ÇtF`` TTT 77yOJL#55ǃ>F3`fIi4`0 &&IIIfAyy9*++QWW|cc#Q[[FaDV3Z0 Xlӯ_Bf38"^t:޽(++ìY`Za6,L&@rr2,KYX}^x<(--nښ>}>D >DDDDDDDD`oO>zrT-hh$V+F#d2U40hZL&<0-999HNNF\\fs%Ʉv2w\L&]<|J\x}#/#"""""""ޔQPPٌ^9)))0mϺ:z| ~m h4^5ɄCw9|z߿Wnw=`QSS㧈'!"""""""ȀhDzzzU#(0yF/8Nx1t* +>h4ؽ{wkE}Ol6#..iw~㢼q|s@#77ݵ+~G(mf-݈Th4vxCjF?@xqsxpzz),˗/fCVVVޢj Z%KtA-R;04sQpWV[lYΟgHR:~;?KuNPPBBB>DDDDDDDD׈NCvv6rss{}N{4J6_؊qb\ ?HIIAzzzT|D؆;;L| }SwB"z.;7faUUUG{DEEAPtz>DDDDDDDDZVDK0*mENpz6ҵz#%%˃hpd(<vלockuIۑMTOsR0H~Kx|.vθnvXkjvwz@׋u#!"""""""%O(0!9CD \8WϋNw*mKBܠܦUKv\,JJJhD^^^jhIʑ݊i{et0ذ\]rhJjK@D=DŽQ/HII`@NNN{J`DS$v=8ޝ h4ru_w܁{hwZڷ\w8jxwP^^{=x L4 zs|rVW|aX^_}/^YiQAM>_꽥p=]VT"22糉DK:"""""""NêUr`0zJe\=Z;U=m(YĨ-ڼUTT4ZlQXXڈGYY`ZQXXjA:s@5GgkqwPBI>߫[nGuĉq%OnF:&+G( 1x x\^7z>)) /"k;wG5Cw:jj NctD*j['w+-#5 rg"hZZ iii~ ߖx >Be7;F0؅7=//Ʉ^;m۶!99qqq0t1A%ϟ+Hc藑][8V Ʉ'xzF]t"77iii;w.L&:e<u_6~lrs)..& sEZZZ̼Rۇ`MM,#88QuDDDDDDDDFUV= EyA#]EOGb"H'Fv'JvmGwF 33)))Xl z,6V+ Oyzb$9a@b{TcXN Ĕ P wZ`P!8hl?::WlvO @$v#**s{V(%%& SNŰa zZ؅ L2d_ϫDDDDDDDDݤ`2^R+xWX=]KT"m]!Ej4 ::ׯ,Pc74By $S"xB(TOݳt:޽pQM:~qTMOy=ö́xlWIiyglٮMVDlFDDDDDDDEnVlX~=/_M6O>AFFs|G1>9"uCNjU|ѣh\Bקڥ Z{@1o\ox!a򟍅(X @ue2L&l6z=, t:F#rssa0ӧOډڥP8>0U>F(X< i6LHW8'h`j,&FCS q]#PJ 'iN^pF`ѣO*y=وFaaa.l޻h͆8,]_Q|(yNiϝqi4D5hd !jhܑ\^y g_``Uc%scg1pwF0%KDDDDDDDԁ$f^0sb}݇_xGW_aٝ^P]j'{&` .Vl62ss*Aͬh-w?9Zz+*sOIIlb#G:\d2`0t尿}T 9U+C"41smr Y xP(C`ȕvl2 mW˝h]T?wxWԘ=3|V!..())AZZ,Kرcv؁gy+Vٳg+"^Q)rbsJ\lk4$%%bt)F7t'BLԯ8 wᕬxB( oxC)hp?NL#99V=W۽{7VZT޽@`jT('sU'c!8 xl!Ȩ(S] @zJF-`fB%V2' qiJ 2l=D?m>KDDŽU.xt[]rJ[YYYOuVdeeQeG܏rN"H/ƹ\8mLP{ښ|$$@UA(d^TyQ0:ME Z <'33!Pgh,j;vºxD1jh<-e]3Ԟ偘R?"ѣK׏#@H$ !n^{N?;׍ """""""Nl6~I\fZa0pw >>[v}yndeB1?)())a|PȪ/8#AÁ&{uϳc먗TĝX,Xhj@м1 YWݶGʜ ɱ:M=?6agLq.94C4]^-/?nG(0enQ}Nݻhܫł$l9&~|o Qw~u$@FBd2!}OUCx7%zx8oQ(ux6~DM-*O/#Q=x.oD=o0.Ǧ3?q9=@Ӭ'~DyYL!1">t2q[I>=#{QϱݰZ-/_hTh~$,K+zWiFeU6[#"jg1J#ng `lXv ൡ?Å/aڿTK"C. ׮.Syd0TT@.m~YLԷuM* WyDވS3yE(m>DDDDDDDth4HMMEJJ ںVu#e1CDa>ĆMm6 |)aI~Voj CgɮTR෸ 1QJ>W \;ꉰ9܇+}~Y7>* #kZ~'[P|="v\]GK?"vҍn()))0ͰZ5kV&{.s4ڱ*l,ÃgDX/c~{=&{W fCp&{5 9 CuЇnwMc K@2cT/L r. j`QN֜*@j&k!""""""NCff&󑜜 տ7;uk<D[_P`к-@İD-?uS_C7Nx[X |6L7=>J%R٨04^/^‡>DDDDDDDt]<')) Fyyy=񠬬 G7J%e555T~;u쿸 aV}=f6jDӃlF]hFt:L";k@ OG@El Ҏ Q.otFt*hs!Ͷn"EUΆF$+ccE"""""""d2{Biiisv{nUU=Oz8T:>)_' iW|}B~{k.)x7u:ۭsl uf3nPN!qXvq5tJD+| @xNFYY\.OWUUnc=jeX{f b5?3Gܣ={*@F|x"\8^Xh`ikj ?j&'X1G( +@.Cx.YLE 2j6x^| """""""4 233 ш|[^^s2˅J%~ a ܿ#t_9=rlPd2<ӐJXd :a ݉`88JIt3Xv` l޻hVLd@)GBpe/mO%/ŊChcV_\ -gP+B.PcA m{,LQ.kyx_8hpC"mvk """"""" \v[o뉆~hǮ `էH#+8Ws¯紅sz'v;ƍ?-=W;%- ې1 L߮,u>QEAEjk\%|.s*\|3ԯMtt_- x\kVmX90bmmc%6+U4=6%񕐊̀ |7'"Q[$(=+DIImzW#e:quD?CDDDDDDDRRRf3@5/I#0iFܩv]ױ҄ǧf`HԽrVX`$D&r9=Ü9s NjM\a5.{|[_}~ݵ7r"(';G9Wl?6n)۞tZj^Bttt>n>|%W|۸}~~( ۑw3X]v!//Ä4ǺxD1jh<-|Oy=J dOBUA(O\_SuP`}dZe_AIm9 77yyyHMMݻBdffb6׌܉!LtG D>DDDDDDD4 h4 t:deemNO["-v˦҆?\ T;9JSZT)ҖVuO#w@a2jA8}=wzضmsfkN, ߎ`ʁ&z5̧0m1xuOc Um41u!n'Pj?%]~]F]#Yuv1iySU[W`g闭Z0ENNb o! kG:(RHK! .HY7\nd]L4LDDDDDDD^jj*RSSa4kHT";QUؿx6&Q6Y2p~|-q?Ǫu]chDJn~gz%N"FT$%%gE\\^zvqsQ)Ph,D8uO"0B NNz\]%WKGc^WRRZ=TĝЇ$6 PVarGbumg0`6qa_< LJӾj0NÒ!8\1bL*Alc"mrj7b@҃*!\w>׈k!""""""~K!;;-Z<m!iNDEypM/^i% SzG]"F9/$`ol;5$jL K=WJ RSSd@^^rrrFcrJhyt!*pD <>-ܪKG>Z-233Vg:,Bd<HMMŒ%K`Xp&B~h^J{ai_␑+w܉TzW0-}iT,Ƕ.gcsxŹu.'DtkxfcD8 `IQ1CDDDDDDD7jz}*3 ӼZP=En4NXd~=3ExH[ew|M3ZUظg5n,xgpiVI\Պt!ŗrI2x]kO*y$(5 -eѢEtxw >X@Ww ``{08%^ߗl_y `0`F ؿNo+ 3G4%%L hEDDDDDDD9=& ^&{"!A~OPyUNU/n}G16Caہ;>n wq Z-R` *Cq/ԯX8KFh|f"]HҾ|(E %j3jBP>31]Vvph3R,DSqVnVVrwuW㖊{vxPQ0CDDDDDDD޽{1h ̚5oD%"1Cϯ‡ex_aqJ~{=Uiltc/~ dBJJJtoT|3 l/4}h3 Ils_P_I0B#pni=Q/hz C4o_ykfCtvIII0K%1ޓvn0:B۪:ƖnDDDDDDDoR g( =1{J7kon)lH!7 RI>:/qKĂ1e[t>DDDDDDDo8NCaχhl5磻Bǫ0P#]F#)eiG!mJ M!r;Qf]q&ѰlIa'BiaMFShi _Hu)HW`7He , O_+=ںm9C4D t#""""""~'??(((lFjjOHT"LH͏VAq!`늃﫴]>$LG!T<Ǐv*poӍ -#:\>R arVÞ+ɞv<3Bn*G*QNԭ)D͓;;MՊ8{W=CEW !-/8Oc#c[Cjj*vލ.}_$ݢHҳ6EG?>]Fы#Gm()m`jX9&%(pݘ'""4|Qz''g8? T8򫩰oC'voD%ꎔA={&1uk gݥ' |] Eÿʵc|n Qٚ>KEuBehA>D} """""""׬V+F#rss %K;<@ڥ9=]Q[v{]f|U 7mP'`9#B+fN:pV-v7A:c6͇pl[ #P,~`]~#r@hqj x$k """"""", -ZNɄ\4 {#|j֑wkU T%E\aJV\p`-"Zs$~nSC* uȆvB N ko? PJ >h%{+qiLZ{ylD5>hkCk\*s=Qoi@`y\ϱB,c!SLрYf!55foZ={#Vy@htu:)SٲUH҈[1N[kW \Jx< h0s۹V$ G^rC0T (-X-zgnQt׈bԐ-"WB< Jh!ӏ*oO_s{FoLjXNN<s2߁cQPL8Dk@Db|!. o\NP=B"s9:ߎs.vݛ)Bt_{U7S^[3!  [!] x7Y~h%v؁?ē_P#3Et O3 q/41y0H3i>j@Ղm9.MTB2N|R$T T&Ô+ XVk'$>2ܼVG->-Tڻ>^9 FsR'"!͗[/ Qg>V$Җ, )n݅NsbSIp\tr:V |ǡ4Nr$4^hoUgex֞ >LJ7UI yi0XR;cMovvvrWӬdggcÆ ؛7Nkw)?ڥ`]U{ rtjF7_fszDߗDDDDDDD48A 8Q/o-J['{PL3zV=lQSWN% Rjrṷìv+~N8|yyD^c {}%κ D_"s/Egi'jImSoJ۵Jy]qq1o86_Mc)87tY^G#~I§ChWLu9!3okurW|yH-C q!`T,ѩ8:5Ua _^F?+7R]N/vY{L /W-b5?ǷXp΍Ma!Bv2;~}/vĿA 2֭jTT&Bxz!(0>WMgFDm}0DԌ-݈8p~ }c8~A8z"A=b6׊j4&4JC*Oc'I ] :/b6}ֈyV|ߎI JlF 1x֞UUdAlX_1T  D6^IbP$HqG3,HJFd;wĝ߭s'>;wĭ}L۷՚zlCDZo M WS>DŽQjx܁p~P:Y&0'Ok{F_ChG]vuG,rE7>έb҇n\un-6>f~aJ1̆xBh['Bc =Sny E:R] Ssޤ Zrayl/IҍxdLVfr<6PG>;5BDDDDDDs3S݃~ [e_ۍ/#[jC*il|o$c⧸hT 'f?¡ 7}q*ܙp *x H0!26kꈟ6/PY\|3ĠxʯF(Џ0"va46?)a v}<b\;j'.%n(Fikc-^/nT֟-΍D}6^iJ5W)`)GuW{#"""""~9v}7U]qh.6?'wI>: {t+ Ǜ~s^+zX+pDӭIS mKDL&Y ݼ(!\k><cz^P_I3 Bp$ _ /{@#4i? y|GQLەFEZԺ=}8?`K7k!F4sG&@4%=ȮQwn44.쑛^0=iH\uxg3]#zCq9FH`0`l„^[doP4?l׻TɝŨVÞ= f HUy9o J 33Hjq>/WDL"WL]5$!RpkD <""""""_9gۭŵ_a`͈ #GCUcϭs-f*t6?l6jbƩt݅ZaTԯ= S^[ z3~__u PH"@P7 == `6ꗽZS@(@!j}K ^V5MsO6NC$۝CDDDDDDm;y&5`im׹וbǻK'oֹ"I#FrWZCV6ڮhdBBBؠD[z3M梕:w \sTdlڴ ۷oaZ:4Ex衇xb8p444 S  ؏%y0, 0#!xc {C g>W(%H\ݺu[f6bIZ2Lj{* >#"""""+ fg54\sKgc?GJ㢣ݜ"`+\Noj p~7) a61enر?я:DEK.Ś5k#%%se q~E5:vŽԿ[$Kϵއ% 9}sxVHUw8Y/Ex(mٌz[[]v,޹o~U BJJ ^{5lܸG,>U8 BNF"LG&@jYsz|ֈp`> w`&운stO~vO1{ 4^>~+CJ8JZҥKqc˃N(Z]w݅1cg_Ǽ7ߨt^ՅBeù]!\?&#A+B R`Oe,?% .`_]3n8|ᇘ2e ~a.㋿'ļD߱L@4Uq ώ/:_DDDDDDtpQylp#ɝsͿi&GeV U{'~aLЭ8QyD5?=!F<ΎjGԩS+X?׋^!Cb XVdddRfA!C}D"r9'w$Ͽ!RTޮ+)Q39;mP_rGBo,*hG!bP7r_ n1B!VӒ\m!T:K0&Pl>30_8JE#b8pIIIxGqFl۶ F}NnŸ0|[۶mիQSӺ[y>D$iNO3{w0Qs GI~\ 6)UcqLq`m4U~_ ==իaÆ566nC&A"C lcP\hkXu'k-݈|‚ 8%{.=.:kTPA+&.(h¨]6ݭMm[llTھ JWI!)d%QM 0 ?!.܀9|{Ny}w 4߇B!BUIvKS{~M~38%| cr)ĹC;f,]&EU;!B!UMM^ۺ5۶m|۶/o񉯹 6/7x_~ilm#0?s `8|0.]:s9ZPP!˝K5rJ@NN/_m[xx8ov΍3}HIIiӐciΐ\.GTT9=\.H\#1dڏ<ҍ3PhDžr t |'E>ӕzG!fB!2ն8;;cNX1pЈZmd=S"1Q~b ܿYؾ+mۆB0L̞=bJlذ;wݻTگ)44x"QSSt">>h Ʉ@ 0;4'99<Gʕ+q%d2{UTT`ʕO J'`ʕf=z!C BبsG 7#4ߧ|'4%!B!dx8;j1!dKp2<N*< g=pxhQ`YB"?.0;硇… ~hll9 Cʕ+8|0֭[gU&oooٳ%%%ݶ?#((a@.g;|̛7G'<==ͮf&41_嗥=_jvNI(r}wL*[ B>dD3\_y 9=2HY^ᆪ8wd#3E!B^z7`$sO~GrV{_ _z=`L&=ecNC9"; FNN}]Q\\LqE5"lvӦMԩSm:'** .~ڍ['/ڽL&Ù3gzKo!/4ÇXB4GT북qQ'dg~B!B=ko .[6 ,͋,.?;_;W{jyv 'Ժm ly`k &{ޏ,DEE]Pضmd2Ġ&zRTHOO\.ǪUPRRӧO`磭 :FԩSP(۹T*ƍ:THB,DhTWcS[ȯ)uЮ]fQTT:,!,oVSgB\%|ȈۺmT *{~dg!B! XUS0cԶ@2mZ0?B!m64WlF&<^VnUSò*Ų5 YUYYx!55iiiػw Dhh(Ol[Fyy9݋PjCzO`45xt >fq $''orrrd2r{T/9ZeK>z WF-ȈaP;q #ED #B! 1->&O F] @ q-,Bwz/U4q6Wu jQXܟfkz-3;|E3Uշr{`hllÇzj ֭L&Cjjɞ gr>t T*G{/l6m4esvr-cΧ1^=~~~8x 6l؀˗ԪN(:=C>o2y 4Cu١ y) w!ǷjU M!B!kN5Kc}7 44K(`2 0&*1ט(% 0pSh4F 2w xLfg6{ |/U>v;W_݋4$%%aعs' nDDUcNCff&r9bbbP^^!""HOOJ`@ӡRqFc۶m@RRRq]Z|,: ~^jZEOylyji2 ǖ-[c.m4گRks$ 2 P~4%{lLC34B!4ݾflho)OqM}3Q\栽a\iX¬hh4 @Pk[0i[C]lco-J,{שF$&&bشikZ#ddOW6o555xꩧ0}tUᩧJBjj٤ Ʉ@ 6s*??+WD~~>lXc2)TQCPAB!B\GUS@6a4vY M!}g"z%m<edZxi4j1J1D bhͽKMԑcdJv Z &<->s[رc8v6mڄTܹǎ\.ŋQRR{:0ҟt:dgg K,JLJE ,YǏGIIɀ51y3:T*~l߾ظq#**̷QBx QwNX<oTkbq{vo8%|Ȱ`1-BC١Xl܉sr 54߇B!mƖ2 ?,^_EФ:T?UTV=:7\lsoeh@v`Hr$x9'=0gik;wDZZ6mڄuarJv,\Lnjjjӧ㩧ӧU x<bbb{T`磭 :FcGr GJJ 󑜜lU%2 ׯǚ5kϛf]u%Wҭ7 C?HPG-Ȑ&@e%{\z>B!@fl\ Yжse!Z\0@[6Phda=Ogz[4X}vKp,f6;}:d^عs'lقdHRrCuq%6o|>Hy3x"mVb  {$_K.!''ׯzk֬g1C&Ն& [=ѷ|B\U!ŏp~ܾw&e1.zH.t!B!8 'WNu-{RcK "|]ho `p30y5*Z%~X{9  M(M\PPxY_5JJJ/t:pE@";;ۥcdXx1WfCףjȑ#HLLDNNlbb'<<ʕ+{\ӮLٳVЀ79D٥NP‡ AFuQIb;) wIʇjpk+!B!ı(m oㄏ0Y>$db%".`n{A}r$xv1M K< 1F'Byr:BBB_#116?-T*"((VBII V3gUz`0:u$ U*l???ڵ زJCRR.]prTE9mYcz_:pPC3[bc„ G 5-VaCr }:233Q^J LTWW#//nU=}immEkkk|pxArr29x&sxLH0w^ׯ ?^?'0BHQ4rjݚ31 *h(^k !vLbǑU><!b$y+aA"}ݶUVVbÆ  ݻQPP;wѪs9B6o/FhhSۼbvz歵zO|̞=O<>Z1Lp\#UJ&FENDa>EmsUFW R߆vM80;~ 30YuY} )Zxx7+ܺeE۷ѣGc׮]"uNWB`lkfo2 W`@ff&mWe7 , ݪ}.^ح4~%Wj0?-H| F%޽UqIF P߿m2t+:?B![W^Zƫ;wzVp,L)ѣpNN ۬ ]u}mTHZU+秂Tu7O}u)⾽tAb''mEO>,!+ƤX/sz7Q{rTc,TM6!,, ۶ms>& B^^RUO_ڠzy.szH$hiiVDŽ V@hF5(h)0uT5zgΜACC! J-DC0p?D}!B%||}} /`ҤI裏:bOžO!f1h[3g K$s%4qwhySRq98]i@bQ"ZQ=x<2335Ko6j5m6$d2,^|>۬"[BLL ˝:6o cXVH$߽yZ[[d21e?.ސ>eqqw`L]; ;P!B!NrogƍÇs,D%wfdezޟ*[%x,Yxď ؙͭ?7js+jV¦t%ӡ^o̻hc鈈7aaaHNNFZZ;$nr,^%%%8}M3>}:233]ǒaUՄ 0a„>+//;6%|b̘1=z4իz2TP‡p14:$[0@L!B=SgҥxQRR7|l/ϞWKG|,pFy|ίyH 2m1+/ܲ4|Q&>Ay. l;?cc/, 7SzBP#Yv:ٴi.]{9UbΜ9ŋZTSRR<GHl> 8sLhiihŨ! %tG ti]&uvP҇B!)##zZ;vsl6W¬si@D[ 㶀h6= cozU؞;hi%ÿ ?gnf=j<<~;;^ӆ} T$7;w}x<,YLǏǚeff8jfwzho~kv&!G T x:; ⢼)!1tB!BgΜV0 C`MŇ IDATxxAޣ'B*v27E>uŚAݻw#00/*++1f̘!֭\xw.\zT*rš _}4_3mmmhjjBCC҂( TWWCVh4vVp8!}MHxArvŵJY`ڜ!B!222۷ꫯΝ;Z}@fF] .\қ~oxv6DOCJ~2Wx67B;Di,?k7ՀאJشi°m۶ϸq+\NCvv6dɒSSSR b ш:1LJ⨴B!B!.k˖-?>L&233o@,yi){${Z yըXKj3mҩэ$$'e#ױszsߐMt0elЫ׭[Çʕ+x衇\5O_jjju~`%.+Rp|lq2/Y6ě/Cq %_נ|0$''#-- =c "q]?ۄܪsz3:H&@2}_3=1#f<7 ?ӟ9!BB!Bە+Wgy۶m͛ϣ0f#8'Ҧ‡d&Dܡp#8_U_dqs;w<t bO|%n|IIIH$Gee$2Q‡Z6uVGY6dkɎZ'@2E})iH`mvDF!B!Oqlٲ?Qt,:566) 1 kXԤSd|si?~ճo@ihXhJ)W00>B<ւعaHLLv! MCB쬣g`ɞbGѣ#3Ƒ ޡo }%L~g1"B!BFK7<ީ16GzɞxF'#ᏰaKrz[8u- : ,d!A>1oN~&,1f9OcߵLV,H`'Ÿ0Fa/j{ !BqJ$}= ONXO%'6m88_OcAJ#~tvX1kIT+gPr঳C"B$c,(WCŒg]:2+Gb_qM ‰KIdl߾{uv(B!8%QR+qVT7T|Ǿwnvà[6n뻊 5ٸ^ s<9/8;,y7pvHBYҍx ofzbߢ$i-k{LBd^8Klz B,z!2ǧo6=KQjvM+c8z [Zƅ7t3BJb'f<EB<&u:꜓"cm[)F!B<;, 9A~&k\.1Y顁Oed$69+S]Ggyo,_]a D<6sc(B7JbG{(-qv(B!2bh8wXX8L@[0!B!dēUYu%{z)콰b!,y'ixoc,6{߇&]B||<|>V^M!Z cǼ|rC!B!m\phGMtuW|wb_c8t#'1qt|v3}t"33555CDD֭[:B>8Ц3 !B!XJc }7>?6=p4? ε㺺kZ7w[4g>|Q1[mWesŔ'v6 ]6_aɒ%xHMMNܖׯgx<^BPK7Bl0!B!dqڰw><&!y'no=pU]kݦ*5?"k]3~isLח~x:6?[uu5&sjjjpt:C.<B!*|q" (Uqv(B!2pG#_InX2`Q|yp0ǛgsGa͜ Dx7qT 8oyBM6ןœ1cۃq8[?5=Dވp"F.44ӧOl֗*#&&ZTC!fFO8+ơoaaƳPp0$!B!1FpGLc#vDﯿ؃W=Zqḿc5d4K\ :xdi#ƒI+0Woge9=& G­/*  SO=ǏnqB9(CľEIXqegBK=+--ATZցB,L&|>|||jP(jQ]] B}M![}=Jz\.nmo> ?m ~ls0Rjkl:^XV|X>nX+oWW>刉9s Je !4!ĉbǎygBF_6l@qq}뭷NHHܯMIɠbh`O ..춾އkllkVTT,cs"22bddd jW@@fΜٯcSr@ZEEE8hv^3}O^_nF:iA鐞\UVŋ(,,AtBFW#L41 qvtw}||~'{, Ftt4zT8kVV!!!>>>=Jr$2||"..nP9>s"!!sεaB krV3Z+jQhqTCDžl|va#|ܥH~ѹCu/b'7GtXˑ Lxxyy9;$B!CU.z wpꬳC$c uQudGddSnއ ::뚒HYYYG]WV,JV3$++Zoqqq6MpA5fxjGqU-P***0qg4u͓8IG޳]<Mmx`OA^IC NlrĠyybO;`퀱``3+$#%|qϡ5׎yuQmx+/y/Y[uO~\v͡7zkf+*SN<8꺖ԩSf+"L hhݖK7s ϗ=] ::aYBE:EM M,(JjH$hʡfU+*UÅ8#JW/.'' / يޔc޽u됝rgEKjk]^hG};AG⇐`=Bpr'T)㊱/* n< B<9vV]?&kcppٛf=9v=;!Ǒ)--_EFFڵ?{_u] P(n P{Z) %Ј nBl5cOUVV55:lG FQmeWjР~*S]e...ɓ{M̆ZB MPU>hXLTJc 5%w@l#WCX&]U ({9QjjjPS!e/!/܉mZcga_nFs6J{%+qz3G6d̝;eeevmkr&77$JNlgf)Q憐qu:orss^>hX<޴ު3ydۊgFբEEE3%=(mH!z)`3d@('O {N|C8Rw-X,cmQ /\`C&o|R?_pBT111j̄NsvX8Mk{m}jkhF-pFoBldDžH6Y+aXcF*7cgu{L/VcQz[8~G8dػmYYYV ;uT'ԩS2>>> vʍtcRZZWp#k_I!YfGL|oH48 Z=Lnn#q<*,lۏ%Km?ӜO&ϧAٕ[z, npcyeh ͻ7pYBkB g$91BJJJgL>d!e q}ҍ[,O:ġobas(-AdD)Cfd|>b`n߻Ѧs;sZ)JXlqgjfnnnUZ*T Z- Q\\H qk׮!$$*z0] (--ԊnnnU[#J%JB7X~>M5mĨiO_߃{^2-LWoVm: ;wpp &G{iZW>Z菗_~(..Gdd@3`pss6/rd2=c Ph[9lWsn5隐1qt ş`JqMć8_oZL~x>:d)//Gjj*"""Ǐ Xڬ:dz7GrssiW4cƌk6JVC" j,9Qo\fal}0Wj47cQX,H2y{P֢.üg>7)ݫ]G'>tX|T^^{"""֭Cvv6˝!v(5zp 5 *T]7SǏ⮲{R*|pF BhDž BVqU ӟz9vn=7 ?ӟ9!2fgvZْ+$\Mn:4Afk!!!QTVVf IDATkKBR5gǮfs憹sbܹr:~ÔheUMttjBBBPVV6:B{qv Ɔ~m+;Vw72g߱y\}jʕP7Gw,H(D4$8:/FEׁ{L'㴵<\~K,Auu5!67Y !EwE(Uԃ31M*DՁ8!lG$Z{;8Qu 'ЧnSX1>+L>(--ũS0wbu{Uknn.zgҕ),,,}JKKP(:dБ\qMU;ft5vnx]UAսrLǛ@ bPھt=7|||,mb?LmKKK%P---q zMEFFR‡bw^^^HKKH$rZ MMMOunǕj콕IҟA\ou4rcM? /Oa1+1O?n];G藰4L 55j2,*!1nx\Z:;冫w"!G B`aƳ(zsKC709Ֆ oPV`nR蚚QPP`7?###;oqW}[N-Vn 22bM魝۽:=vU\\9f5&ZmgB?ɱV锛"V!!!UߣEEE+]t,p22]Bmƒz4!ݙ7uuu??kZ骽kO"s56/zJ{yԷgFY.݆EH;c=n?Eaa!JJJVL!ÆF3&zu{pF B@kŠ/D쇐qŃZKc_Tf< 0yg:E%q8-WtMo 2gzu~UkoF]UdeeLP(8pUq]f#++׶n8g{уJ ^DAAA`ɦ\"( Q*.8bOԩSj6OkPZZQK ^%HH>IOp =.2oAN:( jXCXdm֖PՆ[YknlW=BDA1DGaD$p3403 љ繟yྞ:2܉k .plA~Pu;μ3$}F[sguEɷFR!//R8rrr#19oiGyMD&5q žw Axf9x-c}lrXcEYqK HfcR^d`a\׾Q(NeVx+[eќ=C9V@ּ}GVVv` &BHdƒS&zL}i3ӱ٬+r9rrr+V@$_ ݵCڦC!Ax 0:Qw7e >nk,y6~*]_WFz`Fޯprr[zE~$Wt=\Ywe[3f̰rn9ЀC!;;;wDAA  ,]vc{[z8{ lml> F:+Y=ְy^ 5/ƞ={|NmtyG˹9CkgU/aoݻx(.={~ mf:`mopc81lGI'+X_zxjw^"-- geYaCCʹ 0^vXOG-hpVYs/*5{@=\ y2\])>%3cY3\EEEعs'u RRRl^#[e=/[YWRk`k&¶}!{; w GW0ue8~ cǎXnx<`VL_CᅪF0ֺc:V~.>Xs_!}=^2^2h\[O?|jjjH AxmW]BDIxC<+,Ç <`屷&DA{Զa_n7NjwN,ԕbz_,ʯ5Q1"qx\gp/#$ 6\\2qwyVΔsgJJ_P!,, LIIq*[ȕki*liYx\_&[q<U[⥗^BRR`:dL|# 4@WW82:;arOo#{7 ߷|y>^/>/}e?sG~y⡤jqQ 55rA8SGM ! )s" K'RPs~;шd,;εfy|X|8e-vd֘͆:RN6&򉊊rnkJ %Blݺt`^ b10 X8`CEĒ,q<|dffbCP(nλc0liPsEcڷS BŻLJ }qFQqիW#$$3".Gn=vMe'Mnկ~s< bAxRێ6X%,}lSLce+-؝y5;wt˛ uMIIY#f̝A=g˹y0_}͔a֭6V&A10:G޷C3VbGIXb73LRPxtOWW7=g5h<G[r>9dߐ0xY!6uۏ1;}ʗp $$$xz:nQXX={@&a`XA J#~p.`/Ի>ݍw|ϭc׮]ELLA.AxPYK?X N,qr̼O8)|=uWF/Q-W-2vʚxύ"&k?,Xo ϑd &h4رfo`nXT*mu(rnၞ`4 9K 5!}/DEӈӫ@ HVM2~L&h4z=J%TDRD]0}n~n|\s0t{R:rqJ^Kjӏ7pkZD?EO]l:4DO@7~i:µXRP^^jS&aXb !ޑ <$]6}oyk vzѢEHJJ|vOmGBdeeAVO>,\#g ugq|[ƚ7-cy]K? wZfҏxr/B c+*A.zwxb68YJh9g(*/7\g\e[k:˹b{K #@v"g7å)=;FCveB/E- A`4҂~9f~Y_ =Bќ';*.7|+0΃W]kp'&.z(6wMkt&Mijo&,Yog%%%ػw/̙4Cx%|flzu$&ǃ?6|>֭Ç~!sFF֯_\dggFnn.5 F/!/2;.N[-w6ԕ,S#~ t 3ﮞ1>+VTTLpĜmeu8ېޕLFIY>>>+:G[]Ww\^5R9p{ӟ1q| fP`A3 B,kjD$DgICc<(:\֭[hk 0GL +DP:_ߺ}}8J |ݓcym34[Ҏ`N|9lX NҷLEVmkęp 4NJԸzWvX}ƍx'_2 %%%Xt0T*P^^LzzJ+00T*ZؘH2jLuooJB  tø8޽C2߄lݺXv-jkgbÆ (**֭[GMHbxAxBA][YX3²,;ε\qh5'I97'Kbwbckg׵DY1UVVڜl6zY[!3cFϢ<}b-hk+y xk%G8{ lm移A8Υ"A_v(9_x0<}8tg\n=g/vş7}ww7T*jjAd_!L; xa&o3 QR3:KUUסy׷kNעG&< CsrrӧO! `Gb޽(,,DZZfϞ)J"7{S"`az<"&PAӃ@ w#ly7s lذNg]X~!A +m+9mڞRm߉8H 5Q,'Y=`jK.\MLLFAASwճl$''|\ojtRO?m(a[FBBrc "k׬f!..f; DYee9ekxlhC9̌0@|Ƶkftl+o&HP&])))Nᚈ}] D$D#X0 ]Ue7O)Oe7OdLۯszU\oT78qs4N< YadrF:^o$P0 ԙΡ-4PWw'h:WM+4+ H;C˒Q8R>`ďQЌ x_5l::9;j5֭[>[ln //2 GSS5*d2HR=z555nBBBb jZx1233m=fAA05߃wGeffbFzndž nsZFvv6~zTTT<"!/S,Dz==QEmlj8Q7_܉%P(P(lCXXΝ;7`A" ,, 3f̰hm>ruzf~!p\B9@cǎAN]dwk&%%"Ν$H얒Tnm-;9j4ٰ0Z eeehllDeeEsSTTԀ=+++l5c+++y8;%''C$cٽ[A5' f+h( 14AI #ٌ3{g>WvŶ.}NCO&WUG  9̭ހJԍ% L&t::΢  SPc5bгpjh]V[͢5oUY B߀AebEK=L%L p L-4#㙙sP-ƶmp…A{ !-- 7nĭ[{VѣGTrzzZ#VHHRSSQ^^ns!| d2VXBaJʲ{3X,F!@5'!!<ZjPǿSmm-֮] (''ǡpC>NԝEȽw A уe®yڂ3 bռoRYNEEE8r&[Qe*l]D$&&wˉD"H$A3W]vfpNJҡ.o8GG^?RRRhïy[cFbd$$$"[' d`H.f~ TbpuBA>f-Ü?c֩oD$e7h} zޥֶ1=niiETTjkkҘ@Ofd2AdBԟ!2UoQ}72Rt# NyB&z1aq~$v *(n>]C,AK  NKEWumBrC>om۶Ae|]iii8s̨Ԅ\d2^)X,,XHxo;88PXvFqq1Gyك""F!/2s'Ş AإhPPPK IDATf*#0{6C\X}:*|]O?E~$$$?Ǘ_~NO矇V!< shllDaa+9Xdk J| -- r|Tyw8cp ;h4BXq$Plj } RuĨETqBAwUVV:0mMYYِ/:thDQuWʭT vy Jfʼ{ՙ^Sp عsK|rgWz6f(Q#MERVA^~LL£_zcR9h7ǼդG!ChRDWWt;1 p8)ആ8mpBl޼EEEذaKƁ7Eȇ_VcÆ سg6oތt F!/k8r 95bMYYv ;vb+aaa6KY{fPVV[:}.i4رc }Kh9c;g~g;wtP 3ZP`o?TA8bYRPtL3mpb.C.eU?xZl3zUf:L8S؆cj[ v GӡT*'Oߠ΅BbXL) ~Efl:>lKuBl9HluNc!`}'NLg תh4øL $B:lǖ.`[;!p\K_iӦYݞ>Ö-[?`֬YN JJJw^"--meFL4"??m9%%%AHHVXIIII.6 .ORA,yl-͛7C(bڵ.ƇOB(4 .!d.bժUPؼy}NxRҍ Rێ6ҏ b:J\h0݀:.] \W6m;Q߿VzTbb"T*ՐT*؈A[:F$$$8-e +++bγsι47ysaƌs9 LPҥK.J%v؁A_`֭HNNv4:3K$^`#1P0d4pIH£ؠ3g57ɲԛ% nT_@q_F,jwuuAՂC,-}F#t:nݺth  Fse`,,X@ +ST ~8+y o=KR !9H j&C,\-,:>*4_:c37>ǎgݺuHOO˗:{jT*ݻR.-MBBB0|r1ZmoTԌz6 `0o؇^JASo4W3zLD@ba&1\UuS>|FVV~Ϣ1WOWt7<7f?'κqFōt!?YA])a̝0Ǘ~'xIޘ ޜ:^>>> `OCC4 ]R B1jiw) zs D"A@@@\\7RP(ZF<~|ܑ=^{6mГ H/b$ b3J0ãB:뾶Z NF%cmj<ǣh T Ж-[\.SLqh.jtH*^EX S(krƛP]ظm,^=$/]}< D_\`P dõ?^(Lm4xNo1W&7I%@gg'r9~h4ıcl1"‚ J) L9r788#{?Vq9FVHd=h-ɐdgg@ai_8w گ|;_5AntE2|b(k#F| S4MKDc;zOy;o:GB^9R9Av,<*J;-tup7N߹;uI.X%@L<qWсxh4L&hmmEKK Fŀ2of rK%K7yZVYHݘ?488ñL(Gݸ^KE@  р>=||T׆F|v'>x466bӦMv=cU̓4rR ,ӧqa?~yy9._8z(jj<\vŋ>nA;Nfe˖Yd~}ںu+rrr\ NŤy&)<ԜR`S3bbb~z|78|s F!d{e>ȚBAAx{ XCEiEvMK-g!f&ϥ2oݨ8ہ8<*by h]woxnpM|y١ cٸw Nl:92cC:ItOx)qa*^tvP}U /~p2qT ÊV#q\+ *uaضmJKK#YMM zÚ2\D"RSShђjZG?,XJ#Gx]C @,F۷ow}6YjKƇt8pŃƠ =tQlHqS1RX,FSS}/3,mJbAs-!ɣVA+?{/6\O7~[TF!0h=}zX PU@Ȝ,.-[A}}e$HMM\.Gaa62 RiXdff^e%0uTpT\&&?ɣahkǼzt6Ϟ=8|0222O?ō7:G;[IkiAAᅮ[ṳӸHz_ 8U+o8W  ~p6kf2V D wX+gfЖ ˾<?x$rEBdḭ'>o_Cw,Ln4qMCWgPT R YgjL|'>6bWAm;kѢEؾ};233-=zc^!!!###Uˑ6 Ǐ]3T'Ԍ!y1L$:w)S歶SNEdd͐4C#Rێe^C٣; b_  MjwףãbzA,/ts630e&Bǖ J.٧G:?n5qݢ`zb!44jکQ,k0L)QOB[P@#0߶t:L&ӢWHS15ˑq|#rbSYe'OLV@2FAA1/^@ z7XtQl,1./iP[E@PO F}oP[W 8ow slɄvh4bܸqڿ!((fAPPT0Lu_\7X,TWO݄.kr y!}J4*dȰ[qqq뮻PSS]v>w())\.|D?>`޽ˌq\̙3WF~~ v,&裏b֭xꫯ > %CZ\13˝M:Bddd@(";;jsA>Ęhs/o2q~RdMԭ*˧Rێkm ) rbGnכ>|S   Ƙx<쳘1c.WBekn//tBq Syz0HqF>>z]7jx5 ~CfZ\,$Z9GO%Fmmm`2 FACC]BЩ1Hnz=N |X1tW!bN~=O|w S򙧧fx*1 o6HRl߾;wĮ]>$T* TcbوEaa!r6}˼͟?g2o} ST>W\Z?h|'U)W mi|$!™\TTTxzZ0#b;q3֜|a@, Orۘ'J6ֲҏD۷AAİ Dff&-ZXd`6ed{Y;z]7CqYx>hiNV1e&nN[7Ο;ttteޚ썳  L&t: l6T**m,EQ/ 1b>V 7J[C"c9d>̤Gjj*jjj留G` -- rOv#1171k,bc z _}UVVSĆrpΛ3 '-C]]!<&$v4a@qk|~yaQݐp}=J׎}ny C[9z6>~Ac'O1|>|I_111ϕ) U0!_n5-zh5&VkaPLWzˣ!d2tlTA9|LJ.vZ t7=7х].1 h4p8H$h4`ٸ|ý~BBBzMP ֨>.r 3>ARS5niC>P]!(-F96H0ʆ.Uxޗ~EJYXoO@ӡ󑔔3g_h4B.CR!55t:uuu:Dž z-T*s碣n Xf ***駟͛@H45w\L&T*D"~&Q#"#mSеN}mm2(LM]QMnܸ1\S$!oކK?tjo6D3rRێljcAmPjݛV^,co9_Vp'DAcM<= ŋB%A1:Q}Ic֢a}£g b"8eOrWmtQlHY=8|s9~d2ZGQQѰa,)߇s7c~xwy.N_-?>>Z/~Wo^9-[!}0gd2ddd ??MMMC~ܐ̟?rޕ1\y3g˙VȀZڵNTJ4 l6fIgmg$nz,> &?P`̓x_lyd >@Ok X3YfTtQϯF q.x7u5`G4AA1x_˗/dz3{Z4@d6tۿ_=%j`0- ( {j(=Ն:5>89BߦIE\V~}3|mmmN[tbtge0!o-Ə'pߤǰxPrɩq\ )!.4|oFk T*Ejj*L&rшj!55|>555Cr,K"""_~ V/BR!==l6Ne:ر _>OR1awMahŠP<RZ<L.Y3Ќx(vEQxzпwPVJ7Й,&IE>GyK@ހfM`Sk51갯[ԕ"\p:-ī= AAڵkK/A" hn_hy2FA@0AL'cǠ)۹>4jez"_f̶$դǙ6N85ò!Tps-WrgCDYa@^f"n"eK#  b,IJJr'7*\?'Yob: XCEiE৥Iƒ*3ׯ۝ nT@muQx/uup-MzmC?Pj f-WZO/?I+P4}elnhPwy}6nt\j,55(,,TphllDNN QRRr,X8rCb#==7oFnn.***|mf ???D؂ IDAT"T*t0 ݙcK@qubϙá 650y[ o9s|<ݝ8^$F!;(Xs}OO-m?~CAAA󑕕EwϢYKǿjsg& _HH?Bf4 LVC(:=FPPz= Eop9TӋ ~]mN7Ǣ)=`gIΠ̚(Ó/Bk{7x$%%aϞ=x뭷wyz:^CR!//gFff&9R5LTGl*iZfZ8t۳g> #;;j?~}cX+]h%ǤS˻s4-&OD/eB? L&& * ƍwƓ~ӡxAAAmįՍk׶/#b\^^ t z׃ "FO~h!D>AAAER)? <<ܥMߎ՗4B:HŶ &i\!if:!Q+ =@O4n"3e :;#dKI7np(CR ztvvZ.ܐ`cPЋ=]1t &s#[ćދpҗN#ǚy ڎ]gv ь;#xz:^rGll, <>555 3V`_V9@խ{_`?a5cuΕ$.Ç(,×I<>g;ASb   F$lOd`Z[!o}dnxC2fX1hS(Y==}zQ[ol|5( *9#44zH$H$(J+ 6AMm@E9zӡoEIW4bL"G֢>:O'f!C%5%xmoPzz9&w>C*1F#V/`0 ==PWWgu[xػwKYA:cʔ)R,z+%&&~j0D56q-Z$܎tv 2j2BZWB7(BVg04pYt)DB&PXPb=tzOH\xCOp~Ax}F=5V?+tJ{{2|~+E^L]ͨS_{Mxxlsq8ِRV3v £xW4h4/EWl2xzAA1,/^u zЙ$8_nѣGj@15g@ZT(f AALí/wuPQځEZ υu/ZMMMx_v#((* UUU0VF Fo`ڕh*po?\xqPx;}<G21}z\>/s\,X+V@~~E!662LqZG퇤RpEŸ@t/zJ! ęhRoެjo7|~ϙu,?5.Cic!`Ș nv]G+ŀb߿ )aٴ UUYle1_ B9O!>>hh 00LL8FUUU2zP(`X`0n23>'PM+}UÓ_ƇKQz'[`jL<+ ۧY&M"AIKK\.Gyy9RSSRpἦ&!668}9|O4Lw#'y~TBvtд^dBgG,X̞LQ1J<|. Ǘۡ1t^ҺgN>=[;ɩ?Sߎ{"jt\PF6QtShwrޅ}].OO   ;= ysSsx,/tBq2`X1NBjOϟaZ\,', 4n Qz~ܹsCxx8t:\.nܸaO=T*l6F:΢ԛT?aD\dD-̝0"h OzLɿ,2F=%׃}쫩A^^d2RSSQXXOOk(//˗!/ $܉a zJ`jmi\nO#?ֻɠ& :-Ё Kͧ1tk;!_\:.RXU\U%@ [A>ljO L}cdAA1HR{ H̞kQ+nǭۙ'z]7FiZ /jC~ -MZ|WBά|;0}Cx. Bht:D"L&@+h4l6z= {gn6! Aܢ*p#˕ӸzK/3Om ڱ4i6mڄ^xSJ2cgY ɜ*֠ng_&F [ So5> X*[N⣳O;vwP:x1긅h̙5)7H  ^a5r-n._;ħ: >5va'  >?v43pV)3XQ8OZ*J;,4rLpӸ63w0A~8<*byfZW4NA4dBkk+L&jQ__ow@( L&tzoLh QJT0>eMF?{wW} ;g}F3i[-^Bc !$M(iBR<}KI.Zn~a< B2NJ+p}jguV&Y à ON~6믤up98<'(+ɿҥpN ?]8ُ~Y4,L?DhF],\Ef"B!B~0?g/PYk =%zTq*>![@V|9== F3~V1OzX"Rӓ(E& uuuBfЎaT iC%u(Tkf}Ȉ~8tZ[[z+L/^͛}/”cVWfhO>jt:"OpYحcEF%F΂[CR琔)ߚ >dQa #Q!0EdIp5E` B!B]w݅իWeh9`xb5 7LkzE];IVQRda0*>HUYk 9=zړ8O+T?znz_ڱD"A`6Ѐ>b3h0 L&dY(icVHR^yt3 `AL`'g@˳k_~! 8|0N<;dNxuY|4M˺g2z&_ح,vjf`6*s`sRR< xG IX!Wb ֳI=P MFP_ !B!Kj=p|YZ 0/[@ɒs:TQ摈iEY.j0/u#9=nar#Xvbg'XSEW/o4.~ΟnE0xdٺu+ӎ 'OBelx9oj\+Cxٜ4['OzAJ h@7M"N lHr1op9X$1)c(0`[ŽXNcZgp*++}}}YG!#bُdF`}-q8ߌriJ!B`4]G˦^1ǺJ1ǚpybrp-Tܸtf}>  Ⅷ O>5S`z555F|sRkpY'p)c6®eitmgy[w}jۧ.?`/_?ao_E]>d'AهJҎY~?Gs%*ΜG7ա!)h Ȑ')L}ڇᖪOp+޺[!SY a;@`ؓcpB!rMG>2럍M?pFvp%֍IxwTΕ>TӞ3hNOM)6YRA]тZs q,hum'_ jW߀M;>;ӋX(d2a~T֣D=1tpP[C]ZgZW?;ݏ}o?<XFu]ؿBoYvVguh`HY0P{3t*Scrm||7<~Ջ >d`  cGjv?h B!r۳gϜ=t鷩=xq*}\Y$z`Нuv)D v'EmiBШngZci9=V1M(/JǻAw;x`aӎaNy$ $e9ͿlN: n쮾K&UM7hھfX Tx9zҞK8̃T!K^#\v dDtͅjf+GgS]p=\z#g:,mT!KRPb A_D#hpx|B!BȢp=.`7 g|(ǍVD 5GӞĪ fLF6f7n'tk7s_I[_N=^3'-Eaa!VX !3fg7gCᡨ@H6) , l J@v#F\۩C&Y,,۬m2S"ckKB!BY~#rXٺO{Ѵp@Ʊ0J*X2id1[co0X 5Z$nwAo7~pVBAi V'@#ߍ 0X`Fb}VNp~_(C K(iH>c2rmu=K`7&~~XW>k`0/fM7De3"xd;A`@clp"pw. a߽F΂ewYJϏy)YJojB ӆjBog(z-o'!B^:Bp80*k X2!N'%q*3+b'Fk$KZj+qHgdAtK ݙGͷd|`G ;Z<`q VR0;K!ĂY~W݂_|7x(lF}}=|>;q&0 F#E(i>VT*/gs+3<7N=0 6_3^0u{-6vӧ`1X~%xoMyRSRR[ d^HWlN/c;Tx*lEΑcs]x9WoVm"q^[i0tk<-'S^sgZc;ԈDL'e,y`ݙq@- ׮l{D,2F{R@dTepV]A",ٳزeKjMUU\4F>qn[I ̅Pe VR1$!`0px>!UrNv5/^/}oOnj2\DY_Mٽ7M^-g3nl!:&ԻgE!vX`rh3ofWHuyF$Qό㏣=^~e<\$٣j1(lm w[y ɝ#y$gb +!B!da{Cn0rpjGF]SRGI>LɄu2%-֕L+֘~%eݑ0ZRd''b VܴqHɄZ YG0#rA)qK:g _@}q=`}zx^xC^Ą(|ް϶''5^KoN07 gIp%s bMe2Yb r*`wrz}mf)cF˄k->/J gXuD"$)j @ףd^wVcޮ|ސa5Xr=n_UzٖgWowO"A$KW,Q0ؙK|\ V;ލ ،xT6|އ?̥afayi)نCݿpb JfފV.Oe={6cȴŞ~ivwݙn,rT!ל>m/Y:^PN!B!L)8ҏ, 6b'LKXGK"XS%z0upyS&15G3Vn4OFWGXQWZ܊@hq:ڑs/?-[ވ?Yq\2uDQ(0?qϵj*/x>g.pR?ý4)#?`ŧ b8[ĥh4ѝ`붉I8~\8>[lg>=vQ\ؑ,z-p5E(B!zۄ4UAPd 0;KZix ǡzVN&E& C,'ga`/R~6GҨſ5ꋗc77ݟؽh=S 0ݳ_W/㡇d頂q! Q~^KFaAB!GiiޣwaSMmt=ȱVnPW39=5 FXcX8-'2^;֮] ^ϧ FƟa&7٠M0sՁ4E:f[llUUE< pG{{{STTTB9#>P6C!x\~h( 34EO? ;ރfe),:og}]A!:tݎ~p>(pk >`$gُdF`)hfrz!B#^w >n{ $Q=^#7&Me#>5Gq*k̰;Y#dIWDO{ဌ+yb4㧷[@ǩxZƏ[N qףgΜA?dY2f56,FJvϺ'EQ Q]]h4"f3\.b.]XM'qI}ː!LUb[v^F΄.l6TTT #LBU3?s۟J}=-`Fsp3dau~ika0X:iNO8(LKŲ,!&3ʙup3.P>ha_>Yӎb1rHͻx< voBa6 pYaUݼC%>xͧPj/gnx7^èv-fӓvt9:}(ӭE208nVVg~`n. H8צY̾cw>d_&MaVPuU_kEk81.-O?0N>M9=8*2 OF-7vU0q8d߂J!Bɏ.iǭ+bc#ů>hrAo>5 &Xcp5f4p5sfZVndS9=V.||ݰ-3x h5UUF0 JKK!Iq:lF4<σeYHYd9/# {/FΈ7cײi^PFh4 x|}iYtۇó>oy${囒Y7޻xsXm:`81wR7~>g6v;xv|ߤBBc`fEhyp5E(B!2]>`3Zoni?^h}ul}m1l^[XƌSq Ci5 F؝+ 8K7 ;CÝw{m<`@,jHUUUHdhhB[gylق{ G{u:__MT0KG\B.Xc޸kQuB!q6lؐ:"hP,Gn ʜ%8}<+S>,M6% ދHtBWh|q(w\k ]{ ^蔉c nd{4nq$AXVf chhhڮ, \hшebIa,xωgpb_ݝWw'Fg8˲0y:̧}b1DQ\./x<tvv>{,.^{2SWC=2<P'NLލ-ښ.b*ChX,P C{y[g?3-4S\\o~hllDsss^I>!dL_1!Ͳ(}Fsz h|!B!C8Nuaa}v֕[G~׺%-\1f^O{rzX9=iIY-0`/a/ZVlrmUU QTTՊYut:zpQS5&ObP;sy+îe|{6a|5[.+5./ߣF7:ڍ駟N8/wI>w 0Dvuͅ~v?8)QYJґDhm/0GbB I!Bŋf͚p'%Qz`3j'tkĎ[Wn̂l=\ЂЭ  !e2I0<< шB!Tf&h(E1SkD1xy[r=;zFY,TTT L<.o:$ȑ#iE:AzB@EEx D"<r^GE F2xr/P|υ~;y!Y!Z*ǿÆI~P)G\=B!B&wɴtuunjSpǻOဌcMa, \n&d>1cWyrzf3i9=06ޚZ'&a96 nNL&L&aZQ[[D" 4gY& (Bȸ:}X܈s;i:e2Qwgrz2yFF7Φx6L`g !]__Nvm7re`kpD7°,gg$PYm% >#g~D֘aj}TGbC!BkCЄmmm3'ݍ}wMGꚏty9=뷎psp $tT_/0R^[7}EA8^O$zTt0 Z6] 6%]t$g[v^;-GӥƷ@II bTU8y"de_BP#n- nJ"Ie."pc/E#>V6N t pB!BMpq_555^x<ؼysDf{'ݍs*ykk+KG sz.c*6nrc#po[Yg^L`:Z, ::=cxɊQa2PWWP(4||Y!IR*צY7ӈ[y-ý+>:9=eeeEqNrzxcއ2Ztf08qoG', ^28AÍ}v-m8No MYƊZބ~w lH/.Z`֭S cP}| #o\PgbrǎrZ ,47@,rJnc2Oa: QAz ͪe <AH}8u>KhW_rF&7!8Ʒ%I>eW02헿eڱ+GH :'PHW Vo˱fY$?Q[FwdqG!9+8Dz 6u #bُjPB!%.NyhoD,8I=iw#ooAZb F:G`+!=2bܕc"Qh(.t0X|y^ d2`0$+gf/QǼo5u 6|4qL&S^=p8sxꫯfUyꩧRFQ,(s˝|CP1Wx/g=X-%u:__Mr-)S- .` Aֳ^V =B!\%4MK%2N<X)Fx:c͈UyXt^ |x'( N81ἩlɵFB<B!\&yL&OQZZ:$Ƽ544j.Jh K^ZI^Wv%t;cT_/W۸-wB>ulb'qF2XS{ò,JKK!I#bǤsÆ>a,CaBn7v;@^֜L"H}_ghmmu*tX6-@3?{V,ǩ^ SCY1b'WBwi-!t4~i˿ >GyBo,rT!B!BX.E`dۉ'xH>y&GVVJlAat?boO>MN a+x8 ]] 9X"\+#F䖃(m] \+TԹ@)uJٴ i(@ ^:YF3j4'=`PRRA x"IՕuq,Lb}B (^{7(z%4֥q:VQV̡GP .|GAEEx D"<˰ !T!B!BtG>sV\՚~GAUUa2LJΜT-y0 Ǐ{W]i3s^D]0[:aUOZ L3 t;4_/7~{ӆ}/?%0 ޸Һ[^'(bppV3|m IDATB>yTVVBЬTxrn-zHzEWT, H8T9uF0 N'|>bجy!߳Ų,n7V+"DqnG;~;w~?Μ9 LN{.{ eu1 TXe9G贳qEzEtDUpp0( 80vO`޽xndIbNzB!BNe=gϞŊ+`4FUU~\t <ϣ`6۽&h ҫlLJa^] J$N{I bo_0Oo@>h" Q(E"^=.Af ϸ$BC>m\W[l2堓D0 IJk}hZj[aa!f3FzY\.*++!""Ȝv$ ])LW^/Yn˲87Yn݊I_*k ,ScX8R(>="17J+F cMUQwL3#W?(Anai jS^3jczYz#*TU˲;:Ru_ :ҝi$Iic=G}w!xB!B!y먥d2_|9qDNגF*io[vL}UY#Kc㪯G_O{G!w%ǰ`ܕmH?_C%Α|`z˗/f{d[ҋ>ѡ>idd͕2ᵁ!Iq:(+3+ȊA >lxY;Ɗኢ@E| _Ç+;ٳggrmB!BUkk+f3^],eJ(=6ˑ GHc0p @/%{jx7L{xN;+ PF4>y'W+'JYq9Oދ,apGE2)*B!B!DӁ㸜;eԄ#G`޽ذaC׍x<466cW1&$i C)7@YfG$N{8M+$! F8xiU)u5K؋ CAu67[ 5RZr!@LB^̸׃0 tȸC^­L "DQhD}}=~, JKK!Itt$IFWWW===hjjBwww^!ɠ ]e}]*&! yxpGDAK[PBan(=V%U'#P]Hdml|8RgZa8v |}P}}`V)=dkC>SR9tX$Q Cףh} s+j1d멻\O@MG9pB!B}֬Yݞ-6JHq@32WZd]qb#E/ƼM3'늧rzn8M!'ZF:Ozʦ@=58v\W!c: xFk>TKuFAUU9Es%e6 F{p}.E+i%.`qiq[c1v:dH;qzI$gMjINmZRǭB];\ENn.HjvXvE+iWh%x>3gVmn}ANN-8?>VUU@s9n뱃ۙZ, Ph t>@;k׮wQXXk*R[RWxjHN_F0">sm0\CnE{zԸ,9gւO_3"7Ŝɕ сg(=rsss!77;v}::vQg]>lrjI(QP`[#7 r`nҮ߉-%GՇ__aϯn7Ww,؋t.]aE( ~f0)??[S}DDDDDDDw:YaYVڋUUEmm-2o`MMMx<D{`m݂a)q˼YfI$L0?2!f lXLXxB55D +onEiAV (vY9ʗC*.z-jXގ!oZX8U\LZ̛JEGGN<UUŁpO {(<8y$q|_޽{wބuz0TBěv[v{=>YhCP`.H^ YNmyFo#UC}_fp*.u?yuF{衇pرiKew8%&[coFDDDDDDDAӴ7e<>@(}Z 탦N@Ll7'u rĮfOOyVRo7]aq˽Aek0DQlA]F>N]JDK]߰`0EæPOOe۶m8q;궖eAUU.`̾wpF)~;sw@ ,|* [@l\g6Zu([}~Nf-Zj# ۷o%Ing>M"K;ÐZR < fCK\@yĀ-' z9QTl,v3P57؝m@XEM[^vm<9Z5K joq-n[!+₥sz3'_ ==C)[(ȲQ˲R4B^d \.̙ oKم0'^v=_ʒ_.@턟'] EfcleK0 iAysDѫʒⶇ(r ?%-L -C_s `- _lBNxa`^e𡤧ziX+6H@*`>WE!֥X/_ٳgӓ~سg>I6m썉{_*>~3MbS=!=zh@fF(`r]r+aӎ5k"2["]}]sШׄHϒn ]>[cCDDDDDD4 ECI`&,++q>69C@X {dzfώ r˼ 7xӲ0eMD >ʅ~}_ r.L-A>T=p S|FC-p$0 ˠWܗ2YD)ÑKDhmmÇQYY/~hmmEGGTU c|B/E05=~(AEu7[FqQ=a\؇)] ^sb{&+2 CE.Mc(Be0)>ۋj(eހH'JSSLӄ{)d} Į0,?# 0a> ܄ + ,&7bWD29/ˆH0Tۏsb{BY!˅ f m0.苽lgC}KY)3S~9sW^[2*GŞ={ҒN$8(%hFʼn'O} =N>rg]Z7{0>,ey)90.pt]ĩȭH<=9^3!"0b߃2 """"""iNHEQ`҂TWW0 A6M^---BvOaO@=ЯAoNtXyJa8YاGB}:50Į0fRg>8eؠ-ρğ\ 5iBmWRc-!eb$رntAkXގeC|+364 Νo~⭷BmmmKEl6ӌdY4M/K\r/"JKKq̙ԎeX s]0muINn47ۑ2 d̔'*˲,H'0!""""""Cy,kRy>½ 5!dBj Ym#(C˴ !dFoY Ȟp (T* ]k]1'\Gac0W}=z$zzAo(QOǡC`۱m6tuu9*Hz[A`†t³|b?wgZ>xB"""""" #bl ~fbQwnXv _-&:Blbdٶ(Q }"%. |:~[\<;u˱aDVd_VՖ yP<:cz2Ge8N.D3V0u)f߿=p-Zҵ6;^/9(oR0Sh}fhQ,CӴ+Ѐ]vׯO)innFGG˱dɒ4^erBsm{Hm*k7:t Vzy,-%A[!`@_K0چG|qp%|=˶6n8ٟwޛU.ra\'[)~ѣGqرI"-'zNp8M${n:t[lF)g#e3 |܍4 |wJE'L!""""""`?)QV`!wfO+&r+dr^ msa)nd~8U*fauAҧ4p[FIDATN o i== np @QH4G}0! رcr oÁۚ+S?Y!2_Gׅ~@`3w‡ 0gje}fϞ &+gaȃ̹vXnOp3T7} DyG%v!_ @jY)]ڻM~|h_C/[q=g r8ώa;\`ѯ[8Ɠ(}㮾ONΤfTݑKc?~c9k~JP+#SCNNB gL |DQfcГA C}&kghOQQQJS;`uz3|\dzǧSw,"ľ!>>0 s!_ @j A#a7=;iҏa) rI? *DDDDDDD3~p8<?QYY7p$o}}=d* E.XHsc3.10 loNjF}a$X.M>B rT+R>>hŮ8؛c,Gl7T4\z0KMzZ0Duuzz?#Gh ,#sNnTUUaΝ̙;>jG3D sUً_?1wxidgeHe8EQ8ѓ8CDDDDDD4C ,kR}N:0PVV~i---pJU½0m‘ߋg@E|r<#+ ~݂=bA±"%. Hm&dXpCş[6 /+Ro꨽>Dh{>ft-SE4\xgΜIv¹sC4ͤ2 p~TUUߚG$@=ݰB**N+-hۿN b{ xr՗ 6\z569#I880!""""""ᦲߧ3gNj'gv ֙ȋ& h,)&F}\2~8cNVaA:`A9SwP]?WM~xT'.xVtjs.| #<K 󒾆Iu'3S=!J]AƜ¿tziۄot°{!yUSzW[{ '+++q}d 6QOh}ؿ{Ŗ-[܌m'"N"v=3t#""""""CD.6O{YY6n܈¤zxP^^%K'ɱAڲ0_; 0Q`g?GqC Cic9K刟xZYV/ن Βp, 'wk&?r}Z[/ن@EXC[r V͟S?@JG<YyÔ6EQ9rnAl(//GRRS{:>ՈАAwލCa˖-ؼy3ow!|, |0N(vAzag`| ?ѫA`w(sm0~L w Ͱtcjg\)Hr> Ǿzl{#uG||r2w%uƞ; p:0 P(RoF]#{(;?lݺu.^6l@]}i=ݐ8|ؾ};JKKK/;w3 %˲ &֮]Gy$}j*x<4\ K0vezŷ|:kA>}DV ]D-n[%o-NS bn9^{0xҳTxh˗/>}w^:@DiZl"Ւ5l~G=Gvv6v؁ŋ8<.{Sڧ;[qHwJ][n?~$f18CDDDDDDtcԧ'8rΞ=7"}p1x<TVVt?^ 2;k`RE݂|93c{m!dF)S Bt8!2d.BE:"9bݝ2oCY ֦D3Qcg/$߫N|24M{!z{{o>444=|:(oG_p P|rhJdbQ5Ȋ w&slڴ /|7&$yPPz>#C>y|[ϟOj{Q( ;'|(f2}~@dٖKBD胿;ȅY*6]l@SL58,Y KÎ(Y[c:/O-%.!Tk..HO)*6Q|0\PUUχ'E11 ӟx&mnE!/=z'vJ]rزe nw]> < |(eY5~ G~(*[K!mYE0C'x,]G jɜ+G<@ݥa==#M b@/~|CĞ"/+,esvEŽݿĕ뱟vرV֭[_"6.?ZGMc>&Gf47oƆ /&|FÀbObs0߹3dȉLOl+2,$ST]B?E͂d~ܒl8,7+u׃K5q@aO.>u" ω./;c˲ڰaoزe˘_G !ه#>)垞h_oT#jbCDDDDDDD~} y[b/ .OC'~r259;Y6@%聾/Et8`sojjºS>D`O.IvGF spr%| ;;{iظq#ZZZm {㏙(9q 555عs3(>DDDDDDD_KO/8.Ty Z4aOb2o8K [&njkSEyLjwOHhrcNB!tvN̝;7Dw[:^}ՔV0%pFjEQ`EQ s{Nwj e-XG&QZhXP^@ոs jcΜ9BG?Ž;}/%ooSm\]7oơC DQf<(>DDDDDDD4nSXX+V'I`;ޫ]{]ɛE}1 uvŽزyӫ1/5yc˼>:` `:,CR^C>~'ǡ/6I Q[[P(rj[rJn8Qp8xӔ((+ϲ 6`Ƒ#GG 붉B6 o&~8tv;mۆzǎKZy:t? ʣ[s """""""PeAUU1it8X~=֬Y򾹹XbŴqKq>ºVLjOOCC.]4Ʒhllޞ+K==D'//oXN"6m—e^S "aPXX 6`ӦMp\8p}?l޼6l/8j3քϊ+e;v w}sƋT<3Xpaz<,]tZ?`/,iq Sx쮟 zCDicِ=v/x =z/ Ae(nJxDpiψd}F |ܹ3nh->DDDDDDDVxP^^4\YjNwgq =uKyr ` zCDڴi0>Ä?W<xW[o@,쉪c=4q :ujر4MKs۶máCM$ |lق+V`I- - |(,B8N90]k׮#<ÑYYY(++CiiEsssR7*_DQn$IvN"X<FGG^ŋcŨeYx嗱~ÞF{@֮][7ވ ||4ܢ&MDpZLS0D{{;0000cneYxWO&F{R]-李<7oݻQUUܹsA-;I4QѤ˴~(EQb ?㰧&eY, w|A---8uԄ=JehGڵkqر;~n-J>DDDDDDD4e2Vgφ㙀K`0ׯ{'==D4Ѣϭ,+4$In(z\(>DDDDDDD42g$(77YYY͝ci| Ş"&jRQ(ʌ 鍁M 3onNYYY'B0@,4 >/z:$]4ӣ( iR0!""""""ie}֯_I;P? sFۋ}aR˾ "Ө,C*2v;?hR1!""""""ii*}:q1NywADXːrIJ |hښ~<^k֬ЎLS]]IE6 ,OyeplR.;I"""""""'sO^?~gϞzh߅fi0MNҴ2Óp8zjTTT`…~tEmm->%""2N*p8PQQ2!//oJv֢.\@oo]{z&"""""""HSH^^^,)**BaaT_R^ ɓ{z&"""""""hi" MIh?(++H{ wTUECCCO!""J>DDDDDDD4#LeO*  UXX9l[rTULlbOQ0!""""""e}h8I%""""""fAQis'cO-8 nCQi3ӱh1!""""""KE8Ό CDD45ь'2dYfOhj1!"""""";}&{z~>==DDD """""""#gCDD40!"""""";ZG4hg z/>DDDDDDDD0IAhhz;QGQ=DDDQѥ,˂iuoYm#""Lon"""""""Q4i ØQ,ː$ ,s(C1!"""""""J(˲: Àa,k.y(BXCDDDDDDDDDDD0LӌML AbOߜ!""yMQ!bkDzaN ѝQDMK$D% =^4!"""bCDDDDDDD4S8Q#CDDDD# DDDDDDDDDDDDQcCDDDDDDDDDDDe [UXx<IENDB`njs-0.8.9/README.md000066400000000000000000000414751474132077100136220ustar00rootroot00000000000000[![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) [![Community Support](https://badgen.net/badge/support/commercial/green?icon=awesome)](/SUPPORT.md) ![NGINX JavaScript Banner](NGINX-js-1660x332.png "NGINX JavaScript Banner") # NGINX JavaScript NGINX JavaScript, also known as [NJS](https://nginx.org/en/docs/njs/), is a dynamic module for [NGINX](https://nginx.org/en/download.html) that enables the extension of built-in functionality using familiar JavaScript syntax. The NJS language is a subset of JavaScript, compliant with [ES5](https://262.ecma-international.org/5.1/) (ECMAScript 5.1 [Strict Variant](https://262.ecma-international.org/5.1/#sec-4.2.2)) with some [ES6](https://262.ecma-international.org/6.0/) (ECMAScript 6) and newer extensions. See [compatibility](https://nginx.org/en/docs/njs/compatibility.html) for more details. # Table of Contents - [How it works](#how-it-works) - [Downloading and installing](#downloading-and-installing) - [Provisioning the NGINX package repository](#provisioning-the-nginx-package-repository) - [Installing the NGINX JavaScript modules](#installing-the-nginx-javascipt-modules) - [Installed files and locations](#installed-files-and-locations) - [Getting started with NGINX JavaScript](#getting-started-with-nginx-javascript) - [Verify NGINX is running](#verify-nginx-is-running) - [Enabling the NGINX JavaScript modules](#enabling-the-nginx-javascipt-modules) - [Basics of writing .js script files](#basics-of-writing-js-script-files) - [Reference of custom objects, methods, and properties](#reference-of-custom-objects-methods-and-properties) - [Example: Hello World](#hello-world) - [The NJS command line interface (CLI)](#the-njs-command-line-interface-cli) - [Building from source](#building-from-source) - [Installing dependencies](#installing-dependencies) - [Cloning the NGINX JavaScript GitHub repository](#cloning-the-nginx-javascript-github-repository) - [Building standalone command line interface utility (optional)](#building-standalone-command-line-interface-utility-optional) - [Cloning the NGINX GitHub repository](#cloning-the-nginx-github-repository) - [Building NGINX JavaScript as a module of NGINX](#building-nginx-javascript-as-a-module-of-nginx) - [NGINX JavaScript technical specifications](#nginx-javascript-technical-specifications) - [Supported distributions](#supported-distributions) - [Supported deployment environments](#supported-deployment-environments) - [Supported NGINX versions](#supported-nginx-versions) - [Sizing recommendations](#sizing-recommendations) - [Asking questions, reporting issues, and contributing](#asking-questions-reporting-issues-and-contributing) - [Change log](#change-log) - [License](#license) # How it works [NGINX JavaScript](https://nginx.org/en/docs/njs/) is provided as two [dynamic modules](https://nginx.org/en/linux_packages.html#dynmodules) for NGINX ([ngx_http_js_module](https://nginx.org/en/docs/http/ngx_http_js_module.html) and [ngx_stream_js_module](https://nginx.org/en/docs/stream/ngx_stream_js_module.html)) and can be added to any supported [NGINX Open Source](https://nginx.org/en/download.html) or [NGINX Plus](https://www.f5.com/products/nginx/nginx-plus) installation without recompilation. The NJS module allows NGINX administrators to: - Add complex access control and security checks before requests reach upstream servers - Manipulate response headers - Write flexible, asynchronous content handlers, filters, and more! See [examples](https://github.com/nginx/njs-examples/) and our various projects developed with NJS: #### https://github.com/nginxinc/nginx-openid-connect Extends NGINX Plus functionality to communicate directly with OIDC-compatible Identity Providers, authenticating users and authorizing content delivered by NGINX Plus. #### https://github.com/nginxinc/nginx-saml Reference implementation of NGINX Plus as a service provider for SAML authentication. #### https://github.com/nginxinc/njs-prometheus-module Exposes Prometheus metrics endpoint directly from NGINX Plus. > [!TIP] > NJS can also be used with the [NGINX Unit](https://unit.nginx.org/) application server. Learn more about NGINX Unit's [Control API](https://unit.nginx.org/controlapi/) and how to [define function calls with NJS](https://unit.nginx.org/scripting/). # Downloading and installing Follow these steps to download and install precompiled NGINX and NGINX JavaScript Linux binaries. You may also choose to [build the module locally from source code](#building-from-source). ## Provisioning the NGINX package repository Follow [this guide](https://nginx.org/en/linux_packages.html) to add the official NGINX package repository to your system and install NGINX Open Source. If you already have NGINX Open Source or NGINX Plus installed, skip the NGINX installation portion in the last step. ## Installing the NGINX JavaScript modules Once the repository has been provisioned, you may install NJS by issuing the following command: ### Ubuntu or Debian based systems ```bash sudo apt install nginx-module-njs ``` ### RHEL, RedHat and its derivatives ```bash sudo yum install nginx-module-njs ``` ### Alpine or similar systems ```bash sudo apk add nginx-module-njs@nginx ``` ### SuSE, SLES or similar systems ```bash sudo zypper install nginx-module-njs ``` > [!TIP] > The package repository includes an alternate module that enables debug symbols. Although not recommended for production environments, this module may be helpful when developing NJS-based configurations. To download and install the debug version of the module, replace the module name in the previous command with `nginx-module-njs-dbg`. ## Installed files and locations The package installation scripts install two modules, supporting NGINX [`http`](https://nginx.org/en/docs/http/ngx_http_core_module.html#http) and [`stream`](https://nginx.org/en/docs/stream/ngx_stream_core_module.html#stream) contexts. - [ngx_http_js_module](https://nginx.org/en/docs/http/ngx_http_js_module.html) This NJS module enables manipulation of data transmitted over HTTP. - [ngx_stream_js_module](https://nginx.org/en/docs/stream/ngx_stream_js_module.html) This NJS module enables manipulation of data transmitted via stream protocols such as TCP and UDP. By default, both modules are installed into the `/etc/nginx/modules` directory. # Getting started with NGINX JavaScript Usage of NJS involves enabling the module, adding JavaScript files with defined functions, and invoking exported functions in NGINX configuration files. ## Verify NGINX is running NGINX JavaScript is a module for NGINX Open Source or NGINX Plus. If you haven't done so already, follow these steps to install [NGINX Open Source](https://docs.nginx.com/nginx/admin-guide/installing-nginx/installing-nginx-open-source/) or [NGINX Plus](https://docs.nginx.com/nginx/admin-guide/installing-nginx/installing-nginx-plus/). Once installed, ensure the NGINX instance is running and able to respond to HTTP requests. ### Starting NGINX Issue the following command to start NGINX: ```bash sudo nginx ``` ### Verify NGINX is responding to HTTP requests ```bash curl -I 127.0.0.1 ``` You should see the following response: ```bash HTTP/1.1 200 OK Server: nginx/1.25.5 ``` ## Enabling the NGINX JavaScript modules Once installed, either (or both) NJS module(s) must be included in the NGINX configuration file. On most systems, the NGINX configuration file is located at `/etc/nginx/nginx.conf` by default. ### Edit the NGINX configuration file ```bash sudo vi /etc/nginx/nginx.conf ``` ### Enable dynamic loading of NJS modules Use the [load_module](https://nginx.org/en/docs/ngx_core_module.html#load_module) directive in the top-level (“main”) context to enable either (or both) module(s). ```nginx load_module modules/ngx_http_js_module.so; load_module modules/ngx_stream_js_module.so; ``` ## Basics of writing .js script files NJS script files are typically named with a .js extension and placed in the `/etc/nginx/njs/` directory. They are usually comprised of functions that are then exported, making them available in NGINX configuration files. ## Reference of custom objects, methods, and properties NJS provides a collection of objects with associated methods and properties that are not part of ECMAScript definitions. See the [complete reference](https://nginx.org/en/docs/njs/reference.html) to these objects and how they can be used to further extend and customize NGINX. ## Example: Hello World Here's a basic "Hello World" example. ### example.js The `hello` function in this file returns an HTTP 200 OK status response code along with the string "Hello World!", followed by a line feed. The function is then exported for use in an NGINX configuration file. Add this file to the `/etc/nginx/njs` directory: ```JavaScript function hello(r) { r.return(200, "Hello world!\n"); } export default {hello} ``` ### nginx.conf We modify our NGINX configuration (`/etc/nginx/nginx.conf`) to import the JavaScript file and execute the function under specific circumstances. ```nginx # Load the ngx_http_js_module module load_module modules/ngx_http_js_module.so; events {} http { # Set the path to our njs JavaScript files js_path "/etc/nginx/njs/"; # Import our JavaScript file into the variable "main" js_import main from http/hello.js; server { listen 80; location / { # Execute the "hello" function defined in our JavaScript file on all HTTP requests # and respond with the contents of our function. js_content main.hello; } } } ``` For a full list of njs directives, see the [ngx_http_js_module](https://nginx.org/en/docs/http/ngx_http_js_module.html) and [ngx_stream_js_module](https://nginx.org/en/docs/stream/ngx_stream_js_module.html) module documentation pages. > [!TIP] > A more detailed version of this and other examples can be found in the official [njs-examples repository](https://github.com/nginx/njs-examples/tree/master). ## The NJS command line interface (CLI) NGINX JavaScript installs with a command line interface utility. The interface can be opened as an interactive shell or used to process JavaScript syntax from predefined files or standard input. Since the utility runs independently, NGINX-specific objects such as [HTTP](https://nginx.org/en/docs/njs/reference.html#http) and [Stream](https://nginx.org/en/docs/njs/reference.html#http) are not available within its runtime. ### Example usage of the interactive CLI ```JavaScript $ njs >> globalThis global { njs: njs { version: '0.8.4' }, global: [Circular], process: process { argv: ['/usr/bin/njs'], env: { PATH: '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', HOSTNAME: 'f777c149d4f8', TERM: 'xterm', NGINX_VERSION: '1.25.5', NJS_VERSION: '0.8.4', PKG_RELEASE: '1~buster', HOME: '/root' } }, console: { log: [Function: native], dump: [Function: native], time: [Function: native], timeEnd: [Function: native] }, print: [Function: native] } >> ``` ### Example usage of the non-interactive CLI ```bash $ echo "2**3" | njs -q 8 ``` # Building from source The following steps can be used to build NGINX JavaScript as a dynamic module to be integrated into NGINX or a standalone binary for use as a command line interface utility. > [!IMPORTANT] > To build the module for use with NGINX, you will also need to clone, configure and build NGINX by following the steps outlined in this document. ## Installing dependencies Most Linux distributions will require several dependencies to be installed in order to build NGINX and NGINX JavaScript. The following instructions are specific to the `apt` package manager, widely available on most Ubuntu/Debian distributions and their derivatives. ### Installing compiler and make utility ```bash sudo apt install gcc make ``` ### Installing dependency libraries ```bash sudo apt install libpcre3-dev zlib1g-dev libssl-dev libxml2-dev libxslt-dev ``` For building with [QuickJS](https://nginx.org/en/docs/njs/engine.html), you will also need to build the QuickJS library: ```bash git clone https://github.com/bellard/quickjs cd quickjs CFLAGS='-fPIC' make libquickjs.a ``` > [!WARNING] > This is the minimal set of dependency libraries needed to build NGINX and NJS. Other dependencies may be required if you choose to build NGINX with additional modules. Monitor the output of the `configure` command discussed in the following sections for information on which modules may be missing. ## Cloning the NGINX JavaScript GitHub repository Using your preferred method, clone the NGINX JavaScript repository into your development directory. See [Cloning a GitHub Repository](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository) for additional help. ```bash https://github.com/nginx/njs.git ``` ## Building the standalone command line interface utility (Optional) The following steps are optional and only needed if you choose to build NJS as a standalone utility. ### Install dependencies To use the NJS interactive shell, you will need to install the libedit-dev library ```bash sudo apt install libedit-dev ``` ### Configure and build Run the following commands from the root directory of your cloned repository: ```bash ./configure ``` Build NGINX JavaScript: ```bash make ``` The utility should now be available at `/build/njs`. See [The NJS Command Line Interface (CLI)](#the-njs-command-line-interface-cli) for information on usage. ## Cloning the NGINX GitHub repository Clone the NGINX source code repository in a directory outside of the previously cloned NJS source repository. ```bash https://github.com/nginx/nginx.git ``` ## Building NGINX JavaScript as a module of NGINX To build NGINX JavaScript as a dynamic module, execute the following commands from the NGINX source code repository's root directory: ```bash auto/configure --add-dynamic-module=/nginx ``` To build with [QuickJS](https://nginx.org/en/docs/njs/engine.html) support, provide include and library path using `--with-cc-opt=` and `--with-ld-opt=` options: ```bash auto/configure --add-dynamic-module=/nginx \ --with-cc-opt="-I" --with-ld-opt="-L" ``` > [!WARNING] > By default, this method will only build the `ngx_http_js_module` module. To use NJS with the NGINX Stream module, you'll need to enable it during the `configure` step so it builds with the NGINX binary. Doing so will automatically compile the `ngx_stream_js_module` module when NJS is added to the build. One way of accomplishing this is to alter the `configure` step to: > ```bash > auto/configure --with-stream --add-dynamic-module=/nginx > ``` Compile the module ```bash make ``` > [!TIP] > To build NGINX with NGINX JavaScript embedded into a single binary, alter the `configure` step to the following: > ```bash > auto/configure --add-module=/nginx > ``` ### Install module If built as a dynamic module(s), the NGINX JavaScript module(s) will be available in the `/objs/` directory. The module(s) can then be copied to an existing NGINX installation and enabled. See [Enabling the NGINX JavaScript Modules](#enabling-the-nginx-javascipt-modules) for details. ### Install compiled NGINX and NGINX JavaScript binaries Alternatively, you may choose to install the built NGINX and NGINX JavaScript binaries by issuing the following command: > [!IMPORTANT] > If built into the NGINX binary as a standard (not dynamic) module, this will be the easiest method of installation ```bash make install ``` By default, the NGINX binary will be installed into `/usr/local/nginx/sbin/nginx`. The NGINX JavaScript module(s) will be copied to `/usr/local/nginx/modules/`. # NGINX JavaScript technical specifications Technical specifications for NJS are identical to those of NGINX. ## Supported distributions See [Tested Operating Systems and Platforms](https://nginx.org/en/#tested_os_and_platforms) for a complete list of supported distributions. ## Supported deployment environments - Container - Public cloud (AWS, Google Cloud Platform, Microsoft Azure) - Virtual machine ## Supported NGINX versions NGINX JavaScript is supported by all NGINX Open Source versions starting with nginx-1.14 and all NGINX Plus versions starting with NGINX Plus R15. # Asking questions, reporting issues, and contributing We encourage you to engage with us. Please see the [Contributing](CONTRIBUTING.md) guide for information on how to ask questions, report issues and contribute code. # Change log See our [release page](https://nginx.org/en/docs/njs/changes.html) to keep track of updates. # License [2-clause BSD-like license](LICENSE) --- Additional documentation available at: https://nginx.org/en/docs/njs/ ©2024 F5, Inc. All rights reserved. https://www.f5.com/products/nginx njs-0.8.9/SECURITY.md000066400000000000000000000030561474132077100141250ustar00rootroot00000000000000# Security Policy ## Latest Versions We advise users to run or update to the most recent release of njs. Older versions may not have all enhancements and/or bug fixes applied to them. ## Special Considerations njs does not evaluate dynamic code, especially code received from the network, in any way. The only way to evaluate such code using njs is to configure the `js_import` directive in nginx. JavaScript code is loaded once during nginx start. In the nginx/njs threat model, JavaScript code is considered a trusted source in the same way as `nginx.conf` and site certificates. This means in practice: - Memory disclosure and other security issues triggered by JavaScript code modification are not considered security issues, but as ordinary bugs. - Measures should be taken to protect JavaScript code used by njs. - If no `js_import` directives are present in `nginx.conf`, nginx is safe from JavaScript-related vulnerabilities. ## Reporting a Vulnerability The F5 Security Incident Response Team (F5 SIRT) has an email alias that makes it easy to report potential security vulnerabilities. - If you’re an F5 customer with an active support contract, please contact [F5 Technical Support](https://www.f5.com/services/support). - If you aren’t an F5 customer, please report any potential or current instances of security vulnerabilities with any F5 product to the F5 Security Incident Response Team at F5SIRT@f5.com For more information visit [https://www.f5.com/services/support/report-a-vulnerability](https://www.f5.com/services/support/report-a-vulnerability) njs-0.8.9/SUPPORT.md000066400000000000000000000013741474132077100140330ustar00rootroot00000000000000# Support ## Ask a Question We use GitHub for tracking bugs and feature requests related to njs. Don't know how something in this project works? Curious if this project can achieve your desired functionality? Please open an issue on GitHub with the label `question`. ## NGINX Specific Questions and/or Issues See the [nginx community page](https://nginx.org/en/community.html) for more information on how to get help with NGINX. ## Contributing Please see the [contributing guide](CONTRIBUTING.md) for guidelines on how to best contribute to this project. ## Commercial Support Commercial support for this project may be available. Please get in touch with [NGINX sales](https://www.nginx.com/contact-sales/) or check your contract details for more info! njs-0.8.9/auto/000077500000000000000000000000001474132077100133005ustar00rootroot00000000000000njs-0.8.9/auto/cc000066400000000000000000000120441474132077100136110ustar00rootroot00000000000000 # Copyright (C) Igor Sysoev # Copyright (C) NGINX, Inc. echo checking for C compiler: $CC cat << END >> $NJS_AUTOCONF_ERR ---------------------------------------- checking for C compiler: $CC END # Allow error exit status. set +e if [ -z "$(which $CC)" ]; then echo echo $0: error: $CC not found. echo exit 1; fi if `/bin/sh -c "($CC -v)" 2>&1 | grep "gcc version" >> $NJS_AUTOCONF_ERR 2>&1` then NJS_CC_NAME=gcc echo " + using GNU C compiler" NJS_CC_VERSION=`/bin/sh -c "($CC -v)" 2>&1 | grep "gcc version" 2>&1` echo " + $NJS_CC_VERSION" else if `/bin/sh -c "($CC -v)" 2>&1 | grep "clang version" >> $NJS_AUTOCONF_ERR 2>&1` then NJS_CC_NAME=clang echo " + using Clang C compiler" NJS_CC_VERSION=`/bin/sh -c "($CC -v)" 2>&1 | grep "clang version" 2>&1` echo " + $NJS_CC_VERSION" else if `/bin/sh -c "($CC -v)" 2>&1 \ | grep "Apple LLVM version" >> $NJS_AUTOCONF_ERR 2>&1` then NJS_CC_NAME=clang echo " + using Clang C compiler" NJS_CC_VERSION=`/bin/sh -c "($CC -v)" 2>&1 | grep "Apple LLVM version" 2>&1` echo " + $NJS_CC_VERSION" else if `/bin/sh -c "($CC -V)" 2>&1 | grep "Sun C" >> $NJS_AUTOCONF_ERR 2>&1` then NJS_CC_NAME=SunC echo " + using Sun C compiler" NJS_CC_VERSION=`/bin/sh -c "($CC -V)" 2>&1 | grep "Sun C" 2>&1` echo " + $NJS_CC_VERSION" fi # SunC fi # Apple LLVM clang fi # clang fi # gcc case $NJS_CC_NAME in gcc) njs_define=NJS_GCC . auto/define NJS_CFLAGS="$NJS_CFLAGS -pipe" NJS_CFLAGS="$NJS_CFLAGS -fPIC" # Do not export symbols except explicitly marked with NJS_EXPORT. NJS_CFLAGS="$NJS_CFLAGS -fvisibility=hidden" # c99/gnu99 conflict with Solaris XOPEN. #NJS_CFLAGS="$NJS_CFLAGS -std=gnu99" NJS_CFLAGS="$NJS_CFLAGS -O" #NJS_CFLAGS="$NJS_CFLAGS -O0" NJS_CFLAGS="$NJS_CFLAGS -W -Wall -Wextra" #NJS_CFLAGS="$NJS_CFLAGS -Wunused-result" NJS_CFLAGS="$NJS_CFLAGS -Wno-unused-parameter" #NJS_CFLAGS="$NJS_CFLAGS -Wshorten-64-to-32" NJS_CFLAGS="$NJS_CFLAGS -Wwrite-strings" # -O2 enables -fstrict-aliasing and -fstrict-overflow. #NJS_CFLAGS="$NJS_CFLAGS -O2" #NJS_CFLAGS="$NJS_CFLAGS -Wno-strict-aliasing" #NJS_CFLAGS="$NJS_CFLAGS -fomit-frame-pointer" #NJS_CFLAGS="$NJS_CFLAGS -momit-leaf-frame-pointer" # -Wstrict-overflow is supported by GCC 4.2+. #NJS_CFLAGS="$NJS_CFLAGS -Wstrict-overflow=5" NJS_CFLAGS="$NJS_CFLAGS -Wmissing-prototypes" # Stop on warning. NJS_CFLAGS="$NJS_CFLAGS -Werror" # Debug. NJS_CFLAGS="$NJS_CFLAGS -g" if [ "$NJS_ADDRESS_SANITIZER" = "YES" ]; then NJS_CFLAGS="$NJS_CFLAGS -fsanitize=address" NJS_CFLAGS="$NJS_CFLAGS -fno-omit-frame-pointer" fi ;; clang) njs_define=NJS_CLANG . auto/define NJS_CFLAGS="$NJS_CFLAGS -pipe" NJS_CFLAGS="$NJS_CFLAGS -fPIC" # Do not export symbols except explicitly marked with NJS_EXPORT. NJS_CFLAGS="$NJS_CFLAGS -fvisibility=hidden" NJS_CFLAGS="$NJS_CFLAGS -O" #NJS_CFLAGS="$NJS_CFLAGS -O0" NJS_CFLAGS="$NJS_CFLAGS -W -Wall -Wextra" #NJS_CFLAGS="$NJS_CFLAGS -Wunused-result" NJS_CFLAGS="$NJS_CFLAGS -Wno-unused-parameter" #NJS_CFLAGS="$NJS_CFLAGS -Wshorten-64-to-32" NJS_CFLAGS="$NJS_CFLAGS -Wwrite-strings" #NJS_CFLAGS="$NJS_CFLAGS -O2" #NJS_CFLAGS="$NJS_CFLAGS -fomit-frame-pointer" NJS_CFLAGS="$NJS_CFLAGS -fstrict-aliasing" NJS_CFLAGS="$NJS_CFLAGS -Wstrict-overflow=5" NJS_CFLAGS="$NJS_CFLAGS -Wmissing-prototypes" # Stop on warning. NJS_CFLAGS="$NJS_CFLAGS -Werror" # Debug. if [ "$NJS_SYSTEM_PLATFORM" != "powerpc" ]; then # "-g" flag causes the "unknown pseudo-op: `.cfi_sections'" # error on PowerPC Clang. NJS_CFLAGS="$NJS_CFLAGS -g" fi if [ "$NJS_ADDRESS_SANITIZER" = "YES" ]; then NJS_CFLAGS="$NJS_CFLAGS -fsanitize=address" NJS_CFLAGS="$NJS_CFLAGS -fno-omit-frame-pointer" NJS_CFLAGS="$NJS_CFLAGS -fsanitize-address-use-after-scope" fi ;; SunC) njs_define=NJS_SUNC . auto/define NJS_CFLAGS="$NJS_CFLAGS -fPIC" # Optimization. NJS_CFLAGS="$NJS_CFLAGS -O -fast" # Stop on warning. NJS_CFLAGS="$NJS_CFLAGS -errwarn=%all" # Debug. NJS_CFLAGS="$NJS_CFLAGS -g" if [ "$NJS_ADDRESS_SANITIZER" = "YES" ]; then echo " - Address sanitizer is not supported by $NJS_CC_NAME" fi ;; *) ;; esac if [ "$NJS_DEBUG" = "YES" ]; then njs_define=NJS_DEBUG . auto/define fi if [ "$NJS_DEBUG_MEMORY" = "YES" ]; then njs_define=NJS_DEBUG_MEMORY . auto/define fi if [ "$NJS_DEBUG_OPCODE" = "YES" ]; then njs_define=NJS_DEBUG_OPCODE . auto/define fi if [ "$NJS_DEBUG_GENERATOR" = "YES" ]; then njs_define=NJS_DEBUG_GENERATOR . auto/define fi # Stop on error exit status again. set -e njs-0.8.9/auto/clang000066400000000000000000000126721474132077100143170ustar00rootroot00000000000000 # Copyright (C) Igor Sysoev # Copyright (C) NGINX, Inc. # C language features. njs_feature="GCC unsigned __int128" njs_feature_name=NJS_HAVE_UNSIGNED_INT128 njs_feature_run=no njs_feature_incs= njs_feature_libs= njs_feature_test="int main(void) { unsigned __int128 p = 0; return (int) p; }" . auto/feature njs_feature="GCC __builtin_expect()" njs_feature_name=NJS_HAVE_BUILTIN_EXPECT njs_feature_run=no njs_feature_incs= njs_feature_libs= njs_feature_test="int main(int argc, char *const *argv) { if ((__typeof__(argc == 0)) __builtin_expect((argc == 0), 0)) return 0; return 1; }" . auto/feature njs_feature="GCC __builtin_unreachable()" njs_feature_name=NJS_HAVE_BUILTIN_UNREACHABLE njs_feature_run=no njs_feature_incs= njs_feature_libs= njs_feature_test="int main(void) { __builtin_unreachable(); }" . auto/feature njs_feature="GCC __builtin_prefetch()" njs_feature_name=NJS_HAVE_BUILTIN_PREFETCH njs_feature_run=no njs_feature_incs= njs_feature_libs= njs_feature_test="int main(void) { __builtin_prefetch(0); return 0; }" . auto/feature njs_feature="GCC __builtin_clz()" njs_feature_name=NJS_HAVE_BUILTIN_CLZ njs_feature_run=no njs_feature_incs= njs_feature_libs= njs_feature_test="int main(void) { if (__builtin_clz(1) != 31) { return 1; } return 0; }" . auto/feature njs_feature="GCC __builtin_clzll()" njs_feature_name=NJS_HAVE_BUILTIN_CLZLL njs_feature_run=no njs_feature_incs= njs_feature_libs= njs_feature_test="int main(void) { if (__builtin_clzll(1ULL) != 63) { return 1; } return 0; }" . auto/feature njs_feature="GCC __attribute__ visibility" njs_feature_name=NJS_HAVE_GCC_ATTRIBUTE_VISIBILITY njs_feature_run=no njs_feature_path= njs_feature_libs= njs_feature_test="int n __attribute__ ((visibility(\"default\"))); int main(void) { return 0; }" . auto/feature njs_feature="GCC __attribute__ malloc" njs_feature_name=NJS_HAVE_GCC_ATTRIBUTE_MALLOC njs_feature_run=no njs_feature_path= njs_feature_libs= njs_feature_test="#include void *f(void) __attribute__ ((__malloc__)); void *f(void) { return malloc(1); } int main(void) { if (f() != NULL) { return 1; } return 0; }" . auto/feature njs_feature="GCC __attribute__ aligned" njs_feature_name=NJS_HAVE_GCC_ATTRIBUTE_ALIGNED njs_feature_run=no njs_feature_path= njs_feature_libs= njs_feature_test="int n __attribute__ ((aligned(64))); int main(void) { return 0; }" . auto/feature njs_feature="GCC __attribute__ packed" njs_feature_name=NJS_HAVE_GCC_ATTRIBUTE_PACKED njs_feature_run=no njs_feature_path= njs_feature_libs= njs_feature_test="struct __attribute__((packed)) s { char v; }; int main(void) { return 0; }" . auto/feature njs_feature="GCC __attribute__ fallthrough" njs_feature_name=NJS_HAVE_GCC_ATTRIBUTE_FALLTHROUGH njs_feature_run=no njs_feature_path= njs_feature_libs= njs_feature_test="int main(int argc, char *argv[]) { switch (argc) { case 0: argc++; __attribute__((fallthrough)); default: argc++; } return argc; }" . auto/feature njs_feature="GCC __attribute__ no_sanitize" njs_feature_name=NJS_HAVE_GCC_ATTRIBUTE_NO_SANITIZE njs_feature_run=no njs_feature_path= njs_feature_libs= njs_feature_test="__attribute__((no_sanitize(\"undefined\"))) int main(void) { return 0; }" . auto/feature njs_feature="Address sanitizer" njs_feature_name=NJS_HAVE_ADDRESS_SANITIZER njs_feature_run=no njs_feature_path= njs_feature_libs= njs_feature_test="int main(void) { return #ifdef __SANITIZE_ADDRESS__ 0; #else #if defined(__has_feature) #if __has_feature(address_sanitizer) 0; #endif #endif #endif }" . auto/feature njs_feature="Memory sanitizer" njs_feature_name=NJS_HAVE_MEMORY_SANITIZER njs_feature_run=yes njs_feature_incs= njs_feature_libs= njs_feature_test="#include int main(int argc, char *argv[]) { __msan_unpoison(argv, sizeof(char *)); return 0; }" . auto/feature njs_feature="_mm_setcsr()" njs_feature_name=NJS_HAVE_DENORMALS_CONTROL njs_feature_run=no njs_feature_incs= njs_feature_libs= njs_feature_test="#include int main(void) { _mm_setcsr(_mm_getcsr()); return 0; }" . auto/feature njs-0.8.9/auto/computed_goto000066400000000000000000000010201474132077100160640ustar00rootroot00000000000000 # Copyright (C) Vadim Zhestikov # Copyright (C) NGINX, Inc. NJS_HAVE_COMPUTED_GOTO=NO if [ $NJS_TRY_GOTO = YES ]; then njs_feature="Computed goto" njs_feature_name=NJS_HAVE_COMPUTED_GOTO njs_feature_run=no njs_feature_incs= njs_feature_libs= njs_feature_test="int main(void) { void *ptr; ptr = &&label; goto *ptr; label: return 0; }" . auto/feature fi njs-0.8.9/auto/define000066400000000000000000000002241474132077100144530ustar00rootroot00000000000000 # Copyright (C) Igor Sysoev # Copyright (C) NGINX, Inc. cat << END >> $NJS_AUTO_CONFIG_H #ifndef $njs_define #define $njs_define 1 #endif END njs-0.8.9/auto/deps000066400000000000000000000011221474132077100141520ustar00rootroot00000000000000 # Copyright (C) Igor Sysoev # Copyright (C) NGINX, Inc. case "$NJS_CC_NAME" in SunC): njs_gen_dep_flags() { echo "-xMMD -xMF $NJS_BUILD_DIR/$1.tmp" } njs_gen_dep_post() { printf "@sed -e 's#^.*:#$NJS_BUILD_DIR/$2:#' " printf "$NJS_BUILD_DIR/$1.tmp > $NJS_BUILD_DIR/$1" echo " && rm -f $NJS_BUILD_DIR/$1.tmp" } ;; *) njs_gen_dep_flags() { echo "-MMD -MF $NJS_BUILD_DIR/$1 -MT $NJS_BUILD_DIR/$2" } njs_gen_dep_post() { echo "" } ;; esac njs-0.8.9/auto/endianness000066400000000000000000000014321474132077100153520ustar00rootroot00000000000000 # Copyright (C) Dmitry Volyntsev # Copyright (C) NGINX, Inc. njs_found=no njs_feature="system byte ordering" njs_feature_name=NJS_BYTE_ORDER njs_feature_run=value njs_feature_incs= njs_feature_libs=-lm njs_feature_test="#include #include int main(void) { uint16_t x = 1; /* 0x0001 */ printf(\"%s\n\", (*((uint8_t *) &x) == 0) ? \"big\" : \"little\"); return 0; }" . auto/feature if [ $njs_found = no ]; then echo echo "$0: error: cannot detect system byte ordering" echo exit 1; fi if [ $njs_feature_value = big ]; then njs_define=NJS_HAVE_BIG_ENDIAN . auto/define else njs_define=NJS_HAVE_LITTLE_ENDIAN . auto/define fi njs-0.8.9/auto/expect000066400000000000000000000021421474132077100145120ustar00rootroot00000000000000 # Copyright (C) Dmitry Volyntsev # Copyright (C) NGINX, Inc. njs_found=no printf "checking for expect ..." if /bin/sh -c "(expect -v)" >> $NJS_AUTOCONF_ERR 2>&1; then njs_found=yes fi if [ $njs_found = yes ]; then echo " found" echo " + Expect version: `expect -v`" else echo " not found" fi if [ $njs_found = yes -a $NJS_HAVE_READLINE = YES ]; then cat << END >> $NJS_MAKEFILE shell_test_njs: njs test/shell_test.exp PATH="$NJS_BUILD_DIR:\$(PATH)" LANG=C.UTF-8 TERM=screen \ expect -f test/shell_test.exp PATH="$NJS_BUILD_DIR:\$(PATH)" LANG=C.UTF-8 TERM=screen \ expect -f test/shell_test_njs.exp END if [ $NJS_HAVE_QUICKJS = YES ]; then cat << END >> $NJS_MAKEFILE shell_test: shell_test_njs shell_test_quickjs shell_test_quickjs: njs test/shell_test.exp PATH="$NJS_BUILD_DIR:\$(PATH)" LANG=C.UTF-8 TERM=screen NJS_ENGINE=QuickJS \ expect -f test/shell_test.exp END else cat << END >> $NJS_MAKEFILE shell_test: shell_test_njs END fi else echo " - expect tests are disabled" cat << END >> $NJS_MAKEFILE shell_test: @echo "Skipping expect tests" END fi njs-0.8.9/auto/explicit_bzero000066400000000000000000000016401474132077100162460ustar00rootroot00000000000000 # Copyright (C) Igor Sysoev # Copyright (C) NGINX, Inc. # Linux (glibc and musl from 1.1.20), OpenBSD and FreeBSD. njs_feature="explicit_bzero()" njs_feature_name=NJS_HAVE_EXPLICIT_BZERO njs_feature_run=yes njs_feature_incs= njs_feature_libs= njs_feature_test="#include #include int main(void) { int r; explicit_bzero(&r, sizeof(r)); return 0; }" . auto/feature if [ $njs_found = no ]; then # NetBSD has explicit_memset instead. njs_feature="explicit_memset()" njs_feature_name=NJS_HAVE_EXPLICIT_MEMSET njs_feature_test="#include int main(void) { int r; explicit_memset(&r, 0, sizeof(r)); return 0; }" . auto/feature fi njs-0.8.9/auto/feature000066400000000000000000000046721474132077100146670ustar00rootroot00000000000000 # Copyright (C) Igor Sysoev # Copyright (C) NGINX, Inc. printf "checking for $njs_feature ..." cat << END >> $NJS_AUTOCONF_ERR ---------------------------------------- checking for $njs_feature END njs_found=no njs_feature_value= njs_feature_inc_path= if test -n "$njs_feature_incs"; then case "$njs_feature_incs" in -*) njs_feature_inc_path="$njs_feature_incs" ;; *) for njs_temp in $njs_feature_incs; do njs_feature_inc_path="$njs_feature_inc_path -I $njs_temp" done ;; esac fi cat << END > $NJS_AUTOTEST.c $njs_feature_test END njs_test="$CC $CFLAGS $NJS_CFLAGS $NJS_CC_OPT $NJS_TEST_CFLAGS \ $njs_feature_inc_path -o $NJS_AUTOTEST $NJS_AUTOTEST.c \ $NJS_LD_OPT $NJS_TEST_LIBS $njs_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 "($njs_test || true)" >> $NJS_AUTOCONF_ERR 2>&1 if [ -x $NJS_AUTOTEST ]; then case "$njs_feature_run" in value) if /bin/sh -c "($NJS_AUTOTEST)" >> $NJS_AUTOCONF_ERR 2>&1; then echo >> $NJS_AUTOCONF_ERR njs_found=yes njs_feature_value=`$NJS_AUTOTEST` echo " $njs_feature_value" if [ -n "$njs_feature_name" ]; then cat << END >> $NJS_AUTO_CONFIG_H #ifndef $njs_feature_name #define $njs_feature_name $njs_feature_value #endif END fi else echo " not found" fi ;; yes) if /bin/sh -c "($NJS_AUTOTEST)" >> $NJS_AUTOCONF_ERR 2>&1; then echo " found" njs_found=yes cat << END >> $NJS_AUTO_CONFIG_H #ifndef $njs_feature_name #define $njs_feature_name 1 #endif END else echo " found but is not working" fi ;; *) echo " found" njs_found=yes cat << END >> $NJS_AUTO_CONFIG_H #ifndef $njs_feature_name #define $njs_feature_name 1 #endif END ;; esac else echo " not found" echo "----------" >> $NJS_AUTOCONF_ERR cat $NJS_AUTOTEST.c >> $NJS_AUTOCONF_ERR echo "----------" >> $NJS_AUTOCONF_ERR echo $njs_test >> $NJS_AUTOCONF_ERR echo "----------" >> $NJS_AUTOCONF_ERR fi rm -rf $NJS_AUTOTEST* njs-0.8.9/auto/getrandom000066400000000000000000000053571474132077100152150ustar00rootroot00000000000000 # Copyright (C) Igor Sysoev # Copyright (C) NGINX, Inc. # Linux 3.17 with glibc 2.25, FreeBSD 12, Solaris 11.3. njs_feature="getrandom()" njs_feature_name=NJS_HAVE_GETRANDOM njs_feature_run=yes njs_feature_incs= njs_feature_libs= njs_feature_test="#include #include int main(void) { char buf[4]; if (getrandom(buf, 4, 0) < 0) { return 1; } return 0; }" . auto/feature if [ $njs_found = no ]; then # Linux 3.17 SYS_getrandom. njs_feature="SYS_getrandom in Linux" njs_feature_name=NJS_HAVE_LINUX_SYS_GETRANDOM njs_feature_test="#include #include #include int main(void) { char buf[4]; if (syscall(SYS_getrandom, buf, 4, 0) < 0) { return 1; } return 0; }" . auto/feature fi if [ $njs_found = no ]; then # macOS 10.10. njs_feature="CCRandomGenerateBytes() in CommonCrypto/CommonRandom.h" njs_feature_name=NJS_HAVE_CCRANDOMGENERATEBYTES njs_feature_test="#include #include int main(void) { char buf[4]; if (CCRandomGenerateBytes(buf, 4) != kCCSuccess) { return 1; } return 0; }" . auto/feature fi if [ $njs_found = no ]; then # OpenBSD 5.6 lacks . njs_feature="getentropy()" njs_feature_name=NJS_HAVE_GETENTROPY njs_feature_test="#include int main(void) { char buf[4]; if (getentropy(buf, 4) == -1) { return 1; } return 0; }" . auto/feature fi if [ $njs_found = no ]; then # Solaris based systems. njs_feature="getentropy() in sys/random.h" njs_feature_name=NJS_HAVE_GETENTROPY_SYS_RANDOM njs_feature_test="#include #include int main(void) { char buf[4]; if (getentropy(buf, 4) == -1) { return 1; } return 0; }" . auto/feature fi njs-0.8.9/auto/help000066400000000000000000000050131474132077100141520ustar00rootroot00000000000000 # Copyright (C) Igor Sysoev # Copyright (C) NGINX, Inc. cat << END ./configure options: --addr2line=YES enables native function symbolization, \ default: "$NJS_ADDR2LINE" --address-sanitizer=YES enables build with address sanitizer, \ default: "$NJS_ADDRESS_SANITIZER" --ar=FILE sets static linking program, default: "$AR" --build-dir=DIR sets build directory, default: "$NJS_BUILD_DIR" --cc=FILE sets C compiler filename, default: "$CC" --cc-opt=OPTIONS sets additional C compiler options, \ default: "$NJS_CC_OPT" --debug=YES enables additional runtime checks, \ default: "$NJS_DEBUG" --debug-memory=YES enables memory alloc debug, \ default: "$NJS_DEBUG_MEMORY" --debug-opcode=YES enables runtime function tracing, \ default: "$NJS_DEBUG_OPCODE" --debug-generator=YES enables generator debug, \ default: "$NJS_DEBUG_GENERATOR" --ld-opt=OPTIONS sets additional linker options, \ default: "$NJS_LD_OPT" --no-goto disables computed goto discovery. When this option is enabled 'switch' statement will be always used in instead of computed goto. --no-libxml2 disables libxml2 discovery. When this option is enabled libxml2 dependant code is not built as a part of libnjs.a. --no-openssl disables OpenSSL discovery. When this option is enabled OpenSSL dependant code is not built as a part of libnjs.a. --no-pcre disables PCRE/PCRE2 discovery for RegExp backend. This flag allows to build PCRE/PCRE2 outside of libnjs.a. When this option is enabled functions described in njs_regex.h are not built. Instead this functions are expected to be provided while linking. --no-pcre2 disables PCRE2 discovery for RegExp backend. When this option is enabled only PCRE library is discovered. --no-quickjs disables QuickJS engine discovery. --no-zlib disables zlib discovery. When this option is enabled zlib dependant code is not built as a part of libnjs.a. --with-quickjs requires QuickJS engine. END njs-0.8.9/auto/init000066400000000000000000000006701474132077100141710ustar00rootroot00000000000000 # Copyright (C) Dmitry Volyntsev # Copyright (C) NGINX, Inc. # Initialize variables with null values if they are not defined. CFLAGS=${CFLAGS=} NJS_TEST_CFLAGS=${NJS_TEST_CFLAGS=} NJS_TEST_LIBS=${NJS_TEST_LIBS=} # Initialize variables with default if they are not defined. CC=${CC:-cc} AR=${AR:-ar} NJS_CFLAGS=${NJS_CFLAGS=} NJS_BUILD_DIR=${NJS_BUILD_DIR:-build} NJS_LIB_MODULES= QJS_LIB_MODULES= NJS_LIBRT= njs_regex_cont=' \\\ ' njs-0.8.9/auto/libbfd000066400000000000000000000013621474132077100144470ustar00rootroot00000000000000# Copyright (C) Dmitry Volyntsev # Copyright (C) NGINX, Inc. NJS_HAVE_LIBBFD=NO if [ $NJS_ADDR2LINE = YES ]; then njs_found=no njs_feature="BFD library" njs_feature_name=NJS_HAVE_LIBBFD njs_feature_run=yes njs_feature_incs= njs_feature_libs="-lbfd" njs_feature_test="#include int main() { bfd_init(); return 0; }" . auto/feature if [ $njs_found = no ]; then njs_feature="OpenSSL library -lcrypto" njs_feature_libs="-lcrypto" . auto/feature fi if [ $njs_found = yes ]; then NJS_HAVE_LIBBFD=YES NJS_LIB_AUX_LIBS="$NJS_LIB_AUX_LIBS $njs_feature_libs" fi fi njs-0.8.9/auto/libxml2000066400000000000000000000050021474132077100145710ustar00rootroot00000000000000 # Copyright (C) Dmitry Volyntsev # Copyright (C) NGINX, Inc. NJS_LIBXML2_LIB= NJS_HAVE_LIBXML2=NO if [ $NJS_LIBXML2 = YES ]; then njs_found=no njs_feature_name=NJS_HAVE_LIBXML2 njs_feature_run=no njs_feature_test="#include #include int main() { xmlDocPtr tree; tree = xmlReadMemory(NULL, 0, NULL, NULL, 0); xmlFreeDoc(tree); xmlCleanupParser(); return 0; }" if /bin/sh -c "(pkg-config libxml-2.0 --exists)" >> $NJS_AUTOCONF_ERR 2>&1; then # pkg-config njs_feature="libxml2 via pkg-config" njs_feature_incs=`pkg-config libxml-2.0 --cflags | sed -n -e 's#.*-I *\([^ ][^ ]*\).*#\1#p'` njs_feature_libs=`pkg-config libxml-2.0 --libs` . auto/feature fi if [ $njs_found = no ]; then njs_feature="libxml2" njs_feature_incs="/usr/include/libxml2" njs_feature_libs="-lxml2" . auto/feature fi if [ $njs_found = no ]; then # FreeBSD port njs_feature="libxml2 in /usr/local/" njs_feature_incs="/usr/local/include/libxml2 /usr/local/include" njs_feature_libs="-L/usr/local/lib -lxml2" . auto/feature fi if [ $njs_found = no ]; then # NetBSD port njs_feature="libxml2 in /usr/pkg/" njs_feature_incs="/usr/pkg/include/libxml2 /usr/pkg/include" njs_feature_libs="-L/usr/pkg/lib -lxml2" . auto/feature fi if [ $njs_found = no ]; then # MacPorts njs_feature="libxml2 in /opt/local/" njs_feature_incs="/opt/local/include/libxml2 /opt/local/include" njs_feature_libs="-L/opt/local/lib -lxml2 -lxslt" . auto/feature fi if [ $njs_found = yes ]; then njs_feature="libxml2 version" njs_feature_name=NJS_LIBXML2_VERSION njs_feature_run=value njs_feature_test="#include #include int main() { printf(\"\\\"%s\\\"\", LIBXML_DOTTED_VERSION); return 0; }" . auto/feature NJS_HAVE_LIBXML2=YES NJS_LIBXML2_LIB="$njs_feature_libs" NJS_LIB_INCS="$NJS_LIB_INCS $njs_feature_incs" NJS_LIB_AUX_LIBS="$NJS_LIB_AUX_LIBS $njs_feature_libs" fi fi njs-0.8.9/auto/link000066400000000000000000000014141474132077100141600ustar00rootroot00000000000000 # Copyright (C) Dmitry Volyntsev # Copyright (C) NGINX, Inc. NJS_HAVE_DL_ITERATE_PHDR=NO if [ $NJS_ADDR2LINE = YES ]; then njs_feature="dl_iterate_phdr()" njs_feature_name=NJS_HAVE_DL_ITERATE_PHDR njs_feature_run=yes njs_feature_incs= njs_feature_libs= njs_feature_test="#define _GNU_SOURCE #include static int cb(struct dl_phdr_info *info, size_t size, void *data) { return 0; } int main() { dl_iterate_phdr(cb, 0); return 0; }" . auto/feature if [ $njs_found = yes ]; then NJS_HAVE_DL_ITERATE_PHDR=YES fi fi njs-0.8.9/auto/make000066400000000000000000000235221474132077100141440ustar00rootroot00000000000000 # Copyright (C) Igor Sysoev # Copyright (C) NGINX, Inc. . auto/deps echo "creating $NJS_MAKEFILE" mkdir -p $NJS_BUILD_DIR/src mkdir -p $NJS_BUILD_DIR/external mkdir -p $NJS_BUILD_DIR/$NJS_BUILD_DIR mkdir -p $NJS_BUILD_DIR/test njs_modules_c=$NJS_BUILD_DIR/njs_modules.c NJS_LIB_SRCS="$NJS_LIB_SRCS $njs_modules_c" cat << END > $njs_modules_c #include END for mod in $NJS_LIB_MODULES do echo "extern njs_module_t $mod;" >> $njs_modules_c done echo >> $njs_modules_c echo 'njs_module_t *njs_modules[] = {' >> $njs_modules_c for mod in $NJS_LIB_MODULES do echo " &$mod," >> $njs_modules_c done cat << END >> $njs_modules_c NULL }; END qjs_modules_c=$NJS_BUILD_DIR/qjs_modules.c QJS_LIB_SRCS="$QJS_LIB_SRCS $qjs_modules_c" cat << END > $qjs_modules_c #include END for mod in $QJS_LIB_MODULES do echo "extern qjs_module_t $mod;" >> $qjs_modules_c done echo >> $qjs_modules_c echo 'qjs_module_t *qjs_modules[] = {' >> $qjs_modules_c for mod in $QJS_LIB_MODULES do echo " &$mod," >> $qjs_modules_c done cat << END >> $qjs_modules_c NULL }; END njs_incs=`echo $NJS_LIB_INCS \ | sed -e "s# *\([^ ]*\)#$njs_regex_cont-I\1#g"` njs_objs=`echo $NJS_LIB_SRCS \ | sed -e "s# *\([^ ]*\.\)c#$NJS_BUILD_DIR/\1o$njs_regex_cont#g"` qjs_objs=`echo $QJS_LIB_SRCS \ | sed -e "s# *\([^ ]*\.\)c#$NJS_BUILD_DIR/\1o$njs_regex_cont#g"` QJS_LIB="" if [ $NJS_HAVE_QUICKJS = YES ]; then QJS_LIB="$NJS_BUILD_DIR/libqjs.a" fi cat << END > $NJS_MAKEFILE # This file is auto-generated by configure NJS_CC = ${CC} NJS_STATIC_LINK = ${AR} -r -c NJS_LINK = ${CC} ${NJS_LD_OPT} NJS_CFLAGS = ${NJS_CFLAGS} ${NJS_CC_OPT} ${CFLAGS} NJS_LIB_AUX_CFLAGS = ${NJS_LIB_AUX_CFLAGS} NJS_VER = $(grep NJS_VERSION src/njs.h | sed -e 's#.*"\(.*\)".*#\1#') NJS_TYPES_VER = \$(NJS_VER) NPM = npm default: njs NJS_LIB_INCS = $njs_incs NJS_LIB_OBJS = $njs_objs QJS_LIB_OBJS = $qjs_objs libnjs: $NJS_BUILD_DIR/libnjs.a pc libqjs: $NJS_BUILD_DIR/libqjs.a $NJS_BUILD_DIR/libnjs.a: \\ $NJS_BUILD_DIR/njs_auto_config.h \\ \$(NJS_LIB_OBJS) \$(NJS_STATIC_LINK) $NJS_BUILD_DIR/libnjs.a \\ \$(NJS_LIB_OBJS) $NJS_BUILD_DIR/libqjs.a: \\ $NJS_BUILD_DIR/njs_auto_config.h \\ \$(QJS_LIB_OBJS) \$(NJS_STATIC_LINK) $NJS_BUILD_DIR/libqjs.a \\ \$(QJS_LIB_OBJS) END # object files. for njs_src in $NJS_LIB_SRCS do njs_obj="${njs_src%.c}.o" njs_dep="${njs_src%.c}.dep" njs_dep_flags=`njs_gen_dep_flags $njs_dep $njs_obj` njs_dep_post=`njs_gen_dep_post $njs_dep $njs_obj` cat << END >> $NJS_MAKEFILE $NJS_BUILD_DIR/$njs_obj: $njs_src \$(NJS_CC) -c \$(NJS_LIB_INCS) \$(NJS_CFLAGS) \\ \$(NJS_LIB_AUX_CFLAGS) \\ -o $NJS_BUILD_DIR/$njs_obj \\ $njs_dep_flags \\ $njs_src $njs_dep_post -include $NJS_BUILD_DIR/$njs_dep END done for njs_src in $QJS_LIB_SRCS do njs_obj="${njs_src%.c}.o" njs_dep="${njs_src%.c}.dep" njs_dep_flags=`njs_gen_dep_flags $njs_dep $njs_obj` njs_dep_post=`njs_gen_dep_post $njs_dep $njs_obj` cat << END >> $NJS_MAKEFILE $NJS_BUILD_DIR/$njs_obj: $njs_src \$(NJS_CC) -c \$(NJS_LIB_INCS) \$(NJS_CFLAGS) \\ \$(NJS_LIB_AUX_CFLAGS) \\ -o $NJS_BUILD_DIR/$njs_obj \\ $njs_dep_flags \\ $njs_src $njs_dep_post -include $NJS_BUILD_DIR/$njs_dep END done # njs cli. cat << END >> $NJS_MAKEFILE $NJS_BUILD_DIR/njs: \\ $NJS_BUILD_DIR/libnjs.a $QJS_LIB \\ external/njs_shell.c \$(NJS_LINK) -o $NJS_BUILD_DIR/njs \$(NJS_LIB_INCS) \\ \$(NJS_CFLAGS) \$(NJS_LIB_AUX_CFLAGS)\\ external/njs_shell.c \\ $NJS_BUILD_DIR/libnjs.a $QJS_LIB \\ $NJS_LD_OPT -lm $NJS_LIBS $NJS_LIB_AUX_LIBS $NJS_READLINE_LIB END # njs fuzzer. cat << END >> $NJS_MAKEFILE $NJS_BUILD_DIR/njs_process_script_fuzzer.o: \\ external/njs_shell.c \$(NJS_CC) -c \$(NJS_LIB_INCS) \$(CFLAGS) \\ \$(NJS_LIB_AUX_CFLAGS) \\ -DNJS_FUZZER_TARGET \\ -o $NJS_BUILD_DIR/njs_process_script_fuzzer.o \\ external/njs_shell.c $NJS_BUILD_DIR/njs_process_script_fuzzer: \\ $NJS_BUILD_DIR/libnjs.a \\ $NJS_BUILD_DIR/njs_process_script_fuzzer.o \$(CXX) \$(CXXFLAGS) -o $NJS_BUILD_DIR/njs_process_script_fuzzer \\ \$(NJS_LIB_AUX_CFLAGS) \\ \$(LIB_FUZZING_ENGINE) \\ $NJS_BUILD_DIR/njs_process_script_fuzzer.o \\ $NJS_BUILD_DIR/libnjs.a \\ -lm $NJS_LIBS $NJS_LIB_AUX_LIBS END # lib tests. for njs_src in $NJS_LIB_TEST_SRCS do fname=$(basename $njs_src) njs_dep="test/${fname%.c}.dep" njs_bin="${fname%.c}" njs_dep_flags=`njs_gen_dep_flags $njs_dep $fname` njs_dep_post=`njs_gen_dep_post $njs_dep $fname` cat << END >> $NJS_MAKEFILE $NJS_BUILD_DIR/$njs_bin: $njs_src \\ $NJS_BUILD_DIR/libnjs.a \$(NJS_LINK) -o $NJS_BUILD_DIR/$njs_bin \$(NJS_LIB_INCS) \\ \$(NJS_CFLAGS) $njs_dep_flags \\ $njs_src $NJS_BUILD_DIR/libnjs.a \\ $njs_dep_post -lm $NJS_LD_OPT -include $NJS_BUILD_DIR/$njs_dep END done # njs tests. njs_src="src/test/njs_externals_test.c" fname=$(basename $njs_src) njs_externals_obj="test/${fname%.c}.o" njs_dep="test/${fname%.c}.dep" njs_dep_flags=`njs_gen_dep_flags $njs_dep $njs_externals_obj` njs_dep_post=`njs_gen_dep_post $njs_dep $njs_externals_obj` cat << END >> $NJS_MAKEFILE $NJS_BUILD_DIR/$njs_externals_obj: \\ $njs_src \$(NJS_CC) -c \$(NJS_LIB_INCS) \$(NJS_CFLAGS) \\ \$(NJS_LIB_AUX_CFLAGS) \\ -o $NJS_BUILD_DIR/$njs_externals_obj \\ $njs_dep_flags \\ $njs_src $njs_dep_post -include $NJS_BUILD_DIR/$njs_dep END for njs_src in $NJS_TEST_SRCS do fname=$(basename $njs_src) njs_dep="test/${fname%.c}.dep" njs_bin="${fname%.c}" njs_dep_flags=`njs_gen_dep_flags $njs_dep $fname` njs_dep_post=`njs_gen_dep_post $njs_dep $fname` cat << END >> $NJS_MAKEFILE $NJS_BUILD_DIR/$njs_bin: $njs_src \\ $NJS_BUILD_DIR/libnjs.a \\ $NJS_BUILD_DIR/$njs_externals_obj \$(NJS_LINK) -o $NJS_BUILD_DIR/$njs_bin \$(NJS_LIB_INCS) \\ \$(NJS_CFLAGS) \$(NJS_LIB_AUX_CFLAGS) \\ $njs_dep_flags \\ $NJS_BUILD_DIR/$njs_externals_obj \\ $njs_src $NJS_BUILD_DIR/libnjs.a \\ $NJS_LD_OPT -lm $NJS_LIBS $NJS_LIB_AUX_LIBS $njs_dep_post -include $NJS_BUILD_DIR/$njs_dep END done # main targets. cat << END >> $NJS_MAKEFILE $NJS_BUILD_DIR/njs_auto_config.h: @echo @echo " Please run ./configure before make" @echo @exit 1 all: $NJS_BUILD_DIR/njs_auto_config.h \\ njs ts test lib_test benchmark njs: $NJS_BUILD_DIR/njs_auto_config.h $NJS_BUILD_DIR/njs njs_fuzzer: $NJS_BUILD_DIR/njs_auto_config.h \\ $NJS_BUILD_DIR/njs_process_script_fuzzer lib_test: $NJS_BUILD_DIR/njs_auto_config.h \\ $NJS_BUILD_DIR/random_unit_test \\ $NJS_BUILD_DIR/rbtree_unit_test \\ $NJS_BUILD_DIR/lvlhsh_unit_test \\ $NJS_BUILD_DIR/unicode_unit_test $NJS_BUILD_DIR/random_unit_test $NJS_BUILD_DIR/rbtree_unit_test $NJS_BUILD_DIR/lvlhsh_unit_test $NJS_BUILD_DIR/unicode_unit_test test262_njs: njs test/test262 --binary=$NJS_BUILD_DIR/njs unit_test: $NJS_BUILD_DIR/njs_auto_config.h \\ $NJS_BUILD_DIR/njs_unit_test $NJS_BUILD_DIR/njs_unit_test test: shell_test unit_test test262 benchmark: $NJS_BUILD_DIR/njs_auto_config.h \\ $NJS_BUILD_DIR/njs_benchmark $NJS_BUILD_DIR/njs_benchmark dist: rm -rf njs-\$(NJS_VER) \\ && hg archive njs-\$(NJS_VER).tar.gz \\ -p njs-\$(NJS_VER) \\ -X ".hg*" \\ && echo njs-\$(NJS_VER).tar.gz done END if [ $NJS_HAVE_QUICKJS = YES ]; then cat << END >> $NJS_MAKEFILE test262: njs test262_njs test262_quickjs test262_quickjs: njs NJS_SKIP_LIST="test/js/promise_rejection_tracker_recursive.t.js \\ test/js/async_exception_in_await.t.js" \\ test/test262 --binary='$NJS_BUILD_DIR/njs -n QuickJS -m' END else cat << END >> $NJS_MAKEFILE test262: njs test262_njs END fi njs_ts_deps=`echo $NJS_TS_SRCS \ | sed -e "s# *\([^ ][^ ]*\)#\1$njs_regex_cont#g"` njs_test_ts_deps=`echo $NJS_TEST_TS_SRCS \ | sed -e "s# *\([^ ][^ ]*\)#\1$njs_regex_cont#g"` cat << END >> $NJS_MAKEFILE $NJS_BUILD_DIR/ts/package.json: $njs_ts_deps cp -fr ts $NJS_BUILD_DIR/ cp LICENSE $NJS_BUILD_DIR/ts/ sed 's#__VERSION__#"\$(NJS_TYPES_VER)"#' \\ ts/package.json > $NJS_BUILD_DIR/ts/package.json $NJS_BUILD_DIR/ts/node_modules: $NJS_BUILD_DIR/ts/package.json cd $NJS_BUILD_DIR/ts && \$(NPM) install touch $NJS_BUILD_DIR/ts/node_modules $NJS_BUILD_DIR/njs-types-\$(NJS_TYPES_VER).tgz: $NJS_BUILD_DIR/ts/package.json hg id -i > $NJS_BUILD_DIR/ts/COMMITHASH || true cd $NJS_BUILD_DIR && \$(NPM) pack ./ts .PHONY: ts ts: $NJS_BUILD_DIR/ts/package.json ts_lint: $NJS_BUILD_DIR/ts/node_modules cd $NJS_BUILD_DIR/ts && \$(NPM) run lint $NJS_BUILD_DIR/test/ts/package.json: $njs_test_ts_deps mkdir -p $NJS_BUILD_DIR/test cp -fr test/ts $NJS_BUILD_DIR/test/ $NJS_BUILD_DIR/test/ts/node_modules: \\ $NJS_BUILD_DIR/njs-types-\$(NJS_TYPES_VER).tgz \\ $NJS_BUILD_DIR/test/ts/package.json cd $NJS_BUILD_DIR/test/ts && \$(NPM) install \\ --save-dev file:../../njs-types-\$(NJS_TYPES_VER).tgz cd $NJS_BUILD_DIR/test/ts && \$(NPM) install touch $NJS_BUILD_DIR/test/ts/node_modules ts_test: $NJS_BUILD_DIR/test/ts/node_modules cd $NJS_BUILD_DIR/test/ts && \$(NPM) test ts_publish: ts_clean $NJS_BUILD_DIR/njs-types-\$(NJS_TYPES_VER).tgz cd $NJS_BUILD_DIR/ && \$(NPM) publish njs-types-\$(NJS_TYPES_VER).tgz ts_clean: rm -rf $NJS_BUILD_DIR/ts END # pkg-config file cat << END >> $NJS_MAKEFILE pc: $NJS_BUILD_DIR/njs.pc $NJS_BUILD_DIR/njs.pc: $NJS_BUILD_DIR/njs_auto_config.h sed -e "s#@PREFIX@#$(pwd)/$NJS_BUILD_DIR#" \\ -e "s#@LIBDIR@#$(pwd)/$NJS_BUILD_DIR#" \\ -e "s#@CFLAGS@#-I$(pwd)/$NJS_BUILD_DIR -I$(pwd)/src#" \\ -e "s#@VERSION@#\$(NJS_VER)#" \\ -e "s#@EXTRA_LIBS@#-lm $NJS_LIBS $NJS_LIB_AUX_LIBS#" \\ src/njs.pc.in > \$@ END # Makefile. cat << END > Makefile # This file is auto-generated by configure include $NJS_MAKEFILE .PHONY: clean clean: rm -rf $NJS_BUILD_DIR Makefile END njs-0.8.9/auto/memalign000066400000000000000000000021521474132077100150140ustar00rootroot00000000000000 # 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. njs_feature="posix_memalign()" njs_feature_name=NJS_HAVE_POSIX_MEMALIGN njs_feature_run=yes njs_feature_incs= njs_feature_libs= njs_feature_test="#include int main(void) { void *p; if (posix_memalign(&p, 4096, 4096) != 0) return 1; free(p); return 0; }" . auto/feature if [ $njs_found = no ]; then # Solaris, HP-UX. njs_feature="memalign()" njs_feature_name=NJS_HAVE_MEMALIGN njs_feature_run=yes njs_feature_incs= njs_feature_libs= njs_feature_test="#include int main(void) { void *p; p = memalign(4096, 4096); if (p == NULL) return 1; free(p); return 0; }" . auto/feature fi njs-0.8.9/auto/module000066400000000000000000000003161474132077100145100ustar00rootroot00000000000000# Copyright (C) Dmitry Volyntsev # Copyright (C) NGINX, Inc. NJS_LIB_MODULES="$NJS_LIB_MODULES $njs_module_name" NJS_LIB_SRCS="$NJS_LIB_SRCS $njs_module_srcs" NJS_LIB_INCS="$NJS_LIB_INCS $njs_module_incs" njs-0.8.9/auto/modules000066400000000000000000000021671474132077100147010ustar00rootroot00000000000000# Copyright (C) Dmitry Volyntsev # Copyright (C) NGINX, Inc. njs_module_name=njs_buffer_module njs_module_incs= njs_module_srcs=src/njs_buffer.c . auto/module njs_module_name=njs_crypto_module njs_module_incs= njs_module_srcs="external/njs_crypto_module.c \ external/njs_md5.c \ external/njs_sha1.c \ external/njs_sha2.c" . auto/module if [ $NJS_OPENSSL = YES -a $NJS_HAVE_OPENSSL = YES ]; then njs_module_name=njs_webcrypto_module njs_module_incs= njs_module_srcs=external/njs_webcrypto_module.c . auto/module fi if [ $NJS_LIBXML2 = YES -a $NJS_HAVE_LIBXML2 = YES ]; then njs_module_name=njs_xml_module njs_module_incs= njs_module_srcs=external/njs_xml_module.c . auto/module fi if [ $NJS_ZLIB = YES -a $NJS_HAVE_ZLIB = YES ]; then njs_module_name=njs_zlib_module njs_module_incs= njs_module_srcs=external/njs_zlib_module.c . auto/module fi njs_module_name=njs_fs_module njs_module_incs= njs_module_srcs=external/njs_fs_module.c . auto/module njs_module_name=njs_query_string_module njs_module_incs= njs_module_srcs=external/njs_query_string_module.c . auto/module njs-0.8.9/auto/openssl000066400000000000000000000024421474132077100147100ustar00rootroot00000000000000 # Copyright (C) Dmitry Volyntsev # Copyright (C) NGINX, Inc. NJS_OPENSSL_LIB= NJS_HAVE_OPENSSL=NO if [ $NJS_OPENSSL = YES ]; then njs_found=no njs_feature="OpenSSL library" njs_feature_name=NJS_HAVE_OPENSSL njs_feature_run=yes njs_feature_incs= njs_feature_libs="" njs_feature_test="#include int main() { EVP_CIPHER_CTX *ctx; ctx = EVP_CIPHER_CTX_new(); EVP_CIPHER_CTX_free(ctx); return 0; }" . auto/feature if [ $njs_found = no ]; then njs_feature="OpenSSL library -lcrypto" njs_feature_libs="-lcrypto" . auto/feature fi if [ $njs_found = yes ]; then njs_feature="OpenSSL version" njs_feature_name=NJS_OPENSSL_VERSION njs_feature_run=value njs_feature_test="#include int main() { printf(\"\\\"%s\\\"\", OPENSSL_VERSION_TEXT); return 0; }" . auto/feature NJS_HAVE_OPENSSL=YES NJS_OPENSSL_LIB="$njs_feature_libs" NJS_LIB_AUX_LIBS="$NJS_LIB_AUX_LIBS $njs_feature_libs" fi fi njs-0.8.9/auto/options000066400000000000000000000051361474132077100147230ustar00rootroot00000000000000 # Copyright (C) Igor Sysoev # Copyright (C) NGINX, Inc. NJS_CC_OPT=${NJS_CC_OPT:--O} NJS_LD_OPT=${NJS_CC_OPT:--O} NJS_DEBUG=NO NJS_DEBUG_MEMORY=NO NJS_DEBUG_OPCODE=NO NJS_DEBUG_GENERATOR=NO NJS_ADDRESS_SANITIZER=NO NJS_ADDR2LINE=NO NJS_QUICKJS=NO NJS_TRY_QUICKJS=YES NJS_OPENSSL=YES NJS_LIBXML2=YES NJS_ZLIB=YES NJS_PCRE=YES NJS_TRY_PCRE2=YES NJS_TRY_GOTO=YES NJS_CONFIGURE_OPTIONS= for njs_option do case "$njs_option" in -*=*) value=`echo "$njs_option" | sed -e 's#[-_a-zA-Z0-9]*=##'` ;; *) value="" ;; esac case "$njs_option" in --cc=*) CC="$value" ;; --cc-opt=*) NJS_CC_OPT="$value" ;; --ld-opt=*) NJS_LD_OPT="$value" ;; --ar=*) AR="$value" ;; --build-dir=*) NJS_BUILD_DIR="$value" ;; --address-sanitizer=*) NJS_ADDRESS_SANITIZER="$value" ;; --addr2line=*) NJS_ADDR2LINE="$value" ;; --debug=*) NJS_DEBUG="$value" ;; --debug-memory=*) NJS_DEBUG_MEMORY="$value" ;; --debug-opcode=*) NJS_DEBUG_OPCODE="$value" ;; --debug-generator=*) NJS_DEBUG_GENERATOR="$value" ;; --no-quickjs) NJS_TRY_QUICKJS=NO ;; --no-openssl) NJS_OPENSSL=NO ;; --no-libxml2) NJS_LIBXML2=NO ;; --no-zlib) NJS_ZLIB=NO ;; --no-pcre) NJS_PCRE=NO ;; --no-pcre2) NJS_TRY_PCRE2=NO ;; --no-goto) NJS_TRY_GOTO=NO ;; --with-quickjs) NJS_TRY_QUICKJS=YES; NJS_QUICKJS=YES ;; --help) . auto/help exit 0 ;; *) echo echo $0: error: invalid option \"$njs_option\". echo Run \"$0 --help\" to see available options. echo exit 1 ;; esac njs_opt=`echo $njs_option | sed -e "s#\(--[^=]*=\)\(.* .*\)#\1'\2'#"` NJS_CONFIGURE_OPTIONS="$NJS_CONFIGURE_OPTIONS $njs_opt" done if [ "$NJS_DEBUG_MEMORY" = "YES" ]; then NJS_DEBUG=YES fi if [ "$NJS_DEBUG_OPCODE" = "YES" ]; then NJS_ADDR2LINE=YES fi njs-0.8.9/auto/os000066400000000000000000000020661474132077100136500ustar00rootroot00000000000000 # Copyright (C) Igor Sysoev # Copyright (C) NGINX, Inc. NJS_SYSTEM=`uname -s 2>/dev/null` case "$NJS_SYSTEM" in Linux) njs_define=NJS_LINUX . auto/define NJS_SYSTEM_VERSION=`uname -r 2>/dev/null` # Linux uname -p can return "unknown". NJS_SYSTEM_PLATFORM=`uname -m 2>/dev/null` CC=${CC:-cc} ;; FreeBSD | NetBSD | OpenBSD) NJS_SYSTEM_VERSION=`uname -r 2>/dev/null` NJS_SYSTEM_PLATFORM=`uname -m 2>/dev/null` CC=${CC:-cc} ;; SunOS) njs_define=NJS_SOLARIS . auto/define NJS_SYSTEM_VERSION=`uname -r 2>/dev/null` NJS_SYSTEM_PLATFORM=`uname -p 2>/dev/null` CC=${CC:-gcc} ;; Darwin) NJS_SYSTEM_VERSION=`uname -r 2>/dev/null` NJS_SYSTEM_PLATFORM=`uname -m 2>/dev/null` CC=${CC:-cc} ;; *) NJS_SYSTEM_VERSION=`uname -r 2>/dev/null` NJS_SYSTEM_PLATFORM=`uname -p 2>/dev/null` CC=${CC:-gcc} ;; esac echo configuring for $NJS_SYSTEM $NJS_SYSTEM_VERSION $NJS_SYSTEM_PLATFORM njs-0.8.9/auto/pcre000066400000000000000000000073541474132077100141650ustar00rootroot00000000000000 # Copyright (C) Igor Sysoev # Copyright (C) NGINX, Inc. NJS_PCRE_CFLAGS= NJS_PCRE_LIB= NJS_HAVE_PCRE=NO if [ $NJS_PCRE = YES ]; then njs_found=no if [ $NJS_TRY_PCRE2 = YES ]; then njs_feature="PCRE2 library" njs_feature_name=NJS_HAVE_PCRE2 njs_feature_run=no njs_feature_incs= njs_feature_libs= njs_feature_test="#define PCRE2_CODE_UNIT_WIDTH 8 #include int main(void) { pcre2_code *re; re = pcre2_compile((PCRE2_SPTR)\"\", PCRE2_ZERO_TERMINATED, 0, NULL, NULL, NULL); return (re == NULL); }" . auto/feature if [ $njs_found = no ]; then # pcre2-config if /bin/sh -c "(pcre2-config --version)" >> $NJS_AUTOCONF_ERR 2>&1; then NJS_PCRE_CFLAGS=`pcre2-config --cflags` NJS_PCRE_LIB=`pcre2-config --libs8` njs_feature="PCRE2 library in `pcre2-config --prefix 2>/dev/null`" njs_feature_incs=$NJS_PCRE_CFLAGS njs_feature_libs=$NJS_PCRE_LIB . auto/feature fi fi if [ $njs_found = yes ]; then njs_feature="PCRE2 version" njs_feature_name=NJS_PCRE2_VERSION njs_feature_run=value njs_feature_test="#define PCRE2_CODE_UNIT_WIDTH 8 #include #include int main(void) { printf(\"%d.%d\", PCRE2_MAJOR, PCRE2_MINOR); return 0; }" . auto/feature NJS_HAVE_PCRE=YES fi fi if [ $njs_found = no ]; then njs_feature="PCRE library" njs_feature_name=NJS_HAVE_PCRE njs_feature_run=no njs_feature_incs= njs_feature_libs= njs_feature_test="#include int main(void) { pcre *re; re = pcre_compile(NULL, 0, NULL, 0, NULL); if (re == NULL) return 1; return 0; }" . auto/feature if [ $njs_found = no ]; then # pcre-config njs_pcre_prefix=`pcre-config --prefix 2>/dev/null` if [ -n "$njs_pcre_prefix" ]; then NJS_PCRE_CFLAGS=`pcre-config --cflags` NJS_PCRE_LIB=`pcre-config --libs` njs_feature="PCRE library in $njs_pcre_prefix" njs_feature_incs="$NJS_PCRE_CFLAGS" njs_feature_libs=$NJS_PCRE_LIB . auto/feature fi fi if [ $njs_found = yes ]; then njs_feature="PCRE version" njs_feature_name=NJS_PCRE_VERSION njs_feature_run=value njs_feature_test="#include #include int main(void) { printf(\"%d.%d\", PCRE_MAJOR, PCRE_MINOR); return 0; }" . auto/feature NJS_HAVE_PCRE=YES fi fi if [ $njs_found = no ]; then echo echo $0: error: no PCRE library found. echo exit 1; fi fi NJS_LIB_AUX_CFLAGS="$NJS_LIB_AUX_CFLAGS $NJS_PCRE_CFLAGS" NJS_LIB_AUX_LIBS="$NJS_LIB_AUX_LIBS $NJS_PCRE_LIB" njs-0.8.9/auto/qjs_module000066400000000000000000000003121474132077100153610ustar00rootroot00000000000000# Copyright (C) Dmitry Volyntsev # Copyright (C) F5, Inc QJS_LIB_MODULES="$QJS_LIB_MODULES $njs_module_name" QJS_LIB_SRCS="$QJS_LIB_SRCS $njs_module_srcs" NJS_LIB_INCS="$NJS_LIB_INCS $njs_module_incs" njs-0.8.9/auto/qjs_modules000066400000000000000000000007051474132077100155520ustar00rootroot00000000000000# Copyright (C) Dmitry Volyntsev # Copyright (C) F5, Inc njs_module_name=qjs_buffer_module njs_module_incs= njs_module_srcs=src/qjs_buffer.c . auto/qjs_module njs_module_name=qjs_fs_module njs_module_incs= njs_module_srcs=external/qjs_fs_module.c . auto/qjs_module if [ $NJS_ZLIB = YES -a $NJS_HAVE_ZLIB = YES ]; then njs_module_name=qjs_zlib_module njs_module_incs= njs_module_srcs=external/qjs_zlib_module.c . auto/qjs_module fi njs-0.8.9/auto/quickjs000066400000000000000000000064201474132077100146760ustar00rootroot00000000000000 # Copyright (C) Dmitry Volyntsev # Copyright (C) NGINX, Inc. NJS_QUICKJS_LIB= NJS_HAVE_QUICKJS=NO if [ $NJS_TRY_QUICKJS = YES ]; then njs_found=no njs_feature="QuickJS library -lquickjs.lto" njs_feature_name=NJS_HAVE_QUICKJS njs_feature_run=yes njs_feature_incs= njs_feature_libs="-lquickjs.lto -lm -ldl -lpthread" njs_feature_test="#if defined(__GNUC__) && (__GNUC__ >= 8) #pragma GCC diagnostic push #pragma GCC diagnostic ignored \"-Wcast-function-type\" #endif #include int main() { JSRuntime *rt; rt = JS_NewRuntime(); JS_FreeRuntime(rt); return 0; }" . auto/feature if [ $njs_found = no ]; then njs_feature="QuickJS library -lquickjs" njs_feature_libs="-lquickjs -lm -ldl -lpthread" . auto/feature fi if [ $njs_found = no ]; then njs_feature="QuickJS library -I/usr/include/quickjs/ -L/usr/lib/quickjs/ -lquickjs.lto" njs_feature_incs="/usr/include/quickjs/" njs_feature_libs="-L/usr/lib/quickjs/ -lquickjs.lto -lm -ldl -lpthread" . auto/feature fi if [ $njs_found = no ]; then njs_feature="QuickJS library -I/usr/include/quickjs/ -L/usr/lib/quickjs/ -lquickjs" njs_feature_incs="/usr/include/quickjs/" njs_feature_libs="-L/usr/lib/quickjs/ -lquickjs -lm -ldl -lpthread" . auto/feature fi if [ $njs_found = yes ]; then njs_feature="QuickJS JS_GetClassID()" njs_feature_test="#if defined(__GNUC__) && (__GNUC__ >= 8) #pragma GCC diagnostic push #pragma GCC diagnostic ignored \"-Wcast-function-type\" #endif #include int main() { (void) JS_GetClassID; return 0; }" . auto/feature if [ $njs_found = no ]; then echo echo $0: error: QuickJS library found, but JS_GetClassID\(\) is missing. echo exit 1; fi njs_feature="QuickJS JS_NewTypedArray()" njs_feature_test="#if defined(__GNUC__) && (__GNUC__ >= 8) #pragma GCC diagnostic push #pragma GCC diagnostic ignored \"-Wcast-function-type\" #endif #include int main() { (void) JS_NewTypedArray; return 0; }" . auto/feature if [ $njs_found = yes ]; then njs_define=NJS_HAVE_QUICKJS_NEW_TYPED_ARRAY . auto/define fi NJS_HAVE_QUICKJS=YES NJS_QUICKJS_LIB="$njs_feature_libs" NJS_LIB_INCS="$NJS_LIB_INCS $njs_feature_incs" NJS_LIB_AUX_LIBS="$NJS_LIB_AUX_LIBS $njs_feature_libs" fi if [ $NJS_QUICKJS = YES -a $NJS_HAVE_QUICKJS = NO ]; then echo echo $0: error: no QuickJS library found. echo exit 1; fi fi njs-0.8.9/auto/readline000066400000000000000000000030061474132077100150050ustar00rootroot00000000000000# Copyright (C) Dmitry Volyntsev # Copyright (C) NGINX, Inc. NJS_READLINE_LIB= njs_found=no njs_feature="editline library in editline/readline.h" njs_feature_run=no njs_feature_incs= njs_feature_name=NJS_HAVE_EDITLINE njs_feature_libs="-ledit" njs_feature_test="#include #include int main(void) { add_history(NULL); return 0; }" . auto/feature if [ $njs_found = no ]; then # FreeBSD port njs_feature_name=NJS_HAVE_EDIT_READLINE njs_feature="editline in edit/readline/readline.h" njs_feature_test="#include #include int main(void) { add_history(NULL); return 0; }" . auto/feature fi if [ $njs_found = no ]; then # NetBSD njs_feature_name=NJS_HAVE_NETBSD_READLINE njs_feature="editline in readline/readline.h" njs_feature_test="#include #include int main(void) { add_history(NULL); return 0; }" . auto/feature fi if [ $njs_found = yes ]; then NJS_HAVE_READLINE=YES njs_define=NJS_HAVE_READLINE . auto/define NJS_READLINE_LIB=$njs_feature_libs else NJS_HAVE_READLINE=NO echo " - njs CLI is built without interactive shell support" fi njs-0.8.9/auto/sources000066400000000000000000000033631474132077100147130ustar00rootroot00000000000000NJS_LIB_SRCS=" \ src/njs_diyfp.c \ src/njs_dtoa.c \ src/njs_dtoa_fixed.c \ src/njs_str.c \ src/njs_strtod.c \ src/njs_murmur_hash.c \ src/njs_djb_hash.c \ src/njs_utf8.c \ src/njs_utf16.c \ src/njs_arr.c \ src/njs_rbtree.c \ src/njs_flathsh.c \ src/njs_trace.c \ src/njs_random.c \ src/njs_malloc.c \ src/njs_mp.c \ src/njs_sprintf.c \ src/njs_utils.c \ src/njs_chb.c \ src/njs_value.c \ src/njs_vm.c \ src/njs_vmcode.c \ src/njs_lexer.c \ src/njs_lexer_keyword.c \ src/njs_parser.c \ src/njs_variable.c \ src/njs_scope.c \ src/njs_generator.c \ src/njs_disassembler.c \ src/njs_module.c \ src/njs_extern.c \ src/njs_boolean.c \ src/njs_number.c \ src/njs_symbol.c \ src/njs_string.c \ src/njs_object.c \ src/njs_object_prop.c \ src/njs_array.c \ src/njs_json.c \ src/njs_function.c \ src/njs_regexp.c \ src/njs_date.c \ src/njs_error.c \ src/njs_math.c \ src/njs_array_buffer.c \ src/njs_typed_array.c \ src/njs_promise.c \ src/njs_encoding.c \ src/njs_iterator.c \ src/njs_async.c \ src/njs_builtin.c \ " QJS_LIB_SRCS=" \ src/qjs.c \ " NJS_LIB_TEST_SRCS=" \ src/test/lvlhsh_unit_test.c \ src/test/random_unit_test.c \ src/test/rbtree_unit_test.c \ src/test/unicode_unit_test.c \ " NJS_TEST_SRCS=" \ src/test/njs_unit_test.c \ src/test/njs_benchmark.c \ " if [ "$NJS_PCRE" = "YES" ]; then NJS_LIB_SRCS="$NJS_LIB_SRCS external/njs_regex.c" fi if [ "$NJS_HAVE_LIBBFD" = "YES" -a "$NJS_HAVE_DL_ITERATE_PHDR" = "YES" ]; then NJS_LIB_SRCS="$NJS_LIB_SRCS src/njs_addr2line.c" fi NJS_TS_SRCS=$(find ts/ -name "*.d.ts" -o -name "*.json") NJS_TEST_TS_SRCS=$(find test/ts/ -name "*.ts" -o -name "*.json") njs-0.8.9/auto/stat000066400000000000000000000033561474132077100142050ustar00rootroot00000000000000 # Copyright (C) Dmitry Volyntsev # Copyright (C) NGINX, Inc. njs_feature="stat.st_atimespec" njs_feature_name=NJS_HAVE_STAT_ATIMESPEC njs_feature_run=no njs_feature_incs= njs_feature_libs= njs_feature_test="#include int main(void) { struct stat st; if (fstat(0, &st) != 0) { return 1; } return (int) st.st_atimespec.tv_sec; }" . auto/feature njs_feature="stat.st_birthtim" njs_feature_name=NJS_HAVE_STAT_BIRTHTIM njs_feature_incs= njs_feature_libs= njs_feature_test="#include int main(void) { struct stat st; if (fstat(0, &st) != 0) { return 1; } return (int) st.st_birthtim.tv_sec; }" . auto/feature njs_feature="stat.__st_birthtim" njs_feature_name=NJS_HAVE__STAT_BIRTHTIM njs_feature_incs= njs_feature_libs= njs_feature_test="#include int main(void) { struct stat st; if (fstat(0, &st) != 0) { return 1; } return (int) st.__st_birthtim.tv_sec; }" . auto/feature njs_feature="stat.st_atim" njs_feature_name=NJS_HAVE_STAT_ATIM njs_feature_incs= njs_feature_libs= njs_feature_test="#include int main(void) { struct stat st; if (fstat(0, &st) != 0) { return 1; } return (int) st.st_atim.tv_sec; }" . auto/feature njs-0.8.9/auto/summary000066400000000000000000000016111474132077100147170ustar00rootroot00000000000000 # Copyright (C) Igor Sysoev # Copyright (C) NGINX, Inc. echo echo "NJS configuration summary:" echo echo " + using CC: \"$CC\"" echo " + using CFLAGS: \"$NJS_CFLAGS $NJS_CC_OPT $CFLAGS\"" echo if [ $NJS_HAVE_PCRE = YES ]; then echo " + using PCRE library: $NJS_PCRE_LIB" fi if [ $NJS_HAVE_READLINE = YES ]; then echo " + using readline library: $NJS_READLINE_LIB" fi if [ $NJS_HAVE_QUICKJS = YES ]; then echo " + using QuickJS library: $NJS_QUICKJS_LIB" fi if [ $NJS_HAVE_OPENSSL = YES ]; then echo " + using OpenSSL library: $NJS_OPENSSL_LIB" fi if [ $NJS_HAVE_LIBXML2 = YES ]; then echo " + using libxml2 library: $NJS_LIBXML2_LIB" fi if [ $NJS_HAVE_ZLIB = YES ]; then echo " + using zlib library: $NJS_ZLIB_LIB" fi if [ $NJS_HAVE_COMPUTED_GOTO = YES ]; then echo " + using computed goto" fi echo echo " njs build dir: $NJS_BUILD_DIR" echo " njs CLI: $NJS_BUILD_DIR/njs" echo njs-0.8.9/auto/time000066400000000000000000000027311474132077100141640ustar00rootroot00000000000000 # Copyright (C) Igor Sysoev # Copyright (C) NGINX, Inc. njs_feature="clock_gettime(CLOCK_MONOTONIC)" njs_feature_name=NJS_HAVE_CLOCK_MONOTONIC njs_feature_run=yes njs_feature_incs= njs_feature_libs= njs_feature_test="#include int main() { struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) return 1; return 0; }" . auto/feature if [ $njs_found = no ]; then # Linux and Solaris 10 clock_gettime() are in librt. njs_feature="clock_gettime(CLOCK_MONOTONIC) in librt" njs_feature_libs="-lrt" . auto/feature if [ $njs_found = yes ]; then NJS_LIBRT="-lrt" fi fi # Linux, FreeBSD, MacOSX. njs_feature="struct tm.tm_gmtoff" njs_feature_name=NJS_HAVE_TM_GMTOFF njs_feature_run=no njs_feature_incs= njs_feature_libs= njs_feature_test="#include int main(void) { time_t t; struct tm tm; t = 0; localtime_r(&t, &tm); return tm.tm_gmtoff; }" . auto/feature # Solaris njs_feature="altzone" njs_feature_name=NJS_HAVE_ALTZONE njs_feature_run=no njs_feature_incs= njs_feature_libs= njs_feature_test="#include int main(void) { altzone = 0; return 0; }" . auto/feature njs-0.8.9/auto/types000066400000000000000000000076241474132077100144000ustar00rootroot00000000000000 # Copyright (C) Igor Sysoev # Copyright (C) NGINX, Inc. # Sizes of C types. # "-Wall -Werror" or similar constraints in default CFLAGS may require # to use "%zu" format to printf() result of sizeof(). But "%zu" may # be unavailable, so the "(int)" cast is a simple and portable solution: # printf("%d", (int) sizeof(TYPE)); njs_feature="sizeof(int)" njs_feature_name=NJS_INT_SIZE njs_feature_run=value njs_feature_incs= njs_feature_libs= njs_feature_test="#include int main() { printf(\"%d\", (int) sizeof(int)); return 0; }" . auto/feature njs_feature="sizeof(u_int)" njs_feature_name=NJS_UINT_SIZE njs_feature_run=value njs_feature_incs= njs_feature_libs= njs_feature_test="#include #include int main() { printf(\"%d\", (int) sizeof(u_int)); return 0; }" . auto/feature njs_feature="sizeof(void *)" njs_feature_name=NJS_PTR_SIZE njs_feature_run=value njs_feature_incs= njs_feature_libs= njs_feature_test="#include int main() { printf(\"%d\", (int) sizeof(void *)); return 0; }" . auto/feature njs_feature="sizeof(uintptr_t)" njs_feature_name=NJS_UINTPTR_T_SIZE njs_feature_run=value njs_feature_incs= njs_feature_libs= njs_feature_test="#include #include int main() { printf(\"%d\", (int) sizeof(uintptr_t)); return 0; }" . auto/feature case "$njs_feature_value" in 8) NJS_64BIT=1 ;; *) NJS_64BIT=0 ;; esac njs_feature="sizeof(size_t)" njs_feature_name=NJS_SIZE_T_SIZE njs_feature_run=value njs_feature_incs= njs_feature_libs= njs_feature_test="#include int main() { printf(\"%d\", (int) sizeof(size_t)); return 0; }" . auto/feature njs_feature="sizeof(off_t)" njs_feature_name=NJS_OFF_T_SIZE njs_feature_run=value njs_feature_incs= njs_feature_libs= njs_feature_test="#define _FILE_OFFSET_BITS 64 #include #include int main() { printf(\"%d\", (int) sizeof(off_t)); return 0; }" . auto/feature njs_feature="sizeof(time_t)" njs_feature_name=NJS_TIME_T_SIZE njs_feature_run=value njs_feature_incs= njs_feature_libs= njs_feature_test="#include #include int main(void) { printf(\"%d\", (int) sizeof(time_t)); return 0; }" . auto/feature # Ensuring that double type is always evaluated at standard # precision required by njs_diyfp_t case $NJS_CC_NAME in gcc) NJS_CFLAGS="$NJS_CFLAGS -fexcess-precision=standard" ;; clang) njs_found=no njs_feature="flag -ffp-eval-method=double" njs_feature_name=NJS_HAVE_FP_EVAL_METHOD njs_feature_run=no njs_feature_incs="-ffp-eval-method=double" njs_feature_libs= njs_feature_test="int main(void) { return 0; }" . auto/feature if [ $njs_found = yes ]; then NJS_CFLAGS="$NJS_CFLAGS -ffp-eval-method=double" fi ;; SunC) njs_found=no njs_feature="flag -xarch=sse2" njs_feature_name=NJS_HAVE_XARCH_SSE2 njs_feature_run=no njs_feature_incs="-xarch=sse2" njs_feature_libs= njs_feature_test="int main(void) { return 0; }" . auto/feature if [ $njs_found = yes ]; then NJS_CFLAGS="$NJS_CFLAGS -xarch=sse2" fi ;; esac njs-0.8.9/auto/zlib000066400000000000000000000030361474132077100141650ustar00rootroot00000000000000 # Copyright (C) Dmitry Volyntsev # Copyright (C) NGINX, Inc. NJS_ZLIB_LIB= NJS_HAVE_ZLIB=NO if [ $NJS_ZLIB = YES ]; then njs_found=no njs_feature_name=NJS_HAVE_ZLIB njs_feature_run=no njs_feature_test="#include int main() { int rc; z_stream z; rc = deflate(&z, Z_NO_FLUSH); return (rc == Z_OK) ? 0 : 1; }" if /bin/sh -c "(pkg-config zlib --exists)" >> $NJS_AUTOCONF_ERR 2>&1; then # pkg-config njs_feature="zlib via pkg-config" njs_feature_incs=`pkg-config zlib --cflags | sed -n -e 's#.*-I *\([^ ][^ ]*\).*#\1#p'` njs_feature_libs=`pkg-config zlib --libs` . auto/feature fi if [ $njs_found = no ]; then njs_feature="zlib" njs_feature_libs="-lz" . auto/feature fi if [ $njs_found = yes ]; then njs_feature="zlib version" njs_feature_name=NJS_ZLIB_VERSION njs_feature_run=value njs_feature_test="#include #include int main() { printf(\"\\\"%s\\\"\", zlibVersion()); return 0; }" . auto/feature NJS_HAVE_ZLIB=YES NJS_ZLIB_LIB="$njs_feature_libs" NJS_LIB_INCS="$NJS_LIB_INCS $njs_feature_incs" NJS_LIB_AUX_LIBS="$NJS_LIB_AUX_LIBS $njs_feature_libs" fi fi njs-0.8.9/configure000077500000000000000000000020121474132077100142320ustar00rootroot00000000000000#!/bin/sh # Copyright (C) Igor Sysoev # Copyright (C) NGINX, Inc. # Disable localized program messages. LC_ALL=C export LC_ALL # Stop on error exit status. set -e # Stop on uninitialized variable. set -u . auto/init . auto/options NJS_AUTOTEST=$NJS_BUILD_DIR/autotest NJS_AUTOCONF_ERR=$NJS_BUILD_DIR/autoconf.err NJS_AUTO_CONFIG_H=$NJS_BUILD_DIR/njs_auto_config.h NJS_MAKEFILE=$NJS_BUILD_DIR/Makefile NJS_LIB_INCS="src external $NJS_BUILD_DIR" test -d $NJS_BUILD_DIR || mkdir $NJS_BUILD_DIR > $NJS_AUTOCONF_ERR cat << END > $NJS_AUTO_CONFIG_H /* This file is auto-generated by configure */ END NJS_LIBS="$NJS_LIBRT" NJS_LIB_AUX_CFLAGS= NJS_LIB_AUX_LIBS= . auto/os . auto/cc . auto/types . auto/endianness . auto/clang . auto/time . auto/memalign . auto/getrandom . auto/stat . auto/computed_goto . auto/explicit_bzero . auto/pcre . auto/readline . auto/quickjs . auto/openssl . auto/libxml2 . auto/zlib . auto/libbfd . auto/link . auto/sources . auto/modules . auto/qjs_modules . auto/make . auto/expect . auto/summary njs-0.8.9/external/000077500000000000000000000000001474132077100141525ustar00rootroot00000000000000njs-0.8.9/external/njs_crypto_module.c000066400000000000000000000401761474132077100200650ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #include #include #include #include "njs_hash.h" typedef void (*njs_hash_init)(njs_hash_t *ctx); typedef void (*njs_hash_update)(njs_hash_t *ctx, const void *data, size_t size); typedef void (*njs_hash_final)(u_char result[32], njs_hash_t *ctx); typedef njs_int_t (*njs_digest_encode)(njs_vm_t *vm, njs_value_t *value, const njs_str_t *src); typedef struct { njs_str_t name; size_t size; njs_hash_init init; njs_hash_update update; njs_hash_final final; } njs_hash_alg_t; typedef struct { njs_hash_t ctx; njs_hash_alg_t *alg; } njs_digest_t; typedef struct { u_char opad[64]; njs_hash_t ctx; njs_hash_alg_t *alg; } njs_hmac_t; typedef struct { njs_str_t name; njs_digest_encode encode; } njs_crypto_enc_t; static njs_hash_alg_t *njs_crypto_algorithm(njs_vm_t *vm, njs_value_t *value); static njs_crypto_enc_t *njs_crypto_encoding(njs_vm_t *vm, njs_value_t *value); static njs_int_t njs_buffer_digest(njs_vm_t *vm, njs_value_t *value, const njs_str_t *src); static njs_int_t njs_crypto_create_hash(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_hash_prototype_update(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t hmac, njs_value_t *retval); static njs_int_t njs_hash_prototype_digest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t hmac, njs_value_t *retval); static njs_int_t njs_hash_prototype_copy(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t hmac, njs_value_t *retval); static njs_int_t njs_crypto_create_hmac(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_crypto_init(njs_vm_t *vm); static njs_hash_alg_t njs_hash_algorithms[] = { { njs_str("md5"), 16, njs_md5_init, njs_md5_update, njs_md5_final }, { njs_str("sha1"), 20, njs_sha1_init, njs_sha1_update, njs_sha1_final }, { njs_str("sha256"), 32, njs_sha2_init, njs_sha2_update, njs_sha2_final }, { njs_null_str, 0, NULL, NULL, NULL } }; static njs_crypto_enc_t njs_encodings[] = { { njs_str("buffer"), njs_buffer_digest }, { njs_str("hex"), njs_string_hex }, { njs_str("base64"), njs_string_base64 }, { njs_str("base64url"), njs_string_base64url }, { njs_null_str, NULL } }; static njs_external_t njs_ext_crypto_hash[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "Hash", } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("update"), .writable = 1, .configurable = 1, .u.method = { .native = njs_hash_prototype_update, .magic8 = 0, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("digest"), .writable = 1, .configurable = 1, .u.method = { .native = njs_hash_prototype_digest, .magic8 = 0, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("copy"), .writable = 1, .configurable = 1, .u.method = { .native = njs_hash_prototype_copy, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("constructor"), .writable = 1, .configurable = 1, .u.method = { .native = njs_crypto_create_hash, } }, }; static njs_external_t njs_ext_crypto_hmac[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "Hmac", } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("update"), .writable = 1, .configurable = 1, .u.method = { .native = njs_hash_prototype_update, .magic8 = 1, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("digest"), .writable = 1, .configurable = 1, .u.method = { .native = njs_hash_prototype_digest, .magic8 = 1, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("constructor"), .writable = 1, .configurable = 1, .u.method = { .native = njs_crypto_create_hmac, .magic8 = 0, } }, }; static njs_external_t njs_ext_crypto_crypto[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "crypto", } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("createHash"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_crypto_create_hash, .magic8 = 0, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("createHmac"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_crypto_create_hmac, .magic8 = 0, } }, }; static njs_int_t njs_crypto_hash_proto_id; static njs_int_t njs_crypto_hmac_proto_id; njs_module_t njs_crypto_module = { .name = njs_str("crypto"), .preinit = NULL, .init = njs_crypto_init, }; static njs_int_t njs_crypto_create_hash(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_digest_t *dgst; njs_hash_alg_t *alg; alg = njs_crypto_algorithm(vm, njs_arg(args, nargs, 1)); if (njs_slow_path(alg == NULL)) { return NJS_ERROR; } dgst = njs_mp_alloc(njs_vm_memory_pool(vm), sizeof(njs_digest_t)); if (njs_slow_path(dgst == NULL)) { njs_vm_memory_error(vm); return NJS_ERROR; } dgst->alg = alg; alg->init(&dgst->ctx); return njs_vm_external_create(vm, retval, njs_crypto_hash_proto_id, dgst, 0); } static njs_int_t njs_hash_prototype_update(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t hmac, njs_value_t *retval) { njs_str_t data; njs_int_t ret; njs_hmac_t *ctx; njs_value_t *this, *value; njs_digest_t *dgst; njs_opaque_value_t result; const njs_buffer_encoding_t *enc; this = njs_argument(args, 0); if (!hmac) { dgst = njs_vm_external(vm, njs_crypto_hash_proto_id, this); if (njs_slow_path(dgst == NULL)) { njs_vm_type_error(vm, "\"this\" is not a hash object"); return NJS_ERROR; } if (njs_slow_path(dgst->alg == NULL)) { njs_vm_error(vm, "Digest already called"); return NJS_ERROR; } ctx = NULL; } else { ctx = njs_vm_external(vm, njs_crypto_hmac_proto_id, this); if (njs_slow_path(ctx == NULL)) { njs_vm_type_error(vm, "\"this\" is not a hmac object"); return NJS_ERROR; } if (njs_slow_path(ctx->alg == NULL)) { njs_vm_error(vm, "Digest already called"); return NJS_ERROR; } dgst = NULL; } value = njs_arg(args, nargs, 1); if (njs_value_is_string(value)) { enc = njs_buffer_encoding(vm, njs_arg(args, nargs, 2), 1); if (njs_slow_path(enc == NULL)) { return NJS_ERROR; } ret = njs_buffer_decode_string(vm, value, njs_value_arg(&result), enc); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_value_string_get(njs_value_arg(&result), &data); } else if (njs_value_is_buffer(value)) { ret = njs_value_buffer_get(vm, value, &data); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } else { njs_vm_type_error(vm, "data is not a string or Buffer-like object"); return NJS_ERROR; } if (!hmac) { dgst->alg->update(&dgst->ctx, data.start, data.length); } else { ctx->alg->update(&ctx->ctx, data.start, data.length); } njs_value_assign(retval, this); return NJS_OK; } static njs_int_t njs_hash_prototype_digest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t hmac, njs_value_t *retval) { njs_str_t str; njs_hmac_t *ctx; njs_value_t *this; njs_digest_t *dgst; njs_hash_alg_t *alg; njs_crypto_enc_t *enc; u_char hash1[32], digest[32]; this = njs_argument(args, 0); if (!hmac) { dgst = njs_vm_external(vm, njs_crypto_hash_proto_id, this); if (njs_slow_path(dgst == NULL)) { njs_vm_type_error(vm, "\"this\" is not a hash object"); return NJS_ERROR; } if (njs_slow_path(dgst->alg == NULL)) { goto exception; } ctx = NULL; } else { ctx = njs_vm_external(vm, njs_crypto_hmac_proto_id, this); if (njs_slow_path(ctx == NULL)) { njs_vm_type_error(vm, "\"this\" is not a hmac object"); return NJS_ERROR; } if (njs_slow_path(ctx->alg == NULL)) { goto exception; } dgst = NULL; } enc = njs_crypto_encoding(vm, njs_arg(args, nargs, 1)); if (njs_slow_path(enc == NULL)) { return NJS_ERROR; } if (!hmac) { alg = dgst->alg; alg->final(digest, &dgst->ctx); dgst->alg = NULL; } else { alg = ctx->alg; alg->final(hash1, &ctx->ctx); alg->init(&ctx->ctx); alg->update(&ctx->ctx, ctx->opad, 64); alg->update(&ctx->ctx, hash1, alg->size); alg->final(digest, &ctx->ctx); ctx->alg = NULL; } str.start = digest; str.length = alg->size; return enc->encode(vm, retval, &str); exception: njs_vm_error(vm, "Digest already called"); return NJS_ERROR; } static njs_int_t njs_hash_prototype_copy(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_digest_t *dgst, *copy; dgst = njs_vm_external(vm, njs_crypto_hash_proto_id, njs_argument(args, 0)); if (njs_slow_path(dgst == NULL)) { njs_vm_type_error(vm, "\"this\" is not a hash object"); return NJS_ERROR; } if (njs_slow_path(dgst->alg == NULL)) { njs_vm_error(vm, "Digest already called"); return NJS_ERROR; } copy = njs_mp_alloc(njs_vm_memory_pool(vm), sizeof(njs_digest_t)); if (njs_slow_path(copy == NULL)) { njs_vm_memory_error(vm); return NJS_ERROR; } memcpy(copy, dgst, sizeof(njs_digest_t)); return njs_vm_external_create(vm, retval, njs_crypto_hash_proto_id, copy, 0); } static njs_int_t njs_crypto_create_hmac(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_str_t key; njs_uint_t i; njs_hmac_t *ctx; njs_value_t *value; njs_hash_alg_t *alg; njs_opaque_value_t result; const njs_buffer_encoding_t *enc; u_char digest[32], key_buf[64]; alg = njs_crypto_algorithm(vm, njs_arg(args, nargs, 1)); if (njs_slow_path(alg == NULL)) { return NJS_ERROR; } value = njs_arg(args, nargs, 2); if (njs_value_is_string(value)) { enc = njs_buffer_encoding(vm, njs_value_arg(&njs_value_undefined), 1); if (njs_slow_path(enc == NULL)) { return NJS_ERROR; } ret = njs_buffer_decode_string(vm, value, njs_value_arg(&result), enc); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_value_string_get(njs_value_arg(&result), &key); } else if (njs_value_is_buffer(value)) { ret = njs_value_buffer_get(vm, value, &key); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } else { njs_vm_type_error(vm, "key is not a string or Buffer-like object"); return NJS_ERROR; } ctx = njs_mp_alloc(njs_vm_memory_pool(vm), sizeof(njs_hmac_t)); if (njs_slow_path(ctx == NULL)) { njs_vm_memory_error(vm); return NJS_ERROR; } ctx->alg = alg; if (key.length > sizeof(key_buf)) { alg->init(&ctx->ctx); alg->update(&ctx->ctx, key.start, key.length); alg->final(digest, &ctx->ctx); memcpy(key_buf, digest, alg->size); njs_explicit_memzero(key_buf + alg->size, sizeof(key_buf) - alg->size); } else { memcpy(key_buf, key.start, key.length); njs_explicit_memzero(key_buf + key.length, sizeof(key_buf) - key.length); } for (i = 0; i < 64; i++) { ctx->opad[i] = key_buf[i] ^ 0x5c; } for (i = 0; i < 64; i++) { key_buf[i] ^= 0x36; } alg->init(&ctx->ctx); alg->update(&ctx->ctx, key_buf, 64); return njs_vm_external_create(vm, retval, njs_crypto_hmac_proto_id, ctx, 0); } static njs_hash_alg_t * njs_crypto_algorithm(njs_vm_t *vm, njs_value_t *value) { njs_str_t name; njs_hash_alg_t *e; if (njs_slow_path(!njs_value_is_string(value))) { njs_vm_type_error(vm, "algorithm must be a string"); return NULL; } njs_value_string_get(value, &name); for (e = &njs_hash_algorithms[0]; e->name.length != 0; e++) { if (njs_strstr_eq(&name, &e->name)) { return e; } } njs_vm_type_error(vm, "not supported algorithm: \"%V\"", &name); return NULL; } static njs_crypto_enc_t * njs_crypto_encoding(njs_vm_t *vm, njs_value_t *value) { njs_str_t name; njs_crypto_enc_t *e; if (njs_slow_path(!njs_value_is_string(value))) { if (!njs_value_is_undefined(value)) { njs_vm_type_error(vm, "encoding must be a string"); return NULL; } return &njs_encodings[0]; } njs_value_string_get(value, &name); for (e = &njs_encodings[1]; e->name.length != 0; e++) { if (njs_strstr_eq(&name, &e->name)) { return e; } } njs_vm_type_error(vm, "Unknown digest encoding: \"%V\"", &name); return NULL; } static njs_int_t njs_buffer_digest(njs_vm_t *vm, njs_value_t *value, const njs_str_t *src) { return njs_buffer_new(vm, value, src->start, src->length); } static njs_int_t njs_crypto_init(njs_vm_t *vm) { njs_int_t ret, proto_id; njs_mod_t *module; njs_opaque_value_t value; njs_crypto_hash_proto_id = njs_vm_external_prototype(vm, njs_ext_crypto_hash, njs_nitems(njs_ext_crypto_hash)); if (njs_slow_path(njs_crypto_hash_proto_id < 0)) { return NJS_ERROR; } njs_crypto_hmac_proto_id = njs_vm_external_prototype(vm, njs_ext_crypto_hmac, njs_nitems(njs_ext_crypto_hmac)); if (njs_slow_path(njs_crypto_hmac_proto_id < 0)) { return NJS_ERROR; } proto_id = njs_vm_external_prototype(vm, njs_ext_crypto_crypto, njs_nitems(njs_ext_crypto_crypto)); if (njs_slow_path(proto_id < 0)) { return NJS_ERROR; } ret = njs_vm_external_create(vm, njs_value_arg(&value), proto_id, NULL, 1); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } module = njs_vm_add_module(vm, &njs_str_value("crypto"), njs_value_arg(&value)); if (njs_slow_path(module == NULL)) { return NJS_ERROR; } return NJS_OK; } njs-0.8.9/external/njs_fs_module.c000066400000000000000000003074641474132077100171630ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #include #include #include #include #include #if (NJS_SOLARIS) #define DT_DIR 0 #define DT_REG 1 #define DT_CHR 2 #define DT_LNK 3 #define DT_BLK 4 #define DT_FIFO 5 #define DT_SOCK 6 #define NJS_DT_INVALID 0xffffffff #define njs_dentry_type(_dentry) \ (NJS_DT_INVALID) #else #define NJS_DT_INVALID 0xffffffff #define njs_dentry_type(_dentry) \ ((_dentry)->d_type) #endif #define njs_fs_magic(calltype, mode) \ (((mode) << 2) | calltype) #define njs_fs_magic2(field, type) \ (((type) << 4) | field) typedef enum { NJS_FS_DIRECT, NJS_FS_PROMISE, NJS_FS_CALLBACK, } njs_fs_calltype_t; typedef enum { NJS_FS_TRUNC, NJS_FS_APPEND, } njs_fs_writemode_t; typedef enum { NJS_FS_STAT, NJS_FS_LSTAT, NJS_FS_FSTAT, } njs_fs_statmode_t; typedef struct { njs_str_t name; int value; } njs_fs_entry_t; typedef enum { NJS_FTW_PHYS = 1, NJS_FTW_MOUNT = 2, NJS_FTW_DEPTH = 8, } njs_ftw_flags_t; typedef enum { NJS_FTW_F, NJS_FTW_D, NJS_FTW_DNR, NJS_FTW_NS, NJS_FTW_SL, NJS_FTW_DP, NJS_FTW_SLN, } njs_ftw_type_t; typedef struct { long tv_sec; long tv_nsec; } njs_timespec_t; typedef struct { uint64_t st_dev; uint64_t st_mode; uint64_t st_nlink; uint64_t st_uid; uint64_t st_gid; uint64_t st_rdev; uint64_t st_ino; uint64_t st_size; uint64_t st_blksize; uint64_t st_blocks; njs_timespec_t st_atim; njs_timespec_t st_mtim; njs_timespec_t st_ctim; njs_timespec_t st_birthtim; } njs_stat_t; typedef enum { NJS_FS_STAT_DEV, NJS_FS_STAT_INO, NJS_FS_STAT_MODE, NJS_FS_STAT_NLINK, NJS_FS_STAT_UID, NJS_FS_STAT_GID, NJS_FS_STAT_RDEV, NJS_FS_STAT_SIZE, NJS_FS_STAT_BLKSIZE, NJS_FS_STAT_BLOCKS, NJS_FS_STAT_ATIME, NJS_FS_STAT_BIRTHTIME, NJS_FS_STAT_CTIME, NJS_FS_STAT_MTIME, } njs_stat_prop_t; typedef struct { njs_int_t fd; njs_vm_t *vm; } njs_filehandle_t; typedef struct { njs_int_t bytes; njs_opaque_value_t buffer; } njs_bytes_struct_t; typedef njs_int_t (*njs_file_tree_walk_cb_t)(const char *, const struct stat *, njs_ftw_type_t); static njs_int_t njs_fs_access(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval); static njs_int_t njs_fs_exists_sync(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval); static njs_int_t njs_fs_mkdir(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval); static njs_int_t njs_fs_open(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval); static njs_int_t njs_fs_close(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval); static njs_int_t njs_fs_read(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval); static njs_int_t njs_fs_read_file(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval); static njs_int_t njs_fs_readdir(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval); static njs_int_t njs_fs_readlink(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval); static njs_int_t njs_fs_realpath(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval); static njs_int_t njs_fs_rename(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval); static njs_int_t njs_fs_rmdir(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval); static njs_int_t njs_fs_stat(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval); static njs_int_t njs_fs_symlink(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval); static njs_int_t njs_fs_unlink(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval); static njs_int_t njs_fs_write(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval); static njs_int_t njs_fs_write_file(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval); static njs_int_t njs_fs_constant(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t njs_fs_dirent_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_fs_dirent_test(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t testtype, njs_value_t *retval); static njs_int_t njs_fs_stats_test(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t testtype, njs_value_t *retval); static njs_int_t njs_fs_stats_prop(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t njs_fs_stats_create(njs_vm_t *vm, struct stat *st, njs_value_t *retval); static njs_int_t njs_fs_filehandle_close(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_fs_filehandle_value_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_fs_filehandle_create(njs_vm_t *vm, int fd, njs_bool_t shadow, njs_opaque_value_t *retval); static njs_int_t njs_fs_bytes_read_create(njs_vm_t *vm, int bytes, njs_value_t *buffer, njs_opaque_value_t *retval); static njs_int_t njs_fs_bytes_written_create(njs_vm_t *vm, int bytes, njs_value_t *buffer, njs_opaque_value_t *retval); static njs_int_t njs_fs_fd_read(njs_vm_t *vm, int fd, njs_str_t *data); static njs_int_t njs_fs_error(njs_vm_t *vm, const char *syscall, const char *desc, const char *path, int errn, njs_opaque_value_t *result); static njs_int_t njs_fs_result(njs_vm_t *vm, njs_opaque_value_t *result, njs_index_t calltype, const njs_value_t* callback, njs_uint_t nargs, njs_value_t *retval); static njs_int_t njs_file_tree_walk(const char *path, njs_file_tree_walk_cb_t cb, int fd_limit, njs_ftw_flags_t flags); static njs_int_t njs_fs_make_path(njs_vm_t *vm, char *path, mode_t md, njs_bool_t recursive, njs_opaque_value_t *retval); static njs_int_t njs_fs_rmtree(njs_vm_t *vm, const char *path, njs_bool_t recursive, njs_opaque_value_t *retval); static const char *njs_fs_path(njs_vm_t *vm, char storage[NJS_MAX_PATH + 1], njs_value_t *src, const char *prop_name); static int njs_fs_flags(njs_vm_t *vm, njs_value_t *value, int default_flags); static mode_t njs_fs_mode(njs_vm_t *vm, njs_value_t *value, mode_t default_mode); static njs_int_t njs_fs_dirent_create(njs_vm_t *vm, njs_value_t *name, njs_value_t *type, njs_value_t *retval); static njs_int_t njs_fs_init(njs_vm_t *vm); static const njs_str_t string_flag = njs_str("flag"); static const njs_str_t string_mode = njs_str("mode"); static const njs_str_t string_buffer = njs_str("buffer"); static const njs_str_t string_encoding = njs_str("encoding"); static const njs_str_t string_recursive = njs_str("recursive"); static njs_fs_entry_t njs_flags_table[] = { { njs_str("a"), O_APPEND | O_CREAT | O_WRONLY }, { njs_str("a+"), O_APPEND | O_CREAT | O_RDWR }, { njs_str("as"), O_APPEND | O_CREAT | O_SYNC | O_WRONLY }, { njs_str("as+"), O_APPEND | O_CREAT | O_RDWR | O_SYNC }, { njs_str("ax"), O_APPEND | O_CREAT | O_EXCL | O_WRONLY }, { njs_str("ax+"), O_APPEND | O_CREAT | O_EXCL | O_RDWR }, { njs_str("r"), O_RDONLY }, { njs_str("r+"), O_RDWR }, { njs_str("rs+"), O_RDWR | O_SYNC }, { njs_str("w"), O_CREAT | O_TRUNC | O_WRONLY }, { njs_str("w+"), O_CREAT | O_TRUNC | O_RDWR }, { njs_str("wx"), O_CREAT | O_TRUNC | O_EXCL | O_WRONLY }, { njs_str("wx+"), O_CREAT | O_TRUNC | O_EXCL | O_RDWR }, { njs_null_str, 0 } }; static njs_external_t njs_ext_fs_constants[] = { { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("F_OK"), .enumerable = 1, .u.property = { .handler = njs_fs_constant, .magic32 = F_OK, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("R_OK"), .enumerable = 1, .u.property = { .handler = njs_fs_constant, .magic32 = R_OK, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("W_OK"), .enumerable = 1, .u.property = { .handler = njs_fs_constant, .magic32 = W_OK, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("X_OK"), .enumerable = 1, .u.property = { .handler = njs_fs_constant, .magic32 = X_OK, } }, }; static njs_external_t njs_ext_fs_promises[] = { { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("access"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_access, .magic8 = NJS_FS_PROMISE, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("appendFile"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_write_file, .magic8 = njs_fs_magic(NJS_FS_PROMISE, NJS_FS_APPEND), } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("close"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_close, .magic8 = NJS_FS_PROMISE, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("fstat"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_stat, .magic8 = njs_fs_magic(NJS_FS_PROMISE, NJS_FS_FSTAT), } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("mkdir"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_mkdir, .magic8 = NJS_FS_PROMISE, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("lstat"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_stat, .magic8 = njs_fs_magic(NJS_FS_PROMISE, NJS_FS_LSTAT), } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("open"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_open, .magic8 = NJS_FS_PROMISE, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("readFile"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_read_file, .magic8 = NJS_FS_PROMISE, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("readdir"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_readdir, .magic8 = NJS_FS_PROMISE, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("readlink"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_readlink, .magic8 = NJS_FS_PROMISE, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("realpath"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_realpath, .magic8 = NJS_FS_PROMISE, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("rename"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_rename, .magic8 = NJS_FS_PROMISE, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("rmdir"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_rmdir, .magic8 = NJS_FS_PROMISE, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("stat"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_stat, .magic8 = njs_fs_magic(NJS_FS_PROMISE, NJS_FS_STAT), } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("symlink"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_symlink, .magic8 = NJS_FS_PROMISE, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("unlink"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_unlink, .magic8 = NJS_FS_PROMISE, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("writeFile"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_write_file, .magic8 = njs_fs_magic(NJS_FS_PROMISE, NJS_FS_TRUNC), } }, }; static njs_external_t njs_ext_fs[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "fs", } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("access"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_access, .magic8 = NJS_FS_CALLBACK, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("accessSync"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_access, .magic8 = NJS_FS_DIRECT, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("appendFile"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_write_file, .magic8 = njs_fs_magic(NJS_FS_CALLBACK, NJS_FS_APPEND), } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("appendFileSync"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_write_file, .magic8 = njs_fs_magic(NJS_FS_DIRECT, NJS_FS_APPEND), } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("closeSync"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_close, .magic8 = NJS_FS_DIRECT, } }, { .flags = NJS_EXTERN_OBJECT, .name.string = njs_str("constants"), .writable = 1, .enumerable = 1, .configurable = 1, .u.object = { .properties = njs_ext_fs_constants, .nproperties = njs_nitems(njs_ext_fs_constants), } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("Dirent"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_dirent_constructor, .ctor = 1, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("existsSync"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_exists_sync, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("fstatSync"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_stat, .magic8 = njs_fs_magic(NJS_FS_DIRECT, NJS_FS_FSTAT), } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("lstat"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_stat, .magic8 = njs_fs_magic(NJS_FS_CALLBACK, NJS_FS_LSTAT), } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("lstatSync"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_stat, .magic8 = njs_fs_magic(NJS_FS_DIRECT, NJS_FS_LSTAT), } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("mkdir"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_mkdir, .magic8 = NJS_FS_CALLBACK, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("mkdirSync"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_mkdir, .magic8 = NJS_FS_DIRECT, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("openSync"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_open, .magic8 = NJS_FS_DIRECT, } }, { .flags = NJS_EXTERN_OBJECT, .name.string = njs_str("promises"), .writable = 1, .enumerable = 1, .configurable = 1, .u.object = { .properties = njs_ext_fs_promises, .nproperties = njs_nitems(njs_ext_fs_promises), } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("readdir"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_readdir, .magic8 = NJS_FS_CALLBACK, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("readdirSync"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_readdir, .magic8 = NJS_FS_DIRECT, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("readFile"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_read_file, .magic8 = NJS_FS_CALLBACK, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("readFileSync"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_read_file, .magic8 = NJS_FS_DIRECT, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("readSync"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_read, .magic8 = NJS_FS_DIRECT, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("readlink"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_readlink, .magic8 = NJS_FS_CALLBACK, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("readlinkSync"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_readlink, .magic8 = NJS_FS_DIRECT, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("realpath"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_realpath, .magic8 = NJS_FS_CALLBACK, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("realpathSync"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_realpath, .magic8 = NJS_FS_DIRECT, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("rename"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_rename, .magic8 = NJS_FS_CALLBACK, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("renameSync"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_rename, .magic8 = NJS_FS_DIRECT, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("rmdir"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_rmdir, .magic8 = NJS_FS_CALLBACK, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("rmdirSync"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_rmdir, .magic8 = NJS_FS_DIRECT, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("stat"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_stat, .magic8 = njs_fs_magic(NJS_FS_CALLBACK, NJS_FS_STAT), } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("statSync"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_stat, .magic8 = njs_fs_magic(NJS_FS_DIRECT, NJS_FS_STAT), } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("symlink"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_symlink, .magic8 = NJS_FS_CALLBACK, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("symlinkSync"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_symlink, .magic8 = NJS_FS_DIRECT, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("unlink"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_unlink, .magic8 = NJS_FS_CALLBACK, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("unlinkSync"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_unlink, .magic8 = NJS_FS_DIRECT, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("writeFile"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_write_file, .magic8 = njs_fs_magic(NJS_FS_CALLBACK, NJS_FS_TRUNC), } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("writeFileSync"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_write_file, .magic8 = njs_fs_magic(NJS_FS_DIRECT, NJS_FS_TRUNC), } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("writeSync"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_write, .magic8 = NJS_FS_DIRECT, } }, }; static njs_external_t njs_ext_dirent[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "Dirent", } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("constructor"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_dirent_constructor, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("isBlockDevice"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_dirent_test, .magic8 = DT_BLK, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("isCharacterDevice"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_dirent_test, .magic8 = DT_CHR, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("isDirectory"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_dirent_test, .magic8 = DT_DIR, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("isFIFO"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_dirent_test, .magic8 = DT_FIFO, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("isFile"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_dirent_test, .magic8 = DT_REG, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("isSocket"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_dirent_test, .magic8 = DT_SOCK, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("isSymbolicLink"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_dirent_test, .magic8 = DT_LNK, } }, }; static njs_external_t njs_ext_stats[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "Stats", } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("atime"), .enumerable = 1, .u.property = { .handler = njs_fs_stats_prop, .magic32 = njs_fs_magic2(NJS_FS_STAT_ATIME, 1), } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("atimeMs"), .enumerable = 1, .u.property = { .handler = njs_fs_stats_prop, .magic32 = njs_fs_magic2(NJS_FS_STAT_ATIME, 0), } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("birthtime"), .enumerable = 1, .u.property = { .handler = njs_fs_stats_prop, .magic32 = njs_fs_magic2(NJS_FS_STAT_BIRTHTIME, 1), } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("birthtimeMs"), .enumerable = 1, .u.property = { .handler = njs_fs_stats_prop, .magic32 = njs_fs_magic2(NJS_FS_STAT_BIRTHTIME, 0), } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("ctime"), .enumerable = 1, .u.property = { .handler = njs_fs_stats_prop, .magic32 = njs_fs_magic2(NJS_FS_STAT_CTIME, 1), } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("ctimeMs"), .enumerable = 1, .u.property = { .handler = njs_fs_stats_prop, .magic32 = njs_fs_magic2(NJS_FS_STAT_CTIME, 0), } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("blksize"), .enumerable = 1, .u.property = { .handler = njs_fs_stats_prop, .magic32 = njs_fs_magic2(NJS_FS_STAT_BLKSIZE, 0), } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("blocks"), .enumerable = 1, .u.property = { .handler = njs_fs_stats_prop, .magic32 = njs_fs_magic2(NJS_FS_STAT_BLOCKS, 0), } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("dev"), .enumerable = 1, .u.property = { .handler = njs_fs_stats_prop, .magic32 = njs_fs_magic2(NJS_FS_STAT_DEV, 0), } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("gid"), .enumerable = 1, .u.property = { .handler = njs_fs_stats_prop, .magic32 = njs_fs_magic2(NJS_FS_STAT_GID, 0), } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("ino"), .enumerable = 1, .u.property = { .handler = njs_fs_stats_prop, .magic32 = njs_fs_magic2(NJS_FS_STAT_INO, 0), } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("mode"), .enumerable = 1, .u.property = { .handler = njs_fs_stats_prop, .magic32 = njs_fs_magic2(NJS_FS_STAT_MODE, 0), } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("mtime"), .enumerable = 1, .u.property = { .handler = njs_fs_stats_prop, .magic32 = njs_fs_magic2(NJS_FS_STAT_MTIME, 1), } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("mtimeMs"), .enumerable = 1, .u.property = { .handler = njs_fs_stats_prop, .magic32 = njs_fs_magic2(NJS_FS_STAT_MTIME, 0), } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("nlink"), .enumerable = 1, .u.property = { .handler = njs_fs_stats_prop, .magic32 = njs_fs_magic2(NJS_FS_STAT_NLINK, 0), } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("rdev"), .enumerable = 1, .u.property = { .handler = njs_fs_stats_prop, .magic32 = njs_fs_magic2(NJS_FS_STAT_RDEV, 0), } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("size"), .enumerable = 1, .u.property = { .handler = njs_fs_stats_prop, .magic32 = njs_fs_magic2(NJS_FS_STAT_SIZE, 0), } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("uid"), .enumerable = 1, .u.property = { .handler = njs_fs_stats_prop, .magic32 = njs_fs_magic2(NJS_FS_STAT_UID, 0), } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("isBlockDevice"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_stats_test, .magic8 = DT_BLK, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("isCharacterDevice"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_stats_test, .magic8 = DT_CHR, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("isDirectory"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_stats_test, .magic8 = DT_DIR, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("isFIFO"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_stats_test, .magic8 = DT_FIFO, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("isFile"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_stats_test, .magic8 = DT_REG, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("isSocket"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_stats_test, .magic8 = DT_SOCK, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("isSymbolicLink"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_stats_test, .magic8 = DT_LNK, } }, }; static njs_external_t njs_ext_filehandle[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "FileHandle", } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("close"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_filehandle_close, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("fd"), .enumerable = 1, .u.property = { .handler = njs_external_property, .magic32 = offsetof(njs_filehandle_t, fd), .magic16 = NJS_EXTERN_TYPE_INT, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("read"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_read, .magic8 = NJS_FS_PROMISE, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("stat"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_stat, .magic8 = njs_fs_magic(NJS_FS_PROMISE, NJS_FS_FSTAT), } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("valueOf"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_filehandle_value_of, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("write"), .writable = 1, .configurable = 1, .u.method = { .native = njs_fs_write, .magic8 = NJS_FS_PROMISE, } }, }; static njs_external_t njs_ext_bytes_read[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "BytesRead", } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("buffer"), .enumerable = 1, .u.property = { .handler = njs_external_property, .magic32 = offsetof(njs_bytes_struct_t, buffer), .magic16 = NJS_EXTERN_TYPE_VALUE, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("bytesRead"), .enumerable = 1, .u.property = { .handler = njs_external_property, .magic32 = offsetof(njs_bytes_struct_t, bytes), .magic16 = NJS_EXTERN_TYPE_INT, } }, }; static njs_external_t njs_ext_bytes_written[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "BytesWritten", } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("buffer"), .enumerable = 1, .u.property = { .handler = njs_external_property, .magic32 = offsetof(njs_bytes_struct_t, buffer), .magic16 = NJS_EXTERN_TYPE_VALUE, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("bytesWritten"), .enumerable = 1, .u.property = { .handler = njs_external_property, .magic32 = offsetof(njs_bytes_struct_t, bytes), .magic16 = NJS_EXTERN_TYPE_INT, } }, }; static njs_int_t njs_fs_stats_proto_id; static njs_int_t njs_fs_dirent_proto_id; static njs_int_t njs_fs_filehandle_proto_id; static njs_int_t njs_fs_bytes_read_proto_id; static njs_int_t njs_fs_bytes_written_proto_id; njs_module_t njs_fs_module = { .name = njs_str("fs"), .preinit = NULL, .init = njs_fs_init, }; static njs_int_t njs_fs_access(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval) { int md; njs_int_t ret; const char *path; njs_value_t *callback, *mode; njs_opaque_value_t result; char path_buf[NJS_MAX_PATH + 1]; path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "path"); if (njs_slow_path(path == NULL)) { return NJS_ERROR; } callback = NULL; mode = njs_arg(args, nargs, 2); if (calltype == NJS_FS_CALLBACK) { callback = njs_arg(args, nargs, njs_min(nargs - 1, 3)); if (!njs_value_is_function(callback)) { njs_vm_type_error(vm, "\"callback\" must be a function"); return NJS_ERROR; } if (mode == callback) { mode = njs_value_arg(&njs_value_undefined); } } if (njs_value_is_number(mode)) { md = njs_value_number(mode); } else if (njs_value_is_undefined(mode)) { md = F_OK; } else { njs_vm_type_error(vm, "\"mode\" must be a number"); return NJS_ERROR; } njs_value_undefined_set(njs_value_arg(&result)); ret = access(path, md); if (njs_slow_path(ret != 0)) { ret = njs_fs_error(vm, "access", strerror(errno), path, errno, &result); } if (ret == NJS_OK) { return njs_fs_result(vm, &result, calltype, callback, 1, retval); } return NJS_ERROR; } static njs_int_t njs_fs_exists_sync(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval) { const char *path; char path_buf[NJS_MAX_PATH + 1]; path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "path"); if (njs_slow_path(path == NULL)) { return NJS_ERROR; } njs_value_boolean_set(retval, access(path, F_OK) == 0); return NJS_OK; } static njs_int_t njs_fs_open(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval) { int fd, flags; mode_t md; njs_int_t ret; const char *path; njs_value_t *value; njs_opaque_value_t result; char path_buf[NJS_MAX_PATH + 1]; path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "path"); if (njs_slow_path(path == NULL)) { return NJS_ERROR; } value = njs_arg(args, nargs, 2); if (njs_value_is_function(value)) { value = njs_value_arg(&njs_value_undefined); } flags = njs_fs_flags(vm, value, O_RDONLY); if (njs_slow_path(flags == -1)) { return NJS_ERROR; } value = njs_arg(args, nargs, 3); if (njs_value_is_function(value)) { value = njs_value_arg(&njs_value_undefined); } md = njs_fs_mode(vm, value, 0666); if (njs_slow_path(md == (mode_t) -1)) { return NJS_ERROR; } fd = open(path, flags, md); if (njs_slow_path(fd < 0)) { ret = njs_fs_error(vm, "open", strerror(errno), path, errno, &result); goto done; } ret = njs_fs_filehandle_create(vm, fd, calltype == NJS_FS_DIRECT, &result); if (njs_slow_path(ret != NJS_OK)) { goto done; } if (calltype == NJS_FS_DIRECT) { njs_value_number_set(njs_value_arg(&result), fd); } done: if (ret == NJS_OK) { return njs_fs_result(vm, &result, calltype, NULL, 2, retval); } if (fd != -1) { (void) close(fd); } return NJS_ERROR; } static njs_int_t njs_fs_close(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval) { int64_t fd; njs_int_t ret; njs_value_t *fh; njs_opaque_value_t result; fh = njs_arg(args, nargs, 1); ret = njs_value_to_integer(vm, fh, &fd); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_value_undefined_set(njs_value_arg(&result)); ret = close((int) fd); if (njs_slow_path(ret != 0)) { ret = njs_fs_error(vm, "close", strerror(errno), NULL, errno, &result); } if (ret == NJS_OK) { return njs_fs_result(vm, &result, calltype, NULL, 1, retval); } return NJS_ERROR; } static njs_int_t njs_fs_mkdir(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval) { char *path; mode_t md; njs_int_t ret; njs_value_t *callback, *options; njs_opaque_value_t mode, recursive, result; char path_buf[NJS_MAX_PATH + 1]; path = (char *) njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "path"); if (njs_slow_path(path == NULL)) { return NJS_ERROR; } callback = NULL; options = njs_arg(args, nargs, 2); if (njs_slow_path(calltype == NJS_FS_CALLBACK)) { callback = njs_arg(args, nargs, njs_min(nargs - 1, 3)); if (!njs_value_is_function(callback)) { njs_vm_type_error(vm, "\"callback\" must be a function"); return NJS_ERROR; } if (options == callback) { options = njs_value_arg(&njs_value_undefined); } } njs_value_undefined_set(njs_value_arg(&mode)); njs_value_boolean_set(njs_value_arg(&recursive), 0); if (njs_value_is_number(options)) { njs_value_assign(&mode, options); } else if (!njs_value_is_undefined(options)) { if (!njs_value_is_object(options)) { njs_vm_type_error(vm, "Unknown options type" "(a number or object required)"); return NJS_ERROR; } (void) njs_vm_object_prop(vm, options, &string_recursive, &recursive); (void) njs_vm_object_prop(vm, options, &string_mode, &mode); } md = njs_fs_mode(vm, njs_value_arg(&mode), 0777); if (njs_slow_path(md == (mode_t) -1)) { return NJS_ERROR; } ret = njs_fs_make_path(vm, path, md, njs_value_bool(njs_value_arg(&recursive)), &result); if (ret == NJS_OK) { return njs_fs_result(vm, &result, calltype, callback, 1, retval); } return NJS_ERROR; } static njs_int_t njs_fs_read(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval) { int64_t fd, length, pos, offset; ssize_t n; njs_int_t ret; njs_str_t data; njs_uint_t fd_offset; njs_value_t *buffer, *value; njs_opaque_value_t result; fd_offset = !!(calltype == NJS_FS_DIRECT); ret = njs_value_to_integer(vm, njs_arg(args, nargs, fd_offset), &fd); if (njs_slow_path(ret != NJS_OK)) { return ret; } pos = -1; /* * fh.read(buffer, offset[, length[, position]]) * fs.readSync(fd, buffer, offset[, length[, position]]) */ buffer = njs_arg(args, nargs, fd_offset + 1); ret = njs_value_buffer_get(vm, buffer, &data); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_value_to_integer(vm, njs_arg(args, nargs, fd_offset + 2), &offset); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_slow_path(offset < 0 || (size_t) offset > data.length)) { njs_vm_range_error(vm, "offset is out of range (must be <= %z)", data.length); return NJS_ERROR; } data.length -= offset; data.start += offset; value = njs_arg(args, nargs, fd_offset + 3); if (!njs_value_is_undefined(value)) { ret = njs_value_to_integer(vm, value, &length); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_slow_path(length < 0 || (size_t) length > data.length)) { njs_vm_range_error(vm, "length is out of range (must be <= %z)", data.length); return NJS_ERROR; } data.length = length; } value = njs_arg(args, nargs, fd_offset + 4); if (!njs_value_is_null_or_undefined(value)) { ret = njs_value_to_integer(vm, value, &pos); if (njs_slow_path(ret != NJS_OK)) { return ret; } } if (pos == -1) { n = read(fd, data.start, data.length); } else { n = pread(fd, data.start, data.length, pos); } if (njs_slow_path(n == -1)) { ret = njs_fs_error(vm, "read", strerror(errno), NULL, errno, &result); goto done; } if (calltype == NJS_FS_PROMISE) { ret = njs_fs_bytes_read_create(vm, n, buffer, &result); if (njs_slow_path(ret != NJS_OK)) { goto done; } } else { njs_value_number_set(njs_value_arg(&result), n); } done: if (ret == NJS_OK) { return njs_fs_result(vm, &result, calltype, NULL, 1, retval); } return NJS_ERROR; } static njs_int_t njs_fs_read_file(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval) { int fd, flags; njs_str_t data; njs_int_t ret; const char *path; njs_value_t *callback, *options; struct stat sb; njs_opaque_value_t flag, result, encode; const njs_buffer_encoding_t *encoding; char path_buf[NJS_MAX_PATH + 1]; path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "path"); if (njs_slow_path(path == NULL)) { return NJS_ERROR; } callback = NULL; options = njs_arg(args, nargs, 2); if (calltype == NJS_FS_CALLBACK) { callback = njs_arg(args, nargs, njs_min(nargs - 1, 3)); if (!njs_value_is_function(callback)) { njs_vm_type_error(vm, "\"callback\" must be a function"); return NJS_ERROR; } if (options == callback) { options = njs_value_arg(&njs_value_undefined); } } njs_value_undefined_set(njs_value_arg(&flag)); njs_value_undefined_set(njs_value_arg(&encode)); if (njs_value_is_string(options)) { njs_value_assign(&encode, options); } else if (!njs_value_is_undefined(options)) { if (!njs_value_is_object(options)) { njs_vm_type_error(vm, "Unknown options type " "(a string or object required)"); return NJS_ERROR; } (void) njs_vm_object_prop(vm, options, &string_flag, &flag); (void) njs_vm_object_prop(vm, options, &string_encoding, &encode); } flags = njs_fs_flags(vm, njs_value_arg(&flag), O_RDONLY); if (njs_slow_path(flags == -1)) { return NJS_ERROR; } encoding = NULL; if (!njs_value_is_undefined(njs_value_arg(&encode))) { encoding = njs_buffer_encoding(vm, njs_value_arg(&encode), 1); if (njs_slow_path(encoding == NULL)) { return NJS_ERROR; } } fd = open(path, flags); if (njs_slow_path(fd < 0)) { ret = njs_fs_error(vm, "open", strerror(errno), path, errno, &result); goto done; } ret = fstat(fd, &sb); if (njs_slow_path(ret == -1)) { ret = njs_fs_error(vm, "stat", strerror(errno), path, errno, &result); goto done; } if (njs_slow_path(!S_ISREG(sb.st_mode))) { ret = njs_fs_error(vm, "stat", "File is not regular", path, 0, &result); goto done; } data.start = NULL; data.length = sb.st_size; ret = njs_fs_fd_read(vm, fd, &data); if (njs_slow_path(ret != NJS_OK)) { if (ret == NJS_DECLINED) { ret = njs_fs_error(vm, "read", strerror(errno), path, errno, &result); } goto done; } if (encoding == NULL) { ret = njs_buffer_set(vm, njs_value_arg(&result), data.start, data.length); } else { ret = encoding->encode(vm, njs_value_arg(&result), &data); njs_mp_free(njs_vm_memory_pool(vm), data.start); } done: if (fd != -1) { (void) close(fd); } if (ret == NJS_OK) { return njs_fs_result(vm, &result, calltype, callback, 2, retval); } return NJS_ERROR; } static njs_int_t njs_fs_readdir(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval) { DIR *dir; njs_str_t s; njs_int_t ret; const char *path; njs_value_t *callback, *options, *value; struct dirent *entry; njs_opaque_value_t encode, types, ename, etype, result; const njs_buffer_encoding_t *encoding; char path_buf[NJS_MAX_PATH + 1]; static const njs_str_t string_types = njs_str("withFileTypes"); path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "path"); if (njs_slow_path(path == NULL)) { return NJS_ERROR; } callback = NULL; options = njs_arg(args, nargs, 2); if (njs_slow_path(calltype == NJS_FS_CALLBACK)) { callback = njs_arg(args, nargs, njs_min(nargs - 1, 3)); if (!njs_value_is_function(callback)) { njs_vm_type_error(vm, "\"callback\" must be a function"); return NJS_ERROR; } if (options == callback) { options = njs_value_arg(&njs_value_undefined); } } njs_value_boolean_set(njs_value_arg(&types), 0); njs_value_undefined_set(njs_value_arg(&encode)); if (njs_value_is_string(options)) { njs_value_assign(&encode, options); } else if (!njs_value_is_undefined(options)) { if (!njs_value_is_object(options)) { njs_vm_type_error(vm, "Unknown options type " "(a string or object required)"); return NJS_ERROR; } (void) njs_vm_object_prop(vm, options, &string_encoding, &encode); (void) njs_vm_object_prop(vm, options, &string_types, &types); } encoding = NULL; if (njs_value_is_string(njs_value_arg(&encode))) { njs_value_string_get(njs_value_arg(&encode), &s); } else { s.length = 0; s.start = NULL; } if (!njs_strstr_eq(&s, &string_buffer)) { encoding = njs_buffer_encoding(vm, njs_value_arg(&encode), 1); if (njs_slow_path(encoding == NULL)) { return NJS_ERROR; } } ret = njs_vm_array_alloc(vm, njs_value_arg(&result), 8); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } dir = opendir(path); if (njs_slow_path(dir == NULL)) { ret = njs_fs_error(vm, "opendir", strerror(errno), path, errno, &result); goto done; } ret = NJS_OK; for ( ;; ) { errno = 0; entry = readdir(dir); if (njs_slow_path(entry == NULL)) { if (errno != 0) { ret = njs_fs_error(vm, "readdir", strerror(errno), path, errno, &result); } goto done; } s.start = (u_char *) entry->d_name; s.length = njs_strlen(s.start); if ((s.length == 1 && s.start[0] == '.') || (s.length == 2 && (s.start[0] == '.' && s.start[1] == '.'))) { continue; } value = njs_vm_array_push(vm, njs_value_arg(&result)); if (njs_slow_path(value == NULL)) { goto done; } if (encoding == NULL) { ret = njs_buffer_set(vm, njs_value_arg(&ename), s.start, s.length); } else { ret = encoding->encode(vm, njs_value_arg(&ename), &s); } if (njs_slow_path(ret != NJS_OK)) { goto done; } if (njs_fast_path(!njs_value_bool(njs_value_arg(&types)))) { njs_value_assign(value, &ename); continue; } njs_value_number_set(njs_value_arg(&etype), njs_dentry_type(entry)); ret = njs_fs_dirent_create(vm, njs_value_arg(&ename), njs_value_arg(&etype), value); if (njs_slow_path(ret != NJS_OK)) { goto done; } } done: if (dir != NULL) { (void) closedir(dir); } if (ret == NJS_OK) { return njs_fs_result(vm, &result, calltype, callback, 2, retval); } return NJS_ERROR; } static njs_int_t njs_fs_readlink(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval) { ssize_t n; njs_int_t ret; njs_str_t s; const char *path; njs_value_t *callback, *options; njs_opaque_value_t encode, result; const njs_buffer_encoding_t *encoding; char path_buf[NJS_MAX_PATH + 1], dst_buf[NJS_MAX_PATH + 1]; path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "path"); if (njs_slow_path(path == NULL)) { return NJS_ERROR; } callback = NULL; options = njs_arg(args, nargs, 2); if (calltype == NJS_FS_CALLBACK) { callback = njs_arg(args, nargs, njs_min(nargs - 1, 3)); if (!njs_value_is_function(callback)) { njs_vm_type_error(vm, "\"callback\" must be a function"); return NJS_ERROR; } if (options == callback) { options = njs_value_arg(&njs_value_undefined); } } njs_value_undefined_set(njs_value_arg(&encode)); if (njs_value_is_string(options)) { njs_value_assign(&encode, options); } else if (!njs_value_is_undefined(options)) { if (!njs_value_is_object(options)) { njs_vm_type_error(vm, "Unknown options type " "(a string or object required)"); return NJS_ERROR; } (void) njs_vm_object_prop(vm, options, &string_encoding, &encode); } encoding = NULL; if (njs_value_is_string(njs_value_arg(&encode))) { njs_value_string_get(njs_value_arg(&encode), &s); } else { s.length = 0; s.start = NULL; } if (!njs_strstr_eq(&s, &string_buffer)) { encoding = njs_buffer_encoding(vm, njs_value_arg(&encode), 1); if (njs_slow_path(encoding == NULL)) { return NJS_ERROR; } } s.start = (u_char *) dst_buf; n = readlink(path, dst_buf, sizeof(dst_buf) - 1); if (njs_slow_path(n < 0)) { ret = njs_fs_error(vm, "readlink", strerror(errno), path, errno, &result); goto done; } s.length = n; if (encoding == NULL) { ret = njs_buffer_new(vm, njs_value_arg(&result), s.start, s.length); } else { ret = encoding->encode(vm, njs_value_arg(&result), &s); } done: if (ret == NJS_OK) { return njs_fs_result(vm, &result, calltype, callback, 2, retval); } return NJS_ERROR; } static njs_int_t njs_fs_realpath(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval) { njs_int_t ret; njs_str_t s; const char *path; njs_value_t *callback, *options; njs_opaque_value_t encode, result; const njs_buffer_encoding_t *encoding; char path_buf[NJS_MAX_PATH + 1], dst_buf[NJS_MAX_PATH + 1]; path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "path"); if (njs_slow_path(path == NULL)) { return NJS_ERROR; } callback = NULL; options = njs_arg(args, nargs, 2); if (calltype == NJS_FS_CALLBACK) { callback = njs_arg(args, nargs, njs_min(nargs - 1, 3)); if (!njs_value_is_function(callback)) { njs_vm_type_error(vm, "\"callback\" must be a function"); return NJS_ERROR; } if (options == callback) { options = njs_value_arg(&njs_value_undefined); } } njs_value_undefined_set(njs_value_arg(&encode)); if (njs_value_is_string(options)) { njs_value_assign(&encode, options); } else if (!njs_value_is_undefined(options)) { if (!njs_value_is_object(options)) { njs_vm_type_error(vm, "Unknown options type " "(a string or object required)"); return NJS_ERROR; } (void) njs_vm_object_prop(vm, options, &string_encoding, &encode); } encoding = NULL; if (njs_value_is_string(njs_value_arg(&encode))) { njs_value_string_get(njs_value_arg(&encode), &s); } else { s.length = 0; s.start = NULL; } if (!njs_strstr_eq(&s, &string_buffer)) { encoding = njs_buffer_encoding(vm, njs_value_arg(&encode), 1); if (njs_slow_path(encoding == NULL)) { return NJS_ERROR; } } s.start = (u_char *) realpath(path, dst_buf); if (njs_slow_path(s.start == NULL)) { ret = njs_fs_error(vm, "realpath", strerror(errno), path, errno, &result); goto done; } s.length = njs_strlen(s.start); if (encoding == NULL) { ret = njs_buffer_new(vm, njs_value_arg(&result), s.start, s.length); } else { ret = encoding->encode(vm, njs_value_arg(&result), &s); } done: if (ret == NJS_OK) { return njs_fs_result(vm, &result, calltype, callback, 2, retval); } return NJS_ERROR; } static njs_int_t njs_fs_rename(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval) { njs_int_t ret; const char *path, *newpath; njs_value_t *callback; njs_opaque_value_t result; char path_buf[NJS_MAX_PATH + 1], newpath_buf[NJS_MAX_PATH + 1]; callback = NULL; if (calltype == NJS_FS_CALLBACK) { callback = njs_arg(args, nargs, 3); if (!njs_value_is_function(callback)) { njs_vm_type_error(vm, "\"callback\" must be a function"); return NJS_ERROR; } } path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "oldPath"); if (njs_slow_path(path == NULL)) { return NJS_ERROR; } newpath = njs_fs_path(vm, newpath_buf, njs_arg(args, nargs, 2), "newPath"); if (njs_slow_path(newpath == NULL)) { return NJS_ERROR; } njs_value_undefined_set(njs_value_arg(&result)); ret = rename(path, newpath); if (njs_slow_path(ret != 0)) { ret = njs_fs_error(vm, "rename", strerror(errno), NULL, errno, &result); } if (ret == NJS_OK) { return njs_fs_result(vm, &result, calltype, callback, 1, retval); } return NJS_ERROR; } static njs_int_t njs_fs_rmdir(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval) { njs_int_t ret; const char *path; njs_value_t *callback, *options; njs_opaque_value_t recursive, result; char path_buf[NJS_MAX_PATH + 1]; path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "path"); if (njs_slow_path(path == NULL)) { return NJS_ERROR; } callback = NULL; options = njs_arg(args, nargs, 2); if (njs_slow_path(calltype == NJS_FS_CALLBACK)) { callback = njs_arg(args, nargs, njs_min(nargs - 1, 3)); if (!njs_value_is_function(callback)) { njs_vm_type_error(vm, "\"callback\" must be a function"); return NJS_ERROR; } if (options == callback) { options = njs_value_arg(&njs_value_undefined); } } njs_value_boolean_set(njs_value_arg(&recursive), 0); if (njs_slow_path(!njs_value_is_undefined(options))) { if (!njs_value_is_object(options)) { njs_vm_type_error(vm, "Unknown options type " "(an object required)"); return NJS_ERROR; } (void) njs_vm_object_prop(vm, options, &string_recursive, &recursive); } ret = njs_fs_rmtree(vm, path, njs_value_bool(njs_value_arg(&recursive)), &result); if (ret == NJS_OK) { return njs_fs_result(vm, &result, calltype, callback, 1, retval); } return NJS_ERROR; } static njs_int_t njs_fs_stat(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t magic, njs_value_t *retval) { int64_t fd; njs_int_t ret; njs_uint_t fd_offset; njs_bool_t throw; struct stat sb; const char *path; njs_value_t *callback, *options, *value; njs_opaque_value_t result; njs_fs_calltype_t calltype; char path_buf[NJS_MAX_PATH + 1]; static const njs_str_t string_bigint = njs_str("bigint"); static const njs_str_t string_throw = njs_str("throwIfNoEntry"); fd = -1; path = NULL; calltype = magic & 3; if ((magic >> 2) != NJS_FS_FSTAT) { path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "path"); if (njs_slow_path(path == NULL)) { return NJS_ERROR; } options = njs_arg(args, nargs, 2); } else { fd_offset = !!(calltype == NJS_FS_DIRECT); ret = njs_value_to_integer(vm, njs_argument(args, fd_offset), &fd); if (njs_slow_path(ret != NJS_OK)) { return ret; } options = njs_arg(args, nargs, fd_offset + 1); } callback = NULL; if (njs_slow_path(calltype == NJS_FS_CALLBACK)) { callback = njs_arg(args, nargs, njs_min(nargs - 1, 3)); if (!njs_value_is_function(callback)) { njs_vm_type_error(vm, "\"callback\" must be a function"); return NJS_ERROR; } if (options == callback) { options = njs_value_arg(&njs_value_undefined); } } throw = 1; if (!njs_value_is_undefined(options)) { if (!njs_value_is_object(options)) { njs_vm_type_error(vm, "Unknown options type " "(an object required)"); return NJS_ERROR; } value = njs_vm_object_prop(vm, options, &string_bigint, &result); if (value != NULL && njs_value_bool(value)) { njs_vm_type_error(vm, "\"bigint\" is not supported"); return NJS_ERROR; } if (calltype == NJS_FS_DIRECT) { value = njs_vm_object_prop(vm, options, &string_throw, &result); if (value != NULL) { throw = njs_value_bool(value); } } } switch (magic >> 2) { case NJS_FS_STAT: ret = stat(path, &sb); break; case NJS_FS_LSTAT: ret = lstat(path, &sb); break; case NJS_FS_FSTAT: default: ret = fstat(fd, &sb); break; } if (njs_slow_path(ret != 0)) { if (errno != ENOENT || throw) { ret = njs_fs_error(vm, ((magic >> 2) == NJS_FS_STAT) ? "stat" : "lstat", strerror(errno), path, errno, &result); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } else { njs_value_undefined_set(njs_value_arg(&result)); } return njs_fs_result(vm, &result, calltype, callback, 2, retval); } ret = njs_fs_stats_create(vm, &sb, njs_value_arg(&result)); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } return njs_fs_result(vm, &result, calltype, callback, 2, retval); } static njs_int_t njs_fs_symlink(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval) { njs_int_t ret; const char *target, *path; njs_value_t *callback, *type; njs_opaque_value_t result; char target_buf[NJS_MAX_PATH + 1], path_buf[NJS_MAX_PATH + 1]; target = njs_fs_path(vm, target_buf, njs_arg(args, nargs, 1), "target"); if (njs_slow_path(target == NULL)) { return NJS_ERROR; } path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 2), "path"); if (njs_slow_path(path == NULL)) { return NJS_ERROR; } callback = NULL; type = njs_arg(args, nargs, 3); if (calltype == NJS_FS_CALLBACK) { callback = njs_arg(args, nargs, njs_min(nargs - 1, 4)); if (!njs_value_is_function(callback)) { njs_vm_type_error(vm, "\"callback\" must be a function"); return NJS_ERROR; } if (type == callback) { type = njs_value_arg(&njs_value_undefined); } } if (njs_slow_path(!njs_value_is_undefined(type) && !njs_value_is_string(type))) { njs_vm_type_error(vm, "\"type\" must be a string"); return NJS_ERROR; } njs_value_undefined_set(njs_value_arg(&result)); ret = symlink(target, path); if (njs_slow_path(ret != 0)) { ret = njs_fs_error(vm, "symlink", strerror(errno), path, errno, &result); } if (ret == NJS_OK) { return njs_fs_result(vm, &result, calltype, callback, 1, retval); } return NJS_ERROR; } static njs_int_t njs_fs_unlink(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval) { njs_int_t ret; const char *path; njs_value_t *callback; njs_opaque_value_t result; char path_buf[NJS_MAX_PATH + 1]; path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "path"); if (njs_slow_path(path == NULL)) { return NJS_ERROR; } callback = NULL; if (calltype == NJS_FS_CALLBACK) { callback = njs_arg(args, nargs, 2); if (!njs_value_is_function(callback)) { njs_vm_type_error(vm, "\"callback\" must be a function"); return NJS_ERROR; } } njs_value_undefined_set(njs_value_arg(&result)); ret = unlink(path); if (njs_slow_path(ret != 0)) { ret = njs_fs_error(vm, "unlink", strerror(errno), path, errno, &result); } if (ret == NJS_OK) { return njs_fs_result(vm, &result, calltype, callback, 1, retval); } return NJS_ERROR; } static njs_int_t njs_fs_write(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t calltype, njs_value_t *retval) { int64_t fd, length, pos, offset; ssize_t n; njs_int_t ret; njs_str_t data; njs_uint_t fd_offset; njs_value_t *buffer, *value; njs_opaque_value_t result; const njs_buffer_encoding_t *encoding; fd_offset = !!(calltype == NJS_FS_DIRECT); ret = njs_value_to_integer(vm, njs_arg(args, nargs, fd_offset), &fd); if (njs_slow_path(ret != NJS_OK)) { return ret; } buffer = njs_arg(args, nargs, fd_offset + 1); pos = -1; encoding = NULL; /* * fs.writeSync(fd, string[, position[, encoding]]) * fh.write(string[, position[, encoding]]) */ if (njs_value_is_string(buffer)) { value = njs_arg(args, nargs, fd_offset + 2); if (!njs_value_is_null_or_undefined(value)) { ret = njs_value_to_integer(vm, value, &pos); if (njs_slow_path(ret != NJS_OK)) { return ret; } } encoding = njs_buffer_encoding(vm, njs_arg(args, nargs, fd_offset + 3), 1); if (njs_slow_path(encoding == NULL)) { return NJS_ERROR; } ret = njs_buffer_decode_string(vm, buffer, njs_value_arg(&result), encoding); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_value_string_get(njs_value_arg(&result), &data); goto process; } /* * fh.write(buffer, offset[, length[, position]]) * fs.writeSync(fd, buffer, offset[, length[, position]]) */ ret = njs_vm_value_to_bytes(vm, &data, buffer); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_value_to_integer(vm, njs_arg(args, nargs, fd_offset + 2), &offset); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_slow_path(offset < 0 || (size_t) offset > data.length)) { njs_vm_range_error(vm, "offset is out of range (must be <= %z)", data.length); return NJS_ERROR; } data.length -= offset; data.start += offset; value = njs_arg(args, nargs, fd_offset + 3); if (!njs_value_is_undefined(value)) { ret = njs_value_to_integer(vm, value, &length); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_slow_path(length < 0 || (size_t) length > data.length)) { njs_vm_range_error(vm, "length is out of range (must be <= %z)", data.length); return NJS_ERROR; } data.length = length; } value = njs_arg(args, nargs, fd_offset + 4); if (!njs_value_is_null_or_undefined(value)) { ret = njs_value_to_integer(vm, value, &pos); if (njs_slow_path(ret != NJS_OK)) { return ret; } } process: if (pos == -1) { n = write(fd, data.start, data.length); } else { n = pwrite(fd, data.start, data.length, pos); } if (njs_slow_path(n == -1)) { ret = njs_fs_error(vm, "write", strerror(errno), NULL, errno, &result); goto done; } if (njs_slow_path((size_t) n != data.length)) { ret = njs_fs_error(vm, "write", "failed to write all the data", NULL, 0, &result); goto done; } if (calltype == NJS_FS_PROMISE) { ret = njs_fs_bytes_written_create(vm, n, buffer, &result); if (njs_slow_path(ret != NJS_OK)) { goto done; } } else { njs_value_number_set(njs_value_arg(&result), n); } done: if (ret == NJS_OK) { return njs_fs_result(vm, &result, calltype, NULL, 1, retval); } return NJS_ERROR; } static njs_int_t njs_fs_write_file(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t magic, njs_value_t *retval) { int fd, flags; u_char *p, *end; mode_t md; ssize_t n; njs_str_t content; njs_int_t ret; const char *path; njs_value_t *data, *callback, *options; njs_opaque_value_t flag, mode, encode, result; njs_fs_calltype_t calltype; const njs_buffer_encoding_t *encoding; char path_buf[NJS_MAX_PATH + 1]; path = njs_fs_path(vm, path_buf, njs_arg(args, nargs, 1), "path"); if (njs_slow_path(path == NULL)) { return NJS_ERROR; } callback = NULL; calltype = magic & 3; options = njs_arg(args, nargs, 3); if (calltype == NJS_FS_CALLBACK) { callback = njs_arg(args, nargs, njs_min(nargs - 1, 4)); if (!njs_value_is_function(callback)) { njs_vm_type_error(vm, "\"callback\" must be a function"); return NJS_ERROR; } if (options == callback) { options = njs_value_arg(&njs_value_undefined); } } njs_value_undefined_set(njs_value_arg(&flag)); njs_value_undefined_set(njs_value_arg(&mode)); njs_value_undefined_set(njs_value_arg(&encode)); if (njs_value_is_string(options)) { njs_value_assign(&encode, options); } else if (!njs_value_is_undefined(options)) { if (!njs_value_is_object(options)) { njs_vm_type_error(vm, "Unknown options type " "(a string or object required)"); return NJS_ERROR; } (void) njs_vm_object_prop(vm, options, &string_flag, &flag); (void) njs_vm_object_prop(vm, options, &string_mode, &mode); (void) njs_vm_object_prop(vm, options, &string_encoding, &encode); } data = njs_arg(args, nargs, 2); if (njs_value_is_buffer(data) || njs_value_is_data_view(data)) { ret = njs_value_buffer_get(vm, data, &content); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } else { encoding = njs_buffer_encoding(vm, njs_value_arg(&encode), 1); if (njs_slow_path(encoding == NULL)) { return NJS_ERROR; } ret = njs_value_to_string(vm, njs_value_arg(&result), data); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_buffer_decode_string(vm, njs_value_arg(&result), njs_value_arg(&result), encoding); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_value_string_get(njs_value_arg(&result), &content); } flags = njs_fs_flags(vm, njs_value_arg(&flag), O_CREAT | O_WRONLY); if (njs_slow_path(flags == -1)) { return NJS_ERROR; } flags |= ((magic >> 2) == NJS_FS_APPEND) ? O_APPEND : O_TRUNC; md = njs_fs_mode(vm, njs_value_arg(&mode), 0666); if (njs_slow_path(md == (mode_t) -1)) { return NJS_ERROR; } fd = open(path, flags, md); if (njs_slow_path(fd < 0)) { ret = njs_fs_error(vm, "open", strerror(errno), path, errno, &result); goto done; } p = content.start; end = p + content.length; while (p < end) { n = write(fd, p, end - p); if (njs_slow_path(n == -1)) { if (errno == EINTR) { continue; } ret = njs_fs_error(vm, "write", strerror(errno), path, errno, &result); goto done; } p += n; } ret = NJS_OK; njs_value_undefined_set(njs_value_arg(&result)); done: if (fd != -1) { (void) close(fd); } if (ret == NJS_OK) { return njs_fs_result(vm, &result, calltype, callback, 1, retval); } return NJS_ERROR; } static njs_int_t njs_fs_fd_read(njs_vm_t *vm, int fd, njs_str_t *data) { u_char *p, *end, *start; size_t size; ssize_t n; size = data->length; if (size == 0) { size = 4096; } data->start = njs_mp_alloc(njs_vm_memory_pool(vm), size); if (data->start == NULL) { njs_vm_memory_error(vm); return NJS_ERROR; } p = data->start; end = p + size; for ( ;; ) { n = read(fd, p, end - p); if (njs_slow_path(n < 0)) { return NJS_DECLINED; } p += n; if (n == 0) { break; } if (end - p < 2048) { size *= 2; start = njs_mp_alloc(njs_vm_memory_pool(vm), size); if (start == NULL) { njs_vm_memory_error(vm); return NJS_ERROR; } memcpy(start, data->start, p - data->start); njs_mp_free(njs_vm_memory_pool(vm), data->start); p = start + (p - data->start); end = start + size; data->start = start; } } data->length = p - data->start; return NJS_OK; } static njs_int_t njs_fs_make_path(njs_vm_t *vm, char *path, mode_t md, njs_bool_t recursive, njs_opaque_value_t *retval) { int err; njs_int_t ret; const char *p, *prev, *end; struct stat sb; njs_value_undefined_set(njs_value_arg(retval)); end = path + njs_strlen(path); if (!recursive) { ret = mkdir(path, md); if (ret != 0) { err = errno; goto failed; } return NJS_OK; } p = path; prev = p; for ( ;; ) { p = strchr(prev + 1, '/'); if (p == NULL) { p = end; } if (njs_slow_path((p - path) > NJS_MAX_PATH)) { njs_vm_internal_error(vm, "too large path"); return NJS_ERROR; } path[p - path] = '\0'; ret = mkdir(path, md); err = errno; switch (ret) { case 0: break; case EACCES: case ENOTDIR: case EPERM: goto failed; case EEXIST: default: ret = stat(path, &sb); if (ret == 0) { if (!S_ISDIR(sb.st_mode)) { err = ENOTDIR; goto failed; } break; } goto failed; } if (p == end) { break; } path[p - path] = '/'; prev = p; } return NJS_OK; failed: return njs_fs_error(vm, "mkdir", strerror(err), path, err, retval); } typedef struct njs_ftw_trace_s njs_ftw_trace_t; struct njs_ftw_trace_s { struct njs_ftw_trace_s *chain; dev_t dev; ino_t ino; }; static int njs_ftw(char *path, njs_file_tree_walk_cb_t cb, int fd_limit, njs_ftw_flags_t flags, njs_ftw_trace_t *parent) { int type, ret, dfd; DIR *d; size_t base, len, length; const char *d_name; struct stat st; struct dirent *entry; njs_ftw_trace_t trace, *h; ret = (flags & NJS_FTW_PHYS) ? lstat(path, &st) : stat(path, &st); if (ret < 0) { if (!(flags & NJS_FTW_PHYS) && errno == ENOENT && !lstat(path, &st)) { type = NJS_FTW_SLN; } else if (errno != EACCES) { return NJS_ERROR; } else { type = NJS_FTW_NS; } } else if (S_ISDIR(st.st_mode)) { type = (flags & NJS_FTW_DEPTH) ? NJS_FTW_DP : NJS_FTW_D; } else if (S_ISLNK(st.st_mode)) { type = (flags & NJS_FTW_PHYS) ? NJS_FTW_SL : NJS_FTW_SLN; } else { type = NJS_FTW_F; } if ((flags & NJS_FTW_MOUNT) && parent != NULL && st.st_dev != parent->dev) { return NJS_OK; } for (h = parent; h != NULL; h = h->chain) { if (h->dev == st.st_dev && h->ino == st.st_ino) { return NJS_OK; } } len = njs_strlen(path); base = len && (path[len - 1] == '/') ? len - 1 : len; trace.chain = parent; trace.dev = st.st_dev; trace.ino = st.st_ino; d = NULL; dfd = -1; if (type == NJS_FTW_D || type == NJS_FTW_DP) { dfd = open(path, O_RDONLY); if (dfd < 0) { if (errno != EACCES) { return NJS_ERROR; } type = NJS_FTW_DNR; } } if (!(flags & NJS_FTW_DEPTH)) { ret = cb(path, &st, type); if (njs_slow_path(ret != 0)) { goto done; } } if (type == NJS_FTW_D || type == NJS_FTW_DP) { d = fdopendir(dfd); if (njs_slow_path(d == NULL)) { ret = NJS_ERROR; goto done; } for ( ;; ) { entry = readdir(d); if (entry == NULL) { break; } d_name = entry->d_name; length = njs_strlen(d_name); if ((length == 1 && d_name[0] == '.') || (length == 2 && (d_name[0] == '.' && d_name[1] == '.'))) { continue; } if (njs_slow_path(length >= (NJS_MAX_PATH - len))) { errno = ENAMETOOLONG; ret = NJS_ERROR; goto done; } path[base] = '/'; memcpy(&path[base + 1], d_name, length + njs_length("\0")); if (fd_limit != 0) { ret = njs_ftw(path, cb, fd_limit - 1, flags, &trace); if (njs_slow_path(ret != 0)) { goto done; } } } (void) closedir(d); d = NULL; dfd = -1; } path[len] = '\0'; if (flags & NJS_FTW_DEPTH) { ret = cb(path, &st, type); if (njs_slow_path(ret != 0)) { return ret; } } ret = NJS_OK; done: if (d != NULL) { /* closedir() also closes underlying dfd. */ (void) closedir(d); } else if (dfd >= 0) { (void) close(dfd); } return ret; } static njs_int_t njs_file_tree_walk(const char *path, njs_file_tree_walk_cb_t cb, int fd_limit, njs_ftw_flags_t flags) { size_t len; char pathbuf[NJS_MAX_PATH + 1]; len = njs_strlen(path); if (njs_slow_path(len > NJS_MAX_PATH)) { errno = ENAMETOOLONG; return -1; } memcpy(pathbuf, path, len + 1); return njs_ftw(pathbuf, cb, fd_limit, flags, NULL); } static njs_int_t njs_fs_rmtree_cb(const char *path, const struct stat *sb, njs_ftw_type_t type) { njs_int_t ret; ret = remove(path); if (ret != 0) { return NJS_ERROR; } return NJS_OK; } static njs_int_t njs_fs_rmtree(njs_vm_t *vm, const char *path, njs_bool_t recursive, njs_opaque_value_t *retval) { njs_int_t ret; const char *description; njs_value_undefined_set(njs_value_arg(retval)); ret = rmdir(path); if (ret == 0) { return NJS_OK; } description = strerror(errno); if (recursive && (errno == ENOTEMPTY || errno == EEXIST)) { ret = njs_file_tree_walk(path, njs_fs_rmtree_cb, 16, NJS_FTW_PHYS | NJS_FTW_MOUNT | NJS_FTW_DEPTH); if (ret == NJS_OK) { return NJS_OK; } description = strerror(errno); } return njs_fs_error(vm, "rmdir", description, path, errno, retval); } static const char * njs_fs_path(njs_vm_t *vm, char storage[NJS_MAX_PATH + 1], njs_value_t *src, const char *prop_name) { u_char *p; njs_str_t str; njs_int_t ret; if (njs_value_is_string(src)) { njs_value_string_get(src, &str); } else if (njs_value_is_buffer(src)) { ret = njs_value_buffer_get(vm, src, &str); if (njs_slow_path(ret != NJS_OK)) { return NULL; } } else { njs_vm_type_error(vm, "\"%s\" must be a string or Buffer", prop_name); return NULL; } if (njs_slow_path(str.length > NJS_MAX_PATH - 1)) { njs_vm_internal_error(vm, "\"%s\" is too long >= %d", prop_name, NJS_MAX_PATH); return NULL; } if (njs_slow_path(memchr(str.start, '\0', str.length) != 0)) { njs_vm_type_error(vm, "\"%s\" must be a Buffer without null bytes", prop_name); return NULL; } p = njs_cpymem(storage, str.start, str.length); *p++ = '\0'; return storage; } static int njs_fs_flags(njs_vm_t *vm, njs_value_t *value, int default_flags) { njs_str_t flags; njs_int_t ret; njs_fs_entry_t *fl; if (njs_value_is_undefined(value)) { return default_flags; } ret = njs_value_to_string(vm, value, value); if (njs_slow_path(ret != NJS_OK)) { return -1; } njs_value_string_get(value, &flags); for (fl = &njs_flags_table[0]; fl->name.length != 0; fl++) { if (njs_strstr_eq(&flags, &fl->name)) { return fl->value; } } njs_vm_type_error(vm, "Unknown file open flags: \"%V\"", &flags); return -1; } static mode_t njs_fs_mode(njs_vm_t *vm, njs_value_t *value, mode_t default_mode) { int64_t i64; njs_int_t ret; /* GCC complains about uninitialized i64. */ i64 = 0; if (njs_value_is_undefined(value)) { return default_mode; } ret = njs_value_to_integer(vm, value, &i64); if (njs_slow_path(ret != NJS_OK)) { return (mode_t) -1; } return (mode_t) i64; } static njs_int_t njs_fs_error(njs_vm_t *vm, const char *syscall, const char *description, const char *path, int errn, njs_opaque_value_t *retval) { size_t size; njs_int_t ret; const char *code; njs_opaque_value_t value; static const njs_str_t string_errno = njs_str("errno"); static const njs_str_t string_code = njs_str("code"); static const njs_str_t string_path = njs_str("path"); static const njs_str_t string_syscall = njs_str("syscall"); size = description != NULL ? njs_strlen(description) : 0; njs_vm_error(vm, "%*s", size, description); njs_vm_exception_get(vm, njs_value_arg(retval)); if (errn != 0) { njs_value_number_set(njs_value_arg(&value), errn); ret = njs_vm_object_prop_set(vm, njs_value_arg(retval), &string_errno, &value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } code = njs_errno_string(errn); ret = njs_vm_value_string_create(vm, njs_value_arg(&value), (u_char *) code, njs_strlen(code)); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_vm_object_prop_set(vm, njs_value_arg(retval), &string_code, &value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } if (path != NULL) { ret = njs_vm_value_string_create(vm, njs_value_arg(&value), (u_char *) path, njs_strlen(path)); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_vm_object_prop_set(vm, njs_value_arg(retval), &string_path, &value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } if (syscall != NULL) { ret = njs_vm_value_string_create(vm, njs_value_arg(&value), (u_char *) syscall, njs_strlen(syscall)); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_vm_object_prop_set(vm, njs_value_arg(retval), &string_syscall, &value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } return NJS_OK; } static njs_int_t ngx_fs_promise_trampoline(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_function_t *callback; callback = njs_value_function(njs_argument(args, 1)); return njs_vm_call(vm, callback, njs_argument(args, 2), 1); } static njs_int_t njs_fs_result(njs_vm_t *vm, njs_opaque_value_t *result, njs_index_t calltype, const njs_value_t *callback, njs_uint_t nargs, njs_value_t *retval) { njs_int_t ret; njs_function_t *cb; njs_opaque_value_t promise, callbacks[2], arguments[2]; switch (calltype) { case NJS_FS_DIRECT: if (njs_value_is_error(njs_value_arg(result))) { njs_vm_throw(vm, njs_value_arg(result)); return NJS_ERROR; } njs_value_assign(retval, result); return NJS_OK; case NJS_FS_PROMISE: ret = njs_vm_promise_create(vm, njs_value_arg(&promise), njs_value_arg(&callbacks[0])); if (njs_slow_path(ret != NJS_OK)) { return ret; } cb = njs_vm_function_alloc(vm, ngx_fs_promise_trampoline, 0, 0); if (njs_slow_path(cb == NULL)) { return NJS_ERROR; } njs_value_assign(&arguments[0], &callbacks[njs_value_is_error(njs_value_arg(result))]); njs_value_assign(&arguments[1], result); ret = njs_vm_enqueue_job(vm, cb, njs_value_arg(&arguments), 2); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } njs_value_assign(retval, &promise); return NJS_OK; case NJS_FS_CALLBACK: if (njs_value_is_error(njs_value_arg(result))) { njs_value_assign(&arguments[0], result); njs_value_undefined_set(njs_value_arg(&arguments[1])); } else { njs_value_undefined_set(njs_value_arg(&arguments[0])); njs_value_assign(&arguments[1], result); } ret = njs_vm_enqueue_job(vm, njs_value_function(callback), njs_value_arg(&arguments), 2); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } njs_value_undefined_set(retval); return NJS_OK; default: njs_vm_internal_error(vm, "invalid calltype"); return NJS_ERROR; } } static njs_int_t njs_fs_dirent_create(njs_vm_t *vm, njs_value_t *name, njs_value_t *type, njs_value_t *retval) { njs_int_t ret; static const njs_str_t string_name = njs_str("name"); static const njs_str_t string_type = njs_str("type"); ret = njs_vm_external_create(vm, retval, njs_fs_dirent_proto_id, NULL, 0); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_vm_object_prop_set(vm, retval, &string_name, (njs_opaque_value_t *) name); if (njs_slow_path(ret != NJS_OK)) { return ret; } /* TODO: use a private symbol as a key. */ return njs_vm_object_prop_set(vm, retval, &string_type, (njs_opaque_value_t *) type); } static njs_int_t njs_fs_dirent_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { if (njs_slow_path(!njs_vm_constructor(vm))) { njs_vm_type_error(vm, "the Dirent constructor must be called with new"); return NJS_ERROR; } return njs_fs_dirent_create(vm, njs_arg(args, nargs, 1), njs_arg(args, nargs, 2), retval); } static njs_int_t njs_fs_dirent_test(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t testtype, njs_value_t *retval) { njs_value_t *type; njs_opaque_value_t lvalue; static const njs_str_t string_type = njs_str("type"); type = njs_vm_object_prop(vm, njs_argument(args, 0), &string_type, &lvalue); if (njs_slow_path(type == NULL)) { return NJS_ERROR; } if (njs_slow_path(njs_value_is_number(type) && (njs_value_number(type) == NJS_DT_INVALID))) { njs_vm_internal_error(vm, "dentry type is not supported on this " "platform"); return NJS_ERROR; } njs_value_boolean_set(retval, njs_value_is_number(type) && testtype == njs_value_number(type)); return NJS_OK; } static void njs_fs_to_stat(njs_stat_t *dst, struct stat *st) { dst->st_dev = st->st_dev; dst->st_mode = st->st_mode; dst->st_nlink = st->st_nlink; dst->st_uid = st->st_uid; dst->st_gid = st->st_gid; dst->st_rdev = st->st_rdev; dst->st_ino = st->st_ino; dst->st_size = st->st_size; dst->st_blksize = st->st_blksize; dst->st_blocks = st->st_blocks; #if (NJS_HAVE_STAT_ATIMESPEC) dst->st_atim.tv_sec = st->st_atimespec.tv_sec; dst->st_atim.tv_nsec = st->st_atimespec.tv_nsec; dst->st_mtim.tv_sec = st->st_mtimespec.tv_sec; dst->st_mtim.tv_nsec = st->st_mtimespec.tv_nsec; dst->st_ctim.tv_sec = st->st_ctimespec.tv_sec; dst->st_ctim.tv_nsec = st->st_ctimespec.tv_nsec; #elif (NJS_HAVE_STAT_ATIM) dst->st_atim.tv_sec = st->st_atim.tv_sec; dst->st_atim.tv_nsec = st->st_atim.tv_nsec; dst->st_mtim.tv_sec = st->st_mtim.tv_sec; dst->st_mtim.tv_nsec = st->st_mtim.tv_nsec; dst->st_ctim.tv_sec = st->st_ctim.tv_sec; dst->st_ctim.tv_nsec = st->st_ctim.tv_nsec; #if (NJS_HAVE_STAT_BIRTHTIM) dst->st_birthtim.tv_sec = st->st_birthtim.tv_sec; dst->st_birthtim.tv_nsec = st->st_birthtim.tv_nsec; #elif (NJS_HAVE__STAT_BIRTHTIM) dst->st_birthtim.tv_sec = st->__st_birthtim.tv_sec; dst->st_birthtim.tv_nsec = st->__st_birthtim.tv_nsec; #else dst->st_birthtim.tv_sec = st->st_ctim.tv_sec; dst->st_birthtim.tv_nsec = st->st_ctim.tv_nsec; #endif #else dst->st_atim.tv_sec = st->st_atime; dst->st_atim.tv_nsec = 0; dst->st_mtim.tv_sec = st->st_mtime; dst->st_mtim.tv_nsec = 0; dst->st_ctim.tv_sec = st->st_ctime; dst->st_ctim.tv_nsec = 0; dst->st_birthtim.tv_sec = st->st_ctime; dst->st_birthtim.tv_nsec = 0; #endif } static njs_int_t njs_fs_stats_create(njs_vm_t *vm, struct stat *st, njs_value_t *retval) { njs_stat_t *stat; stat = njs_mp_alloc(njs_vm_memory_pool(vm), sizeof(njs_stat_t)); if (njs_slow_path(stat == NULL)) { njs_vm_memory_error(vm); return NJS_ERROR; } njs_fs_to_stat(stat, st); return njs_vm_external_create(vm, retval, njs_fs_stats_proto_id, stat, 0); } static njs_int_t njs_fs_stats_test(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t testtype, njs_value_t *retval) { unsigned mask; njs_stat_t *st; st = njs_vm_external(vm, njs_fs_stats_proto_id, njs_argument(args, 0)); if (njs_slow_path(st == NULL)) { return NJS_DECLINED; } switch (testtype) { case DT_DIR: mask = S_IFDIR; break; case DT_REG: mask = S_IFREG; break; case DT_CHR: mask = S_IFCHR; break; case DT_LNK: mask = S_IFLNK; break; case DT_BLK: mask = S_IFBLK; break; case DT_FIFO: mask = S_IFIFO; break; case DT_SOCK: default: mask = S_IFSOCK; } njs_value_boolean_set(retval, (st->st_mode & S_IFMT) == mask); return NJS_OK; } static njs_int_t njs_fs_stats_prop(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { double v; njs_int_t ret; njs_stat_t *st; #define njs_fs_time_ms(ts) ((ts)->tv_sec * 1000.0 + (ts)->tv_nsec / 1000000.0) st = njs_vm_external(vm, njs_fs_stats_proto_id, value); if (njs_slow_path(st == NULL)) { return NJS_DECLINED; } switch (njs_vm_prop_magic32(prop) & 0xf) { case NJS_FS_STAT_DEV: v = st->st_dev; break; case NJS_FS_STAT_INO: v = st->st_ino; break; case NJS_FS_STAT_MODE: v = st->st_mode; break; case NJS_FS_STAT_NLINK: v = st->st_nlink; break; case NJS_FS_STAT_UID: v = st->st_uid; break; case NJS_FS_STAT_GID: v = st->st_gid; break; case NJS_FS_STAT_RDEV: v = st->st_rdev; break; case NJS_FS_STAT_SIZE: v = st->st_size; break; case NJS_FS_STAT_BLKSIZE: v = st->st_blksize; break; case NJS_FS_STAT_BLOCKS: v = st->st_blocks; break; case NJS_FS_STAT_ATIME: v = njs_fs_time_ms(&st->st_atim); break; case NJS_FS_STAT_BIRTHTIME: v = njs_fs_time_ms(&st->st_birthtim); break; case NJS_FS_STAT_CTIME: v = njs_fs_time_ms(&st->st_ctim); break; case NJS_FS_STAT_MTIME: default: v = njs_fs_time_ms(&st->st_mtim); break; } switch (njs_vm_prop_magic32(prop) >> 4) { case 0: njs_value_number_set(retval, v); break; case 1: default: ret = njs_vm_date_alloc(vm, retval, v); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } break; } return NJS_OK; } static njs_int_t njs_fs_filehandle_close(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_filehandle_t *fh; njs_opaque_value_t result; fh = njs_vm_external(vm, njs_fs_filehandle_proto_id, njs_argument(args, 0)); if (njs_slow_path(fh == NULL)) { njs_vm_type_error(vm, "\"this\" is not a filehandle object"); return NJS_ERROR; } if (njs_slow_path(fh->fd == -1)) { njs_vm_error(vm, "file was already closed"); return NJS_ERROR; } (void) close(fh->fd); fh->fd = -1; njs_value_undefined_set(njs_value_arg(&result)); return njs_fs_result(vm, &result, NJS_FS_PROMISE, NULL, 1, retval); } static njs_int_t njs_fs_filehandle_value_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_filehandle_t *fh; fh = njs_vm_external(vm, njs_fs_filehandle_proto_id, njs_argument(args, 0)); if (njs_slow_path(fh == NULL)) { njs_vm_type_error(vm, "\"this\" is not a filehandle object"); return NJS_ERROR; } njs_value_number_set(retval, fh->fd); return NJS_OK; } static void njs_fs_filehandle_cleanup(void *data) { njs_filehandle_t *fh = data; if (fh->vm != NULL && fh->fd != -1) { (void) close(fh->fd); } } static njs_int_t njs_fs_filehandle_create(njs_vm_t *vm, int fd, njs_bool_t shadow, njs_opaque_value_t *retval) { njs_filehandle_t *fh; njs_mp_cleanup_t *cln; fh = njs_mp_alloc(njs_vm_memory_pool(vm), sizeof(njs_filehandle_t)); if (njs_slow_path(fh == NULL)) { njs_vm_memory_error(vm); return NJS_ERROR; } fh->fd = fd; fh->vm = !shadow ? vm : NULL; cln = njs_mp_cleanup_add(njs_vm_memory_pool(vm), 0); if (cln == NULL) { njs_vm_memory_error(vm); return NJS_ERROR; } cln->handler = njs_fs_filehandle_cleanup; cln->data = fh; return njs_vm_external_create(vm, njs_value_arg(retval), njs_fs_filehandle_proto_id, fh, 0); } static njs_int_t njs_fs_bytes_read_create(njs_vm_t *vm, int bytes, njs_value_t *buffer, njs_opaque_value_t *retval) { njs_bytes_struct_t *bs; bs = njs_mp_alloc(njs_vm_memory_pool(vm), sizeof(njs_bytes_struct_t)); if (njs_slow_path(bs == NULL)) { njs_vm_memory_error(vm); return NJS_ERROR; } bs->bytes = bytes; njs_value_assign(&bs->buffer, buffer); return njs_vm_external_create(vm, njs_value_arg(retval), njs_fs_bytes_read_proto_id, bs, 0); } static njs_int_t njs_fs_bytes_written_create(njs_vm_t *vm, int bytes, njs_value_t *buffer, njs_opaque_value_t *retval) { njs_bytes_struct_t *bs; bs = njs_mp_alloc(njs_vm_memory_pool(vm), sizeof(njs_bytes_struct_t)); if (njs_slow_path(bs == NULL)) { njs_vm_memory_error(vm); return NJS_ERROR; } bs->bytes = bytes; njs_value_assign(&bs->buffer, buffer); return njs_vm_external_create(vm, njs_value_arg(retval), njs_fs_bytes_written_proto_id, bs, 0); } njs_int_t njs_fs_constant(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { njs_value_number_set(retval, njs_vm_prop_magic32(prop)); return NJS_OK; } static njs_int_t njs_fs_init(njs_vm_t *vm) { njs_int_t ret, proto_id; njs_mod_t *module; njs_opaque_value_t value; if (njs_vm_options(vm)->sandbox) { return NJS_OK; } njs_fs_stats_proto_id = njs_vm_external_prototype(vm, njs_ext_stats, njs_nitems(njs_ext_stats)); if (njs_slow_path(njs_fs_stats_proto_id < 0)) { return NJS_ERROR; } njs_fs_dirent_proto_id = njs_vm_external_prototype(vm, njs_ext_dirent, njs_nitems(njs_ext_dirent)); if (njs_slow_path(njs_fs_dirent_proto_id < 0)) { return NJS_ERROR; } njs_fs_filehandle_proto_id = njs_vm_external_prototype(vm, njs_ext_filehandle, njs_nitems(njs_ext_filehandle)); if (njs_slow_path(njs_fs_filehandle_proto_id < 0)) { return NJS_ERROR; } njs_fs_bytes_read_proto_id = njs_vm_external_prototype(vm, njs_ext_bytes_read, njs_nitems(njs_ext_bytes_read)); if (njs_slow_path(njs_fs_bytes_written_proto_id < 0)) { return NJS_ERROR; } njs_fs_bytes_written_proto_id = njs_vm_external_prototype(vm, njs_ext_bytes_written, njs_nitems(njs_ext_bytes_written)); if (njs_slow_path(njs_fs_bytes_written_proto_id < 0)) { return NJS_ERROR; } proto_id = njs_vm_external_prototype(vm, njs_ext_fs, njs_nitems(njs_ext_fs)); if (njs_slow_path(proto_id < 0)) { return NJS_ERROR; } ret = njs_vm_external_create(vm, njs_value_arg(&value), proto_id, NULL, 1); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } module = njs_vm_add_module(vm, &njs_str_value("fs"), njs_value_arg(&value)); if (njs_slow_path(module == NULL)) { return NJS_ERROR; } return NJS_OK; } njs-0.8.9/external/njs_hash.h000066400000000000000000000015601474132077100161220ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_HASH_H_INCLUDED_ #define _NJS_HASH_H_INCLUDED_ typedef struct { uint64_t bytes; uint32_t a, b, c, d, e, f, g, h; u_char buffer[64]; } njs_hash_t; NJS_EXPORT void njs_md5_init(njs_hash_t *ctx); NJS_EXPORT void njs_md5_update(njs_hash_t *ctx, const void *data, size_t size); NJS_EXPORT void njs_md5_final(u_char result[32], njs_hash_t *ctx); NJS_EXPORT void njs_sha1_init(njs_hash_t *ctx); NJS_EXPORT void njs_sha1_update(njs_hash_t *ctx, const void *data, size_t size); NJS_EXPORT void njs_sha1_final(u_char result[32], njs_hash_t *ctx); NJS_EXPORT void njs_sha2_init(njs_hash_t *ctx); NJS_EXPORT void njs_sha2_update(njs_hash_t *ctx, const void *data, size_t size); NJS_EXPORT void njs_sha2_final(u_char result[32], njs_hash_t *ctx); #endif /* _NJS_HASH_H_INCLUDED_ */ njs-0.8.9/external/njs_md5.c000066400000000000000000000202451474132077100156600ustar00rootroot00000000000000 /* * An internal implementation, based on Alexander Peslyak's * public domain implementation: * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 */ #include #include #include #include #include "njs_hash.h" static const u_char *njs_md5_body(njs_hash_t *ctx, const u_char *data, size_t size); void njs_md5_init(njs_hash_t *ctx) { ctx->a = 0x67452301; ctx->b = 0xefcdab89; ctx->c = 0x98badcfe; ctx->d = 0x10325476; ctx->bytes = 0; } void njs_md5_update(njs_hash_t *ctx, const void *data, size_t size) { size_t used, free; used = (size_t) (ctx->bytes & 0x3f); ctx->bytes += size; if (used) { free = 64 - used; if (size < free) { memcpy(&ctx->buffer[used], data, size); return; } memcpy(&ctx->buffer[used], data, free); data = (u_char *) data + free; size -= free; (void) njs_md5_body(ctx, ctx->buffer, 64); } if (size >= 64) { data = njs_md5_body(ctx, data, size & ~(size_t) 0x3f); size &= 0x3f; } memcpy(ctx->buffer, data, size); } void njs_md5_final(u_char result[32], njs_hash_t *ctx) { size_t used, free; used = (size_t) (ctx->bytes & 0x3f); ctx->buffer[used++] = 0x80; free = 64 - used; if (free < 8) { njs_memzero(&ctx->buffer[used], free); (void) njs_md5_body(ctx, ctx->buffer, 64); used = 0; free = 64; } njs_memzero(&ctx->buffer[used], free - 8); ctx->bytes <<= 3; ctx->buffer[56] = (u_char) ctx->bytes; ctx->buffer[57] = (u_char) (ctx->bytes >> 8); ctx->buffer[58] = (u_char) (ctx->bytes >> 16); ctx->buffer[59] = (u_char) (ctx->bytes >> 24); ctx->buffer[60] = (u_char) (ctx->bytes >> 32); ctx->buffer[61] = (u_char) (ctx->bytes >> 40); ctx->buffer[62] = (u_char) (ctx->bytes >> 48); ctx->buffer[63] = (u_char) (ctx->bytes >> 56); (void) njs_md5_body(ctx, ctx->buffer, 64); result[0] = (u_char) ctx->a; result[1] = (u_char) (ctx->a >> 8); result[2] = (u_char) (ctx->a >> 16); result[3] = (u_char) (ctx->a >> 24); result[4] = (u_char) ctx->b; result[5] = (u_char) (ctx->b >> 8); result[6] = (u_char) (ctx->b >> 16); result[7] = (u_char) (ctx->b >> 24); result[8] = (u_char) ctx->c; result[9] = (u_char) (ctx->c >> 8); result[10] = (u_char) (ctx->c >> 16); result[11] = (u_char) (ctx->c >> 24); result[12] = (u_char) ctx->d; result[13] = (u_char) (ctx->d >> 8); result[14] = (u_char) (ctx->d >> 16); result[15] = (u_char) (ctx->d >> 24); njs_explicit_memzero(ctx, sizeof(*ctx)); } /* * The basic MD5 functions. * * F and G are optimized compared to their RFC 1321 definitions for * architectures that lack an AND-NOT instruction, just like in * Colin Plumb's implementation. */ #define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) #define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) #define H(x, y, z) ((x) ^ (y) ^ (z)) #define I(x, y, z) ((y) ^ ((x) | ~(z))) /* * The MD5 transformation for all four rounds. */ #define STEP(f, a, b, c, d, x, t, s) \ (a) += f((b), (c), (d)) + (x) + (t); \ (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \ (a) += (b) /* * SET() reads 4 input bytes in little-endian byte order and stores them * in a properly aligned word in host byte order. */ #define SET(n) \ (block[n] = \ ( (uint32_t) p[n * 4] \ | ((uint32_t) p[n * 4 + 1] << 8) \ | ((uint32_t) p[n * 4 + 2] << 16) \ | ((uint32_t) p[n * 4 + 3] << 24))) \ #define GET(n) block[n] /* * This processes one or more 64-byte data blocks, but does not update * the bit counters. There are no alignment requirements. */ static const u_char * njs_md5_body(njs_hash_t *ctx, const u_char *data, size_t size) { uint32_t a, b, c, d; uint32_t saved_a, saved_b, saved_c, saved_d; const u_char *p; uint32_t block[16]; p = data; a = ctx->a; b = ctx->b; c = ctx->c; d = ctx->d; do { saved_a = a; saved_b = b; saved_c = c; saved_d = d; /* Round 1 */ STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7); STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12); STEP(F, c, d, a, b, SET(2), 0x242070db, 17); STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22); STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7); STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12); STEP(F, c, d, a, b, SET(6), 0xa8304613, 17); STEP(F, b, c, d, a, SET(7), 0xfd469501, 22); STEP(F, a, b, c, d, SET(8), 0x698098d8, 7); STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12); STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17); STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22); STEP(F, a, b, c, d, SET(12), 0x6b901122, 7); STEP(F, d, a, b, c, SET(13), 0xfd987193, 12); STEP(F, c, d, a, b, SET(14), 0xa679438e, 17); STEP(F, b, c, d, a, SET(15), 0x49b40821, 22); /* Round 2 */ STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5); STEP(G, d, a, b, c, GET(6), 0xc040b340, 9); STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14); STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20); STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5); STEP(G, d, a, b, c, GET(10), 0x02441453, 9); STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14); STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20); STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5); STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9); STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14); STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20); STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5); STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9); STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14); STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20); /* Round 3 */ STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4); STEP(H, d, a, b, c, GET(8), 0x8771f681, 11); STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16); STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23); STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4); STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11); STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16); STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23); STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4); STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11); STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16); STEP(H, b, c, d, a, GET(6), 0x04881d05, 23); STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4); STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11); STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16); STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23); /* Round 4 */ STEP(I, a, b, c, d, GET(0), 0xf4292244, 6); STEP(I, d, a, b, c, GET(7), 0x432aff97, 10); STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15); STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21); STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6); STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10); STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15); STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21); STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6); STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10); STEP(I, c, d, a, b, GET(6), 0xa3014314, 15); STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21); STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6); STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10); STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15); STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21); a += saved_a; b += saved_b; c += saved_c; d += saved_d; p += 64; } while (size -= 64); ctx->a = a; ctx->b = b; ctx->c = c; ctx->d = d; return p; } njs-0.8.9/external/njs_openssl.h000066400000000000000000000143261474132077100166660ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_EXTERNAL_OPENSSL_H_INCLUDED_ #define _NJS_EXTERNAL_OPENSSL_H_INCLUDED_ #define OPENSSL_SUPPRESS_DEPRECATED #include #include #include #include #include #include #include #include #include #include #ifdef EVP_PKEY_HKDF #include #endif #if (defined LIBRESSL_VERSION_NUMBER && OPENSSL_VERSION_NUMBER == 0x20000000L) #undef OPENSSL_VERSION_NUMBER #if (LIBRESSL_VERSION_NUMBER >= 0x2080000fL) #define OPENSSL_VERSION_NUMBER 0x1010000fL #else #define OPENSSL_VERSION_NUMBER 0x1000107fL #endif #endif #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) #define njs_evp_md_ctx_new() EVP_MD_CTX_new() #define njs_evp_md_ctx_free(_ctx) EVP_MD_CTX_free(_ctx) #else #define njs_evp_md_ctx_new() EVP_MD_CTX_create() #define njs_evp_md_ctx_free(_ctx) EVP_MD_CTX_destroy(_ctx) #endif #define njs_bio_new_mem_buf(b, len) BIO_new_mem_buf((void *) b, len) #if (OPENSSL_VERSION_NUMBER < 0x30000000L && !defined ERR_peek_error_data) #define ERR_peek_error_data(d, f) ERR_peek_error_line_data(NULL, NULL, d, f) #endif njs_inline int njs_bn_bn2binpad(const BIGNUM *bn, unsigned char *to, int tolen) { #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) return BN_bn2binpad(bn, to, tolen); #else int len; len = BN_num_bytes(bn); if (tolen > len) { memset(to, 0, tolen - len); } else if (tolen < len) { return -1; } return BN_bn2bin(bn, &to[tolen - len]); #endif } njs_inline int njs_pkey_up_ref(EVP_PKEY *pkey) { #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) return EVP_PKEY_up_ref(pkey); #else CRYPTO_add(&pkey->references, 1, CRYPTO_LOCK_EVP_PKEY); return 1; #endif } njs_inline const RSA * njs_pkey_get_rsa_key(EVP_PKEY *pkey) { #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) return EVP_PKEY_get0_RSA(pkey); #else return EVP_PKEY_get0(pkey); #endif } njs_inline void njs_rsa_get0_key(const RSA *rsa, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d) { #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) RSA_get0_key(rsa, n, e, d); #else if (n != NULL) { *n = rsa->n; } if (e != NULL) { *e = rsa->e; } if (d != NULL) { *d = rsa->d; } #endif } njs_inline void njs_rsa_get0_factors(const RSA *rsa, const BIGNUM **p, const BIGNUM **q) { #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) RSA_get0_factors(rsa, p, q); #else if (p != NULL) { *p = rsa->p; } if (q != NULL) { *q = rsa->q; } #endif } njs_inline void njs_rsa_get0_ctr_params(const RSA *rsa, const BIGNUM **dp, const BIGNUM **dq, const BIGNUM **qi) { #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) RSA_get0_crt_params(rsa, dp, dq, qi); #else if (dp != NULL) { *dp = rsa->dmp1; } if (dq != NULL) { *dq = rsa->dmq1; } if (qi != NULL) { *qi = rsa->iqmp; } #endif } njs_inline int njs_rsa_set0_key(RSA *rsa, BIGNUM *n, BIGNUM *e, BIGNUM *d) { #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) return RSA_set0_key(rsa, n, e, d); #else if ((rsa->n == NULL && n == NULL) || (rsa->e == NULL && e == NULL)) { return 0; } if (n != NULL) { BN_free(rsa->n); rsa->n = n; } if (e != NULL) { BN_free(rsa->e); rsa->e = e; } if (d != NULL) { BN_clear_free(rsa->d); rsa->d = d; BN_set_flags(rsa->d, BN_FLG_CONSTTIME); } return 1; #endif } njs_inline int njs_rsa_set0_factors(RSA *rsa, BIGNUM *p, BIGNUM *q) { #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) return RSA_set0_factors(rsa, p, q); #else if ((rsa->p == NULL && p == NULL) || (rsa->q == NULL && q == NULL)) { return 0; } if (p != NULL) { BN_clear_free(rsa->p); rsa->p = p; BN_set_flags(rsa->p, BN_FLG_CONSTTIME); } if (q != NULL) { BN_clear_free(rsa->q); rsa->q = q; BN_set_flags(rsa->q, BN_FLG_CONSTTIME); } return 1; #endif } njs_inline int njs_rsa_set0_ctr_params(RSA *rsa, BIGNUM *dp, BIGNUM *dq, BIGNUM *qi) { #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) return RSA_set0_crt_params(rsa, dp, dq, qi); #else if ((rsa->dmp1 == NULL && dp == NULL) || (rsa->dmq1 == NULL && dq == NULL) || (rsa->iqmp == NULL && qi == NULL)) { return 0; } if (dp != NULL) { BN_clear_free(rsa->dmp1); rsa->dmp1 = dp; BN_set_flags(rsa->dmp1, BN_FLG_CONSTTIME); } if (dq != NULL) { BN_clear_free(rsa->dmq1); rsa->dmq1 = dq; BN_set_flags(rsa->dmq1, BN_FLG_CONSTTIME); } if (qi != NULL) { BN_clear_free(rsa->iqmp); rsa->iqmp = qi; BN_set_flags(rsa->iqmp, BN_FLG_CONSTTIME); } return 1; #endif } njs_inline const EC_KEY * njs_pkey_get_ec_key(EVP_PKEY *pkey) { #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) return EVP_PKEY_get0_EC_KEY(pkey); #else if (pkey->type != EVP_PKEY_EC) { return NULL; } return pkey->pkey.ec; #endif } njs_inline int njs_ec_group_order_bits(const EC_GROUP *group) { #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) return EC_GROUP_order_bits(group); #else int bits; BIGNUM *order; order = BN_new(); if (order == NULL) { return 0; } if (EC_GROUP_get_order(group, order, NULL) == 0) { return 0; } bits = BN_num_bits(order); BN_free(order); return bits; #endif } njs_inline int njs_ec_point_get_affine_coordinates(const EC_GROUP *group, const EC_POINT *p, BIGNUM *x, BIGNUM *y) { #if (OPENSSL_VERSION_NUMBER >= 0x10101001L) return EC_POINT_get_affine_coordinates(group, p, x, y, NULL); #else return EC_POINT_get_affine_coordinates_GFp(group, p, x, y, NULL); #endif } njs_inline int njs_ecdsa_sig_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s) { #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) return ECDSA_SIG_set0(sig, r, s); #else if (r == NULL || s == NULL) { return 0; } BN_clear_free(sig->r); BN_clear_free(sig->s); sig->r = r; sig->s = s; return 1; #endif } #endif /* _NJS_EXTERNAL_OPENSSL_H_INCLUDED_ */ njs-0.8.9/external/njs_query_string_module.c000066400000000000000000000607271474132077100213040ustar00rootroot00000000000000 /* * Copyright (C) Alexander Borisov * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #include #include static njs_int_t njs_query_string_parser(njs_vm_t *vm, u_char *query, u_char *end, const njs_str_t *sep, const njs_str_t *eq, njs_function_t *decode, njs_uint_t max_keys, njs_value_t *retval); static njs_int_t njs_query_string_parse(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_query_string_stringify(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_query_string_escape(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_query_string_unescape(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_query_string_init(njs_vm_t *vm); static njs_external_t njs_ext_query_string[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "querystring", } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("parse"), .writable = 1, .configurable = 1, .u.method = { .native = njs_query_string_parse, .magic8 = 0, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("stringify"), .writable = 1, .configurable = 1, .u.method = { .native = njs_query_string_stringify, .magic8 = 0, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("decode"), .writable = 1, .configurable = 1, .u.method = { .native = njs_query_string_parse, .magic8 = 0, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("encode"), .writable = 1, .configurable = 1, .u.method = { .native = njs_query_string_stringify, .magic8 = 0, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("escape"), .writable = 1, .configurable = 1, .u.method = { .native = njs_query_string_escape, .magic8 = 0, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("unescape"), .writable = 1, .configurable = 1, .u.method = { .native = njs_query_string_unescape, .magic8 = 0, } }, }; njs_module_t njs_query_string_module = { .name = njs_str("querystring"), .preinit = NULL, .init = njs_query_string_init, }; static const njs_str_t njs_escape_str = njs_str("escape"); static const njs_str_t njs_unescape_str = njs_str("unescape"); static const njs_str_t njs_encode_uri_str = njs_str("encodeURIComponent"); static const njs_str_t njs_decode_uri_str = njs_str("decodeURIComponent"); static const njs_str_t njs_max_keys_str = njs_str("maxKeys"); static const njs_str_t njs_sep_default = njs_str("&"); static const njs_str_t njs_eq_default = njs_str("="); static njs_int_t njs_query_string_decode(njs_vm_t *vm, njs_value_t *value, const u_char *start, size_t size) { u_char *dst; uint32_t cp; njs_int_t ret; njs_chb_t chain; const u_char *p, *end; njs_unicode_decode_t ctx; static const int8_t hex[256] njs_aligned(32) = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, }; NJS_CHB_MP_INIT(&chain, vm); njs_utf8_decode_init(&ctx); cp = 0; p = start; end = p + size; while (p < end) { if (*p == '%' && end - p > 2 && hex[p[1]] >= 0 && hex[p[2]] >= 0) { cp = njs_utf8_consume(&ctx, (hex[p[1]] << 4) | hex[p[2]]); p += 3; } else { if (*p == '+') { cp = ' '; p++; } else { cp = njs_utf8_decode(&ctx, &p, end); } } if (cp > NJS_UNICODE_MAX_CODEPOINT) { if (cp == NJS_UNICODE_CONTINUE) { continue; } cp = NJS_UNICODE_REPLACEMENT; } dst = njs_chb_reserve(&chain, 4); if (njs_slow_path(dst == NULL)) { return NJS_ERROR; } njs_chb_written(&chain, njs_utf8_encode(dst, cp) - dst); } if (njs_slow_path(cp == NJS_UNICODE_CONTINUE)) { dst = njs_chb_reserve(&chain, 3); if (njs_slow_path(dst == NULL)) { return NJS_ERROR; } njs_chb_written(&chain, njs_utf8_encode(dst, NJS_UNICODE_REPLACEMENT) - dst); } ret = njs_vm_value_string_create_chb(vm, value, &chain); njs_chb_destroy(&chain); return ret; } njs_inline njs_bool_t njs_query_string_is_native_decoder(njs_function_t *decoder) { njs_opaque_value_t function; njs_function_native_t native; if (decoder == NULL) { return 1; } njs_value_function_set(njs_value_arg(&function), decoder); native = njs_value_native_function(njs_value_arg(&function)); return native == njs_query_string_unescape; } njs_inline njs_int_t njs_query_string_append(njs_vm_t *vm, njs_value_t *object, const u_char *key, size_t key_size, const u_char *val, size_t val_size, njs_function_t *decoder) { njs_int_t ret; njs_value_t *push; njs_opaque_value_t array, name, value, retval; if (njs_query_string_is_native_decoder(decoder)) { ret = njs_query_string_decode(vm, njs_value_arg(&name), key, key_size); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_query_string_decode(vm, njs_value_arg(&value), val, val_size); if (njs_slow_path(ret != NJS_OK)) { return ret; } } else { ret = njs_vm_value_string_create(vm, njs_value_arg(&name), key, key_size); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (key_size > 0) { ret = njs_vm_invoke(vm, decoder, njs_value_arg(&name), 1, njs_value_arg(&name)); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (!njs_value_is_string(njs_value_arg(&name))) { ret = njs_value_to_string(vm, njs_value_arg(&name), njs_value_arg(&name)); if (njs_slow_path(ret != NJS_OK)) { return ret; } } } ret = njs_vm_value_string_create(vm, njs_value_arg(&value), val, val_size); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (val_size > 0) { ret = njs_vm_invoke(vm, decoder, njs_value_arg(&value), 1, njs_value_arg(&value)); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (!njs_value_is_string(njs_value_arg(&value))) { ret = njs_value_to_string(vm, njs_value_arg(&value), njs_value_arg(&value)); if (njs_slow_path(ret != NJS_OK)) { return ret; } } } } ret = njs_value_property(vm, object, njs_value_arg(&name), njs_value_arg(&retval)); if (ret == NJS_OK) { if (njs_value_is_array(njs_value_arg(&retval))) { push = njs_vm_array_push(vm, njs_value_arg(&retval)); if (njs_slow_path(push == NULL)) { return NJS_ERROR; } njs_value_assign(push, njs_value_arg(&value)); return NJS_OK; } ret = njs_vm_array_alloc(vm, njs_value_arg(&array), 2); if (njs_slow_path(ret != NJS_OK)) { return ret; } push = njs_vm_array_push(vm, njs_value_arg(&array)); if (njs_slow_path(push == NULL)) { return NJS_ERROR; } njs_value_assign(push, njs_value_arg(&retval)); push = njs_vm_array_push(vm, njs_value_arg(&array)); if (njs_slow_path(push == NULL)) { return NJS_ERROR; } njs_value_assign(push, njs_value_arg(&value)); njs_value_assign(&value, &array); } return njs_value_property_set(vm, object, njs_value_arg(&name), njs_value_arg(&value)); } static u_char * njs_query_string_match(u_char *p, u_char *end, const njs_str_t *v) { size_t length; length = v->length; if (njs_fast_path(length == 1)) { p = njs_strlchr(p, end, v->start[0]); if (p == NULL) { p = end; } return p; } while (p <= (end - length)) { if (memcmp(p, v->start, length) == 0) { return p; } p++; } return end; } static njs_int_t njs_query_string_parse(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t max_keys; njs_int_t ret; njs_str_t str, sep, eq; njs_value_t *this, *string, *options, *arg, *val; njs_function_t *decode; njs_opaque_value_t value, val_sep, val_eq; decode = NULL; max_keys = 1000; this = njs_argument(args, 0); string = njs_arg(args, nargs, 1); if (njs_value_is_string(string)) { njs_value_string_get(string, &str); } else { str = njs_str_value(""); } sep = njs_sep_default; eq = njs_eq_default; arg = njs_arg(args, nargs, 2); if (!njs_value_is_null_or_undefined(arg)) { ret = njs_value_to_string(vm, njs_value_arg(&val_sep), arg); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_string_length(njs_value_arg(&val_sep)) != 0) { njs_value_string_get(njs_value_arg(&val_sep), &sep); } } arg = njs_arg(args, nargs, 3); if (!njs_value_is_null_or_undefined(arg)) { ret = njs_value_to_string(vm, njs_value_arg(&val_eq), arg); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_string_length(njs_value_arg(&val_eq)) != 0) { njs_value_string_get(njs_value_arg(&val_eq), &eq); } } options = njs_arg(args, nargs, 4); if (njs_value_is_object(options)) { val = njs_vm_object_prop(vm, options, &njs_max_keys_str, &value); if (val != NULL) { if (!njs_value_is_valid_number(val)) { njs_vm_type_error(vm, "is not a number"); return NJS_ERROR; } max_keys = njs_value_number(val); if (max_keys == 0) { max_keys = INT64_MAX; } } val = njs_vm_object_prop(vm, options, &njs_decode_uri_str, &value); if (val != NULL) { if (njs_slow_path(!njs_value_is_function(val))) { njs_vm_type_error(vm, "option decodeURIComponent is not " "a function"); return NJS_ERROR; } decode = njs_value_function(val); } } if (decode == NULL) { val = njs_vm_object_prop(vm, this, &njs_unescape_str, &value); if (val == NULL || !njs_value_is_function(val)) { njs_vm_type_error(vm, "QueryString.unescape is not a function"); return NJS_ERROR; } decode = njs_value_function(val); } return njs_query_string_parser(vm, str.start, str.start + str.length, &sep, &eq, decode, max_keys, retval); } njs_int_t njs_vm_query_string_parse(njs_vm_t *vm, u_char *start, u_char *end, njs_value_t *retval) { return njs_query_string_parser(vm, start, end, &njs_sep_default, &njs_eq_default, NULL, 1000, retval); } static njs_int_t njs_query_string_parser(njs_vm_t *vm, u_char *query, u_char *end, const njs_str_t *sep, const njs_str_t *eq, njs_function_t *decode, njs_uint_t max_keys, njs_value_t *retval) { size_t size; u_char *part, *key, *val; njs_int_t ret; njs_uint_t count; ret = njs_vm_object_alloc(vm, retval, NULL); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } count = 0; key = query; while (key < end) { if (count++ == max_keys) { break; } part = njs_query_string_match(key, end, sep); if (part == key) { goto next; } val = njs_query_string_match(key, part, eq); size = val - key; if (val != part) { val += eq->length; } ret = njs_query_string_append(vm, retval, key, size, val, part - val, decode); if (njs_slow_path(ret != NJS_OK)) { return ret; } next: key = part + sep->length; } return NJS_OK; } njs_inline njs_int_t njs_query_string_encode(njs_chb_t *chain, njs_str_t *str) { size_t size; u_char *p, *start, *end; static const uint32_t escape[] = { 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ 0xfc00987d, /* 1111 1100 0000 0000 1001 1000 0111 1101 */ /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ 0x78000001, /* 0111 1000 0000 0000 0000 0000 0000 0001 */ /* ~}| {zyx wvut srqp onml kjih gfed cba` */ 0xb8000001, /* 1011 1000 0000 0000 0000 0000 0000 0001 */ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ }; if (chain->error) { return NJS_ERROR; } if (str->length == 0) { return NJS_OK; } p = str->start; end = p + str->length; size = str->length; while (p < end) { if (njs_need_escape(escape, *p++)) { size += 2; } } start = njs_chb_reserve(chain, size); if (njs_slow_path(start == NULL)) { return NJS_ERROR; } if (size == str->length) { memcpy(start, str->start, str->length); njs_chb_written(chain, str->length); return NJS_OK; } (void) njs_string_encode(escape, str->length, str->start, start); njs_chb_written(chain, size); return NJS_OK; } njs_inline njs_bool_t njs_query_string_is_native_encoder(njs_function_t *encoder) { njs_opaque_value_t function; njs_value_function_set(njs_value_arg(&function), encoder); return njs_value_native_function(njs_value_arg(&function)) == njs_query_string_escape; } njs_inline njs_int_t njs_query_string_encoder_call(njs_vm_t *vm, njs_chb_t *chain, njs_function_t *encoder, njs_value_t *string) { njs_str_t str; njs_int_t ret; njs_opaque_value_t retval; if (njs_slow_path(!njs_value_is_string(string))) { ret = njs_value_to_string(vm, string, string); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } if (njs_fast_path(njs_query_string_is_native_encoder(encoder))) { njs_value_string_get(string, &str); return njs_query_string_encode(chain, &str); } ret = njs_vm_invoke(vm, encoder, string, 1, njs_value_arg(&retval)); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_slow_path(!njs_value_is_string(njs_value_arg(&retval)))) { ret = njs_value_to_string(vm, njs_value_arg(&retval), njs_value_arg(&retval)); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } njs_value_string_get(njs_value_arg(&retval), &str); njs_chb_append_str(chain, &str); return NJS_OK; } njs_inline njs_int_t njs_query_string_push(njs_vm_t *vm, njs_chb_t *chain, njs_value_t *key, njs_value_t *value, njs_str_t *eq, njs_function_t *encoder) { njs_int_t ret; ret = njs_query_string_encoder_call(vm, chain, encoder, key); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_chb_append(chain, eq->start, eq->length); if (njs_value_is_valid_number(value) || njs_value_is_boolean(value) || njs_value_is_string(value)) { if (!njs_value_is_string(value)) { ret = njs_value_to_string(vm, value, value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } ret = njs_query_string_encoder_call(vm, chain, encoder, value); if (njs_slow_path(ret < 0)) { return NJS_ERROR; } } return NJS_OK; } static njs_int_t njs_query_string_stringify(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t len, keys_length; uint32_t n, i; njs_int_t ret; njs_str_t sep, eq; njs_chb_t chain; njs_value_t *this, *object, *arg, *options, *val, *keys; njs_function_t *encode; njs_opaque_value_t value, result, key, *string; encode = NULL; sep = njs_sep_default; eq = njs_eq_default; this = njs_argument(args, 0); object = njs_arg(args, nargs, 1); if (njs_slow_path(!njs_value_is_object(object))) { njs_vm_value_string_create(vm, retval, (u_char *) "", 0); return NJS_OK; } arg = njs_arg(args, nargs, 2); if (!njs_value_is_null_or_undefined(arg)) { ret = njs_value_to_string(vm, arg, arg); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_string_length(arg) > 0) { njs_value_string_get(arg, &sep); } } arg = njs_arg(args, nargs, 3); if (!njs_value_is_null_or_undefined(arg)) { ret = njs_value_to_string(vm, arg, arg); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_string_length(arg) > 0) { njs_value_string_get(arg, &eq); } } options = njs_arg(args, nargs, 4); if (njs_value_is_object(options)) { val = njs_vm_object_prop(vm, options, &njs_encode_uri_str, &value); if (val != NULL) { if (njs_slow_path(!njs_value_is_function(val))) { njs_vm_type_error(vm, "option encodeURIComponent is not " "a function"); return NJS_ERROR; } encode = njs_value_function(val); } } if (encode == NULL) { val = njs_vm_object_prop(vm, this, &njs_escape_str, &value); if (val == NULL || !njs_value_is_function(val)) { njs_vm_type_error(vm, "QueryString.escape is not a function"); return NJS_ERROR; } encode = njs_value_function(val); } NJS_CHB_MP_INIT(&chain, vm); keys = njs_vm_object_keys(vm, object, njs_value_arg(&value)); if (njs_slow_path(keys == NULL)) { return NJS_ERROR; } (void) njs_vm_array_length(vm, keys, &keys_length); string = (njs_opaque_value_t *) njs_vm_array_start(vm, keys); if (njs_slow_path(string == NULL)) { return NJS_ERROR; } for (n = 0; n < keys_length; n++, string++) { ret = njs_value_property(vm, object, njs_value_arg(string), njs_value_arg(&value)); if (njs_slow_path(ret == NJS_ERROR)) { goto failed; } if (njs_value_is_array(njs_value_arg(&value))) { (void) njs_vm_array_length(vm, njs_value_arg(&value), &len); for (i = 0; i < len; i++) { njs_value_number_set(njs_value_arg(&key), i); ret = njs_value_property(vm, njs_value_arg(&value), njs_value_arg(&key), njs_value_arg(&result)); if (njs_slow_path(ret == NJS_ERROR)) { goto failed; } if (chain.last != NULL) { njs_chb_append(&chain, sep.start, sep.length); } ret = njs_query_string_push(vm, &chain, njs_value_arg(string), njs_value_arg(&result), &eq, encode); if (njs_slow_path(ret != NJS_OK)) { goto failed; } } continue; } if (n != 0) { njs_chb_append(&chain, sep.start, sep.length); } ret = njs_query_string_push(vm, &chain, njs_value_arg(string), njs_value_arg(&value), &eq, encode); if (njs_slow_path(ret != NJS_OK)) { goto failed; } } ret = njs_vm_value_string_create_chb(vm, retval, &chain); failed: njs_chb_destroy(&chain); return ret; } static njs_int_t njs_query_string_escape(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_str_t str; njs_chb_t chain; njs_value_t *string; njs_opaque_value_t value; string = njs_arg(args, nargs, 1); if (!njs_value_is_string(string)) { ret = njs_value_to_string(vm, njs_value_arg(&value), string); if (njs_slow_path(ret != NJS_OK)) { return ret; } string = njs_value_arg(&value); } njs_value_string_get(string, &str); NJS_CHB_MP_INIT(&chain, vm); ret = njs_query_string_encode(&chain, &str); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_vm_value_string_create_chb(vm, retval, &chain); njs_chb_destroy(&chain); return ret; } static njs_int_t njs_query_string_unescape(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_str_t str; njs_value_t *string; njs_opaque_value_t value; string = njs_arg(args, nargs, 1); if (!njs_value_is_string(string)) { ret = njs_value_to_string(vm, njs_value_arg(&value), string); if (njs_slow_path(ret != NJS_OK)) { return ret; } string = njs_value_arg(&value); } njs_value_string_get(string, &str); return njs_query_string_decode(vm, retval, str.start, str.length); } static njs_int_t njs_query_string_init(njs_vm_t *vm) { njs_int_t ret, proto_id; njs_mod_t *module; njs_opaque_value_t value; proto_id = njs_vm_external_prototype(vm, njs_ext_query_string, njs_nitems(njs_ext_query_string)); if (njs_slow_path(proto_id < 0)) { return NJS_ERROR; } ret = njs_vm_external_create(vm, njs_value_arg(&value), proto_id, NULL, 1); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } module = njs_vm_add_module(vm, &njs_str_value("querystring"), njs_value_arg(&value)); if (njs_slow_path(module == NULL)) { return NJS_ERROR; } return NJS_OK; } njs-0.8.9/external/njs_regex.c000066400000000000000000000374711474132077100163160ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #include #ifdef NJS_HAVE_PCRE2 #define PCRE2_CODE_UNIT_WIDTH 8 #include static const u_char* njs_regex_pcre2_error(int errcode, u_char buffer[128]); #else #include static void *njs_pcre_malloc(size_t size); static void njs_pcre_free(void *p); static njs_regex_generic_ctx_t *regex_context; #endif njs_regex_generic_ctx_t * njs_regex_generic_ctx_create(njs_pcre_malloc_t private_malloc, njs_pcre_free_t private_free, void *memory_data) { #ifdef NJS_HAVE_PCRE2 return pcre2_general_context_create(private_malloc, private_free, memory_data); #else njs_regex_generic_ctx_t *ctx; ctx = private_malloc(sizeof(njs_regex_generic_ctx_t), memory_data); if (njs_fast_path(ctx != NULL)) { ctx->private_malloc = private_malloc; ctx->private_free = private_free; ctx->memory_data = memory_data; } return ctx; #endif } njs_regex_compile_ctx_t * njs_regex_compile_ctx_create(njs_regex_generic_ctx_t *ctx) { #ifdef NJS_HAVE_PCRE2 pcre2_compile_context *cc; cc = pcre2_compile_context_create(ctx); #ifdef PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES if (njs_fast_path(cc != NULL)) { /* Workaround for surrogate pairs in regular expressions * * This option is needed because njs, unlike the standard ECMAScript, * stores and processes strings in UTF-8 encoding. * PCRE2 does not support surrogate pairs by default when it * is compiled for UTF-8 only strings. But many polyfills * and transpilers use such surrogate pairs expressions. */ pcre2_set_compile_extra_options(cc, PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES); } #endif return cc; #else return ctx; #endif } njs_int_t njs_regex_escape(njs_mp_t *mp, njs_str_t *text) { #ifdef NJS_HAVE_PCRE2 size_t anychars, nomatches; u_char *p, *dst, *start, *end; /* * 1) [^] is a valid regexp expression in JavaScript, but PCRE2 * rejects it as invalid, replacing it with equivalent PCRE2 [\s\S] * expression. * 2) [] is a valid regexp expression in JavaScript, but PCRE2 * rejects it as invalid, replacing it with equivalent PCRE2 (?!) * expression which matches nothing. */ start = text->start; end = text->start + text->length; anychars = 0; nomatches = 0; for (p = start; p < end; p++) { switch (*p) { case '[': if (p + 1 < end && p[1] == ']') { p += 1; nomatches += 1; } else if (p + 2 < end && p[1] == '^' && p[2] == ']') { p += 2; anychars += 1; } break; } } if (!anychars && !nomatches) { return NJS_OK; } text->length = text->length + anychars * (njs_length("\\s\\S") - njs_length("^")) + nomatches * (njs_length("?!")); text->start = njs_mp_alloc(mp, text->length); if (njs_slow_path(text->start == NULL)) { return NJS_ERROR; } dst = text->start; for (p = start; p < end; p++) { switch (*p) { case '[': if (p + 1 < end && p[1] == ']') { p += 1; dst = njs_cpymem(dst, "(?!)", 4); continue; } else if (p + 2 < end && p[1] == '^' && p[2] == ']') { p += 2; dst = njs_cpymem(dst, "[\\s\\S]", 6); continue; } } *dst++ = *p; } return NJS_OK; #else /* * 1) PCRE with PCRE_JAVASCRIPT_COMPAT flag rejects regexps with * lone closing square brackets as invalid. Whereas according * to ES6: 11.8.5 it is a valid regexp expression. * * 2) escaping zero byte characters as "\u0000". * * Escaping it here as a workaround. */ size_t brackets, zeros; u_char *p, *dst, *start, *end; njs_bool_t in; start = text->start; end = text->start + text->length; in = 0; zeros = 0; brackets = 0; for (p = start; p < end; p++) { switch (*p) { case '[': in = 1; break; case ']': if (!in) { brackets++; } in = 0; break; case '\\': p++; if (p == end || *p != '\0') { break; } /* Fall through. */ case '\0': zeros++; break; } } if (!brackets && !zeros) { return NJS_OK; } text->length = text->length + brackets + zeros * njs_length("\\u0000"); text->start = njs_mp_alloc(mp, text->length); if (njs_slow_path(text->start == NULL)) { return NJS_ERROR; } in = 0; dst = text->start; for (p = start; p < end; p++) { switch (*p) { case '[': in = 1; break; case ']': if (!in) { *dst++ = '\\'; } in = 0; break; case '\\': *dst++ = *p++; if (p == end) { goto done; } if (*p != '\0') { break; } /* Fall through. */ case '\0': dst = njs_cpymem(dst, "\\u0000", 6); continue; } *dst++ = *p; } done: text->length = dst - text->start; return NJS_OK; #endif } njs_int_t njs_regex_compile(njs_regex_t *regex, u_char *source, size_t len, njs_regex_flags_t flags, njs_regex_compile_ctx_t *cctx, njs_trace_t *trace) { #ifdef NJS_HAVE_PCRE2 int ret; u_char *error; size_t erroff; njs_uint_t options; u_char errstr[128]; options = PCRE2_ALT_BSUX | PCRE2_MATCH_UNSET_BACKREF; if ((flags & NJS_REGEX_IGNORE_CASE)) { options |= PCRE2_CASELESS; } if ((flags & NJS_REGEX_MULTILINE)) { options |= PCRE2_MULTILINE; } if ((flags & NJS_REGEX_STICKY)) { options |= PCRE2_ANCHORED; } if ((flags & NJS_REGEX_UTF8)) { options |= PCRE2_UTF; } regex->code = pcre2_compile(source, len, options, &ret, &erroff, cctx); if (njs_slow_path(regex->code == NULL)) { error = &source[erroff]; njs_alert(trace, NJS_LEVEL_ERROR, "pcre_compile2(\"%s\") failed: %s at \"%s\"", source, njs_regex_pcre2_error(ret, errstr), error); return NJS_DECLINED; } ret = pcre2_pattern_info(regex->code, PCRE2_INFO_CAPTURECOUNT, ®ex->ncaptures); if (njs_slow_path(ret < 0)) { njs_alert(trace, NJS_LEVEL_ERROR, "pcre2_pattern_info(\"%s\", PCRE2_INFO_CAPTURECOUNT) failed: %s", source, njs_regex_pcre2_error(ret, errstr)); return NJS_ERROR; } ret = pcre2_pattern_info(regex->code, PCRE2_INFO_BACKREFMAX, ®ex->backrefmax); if (njs_slow_path(ret < 0)) { njs_alert(trace, NJS_LEVEL_ERROR, "pcre2_pattern_info(\"%s\", PCRE2_INFO_BACKREFMAX) failed: %s", source, njs_regex_pcre2_error(ret, errstr)); return NJS_ERROR; } /* Reserve additional elements for the first "$0" capture. */ regex->ncaptures++; if (regex->ncaptures > 1) { ret = pcre2_pattern_info(regex->code, PCRE2_INFO_NAMECOUNT, ®ex->nentries); if (njs_slow_path(ret < 0)) { njs_alert(trace, NJS_LEVEL_ERROR, "pcre2_pattern_info(\"%s\", PCRE2_INFO_NAMECOUNT) failed: %s", source, njs_regex_pcre2_error(ret, errstr)); return NJS_ERROR; } if (regex->nentries != 0) { ret = pcre2_pattern_info(regex->code, PCRE2_INFO_NAMEENTRYSIZE, ®ex->entry_size); if (njs_slow_path(ret < 0)) { njs_alert(trace, NJS_LEVEL_ERROR, "pcre2_pattern_info(\"%s\", PCRE2_INFO_NAMEENTRYSIZE)" " failed: %s", source, njs_regex_pcre2_error(ret, errstr)); return NJS_ERROR; } ret = pcre2_pattern_info(regex->code, PCRE2_INFO_NAMETABLE, ®ex->entries); if (njs_slow_path(ret < 0)) { njs_alert(trace, NJS_LEVEL_ERROR, "pcre2_pattern_info(\"%s\", PCRE2_INFO_NAMETABLE) " "failed: %s", source, njs_regex_pcre2_error(ret, errstr)); return NJS_ERROR; } } } return NJS_OK; #else int ret, err, erroff; char *pattern, *error; void *(*saved_malloc)(size_t size); void (*saved_free)(void *p); njs_uint_t options; const char *errstr; njs_regex_generic_ctx_t *ctx; ctx = cctx; ret = NJS_ERROR; saved_malloc = pcre_malloc; pcre_malloc = njs_pcre_malloc; saved_free = pcre_free; pcre_free = njs_pcre_free; regex_context = ctx; #ifdef PCRE_JAVASCRIPT_COMPAT /* JavaScript compatibility has been introduced in PCRE-7.7. */ options = PCRE_JAVASCRIPT_COMPAT; #else options = 0; #endif if ((flags & NJS_REGEX_IGNORE_CASE)) { options |= PCRE_CASELESS; } if ((flags & NJS_REGEX_MULTILINE)) { options |= PCRE_MULTILINE; } if ((flags & NJS_REGEX_STICKY)) { options |= PCRE_ANCHORED; } if ((flags & NJS_REGEX_UTF8)) { options |= PCRE_UTF8; } pattern = (char *) source; regex->code = pcre_compile(pattern, options, &errstr, &erroff, NULL); if (njs_slow_path(regex->code == NULL)) { error = pattern + erroff; if (*error != '\0') { njs_alert(trace, NJS_LEVEL_ERROR, "pcre_compile(\"%s\") failed: %s at \"%s\"", pattern, errstr, error); } else { njs_alert(trace, NJS_LEVEL_ERROR, "pcre_compile(\"%s\") failed: %s", pattern, errstr); } ret = NJS_DECLINED; goto done; } regex->extra = pcre_study(regex->code, 0, &errstr); if (njs_slow_path(errstr != NULL)) { njs_alert(trace, NJS_LEVEL_WARN, "pcre_study(\"%s\") failed: %s", pattern, errstr); } err = pcre_fullinfo(regex->code, NULL, PCRE_INFO_CAPTURECOUNT, ®ex->ncaptures); if (njs_slow_path(err < 0)) { njs_alert(trace, NJS_LEVEL_ERROR, "pcre_fullinfo(\"%s\", PCRE_INFO_CAPTURECOUNT) failed: %d", pattern, err); goto done; } err = pcre_fullinfo(regex->code, NULL, PCRE_INFO_BACKREFMAX, ®ex->backrefmax); if (njs_slow_path(err < 0)) { njs_alert(trace, NJS_LEVEL_ERROR, "pcre_fullinfo(\"%s\", PCRE_INFO_BACKREFMAX) failed: %d", pattern, err); goto done; } /* Reserve additional elements for the first "$0" capture. */ regex->ncaptures++; if (regex->ncaptures > 1) { err = pcre_fullinfo(regex->code, NULL, PCRE_INFO_NAMECOUNT, ®ex->nentries); if (njs_slow_path(err < 0)) { njs_alert(trace, NJS_LEVEL_ERROR, "pcre_fullinfo(\"%s\", PCRE_INFO_NAMECOUNT) failed: %d", pattern, err); goto done; } if (regex->nentries != 0) { err = pcre_fullinfo(regex->code, NULL, PCRE_INFO_NAMEENTRYSIZE, ®ex->entry_size); if (njs_slow_path(err < 0)) { njs_alert(trace, NJS_LEVEL_ERROR, "pcre_fullinfo(\"%s\", " "PCRE_INFO_NAMEENTRYSIZE) failed: %d", pattern, err); goto done; } err = pcre_fullinfo(regex->code, NULL, PCRE_INFO_NAMETABLE, ®ex->entries); if (njs_slow_path(err < 0)) { njs_alert(trace, NJS_LEVEL_ERROR, "pcre_fullinfo(\"%s\", " "PCRE_INFO_NAMETABLE) failed: %d", pattern, err); goto done; } } } ret = NJS_OK; done: pcre_malloc = saved_malloc; pcre_free = saved_free; regex_context = NULL; return ret; #endif } njs_bool_t njs_regex_is_valid(njs_regex_t *regex) { return (regex->code != NULL); } njs_int_t njs_regex_named_captures(njs_regex_t *regex, njs_str_t *name, int n) { char *entry; if (name == NULL) { return regex->nentries; } if (n >= regex->nentries) { return NJS_ERROR; } entry = regex->entries + regex->entry_size * n; name->start = (u_char *) entry + 2; name->length = njs_strlen(name->start); return (entry[0] << 8) + entry[1]; } njs_regex_match_data_t * njs_regex_match_data(njs_regex_t *regex, njs_regex_generic_ctx_t *ctx) { #ifdef NJS_HAVE_PCRE2 if (regex != NULL) { return pcre2_match_data_create_from_pattern(regex->code, ctx); } return pcre2_match_data_create(0, ctx); #else size_t size; njs_uint_t ncaptures; njs_regex_match_data_t *match_data; if (regex != NULL) { ncaptures = regex->ncaptures - 1; } else { ncaptures = 0; } /* Each capture is stored in 3 "int" vector elements. */ ncaptures *= 3; size = sizeof(njs_regex_match_data_t) + ncaptures * sizeof(int); match_data = ctx->private_malloc(size, ctx->memory_data); if (njs_fast_path(match_data != NULL)) { match_data->ncaptures = ncaptures + 3; } return match_data; #endif } void njs_regex_match_data_free(njs_regex_match_data_t *match_data, njs_regex_generic_ctx_t *ctx) { #ifdef NJS_HAVE_PCRE2 pcre2_match_data_free(match_data); #else ctx->private_free(match_data, ctx->memory_data); #endif } njs_int_t njs_regex_match(njs_regex_t *regex, const u_char *subject, size_t off, size_t len, njs_regex_match_data_t *match_data, njs_trace_t *trace) { #ifdef NJS_HAVE_PCRE2 int ret; u_char errstr[128]; ret = pcre2_match(regex->code, subject, len, off, 0, match_data, NULL); if (ret < 0) { if (ret == PCRE2_ERROR_NOMATCH) { return NJS_DECLINED; } njs_alert(trace, NJS_LEVEL_ERROR, "pcre2_match() failed: %s", njs_regex_pcre2_error(ret, errstr)); return NJS_ERROR; } return ret; #else int ret; ret = pcre_exec(regex->code, regex->extra, (const char *) subject, len, off, 0, match_data->captures, match_data->ncaptures); if (ret <= PCRE_ERROR_NOMATCH) { if (ret == PCRE_ERROR_NOMATCH) { return NJS_DECLINED; } njs_alert(trace, NJS_LEVEL_ERROR, "pcre_exec() failed: %d", ret); return NJS_ERROR; } return ret; #endif } size_t njs_regex_capture(njs_regex_match_data_t *match_data, njs_uint_t n) { #ifdef NJS_HAVE_PCRE2 size_t c; c = pcre2_get_ovector_pointer(match_data)[n]; if (c == PCRE2_UNSET) { return NJS_REGEX_UNSET; } return c; #else return match_data->captures[n]; #endif } #ifdef NJS_HAVE_PCRE2 static const u_char * njs_regex_pcre2_error(int errcode, u_char buffer[128]) { pcre2_get_error_message(errcode, buffer, 128); return buffer; } #else static void * njs_pcre_malloc(size_t size) { return regex_context->private_malloc(size, regex_context->memory_data); } static void njs_pcre_free(void *p) { regex_context->private_free(p, regex_context->memory_data); } #endif njs-0.8.9/external/njs_sha1.c000066400000000000000000000231411474132077100160250ustar00rootroot00000000000000 /* * Copyright (C) Maxim Dounin * Copyright (C) NGINX, Inc. * * An internal SHA1 implementation. */ #include #include #include #include #include "njs_hash.h" static const u_char *njs_sha1_body(njs_hash_t *ctx, const u_char *data, size_t size); void njs_sha1_init(njs_hash_t *ctx) { ctx->a = 0x67452301; ctx->b = 0xefcdab89; ctx->c = 0x98badcfe; ctx->d = 0x10325476; ctx->e = 0xc3d2e1f0; ctx->bytes = 0; } void njs_sha1_update(njs_hash_t *ctx, const void *data, size_t size) { size_t used, free; used = (size_t) (ctx->bytes & 0x3f); ctx->bytes += size; if (used) { free = 64 - used; if (size < free) { memcpy(&ctx->buffer[used], data, size); return; } memcpy(&ctx->buffer[used], data, free); data = (u_char *) data + free; size -= free; (void) njs_sha1_body(ctx, ctx->buffer, 64); } if (size >= 64) { data = njs_sha1_body(ctx, data, size & ~(size_t) 0x3f); size &= 0x3f; } memcpy(ctx->buffer, data, size); } void njs_sha1_final(u_char result[32], njs_hash_t *ctx) { size_t used, free; used = (size_t) (ctx->bytes & 0x3f); ctx->buffer[used++] = 0x80; free = 64 - used; if (free < 8) { njs_memzero(&ctx->buffer[used], free); (void) njs_sha1_body(ctx, ctx->buffer, 64); used = 0; free = 64; } njs_memzero(&ctx->buffer[used], free - 8); ctx->bytes <<= 3; ctx->buffer[56] = (u_char) (ctx->bytes >> 56); ctx->buffer[57] = (u_char) (ctx->bytes >> 48); ctx->buffer[58] = (u_char) (ctx->bytes >> 40); ctx->buffer[59] = (u_char) (ctx->bytes >> 32); ctx->buffer[60] = (u_char) (ctx->bytes >> 24); ctx->buffer[61] = (u_char) (ctx->bytes >> 16); ctx->buffer[62] = (u_char) (ctx->bytes >> 8); ctx->buffer[63] = (u_char) ctx->bytes; (void) njs_sha1_body(ctx, ctx->buffer, 64); result[0] = (u_char) (ctx->a >> 24); result[1] = (u_char) (ctx->a >> 16); result[2] = (u_char) (ctx->a >> 8); result[3] = (u_char) ctx->a; result[4] = (u_char) (ctx->b >> 24); result[5] = (u_char) (ctx->b >> 16); result[6] = (u_char) (ctx->b >> 8); result[7] = (u_char) ctx->b; result[8] = (u_char) (ctx->c >> 24); result[9] = (u_char) (ctx->c >> 16); result[10] = (u_char) (ctx->c >> 8); result[11] = (u_char) ctx->c; result[12] = (u_char) (ctx->d >> 24); result[13] = (u_char) (ctx->d >> 16); result[14] = (u_char) (ctx->d >> 8); result[15] = (u_char) ctx->d; result[16] = (u_char) (ctx->e >> 24); result[17] = (u_char) (ctx->e >> 16); result[18] = (u_char) (ctx->e >> 8); result[19] = (u_char) ctx->e; njs_explicit_memzero(ctx, sizeof(*ctx)); } /* * Helper functions. */ #define ROTATE(bits, word) (((word) << (bits)) | ((word) >> (32 - (bits)))) #define F1(b, c, d) (((b) & (c)) | ((~(b)) & (d))) #define F2(b, c, d) ((b) ^ (c) ^ (d)) #define F3(b, c, d) (((b) & (c)) | ((b) & (d)) | ((c) & (d))) #define STEP(f, a, b, c, d, e, w, t) \ temp = ROTATE(5, (a)) + f((b), (c), (d)) + (e) + (w) + (t); \ (e) = (d); \ (d) = (c); \ (c) = ROTATE(30, (b)); \ (b) = (a); \ (a) = temp; /* * GET() reads 4 input bytes in big-endian byte order and returns * them as uint32_t. */ #define GET(n) \ ( ((uint32_t) p[n * 4 + 3]) \ | ((uint32_t) p[n * 4 + 2] << 8) \ | ((uint32_t) p[n * 4 + 1] << 16) \ | ((uint32_t) p[n * 4] << 24)) /* * This processes one or more 64-byte data blocks, but does not update * the bit counters. There are no alignment requirements. */ static const u_char * njs_sha1_body(njs_hash_t *ctx, const u_char *data, size_t size) { uint32_t a, b, c, d, e, temp; uint32_t saved_a, saved_b, saved_c, saved_d, saved_e; uint32_t words[80]; njs_uint_t i; const u_char *p; p = data; a = ctx->a; b = ctx->b; c = ctx->c; d = ctx->d; e = ctx->e; do { saved_a = a; saved_b = b; saved_c = c; saved_d = d; saved_e = e; /* Load data block into the words array */ for (i = 0; i < 16; i++) { words[i] = GET(i); } for (i = 16; i < 80; i++) { words[i] = ROTATE(1, words[i - 3] ^ words[i - 8] ^ words[i - 14] ^ words[i - 16]); } /* Transformations */ STEP(F1, a, b, c, d, e, words[0], 0x5a827999); STEP(F1, a, b, c, d, e, words[1], 0x5a827999); STEP(F1, a, b, c, d, e, words[2], 0x5a827999); STEP(F1, a, b, c, d, e, words[3], 0x5a827999); STEP(F1, a, b, c, d, e, words[4], 0x5a827999); STEP(F1, a, b, c, d, e, words[5], 0x5a827999); STEP(F1, a, b, c, d, e, words[6], 0x5a827999); STEP(F1, a, b, c, d, e, words[7], 0x5a827999); STEP(F1, a, b, c, d, e, words[8], 0x5a827999); STEP(F1, a, b, c, d, e, words[9], 0x5a827999); STEP(F1, a, b, c, d, e, words[10], 0x5a827999); STEP(F1, a, b, c, d, e, words[11], 0x5a827999); STEP(F1, a, b, c, d, e, words[12], 0x5a827999); STEP(F1, a, b, c, d, e, words[13], 0x5a827999); STEP(F1, a, b, c, d, e, words[14], 0x5a827999); STEP(F1, a, b, c, d, e, words[15], 0x5a827999); STEP(F1, a, b, c, d, e, words[16], 0x5a827999); STEP(F1, a, b, c, d, e, words[17], 0x5a827999); STEP(F1, a, b, c, d, e, words[18], 0x5a827999); STEP(F1, a, b, c, d, e, words[19], 0x5a827999); STEP(F2, a, b, c, d, e, words[20], 0x6ed9eba1); STEP(F2, a, b, c, d, e, words[21], 0x6ed9eba1); STEP(F2, a, b, c, d, e, words[22], 0x6ed9eba1); STEP(F2, a, b, c, d, e, words[23], 0x6ed9eba1); STEP(F2, a, b, c, d, e, words[24], 0x6ed9eba1); STEP(F2, a, b, c, d, e, words[25], 0x6ed9eba1); STEP(F2, a, b, c, d, e, words[26], 0x6ed9eba1); STEP(F2, a, b, c, d, e, words[27], 0x6ed9eba1); STEP(F2, a, b, c, d, e, words[28], 0x6ed9eba1); STEP(F2, a, b, c, d, e, words[29], 0x6ed9eba1); STEP(F2, a, b, c, d, e, words[30], 0x6ed9eba1); STEP(F2, a, b, c, d, e, words[31], 0x6ed9eba1); STEP(F2, a, b, c, d, e, words[32], 0x6ed9eba1); STEP(F2, a, b, c, d, e, words[33], 0x6ed9eba1); STEP(F2, a, b, c, d, e, words[34], 0x6ed9eba1); STEP(F2, a, b, c, d, e, words[35], 0x6ed9eba1); STEP(F2, a, b, c, d, e, words[36], 0x6ed9eba1); STEP(F2, a, b, c, d, e, words[37], 0x6ed9eba1); STEP(F2, a, b, c, d, e, words[38], 0x6ed9eba1); STEP(F2, a, b, c, d, e, words[39], 0x6ed9eba1); STEP(F3, a, b, c, d, e, words[40], 0x8f1bbcdc); STEP(F3, a, b, c, d, e, words[41], 0x8f1bbcdc); STEP(F3, a, b, c, d, e, words[42], 0x8f1bbcdc); STEP(F3, a, b, c, d, e, words[43], 0x8f1bbcdc); STEP(F3, a, b, c, d, e, words[44], 0x8f1bbcdc); STEP(F3, a, b, c, d, e, words[45], 0x8f1bbcdc); STEP(F3, a, b, c, d, e, words[46], 0x8f1bbcdc); STEP(F3, a, b, c, d, e, words[47], 0x8f1bbcdc); STEP(F3, a, b, c, d, e, words[48], 0x8f1bbcdc); STEP(F3, a, b, c, d, e, words[49], 0x8f1bbcdc); STEP(F3, a, b, c, d, e, words[50], 0x8f1bbcdc); STEP(F3, a, b, c, d, e, words[51], 0x8f1bbcdc); STEP(F3, a, b, c, d, e, words[52], 0x8f1bbcdc); STEP(F3, a, b, c, d, e, words[53], 0x8f1bbcdc); STEP(F3, a, b, c, d, e, words[54], 0x8f1bbcdc); STEP(F3, a, b, c, d, e, words[55], 0x8f1bbcdc); STEP(F3, a, b, c, d, e, words[56], 0x8f1bbcdc); STEP(F3, a, b, c, d, e, words[57], 0x8f1bbcdc); STEP(F3, a, b, c, d, e, words[58], 0x8f1bbcdc); STEP(F3, a, b, c, d, e, words[59], 0x8f1bbcdc); STEP(F2, a, b, c, d, e, words[60], 0xca62c1d6); STEP(F2, a, b, c, d, e, words[61], 0xca62c1d6); STEP(F2, a, b, c, d, e, words[62], 0xca62c1d6); STEP(F2, a, b, c, d, e, words[63], 0xca62c1d6); STEP(F2, a, b, c, d, e, words[64], 0xca62c1d6); STEP(F2, a, b, c, d, e, words[65], 0xca62c1d6); STEP(F2, a, b, c, d, e, words[66], 0xca62c1d6); STEP(F2, a, b, c, d, e, words[67], 0xca62c1d6); STEP(F2, a, b, c, d, e, words[68], 0xca62c1d6); STEP(F2, a, b, c, d, e, words[69], 0xca62c1d6); STEP(F2, a, b, c, d, e, words[70], 0xca62c1d6); STEP(F2, a, b, c, d, e, words[71], 0xca62c1d6); STEP(F2, a, b, c, d, e, words[72], 0xca62c1d6); STEP(F2, a, b, c, d, e, words[73], 0xca62c1d6); STEP(F2, a, b, c, d, e, words[74], 0xca62c1d6); STEP(F2, a, b, c, d, e, words[75], 0xca62c1d6); STEP(F2, a, b, c, d, e, words[76], 0xca62c1d6); STEP(F2, a, b, c, d, e, words[77], 0xca62c1d6); STEP(F2, a, b, c, d, e, words[78], 0xca62c1d6); STEP(F2, a, b, c, d, e, words[79], 0xca62c1d6); a += saved_a; b += saved_b; c += saved_c; d += saved_d; e += saved_e; p += 64; } while (size -= 64); ctx->a = a; ctx->b = b; ctx->c = c; ctx->d = d; ctx->e = e; return p; } njs-0.8.9/external/njs_sha2.c000066400000000000000000000246211474132077100160320ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. * * An internal SHA2 implementation. */ #include #include #include #include #include "njs_hash.h" static const u_char *njs_sha2_body(njs_hash_t *ctx, const u_char *data, size_t size); void njs_sha2_init(njs_hash_t *ctx) { ctx->a = 0x6a09e667; ctx->b = 0xbb67ae85; ctx->c = 0x3c6ef372; ctx->d = 0xa54ff53a; ctx->e = 0x510e527f; ctx->f = 0x9b05688c; ctx->g = 0x1f83d9ab; ctx->h = 0x5be0cd19; ctx->bytes = 0; } void njs_sha2_update(njs_hash_t *ctx, const void *data, size_t size) { size_t used, free; used = (size_t) (ctx->bytes & 0x3f); ctx->bytes += size; if (used) { free = 64 - used; if (size < free) { memcpy(&ctx->buffer[used], data, size); return; } memcpy(&ctx->buffer[used], data, free); data = (u_char *) data + free; size -= free; (void) njs_sha2_body(ctx, ctx->buffer, 64); } if (size >= 64) { data = njs_sha2_body(ctx, data, size & ~(size_t) 0x3f); size &= 0x3f; } memcpy(ctx->buffer, data, size); } void njs_sha2_final(u_char result[32], njs_hash_t *ctx) { size_t used, free; used = (size_t) (ctx->bytes & 0x3f); ctx->buffer[used++] = 0x80; free = 64 - used; if (free < 8) { njs_memzero(&ctx->buffer[used], free); (void) njs_sha2_body(ctx, ctx->buffer, 64); used = 0; free = 64; } njs_memzero(&ctx->buffer[used], free - 8); ctx->bytes <<= 3; ctx->buffer[56] = (u_char) (ctx->bytes >> 56); ctx->buffer[57] = (u_char) (ctx->bytes >> 48); ctx->buffer[58] = (u_char) (ctx->bytes >> 40); ctx->buffer[59] = (u_char) (ctx->bytes >> 32); ctx->buffer[60] = (u_char) (ctx->bytes >> 24); ctx->buffer[61] = (u_char) (ctx->bytes >> 16); ctx->buffer[62] = (u_char) (ctx->bytes >> 8); ctx->buffer[63] = (u_char) ctx->bytes; (void) njs_sha2_body(ctx, ctx->buffer, 64); result[0] = (u_char) (ctx->a >> 24); result[1] = (u_char) (ctx->a >> 16); result[2] = (u_char) (ctx->a >> 8); result[3] = (u_char) ctx->a; result[4] = (u_char) (ctx->b >> 24); result[5] = (u_char) (ctx->b >> 16); result[6] = (u_char) (ctx->b >> 8); result[7] = (u_char) ctx->b; result[8] = (u_char) (ctx->c >> 24); result[9] = (u_char) (ctx->c >> 16); result[10] = (u_char) (ctx->c >> 8); result[11] = (u_char) ctx->c; result[12] = (u_char) (ctx->d >> 24); result[13] = (u_char) (ctx->d >> 16); result[14] = (u_char) (ctx->d >> 8); result[15] = (u_char) ctx->d; result[16] = (u_char) (ctx->e >> 24); result[17] = (u_char) (ctx->e >> 16); result[18] = (u_char) (ctx->e >> 8); result[19] = (u_char) ctx->e; result[20] = (u_char) (ctx->f >> 24); result[21] = (u_char) (ctx->f >> 16); result[22] = (u_char) (ctx->f >> 8); result[23] = (u_char) ctx->f; result[24] = (u_char) (ctx->g >> 24); result[25] = (u_char) (ctx->g >> 16); result[26] = (u_char) (ctx->g >> 8); result[27] = (u_char) ctx->g; result[28] = (u_char) (ctx->h >> 24); result[29] = (u_char) (ctx->h >> 16); result[30] = (u_char) (ctx->h >> 8); result[31] = (u_char) ctx->h; njs_explicit_memzero(ctx, sizeof(*ctx)); } /* * Helper functions. */ #define ROTATE(bits, word) (((word) >> (bits)) | ((word) << (32 - (bits)))) #define S0(a) (ROTATE(2, a) ^ ROTATE(13, a) ^ ROTATE(22, a)) #define S1(e) (ROTATE(6, e) ^ ROTATE(11, e) ^ ROTATE(25, e)) #define CH(e, f, g) (((e) & (f)) ^ ((~(e)) & (g))) #define MAJ(a, b, c) (((a) & (b)) ^ ((a) & (c)) ^ ((b) & (c))) #define STEP(a, b, c, d, e, f, g, h, w, k) \ temp1 = (h) + S1(e) + CH(e, f, g) + (k) + (w); \ temp2 = S0(a) + MAJ(a, b, c); \ (h) = (g); \ (g) = (f); \ (f) = (e); \ (e) = (d) + temp1; \ (d) = (c); \ (c) = (b); \ (b) = (a); \ (a) = temp1 + temp2; /* * GET() reads 4 input bytes in big-endian byte order and returns * them as uint32_t. */ #define GET(n) \ ( ((uint32_t) p[n * 4 + 3]) \ | ((uint32_t) p[n * 4 + 2] << 8) \ | ((uint32_t) p[n * 4 + 1] << 16) \ | ((uint32_t) p[n * 4] << 24)) /* * This processes one or more 64-byte data blocks, but does not update * the bit counters. There are no alignment requirements. */ static const u_char * njs_sha2_body(njs_hash_t *ctx, const u_char *data, size_t size) { uint32_t a, b, c, d, e, f, g, h, s0, s1, temp1, temp2; uint32_t saved_a, saved_b, saved_c, saved_d, saved_e, saved_f, saved_g, saved_h; uint32_t words[64]; njs_uint_t i; const u_char *p; p = data; a = ctx->a; b = ctx->b; c = ctx->c; d = ctx->d; e = ctx->e; f = ctx->f; g = ctx->g; h = ctx->h; do { saved_a = a; saved_b = b; saved_c = c; saved_d = d; saved_e = e; saved_f = f; saved_g = g; saved_h = h; /* Load data block into the words array */ for (i = 0; i < 16; i++) { words[i] = GET(i); } for (i = 16; i < 64; i++) { s0 = ROTATE(7, words[i - 15]) ^ ROTATE(18, words[i - 15]) ^ (words[i - 15] >> 3); s1 = ROTATE(17, words[i - 2]) ^ ROTATE(19, words[i - 2]) ^ (words[i - 2] >> 10); words[i] = words[i - 16] + s0 + words[i - 7] + s1; } /* Transformations */ STEP(a, b, c, d, e, f, g, h, words[0], 0x428a2f98); STEP(a, b, c, d, e, f, g, h, words[1], 0x71374491); STEP(a, b, c, d, e, f, g, h, words[2], 0xb5c0fbcf); STEP(a, b, c, d, e, f, g, h, words[3], 0xe9b5dba5); STEP(a, b, c, d, e, f, g, h, words[4], 0x3956c25b); STEP(a, b, c, d, e, f, g, h, words[5], 0x59f111f1); STEP(a, b, c, d, e, f, g, h, words[6], 0x923f82a4); STEP(a, b, c, d, e, f, g, h, words[7], 0xab1c5ed5); STEP(a, b, c, d, e, f, g, h, words[8], 0xd807aa98); STEP(a, b, c, d, e, f, g, h, words[9], 0x12835b01); STEP(a, b, c, d, e, f, g, h, words[10], 0x243185be); STEP(a, b, c, d, e, f, g, h, words[11], 0x550c7dc3); STEP(a, b, c, d, e, f, g, h, words[12], 0x72be5d74); STEP(a, b, c, d, e, f, g, h, words[13], 0x80deb1fe); STEP(a, b, c, d, e, f, g, h, words[14], 0x9bdc06a7); STEP(a, b, c, d, e, f, g, h, words[15], 0xc19bf174); STEP(a, b, c, d, e, f, g, h, words[16], 0xe49b69c1); STEP(a, b, c, d, e, f, g, h, words[17], 0xefbe4786); STEP(a, b, c, d, e, f, g, h, words[18], 0x0fc19dc6); STEP(a, b, c, d, e, f, g, h, words[19], 0x240ca1cc); STEP(a, b, c, d, e, f, g, h, words[20], 0x2de92c6f); STEP(a, b, c, d, e, f, g, h, words[21], 0x4a7484aa); STEP(a, b, c, d, e, f, g, h, words[22], 0x5cb0a9dc); STEP(a, b, c, d, e, f, g, h, words[23], 0x76f988da); STEP(a, b, c, d, e, f, g, h, words[24], 0x983e5152); STEP(a, b, c, d, e, f, g, h, words[25], 0xa831c66d); STEP(a, b, c, d, e, f, g, h, words[26], 0xb00327c8); STEP(a, b, c, d, e, f, g, h, words[27], 0xbf597fc7); STEP(a, b, c, d, e, f, g, h, words[28], 0xc6e00bf3); STEP(a, b, c, d, e, f, g, h, words[29], 0xd5a79147); STEP(a, b, c, d, e, f, g, h, words[30], 0x06ca6351); STEP(a, b, c, d, e, f, g, h, words[31], 0x14292967); STEP(a, b, c, d, e, f, g, h, words[32], 0x27b70a85); STEP(a, b, c, d, e, f, g, h, words[33], 0x2e1b2138); STEP(a, b, c, d, e, f, g, h, words[34], 0x4d2c6dfc); STEP(a, b, c, d, e, f, g, h, words[35], 0x53380d13); STEP(a, b, c, d, e, f, g, h, words[36], 0x650a7354); STEP(a, b, c, d, e, f, g, h, words[37], 0x766a0abb); STEP(a, b, c, d, e, f, g, h, words[38], 0x81c2c92e); STEP(a, b, c, d, e, f, g, h, words[39], 0x92722c85); STEP(a, b, c, d, e, f, g, h, words[40], 0xa2bfe8a1); STEP(a, b, c, d, e, f, g, h, words[41], 0xa81a664b); STEP(a, b, c, d, e, f, g, h, words[42], 0xc24b8b70); STEP(a, b, c, d, e, f, g, h, words[43], 0xc76c51a3); STEP(a, b, c, d, e, f, g, h, words[44], 0xd192e819); STEP(a, b, c, d, e, f, g, h, words[45], 0xd6990624); STEP(a, b, c, d, e, f, g, h, words[46], 0xf40e3585); STEP(a, b, c, d, e, f, g, h, words[47], 0x106aa070); STEP(a, b, c, d, e, f, g, h, words[48], 0x19a4c116); STEP(a, b, c, d, e, f, g, h, words[49], 0x1e376c08); STEP(a, b, c, d, e, f, g, h, words[50], 0x2748774c); STEP(a, b, c, d, e, f, g, h, words[51], 0x34b0bcb5); STEP(a, b, c, d, e, f, g, h, words[52], 0x391c0cb3); STEP(a, b, c, d, e, f, g, h, words[53], 0x4ed8aa4a); STEP(a, b, c, d, e, f, g, h, words[54], 0x5b9cca4f); STEP(a, b, c, d, e, f, g, h, words[55], 0x682e6ff3); STEP(a, b, c, d, e, f, g, h, words[56], 0x748f82ee); STEP(a, b, c, d, e, f, g, h, words[57], 0x78a5636f); STEP(a, b, c, d, e, f, g, h, words[58], 0x84c87814); STEP(a, b, c, d, e, f, g, h, words[59], 0x8cc70208); STEP(a, b, c, d, e, f, g, h, words[60], 0x90befffa); STEP(a, b, c, d, e, f, g, h, words[61], 0xa4506ceb); STEP(a, b, c, d, e, f, g, h, words[62], 0xbef9a3f7); STEP(a, b, c, d, e, f, g, h, words[63], 0xc67178f2); a += saved_a; b += saved_b; c += saved_c; d += saved_d; e += saved_e; f += saved_f; g += saved_g; h += saved_h; p += 64; } while (size -= 64); ctx->a = a; ctx->b = b; ctx->c = c; ctx->d = d; ctx->e = e; ctx->f = f; ctx->g = g; ctx->h = h; return p; } njs-0.8.9/external/njs_shell.c000066400000000000000000002755001474132077100163100ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #include #include #include #include #include #if (NJS_HAVE_QUICKJS) #include #endif #if (!defined NJS_FUZZER_TARGET && defined NJS_HAVE_READLINE) #include #include #include #if (NJS_HAVE_EDITLINE) #include #elif (NJS_HAVE_EDIT_READLINE) #include #else #include #endif #endif typedef struct { uint8_t disassemble; uint8_t denormals; uint8_t interactive; uint8_t module; uint8_t quiet; uint8_t sandbox; uint8_t safe; uint8_t version; uint8_t ast; uint8_t unhandled_rejection; uint8_t suppress_stdout; uint8_t opcode_debug; uint8_t generator_debug; uint8_t can_block; int exit_code; int stack_size; char *file; njs_str_t command; size_t n_paths; njs_str_t *paths; char **argv; njs_uint_t argc; enum { NJS_ENGINE_NJS = 0, NJS_ENGINE_QUICKJS = 1, } engine; } njs_opts_t; typedef enum { NJS_LOG_ERROR = 4, NJS_LOG_WARN = 5, NJS_LOG_INFO = 7, } njs_log_level_t; typedef struct { size_t index; njs_arr_t *suffix_completions; } njs_completion_t; typedef struct { NJS_RBTREE_NODE (node); union { struct { njs_function_t *function; njs_value_t *args; } njs; #if (NJS_HAVE_QUICKJS) struct { JSValue function; JSValue *args; } qjs; #endif } u; njs_uint_t nargs; uint32_t id; njs_queue_link_t link; } njs_ev_t; typedef struct { njs_str_t name; uint64_t time; njs_queue_link_t link; } njs_timelabel_t; typedef struct { union { struct { njs_opaque_value_t promise; njs_opaque_value_t message; } njs; #if (NJS_HAVE_QUICKJS) struct { JSValue promise; JSValue message; } qjs; #endif } u; } njs_rejected_promise_t; typedef struct { int fd; njs_str_t name; njs_str_t file; char path[NJS_MAX_PATH + 1]; } njs_module_info_t; typedef struct njs_engine_s njs_engine_t; struct njs_engine_s { union { struct { njs_vm_t *vm; njs_opaque_value_t value; } njs; #if (NJS_HAVE_QUICKJS) struct { JSRuntime *rt; JSContext *ctx; JSValue value; } qjs; #endif } u; njs_int_t (*eval)(njs_engine_t *engine, njs_str_t *script); njs_int_t (*execute_pending_job)(njs_engine_t *engine); njs_int_t (*unhandled_rejection)(njs_engine_t *engine); njs_int_t (*process_events)(njs_engine_t *engine); njs_int_t (*destroy)(njs_engine_t *engine); njs_int_t (*output)(njs_engine_t *engine, njs_int_t ret); njs_arr_t *(*complete)(njs_engine_t *engine, njs_str_t *ex); unsigned type; njs_mp_t *pool; njs_completion_t completion; }; typedef struct { njs_engine_t *engine; uint32_t event_id; njs_rbtree_t events; /* njs_ev_t * */ njs_queue_t posted_events; njs_queue_t labels; njs_str_t cwd; njs_arr_t *rejected_promises; njs_bool_t suppress_stdout; njs_bool_t interactive; njs_bool_t module; char **argv; njs_uint_t argc; #if (NJS_HAVE_QUICKJS) JSValue process; njs_queue_t agents; njs_queue_t reports; pthread_mutex_t agent_mutex; pthread_cond_t agent_cond; pthread_mutex_t report_mutex; #endif } njs_console_t; #if (NJS_HAVE_QUICKJS) typedef struct { njs_queue_link_t link; pthread_t tid; njs_console_t *console; char *script; JSValue broadcast_func; njs_bool_t broadcast_pending; JSValue broadcast_sab; uint8_t *broadcast_sab_buf; size_t broadcast_sab_size; int32_t broadcast_val; } njs_262agent_t; typedef struct { njs_queue_link_t link; char *str; } njs_agent_report_t; #endif static njs_int_t njs_main(njs_opts_t *opts); static njs_int_t njs_console_init(njs_opts_t *opts, njs_console_t *console); static njs_int_t njs_externals_init(njs_vm_t *vm); static njs_engine_t *njs_create_engine(njs_opts_t *opts); static njs_int_t njs_process_file(njs_opts_t *opts); static njs_int_t njs_process_script(njs_engine_t *engine, njs_console_t *console, njs_str_t *script); #ifndef NJS_FUZZER_TARGET static njs_int_t njs_options_parse(njs_opts_t *opts, int argc, char **argv); static njs_int_t njs_options_parse_engine(njs_opts_t *opts, const char *engine); static njs_int_t njs_options_add_path(njs_opts_t *opts, char *path, size_t len); static void njs_options_free(njs_opts_t *opts); #ifdef NJS_HAVE_READLINE static njs_int_t njs_interactive_shell(njs_opts_t *opts); static njs_int_t njs_editline_init(void); static char *njs_completion_generator(const char *text, int state); #endif #endif static njs_int_t njs_set_timeout(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_set_immediate(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_clear_timeout(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_ext_console_log(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t magic, njs_value_t *retval); static njs_int_t njs_ext_console_time(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_ext_console_time_end(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static void njs_console_log(njs_log_level_t level, const char *fmt, ...); static void njs_console_logger(njs_log_level_t level, const u_char *start, size_t length); static njs_int_t njs_console_time(njs_console_t *console, njs_str_t *name); static void njs_console_time_end(njs_console_t *console, njs_str_t *name, uint64_t time); static intptr_t njs_event_rbtree_compare(njs_rbtree_node_t *node1, njs_rbtree_node_t *node2); static uint64_t njs_time(void); njs_int_t njs_array_buffer_detach(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_external_t njs_ext_console[] = { { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("dump"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_ext_console_log, #define NJS_LOG_DUMP 16 #define NJS_LOG_MASK 15 .magic8 = NJS_LOG_INFO | NJS_LOG_DUMP, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("error"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_ext_console_log, .magic8 = NJS_LOG_ERROR, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("info"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_ext_console_log, .magic8 = NJS_LOG_INFO, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("log"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_ext_console_log, .magic8 = NJS_LOG_INFO, } }, { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "Console", } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("time"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_ext_console_time, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("timeEnd"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_ext_console_time_end, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("warn"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_ext_console_log, .magic8 = NJS_LOG_WARN, } }, }; static njs_external_t njs_ext_262[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "$262", } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("detachArrayBuffer"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_array_buffer_detach, } }, }; njs_module_t njs_console_module = { .name = njs_str("console"), .preinit = NULL, .init = njs_externals_init, }; static njs_module_t *njs_console_addon_modules[] = { &njs_console_module, NULL, }; static njs_int_t njs_console_proto_id; static njs_console_t njs_console; static njs_int_t njs_main(njs_opts_t *opts) { njs_int_t ret; njs_engine_t *engine; njs_mm_denormals(opts->denormals); if (opts->file == NULL) { if (opts->command.length != 0) { opts->file = (char *) "string"; } #ifdef NJS_HAVE_READLINE else if (opts->interactive) { opts->file = (char *) "shell"; } #endif if (opts->file == NULL) { njs_stderror("file name is required in non-interactive mode\n"); return NJS_ERROR; } } ret = njs_console_init(opts, &njs_console); if (njs_slow_path(ret != NJS_OK)) { njs_stderror("njs_console_init() failed\n"); return NJS_ERROR; } #if (!defined NJS_FUZZER_TARGET && defined NJS_HAVE_READLINE) if (opts->interactive) { ret = njs_interactive_shell(opts); } else #endif if (opts->command.length != 0) { engine = njs_create_engine(opts); if (engine == NULL) { return NJS_ERROR; } ret = njs_process_script(engine, &njs_console, &opts->command); engine->destroy(engine); } else { ret = njs_process_file(opts); } return ret; } #ifndef NJS_FUZZER_TARGET int main(int argc, char **argv) { njs_int_t ret; njs_opts_t opts; njs_memzero(&opts, sizeof(njs_opts_t)); opts.interactive = 1; ret = njs_options_parse(&opts, argc, argv); if (ret != NJS_OK) { ret = (ret == NJS_DONE) ? NJS_OK : NJS_ERROR; goto done; } if (opts.version != 0) { njs_printf("%s\n", NJS_VERSION); ret = NJS_OK; goto done; } ret = njs_main(&opts); done: njs_options_free(&opts); return (ret == NJS_OK) ? EXIT_SUCCESS : opts.exit_code; } static njs_int_t njs_options_parse(njs_opts_t *opts, int argc, char **argv) { char *p, *start; size_t len; njs_int_t i, ret; njs_uint_t n; static const char help[] = "njs [options] [-c string | script.js | -] [script args]\n" "\n" "Interactive shell: " #ifdef NJS_HAVE_READLINE "enabled\n" #else "disabled\n" #endif "\n" "Options:\n" " -a print AST.\n" " -c specify the command to execute.\n" " -d print disassembled code.\n" " -e set failure exit code.\n" " -f disabled denormals mode.\n" #ifdef NJS_DEBUG_GENERATOR " -g enable generator debug.\n" #endif " -j set the maximum stack size in bytes.\n" " -m load as ES6 module (script is default).\n" #ifdef NJS_HAVE_QUICKJS " -n njs|QuickJS set JS engine (njs is default)\n" #endif #ifdef NJS_DEBUG_OPCODE " -o enable opcode debug.\n" #endif " -p set path prefix for modules.\n" " -q disable interactive introduction prompt.\n" " -r ignore unhandled promise rejection.\n" " -s sandbox mode.\n" " -v print njs version and exit.\n" " -u disable \"unsafe\" mode.\n" " script.js | - run code from a file or stdin.\n"; opts->denormals = 1; opts->can_block = 1; opts->exit_code = EXIT_FAILURE; opts->engine = NJS_ENGINE_NJS; opts->unhandled_rejection = 1; p = getenv("NJS_EXIT_CODE"); if (p != NULL) { opts->exit_code = atoi(p); } p = getenv("NJS_CAN_BLOCK"); if (p != NULL) { opts->can_block = atoi(p); } p = getenv("NJS_LOAD_AS_MODULE"); if (p != NULL) { opts->module = 1; } p = getenv("NJS_ENGINE"); if (p != NULL) { ret = njs_options_parse_engine(opts, p); if (ret != NJS_OK) { return NJS_ERROR; } } start = getenv("NJS_PATH"); if (start != NULL) { for ( ;; ) { p = (char *) njs_strchr(start, ':'); len = (p != NULL) ? (size_t) (p - start) : njs_strlen(start); ret = njs_options_add_path(opts, start, len); if (ret != NJS_OK) { njs_stderror("failed to add path\n"); return NJS_ERROR; } if (p == NULL) { break; } start = p + 1; } } for (i = 1; i < argc; i++) { p = argv[i]; if (p[0] != '-' || (p[0] == '-' && p[1] == '\0')) { opts->interactive = 0; opts->file = argv[i]; goto done; } p++; switch (*p) { case '?': case 'h': njs_printf("%*s", njs_length(help), help); return NJS_DONE; case 'a': opts->ast = 1; break; case 'c': opts->interactive = 0; if (++i < argc) { opts->command.start = (u_char *) argv[i]; opts->command.length = njs_strlen(argv[i]); goto done; } njs_stderror("option \"-c\" requires argument\n"); return NJS_ERROR; case 'd': opts->disassemble = 1; break; case 'e': if (++i < argc) { opts->exit_code = atoi(argv[i]); break; } njs_stderror("option \"-e\" requires argument\n"); return NJS_ERROR; case 'f': #if !(NJS_HAVE_DENORMALS_CONTROL) njs_stderror("option \"-f\" is not supported\n"); return NJS_ERROR; #endif opts->denormals = 0; break; #ifdef NJS_DEBUG_GENERATOR case 'g': opts->generator_debug = 1; break; #endif case 'j': if (++i < argc) { opts->stack_size = atoi(argv[i]); break; } njs_stderror("option \"-j\" requires argument\n"); return NJS_ERROR; case 'm': opts->module = 1; break; case 'n': if (++i < argc) { ret = njs_options_parse_engine(opts, argv[i]); if (ret != NJS_OK) { return NJS_ERROR; } break; } njs_stderror("option \"-n\" requires argument\n"); return NJS_ERROR; #ifdef NJS_DEBUG_OPCODE case 'o': opts->opcode_debug = 1; break; #endif case 'p': if (++i < argc) { ret = njs_options_add_path(opts, argv[i], njs_strlen(argv[i])); if (ret != NJS_OK) { njs_stderror("failed to add path\n"); return NJS_ERROR; } break; } njs_stderror("option \"-p\" requires directory name\n"); return NJS_ERROR; case 'q': opts->quiet = 1; break; case 'r': opts->unhandled_rejection = 0; break; case 's': opts->sandbox = 1; break; case 't': if (++i < argc) { if (strcmp(argv[i], "module") == 0) { opts->module = 1; } else if (strcmp(argv[i], "script") != 0) { njs_stderror("option \"-t\" unexpected source type: %s\n", argv[i]); return NJS_ERROR; } break; } njs_stderror("option \"-t\" requires source type\n"); return NJS_ERROR; case 'v': case 'V': opts->version = 1; break; case 'u': opts->safe = 1; break; default: njs_stderror("Unknown argument: \"%s\" " "try \"%s -h\" for available options\n", argv[i], argv[0]); return NJS_ERROR; } } done: #ifdef NJS_HAVE_QUICKJS if (opts->engine == NJS_ENGINE_QUICKJS) { if (opts->ast) { njs_stderror("option \"-a\" is not supported for quickjs\n"); return NJS_ERROR; } if (opts->disassemble) { njs_stderror("option \"-d\" is not supported for quickjs\n"); return NJS_ERROR; } if (opts->generator_debug) { njs_stderror("option \"-g\" is not supported for quickjs\n"); return NJS_ERROR; } if (opts->opcode_debug) { njs_stderror("option \"-o\" is not supported for quickjs\n"); return NJS_ERROR; } if (opts->sandbox) { njs_stderror("option \"-s\" is not supported for quickjs\n"); return NJS_ERROR; } if (opts->safe) { njs_stderror("option \"-u\" is not supported for quickjs\n"); return NJS_ERROR; } } #endif opts->argc = njs_max(argc - i + 1, 2); opts->argv = malloc(sizeof(char*) * opts->argc); if (opts->argv == NULL) { njs_stderror("failed to alloc argv\n"); return NJS_ERROR; } opts->argv[0] = argv[0]; opts->argv[1] = (opts->file != NULL) ? opts->file : (char *) ""; for (n = 2; n < opts->argc; n++) { opts->argv[n] = argv[i + n - 1]; } return NJS_OK; } static njs_int_t njs_options_parse_engine(njs_opts_t *opts, const char *engine) { if (strncasecmp(engine, "njs", 3) == 0) { opts->engine = NJS_ENGINE_NJS; #ifdef NJS_HAVE_QUICKJS } else if (strncasecmp(engine, "QuickJS", 7) == 0) { opts->engine = NJS_ENGINE_QUICKJS; #endif } else { njs_stderror("unknown engine \"%s\"\n", engine); return NJS_ERROR; } return NJS_OK; } static njs_int_t njs_options_add_path(njs_opts_t *opts, char *path, size_t len) { njs_str_t *paths; opts->n_paths++; paths = realloc(opts->paths, opts->n_paths * sizeof(njs_str_t)); if (paths == NULL) { njs_stderror("failed to add path\n"); return NJS_ERROR; } opts->paths = paths; opts->paths[opts->n_paths - 1].start = (u_char *) path; opts->paths[opts->n_paths - 1].length = len; return NJS_OK; } static void njs_options_free(njs_opts_t *opts) { if (opts->paths != NULL) { free(opts->paths); } if (opts->argv != NULL) { free(opts->argv); } } #else int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { njs_opts_t opts; if (size == 0) { return 0; } njs_memzero(&opts, sizeof(njs_opts_t)); opts.file = (char *) "fuzzer"; opts.command.start = (u_char *) data; opts.command.length = size; opts.suppress_stdout = 1; return njs_main(&opts); } #endif static njs_int_t njs_console_init(njs_opts_t *opts, njs_console_t *console) { njs_memzero(console, sizeof(njs_console_t)); njs_rbtree_init(&console->events, njs_event_rbtree_compare); njs_queue_init(&console->posted_events); njs_queue_init(&console->labels); console->interactive = opts->interactive; console->suppress_stdout = opts->suppress_stdout; console->module = opts->module; console->argv = opts->argv; console->argc = opts->argc; #if (NJS_HAVE_QUICKJS) if (opts->engine == NJS_ENGINE_QUICKJS) { njs_queue_init(&console->agents); njs_queue_init(&console->reports); pthread_mutex_init(&console->report_mutex, NULL); pthread_mutex_init(&console->agent_mutex, NULL); pthread_cond_init(&console->agent_cond, NULL); console->process = JS_UNDEFINED; } #endif return NJS_OK; } static njs_int_t njs_function_bind(njs_vm_t *vm, const njs_str_t *name, njs_function_native_t native, njs_bool_t ctor) { njs_function_t *f; njs_opaque_value_t value; f = njs_vm_function_alloc(vm, native, 1, ctor); if (f == NULL) { return NJS_ERROR; } njs_value_function_set(njs_value_arg(&value), f); return njs_vm_bind(vm, name, njs_value_arg(&value), 1); } static njs_int_t njs_externals_init(njs_vm_t *vm) { njs_int_t ret, proto_id; njs_console_t *console; njs_opaque_value_t value, method; static const njs_str_t console_name = njs_str("console"); static const njs_str_t dollar_262 = njs_str("$262"); static const njs_str_t print_name = njs_str("print"); static const njs_str_t console_log = njs_str("console.log"); static const njs_str_t set_timeout = njs_str("setTimeout"); static const njs_str_t set_immediate = njs_str("setImmediate"); static const njs_str_t clear_timeout = njs_str("clearTimeout"); console = njs_vm_external_ptr(vm); njs_console_proto_id = njs_vm_external_prototype(vm, njs_ext_console, njs_nitems(njs_ext_console)); if (njs_slow_path(njs_console_proto_id < 0)) { njs_stderror("failed to add \"console\" proto\n"); return NJS_ERROR; } ret = njs_vm_external_create(vm, njs_value_arg(&value), njs_console_proto_id, console, 0); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_vm_bind(vm, &console_name, njs_value_arg(&value), 0); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_vm_value(vm, &console_log, njs_value_arg(&method)); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_vm_bind(vm, &print_name, njs_value_arg(&method), 0); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_function_bind(vm, &set_timeout, njs_set_timeout, 0); if (ret != NJS_OK) { return NJS_ERROR; } ret = njs_function_bind(vm, &set_immediate, njs_set_immediate, 0); if (ret != NJS_OK) { return NJS_ERROR; } ret = njs_function_bind(vm, &clear_timeout, njs_clear_timeout, 0); if (ret != NJS_OK) { return NJS_ERROR; } proto_id = njs_vm_external_prototype(vm, njs_ext_262, njs_nitems(njs_ext_262)); if (njs_slow_path(proto_id < 0)) { njs_stderror("failed to add \"$262\" proto\n"); return NJS_ERROR; } ret = njs_vm_external_create(vm, njs_value_arg(&value), proto_id, NULL, 1); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_vm_bind(vm, &dollar_262, njs_value_arg(&value), 1); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } return NJS_OK; } static void njs_rejection_tracker(njs_vm_t *vm, njs_external_ptr_t external, njs_bool_t is_handled, njs_value_t *promise, njs_value_t *reason) { void *promise_obj; uint32_t i, length; njs_console_t *console; njs_rejected_promise_t *rejected_promise; console = external; if (is_handled && console->rejected_promises != NULL) { rejected_promise = console->rejected_promises->start; length = console->rejected_promises->items; promise_obj = njs_value_ptr(promise); for (i = 0; i < length; i++) { if (njs_value_ptr(njs_value_arg(&rejected_promise[i].u.njs.promise)) == promise_obj) { njs_arr_remove(console->rejected_promises, &rejected_promise[i]); break; } } return; } if (console->rejected_promises == NULL) { console->rejected_promises = njs_arr_create(console->engine->pool, 4, sizeof(njs_rejected_promise_t)); if (njs_slow_path(console->rejected_promises == NULL)) { return; } } rejected_promise = njs_arr_add(console->rejected_promises); if (njs_slow_path(rejected_promise == NULL)) { return; } njs_value_assign(&rejected_promise->u.njs.promise, promise); njs_value_assign(&rejected_promise->u.njs.message, reason); } static njs_int_t njs_module_path(const njs_str_t *dir, njs_module_info_t *info) { char *p; size_t length; njs_bool_t trail; char src[NJS_MAX_PATH + 1]; trail = 0; length = info->name.length; if (dir != NULL) { length += dir->length; if (length == 0 || dir->length == 0) { return NJS_DECLINED; } trail = (dir->start[dir->length - 1] != '/'); if (trail) { length++; } } if (njs_slow_path(length > NJS_MAX_PATH)) { return NJS_ERROR; } p = &src[0]; if (dir != NULL) { p = (char *) njs_cpymem(p, dir->start, dir->length); if (trail) { *p++ = '/'; } } p = (char *) njs_cpymem(p, info->name.start, info->name.length); *p = '\0'; p = realpath(&src[0], &info->path[0]); if (p == NULL) { return NJS_DECLINED; } info->fd = open(&info->path[0], O_RDONLY); if (info->fd < 0) { return NJS_DECLINED; } info->file.start = (u_char *) &info->path[0]; info->file.length = njs_strlen(info->file.start); return NJS_OK; } static njs_int_t njs_module_lookup(njs_opts_t *opts, const njs_str_t *cwd, njs_module_info_t *info) { njs_int_t ret; njs_str_t *path; njs_uint_t i; if (info->name.start[0] == '/') { return njs_module_path(NULL, info); } ret = njs_module_path(cwd, info); if (ret != NJS_DECLINED) { return ret; } path = opts->paths; for (i = 0; i < opts->n_paths; i++) { ret = njs_module_path(&path[i], info); if (ret != NJS_DECLINED) { return ret; } } return NJS_DECLINED; } static njs_int_t njs_module_read(njs_mp_t *mp, int fd, njs_str_t *text) { ssize_t n; struct stat sb; text->start = NULL; if (fstat(fd, &sb) == -1) { goto fail; } if (!S_ISREG(sb.st_mode)) { goto fail; } text->length = sb.st_size; text->start = njs_mp_alloc(mp, text->length + 1); if (text->start == NULL) { goto fail; } n = read(fd, text->start, sb.st_size); if (n < 0 || n != sb.st_size) { goto fail; } text->start[text->length] = '\0'; return NJS_OK; fail: if (text->start != NULL) { njs_mp_free(mp, text->start); } return NJS_ERROR; } static void njs_file_dirname(const njs_str_t *path, njs_str_t *name) { const u_char *p, *end; if (path->length == 0) { goto current_dir; } p = path->start + path->length - 1; /* Stripping basename. */ while (p >= path->start && *p != '/') { p--; } end = p + 1; if (end == path->start) { goto current_dir; } /* Stripping trailing slashes. */ while (p >= path->start && *p == '/') { p--; } p++; if (p == path->start) { p = end; } name->start = path->start; name->length = p - path->start; return; current_dir: *name = njs_str_value("."); } static njs_int_t njs_console_set_cwd(njs_console_t *console, njs_str_t *file) { njs_str_t cwd; njs_file_dirname(file, &cwd); console->cwd.start = njs_mp_alloc(console->engine->pool, cwd.length); if (njs_slow_path(console->cwd.start == NULL)) { return NJS_ERROR; } memcpy(console->cwd.start, cwd.start, cwd.length); console->cwd.length = cwd.length; return NJS_OK; } static njs_mod_t * njs_module_loader(njs_vm_t *vm, njs_external_ptr_t external, njs_str_t *name) { u_char *start; njs_int_t ret; njs_str_t text, prev_cwd; njs_mod_t *module; njs_opts_t *opts; njs_console_t *console; njs_module_info_t info; opts = external; console = njs_vm_external_ptr(vm); njs_memzero(&info, sizeof(njs_module_info_t)); info.name = *name; ret = njs_module_lookup(opts, &console->cwd, &info); if (njs_slow_path(ret != NJS_OK)) { return NULL; } ret = njs_module_read(console->engine->pool, info.fd, &text); (void) close(info.fd); if (njs_slow_path(ret != NJS_OK)) { njs_vm_internal_error(vm, "while reading \"%V\" module", &info.file); return NULL; } prev_cwd = console->cwd; ret = njs_console_set_cwd(console, &info.file); if (njs_slow_path(ret != NJS_OK)) { njs_vm_internal_error(vm, "while setting cwd for \"%V\" module", &info.file); return NULL; } start = text.start; module = njs_vm_compile_module(vm, &info.file, &start, &text.start[text.length]); njs_mp_free(console->engine->pool, console->cwd.start); console->cwd = prev_cwd; njs_mp_free(console->engine->pool, text.start); return module; } static njs_int_t njs_engine_njs_init(njs_engine_t *engine, njs_opts_t *opts) { njs_vm_t *vm; njs_int_t ret; njs_vm_opt_t vm_options; njs_vm_opt_init(&vm_options); vm_options.file.start = (u_char *) opts->file; vm_options.file.length = njs_strlen(opts->file); vm_options.init = 1; vm_options.interactive = opts->interactive; vm_options.disassemble = opts->disassemble; vm_options.backtrace = 1; vm_options.quiet = opts->quiet; vm_options.sandbox = opts->sandbox; vm_options.unsafe = !opts->safe; vm_options.module = opts->module; #ifdef NJS_DEBUG_GENERATOR vm_options.generator_debug = opts->generator_debug; #endif #ifdef NJS_DEBUG_OPCODE vm_options.opcode_debug = opts->opcode_debug; #endif vm_options.addons = njs_console_addon_modules; vm_options.external = &njs_console; vm_options.argv = opts->argv; vm_options.argc = opts->argc; vm_options.ast = opts->ast; if (opts->stack_size != 0) { vm_options.max_stack_size = opts->stack_size; } vm = njs_vm_create(&vm_options); if (vm == NULL) { njs_stderror("failed to create vm\n"); return NJS_ERROR; } if (opts->unhandled_rejection) { njs_vm_set_rejection_tracker(vm, njs_rejection_tracker, njs_vm_external_ptr(vm)); } ret = njs_console_set_cwd(njs_vm_external_ptr(vm), &vm_options.file); if (njs_slow_path(ret != NJS_OK)) { njs_stderror("failed to set cwd\n"); return NJS_ERROR; } njs_vm_set_module_loader(vm, njs_module_loader, opts); engine->u.njs.vm = vm; return NJS_OK; } static njs_int_t njs_engine_njs_destroy(njs_engine_t *engine) { njs_vm_destroy(engine->u.njs.vm); njs_mp_destroy(engine->pool); return NJS_OK; } static njs_int_t njs_engine_njs_eval(njs_engine_t *engine, njs_str_t *script) { u_char *start, *end; njs_int_t ret; start = script->start; end = start + script->length; ret = njs_vm_compile(engine->u.njs.vm, &start, end); if (ret == NJS_OK && start == end) { return njs_vm_start(engine->u.njs.vm, njs_value_arg(&engine->u.njs.value)); } return NJS_ERROR; } static njs_int_t njs_engine_njs_execute_pending_job(njs_engine_t *engine) { return njs_vm_execute_pending_job(engine->u.njs.vm); } static njs_int_t njs_engine_njs_output(njs_engine_t *engine, njs_int_t ret) { njs_vm_t *vm; njs_str_t out; njs_console_t *console; vm = engine->u.njs.vm; console = njs_vm_external_ptr(vm); if (ret == NJS_OK) { if (console->interactive) { if (njs_vm_value_dump(vm, &out, njs_value_arg(&engine->u.njs.value), 0, 1) != NJS_OK) { njs_stderror("Shell:failed to get retval from VM\n"); return NJS_ERROR; } njs_print(out.start, out.length); njs_print("\n", 1); } } else { njs_vm_exception_string(vm, &out); njs_stderror("Thrown:\n%V\n", &out); } return NJS_OK; } static njs_arr_t * njs_object_completions(njs_vm_t *vm, njs_value_t *object, njs_str_t *expression) { u_char *prefix; size_t len, prefix_len; int64_t k, n, length; njs_int_t ret; njs_arr_t *array; njs_str_t *completion, key; njs_value_t *keys; njs_opaque_value_t *start, retval, prototype; prefix = expression->start + expression->length; while (prefix > expression->start && *prefix != '.') { prefix--; } if (prefix != expression->start) { prefix++; } prefix_len = prefix - expression->start; len = expression->length - prefix_len; array = njs_arr_create(njs_vm_memory_pool(vm), 8, sizeof(njs_str_t)); if (njs_slow_path(array == NULL)) { goto fail; } while (!njs_value_is_null(object)) { keys = njs_vm_value_enumerate(vm, object, NJS_ENUM_KEYS | NJS_ENUM_STRING, njs_value_arg(&retval)); if (njs_slow_path(keys == NULL)) { goto fail; } (void) njs_vm_array_length(vm, keys, &length); start = (njs_opaque_value_t *) njs_vm_array_start(vm, keys); if (start == NULL) { goto fail; } for (n = 0; n < length; n++) { ret = njs_vm_value_to_string(vm, &key, njs_value_arg(start)); if (njs_slow_path(ret != NJS_OK)) { goto fail; } start++; if (len > key.length || njs_strncmp(key.start, prefix, len) != 0) { continue; } for (k = 0; k < array->items; k++) { completion = njs_arr_item(array, k); if ((completion->length - prefix_len - 1) == key.length && njs_strncmp(&completion->start[prefix_len], key.start, key.length) == 0) { break; } } if (k != array->items) { continue; } completion = njs_arr_add(array); if (njs_slow_path(completion == NULL)) { goto fail; } completion->length = prefix_len + key.length + 1; completion->start = njs_mp_alloc(njs_vm_memory_pool(vm), completion->length); if (njs_slow_path(completion->start == NULL)) { goto fail; } njs_sprintf(completion->start, completion->start + completion->length, "%*s%V%Z", prefix_len, expression->start, &key); } ret = njs_vm_prototype(vm, object, njs_value_arg(&prototype)); if (njs_slow_path(ret != NJS_OK)) { goto fail; } object = njs_value_arg(&prototype); } return array; fail: if (array != NULL) { njs_arr_destroy(array); } return NULL; } static njs_arr_t * njs_engine_njs_complete(njs_engine_t *engine, njs_str_t *expression) { u_char *p, *start, *end; njs_vm_t *vm; njs_int_t ret; njs_bool_t global; njs_opaque_value_t value, key, retval; vm = engine->u.njs.vm; p = expression->start; end = p + expression->length; global = 1; (void) njs_vm_global(vm, njs_value_arg(&value)); while (p < end && *p != '.') { p++; } if (p == end) { goto done; } p = expression->start; for ( ;; ) { start = (*p == '.' && p < end) ? ++p: p; if (p == end) { break; } while (p < end && *p != '.') { p++; } ret = njs_vm_value_string_create(vm, njs_value_arg(&key), start, p - start); if (njs_slow_path(ret != NJS_OK)) { return NULL; } ret = njs_value_property(vm, njs_value_arg(&value), njs_value_arg(&key), njs_value_arg(&retval)); if (njs_slow_path(ret != NJS_OK)) { if (ret == NJS_DECLINED && !global) { goto done; } return NULL; } global = 0; njs_value_assign(&value, &retval); } done: return njs_object_completions(vm, njs_value_arg(&value), expression); } static njs_int_t njs_engine_njs_process_events(njs_engine_t *engine) { njs_ev_t *ev; njs_vm_t *vm; njs_int_t ret; njs_queue_t *events; njs_console_t *console; njs_queue_link_t *link; njs_opaque_value_t retval; vm = engine->u.njs.vm; console = njs_vm_external_ptr(vm); events = &console->posted_events; for ( ;; ) { link = njs_queue_first(events); if (link == njs_queue_tail(events)) { break; } ev = njs_queue_link_data(link, njs_ev_t, link); njs_queue_remove(&ev->link); njs_rbtree_delete(&console->events, &ev->node); ret = njs_vm_invoke(vm, ev->u.njs.function, ev->u.njs.args, ev->nargs, njs_value_arg(&retval)); if (ret == NJS_ERROR) { njs_engine_njs_output(engine, ret); if (!console->interactive) { return NJS_ERROR; } } } if (!njs_rbtree_is_empty(&console->events)) { return NJS_AGAIN; } return njs_vm_pending(vm) ? NJS_AGAIN: NJS_OK; } static njs_int_t njs_engine_njs_unhandled_rejection(njs_engine_t *engine) { njs_vm_t *vm; njs_int_t ret; njs_str_t message; njs_console_t *console; njs_rejected_promise_t *rejected_promise; vm = engine->u.njs.vm; console = njs_vm_external_ptr(vm); if (console->rejected_promises == NULL || console->rejected_promises->items == 0) { return 0; } rejected_promise = console->rejected_promises->start; ret = njs_vm_value_to_string(vm, &message, njs_value_arg(&rejected_promise->u.njs.message)); if (njs_slow_path(ret != NJS_OK)) { return -1; } njs_vm_error(vm, "unhandled promise rejection: %V", &message); njs_arr_destroy(console->rejected_promises); console->rejected_promises = NULL; return 1; } #ifdef NJS_HAVE_QUICKJS static JSValue njs_qjs_console_log(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) { int i; size_t len; const char *str; for (i = 0; i < argc; i++) { str = JS_ToCStringLen(ctx, &len, argv[i]); if (!str) { return JS_EXCEPTION; } njs_console_logger(magic, (const u_char*) str, len); JS_FreeCString(ctx, str); } return JS_UNDEFINED; } static JSValue njs_qjs_console_time(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { njs_str_t name; const char *str; njs_console_t *console; static const njs_str_t default_label = njs_str("default"); console = JS_GetRuntimeOpaque(JS_GetRuntime(ctx)); name = default_label; if (argc > 0 && !JS_IsUndefined(argv[0])) { str = JS_ToCStringLen(ctx, &name.length, argv[0]); if (str == NULL) { return JS_EXCEPTION; } name.start = njs_mp_alloc(console->engine->pool, name.length); if (njs_slow_path(name.start == NULL)) { JS_ThrowOutOfMemory(ctx); return JS_EXCEPTION; } (void) memcpy(name.start, str, name.length); JS_FreeCString(ctx, str); } if (njs_console_time(console, &name) != NJS_OK) { return JS_EXCEPTION; } return JS_UNDEFINED; } static JSValue njs_qjs_console_time_end(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { uint64_t ns; njs_str_t name; const char *str; njs_console_t *console; static const njs_str_t default_label = njs_str("default"); ns = njs_time(); console = JS_GetRuntimeOpaque(JS_GetRuntime(ctx)); name = default_label; if (argc > 0 && !JS_IsUndefined(argv[0])) { str = JS_ToCStringLen(ctx, &name.length, argv[0]); if (str == NULL) { return JS_EXCEPTION; } name.start = njs_mp_alloc(console->engine->pool, name.length); if (njs_slow_path(name.start == NULL)) { JS_ThrowOutOfMemory(ctx); return JS_EXCEPTION; } (void) memcpy(name.start, str, name.length); JS_FreeCString(ctx, str); } njs_console_time_end(console, &name, ns); return JS_UNDEFINED; } static JSValue njs_qjs_set_timer(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int immediate) { int n; int64_t delay; njs_ev_t *ev; njs_uint_t i; njs_console_t *console; if (njs_slow_path(argc < 1)) { JS_ThrowTypeError(ctx, "too few arguments"); return JS_EXCEPTION; } if (njs_slow_path(!JS_IsFunction(ctx, argv[0]))) { JS_ThrowTypeError(ctx, "first arg must be a function"); return JS_EXCEPTION; } delay = 0; if (!immediate && argc >= 2 && JS_IsNumber(argv[1])) { JS_ToInt64(ctx, &delay, argv[1]); } if (delay != 0) { JS_ThrowInternalError(ctx, "njs_set_timer(): async timers unsupported"); return JS_EXCEPTION; } console = JS_GetRuntimeOpaque(JS_GetRuntime(ctx)); n = immediate ? 1 : 2; argc = (argc >= n) ? argc - n : 0; ev = njs_mp_alloc(console->engine->pool, sizeof(njs_ev_t) + sizeof(njs_opaque_value_t) * argc); if (njs_slow_path(ev == NULL)) { JS_ThrowOutOfMemory(ctx); return JS_EXCEPTION; } ev->u.qjs.function = JS_DupValue(ctx, argv[0]); ev->u.qjs.args = (JSValue *) &ev[1]; ev->nargs = (njs_uint_t) argc; ev->id = console->event_id++; if (ev->nargs != 0) { for (i = 0; i < ev->nargs; i++) { ev->u.qjs.args[i] = JS_DupValue(ctx, argv[i + n]); } } njs_rbtree_insert(&console->events, &ev->node); njs_queue_insert_tail(&console->posted_events, &ev->link); return JS_NewUint32(ctx, ev->id); } static void njs_qjs_destroy_event(JSContext *ctx, njs_console_t *console, njs_ev_t *ev) { njs_uint_t i; JS_FreeValue(ctx, ev->u.qjs.function); if (ev->nargs != 0) { for (i = 0; i < ev->nargs; i++) { JS_FreeValue(ctx, ev->u.qjs.args[i]); } } njs_mp_free(console->engine->pool, ev); } static JSValue njs_qjs_clear_timeout(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { njs_ev_t ev_lookup, *ev; njs_console_t *console; njs_rbtree_node_t *rb; if (argc < 1 || !JS_IsNumber(argv[0])) { return JS_UNDEFINED; } console = JS_GetRuntimeOpaque(JS_GetRuntime(ctx)); if (JS_ToUint32(ctx, &ev_lookup.id, argv[0])) { return JS_EXCEPTION; } rb = njs_rbtree_find(&console->events, &ev_lookup.node); if (njs_slow_path(rb == NULL)) { JS_ThrowTypeError(ctx, "failed to find timer"); return JS_EXCEPTION; } ev = (njs_ev_t *) rb; njs_queue_remove(&ev->link); njs_rbtree_delete(&console->events, (njs_rbtree_part_t *) rb); njs_qjs_destroy_event(ctx, console, ev); return JS_UNDEFINED; } static JSValue njs_qjs_console_to_string_tag(JSContext *ctx, JSValueConst this_val) { return JS_NewString(ctx, "Console"); } static JSValue njs_qjs_process_getter(JSContext *ctx, JSValueConst this_val) { JSValue obj; njs_console_t *console; console = JS_GetRuntimeOpaque(JS_GetRuntime(ctx)); if (!JS_IsUndefined(console->process)) { return JS_DupValue(ctx, console->process); } obj = qjs_process_object(ctx, console->argc, (const char **) console->argv); if (JS_IsException(obj)) { return JS_EXCEPTION; } console->process = JS_DupValue(ctx, obj); return obj; } static njs_int_t njs_qjs_global_init(JSContext *ctx, JSValue global_obj); static void njs_qjs_dump_error(JSContext *ctx); static void njs_qjs_dump_obj(JSContext *ctx, FILE *f, JSValueConst val, const char *prefix, const char *quote) { njs_bool_t is_str; const char *str; is_str = JS_IsString(val); str = JS_ToCString(ctx, val); if (str) { fprintf(f, "%s%s%s%s\n", prefix, is_str ? quote : "", str, is_str ? quote : ""); JS_FreeCString(ctx, str); } else { njs_qjs_dump_error(ctx); } } static void njs_qjs_dump_error2(JSContext *ctx, JSValueConst exception) { _Bool is_error; JSValue val; is_error = JS_IsError(ctx, exception); njs_qjs_dump_obj(ctx, stderr, exception, "Thrown:\n", ""); if (is_error) { val = JS_GetPropertyStr(ctx, exception, "stack"); if (!JS_IsUndefined(val)) { njs_qjs_dump_obj(ctx, stderr, val, "", ""); } JS_FreeValue(ctx, val); } } static void njs_qjs_dump_error(JSContext *ctx) { JSValue exception; exception = JS_GetException(ctx); njs_qjs_dump_error2(ctx, exception); JS_FreeValue(ctx, exception); } static void * njs_qjs_agent(void *arg) { int ret; JSValue ret_val, global_obj; JSRuntime *rt; JSContext *ctx, *ctx1; njs_console_t *console; JSValue args[2]; njs_262agent_t *agent = arg; console = agent->console; rt = JS_NewRuntime(); if (rt == NULL) { njs_stderror("JS_NewRuntime failure\n"); exit(1); } ctx = JS_NewContext(rt); if (ctx == NULL) { JS_FreeRuntime(rt); njs_stderror("JS_NewContext failure\n"); exit(1); } JS_SetContextOpaque(ctx, agent); JS_SetRuntimeInfo(rt, "agent"); JS_SetCanBlock(rt, 1); global_obj = JS_GetGlobalObject(ctx); ret = njs_qjs_global_init(ctx, global_obj); if (ret == -1) { JS_FreeContext(ctx); JS_FreeRuntime(rt); njs_stderror("njs_qjs_global_init failure\n"); exit(1); } JS_FreeValue(ctx, global_obj); ret_val = JS_Eval(ctx, agent->script, strlen(agent->script), "", JS_EVAL_TYPE_GLOBAL); free(agent->script); agent->script = NULL; if (JS_IsException(ret_val)) { njs_qjs_dump_error(ctx); } JS_FreeValue(ctx, ret_val); for (;;) { ret = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1); if (ret < 0) { njs_qjs_dump_error(ctx); break; } else if (ret == 0) { if (JS_IsUndefined(agent->broadcast_func)) { break; } else { pthread_mutex_lock(&console->agent_mutex); while (!agent->broadcast_pending) { pthread_cond_wait(&console->agent_cond, &console->agent_mutex); } agent->broadcast_pending = 0; pthread_cond_signal(&console->agent_cond); pthread_mutex_unlock(&console->agent_mutex); args[0] = JS_NewArrayBuffer(ctx, agent->broadcast_sab_buf, agent->broadcast_sab_size, NULL, NULL, 1); args[1] = JS_NewInt32(ctx, agent->broadcast_val); ret_val = JS_Call(ctx, agent->broadcast_func, JS_UNDEFINED, 2, (JSValueConst *)args); JS_FreeValue(ctx, args[0]); JS_FreeValue(ctx, args[1]); if (JS_IsException(ret_val)) { njs_qjs_dump_error(ctx); } JS_FreeValue(ctx, ret_val); JS_FreeValue(ctx, agent->broadcast_func); agent->broadcast_func = JS_UNDEFINED; } } } JS_FreeValue(ctx, agent->broadcast_func); JS_FreeContext(ctx); JS_FreeRuntime(rt); return NULL; } static JSValue njs_qjs_agent_start(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { const char *script; njs_console_t *console; njs_262agent_t *agent; if (JS_GetContextOpaque(ctx) != NULL) { return JS_ThrowTypeError(ctx, "cannot be called inside an agent"); } script = JS_ToCString(ctx, argv[0]); if (script == NULL) { return JS_EXCEPTION; } console = JS_GetRuntimeOpaque(JS_GetRuntime(ctx)); agent = malloc(sizeof(*agent)); if (agent == NULL) { return JS_ThrowOutOfMemory(ctx); } njs_memzero(agent, sizeof(*agent)); agent->broadcast_func = JS_UNDEFINED; agent->broadcast_sab = JS_UNDEFINED; agent->script = strdup(script); if (agent->script == NULL) { return JS_ThrowOutOfMemory(ctx); } JS_FreeCString(ctx, script); agent->console = console; njs_queue_insert_tail(&console->agents, &agent->link); pthread_create(&agent->tid, NULL, njs_qjs_agent, agent); return JS_UNDEFINED; } static JSValue njs_qjsr_agent_get_report(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { JSValue ret; njs_console_t *console; njs_queue_link_t *link; njs_agent_report_t *rep; console = JS_GetRuntimeOpaque(JS_GetRuntime(ctx)); pthread_mutex_lock(&console->report_mutex); rep = NULL; for ( ;; ) { link = njs_queue_first(&console->reports); if (link == njs_queue_tail(&console->reports)) { break; } rep = njs_queue_link_data(link, njs_agent_report_t, link); njs_queue_remove(&rep->link); break; } pthread_mutex_unlock(&console->report_mutex); if (rep != NULL) { ret = JS_NewString(ctx, rep->str); free(rep->str); free(rep); } else { ret = JS_NULL; } return ret; } static njs_bool_t njs_qjs_broadcast_pending(njs_console_t *console) { njs_262agent_t *agent; njs_queue_link_t *link; link = njs_queue_first(&console->agents); for ( ;; ) { if (link == njs_queue_tail(&console->agents)) { break; } agent = njs_queue_link_data(link, njs_262agent_t, link); if (agent->broadcast_pending) { return 1; } link = njs_queue_next(link); } return 0; } static JSValue njs_qjs_agent_broadcast(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { uint8_t *buf; size_t buf_size; int32_t val; njs_console_t *console; njs_262agent_t *agent; njs_queue_link_t *link; JSValueConst sab = argv[0]; if (JS_GetContextOpaque(ctx) != NULL) { return JS_ThrowTypeError(ctx, "cannot be called inside an agent"); } buf = JS_GetArrayBuffer(ctx, &buf_size, sab); if (buf == NULL) { return JS_EXCEPTION; } if (JS_ToInt32(ctx, &val, argv[1])) { return JS_EXCEPTION; } console = JS_GetRuntimeOpaque(JS_GetRuntime(ctx)); pthread_mutex_lock(&console->agent_mutex); link = njs_queue_first(&console->agents); for ( ;; ) { if (link == njs_queue_tail(&console->agents)) { break; } agent = njs_queue_link_data(link, njs_262agent_t, link); agent->broadcast_pending = 1; agent->broadcast_sab = JS_DupValue(ctx, sab); agent->broadcast_sab_buf = buf; agent->broadcast_sab_size = buf_size; agent->broadcast_val = val; link = njs_queue_next(link); } pthread_cond_broadcast(&console->agent_cond); while (njs_qjs_broadcast_pending(console)) { pthread_cond_wait(&console->agent_cond, &console->agent_mutex); } pthread_mutex_unlock(&console->agent_mutex); return JS_UNDEFINED; } static JSValue njs_qjs_agent_receive_broadcast(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { njs_262agent_t *agent = JS_GetContextOpaque(ctx); if (agent == NULL) { return JS_ThrowTypeError(ctx, "must be called inside an agent"); } if (!JS_IsFunction(ctx, argv[0])) { return JS_ThrowTypeError(ctx, "expecting function"); } JS_FreeValue(ctx, agent->broadcast_func); agent->broadcast_func = JS_DupValue(ctx, argv[0]); return JS_UNDEFINED; } static JSValue njs_qjs_agent_report(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { const char *str; njs_console_t *console; njs_262agent_t *agent; njs_agent_report_t *rep; str = JS_ToCString(ctx, argv[0]); if (str == NULL) { return JS_EXCEPTION; } rep = malloc(sizeof(*rep)); rep->str = strdup(str); JS_FreeCString(ctx, str); agent = JS_GetContextOpaque(ctx); console = agent->console; pthread_mutex_lock(&console->report_mutex); njs_queue_insert_tail(&console->reports, &rep->link); pthread_mutex_unlock(&console->report_mutex); return JS_UNDEFINED; } static JSValue njs_qjs_agent_leaving(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { njs_262agent_t *agent = JS_GetContextOpaque(ctx); if (agent == NULL) { return JS_ThrowTypeError(ctx, "must be called inside an agent"); } return JS_UNDEFINED; } static JSValue njs_qjs_agent_sleep(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { uint32_t duration; if (JS_ToUint32(ctx, &duration, argv[0])) { return JS_EXCEPTION; } usleep(duration * 1000); return JS_UNDEFINED; } static JSValue njs_qjs_agent_monotonic_now(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { return JS_NewInt64(ctx, njs_time() / 1000000); } static const JSCFunctionListEntry njs_qjs_agent_proto[] = { JS_CFUNC_DEF("start", 1, njs_qjs_agent_start), JS_CFUNC_DEF("getReport", 0, njs_qjsr_agent_get_report), JS_CFUNC_DEF("broadcast", 2, njs_qjs_agent_broadcast), JS_CFUNC_DEF("report", 1, njs_qjs_agent_report), JS_CFUNC_DEF("leaving", 0, njs_qjs_agent_leaving), JS_CFUNC_DEF("receiveBroadcast", 1, njs_qjs_agent_receive_broadcast), JS_CFUNC_DEF("sleep", 1, njs_qjs_agent_sleep), JS_CFUNC_DEF("monotonicNow", 0, njs_qjs_agent_monotonic_now), }; static JSValue njs_qjs_new_agent(JSContext *ctx) { JSValue agent; agent = JS_NewObject(ctx); if (JS_IsException(agent)) { return JS_EXCEPTION; } JS_SetPropertyFunctionList(ctx, agent, njs_qjs_agent_proto, njs_nitems(njs_qjs_agent_proto)); return agent; } static JSValue njs_qjs_detach_array_buffer(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { JS_DetachArrayBuffer(ctx, argv[0]); return JS_NULL; } static JSValue njs_qjs_eval_script(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { size_t len; JSValue ret; const char *str; str = JS_ToCStringLen(ctx, &len, argv[0]); if (str == NULL) { return JS_EXCEPTION; } ret = JS_Eval(ctx, str, len, "", JS_EVAL_TYPE_GLOBAL); JS_FreeCString(ctx, str); return ret; } static JSValue njs_qjs_create_realm(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { JSValue ret_val, global_obj; njs_int_t ret; JSContext *ctx1; ctx1 = JS_NewContext(JS_GetRuntime(ctx)); if (ctx1 == NULL) { return JS_ThrowOutOfMemory(ctx); } global_obj = JS_GetGlobalObject(ctx1); ret = njs_qjs_global_init(ctx1, global_obj); if (ret == -1) { JS_FreeContext(ctx1); return JS_EXCEPTION; } ret_val = JS_GetPropertyStr(ctx1, global_obj, "$262"); JS_FreeValue(ctx1, global_obj); JS_FreeContext(ctx1); return ret_val; } static JSValue njs_qjs_is_HTMLDDA(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) { return JS_NULL; } static const JSCFunctionListEntry njs_qjs_262_proto[] = { JS_CFUNC_DEF("detachArrayBuffer", 1, njs_qjs_detach_array_buffer), JS_CFUNC_DEF("evalScript", 1, njs_qjs_eval_script), JS_CFUNC_DEF("codePointRange", 2, js_string_codePointRange), JS_CFUNC_DEF("createRealm", 0, njs_qjs_create_realm), }; static JSValue njs_qjs_new_262(JSContext *ctx, JSValueConst this_val) { JSValue obj, obj262, global_obj; obj262 = JS_NewObject(ctx); if (JS_IsException(obj262)) { return JS_EXCEPTION; } JS_SetPropertyFunctionList(ctx, obj262, njs_qjs_262_proto, njs_nitems(njs_qjs_262_proto)); global_obj = JS_GetGlobalObject(ctx); JS_SetPropertyStr(ctx, obj262, "global", JS_DupValue(ctx, global_obj)); JS_FreeValue(ctx, global_obj); obj = JS_NewCFunction(ctx, njs_qjs_is_HTMLDDA, "IsHTMLDDA", 0); JS_SetIsHTMLDDA(ctx, obj); JS_SetPropertyStr(ctx, obj262, "IsHTMLDDA", obj); JS_SetPropertyStr(ctx, obj262, "agent", njs_qjs_new_agent(ctx)); return obj262; } static const JSCFunctionListEntry njs_qjs_global_proto[] = { JS_CFUNC_DEF("clearTimeout", 1, njs_qjs_clear_timeout), JS_CFUNC_MAGIC_DEF("print", 0, njs_qjs_console_log, NJS_LOG_INFO), JS_CGETSET_DEF("process", njs_qjs_process_getter, NULL), JS_CFUNC_MAGIC_DEF("setImmediate", 0, njs_qjs_set_timer, 1), JS_CFUNC_MAGIC_DEF("setTimeout", 0, njs_qjs_set_timer, 0), }; static const JSCFunctionListEntry njs_qjs_console_proto[] = { JS_CGETSET_DEF("[Symbol.toStringTag]", njs_qjs_console_to_string_tag, NULL), JS_CFUNC_MAGIC_DEF("error", 0, njs_qjs_console_log, NJS_LOG_ERROR), JS_CFUNC_MAGIC_DEF("info", 0, njs_qjs_console_log, NJS_LOG_INFO), JS_CFUNC_MAGIC_DEF("log", 0, njs_qjs_console_log, NJS_LOG_INFO), JS_CFUNC_DEF("time", 0, njs_qjs_console_time), JS_CFUNC_DEF("timeEnd", 0, njs_qjs_console_time_end), JS_CFUNC_MAGIC_DEF("warn", 0, njs_qjs_console_log, NJS_LOG_WARN), }; static njs_int_t njs_qjs_global_init(JSContext *ctx, JSValue global_obj) { JS_SetPropertyFunctionList(ctx, global_obj, njs_qjs_global_proto, njs_nitems(njs_qjs_global_proto)); return JS_SetPropertyStr(ctx, global_obj, "$262", njs_qjs_new_262(ctx, global_obj)); } static void njs_qjs_rejection_tracker(JSContext *ctx, JSValueConst promise, JSValueConst reason, JS_BOOL is_handled, void *opaque) { void *promise_obj; uint32_t i, length; njs_console_t *console; njs_rejected_promise_t *rejected_promise; console = opaque; if (is_handled && console->rejected_promises != NULL) { rejected_promise = console->rejected_promises->start; length = console->rejected_promises->items; promise_obj = JS_VALUE_GET_PTR(promise); for (i = 0; i < length; i++) { if (JS_VALUE_GET_PTR(rejected_promise[i].u.qjs.promise) == promise_obj) { JS_FreeValue(ctx, rejected_promise[i].u.qjs.promise); JS_FreeValue(ctx, rejected_promise[i].u.qjs.message); njs_arr_remove(console->rejected_promises, &rejected_promise[i]); break; } } return; } if (console->rejected_promises == NULL) { console->rejected_promises = njs_arr_create(console->engine->pool, 4, sizeof(njs_rejected_promise_t)); if (njs_slow_path(console->rejected_promises == NULL)) { return; } } rejected_promise = njs_arr_add(console->rejected_promises); if (njs_slow_path(rejected_promise == NULL)) { return; } rejected_promise->u.qjs.promise = JS_DupValue(ctx, promise); rejected_promise->u.qjs.message = JS_DupValue(ctx, reason); } static JSModuleDef * njs_qjs_module_loader(JSContext *ctx, const char *module_name, void *opaque) { JSValue func_val; njs_int_t ret; njs_str_t text, prev_cwd; njs_opts_t *opts; njs_console_t *console; JSModuleDef *m; njs_module_info_t info; opts = opaque; console = JS_GetRuntimeOpaque(JS_GetRuntime(ctx)); njs_memzero(&info, sizeof(njs_module_info_t)); info.name.start = (u_char *) module_name; info.name.length = njs_strlen(module_name); ret = njs_module_lookup(opts, &console->cwd, &info); if (njs_slow_path(ret != NJS_OK)) { JS_ThrowReferenceError(ctx, "could not load module filename '%s'", module_name); return NULL; } ret = njs_module_read(console->engine->pool, info.fd, &text); (void) close(info.fd); if (njs_slow_path(ret != NJS_OK)) { JS_ThrowInternalError(ctx, "while reading \"%*s\" module", (int) info.file.length, info.file.start); return NULL; } prev_cwd = console->cwd; ret = njs_console_set_cwd(console, &info.file); if (njs_slow_path(ret != NJS_OK)) { JS_ThrowInternalError(ctx, "while setting cwd for \"%*s\" module", (int) info.file.length, info.file.start); return NULL; } func_val = JS_Eval(ctx, (char *) text.start, text.length, module_name, JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); njs_mp_free(console->engine->pool, console->cwd.start); console->cwd = prev_cwd; njs_mp_free(console->engine->pool, text.start); if (JS_IsException(func_val)) { return NULL; } m = JS_VALUE_GET_PTR(func_val); JS_FreeValue(ctx, func_val); return m; } static njs_int_t njs_engine_qjs_init(njs_engine_t *engine, njs_opts_t *opts) { JSValue global_obj, obj; njs_int_t ret; JSContext *ctx; engine->u.qjs.rt = JS_NewRuntime(); if (engine->u.qjs.rt == NULL) { njs_stderror("JS_NewRuntime() failed\n"); return NJS_ERROR; } engine->u.qjs.ctx = qjs_new_context(engine->u.qjs.rt, NULL); if (engine->u.qjs.ctx == NULL) { njs_stderror("JS_NewContext() failed\n"); return NJS_ERROR; } JS_SetRuntimeOpaque(engine->u.qjs.rt, &njs_console); engine->u.qjs.value = JS_UNDEFINED; ctx = engine->u.qjs.ctx; global_obj = JS_GetGlobalObject(ctx); ret = njs_qjs_global_init(ctx, global_obj); if (ret == -1) { njs_stderror("njs_qjs_global_init() failed\n"); ret = NJS_ERROR; goto done; } obj = JS_NewObject(ctx); if (JS_IsException(obj)) { njs_stderror("JS_NewObject() failed\n"); ret = NJS_ERROR; goto done; } JS_SetOpaque(obj, &njs_console); JS_SetPropertyFunctionList(ctx, obj, njs_qjs_console_proto, njs_nitems(njs_qjs_console_proto)); ret = JS_SetPropertyStr(ctx, global_obj, "console", obj); if (ret == -1) { njs_stderror("JS_SetPropertyStr() failed\n"); ret = NJS_ERROR; goto done; } if (opts->unhandled_rejection) { JS_SetHostPromiseRejectionTracker(engine->u.qjs.rt, njs_qjs_rejection_tracker, JS_GetRuntimeOpaque(engine->u.qjs.rt)); } JS_SetModuleLoaderFunc(engine->u.qjs.rt, NULL, njs_qjs_module_loader, opts); JS_SetCanBlock(engine->u.qjs.rt, opts->can_block); ret = NJS_OK; done: JS_FreeValue(ctx, global_obj); return ret; } static njs_int_t njs_engine_qjs_destroy(njs_engine_t *engine) { uint32_t i; njs_ev_t *ev; njs_queue_t *events; njs_console_t *console; njs_262agent_t *agent; njs_queue_link_t *link; njs_rejected_promise_t *rejected_promise; console = JS_GetRuntimeOpaque(engine->u.qjs.rt); if (console->rejected_promises != NULL) { rejected_promise = console->rejected_promises->start; for (i = 0; i < console->rejected_promises->items; i++) { JS_FreeValue(engine->u.qjs.ctx, rejected_promise[i].u.qjs.promise); JS_FreeValue(engine->u.qjs.ctx, rejected_promise[i].u.qjs.message); } } events = &console->posted_events; for ( ;; ) { link = njs_queue_first(events); if (link == njs_queue_tail(events)) { break; } ev = njs_queue_link_data(link, njs_ev_t, link); njs_queue_remove(&ev->link); njs_rbtree_delete(&console->events, &ev->node); njs_qjs_destroy_event(engine->u.qjs.ctx, console, ev); } for ( ;; ) { link = njs_queue_first(&console->agents); if (link == njs_queue_tail(&console->agents)) { break; } agent = njs_queue_link_data(link, njs_262agent_t, link); njs_queue_remove(&agent->link); pthread_join(agent->tid, NULL); JS_FreeValue(engine->u.qjs.ctx, agent->broadcast_sab); free(agent->script); free(agent); } JS_FreeValue(engine->u.qjs.ctx, console->process); JS_FreeValue(engine->u.qjs.ctx, engine->u.qjs.value); JS_FreeContext(engine->u.qjs.ctx); JS_FreeRuntime(engine->u.qjs.rt); return NJS_OK; } static njs_int_t njs_engine_qjs_eval(njs_engine_t *engine, njs_str_t *script) { int flags; JSValue code; njs_console_t *console; flags = JS_EVAL_TYPE_GLOBAL | JS_EVAL_FLAG_STRICT | JS_EVAL_FLAG_COMPILE_ONLY; console = JS_GetRuntimeOpaque(engine->u.qjs.rt); if (console->module) { flags |= JS_EVAL_TYPE_MODULE; } code = JS_Eval(engine->u.qjs.ctx, (char *) script->start, script->length, "", flags); if (JS_IsException(code)) { return NJS_ERROR; } JS_FreeValue(engine->u.qjs.ctx, engine->u.qjs.value); engine->u.qjs.value = JS_EvalFunction(engine->u.qjs.ctx, code); return (JS_IsException(engine->u.qjs.value)) ? NJS_ERROR : NJS_OK; } static njs_int_t njs_engine_qjs_execute_pending_job(njs_engine_t *engine) { JSContext *ctx1; return JS_ExecutePendingJob(engine->u.qjs.rt, &ctx1); } static njs_int_t njs_engine_qjs_unhandled_rejection(njs_engine_t *engine) { size_t len; uint32_t i; JSContext *ctx; const char *str; njs_console_t *console; njs_rejected_promise_t *rejected_promise; ctx = engine->u.qjs.ctx; console = JS_GetRuntimeOpaque(engine->u.qjs.rt); if (console->rejected_promises == NULL || console->rejected_promises->items == 0) { return 0; } rejected_promise = console->rejected_promises->start; str = JS_ToCStringLen(ctx, &len, rejected_promise->u.qjs.message); if (njs_slow_path(str == NULL)) { return -1; } JS_ThrowTypeError(ctx, "unhandled promise rejection: %*s", (int) len, str); JS_FreeCString(ctx, str); for (i = 0; i < console->rejected_promises->items; i++) { JS_FreeValue(ctx, rejected_promise[i].u.qjs.promise); JS_FreeValue(ctx, rejected_promise[i].u.qjs.message); } njs_arr_destroy(console->rejected_promises); console->rejected_promises = NULL; return 1; } static njs_int_t njs_engine_qjs_process_events(njs_engine_t *engine) { JSValue ret; njs_ev_t *ev; JSContext *ctx; njs_queue_t *events; njs_console_t *console; njs_queue_link_t *link; ctx = engine->u.qjs.ctx; console = JS_GetRuntimeOpaque(engine->u.qjs.rt); events = &console->posted_events; for ( ;; ) { link = njs_queue_first(events); if (link == njs_queue_tail(events)) { break; } ev = njs_queue_link_data(link, njs_ev_t, link); njs_queue_remove(&ev->link); njs_rbtree_delete(&console->events, &ev->node); ret = JS_Call(ctx, ev->u.qjs.function, JS_UNDEFINED, ev->nargs, ev->u.qjs.args); njs_qjs_destroy_event(ctx, console, ev); if (JS_IsException(ret)) { engine->output(engine, NJS_ERROR); if (!console->interactive) { return NJS_ERROR; } } JS_FreeValue(ctx, ret); } if (!njs_rbtree_is_empty(&console->events)) { return NJS_AGAIN; } return JS_IsJobPending(engine->u.qjs.rt) ? NJS_AGAIN: NJS_OK; } static njs_int_t njs_engine_qjs_output(njs_engine_t *engine, njs_int_t ret) { JSContext *ctx; njs_console_t *console; ctx = engine->u.qjs.ctx; console = JS_GetRuntimeOpaque(engine->u.qjs.rt); if (ret == NJS_OK) { if (console->interactive) { njs_qjs_dump_obj(ctx, stdout, engine->u.qjs.value, "", "\'"); } } else { njs_qjs_dump_error(ctx); } return NJS_OK; } static njs_arr_t * njs_qjs_object_completions(njs_engine_t *engine, JSContext *ctx, JSValueConst object, njs_str_t *expression) { u_char *prefix; size_t len, prefix_len; JSValue prototype; uint32_t k, n, length; njs_int_t ret; njs_arr_t *array; njs_str_t *completion, key; JSPropertyEnum *ptab; prefix = expression->start + expression->length; while (prefix > expression->start && *prefix != '.') { prefix--; } if (prefix != expression->start) { prefix++; } ptab = NULL; key.start = NULL; prefix_len = prefix - expression->start; len = expression->length - prefix_len; array = njs_arr_create(engine->pool, 8, sizeof(njs_str_t)); if (njs_slow_path(array == NULL)) { goto fail; } while (!JS_IsNull(object)) { ret = JS_GetOwnPropertyNames(ctx, &ptab, &length, object, JS_GPN_STRING_MASK); if (ret < 0) { goto fail; } for (n = 0; n < length; n++) { key.start = (u_char *) JS_AtomToCString(ctx, ptab[n].atom); JS_FreeAtom(ctx, ptab[n].atom); if (njs_slow_path(key.start == NULL)) { goto fail; } key.length = njs_strlen(key.start); if (len > key.length || njs_strncmp(key.start, prefix, len) != 0) { goto next; } for (k = 0; k < array->items; k++) { completion = njs_arr_item(array, k); if ((completion->length - prefix_len - 1) == key.length && njs_strncmp(&completion->start[prefix_len], key.start, key.length) == 0) { goto next; } } completion = njs_arr_add(array); if (njs_slow_path(completion == NULL)) { goto fail; } completion->length = prefix_len + key.length + 1; completion->start = njs_mp_alloc(engine->pool, completion->length); if (njs_slow_path(completion->start == NULL)) { goto fail; } njs_sprintf(completion->start, completion->start + completion->length, "%*s%V%Z", prefix_len, expression->start, &key); next: JS_FreeCString(ctx, (const char *) key.start); } js_free_rt(JS_GetRuntime(ctx), ptab); prototype = JS_GetPrototype(ctx, object); if (JS_IsException(prototype)) { goto fail; } JS_FreeValue(ctx, object); object = prototype; } return array; fail: if (array != NULL) { njs_arr_destroy(array); } if (key.start != NULL) { JS_FreeCString(ctx, (const char *) key.start); } if (ptab != NULL) { js_free_rt(JS_GetRuntime(ctx), ptab); } JS_FreeValue(ctx, object); return NULL; } static njs_arr_t * njs_engine_qjs_complete(njs_engine_t *engine, njs_str_t *expression) { u_char *p, *start, *end; JSAtom key; JSValue value, retval; njs_arr_t *arr; JSContext *ctx; njs_bool_t global; ctx = engine->u.qjs.ctx; p = expression->start; end = p + expression->length; global = 1; value = JS_GetGlobalObject(ctx); while (p < end && *p != '.') { p++; } if (p == end) { goto done; } p = expression->start; for ( ;; ) { start = (*p == '.' && p < end) ? ++p: p; if (p == end) { break; } while (p < end && *p != '.') { p++; } key = JS_NewAtomLen(ctx, (char *) start, p - start); if (key == JS_ATOM_NULL) { goto fail; } retval = JS_GetProperty(ctx, value, key); JS_FreeAtom(ctx, key); if (JS_IsUndefined(retval)) { if (global) { goto fail; } goto done; } if (JS_IsException(retval)) { goto fail; } JS_FreeValue(ctx, value); value = retval; global = 0; } done: arr = njs_qjs_object_completions(engine, ctx, JS_DupValue(ctx, value), expression); JS_FreeValue(ctx, value); return arr; fail: JS_FreeValue(ctx, value); return NULL; } #endif static njs_engine_t * njs_create_engine(njs_opts_t *opts) { njs_mp_t *mp; njs_int_t ret; njs_engine_t *engine; mp = njs_mp_fast_create(2 * njs_pagesize(), 128, 512, 16); if (njs_slow_path(mp == NULL)) { return NULL; } engine = njs_mp_zalloc(mp, sizeof(njs_engine_t)); if (njs_slow_path(engine == NULL)) { return NULL; } engine->pool = mp; njs_console.engine = engine; switch (opts->engine) { case NJS_ENGINE_NJS: ret = njs_engine_njs_init(engine, opts); if (njs_slow_path(ret != NJS_OK)) { njs_stderror("njs_engine_njs_init() failed\n"); return NULL; } engine->type = NJS_ENGINE_NJS; engine->eval = njs_engine_njs_eval; engine->execute_pending_job = njs_engine_njs_execute_pending_job; engine->unhandled_rejection = njs_engine_njs_unhandled_rejection; engine->process_events = njs_engine_njs_process_events; engine->destroy = njs_engine_njs_destroy; engine->output = njs_engine_njs_output; engine->complete = njs_engine_njs_complete; break; #ifdef NJS_HAVE_QUICKJS case NJS_ENGINE_QUICKJS: ret = njs_engine_qjs_init(engine, opts); if (njs_slow_path(ret != NJS_OK)) { njs_stderror("njs_engine_qjs_init() failed\n"); return NULL; } engine->type = NJS_ENGINE_QUICKJS; engine->eval = njs_engine_qjs_eval; engine->execute_pending_job = njs_engine_qjs_execute_pending_job; engine->unhandled_rejection = njs_engine_qjs_unhandled_rejection; engine->process_events = njs_engine_qjs_process_events; engine->destroy = njs_engine_qjs_destroy; engine->output = njs_engine_qjs_output; engine->complete = njs_engine_qjs_complete; break; #endif default: njs_stderror("unknown engine type\n"); return NULL; } return engine; } static njs_int_t njs_read_file(njs_opts_t *opts, njs_str_t *content) { int fd; char *file; u_char *p, *end, *start; size_t size; ssize_t n; njs_int_t ret; struct stat sb; file = opts->file; if (file[0] == '-' && file[1] == '\0') { fd = STDIN_FILENO; } else { fd = open(file, O_RDONLY); if (fd == -1) { njs_stderror("failed to open file: '%s' (%s)\n", file, strerror(errno)); return NJS_ERROR; } } if (fstat(fd, &sb) == -1) { njs_stderror("fstat(%d) failed while reading '%s' (%s)\n", fd, file, strerror(errno)); ret = NJS_ERROR; goto close_fd; } size = 4096; if (S_ISREG(sb.st_mode) && sb.st_size) { size = sb.st_size; } content->length = 0; content->start = realloc(NULL, size + 1); if (content->start == NULL) { njs_stderror("alloc failed while reading '%s'\n", file); ret = NJS_ERROR; goto close_fd; } p = content->start; end = p + size; for ( ;; ) { n = read(fd, p, end - p); if (n == 0) { break; } if (n < 0) { njs_stderror("failed to read file: '%s' (%s)\n", file, strerror(errno)); ret = NJS_ERROR; goto close_fd; } if (p + n == end) { size *= 2; start = realloc(content->start, size + 1); if (start == NULL) { njs_stderror("alloc failed while reading '%s'\n", file); ret = NJS_ERROR; goto close_fd; } content->start = start; p = content->start + content->length; end = content->start + size; } p += n; content->length += n; } content->start[content->length] = '\0'; ret = NJS_OK; close_fd: if (fd != STDIN_FILENO) { (void) close(fd); } return ret; } static njs_int_t njs_process_file(njs_opts_t *opts) { u_char *p; njs_int_t ret; njs_str_t source, script; njs_engine_t *engine; engine = NULL; source.start = NULL; ret = njs_read_file(opts, &source); if (ret != NJS_OK) { goto done; } script = source; /* shebang */ if (script.length > 2 && memcmp(script.start, "#!", 2) == 0) { p = njs_strlchr(script.start, script.start + script.length, '\n'); if (p != NULL) { script.length -= (p + 1 - script.start); script.start = p + 1; } else { script.length = 0; } } engine = njs_create_engine(opts); if (engine == NULL) { ret = NJS_ERROR; goto done; } ret = njs_process_script(engine, &njs_console, &script); if (ret != NJS_OK) { ret = NJS_ERROR; goto done; } ret = NJS_OK; done: if (engine != NULL) { engine->destroy(engine); } if (source.start != NULL) { free(source.start); } return ret; } static njs_int_t njs_process_script(njs_engine_t *engine, njs_console_t *console, njs_str_t *script) { njs_int_t ret; ret = engine->eval(engine, script); if (!console->suppress_stdout) { engine->output(engine, ret); } if (!console->interactive && ret == NJS_ERROR) { return NJS_ERROR; } for ( ;; ) { for ( ;; ) { ret = engine->execute_pending_job(engine); if (ret <= NJS_OK) { if (ret == NJS_ERROR) { if (!console->suppress_stdout) { engine->output(engine, ret); } if (!console->interactive) { return NJS_ERROR; } } break; } } ret = engine->process_events(engine); if (njs_slow_path(ret == NJS_ERROR)) { break; } if (engine->unhandled_rejection(engine)) { if (!console->suppress_stdout) { engine->output(engine, NJS_ERROR); } if (!console->interactive) { return NJS_ERROR; } } if (ret == NJS_OK) { break; } } return ret; } #if (!defined NJS_FUZZER_TARGET && defined NJS_HAVE_READLINE) volatile sig_atomic_t njs_running; volatile sig_atomic_t njs_sigint_count; volatile sig_atomic_t njs_sigint_received; static void njs_cb_line_handler(char *line_in) { njs_int_t ret; njs_str_t line; if (line_in == NULL) { njs_running = NJS_DONE; return; } line.start = (u_char *) line_in; line.length = njs_strlen(line.start); if (strcmp(line_in, ".exit") == 0) { njs_running = NJS_DONE; goto free_line; } njs_sigint_count = 0; if (line.length == 0) { rl_callback_handler_install(">> ", njs_cb_line_handler); goto free_line; } add_history((char *) line.start); ret = njs_process_script(njs_console.engine, &njs_console, &line); if (ret == NJS_ERROR) { njs_running = NJS_ERROR; } if (ret == NJS_OK) { rl_callback_handler_install(">> ", njs_cb_line_handler); } free_line: free(line.start); } static njs_int_t njs_interactive_shell(njs_opts_t *opts) { int flags; fd_set fds; njs_int_t ret; njs_engine_t *engine; struct timeval timeout; if (njs_editline_init() != NJS_OK) { njs_stderror("failed to init completions\n"); return NJS_ERROR; } engine = njs_create_engine(opts); if (engine == NULL) { njs_stderror("njs_create_engine() failed\n"); return NJS_ERROR; } if (!opts->quiet) { if (engine->type == NJS_ENGINE_NJS) { njs_printf("interactive njs (njs:%s)\n\n", NJS_VERSION); #if (NJS_HAVE_QUICKJS) } else { njs_printf("interactive njs (QuickJS:%s)\n\n", NJS_QUICKJS_VERSION); #endif } } rl_callback_handler_install(">> ", njs_cb_line_handler); flags = fcntl(STDIN_FILENO, F_GETFL, 0); fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK); njs_running = NJS_OK; while (njs_running == NJS_OK) { FD_ZERO(&fds); FD_SET(STDIN_FILENO, &fds); timeout = (struct timeval) {1, 0}; ret = select(FD_SETSIZE, &fds, NULL, NULL, &timeout); if (ret < 0 && errno != EINTR) { njs_stderror("select() failed\n"); njs_running = NJS_ERROR; break; } if (njs_sigint_received) { if (njs_sigint_count > 1) { njs_running = NJS_DONE; break; } if (rl_end != 0) { njs_printf("\n"); njs_sigint_count = 0; } else { njs_printf("(To exit, press Ctrl+C again or Ctrl+D " "or type .exit)\n"); njs_sigint_count = 1; } rl_point = rl_end = 0; rl_on_new_line(); rl_redisplay(); njs_sigint_received = 0; } if (ret < 0) { continue; } if (FD_ISSET(fileno(rl_instream), &fds)) { rl_callback_read_char(); } } rl_callback_handler_remove(); if (njs_running == NJS_DONE) { njs_printf("exiting\n"); } engine->destroy(engine); return njs_running == NJS_DONE ? NJS_OK : njs_running; } static char ** njs_completion_handler(const char *text, int start, int end) { rl_attempted_completion_over = 1; return rl_completion_matches(text, njs_completion_generator); } static void njs_signal_handler(int signal) { switch (signal) { case SIGINT: njs_sigint_received = 1; njs_sigint_count += 1; break; default: break; } } static njs_int_t njs_editline_init(void) { rl_completion_append_character = '\0'; rl_attempted_completion_function = njs_completion_handler; rl_basic_word_break_characters = (char *) " \t\n\"\\'`@$><=;,|&{("; setlocale(LC_ALL, ""); signal(SIGINT, njs_signal_handler); return NJS_OK; } static char * njs_completion_generator(const char *text, int state) { njs_str_t expression, *suffix; njs_engine_t *engine; njs_completion_t *cmpl; engine = njs_console.engine; cmpl = &engine->completion; if (state == 0) { cmpl->index = 0; expression.start = (u_char *) text; expression.length = njs_strlen(text); cmpl->suffix_completions = engine->complete(engine, &expression); if (cmpl->suffix_completions == NULL) { return NULL; } } if (cmpl->index == cmpl->suffix_completions->items) { return NULL; } suffix = njs_arr_item(cmpl->suffix_completions, cmpl->index++); return strndup((char *) suffix->start, suffix->length); } #endif static njs_int_t njs_ext_console_log(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t magic, njs_value_t *retval) { njs_str_t msg; njs_uint_t n; njs_log_level_t level; n = 1; level = (njs_log_level_t) magic & NJS_LOG_MASK; while (n < nargs) { if (njs_vm_value_dump(vm, &msg, njs_argument(args, n), 1, !!(magic & NJS_LOG_DUMP)) == NJS_ERROR) { return NJS_ERROR; } njs_console_logger(level, msg.start, msg.length); n++; } njs_value_undefined_set(retval); return NJS_OK; } static njs_int_t njs_ext_console_time(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_str_t name; njs_value_t *value; njs_console_t *console; static const njs_str_t default_label = njs_str("default"); console = njs_vm_external(vm, njs_console_proto_id, njs_argument(args, 0)); if (njs_slow_path(console == NULL)) { njs_vm_error(vm, "external value is expected"); return NJS_ERROR; } name = default_label; value = njs_arg(args, nargs, 1); if (njs_slow_path(!njs_value_is_string(value))) { if (!njs_value_is_undefined(value)) { ret = njs_value_to_string(vm, value, value); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_value_string_get(value, &name); } } else { njs_value_string_get(value, &name); } if (njs_console_time(console, &name) != NJS_OK) { njs_vm_error(vm, "failed to add timer"); return NJS_ERROR; } njs_value_undefined_set(retval); return NJS_OK; } static njs_int_t njs_ext_console_time_end(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { uint64_t ns; njs_int_t ret; njs_str_t name; njs_value_t *value; njs_console_t *console; static const njs_str_t default_label = njs_str("default"); ns = njs_time(); console = njs_vm_external(vm, njs_console_proto_id, njs_argument(args, 0)); if (njs_slow_path(console == NULL)) { njs_vm_error(vm, "external value is expected"); return NJS_ERROR; } name = default_label; value = njs_arg(args, nargs, 1); if (njs_slow_path(!njs_value_is_string(value))) { if (!njs_value_is_undefined(value)) { ret = njs_value_to_string(vm, value, value); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_value_string_get(value, &name); } } else { njs_value_string_get(value, &name); } njs_console_time_end(console, &name, ns); njs_value_undefined_set(retval); return NJS_OK; } static njs_int_t njs_set_timer(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_bool_t immediate, njs_value_t *retval) { njs_ev_t *ev; uint64_t delay; njs_uint_t n; njs_console_t *console; console = njs_vm_external_ptr(vm); if (njs_slow_path(nargs < 2)) { njs_vm_type_error(vm, "too few arguments"); return NJS_ERROR; } if (njs_slow_path(!njs_value_is_function(njs_argument(args, 1)))) { njs_vm_type_error(vm, "first arg must be a function"); return NJS_ERROR; } delay = 0; if (!immediate && nargs >= 3 && njs_value_is_number(njs_argument(args, 2))) { delay = njs_value_number(njs_argument(args, 2)); } if (delay != 0) { njs_vm_internal_error(vm, "njs_set_timer(): async timers unsupported"); return NJS_ERROR; } n = immediate ? 2 : 3; nargs = (nargs >= n) ? nargs - n : 0; ev = njs_mp_alloc(njs_vm_memory_pool(vm), sizeof(njs_ev_t) + sizeof(njs_opaque_value_t) * nargs); if (njs_slow_path(ev == NULL)) { njs_vm_memory_error(vm); return NJS_ERROR; } ev->u.njs.function = njs_value_function(njs_argument(args, 1)); ev->u.njs.args = (njs_value_t *) ((u_char *) ev + sizeof(njs_ev_t)); ev->nargs = nargs; ev->id = console->event_id++; if (ev->nargs != 0) { memcpy(ev->u.njs.args, njs_argument(args, n), sizeof(njs_opaque_value_t) * ev->nargs); } njs_rbtree_insert(&console->events, &ev->node); njs_queue_insert_tail(&console->posted_events, &ev->link); njs_value_number_set(retval, ev->id); return NJS_OK; } static njs_int_t njs_set_timeout(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { return njs_set_timer(vm, args, nargs, unused, 0, retval); } static njs_int_t njs_set_immediate(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { return njs_set_timer(vm, args, nargs, unused, 1, retval); } static njs_int_t njs_clear_timeout(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_ev_t ev_lookup, *ev; njs_console_t *console; njs_rbtree_node_t *rb; if (nargs < 2 || !njs_value_is_number(njs_argument(args, 1))) { njs_value_undefined_set(retval); return NJS_OK; } console = njs_vm_external_ptr(vm); ev_lookup.id = njs_value_number(njs_argument(args, 1)); rb = njs_rbtree_find(&console->events, &ev_lookup.node); if (njs_slow_path(rb == NULL)) { njs_vm_internal_error(vm, "failed to find timer"); return NJS_ERROR; } ev = (njs_ev_t *) rb; njs_queue_remove(&ev->link); njs_rbtree_delete(&console->events, (njs_rbtree_part_t *) rb); njs_mp_free(njs_vm_memory_pool(vm), ev); njs_value_undefined_set(retval); return NJS_OK; } static void njs_console_log(njs_log_level_t level, const char *fmt, ...) { u_char *p; va_list args; u_char buf[2048]; va_start(args, fmt); p = njs_vsprintf(buf, buf + sizeof(buf), fmt, args); va_end(args); njs_console_logger(level, buf, p - buf); } static void njs_console_logger(njs_log_level_t level, const u_char *start, size_t length) { switch (level) { case NJS_LOG_WARN: njs_printf("W: "); break; case NJS_LOG_ERROR: njs_printf("E: "); break; case NJS_LOG_INFO: break; } njs_print(start, length); njs_print("\n", 1); } static njs_int_t njs_console_time(njs_console_t *console, njs_str_t *name) { njs_queue_t *labels; njs_timelabel_t *label; njs_queue_link_t *link; labels = &console->labels; link = njs_queue_first(labels); while (link != njs_queue_tail(labels)) { label = njs_queue_link_data(link, njs_timelabel_t, link); if (njs_strstr_eq(name, &label->name)) { njs_console_log(NJS_LOG_INFO, "Timer \"%V\" already exists.", name); return NJS_OK; } link = njs_queue_next(link); } label = njs_mp_alloc(console->engine->pool, sizeof(njs_timelabel_t) + name->length); if (njs_slow_path(label == NULL)) { return NJS_ERROR; } label->name.start = (u_char *) label + sizeof(njs_timelabel_t); memcpy(label->name.start, name->start, name->length); label->name.length = name->length; label->time = njs_time(); njs_queue_insert_tail(&console->labels, &label->link); return NJS_OK; } static void njs_console_time_end(njs_console_t *console, njs_str_t *name, uint64_t ns) { uint64_t ms; njs_queue_t *labels; njs_timelabel_t *label; njs_queue_link_t *link; labels = &console->labels; link = njs_queue_first(labels); for ( ;; ) { if (link == njs_queue_tail(labels)) { njs_console_log(NJS_LOG_INFO, "Timer \"%V\" doesn’t exist.", name); return; } label = njs_queue_link_data(link, njs_timelabel_t, link); if (njs_strstr_eq(name, &label->name)) { njs_queue_remove(&label->link); break; } link = njs_queue_next(link); } ns = ns - label->time; ms = ns / 1000000; ns = ns % 1000000; njs_console_log(NJS_LOG_INFO, "%V: %uL.%06uLms", name, ms, ns); njs_mp_free(console->engine->pool, label); } static intptr_t njs_event_rbtree_compare(njs_rbtree_node_t *node1, njs_rbtree_node_t *node2) { njs_ev_t *ev1, *ev2; ev1 = (njs_ev_t *) node1; ev2 = (njs_ev_t *) node2; if (ev1->id < ev2->id) { return -1; } if (ev1->id > ev2->id) { return 1; } return 0; } static uint64_t njs_time(void) { #if (NJS_HAVE_CLOCK_MONOTONIC) struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (uint64_t) ts.tv_sec * 1000000000 + ts.tv_nsec; #else struct timeval tv; gettimeofday(&tv, NULL); return (uint64_t) tv.tv_sec * 1000000000 + tv.tv_usec * 1000; #endif } njs-0.8.9/external/njs_webcrypto_module.c000066400000000000000000004056111474132077100205620ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #include #include #include #include "njs_openssl.h" typedef enum { NJS_KEY_FORMAT_RAW = 1 << 1, NJS_KEY_FORMAT_PKCS8 = 1 << 2, NJS_KEY_FORMAT_SPKI = 1 << 3, NJS_KEY_FORMAT_JWK = 1 << 4, NJS_KEY_FORMAT_UNKNOWN = 1 << 5, } njs_webcrypto_key_format_t; typedef enum { NJS_KEY_USAGE_DECRYPT = 1 << 1, NJS_KEY_USAGE_DERIVE_BITS = 1 << 2, NJS_KEY_USAGE_DERIVE_KEY = 1 << 3, NJS_KEY_USAGE_ENCRYPT = 1 << 4, NJS_KEY_USAGE_GENERATE_KEY = 1 << 5, NJS_KEY_USAGE_SIGN = 1 << 6, NJS_KEY_USAGE_VERIFY = 1 << 7, NJS_KEY_USAGE_WRAP_KEY = 1 << 8, NJS_KEY_USAGE_UNSUPPORTED = 1 << 9, NJS_KEY_USAGE_UNWRAP_KEY = 1 << 10, } njs_webcrypto_key_usage_t; typedef enum { NJS_ALGORITHM_RSASSA_PKCS1_v1_5 = 0, NJS_ALGORITHM_RSA_PSS, NJS_ALGORITHM_RSA_OAEP, NJS_ALGORITHM_HMAC, NJS_ALGORITHM_AES_GCM, NJS_ALGORITHM_AES_CTR, NJS_ALGORITHM_AES_CBC, NJS_ALGORITHM_ECDSA, NJS_ALGORITHM_ECDH, NJS_ALGORITHM_PBKDF2, NJS_ALGORITHM_HKDF, NJS_ALGORITHM_MAX, } njs_webcrypto_alg_t; typedef enum { NJS_HASH_UNSET = 0, NJS_HASH_SHA1, NJS_HASH_SHA256, NJS_HASH_SHA384, NJS_HASH_SHA512, NJS_HASH_MAX, } njs_webcrypto_hash_t; typedef struct { njs_str_t name; uintptr_t value; } njs_webcrypto_entry_t; typedef struct { njs_webcrypto_alg_t type; unsigned usage; unsigned fmt; unsigned raw; } njs_webcrypto_algorithm_t; typedef struct { njs_webcrypto_algorithm_t *alg; unsigned usage; njs_bool_t extractable; njs_webcrypto_hash_t hash; union { struct { EVP_PKEY *pkey; njs_bool_t privat; int curve; } a; struct { njs_str_t raw; } s; } u; } njs_webcrypto_key_t; typedef int (*EVP_PKEY_cipher_init_t)(EVP_PKEY_CTX *ctx); typedef int (*EVP_PKEY_cipher_t)(EVP_PKEY_CTX *ctx, unsigned char *out, size_t *outlen, const unsigned char *in, size_t inlen); static njs_int_t njs_ext_cipher(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_cipher_pkey(njs_vm_t *vm, njs_str_t *data, njs_webcrypto_key_t *key, njs_index_t encrypt, njs_value_t *retval); static njs_int_t njs_cipher_aes_gcm(njs_vm_t *vm, njs_str_t *data, njs_webcrypto_key_t *key, njs_value_t *options, njs_bool_t encrypt, njs_value_t *retval); static njs_int_t njs_cipher_aes_ctr(njs_vm_t *vm, njs_str_t *data, njs_webcrypto_key_t *key, njs_value_t *options, njs_bool_t encrypt, njs_value_t *retval); static njs_int_t njs_cipher_aes_cbc(njs_vm_t *vm, njs_str_t *data, njs_webcrypto_key_t *key, njs_value_t *options, njs_bool_t encrypt, njs_value_t *retval); static njs_int_t njs_ext_derive(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t derive_key, njs_value_t *retval); static njs_int_t njs_ext_digest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_ext_export_key(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_ext_generate_key(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_ext_import_key(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_ext_sign(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t verify, njs_value_t *retval); static njs_int_t njs_ext_unwrap_key(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_ext_wrap_key(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_key_ext_algorithm(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t njs_key_ext_extractable(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t njs_key_ext_type(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t njs_key_ext_usages(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t njs_ext_get_random_values(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_webcrypto_key_t *njs_webcrypto_key_alloc(njs_vm_t *vm, njs_webcrypto_algorithm_t *alg, unsigned usage, njs_bool_t extractable); static njs_webcrypto_key_format_t njs_key_format(njs_vm_t *vm, njs_value_t *value); static njs_str_t *njs_format_string(njs_webcrypto_key_format_t fmt); static njs_int_t njs_key_usage(njs_vm_t *vm, njs_value_t *value, unsigned *mask); static njs_int_t njs_key_ops(njs_vm_t *vm, njs_value_t *retval, unsigned mask); static njs_webcrypto_algorithm_t *njs_key_algorithm(njs_vm_t *vm, njs_value_t *value); static njs_str_t *njs_algorithm_string(njs_webcrypto_algorithm_t *algorithm); static njs_int_t njs_algorithm_hash(njs_vm_t *vm, njs_value_t *value, njs_webcrypto_hash_t *hash); static njs_str_t *njs_algorithm_hash_name(njs_webcrypto_hash_t hash); static const EVP_MD *njs_algorithm_hash_digest(njs_webcrypto_hash_t hash); static njs_int_t njs_algorithm_curve(njs_vm_t *vm, njs_value_t *value, int *curve); static njs_str_t *njs_algorithm_curve_name(int curve); static njs_int_t njs_webcrypto_result(njs_vm_t *vm, njs_opaque_value_t *result, njs_int_t rc, njs_value_t *retval); static njs_int_t njs_webcrypto_array_buffer(njs_vm_t *vm, njs_value_t *retval, u_char *start, size_t length); static void njs_webcrypto_error(njs_vm_t *vm, const char *fmt, ...); static njs_int_t njs_webcrypto_init(njs_vm_t *vm); static njs_webcrypto_entry_t njs_webcrypto_alg[] = { #define njs_webcrypto_algorithm(type, usage, fmt, raw) \ (uintptr_t) & (njs_webcrypto_algorithm_t) { type, usage, fmt, raw } { njs_str("RSASSA-PKCS1-v1_5"), njs_webcrypto_algorithm(NJS_ALGORITHM_RSASSA_PKCS1_v1_5, NJS_KEY_USAGE_SIGN | NJS_KEY_USAGE_VERIFY | NJS_KEY_USAGE_GENERATE_KEY, NJS_KEY_FORMAT_PKCS8 | NJS_KEY_FORMAT_SPKI | NJS_KEY_FORMAT_JWK, 0) }, { njs_str("RSA-PSS"), njs_webcrypto_algorithm(NJS_ALGORITHM_RSA_PSS, NJS_KEY_USAGE_SIGN | NJS_KEY_USAGE_VERIFY | NJS_KEY_USAGE_GENERATE_KEY, NJS_KEY_FORMAT_PKCS8 | NJS_KEY_FORMAT_SPKI | NJS_KEY_FORMAT_JWK, 0) }, { njs_str("RSA-OAEP"), njs_webcrypto_algorithm(NJS_ALGORITHM_RSA_OAEP, NJS_KEY_USAGE_ENCRYPT | NJS_KEY_USAGE_DECRYPT | NJS_KEY_USAGE_WRAP_KEY | NJS_KEY_USAGE_UNWRAP_KEY | NJS_KEY_USAGE_GENERATE_KEY, NJS_KEY_FORMAT_PKCS8 | NJS_KEY_FORMAT_SPKI | NJS_KEY_FORMAT_JWK, 0) }, { njs_str("HMAC"), njs_webcrypto_algorithm(NJS_ALGORITHM_HMAC, NJS_KEY_USAGE_GENERATE_KEY | NJS_KEY_USAGE_SIGN | NJS_KEY_USAGE_VERIFY, NJS_KEY_FORMAT_RAW | NJS_KEY_FORMAT_JWK, 1) }, { njs_str("AES-GCM"), njs_webcrypto_algorithm(NJS_ALGORITHM_AES_GCM, NJS_KEY_USAGE_ENCRYPT | NJS_KEY_USAGE_DECRYPT | NJS_KEY_USAGE_WRAP_KEY | NJS_KEY_USAGE_UNWRAP_KEY | NJS_KEY_USAGE_GENERATE_KEY, NJS_KEY_FORMAT_RAW | NJS_KEY_FORMAT_JWK, 1) }, { njs_str("AES-CTR"), njs_webcrypto_algorithm(NJS_ALGORITHM_AES_CTR, NJS_KEY_USAGE_ENCRYPT | NJS_KEY_USAGE_DECRYPT | NJS_KEY_USAGE_WRAP_KEY | NJS_KEY_USAGE_UNWRAP_KEY | NJS_KEY_USAGE_GENERATE_KEY, NJS_KEY_FORMAT_RAW | NJS_KEY_FORMAT_JWK, 1) }, { njs_str("AES-CBC"), njs_webcrypto_algorithm(NJS_ALGORITHM_AES_CBC, NJS_KEY_USAGE_ENCRYPT | NJS_KEY_USAGE_DECRYPT | NJS_KEY_USAGE_WRAP_KEY | NJS_KEY_USAGE_UNWRAP_KEY | NJS_KEY_USAGE_GENERATE_KEY, NJS_KEY_FORMAT_RAW | NJS_KEY_FORMAT_JWK, 1) }, { njs_str("ECDSA"), njs_webcrypto_algorithm(NJS_ALGORITHM_ECDSA, NJS_KEY_USAGE_SIGN | NJS_KEY_USAGE_VERIFY | NJS_KEY_USAGE_GENERATE_KEY, NJS_KEY_FORMAT_PKCS8 | NJS_KEY_FORMAT_SPKI | NJS_KEY_FORMAT_RAW | NJS_KEY_FORMAT_JWK, 0) }, { njs_str("ECDH"), njs_webcrypto_algorithm(NJS_ALGORITHM_ECDH, NJS_KEY_USAGE_DERIVE_KEY | NJS_KEY_USAGE_DERIVE_BITS | NJS_KEY_USAGE_GENERATE_KEY | NJS_KEY_USAGE_UNSUPPORTED, NJS_KEY_FORMAT_UNKNOWN, 0) }, { njs_str("PBKDF2"), njs_webcrypto_algorithm(NJS_ALGORITHM_PBKDF2, NJS_KEY_USAGE_DERIVE_KEY | NJS_KEY_USAGE_DERIVE_BITS, NJS_KEY_FORMAT_RAW, 1) }, { njs_str("HKDF"), njs_webcrypto_algorithm(NJS_ALGORITHM_HKDF, NJS_KEY_USAGE_DERIVE_KEY | NJS_KEY_USAGE_DERIVE_BITS, NJS_KEY_FORMAT_RAW, 1) }, { njs_null_str, 0 } }; static njs_webcrypto_entry_t njs_webcrypto_hash[] = { { njs_str("SHA-256"), NJS_HASH_SHA256 }, { njs_str("SHA-384"), NJS_HASH_SHA384 }, { njs_str("SHA-512"), NJS_HASH_SHA512 }, { njs_str("SHA-1"), NJS_HASH_SHA1 }, { njs_null_str, 0 } }; static njs_webcrypto_entry_t njs_webcrypto_curve[] = { { njs_str("P-256"), NID_X9_62_prime256v1 }, { njs_str("P-384"), NID_secp384r1 }, { njs_str("P-521"), NID_secp521r1 }, { njs_null_str, 0 } }; static njs_webcrypto_entry_t njs_webcrypto_format[] = { { njs_str("raw"), NJS_KEY_FORMAT_RAW }, { njs_str("pkcs8"), NJS_KEY_FORMAT_PKCS8 }, { njs_str("spki"), NJS_KEY_FORMAT_SPKI }, { njs_str("jwk"), NJS_KEY_FORMAT_JWK }, { njs_null_str, NJS_KEY_FORMAT_UNKNOWN } }; static njs_webcrypto_entry_t njs_webcrypto_usage[] = { { njs_str("decrypt"), NJS_KEY_USAGE_DECRYPT }, { njs_str("deriveBits"), NJS_KEY_USAGE_DERIVE_BITS }, { njs_str("deriveKey"), NJS_KEY_USAGE_DERIVE_KEY }, { njs_str("encrypt"), NJS_KEY_USAGE_ENCRYPT }, { njs_str("sign"), NJS_KEY_USAGE_SIGN }, { njs_str("unwrapKey"), NJS_KEY_USAGE_UNWRAP_KEY }, { njs_str("verify"), NJS_KEY_USAGE_VERIFY }, { njs_str("wrapKey"), NJS_KEY_USAGE_WRAP_KEY }, { njs_null_str, 0 } }; static njs_webcrypto_entry_t njs_webcrypto_alg_hash[] = { { njs_str("RS1"), NJS_HASH_SHA1 }, { njs_str("RS256"), NJS_HASH_SHA256 }, { njs_str("RS384"), NJS_HASH_SHA384 }, { njs_str("RS512"), NJS_HASH_SHA512 }, { njs_str("PS1"), NJS_HASH_SHA1 }, { njs_str("PS256"), NJS_HASH_SHA256 }, { njs_str("PS384"), NJS_HASH_SHA384 }, { njs_str("PS512"), NJS_HASH_SHA512 }, { njs_str("RSA-OAEP"), NJS_HASH_SHA1 }, { njs_str("RSA-OAEP-256"), NJS_HASH_SHA256 }, { njs_str("RSA-OAEP-384"), NJS_HASH_SHA384 }, { njs_str("RSA-OAEP-512"), NJS_HASH_SHA512 }, { njs_null_str, 0 } }; static njs_str_t njs_webcrypto_alg_name[NJS_ALGORITHM_HMAC + 1][NJS_HASH_MAX] = { { njs_null_str, njs_str("RS1"), njs_str("RS256"), njs_str("RS384"), njs_str("RS512"), }, { njs_null_str, njs_str("PS1"), njs_str("PS256"), njs_str("PS384"), njs_str("PS512"), }, { njs_null_str, njs_str("RSA-OAEP"), njs_str("RSA-OAEP-256"), njs_str("RSA-OAEP-384"), njs_str("RSA-OAEP-512"), }, { njs_null_str, njs_str("HS1"), njs_str("HS256"), njs_str("HS384"), njs_str("HS512"), }, }; static njs_str_t njs_webcrypto_alg_aes_name[3][3 + 1] = { { njs_str("A128GCM"), njs_str("A192GCM"), njs_str("A256GCM"), njs_null_str, }, { njs_str("A128CTR"), njs_str("A192CTR"), njs_str("A256CTR"), njs_null_str, }, { njs_str("A128CBC"), njs_str("A192CBC"), njs_str("A256CBC"), njs_null_str, }, }; static njs_external_t njs_ext_subtle_webcrypto[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "SubtleCrypto", } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("decrypt"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_ext_cipher, .magic8 = 0, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("deriveBits"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_ext_derive, .magic8 = 0, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("deriveKey"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_ext_derive, .magic8 = 1, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("digest"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_ext_digest, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("encrypt"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_ext_cipher, .magic8 = 1, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("exportKey"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_ext_export_key, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("generateKey"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_ext_generate_key, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("importKey"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_ext_import_key, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("sign"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_ext_sign, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("unwrapKey"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_ext_unwrap_key, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("verify"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_ext_sign, .magic8 = 1, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("wrapKey"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_ext_wrap_key, } }, }; static njs_external_t njs_ext_webcrypto_crypto_key[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "CryptoKey", } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("algorithm"), .enumerable = 1, .u.property = { .handler = njs_key_ext_algorithm, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("extractable"), .enumerable = 1, .u.property = { .handler = njs_key_ext_extractable, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("type"), .enumerable = 1, .u.property = { .handler = njs_key_ext_type, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("usages"), .enumerable = 1, .u.property = { .handler = njs_key_ext_usages, } }, }; static njs_external_t njs_ext_webcrypto[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "Crypto", } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("getRandomValues"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_ext_get_random_values, } }, { .flags = NJS_EXTERN_OBJECT, .name.string = njs_str("subtle"), .enumerable = 1, .writable = 1, .u.object = { .enumerable = 1, .properties = njs_ext_subtle_webcrypto, .nproperties = njs_nitems(njs_ext_subtle_webcrypto), } }, }; njs_module_t njs_webcrypto_module = { .name = njs_str("webcrypto"), .preinit = NULL, .init = njs_webcrypto_init, }; static const njs_str_t string_alg = njs_str("alg"); static const njs_str_t string_d = njs_str("d"); static const njs_str_t string_dp = njs_str("dp"); static const njs_str_t string_dq = njs_str("dq"); static const njs_str_t string_e = njs_str("e"); static const njs_str_t string_k = njs_str("k"); static const njs_str_t string_n = njs_str("n"); static const njs_str_t string_p = njs_str("p"); static const njs_str_t string_q = njs_str("q"); static const njs_str_t string_qi = njs_str("qi"); static const njs_str_t string_x = njs_str("x"); static const njs_str_t string_y = njs_str("y"); static const njs_str_t string_ext = njs_str("ext"); static const njs_str_t string_crv = njs_str("crv"); static const njs_str_t string_kty = njs_str("kty"); static const njs_str_t key_ops = njs_str("key_ops"); static const njs_str_t string_hash = njs_str("hash"); static const njs_str_t string_name = njs_str("name"); static const njs_str_t string_length = njs_str("length"); static const njs_str_t string_ml = njs_str("modulusLength"); static const njs_str_t string_curve = njs_str("namedCurve"); static njs_int_t njs_webcrypto_crypto_key_proto_id; static njs_int_t njs_ext_cipher(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t encrypt, njs_value_t *retval) { unsigned mask; njs_int_t ret; njs_str_t data; njs_value_t *options; njs_opaque_value_t result; njs_webcrypto_key_t *key; njs_webcrypto_algorithm_t *alg; options = njs_arg(args, nargs, 1); alg = njs_key_algorithm(vm, options); if (njs_slow_path(alg == NULL)) { goto fail; } key = njs_vm_external(vm, njs_webcrypto_crypto_key_proto_id, njs_arg(args, nargs, 2)); if (njs_slow_path(key == NULL)) { njs_vm_type_error(vm, "\"key\" is not a CryptoKey object"); goto fail; } mask = encrypt ? NJS_KEY_USAGE_ENCRYPT : NJS_KEY_USAGE_DECRYPT; if (njs_slow_path(!(key->usage & mask))) { njs_vm_type_error(vm, "provide key does not support %s operation", encrypt ? "encrypt" : "decrypt"); goto fail; } if (njs_slow_path(key->alg != alg)) { njs_vm_type_error(vm, "cannot %s using \"%V\" with \"%V\" key", encrypt ? "encrypt" : "decrypt", njs_algorithm_string(key->alg), njs_algorithm_string(alg)); goto fail; } ret = njs_vm_value_to_bytes(vm, &data, njs_arg(args, nargs, 3)); if (njs_slow_path(ret != NJS_OK)) { goto fail; } switch (alg->type) { case NJS_ALGORITHM_RSA_OAEP: ret = njs_cipher_pkey(vm, &data, key, encrypt, njs_value_arg(&result)); break; case NJS_ALGORITHM_AES_GCM: ret = njs_cipher_aes_gcm(vm, &data, key, options, encrypt, njs_value_arg(&result)); break; case NJS_ALGORITHM_AES_CTR: ret = njs_cipher_aes_ctr(vm, &data, key, options, encrypt, njs_value_arg(&result)); break; case NJS_ALGORITHM_AES_CBC: default: ret = njs_cipher_aes_cbc(vm, &data, key, options, encrypt, njs_value_arg(&result)); } return njs_webcrypto_result(vm, &result, ret, retval); fail: return njs_webcrypto_result(vm, NULL, NJS_ERROR, retval); } static njs_int_t njs_cipher_pkey(njs_vm_t *vm, njs_str_t *data, njs_webcrypto_key_t *key, njs_index_t encrypt, njs_value_t *retval) { u_char *dst; size_t outlen; njs_int_t ret; const EVP_MD *md; EVP_PKEY_CTX *ctx; EVP_PKEY_cipher_t cipher; EVP_PKEY_cipher_init_t init; ctx = EVP_PKEY_CTX_new(key->u.a.pkey, NULL); if (njs_slow_path(ctx == NULL)) { njs_webcrypto_error(vm, "EVP_PKEY_CTX_new() failed"); return NJS_ERROR; } if (encrypt) { init = EVP_PKEY_encrypt_init; cipher = EVP_PKEY_encrypt; } else { init = EVP_PKEY_decrypt_init; cipher = EVP_PKEY_decrypt; } ret = init(ctx); if (njs_slow_path(ret <= 0)) { njs_webcrypto_error(vm, "EVP_PKEY_%scrypt_init() failed", encrypt ? "en" : "de"); ret = NJS_ERROR; goto fail; } md = njs_algorithm_hash_digest(key->hash); EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING); EVP_PKEY_CTX_set_signature_md(ctx, md); EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, md); ret = cipher(ctx, NULL, &outlen, data->start, data->length); if (njs_slow_path(ret <= 0)) { njs_webcrypto_error(vm, "EVP_PKEY_%scrypt() failed", encrypt ? "en" : "de"); ret = NJS_ERROR; goto fail; } dst = njs_mp_alloc(njs_vm_memory_pool(vm), outlen); if (njs_slow_path(dst == NULL)) { njs_vm_memory_error(vm); ret = NJS_ERROR; goto fail; } ret = cipher(ctx, dst, &outlen, data->start, data->length); if (njs_slow_path(ret <= 0)) { njs_webcrypto_error(vm, "EVP_PKEY_%scrypt() failed", encrypt ? "en" : "de"); ret = NJS_ERROR; goto fail; } ret = njs_vm_value_array_buffer_set(vm, retval, dst, outlen); fail: EVP_PKEY_CTX_free(ctx); return ret; } static njs_int_t njs_cipher_aes_gcm(njs_vm_t *vm, njs_str_t *data, njs_webcrypto_key_t *key, njs_value_t *options, njs_bool_t encrypt, njs_value_t *retval) { int len, outlen, dstlen; u_char *dst, *p; int64_t taglen; njs_str_t iv, aad; njs_int_t ret; njs_value_t *value; EVP_CIPHER_CTX *ctx; const EVP_CIPHER *cipher; njs_opaque_value_t lvalue; static const njs_str_t string_iv = njs_str("iv"); static const njs_str_t string_ad = njs_str("additionalData"); static const njs_str_t string_tl = njs_str("tagLength"); switch (key->u.s.raw.length) { case 16: cipher = EVP_aes_128_gcm(); break; case 24: cipher = EVP_aes_192_gcm(); break; case 32: cipher = EVP_aes_256_gcm(); break; default: njs_vm_type_error(vm, "AES-GCM Invalid key length"); return NJS_ERROR; } value = njs_vm_object_prop(vm, options, &string_iv, &lvalue); if (value == NULL) { njs_vm_type_error(vm, "AES-GCM algorithm.iv is not provided"); return NJS_ERROR; } ret = njs_vm_value_to_bytes(vm, &iv, njs_value_arg(&lvalue)); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } taglen = 128; value = njs_vm_object_prop(vm, options, &string_tl, &lvalue); if (value != NULL && !njs_value_is_undefined(value)) { ret = njs_value_to_integer(vm, value, &taglen); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } if (njs_slow_path(taglen != 32 && taglen != 64 && taglen != 96 && taglen != 104 && taglen != 112 && taglen != 120 && taglen != 128)) { njs_vm_type_error(vm, "AES-GCM Invalid tagLength"); return NJS_ERROR; } taglen /= 8; if (njs_slow_path(!encrypt && (data->length < (size_t) taglen))) { njs_vm_type_error(vm, "AES-GCM data is too short"); return NJS_ERROR; } ctx = EVP_CIPHER_CTX_new(); if (njs_slow_path(ctx == NULL)) { njs_webcrypto_error(vm, "EVP_CIPHER_CTX_new() failed"); return NJS_ERROR; } ret = EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, encrypt); if (njs_slow_path(ret <= 0)) { njs_webcrypto_error(vm, "EVP_%sInit_ex() failed", encrypt ? "Encrypt" : "Decrypt"); ret = NJS_ERROR; goto fail; } ret = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, iv.length, NULL); if (njs_slow_path(ret <= 0)) { njs_webcrypto_error(vm, "EVP_CIPHER_CTX_ctrl() failed"); ret = NJS_ERROR; goto fail; } ret = EVP_CipherInit_ex(ctx, NULL, NULL, key->u.s.raw.start, iv.start, encrypt); if (njs_slow_path(ret <= 0)) { njs_webcrypto_error(vm, "EVP_%sInit_ex() failed", encrypt ? "Encrypt" : "Decrypt"); ret = NJS_ERROR; goto fail; } if (!encrypt) { ret = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, taglen, &data->start[data->length - taglen]); if (njs_slow_path(ret <= 0)) { njs_webcrypto_error(vm, "EVP_CIPHER_CTX_ctrl() failed"); ret = NJS_ERROR; goto fail; } } aad.length = 0; value = njs_vm_object_prop(vm, options, &string_ad, &lvalue); if (value != NULL && !njs_value_is_undefined(value)) { ret = njs_vm_value_to_bytes(vm, &aad, value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } if (aad.length != 0) { ret = EVP_CipherUpdate(ctx, NULL, &outlen, aad.start, aad.length); if (njs_slow_path(ret <= 0)) { njs_webcrypto_error(vm, "EVP_%sUpdate() failed", encrypt ? "Encrypt" : "Decrypt"); ret = NJS_ERROR; goto fail; } } dstlen = data->length + EVP_CIPHER_CTX_block_size(ctx) + taglen; dst = njs_mp_alloc(njs_vm_memory_pool(vm), dstlen); if (njs_slow_path(dst == NULL)) { njs_vm_memory_error(vm); return NJS_ERROR; } ret = EVP_CipherUpdate(ctx, dst, &outlen, data->start, data->length - (encrypt ? 0 : taglen)); if (njs_slow_path(ret <= 0)) { njs_webcrypto_error(vm, "EVP_%sUpdate() failed", encrypt ? "Encrypt" : "Decrypt"); ret = NJS_ERROR; goto fail; } p = &dst[outlen]; len = EVP_CIPHER_CTX_block_size(ctx); ret = EVP_CipherFinal_ex(ctx, p, &len); if (njs_slow_path(ret <= 0)) { njs_webcrypto_error(vm, "EVP_%sFinal_ex() failed", encrypt ? "Encrypt" : "Decrypt"); ret = NJS_ERROR; goto fail; } outlen += len; p += len; if (encrypt) { ret = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, taglen, p); if (njs_slow_path(ret <= 0)) { njs_webcrypto_error(vm, "EVP_CIPHER_CTX_ctrl() failed"); ret = NJS_ERROR; goto fail; } outlen += taglen; } ret = njs_vm_value_array_buffer_set(vm, retval, dst, outlen); fail: EVP_CIPHER_CTX_free(ctx); return ret; } static njs_int_t njs_cipher_aes_ctr128(njs_vm_t *vm, const EVP_CIPHER *cipher, u_char *key, u_char *data, size_t dlen, u_char *counter, u_char *dst, int *olen, njs_bool_t encrypt) { int len, outlen; njs_int_t ret; EVP_CIPHER_CTX *ctx; ctx = EVP_CIPHER_CTX_new(); if (njs_slow_path(ctx == NULL)) { njs_webcrypto_error(vm, "EVP_CIPHER_CTX_new() failed"); return NJS_ERROR; } ret = EVP_CipherInit_ex(ctx, cipher, NULL, key, counter, encrypt); if (njs_slow_path(ret <= 0)) { njs_webcrypto_error(vm, "EVP_%sInit_ex() failed", encrypt ? "Encrypt" : "Decrypt"); ret = NJS_ERROR; goto fail; } ret = EVP_CipherUpdate(ctx, dst, &outlen, data, dlen); if (njs_slow_path(ret <= 0)) { njs_webcrypto_error(vm, "EVP_%sUpdate() failed", encrypt ? "Encrypt" : "Decrypt"); ret = NJS_ERROR; goto fail; } ret = EVP_CipherFinal_ex(ctx, &dst[outlen], &len); if (njs_slow_path(ret <= 0)) { njs_webcrypto_error(vm, "EVP_%sFinal_ex() failed", encrypt ? "Encrypt" : "Decrypt"); ret = NJS_ERROR; goto fail; } outlen += len; *olen = outlen; ret = NJS_OK; fail: EVP_CIPHER_CTX_free(ctx); return ret; } njs_inline njs_uint_t njs_ceil_div(njs_uint_t dend, njs_uint_t dsor) { return (dsor == 0) ? 0 : 1 + (dend - 1) / dsor; } njs_inline BIGNUM * njs_bn_counter128(njs_str_t *ctr, njs_uint_t bits) { njs_uint_t remainder, bytes; uint8_t buf[16]; remainder = bits % 8; if (remainder == 0) { bytes = bits / 8; return BN_bin2bn(&ctr->start[ctr->length - bytes], bytes, NULL); } bytes = njs_ceil_div(bits, 8); memcpy(buf, &ctr->start[ctr->length - bytes], bytes); buf[0] &= ~(0xFF << remainder); return BN_bin2bn(buf, bytes, NULL); } njs_inline void njs_counter128_reset(u_char *src, u_char *dst, njs_uint_t bits) { size_t index; njs_uint_t remainder, bytes; bytes = bits / 8; remainder = bits % 8; memcpy(dst, src, 16); index = 16 - bytes; memset(&dst[index], 0, bytes); if (remainder) { dst[index - 1] &= 0xff << remainder; } } static njs_int_t njs_cipher_aes_ctr(njs_vm_t *vm, njs_str_t *data, njs_webcrypto_key_t *key, njs_value_t *options, njs_bool_t encrypt, njs_value_t *retval) { int len, len2; u_char *dst; int64_t length; BIGNUM *total, *blocks, *left, *ctr; njs_int_t ret; njs_str_t iv; njs_uint_t size1; njs_value_t *value; const EVP_CIPHER *cipher; njs_opaque_value_t lvalue; u_char iv2[16]; static const njs_str_t string_counter = njs_str("counter"); switch (key->u.s.raw.length) { case 16: cipher = EVP_aes_128_ctr(); break; case 24: cipher = EVP_aes_192_ctr(); break; case 32: cipher = EVP_aes_256_ctr(); break; default: njs_vm_type_error(vm, "AES-CTR Invalid key length"); return NJS_ERROR; } value = njs_vm_object_prop(vm, options, &string_counter, &lvalue); if (value == NULL) { njs_vm_type_error(vm, "AES-CTR algorithm.counter is not provided"); return NJS_ERROR; } ret = njs_vm_value_to_bytes(vm, &iv, value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } if (njs_slow_path(iv.length != 16)) { njs_vm_type_error(vm, "AES-CTR algorithm.counter must be 16 bytes " "long"); return NJS_ERROR; } value = njs_vm_object_prop(vm, options, &string_length, &lvalue); if (value == NULL) { njs_vm_type_error(vm, "AES-CTR algorithm.length is not provided"); return NJS_ERROR; } ret = njs_value_to_integer(vm, value, &length); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } if (njs_slow_path(length == 0 || length > 128)) { njs_vm_type_error(vm, "AES-CTR algorithm.length must be between " "1 and 128"); return NJS_ERROR; } ctr = NULL; blocks = NULL; left = NULL; total = BN_new(); if (njs_slow_path(total == NULL)) { njs_webcrypto_error(vm, "BN_new() failed"); return NJS_ERROR; } ret = BN_lshift(total, BN_value_one(), length); if (njs_slow_path(ret != 1)) { njs_webcrypto_error(vm, "BN_lshift() failed"); ret = NJS_ERROR; goto fail; } ctr = njs_bn_counter128(&iv, length); if (njs_slow_path(ctr == NULL)) { njs_webcrypto_error(vm, "BN_bin2bn() failed"); ret = NJS_ERROR; goto fail; } blocks = BN_new(); if (njs_slow_path(blocks == NULL)) { njs_webcrypto_error(vm, "BN_new() failed"); return NJS_ERROR; } ret = BN_set_word(blocks, njs_ceil_div(data->length, AES_BLOCK_SIZE)); if (njs_slow_path(ret != 1)) { njs_webcrypto_error(vm, "BN_set_word() failed"); ret = NJS_ERROR; goto fail; } ret = BN_cmp(blocks, total); if (njs_slow_path(ret > 0)) { njs_vm_type_error(vm, "AES-CTR repeated counter"); ret = NJS_ERROR; goto fail; } left = BN_new(); if (njs_slow_path(left == NULL)) { njs_webcrypto_error(vm, "BN_new() failed"); return NJS_ERROR; } ret = BN_sub(left, total, ctr); if (njs_slow_path(ret != 1)) { njs_webcrypto_error(vm, "BN_sub() failed"); ret = NJS_ERROR; goto fail; } dst = njs_mp_alloc(njs_vm_memory_pool(vm), data->length + EVP_MAX_BLOCK_LENGTH); if (njs_slow_path(dst == NULL)) { njs_vm_memory_error(vm); return NJS_ERROR; } ret = BN_cmp(left, blocks); if (ret >= 0) { /* * Doing a single run if a counter is not wrapped-around * during the ciphering. * */ ret = njs_cipher_aes_ctr128(vm, cipher, key->u.s.raw.start, data->start, data->length, iv.start, dst, &len, encrypt); if (njs_slow_path(ret != NJS_OK)) { goto fail; } goto done; } /* * Otherwise splitting ciphering into two parts: * Until the wrapping moment * After the resetting counter to zero. */ size1 = BN_get_word(left) * AES_BLOCK_SIZE; ret = njs_cipher_aes_ctr128(vm, cipher, key->u.s.raw.start, data->start, size1, iv.start, dst, &len, encrypt); if (njs_slow_path(ret != NJS_OK)) { goto fail; } njs_counter128_reset(iv.start, (u_char *) iv2, length); ret = njs_cipher_aes_ctr128(vm, cipher, key->u.s.raw.start, &data->start[size1], data->length - size1, iv2, &dst[size1], &len2, encrypt); if (njs_slow_path(ret != NJS_OK)) { goto fail; } len += len2; done: ret = njs_vm_value_array_buffer_set(vm, retval, dst, len); fail: BN_free(total); if (ctr != NULL) { BN_free(ctr); } if (blocks != NULL) { BN_free(blocks); } if (left != NULL) { BN_free(left); } return ret; } static njs_int_t njs_cipher_aes_cbc(njs_vm_t *vm, njs_str_t *data, njs_webcrypto_key_t *key, njs_value_t *options, njs_bool_t encrypt, njs_value_t *retval) { int olen_max, olen, olen2; u_char *dst; unsigned remainder; njs_str_t iv; njs_int_t ret; njs_value_t *value; EVP_CIPHER_CTX *ctx; const EVP_CIPHER *cipher; njs_opaque_value_t lvalue; static const njs_str_t string_iv = njs_str("iv"); switch (key->u.s.raw.length) { case 16: cipher = EVP_aes_128_cbc(); break; case 24: cipher = EVP_aes_192_cbc(); break; case 32: cipher = EVP_aes_256_cbc(); break; default: njs_vm_type_error(vm, "AES-CBC Invalid key length"); return NJS_ERROR; } value = njs_vm_object_prop(vm, options, &string_iv, &lvalue); if (value == NULL) { njs_vm_type_error(vm, "AES-CBC algorithm.iv is not provided"); return NJS_ERROR; } ret = njs_vm_value_to_bytes(vm, &iv, value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } if (njs_slow_path(iv.length != 16)) { njs_vm_type_error(vm, "AES-CBC algorithm.iv must be 16 bytes long"); return NJS_ERROR; } olen_max = data->length + AES_BLOCK_SIZE - 1; remainder = olen_max % AES_BLOCK_SIZE; if (remainder != 0) { olen_max += AES_BLOCK_SIZE - remainder; } ctx = EVP_CIPHER_CTX_new(); if (njs_slow_path(ctx == NULL)) { njs_webcrypto_error(vm, "EVP_CIPHER_CTX_new() failed"); return NJS_ERROR; } ret = EVP_CipherInit_ex(ctx, cipher, NULL, key->u.s.raw.start, iv.start, encrypt); if (njs_slow_path(ret <= 0)) { njs_webcrypto_error(vm, "EVP_%SInit_ex() failed", encrypt ? "Encrypt" : "Decrypt"); ret = NJS_ERROR; goto fail; } dst = njs_mp_alloc(njs_vm_memory_pool(vm), olen_max); if (njs_slow_path(dst == NULL)) { njs_vm_memory_error(vm); ret = NJS_ERROR; goto fail; } ret = EVP_CipherUpdate(ctx, dst, &olen, data->start, data->length); if (njs_slow_path(ret <= 0)) { njs_webcrypto_error(vm, "EVP_%SUpdate() failed", encrypt ? "Encrypt" : "Decrypt"); ret = NJS_ERROR; goto fail; } ret = EVP_CipherFinal_ex(ctx, &dst[olen], &olen2); if (njs_slow_path(ret <= 0)) { njs_webcrypto_error(vm, "EVP_%sFinal_ex() failed", encrypt ? "Encrypt" : "Decrypt"); ret = NJS_ERROR; goto fail; } olen += olen2; ret = njs_vm_value_array_buffer_set(vm, retval, dst, olen); fail: EVP_CIPHER_CTX_free(ctx); return ret; } static njs_int_t njs_ext_derive(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t derive_key, njs_value_t *retval) { u_char *k; size_t olen; int64_t iterations, length; unsigned usage, mask; njs_int_t ret; njs_str_t salt, info; njs_value_t *value, *aobject, *dobject; const EVP_MD *md; EVP_PKEY_CTX *pctx; njs_webcrypto_key_t *key, *dkey; njs_opaque_value_t lvalue; njs_webcrypto_hash_t hash; njs_webcrypto_algorithm_t *alg, *dalg; static const njs_str_t string_info = njs_str("info"); static const njs_str_t string_salt = njs_str("salt"); static const njs_str_t string_iterations = njs_str("iterations"); aobject = njs_arg(args, nargs, 1); alg = njs_key_algorithm(vm, aobject); if (njs_slow_path(alg == NULL)) { goto fail; } key = njs_vm_external(vm, njs_webcrypto_crypto_key_proto_id, njs_arg(args, nargs, 2)); if (njs_slow_path(key == NULL)) { njs_vm_type_error(vm, "\"baseKey\" is not a CryptoKey object"); goto fail; } mask = derive_key ? NJS_KEY_USAGE_DERIVE_KEY : NJS_KEY_USAGE_DERIVE_BITS; if (njs_slow_path(!(key->usage & mask))) { njs_vm_type_error(vm, "provide key does not support \"%s\" operation", derive_key ? "deriveKey" : "deriveBits"); goto fail; } if (njs_slow_path(key->alg != alg)) { njs_vm_type_error(vm, "cannot derive %s using \"%V\" with \"%V\" key", derive_key ? "key" : "bits", njs_algorithm_string(key->alg), njs_algorithm_string(alg)); goto fail; } dobject = njs_arg(args, nargs, 3); if (derive_key) { dalg = njs_key_algorithm(vm, dobject); if (njs_slow_path(dalg == NULL)) { goto fail; } value = njs_vm_object_prop(vm, dobject, &string_length, &lvalue); if (value == NULL) { njs_vm_type_error(vm, "derivedKeyAlgorithm.length is not provided"); goto fail; } } else { dalg = NULL; value = dobject; } ret = njs_value_to_integer(vm, value, &length); if (njs_slow_path(ret != NJS_OK)) { goto fail; } dkey = NULL; length /= 8; if (derive_key) { switch (dalg->type) { case NJS_ALGORITHM_AES_GCM: case NJS_ALGORITHM_AES_CTR: case NJS_ALGORITHM_AES_CBC: if (length != 16 && length != 32) { njs_vm_type_error(vm, "deriveKey \"%V\" length must be " "128 or 256", njs_algorithm_string(dalg)); goto fail; } break; default: njs_vm_internal_error(vm, "not implemented deriveKey: \"%V\"", njs_algorithm_string(dalg)); goto fail; } ret = njs_key_usage(vm, njs_arg(args, nargs, 5), &usage); if (njs_slow_path(ret != NJS_OK)) { goto fail; } if (njs_slow_path(usage & ~dalg->usage)) { njs_vm_type_error(vm, "unsupported key usage for \"%V\" key", njs_algorithm_string(alg)); goto fail; } dkey = njs_mp_zalloc(njs_vm_memory_pool(vm), sizeof(njs_webcrypto_key_t)); if (njs_slow_path(dkey == NULL)) { njs_vm_memory_error(vm); goto fail; } dkey->alg = dalg; dkey->usage = usage; } k = njs_mp_zalloc(njs_vm_memory_pool(vm), length); if (njs_slow_path(k == NULL)) { njs_vm_memory_error(vm); goto fail; } switch (alg->type) { case NJS_ALGORITHM_PBKDF2: ret = njs_algorithm_hash(vm, aobject, &hash); if (njs_slow_path(ret == NJS_ERROR)) { goto fail; } value = njs_vm_object_prop(vm, aobject, &string_salt, &lvalue); if (value == NULL) { njs_vm_type_error(vm, "PBKDF2 algorithm.salt is not provided"); goto fail; } ret = njs_vm_value_to_bytes(vm, &salt, value); if (njs_slow_path(ret != NJS_OK)) { goto fail; } if (njs_slow_path(salt.length < 16)) { njs_vm_type_error(vm, "PBKDF2 algorithm.salt must be " "at least 16 bytes long"); goto fail; } value = njs_vm_object_prop(vm, aobject, &string_iterations, &lvalue); if (value == NULL) { njs_vm_type_error(vm, "PBKDF2 algorithm.iterations is not " "provided"); goto fail; } ret = njs_value_to_integer(vm, value, &iterations); if (njs_slow_path(ret != NJS_OK)) { goto fail; } md = njs_algorithm_hash_digest(hash); ret = PKCS5_PBKDF2_HMAC((char *) key->u.s.raw.start, key->u.s.raw.length, salt.start, salt.length, iterations, md, length, k); if (njs_slow_path(ret <= 0)) { njs_webcrypto_error(vm, "PKCS5_PBKDF2_HMAC() failed"); goto fail; } break; case NJS_ALGORITHM_HKDF: #ifdef EVP_PKEY_HKDF ret = njs_algorithm_hash(vm, aobject, &hash); if (njs_slow_path(ret == NJS_ERROR)) { goto fail; } value = njs_vm_object_prop(vm, aobject, &string_salt, &lvalue); if (value == NULL) { njs_vm_type_error(vm, "HKDF algorithm.salt is not provided"); goto fail; } ret = njs_vm_value_to_bytes(vm, &salt, value); if (njs_slow_path(ret != NJS_OK)) { goto fail; } value = njs_vm_object_prop(vm, aobject, &string_info, &lvalue); if (value == NULL) { njs_vm_type_error(vm, "HKDF algorithm.info is not provided"); goto fail; } ret = njs_vm_value_to_bytes(vm, &info, value); if (njs_slow_path(ret != NJS_OK)) { goto fail; } pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); if (njs_slow_path(pctx == NULL)) { njs_webcrypto_error(vm, "EVP_PKEY_CTX_new_id() failed"); goto fail; } ret = EVP_PKEY_derive_init(pctx); if (njs_slow_path(ret <= 0)) { njs_webcrypto_error(vm, "EVP_PKEY_derive_init() failed"); goto free; } md = njs_algorithm_hash_digest(hash); ret = EVP_PKEY_CTX_set_hkdf_md(pctx, md); if (njs_slow_path(ret <= 0)) { njs_webcrypto_error(vm, "EVP_PKEY_CTX_set_hkdf_md() failed"); goto free; } ret = EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt.start, salt.length); if (njs_slow_path(ret <= 0)) { njs_webcrypto_error(vm, "EVP_PKEY_CTX_set1_hkdf_salt() failed"); goto free; } ret = EVP_PKEY_CTX_set1_hkdf_key(pctx, key->u.s.raw.start, key->u.s.raw.length); if (njs_slow_path(ret <= 0)) { njs_webcrypto_error(vm, "EVP_PKEY_CTX_set1_hkdf_key() failed"); goto free; } ret = EVP_PKEY_CTX_add1_hkdf_info(pctx, info.start, info.length); if (njs_slow_path(ret <= 0)) { njs_webcrypto_error(vm, "EVP_PKEY_CTX_add1_hkdf_info() failed"); goto free; } olen = (size_t) length; ret = EVP_PKEY_derive(pctx, k, &olen); if (njs_slow_path(ret <= 0 || olen != (size_t) length)) { njs_webcrypto_error(vm, "EVP_PKEY_derive() failed"); goto free; } free: EVP_PKEY_CTX_free(pctx); if (njs_slow_path(ret <= 0)) { goto fail; } break; #else (void) pctx; (void) olen; (void) &string_info; (void) &info; #endif case NJS_ALGORITHM_ECDH: default: njs_vm_internal_error(vm, "not implemented deriveKey " "algorithm: \"%V\"", njs_algorithm_string(alg)); goto fail; } if (derive_key) { if (dalg->type == NJS_ALGORITHM_HMAC) { ret = njs_algorithm_hash(vm, dobject, &dkey->hash); if (njs_slow_path(ret == NJS_ERROR)) { goto fail; } } dkey->u.s.raw.start = k; dkey->u.s.raw.length = length; ret = njs_vm_external_create(vm, njs_value_arg(&lvalue), njs_webcrypto_crypto_key_proto_id, dkey, 0); } else { ret = njs_vm_value_array_buffer_set(vm, njs_value_arg(&lvalue), k, length); } if (njs_slow_path(ret != NJS_OK)) { goto fail; } return njs_webcrypto_result(vm, &lvalue, NJS_OK, retval); fail: return njs_webcrypto_result(vm, NULL, NJS_ERROR, retval); } static njs_int_t njs_ext_digest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { unsigned olen; u_char *dst; njs_str_t data; njs_int_t ret; const EVP_MD *md; njs_opaque_value_t result; njs_webcrypto_hash_t hash; ret = njs_algorithm_hash(vm, njs_arg(args, nargs, 1), &hash); if (njs_slow_path(ret == NJS_ERROR)) { goto fail; } ret = njs_vm_value_to_bytes(vm, &data, njs_arg(args, nargs, 2)); if (njs_slow_path(ret != NJS_OK)) { goto fail; } md = njs_algorithm_hash_digest(hash); olen = EVP_MD_size(md); dst = njs_mp_zalloc(njs_vm_memory_pool(vm), olen); if (njs_slow_path(dst == NULL)) { njs_vm_memory_error(vm); goto fail; } ret = EVP_Digest(data.start, data.length, dst, &olen, md, NULL); if (njs_slow_path(ret <= 0)) { njs_webcrypto_error(vm, "EVP_Digest() failed"); goto fail; } ret = njs_vm_value_array_buffer_set(vm, njs_value_arg(&result), dst, olen); if (njs_slow_path(ret != NJS_OK)) { goto fail; } return njs_webcrypto_result(vm, &result, NJS_OK, retval); fail: return njs_webcrypto_result(vm, NULL, NJS_ERROR, retval); } static njs_int_t njs_export_base64url_bignum(njs_vm_t *vm, njs_opaque_value_t *retval, const BIGNUM *v, size_t size) { njs_str_t src; u_char buf[512]; if (size == 0) { size = BN_num_bytes(v); } if (njs_bn_bn2binpad(v, &buf[0], size) <= 0) { return NJS_ERROR; } src.start = buf; src.length = size; return njs_string_base64url(vm, njs_value_arg(retval), &src); } static njs_int_t njs_base64url_bignum_set(njs_vm_t *vm, njs_value_t *jwk, const njs_str_t *key, const BIGNUM *v, size_t size) { njs_int_t ret; njs_opaque_value_t value; ret = njs_export_base64url_bignum(vm, &value, v, size); if (ret != NJS_OK) { return NJS_ERROR; } return njs_vm_object_prop_set(vm, jwk, key, &value); } static njs_int_t njs_export_jwk_rsa(njs_vm_t *vm, njs_webcrypto_key_t *key, njs_value_t *retval) { njs_int_t ret; const RSA *rsa; njs_str_t *nm; const BIGNUM *n_bn, *e_bn, *d_bn, *p_bn, *q_bn, *dp_bn, *dq_bn, *qi_bn; njs_opaque_value_t nvalue, evalue, alg, rsa_s; rsa = njs_pkey_get_rsa_key(key->u.a.pkey); njs_rsa_get0_key(rsa, &n_bn, &e_bn, &d_bn); ret = njs_export_base64url_bignum(vm, &nvalue, n_bn, 0); if (ret != NJS_OK) { return NJS_ERROR; } ret = njs_export_base64url_bignum(vm, &evalue, e_bn, 0); if (ret != NJS_OK) { return NJS_ERROR; } ret = njs_vm_object_alloc(vm, retval, NULL); if (ret != NJS_OK) { return NJS_ERROR; } njs_vm_value_string_create(vm, njs_value_arg(&rsa_s), (u_char *) "RSA", 3); ret = njs_vm_object_prop_set(vm, retval, &string_kty, &rsa_s); if (ret != NJS_OK) { return NJS_ERROR; } ret = njs_vm_object_prop_set(vm, retval, &string_n, &nvalue); if (ret != NJS_OK) { return NJS_ERROR; } ret = njs_vm_object_prop_set(vm, retval, &string_e, &evalue); if (ret != NJS_OK) { return NJS_ERROR; } if (key->u.a.privat) { njs_rsa_get0_factors(rsa, &p_bn, &q_bn); njs_rsa_get0_ctr_params(rsa, &dp_bn, &dq_bn, &qi_bn); ret = njs_base64url_bignum_set(vm, retval, &string_d, d_bn, 0); if (ret != NJS_OK) { return NJS_ERROR; } ret = njs_base64url_bignum_set(vm, retval, &string_p, p_bn, 0); if (ret != NJS_OK) { return NJS_ERROR; } ret = njs_base64url_bignum_set(vm, retval, &string_q, q_bn, 0); if (ret != NJS_OK) { return NJS_ERROR; } ret = njs_base64url_bignum_set(vm, retval, &string_dp, dp_bn, 0); if (ret != NJS_OK) { return NJS_ERROR; } ret = njs_base64url_bignum_set(vm, retval, &string_dq, dq_bn, 0); if (ret != NJS_OK) { return NJS_ERROR; } ret = njs_base64url_bignum_set(vm, retval, &string_qi, qi_bn, 0); if (ret != NJS_OK) { return NJS_ERROR; } } nm = &njs_webcrypto_alg_name[key->alg->type][key->hash]; (void) njs_vm_value_string_create(vm, njs_value_arg(&alg), nm->start, nm->length); return njs_vm_object_prop_set(vm, retval, &string_alg, &alg); } static njs_int_t njs_export_jwk_ec(njs_vm_t *vm, njs_webcrypto_key_t *key, njs_value_t *retval) { int nid, group_bits, group_bytes; BIGNUM *x_bn, *y_bn; njs_int_t ret; njs_str_t *cname; const EC_KEY *ec; const BIGNUM *d_bn; const EC_POINT *pub; const EC_GROUP *group; njs_opaque_value_t xvalue, yvalue, dvalue, name, ec_s; x_bn = NULL; y_bn = NULL; d_bn = NULL; ec = njs_pkey_get_ec_key(key->u.a.pkey); pub = EC_KEY_get0_public_key(ec); group = EC_KEY_get0_group(ec); group_bits = EC_GROUP_get_degree(group); group_bytes = (group_bits / 8) + (7 + (group_bits % 8)) / 8; x_bn = BN_new(); if (x_bn == NULL) { goto fail; } y_bn = BN_new(); if (y_bn == NULL) { goto fail; } if (!njs_ec_point_get_affine_coordinates(group, pub, x_bn, y_bn)) { njs_webcrypto_error(vm, "EC_POINT_get_affine_coordinates() failed"); goto fail; } ret = njs_export_base64url_bignum(vm, &xvalue, x_bn, group_bytes); if (ret != NJS_OK) { goto fail; } BN_free(x_bn); x_bn = NULL; ret = njs_export_base64url_bignum(vm, &yvalue, y_bn, group_bytes); if (ret != NJS_OK) { goto fail; } BN_free(y_bn); y_bn = NULL; nid = EC_GROUP_get_curve_name(group); cname = njs_algorithm_curve_name(nid); (void) njs_vm_value_string_create(vm, njs_value_arg(&name), cname->start, cname->length); if (cname->length == 0) { njs_vm_type_error(vm, "Unsupported JWK EC curve: %s", OBJ_nid2sn(nid)); goto fail; } ret = njs_vm_object_alloc(vm, retval, NULL); if (ret != NJS_OK) { goto fail; } njs_vm_value_string_create(vm, njs_value_arg(&ec_s), (u_char *) "EC", 2); ret = njs_vm_object_prop_set(vm, retval, &string_kty, &ec_s); if (ret != NJS_OK) { return NJS_ERROR; } ret = njs_vm_object_prop_set(vm, retval, &string_x, &xvalue); if (ret != NJS_OK) { return NJS_ERROR; } ret = njs_vm_object_prop_set(vm, retval, &string_y, &yvalue); if (ret != NJS_OK) { return NJS_ERROR; } ret = njs_vm_object_prop_set(vm, retval, &string_crv, &name); if (ret != NJS_OK) { return NJS_ERROR; } if (key->u.a.privat) { d_bn = EC_KEY_get0_private_key(ec); ret = njs_export_base64url_bignum(vm, &dvalue, d_bn, group_bytes); if (ret != NJS_OK) { goto fail; } ret = njs_vm_object_prop_set(vm, retval, &string_d, &dvalue); if (ret != NJS_OK) { goto fail; } } return NJS_OK; fail: if (x_bn != NULL) { BN_free(x_bn); } if (y_bn != NULL) { BN_free(y_bn); } return NJS_ERROR; } static njs_int_t njs_export_raw_ec(njs_vm_t *vm, njs_webcrypto_key_t *key, njs_value_t *retval) { size_t size; u_char *dst; const EC_KEY *ec; const EC_GROUP *group; const EC_POINT *point; point_conversion_form_t form; njs_assert(key->u.a.pkey != NULL); if (key->u.a.privat) { njs_vm_type_error(vm, "private key of \"%V\" cannot be exported " "in \"raw\" format", njs_algorithm_string(key->alg)); return NJS_ERROR; } ec = njs_pkey_get_ec_key(key->u.a.pkey); group = EC_KEY_get0_group(ec); point = EC_KEY_get0_public_key(ec); form = POINT_CONVERSION_UNCOMPRESSED; size = EC_POINT_point2oct(group, point, form, NULL, 0, NULL); if (njs_slow_path(size == 0)) { njs_webcrypto_error(vm, "EC_POINT_point2oct() failed"); return NJS_ERROR; } dst = njs_mp_alloc(njs_vm_memory_pool(vm), size); if (njs_slow_path(dst == NULL)) { return NJS_ERROR; } size = EC_POINT_point2oct(group, point, form, dst, size, NULL); if (njs_slow_path(size == 0)) { njs_webcrypto_error(vm, "EC_POINT_point2oct() failed"); return NJS_ERROR; } return njs_vm_value_array_buffer_set(vm, retval, dst, size); } static njs_int_t njs_export_jwk_asymmetric(njs_vm_t *vm, njs_webcrypto_key_t *key, njs_value_t *retval) { njs_int_t ret; njs_opaque_value_t ops, extractable; njs_assert(key->u.a.pkey != NULL); switch (EVP_PKEY_id(key->u.a.pkey)) { case EVP_PKEY_RSA: #if (OPENSSL_VERSION_NUMBER >= 0x10101001L) case EVP_PKEY_RSA_PSS: #endif ret = njs_export_jwk_rsa(vm, key, retval); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } break; case EVP_PKEY_EC: ret = njs_export_jwk_ec(vm, key, retval); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } break; default: njs_vm_type_error(vm, "provided key cannot be exported as JWK"); return NJS_ERROR; } ret = njs_key_ops(vm, njs_value_arg(&ops), key->usage); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_vm_object_prop_set(vm, retval, &key_ops, &ops); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_value_boolean_set(njs_value_arg(&extractable), key->extractable); return njs_vm_object_prop_set(vm, retval, &string_ext, &extractable); } static njs_int_t njs_export_jwk_oct(njs_vm_t *vm, njs_webcrypto_key_t *key, njs_value_t *retval) { njs_int_t ret; njs_str_t *nm; njs_opaque_value_t k, alg, ops, extractable, oct_s; njs_webcrypto_alg_t type; njs_assert(key->u.s.raw.start != NULL); ret = njs_string_base64url(vm, njs_value_arg(&k), &key->u.s.raw); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } type = key->alg->type; if (key->alg->type == NJS_ALGORITHM_HMAC) { nm = &njs_webcrypto_alg_name[type][key->hash]; (void) njs_vm_value_string_create(vm, njs_value_arg(&alg), nm->start, nm->length); } else { switch (key->u.s.raw.length) { case 16: case 24: case 32: nm = &njs_webcrypto_alg_aes_name [type - NJS_ALGORITHM_AES_GCM][(key->u.s.raw.length - 16) / 8]; (void) njs_vm_value_string_create(vm, njs_value_arg(&alg), nm->start, nm->length); break; default: njs_value_undefined_set(njs_value_arg(&alg)); break; } } ret = njs_key_ops(vm, njs_value_arg(&ops), key->usage); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_value_boolean_set(njs_value_arg(&extractable), key->extractable); ret = njs_vm_object_alloc(vm, retval, NULL); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_vm_value_string_create(vm, njs_value_arg(&oct_s), (u_char *) "oct", 3); ret = njs_vm_object_prop_set(vm, retval, &string_kty, &oct_s); if (ret != NJS_OK) { return NJS_ERROR; } ret = njs_vm_object_prop_set(vm, retval, &string_k, &k); if (ret != NJS_OK) { return NJS_ERROR; } ret = njs_vm_object_prop_set(vm, retval, &key_ops, &ops); if (ret != NJS_OK) { return NJS_ERROR; } ret = njs_vm_object_prop_set(vm, retval, &string_ext, &extractable); if (ret != NJS_OK) { return NJS_ERROR; } if (!njs_value_is_undefined(njs_value_arg(&alg))) { ret = njs_vm_object_prop_set(vm, retval, &string_alg, &alg); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } return NJS_OK; } static njs_int_t njs_ext_export_key(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { BIO *bio; BUF_MEM *mem; njs_int_t ret; njs_webcrypto_key_t *key; PKCS8_PRIV_KEY_INFO *pkcs8; njs_opaque_value_t value; njs_webcrypto_key_format_t fmt; fmt = njs_key_format(vm, njs_arg(args, nargs, 1)); if (njs_slow_path(fmt == NJS_KEY_FORMAT_UNKNOWN)) { goto fail; } key = njs_vm_external(vm, njs_webcrypto_crypto_key_proto_id, njs_arg(args, nargs, 2)); if (njs_slow_path(key == NULL)) { njs_vm_type_error(vm, "\"key\" is not a CryptoKey object"); goto fail; } if (njs_slow_path(!(fmt & key->alg->fmt))) { njs_vm_type_error(vm, "unsupported key fmt \"%V\" for \"%V\" key", njs_format_string(fmt), njs_algorithm_string(key->alg)); goto fail; } if (njs_slow_path(!key->extractable)) { njs_vm_type_error(vm, "provided key cannot be extracted"); goto fail; } switch (fmt) { case NJS_KEY_FORMAT_JWK: switch (key->alg->type) { case NJS_ALGORITHM_RSASSA_PKCS1_v1_5: case NJS_ALGORITHM_RSA_PSS: case NJS_ALGORITHM_RSA_OAEP: case NJS_ALGORITHM_ECDSA: ret = njs_export_jwk_asymmetric(vm, key, njs_value_arg(&value)); if (njs_slow_path(ret != NJS_OK)) { goto fail; } break; case NJS_ALGORITHM_AES_GCM: case NJS_ALGORITHM_AES_CTR: case NJS_ALGORITHM_AES_CBC: case NJS_ALGORITHM_HMAC: ret = njs_export_jwk_oct(vm, key, njs_value_arg(&value)); if (njs_slow_path(ret != NJS_OK)) { goto fail; } break; default: break; } break; case NJS_KEY_FORMAT_PKCS8: if (!key->u.a.privat) { njs_vm_type_error(vm, "public key of \"%V\" cannot be exported " "as PKCS8", njs_algorithm_string(key->alg)); goto fail; } bio = BIO_new(BIO_s_mem()); if (njs_slow_path(bio == NULL)) { njs_webcrypto_error(vm, "BIO_new(BIO_s_mem()) failed"); goto fail; } njs_assert(key->u.a.pkey != NULL); pkcs8 = EVP_PKEY2PKCS8(key->u.a.pkey); if (njs_slow_path(pkcs8 == NULL)) { BIO_free(bio); njs_webcrypto_error(vm, "EVP_PKEY2PKCS8() failed"); goto fail; } if (!i2d_PKCS8_PRIV_KEY_INFO_bio(bio, pkcs8)) { BIO_free(bio); PKCS8_PRIV_KEY_INFO_free(pkcs8); njs_webcrypto_error(vm, "i2d_PKCS8_PRIV_KEY_INFO_bio() failed"); goto fail; } BIO_get_mem_ptr(bio, &mem); ret = njs_webcrypto_array_buffer(vm, njs_value_arg(&value), (u_char *) mem->data, mem->length); BIO_free(bio); PKCS8_PRIV_KEY_INFO_free(pkcs8); if (njs_slow_path(ret != NJS_OK)) { goto fail; } break; case NJS_KEY_FORMAT_SPKI: if (key->u.a.privat) { njs_vm_type_error(vm, "private key of \"%V\" cannot be exported " "as SPKI", njs_algorithm_string(key->alg)); goto fail; } bio = BIO_new(BIO_s_mem()); if (njs_slow_path(bio == NULL)) { njs_webcrypto_error(vm, "BIO_new(BIO_s_mem()) failed"); goto fail; } njs_assert(key->u.a.pkey != NULL); if (!i2d_PUBKEY_bio(bio, key->u.a.pkey)) { BIO_free(bio); njs_webcrypto_error(vm, "i2d_PUBKEY_bio() failed"); goto fail; } BIO_get_mem_ptr(bio, &mem); ret = njs_webcrypto_array_buffer(vm, njs_value_arg(&value), (u_char *) mem->data, mem->length); BIO_free(bio); if (njs_slow_path(ret != NJS_OK)) { goto fail; } break; case NJS_KEY_FORMAT_RAW: default: if (key->alg->type == NJS_ALGORITHM_ECDSA) { ret = njs_export_raw_ec(vm, key, njs_value_arg(&value)); if (njs_slow_path(ret != NJS_OK)) { goto fail; } break; } ret = njs_vm_value_array_buffer_set(vm, njs_value_arg(&value), key->u.s.raw.start, key->u.s.raw.length); if (njs_slow_path(ret != NJS_OK)) { goto fail; } break; } return njs_webcrypto_result(vm, &value, NJS_OK, retval); fail: return njs_webcrypto_result(vm, NULL, NJS_ERROR, retval); } static njs_int_t njs_ext_generate_key(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int nid; unsigned usage; njs_int_t ret; njs_bool_t extractable; njs_value_t *aobject, *val; EVP_PKEY_CTX *ctx; njs_webcrypto_key_t *key, *keypub; njs_opaque_value_t value, pub, priv; njs_webcrypto_algorithm_t *alg; static const njs_str_t string_priv = njs_str("privateKey"); static const njs_str_t string_pub = njs_str("publicKey"); ctx = NULL; aobject = njs_arg(args, nargs, 1); extractable = njs_value_bool(njs_arg(args, nargs, 2)); alg = njs_key_algorithm(vm, aobject); if (njs_slow_path(alg == NULL)) { goto fail; } ret = njs_key_usage(vm, njs_arg(args, nargs, 3), &usage); if (njs_slow_path(ret != NJS_OK)) { goto fail; } key = njs_webcrypto_key_alloc(vm, alg, usage, extractable); if (njs_slow_path(key == NULL)) { goto fail; } if (njs_slow_path(usage & ~alg->usage)) { njs_vm_type_error(vm, "unsupported key usage for \"%V\" key", njs_algorithm_string(alg)); goto fail; } switch (alg->type) { case NJS_ALGORITHM_RSASSA_PKCS1_v1_5: case NJS_ALGORITHM_RSA_PSS: case NJS_ALGORITHM_RSA_OAEP: ret = njs_algorithm_hash(vm, aobject, &key->hash); if (njs_slow_path(ret == NJS_ERROR)) { goto fail; } val = njs_vm_object_prop(vm, aobject, &string_ml, &value); if (njs_slow_path(val == NULL)) { goto fail; } if (!njs_value_is_number(val)) { njs_vm_type_error(vm, "\"modulusLength\" is not a number"); goto fail; } ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL); if (njs_slow_path(ctx == NULL)) { njs_webcrypto_error(vm, "EVP_PKEY_CTX_new_id() failed"); goto fail; } if (EVP_PKEY_keygen_init(ctx) <= 0) { njs_webcrypto_error(vm, "EVP_PKEY_keygen_init() failed"); goto fail; } if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, njs_value_number(val)) <= 0) { njs_webcrypto_error(vm, "EVP_PKEY_CTX_set_rsa_keygen_bits() " "failed"); goto fail; } if (EVP_PKEY_keygen(ctx, &key->u.a.pkey) <= 0) { njs_webcrypto_error(vm, "EVP_PKEY_keygen() failed"); goto fail; } EVP_PKEY_CTX_free(ctx); ctx = NULL; key->u.a.privat = 1; key->usage = (alg->type == NJS_ALGORITHM_RSA_OAEP) ? NJS_KEY_USAGE_DECRYPT : NJS_KEY_USAGE_SIGN; keypub = njs_webcrypto_key_alloc(vm, alg, usage, extractable); if (njs_slow_path(keypub == NULL)) { goto fail; } if (njs_pkey_up_ref(key->u.a.pkey) <= 0) { njs_webcrypto_error(vm, "njs_pkey_up_ref() failed"); goto fail; } keypub->u.a.pkey = key->u.a.pkey; keypub->hash = key->hash; keypub->usage = (alg->type == NJS_ALGORITHM_RSA_OAEP) ? NJS_KEY_USAGE_ENCRYPT : NJS_KEY_USAGE_VERIFY; ret = njs_vm_external_create(vm, njs_value_arg(&priv), njs_webcrypto_crypto_key_proto_id, key, 0); if (njs_slow_path(ret != NJS_OK)) { goto fail; } ret = njs_vm_external_create(vm, njs_value_arg(&pub), njs_webcrypto_crypto_key_proto_id, keypub, 0); if (njs_slow_path(ret != NJS_OK)) { goto fail; } ret = njs_vm_object_alloc(vm, njs_value_arg(&value), NULL); if (ret != NJS_OK) { goto fail; } ret = njs_vm_object_prop_set(vm, njs_value_arg(&value), &string_priv, &priv); if (ret != NJS_OK) { goto fail; } ret = njs_vm_object_prop_set(vm, njs_value_arg(&value), &string_pub, &pub); if (ret != NJS_OK) { goto fail; } break; case NJS_ALGORITHM_ECDSA: nid = 0; ret = njs_algorithm_curve(vm, aobject, &nid); if (njs_slow_path(ret == NJS_ERROR)) { goto fail; } ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL); if (njs_slow_path(ctx == NULL)) { njs_webcrypto_error(vm, "EVP_PKEY_CTX_new_id() failed"); goto fail; } if (EVP_PKEY_keygen_init(ctx) <= 0) { njs_webcrypto_error(vm, "EVP_PKEY_keygen_init() failed"); goto fail; } if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, nid) <= 0) { njs_webcrypto_error(vm, "EVP_PKEY_CTX_set_ec_paramgen_curve_nid() " "failed"); goto fail; } if (EVP_PKEY_keygen(ctx, &key->u.a.pkey) <= 0) { njs_webcrypto_error(vm, "EVP_PKEY_keygen() failed"); goto fail; } EVP_PKEY_CTX_free(ctx); ctx = NULL; key->u.a.privat = 1; key->usage = NJS_KEY_USAGE_SIGN; keypub = njs_webcrypto_key_alloc(vm, alg, usage, extractable); if (njs_slow_path(keypub == NULL)) { goto fail; } if (njs_pkey_up_ref(key->u.a.pkey) <= 0) { njs_webcrypto_error(vm, "njs_pkey_up_ref() failed"); goto fail; } keypub->u.a.pkey = key->u.a.pkey; keypub->u.a.curve = key->u.a.curve; keypub->usage = NJS_KEY_USAGE_VERIFY; ret = njs_vm_external_create(vm, njs_value_arg(&priv), njs_webcrypto_crypto_key_proto_id, key, 0); if (njs_slow_path(ret != NJS_OK)) { goto fail; } ret = njs_vm_external_create(vm, njs_value_arg(&pub), njs_webcrypto_crypto_key_proto_id, keypub, 0); if (njs_slow_path(ret != NJS_OK)) { goto fail; } ret = njs_vm_object_alloc(vm, njs_value_arg(&value), NULL); if (ret != NJS_OK) { goto fail; } ret = njs_vm_object_prop_set(vm, njs_value_arg(&value), &string_priv, &priv); if (ret != NJS_OK) { goto fail; } ret = njs_vm_object_prop_set(vm, njs_value_arg(&value), &string_pub, &pub); if (ret != NJS_OK) { goto fail; } break; case NJS_ALGORITHM_AES_GCM: case NJS_ALGORITHM_AES_CTR: case NJS_ALGORITHM_AES_CBC: case NJS_ALGORITHM_HMAC: if (alg->type == NJS_ALGORITHM_HMAC) { ret = njs_algorithm_hash(vm, aobject, &key->hash); if (njs_slow_path(ret == NJS_ERROR)) { goto fail; } key->u.s.raw.length = EVP_MD_size(njs_algorithm_hash_digest(key->hash)); } else { val = njs_vm_object_prop(vm, aobject, &string_length, &value); if (val != NULL) { key->u.s.raw.length = njs_value_number(val) / 8; if (key->u.s.raw.length != 16 && key->u.s.raw.length != 24 && key->u.s.raw.length != 32) { njs_vm_type_error(vm, "length for \"%V\" key should be " "one of 128, 192, 256", njs_algorithm_string(alg)); goto fail; } } } key->u.s.raw.start = njs_mp_alloc(njs_vm_memory_pool(vm), key->u.s.raw.length); if (njs_slow_path(key->u.s.raw.start == NULL)) { njs_vm_memory_error(vm); goto fail; } if (RAND_bytes(key->u.s.raw.start, key->u.s.raw.length) <= 0) { njs_webcrypto_error(vm, "RAND_bytes() failed"); goto fail; } ret = njs_vm_external_create(vm, njs_value_arg(&value), njs_webcrypto_crypto_key_proto_id, key, 0); if (njs_slow_path(ret != NJS_OK)) { goto fail; } break; default: njs_vm_internal_error(vm, "not implemented generateKey" "algorithm: \"%V\"", njs_algorithm_string(alg)); return NJS_ERROR; } return njs_webcrypto_result(vm, &value, NJS_OK, retval); fail: if (ctx != NULL) { EVP_PKEY_CTX_free(ctx); } return njs_webcrypto_result(vm, NULL, NJS_ERROR, retval); } static BIGNUM * njs_import_base64url_bignum(njs_vm_t *vm, njs_opaque_value_t *value) { njs_int_t ret; njs_str_t data, decoded; u_char buf[512]; ret = njs_vm_value_to_bytes(vm, &data, njs_value_arg(value)); if (njs_slow_path(ret != NJS_OK)) { return NULL; } (void) njs_decode_base64url_length(&data, &decoded.length); if (njs_slow_path(decoded.length > sizeof(buf))) { return NULL; } decoded.start = buf; njs_decode_base64url(&decoded, &data); return BN_bin2bn(decoded.start, decoded.length, NULL); } static EVP_PKEY * njs_import_jwk_rsa(njs_vm_t *vm, njs_value_t *jwk, njs_webcrypto_key_t *key) { RSA *rsa; BIGNUM *n_bn, *e_bn, *d_bn, *p_bn, *q_bn, *dp_bn, *dq_bn, *qi_bn; njs_str_t alg; unsigned usage; EVP_PKEY *pkey; njs_int_t ret; njs_value_t *val; njs_opaque_value_t n, e, d, p, q, dp, dq, qi, value; njs_webcrypto_entry_t *w; val = njs_vm_object_prop(vm, jwk, &string_n, &n); if (njs_slow_path(val == NULL)) { goto fail0; } val = njs_vm_object_prop(vm, jwk, &string_e, &e); if (njs_slow_path(val == NULL)) { goto fail0; } val = njs_vm_object_prop(vm, jwk, &string_d, &d); if (njs_slow_path(val == NULL)) { njs_value_undefined_set(njs_value_arg(&d)); } if (!njs_value_is_string(njs_value_arg(&n)) || !njs_value_is_string(njs_value_arg(&e)) || (!njs_value_is_undefined(njs_value_arg(&d)) && !njs_value_is_string(njs_value_arg(&d)))) { fail0: njs_vm_type_error(vm, "Invalid JWK RSA key"); return NULL; } key->u.a.privat = njs_value_is_string(njs_value_arg(&d)); val = njs_vm_object_prop(vm, jwk, &key_ops, &value); if (val != NULL && !njs_value_is_undefined(val)){ ret = njs_key_usage(vm, val, &usage); if (njs_slow_path(ret != NJS_OK)) { return NULL; } if ((key->usage & usage) != key->usage) { njs_vm_type_error(vm, "Key operations and usage mismatch"); return NULL; } } val = njs_vm_object_prop(vm, jwk, &string_alg, &value); if (val != NULL && !njs_value_is_undefined(val)){ ret = njs_value_to_string(vm, val, val); if (njs_slow_path(ret != NJS_OK)) { return NULL; } njs_value_string_get(val, &alg); for (w = &njs_webcrypto_alg_hash[0]; w->name.length != 0; w++) { if (njs_strstr_eq(&alg, &w->name)) { key->hash = w->value; break; } } } if (key->extractable) { val = njs_vm_object_prop(vm, jwk, &string_ext, &value); if (val != NULL && !njs_value_is_undefined(val) && !njs_value_bool(val)) { njs_vm_type_error(vm, "JWK RSA is not extractable"); return NULL; } } rsa = RSA_new(); if (rsa == NULL) { njs_webcrypto_error(vm, "RSA_new() failed"); return NULL; } n_bn = njs_import_base64url_bignum(vm, &n); if (njs_slow_path(n_bn == NULL)) { goto fail; } e_bn = njs_import_base64url_bignum(vm, &e); if (njs_slow_path(e_bn == NULL)) { goto fail; } if (!njs_rsa_set0_key(rsa, n_bn, e_bn, NULL)) { BN_free(n_bn); BN_free(e_bn); njs_webcrypto_error(vm, "RSA_set0_key() failed"); goto fail; } if (!key->u.a.privat) { goto done; } val = njs_vm_object_prop(vm, jwk, &string_p, &p); if (njs_slow_path(val == NULL)) { goto fail1; } val = njs_vm_object_prop(vm, jwk, &string_q, &q); if (njs_slow_path(val == NULL)) { goto fail1; } val = njs_vm_object_prop(vm, jwk, &string_dp, &dp); if (njs_slow_path(val == NULL)) { goto fail1; } val = njs_vm_object_prop(vm, jwk, &string_dq, &dq); if (njs_slow_path(val == NULL)) { goto fail1; } val = njs_vm_object_prop(vm, jwk, &string_qi, &qi); if (njs_slow_path(val == NULL)) { goto fail1; } if (!njs_value_is_string(njs_value_arg(&d)) || !njs_value_is_string(njs_value_arg(&p)) || !njs_value_is_string(njs_value_arg(&q)) || !njs_value_is_string(njs_value_arg(&dp)) || !njs_value_is_string(njs_value_arg(&dq)) || !njs_value_is_string(njs_value_arg(&qi))) { fail1: njs_vm_type_error(vm, "Invalid JWK RSA key"); goto fail; } d_bn = njs_import_base64url_bignum(vm, &d); if (njs_slow_path(d_bn == NULL)) { goto fail; } if (!njs_rsa_set0_key(rsa, NULL, NULL, d_bn)) { BN_free(d_bn); njs_webcrypto_error(vm, "RSA_set0_key() failed"); goto fail; } p_bn = njs_import_base64url_bignum(vm, &p); if (njs_slow_path(p_bn == NULL)) { goto fail; } q_bn = njs_import_base64url_bignum(vm, &q); if (njs_slow_path(q_bn == NULL)) { BN_free(p_bn); goto fail; } if (!njs_rsa_set0_factors(rsa, p_bn, q_bn)) { BN_free(p_bn); BN_free(q_bn); njs_webcrypto_error(vm, "RSA_set0_factors() failed"); goto fail; } dp_bn = njs_import_base64url_bignum(vm, &dp); if (njs_slow_path(dp_bn == NULL)) { goto fail; } dq_bn = njs_import_base64url_bignum(vm, &dq); if (njs_slow_path(dq_bn == NULL)) { BN_free(dp_bn); goto fail; } qi_bn = njs_import_base64url_bignum(vm, &qi); if (njs_slow_path(qi_bn == NULL)) { BN_free(dp_bn); BN_free(dq_bn); goto fail; } if (!njs_rsa_set0_ctr_params(rsa, dp_bn, dq_bn, qi_bn)) { BN_free(dp_bn); BN_free(dq_bn); BN_free(qi_bn); njs_webcrypto_error(vm, "RSA_set0_crt_params() failed"); goto fail; } done: pkey = EVP_PKEY_new(); if (njs_slow_path(pkey == NULL)) { goto fail; } if (!EVP_PKEY_set1_RSA(pkey, rsa)) { EVP_PKEY_free(pkey); goto fail; } RSA_free(rsa); return pkey; fail: RSA_free(rsa); return NULL; } static EVP_PKEY * njs_import_raw_ec(njs_vm_t *vm, njs_str_t *data, njs_webcrypto_key_t *key) { EC_KEY *ec; EVP_PKEY *pkey; EC_POINT *pub; const EC_GROUP *group; ec = EC_KEY_new_by_curve_name(key->u.a.curve); if (njs_slow_path(ec == NULL)) { njs_webcrypto_error(vm, "EC_KEY_new_by_curve_name() failed"); return NULL; } group = EC_KEY_get0_group(ec); pub = EC_POINT_new(group); if (njs_slow_path(pub == NULL)) { EC_KEY_free(ec); njs_webcrypto_error(vm, "EC_POINT_new() failed"); return NULL; } if (!EC_POINT_oct2point(group, pub, data->start, data->length, NULL)) { EC_KEY_free(ec); EC_POINT_free(pub); njs_webcrypto_error(vm, "EC_POINT_oct2point() failed"); return NULL; } if (!EC_KEY_set_public_key(ec, pub)) { EC_KEY_free(ec); EC_POINT_free(pub); njs_webcrypto_error(vm, "EC_KEY_set_public_key() failed"); return NULL; } pkey = EVP_PKEY_new(); if (njs_slow_path(pkey == NULL)) { EC_KEY_free(ec); EC_POINT_free(pub); njs_webcrypto_error(vm, "EVP_PKEY_new() failed"); return NULL; } if (!EVP_PKEY_set1_EC_KEY(pkey, ec)) { EC_KEY_free(ec); EC_POINT_free(pub); EVP_PKEY_free(pkey); njs_webcrypto_error(vm, "EVP_PKEY_set1_EC_KEY() failed"); return NULL; } EC_KEY_free(ec); EC_POINT_free(pub); return pkey; } static EVP_PKEY * njs_import_jwk_ec(njs_vm_t *vm, njs_value_t *jwk, njs_webcrypto_key_t *key) { int curve; EC_KEY *ec; BIGNUM *x_bn, *y_bn, *d_bn; unsigned usage; EVP_PKEY *pkey; njs_str_t name; njs_int_t ret; njs_value_t *val; njs_opaque_value_t x, y, d, value; njs_webcrypto_entry_t *e; ec = NULL; x_bn = NULL; y_bn = NULL; d_bn = NULL; val = njs_vm_object_prop(vm, jwk, &string_x, &x); if (njs_slow_path(val == NULL)) { goto fail0; } val = njs_vm_object_prop(vm, jwk, &string_y, &y); if (njs_slow_path(val == NULL)) { goto fail0; } val = njs_vm_object_prop(vm, jwk, &string_d, &d); if (njs_slow_path(val == NULL)) { njs_value_undefined_set(njs_value_arg(&d)); } if (!njs_value_is_string(njs_value_arg(&x)) || !njs_value_is_string(njs_value_arg(&y)) || (!njs_value_is_undefined(njs_value_arg(&d)) && !njs_value_is_string(njs_value_arg(&d)))) { fail0: njs_vm_type_error(vm, "Invalid JWK EC key"); return NULL; } key->u.a.privat = njs_value_is_string(njs_value_arg(&d)); val = njs_vm_object_prop(vm, jwk, &key_ops, &value); if (val != NULL && !njs_value_is_undefined(val)) { ret = njs_key_usage(vm, val, &usage); if (njs_slow_path(ret != NJS_OK)) { return NULL; } if ((key->usage & usage) != key->usage) { njs_vm_type_error(vm, "Key operations and usage mismatch"); return NULL; } } if (key->extractable) { val = njs_vm_object_prop(vm, jwk, &string_ext, &value); if (val != NULL && !njs_value_is_undefined(val) && !njs_value_bool(val)) { njs_vm_type_error(vm, "JWK EC is not extractable"); return NULL; } } curve = 0; val = njs_vm_object_prop(vm, jwk, &string_crv, &value); if (val != NULL && !njs_value_is_undefined(val)) { njs_value_string_get(val, &name); for (e = &njs_webcrypto_curve[0]; e->name.length != 0; e++) { if (njs_strstr_eq(&name, &e->name)) { curve = e->value; break; } } } if (curve != key->u.a.curve) { njs_vm_type_error(vm, "JWK EC curve mismatch"); return NULL; } ec = EC_KEY_new_by_curve_name(key->u.a.curve); if (njs_slow_path(ec == NULL)) { njs_webcrypto_error(vm, "EC_KEY_new_by_curve_name() failed"); return NULL; } x_bn = njs_import_base64url_bignum(vm, &x); if (njs_slow_path(x_bn == NULL)) { goto fail; } y_bn = njs_import_base64url_bignum(vm, &y); if (njs_slow_path(y_bn == NULL)) { goto fail; } if (key->u.a.privat) { d_bn = njs_import_base64url_bignum(vm, &d); if (njs_slow_path(d_bn == NULL)) { goto fail; } } if (!EC_KEY_set_public_key_affine_coordinates(ec, x_bn, y_bn)) { njs_webcrypto_error(vm, "EC_KEY_set_public_key_affine_coordinates() " "failed"); goto fail; } BN_free(x_bn); x_bn = NULL; BN_free(y_bn); y_bn = NULL; pkey = EVP_PKEY_new(); if (njs_slow_path(pkey == NULL)) { goto fail; } if (!EVP_PKEY_set1_EC_KEY(pkey, ec)) { njs_webcrypto_error(vm, "EVP_PKEY_set1_EC_KEY() failed"); goto fail_pkey; } if (key->u.a.privat) { if (!EC_KEY_set_private_key(ec, d_bn)) { njs_webcrypto_error(vm, "EC_KEY_set_private_key() failed"); goto fail_pkey; } BN_free(d_bn); d_bn = NULL; } EC_KEY_free(ec); return pkey; fail_pkey: EVP_PKEY_free(pkey); EC_KEY_free(ec); ec = NULL; fail: EC_KEY_free(ec); if (x_bn != NULL) { BN_free(x_bn); } if (y_bn != NULL) { BN_free(y_bn); } if (d_bn != NULL) { BN_free(d_bn); } return NULL; } static njs_int_t njs_import_jwk_oct(njs_vm_t *vm, njs_value_t *jwk, njs_webcrypto_key_t *key) { size_t size; unsigned usage; njs_int_t ret; njs_str_t *a, alg, b64; njs_value_t *val; njs_opaque_value_t value; njs_webcrypto_alg_t type; njs_webcrypto_entry_t *w; static njs_webcrypto_entry_t hashes[] = { { njs_str("HS1"), NJS_HASH_SHA1 }, { njs_str("HS256"), NJS_HASH_SHA256 }, { njs_str("HS384"), NJS_HASH_SHA384 }, { njs_str("HS512"), NJS_HASH_SHA512 }, { njs_null_str, 0 } }; val = njs_vm_object_prop(vm, jwk, &string_k, &value); if (njs_slow_path(val == NULL || !njs_value_is_string(val))) { njs_vm_type_error(vm, "Invalid JWK oct key"); return NJS_ERROR; } njs_value_string_get(val, &b64); (void) njs_decode_base64url_length(&b64, &key->u.s.raw.length); key->u.s.raw.start = njs_mp_alloc(njs_vm_memory_pool(vm), key->u.s.raw.length); if (njs_slow_path(key->u.s.raw.start == NULL)) { njs_vm_memory_error(vm); return NJS_ERROR; } njs_decode_base64url(&key->u.s.raw, &b64); size = 16; val = njs_vm_object_prop(vm, jwk, &string_alg, &value); if (val != NULL && njs_value_is_string(val)) { njs_value_string_get(val, &alg); if (key->alg->type == NJS_ALGORITHM_HMAC) { for (w = &hashes[0]; w->name.length != 0; w++) { if (njs_strstr_eq(&alg, &w->name)) { key->hash = w->value; goto done; } } } else { type = key->alg->type; a = &njs_webcrypto_alg_aes_name[type - NJS_ALGORITHM_AES_GCM][0]; for (; a->length != 0; a++) { if (njs_strstr_eq(&alg, a)) { goto done; } size += 8; } } njs_vm_type_error(vm, "unexpected \"alg\" value \"%V\" for JWK key", &alg); return NJS_ERROR; } done: if (key->alg->type != NJS_ALGORITHM_HMAC) { if (key->u.s.raw.length != size) { njs_vm_type_error(vm, "key size and \"alg\" value \"%V\" mismatch", &alg); return NJS_ERROR; } } val = njs_vm_object_prop(vm, jwk, &key_ops, &value); if (val != NULL && !njs_value_is_undefined(val)) { ret = njs_key_usage(vm, val, &usage); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } if ((key->usage & usage) != key->usage) { njs_vm_type_error(vm, "Key operations and usage mismatch"); return NJS_ERROR; } } if (key->extractable) { val = njs_vm_object_prop(vm, jwk, &string_ext, &value); if (val != NULL && !njs_value_is_undefined(val) && !njs_value_bool(val)) { njs_vm_type_error(vm, "JWK oct is not extractable"); return NJS_ERROR; } } return NJS_OK; } static njs_int_t njs_ext_import_key(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int nid; BIO *bio; #if (OPENSSL_VERSION_NUMBER < 0x30000000L) RSA *rsa; EC_KEY *ec; #else char gname[80]; #endif unsigned mask, usage; EVP_PKEY *pkey; njs_int_t ret; njs_str_t key_data, kty; njs_value_t *options, *jwk, *val; const u_char *start; #if (OPENSSL_VERSION_NUMBER < 0x30000000L) const EC_GROUP *group; #endif njs_webcrypto_key_t *key; PKCS8_PRIV_KEY_INFO *pkcs8; njs_opaque_value_t value; njs_webcrypto_hash_t hash; njs_webcrypto_algorithm_t *alg; njs_webcrypto_key_format_t fmt; pkey = NULL; key_data.start = NULL; key_data.length = 0; fmt = njs_key_format(vm, njs_arg(args, nargs, 1)); if (njs_slow_path(fmt == NJS_KEY_FORMAT_UNKNOWN)) { goto fail; } options = njs_arg(args, nargs, 3); alg = njs_key_algorithm(vm, options); if (njs_slow_path(alg == NULL)) { goto fail; } if (njs_slow_path(!(fmt & alg->fmt))) { njs_vm_type_error(vm, "unsupported key fmt \"%V\" for \"%V\" key", njs_format_string(fmt), njs_algorithm_string(alg)); goto fail; } ret = njs_key_usage(vm, njs_arg(args, nargs, 5), &usage); if (njs_slow_path(ret != NJS_OK)) { goto fail; } if (njs_slow_path(usage & ~alg->usage)) { njs_vm_type_error(vm, "unsupported key usage for \"%V\" key", njs_algorithm_string(alg)); goto fail; } if (fmt != NJS_KEY_FORMAT_JWK) { ret = njs_vm_value_to_bytes(vm, &key_data, njs_arg(args, nargs, 2)); if (njs_slow_path(ret != NJS_OK)) { goto fail; } } key = njs_webcrypto_key_alloc(vm, alg, usage, njs_value_bool(njs_arg(args, nargs, 4))); if (njs_slow_path(key == NULL)) { goto fail; } /* * set by njs_webcrypto_key_alloc(): * * key->u.a.pkey = NULL; * key->u.s.raw.length = 0; * key->u.s.raw.start = NULL; * key->u.a.curve = 0; * key->u.a.privat = 0; * key->hash = NJS_HASH_UNSET; */ switch (fmt) { case NJS_KEY_FORMAT_PKCS8: bio = njs_bio_new_mem_buf(key_data.start, key_data.length); if (njs_slow_path(bio == NULL)) { njs_webcrypto_error(vm, "BIO_new_mem_buf() failed"); goto fail; } pkcs8 = d2i_PKCS8_PRIV_KEY_INFO_bio(bio, NULL); if (njs_slow_path(pkcs8 == NULL)) { BIO_free(bio); njs_webcrypto_error(vm, "d2i_PKCS8_PRIV_KEY_INFO_bio() failed"); goto fail; } pkey = EVP_PKCS82PKEY(pkcs8); if (njs_slow_path(pkey == NULL)) { PKCS8_PRIV_KEY_INFO_free(pkcs8); BIO_free(bio); njs_webcrypto_error(vm, "EVP_PKCS82PKEY() failed"); goto fail; } PKCS8_PRIV_KEY_INFO_free(pkcs8); BIO_free(bio); key->u.a.privat = 1; break; case NJS_KEY_FORMAT_SPKI: start = key_data.start; pkey = d2i_PUBKEY(NULL, &start, key_data.length); if (njs_slow_path(pkey == NULL)) { njs_webcrypto_error(vm, "d2i_PUBKEY() failed"); goto fail; } break; case NJS_KEY_FORMAT_JWK: jwk = njs_arg(args, nargs, 2); if (!njs_value_is_object(jwk)) { njs_vm_type_error(vm, "invalid JWK key data: object value " "expected"); goto fail; } val = njs_vm_object_prop(vm, jwk, &string_kty, &value); if (njs_slow_path(val == NULL)) { val = njs_value_arg(&njs_value_undefined); } ret = njs_vm_value_to_bytes(vm, &kty, val); if (njs_slow_path(ret != NJS_OK)) { goto fail; } if (njs_strstr_eq(&kty, &njs_str_value("RSA"))) { pkey = njs_import_jwk_rsa(vm, jwk, key); if (njs_slow_path(pkey == NULL)) { goto fail; } } else if (njs_strstr_eq(&kty, &njs_str_value("EC"))) { ret = njs_algorithm_curve(vm, options, &key->u.a.curve); if (njs_slow_path(ret == NJS_ERROR)) { goto fail; } pkey = njs_import_jwk_ec(vm, jwk, key); if (njs_slow_path(pkey == NULL)) { goto fail; } } else if (njs_strstr_eq(&kty, &njs_str_value("oct"))) { ret = njs_import_jwk_oct(vm, jwk, key); if (njs_slow_path(ret != NJS_OK)) { goto fail; } } else { njs_vm_type_error(vm, "invalid JWK key type: %V", &kty); goto fail; } break; case NJS_KEY_FORMAT_RAW: default: break; } switch (alg->type) { case NJS_ALGORITHM_RSA_OAEP: case NJS_ALGORITHM_RSA_PSS: case NJS_ALGORITHM_RSASSA_PKCS1_v1_5: #if (OPENSSL_VERSION_NUMBER < 0x30000000L) rsa = EVP_PKEY_get1_RSA(pkey); if (njs_slow_path(rsa == NULL)) { njs_webcrypto_error(vm, "RSA key is not found"); goto fail; } RSA_free(rsa); #else if (!EVP_PKEY_is_a(pkey, "RSA")) { njs_webcrypto_error(vm, "RSA key is not found"); goto fail; } #endif ret = njs_algorithm_hash(vm, options, &hash); if (njs_slow_path(ret == NJS_ERROR)) { goto fail; } if (key->hash != NJS_HASH_UNSET && key->hash != hash) { njs_vm_type_error(vm, "RSA JWK hash mismatch"); goto fail; } if (key->u.a.privat) { mask = (alg->type == NJS_ALGORITHM_RSA_OAEP) ? ~(NJS_KEY_USAGE_DECRYPT | NJS_KEY_USAGE_UNWRAP_KEY) : ~(NJS_KEY_USAGE_SIGN); } else { mask = (alg->type == NJS_ALGORITHM_RSA_OAEP) ? ~(NJS_KEY_USAGE_ENCRYPT | NJS_KEY_USAGE_WRAP_KEY) : ~(NJS_KEY_USAGE_VERIFY); } if (key->usage & mask) { njs_vm_type_error(vm, "key usage mismatch for \"%V\" key", njs_algorithm_string(alg)); goto fail; } key->hash = hash; key->u.a.pkey = pkey; break; case NJS_ALGORITHM_ECDSA: case NJS_ALGORITHM_ECDH: ret = njs_algorithm_curve(vm, options, &key->u.a.curve); if (njs_slow_path(ret == NJS_ERROR)) { goto fail; } if (fmt == NJS_KEY_FORMAT_RAW) { pkey = njs_import_raw_ec(vm, &key_data, key); if (njs_slow_path(pkey == NULL)) { goto fail; } } #if (OPENSSL_VERSION_NUMBER < 0x30000000L) ec = EVP_PKEY_get1_EC_KEY(pkey); if (njs_slow_path(ec == NULL)) { njs_webcrypto_error(vm, "EC key is not found"); goto fail; } group = EC_KEY_get0_group(ec); nid = EC_GROUP_get_curve_name(group); EC_KEY_free(ec); #else if (!EVP_PKEY_is_a(pkey, "EC")) { njs_webcrypto_error(vm, "EC key is not found"); goto fail; } if (EVP_PKEY_get_group_name(pkey, gname, sizeof(gname), NULL) != 1) { njs_webcrypto_error(vm, "EVP_PKEY_get_group_name() failed"); goto fail; } nid = OBJ_txt2nid(gname); #endif if (njs_slow_path(key->u.a.curve != nid)) { njs_webcrypto_error(vm, "name curve mismatch"); goto fail; } mask = key->u.a.privat ? ~NJS_KEY_USAGE_SIGN : ~NJS_KEY_USAGE_VERIFY; if (key->usage & mask) { njs_vm_type_error(vm, "key usage mismatch for \"%V\" key", njs_algorithm_string(alg)); goto fail; } key->u.a.pkey = pkey; break; case NJS_ALGORITHM_HMAC: if (fmt == NJS_KEY_FORMAT_RAW) { ret = njs_algorithm_hash(vm, options, &key->hash); if (njs_slow_path(ret == NJS_ERROR)) { goto fail; } key->u.s.raw = key_data; } else { /* NJS_KEY_FORMAT_JWK. */ ret = njs_algorithm_hash(vm, options, &hash); if (njs_slow_path(ret == NJS_ERROR)) { goto fail; } if (key->hash != NJS_HASH_UNSET && key->hash != hash) { njs_vm_type_error(vm, "HMAC JWK hash mismatch"); goto fail; } } break; case NJS_ALGORITHM_AES_GCM: case NJS_ALGORITHM_AES_CTR: case NJS_ALGORITHM_AES_CBC: if (fmt == NJS_KEY_FORMAT_RAW) { switch (key_data.length) { case 16: case 24: case 32: break; default: njs_vm_type_error(vm, "AES Invalid key length"); goto fail; } key->u.s.raw = key_data; } break; case NJS_ALGORITHM_PBKDF2: case NJS_ALGORITHM_HKDF: default: key->u.s.raw = key_data; break; } ret = njs_vm_external_create(vm, njs_value_arg(&value), njs_webcrypto_crypto_key_proto_id, key, 0); if (njs_slow_path(ret != NJS_OK)) { goto fail; } return njs_webcrypto_result(vm, &value, NJS_OK, retval); fail: if (pkey != NULL) { EVP_PKEY_free(pkey); } return njs_webcrypto_result(vm, NULL, NJS_ERROR, retval); } static njs_int_t njs_set_rsa_padding(njs_vm_t *vm, njs_value_t *options, EVP_PKEY *pkey, EVP_PKEY_CTX *ctx, njs_webcrypto_alg_t type) { int padding; int64_t salt_length; njs_int_t ret; njs_value_t *value; njs_opaque_value_t lvalue; static const njs_str_t string_saltl = njs_str("saltLength"); if (type == NJS_ALGORITHM_ECDSA) { return NJS_OK; } padding = (type == NJS_ALGORITHM_RSA_PSS) ? RSA_PKCS1_PSS_PADDING : RSA_PKCS1_PADDING; ret = EVP_PKEY_CTX_set_rsa_padding(ctx, padding); if (njs_slow_path(ret <= 0)) { njs_webcrypto_error(vm, "EVP_PKEY_CTX_set_rsa_padding() failed"); return NJS_ERROR; } if (padding == RSA_PKCS1_PSS_PADDING) { value = njs_vm_object_prop(vm, options, &string_saltl, &lvalue); if (njs_slow_path(value == NULL)) { njs_vm_type_error(vm, "RSA-PSS algorithm.saltLength is not " "provided"); return NJS_ERROR; } ret = njs_value_to_integer(vm, value, &salt_length); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, salt_length); if (njs_slow_path(ret <= 0)) { njs_webcrypto_error(vm, "EVP_PKEY_CTX_set_rsa_pss_saltlen() failed"); return NJS_ERROR; } } return NJS_OK; } static unsigned int njs_ec_rs_size(EVP_PKEY *pkey) { int bits; const EC_KEY *ec_key; const EC_GROUP *ec_group; ec_key = njs_pkey_get_ec_key(pkey); if (ec_key == NULL) { return 0; } ec_group = EC_KEY_get0_group(ec_key); if (ec_group == NULL) { return 0; } bits = njs_ec_group_order_bits(ec_group); if (bits == 0) { return 0; } return (bits + 7) / 8; } static njs_int_t njs_convert_der_to_p1363(njs_vm_t *vm, EVP_PKEY *pkey, const u_char *der, size_t der_len, u_char **pout, size_t *out_len) { u_char *data; unsigned n; njs_int_t ret; ECDSA_SIG *ec_sig; const BIGNUM *r, *s; ret = NJS_OK; ec_sig = NULL; n = njs_ec_rs_size(pkey); if (n == 0) { goto fail; } data = njs_mp_alloc(njs_vm_memory_pool(vm), 2 * n); if (njs_slow_path(data == NULL)) { goto memory_error; } ec_sig = d2i_ECDSA_SIG(NULL, &der, der_len); if (ec_sig == NULL) { goto fail; } #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) ECDSA_SIG_get0(ec_sig, &r, &s); #else r = ec_sig->r; s = ec_sig->s; #endif if (njs_bn_bn2binpad(r, data, n) <= 0) { goto fail; } if (njs_bn_bn2binpad(s, &data[n], n) <= 0) { goto fail; } *pout = data; *out_len = 2 * n; goto done; fail: *out_len = 0; done: if (ec_sig != NULL) { ECDSA_SIG_free(ec_sig); } return ret; memory_error: njs_vm_memory_error(vm); return NJS_ERROR; } static njs_int_t njs_convert_p1363_to_der(njs_vm_t *vm, EVP_PKEY *pkey, u_char *p1363, size_t p1363_len, u_char **pout, size_t *out_len) { int len; BIGNUM *r, *s; u_char *data; unsigned n; njs_int_t ret; ECDSA_SIG *ec_sig; ret = NJS_OK; ec_sig = NULL; n = njs_ec_rs_size(pkey); if (njs_slow_path(n == 0 || p1363_len != 2 * n)) { goto fail; } ec_sig = ECDSA_SIG_new(); if (njs_slow_path(ec_sig == NULL)) { goto memory_error; } r = BN_new(); if (njs_slow_path(r == NULL)) { goto memory_error; } s = BN_new(); if (njs_slow_path(s == NULL)) { goto memory_error; } if (r != BN_bin2bn(p1363, n, r)) { goto fail; } if (s != BN_bin2bn(&p1363[n], n, s)) { goto fail; } if (njs_ecdsa_sig_set0(ec_sig, r, s) != 1) { njs_webcrypto_error(vm, "njs_ecdsa_sig_set0() failed"); ret = NJS_ERROR; goto fail; } data = njs_mp_alloc(njs_vm_memory_pool(vm), 2 * n + 16); if (njs_slow_path(data == NULL)) { goto memory_error; } *pout = data; len = i2d_ECDSA_SIG(ec_sig, &data); if (len < 0) { goto fail; } *out_len = len; goto done; fail: *out_len = 0; done: if (ec_sig != NULL) { ECDSA_SIG_free(ec_sig); } return ret; memory_error: njs_vm_memory_error(vm); return NJS_ERROR; } static njs_int_t njs_ext_sign(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t verify, njs_value_t *retval) { u_char *dst, *p; size_t olen, outlen; unsigned mask, m_len; njs_int_t ret; njs_str_t data, sig; EVP_MD_CTX *mctx; njs_value_t *options; EVP_PKEY_CTX *pctx; const EVP_MD *md; njs_opaque_value_t result; njs_webcrypto_key_t *key; njs_webcrypto_hash_t hash; njs_webcrypto_algorithm_t *alg; unsigned char m[EVP_MAX_MD_SIZE]; mctx = NULL; pctx = NULL; options = njs_arg(args, nargs, 1); alg = njs_key_algorithm(vm, options); if (njs_slow_path(alg == NULL)) { goto fail; } key = njs_vm_external(vm, njs_webcrypto_crypto_key_proto_id, njs_arg(args, nargs, 2)); if (njs_slow_path(key == NULL)) { njs_vm_type_error(vm, "\"key\" is not a CryptoKey object"); goto fail; } mask = verify ? NJS_KEY_USAGE_VERIFY : NJS_KEY_USAGE_SIGN; if (njs_slow_path(!(key->usage & mask))) { njs_vm_type_error(vm, "provide key does not support \"sign\" " "operation"); goto fail; } if (njs_slow_path(key->alg != alg)) { njs_vm_type_error(vm, "cannot %s using \"%V\" with \"%V\" key", verify ? "verify" : "sign", njs_algorithm_string(key->alg), njs_algorithm_string(alg)); goto fail; } if (verify) { ret = njs_vm_value_to_bytes(vm, &sig, njs_arg(args, nargs, 3)); if (njs_slow_path(ret != NJS_OK)) { goto fail; } ret = njs_vm_value_to_bytes(vm, &data, njs_arg(args, nargs, 4)); if (njs_slow_path(ret != NJS_OK)) { goto fail; } } else { ret = njs_vm_value_to_bytes(vm, &data, njs_arg(args, nargs, 3)); if (njs_slow_path(ret != NJS_OK)) { goto fail; } } if (alg->type == NJS_ALGORITHM_ECDSA) { ret = njs_algorithm_hash(vm, options, &hash); if (njs_slow_path(ret == NJS_ERROR)) { goto fail; } } else { hash = key->hash; } md = njs_algorithm_hash_digest(hash); outlen = 0; switch (alg->type) { case NJS_ALGORITHM_HMAC: m_len = EVP_MD_size(md); if (!verify) { dst = njs_mp_alloc(njs_vm_memory_pool(vm), m_len); if (njs_slow_path(dst == NULL)) { njs_vm_memory_error(vm); goto fail; } } else { dst = (u_char *) &m[0]; } outlen = m_len; p = HMAC(md, key->u.s.raw.start, key->u.s.raw.length, data.start, data.length, dst, &m_len); if (njs_slow_path(p == NULL || m_len != outlen)) { njs_webcrypto_error(vm, "HMAC() failed"); goto fail; } if (verify) { ret = (sig.length == outlen && memcmp(sig.start, dst, outlen) == 0); } break; case NJS_ALGORITHM_RSASSA_PKCS1_v1_5: case NJS_ALGORITHM_RSA_PSS: case NJS_ALGORITHM_ECDSA: default: mctx = njs_evp_md_ctx_new(); if (njs_slow_path(mctx == NULL)) { njs_webcrypto_error(vm, "njs_evp_md_ctx_new() failed"); goto fail; } ret = EVP_DigestInit_ex(mctx, md, NULL); if (njs_slow_path(ret <= 0)) { njs_webcrypto_error(vm, "EVP_DigestInit_ex() failed"); goto fail; } ret = EVP_DigestUpdate(mctx, data.start, data.length); if (njs_slow_path(ret <= 0)) { njs_webcrypto_error(vm, "EVP_DigestUpdate() failed"); goto fail; } ret = EVP_DigestFinal_ex(mctx, m, &m_len); if (njs_slow_path(ret <= 0)) { njs_webcrypto_error(vm, "EVP_DigestFinal_ex() failed"); goto fail; } olen = EVP_PKEY_size(key->u.a.pkey); dst = njs_mp_zalloc(njs_vm_memory_pool(vm), olen); if (njs_slow_path(dst == NULL)) { njs_vm_memory_error(vm); goto fail; } pctx = EVP_PKEY_CTX_new(key->u.a.pkey, NULL); if (njs_slow_path(pctx == NULL)) { njs_webcrypto_error(vm, "EVP_PKEY_CTX_new() failed"); goto fail; } if (!verify) { ret = EVP_PKEY_sign_init(pctx); if (njs_slow_path(ret <= 0)) { njs_webcrypto_error(vm, "EVP_PKEY_sign_init() failed"); goto fail; } } else { ret = EVP_PKEY_verify_init(pctx); if (njs_slow_path(ret <= 0)) { njs_webcrypto_error(vm, "EVP_PKEY_verify_init() failed"); goto fail; } } ret = njs_set_rsa_padding(vm, options, key->u.a.pkey, pctx, alg->type); if (njs_slow_path(ret != NJS_OK)) { goto fail; } ret = EVP_PKEY_CTX_set_signature_md(pctx, md); if (njs_slow_path(ret <= 0)) { njs_webcrypto_error(vm, "EVP_PKEY_CTX_set_signature_md() failed"); goto fail; } if (!verify) { outlen = olen; ret = EVP_PKEY_sign(pctx, dst, &outlen, m, m_len); if (njs_slow_path(ret <= 0)) { njs_webcrypto_error(vm, "EVP_PKEY_sign() failed"); goto fail; } if (alg->type == NJS_ALGORITHM_ECDSA) { ret = njs_convert_der_to_p1363(vm, key->u.a.pkey, dst, outlen, &dst, &outlen); if (njs_slow_path(ret != NJS_OK)) { goto fail; } } } else { if (alg->type == NJS_ALGORITHM_ECDSA) { ret = njs_convert_p1363_to_der(vm, key->u.a.pkey, sig.start, sig.length, &sig.start, &sig.length); if (njs_slow_path(ret != NJS_OK)) { goto fail; } } ret = EVP_PKEY_verify(pctx, sig.start, sig.length, m, m_len); if (njs_slow_path(ret < 0)) { njs_webcrypto_error(vm, "EVP_PKEY_verify() failed"); goto fail; } } njs_evp_md_ctx_free(mctx); EVP_PKEY_CTX_free(pctx); break; } if (!verify) { ret = njs_vm_value_array_buffer_set(vm, njs_value_arg(&result), dst, outlen); if (njs_slow_path(ret != NJS_OK)) { goto fail; } } else { njs_value_boolean_set(njs_value_arg(&result), ret != 0); } return njs_webcrypto_result(vm, &result, NJS_OK, retval); fail: if (mctx != NULL) { njs_evp_md_ctx_free(mctx); } if (pctx != NULL) { EVP_PKEY_CTX_free(pctx); } return njs_webcrypto_result(vm, NULL, NJS_ERROR, retval); } static njs_int_t njs_ext_unwrap_key(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_vm_internal_error(vm, "\"unwrapKey\" not implemented"); return NJS_ERROR; } static njs_int_t njs_ext_wrap_key(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_vm_internal_error(vm, "\"wrapKey\" not implemented"); return NJS_ERROR; } static njs_int_t njs_key_ext_algorithm(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { u_char *start; njs_int_t ret; njs_str_t *name; const BIGNUM *n_bn, *e_bn; const EC_GROUP *group; njs_opaque_value_t alg, name_s, val, hash; njs_webcrypto_key_t *key; static const njs_str_t string_pexponent = njs_str("publicExponent"); key = njs_vm_external(vm, njs_webcrypto_crypto_key_proto_id, value); if (njs_slow_path(key == NULL)) { njs_value_undefined_set(retval); return NJS_DECLINED; } name = &njs_webcrypto_alg[key->alg->type].name; ret = njs_vm_value_string_create(vm, njs_value_arg(&alg), name->start, name->length); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } (void) njs_vm_value_string_create(vm, njs_value_arg(&name_s), (u_char *) "name", njs_length("name")); ret = njs_vm_object_alloc(vm, retval, &name_s, &alg, NULL); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } switch (key->alg->type) { case NJS_ALGORITHM_RSASSA_PKCS1_v1_5: case NJS_ALGORITHM_RSA_PSS: case NJS_ALGORITHM_RSA_OAEP: /* RsaHashedKeyGenParams */ njs_assert(key->u.a.pkey != NULL); njs_assert(EVP_PKEY_id(key->u.a.pkey) == EVP_PKEY_RSA); njs_rsa_get0_key(njs_pkey_get_rsa_key(key->u.a.pkey), &n_bn, &e_bn, NULL); njs_value_number_set(njs_value_arg(&val), BN_num_bits(n_bn)); ret = njs_vm_object_prop_set(vm, retval, &string_ml, &val); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } start = njs_mp_alloc(njs_vm_memory_pool(vm), BN_num_bytes(e_bn)); if (njs_slow_path(start == NULL)) { njs_vm_memory_error(vm); return NJS_ERROR; } BN_bn2bin(e_bn, start); ret = njs_vm_value_buffer_set(vm, njs_value_arg(&val), start, BN_num_bytes(e_bn)); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_vm_object_prop_set(vm, retval, &string_pexponent, &val); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } name = njs_algorithm_hash_name(key->hash); ret = njs_vm_value_string_create(vm, njs_value_arg(&hash), name->start, name->length); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_vm_object_alloc(vm, njs_value_arg(&val), NULL); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_vm_object_prop_set(vm, njs_value_arg(&val), &string_name, &hash); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_vm_object_prop_set(vm, retval, &string_hash, &val); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } break; case NJS_ALGORITHM_AES_GCM: case NJS_ALGORITHM_AES_CTR: case NJS_ALGORITHM_AES_CBC: /* AesKeyGenParams */ njs_value_number_set(njs_value_arg(&val), key->u.s.raw.length * 8); ret = njs_vm_object_prop_set(vm, retval, &string_length, &val); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } break; case NJS_ALGORITHM_ECDSA: case NJS_ALGORITHM_ECDH: /* EcKeyGenParams */ njs_assert(key->u.a.pkey != NULL); njs_assert(EVP_PKEY_id(key->u.a.pkey) == EVP_PKEY_EC); group = EC_KEY_get0_group(njs_pkey_get_ec_key(key->u.a.pkey)); name = njs_algorithm_curve_name(EC_GROUP_get_curve_name(group)); ret = njs_vm_value_string_create(vm, njs_value_arg(&val), name->start, name->length); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_vm_object_prop_set(vm, retval, &string_curve, &val); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } break; case NJS_ALGORITHM_HMAC: default: /* HmacKeyGenParams */ name = njs_algorithm_hash_name(key->hash); ret = njs_vm_value_string_create(vm, njs_value_arg(&val), name->start, name->length); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_vm_object_prop_set(vm, retval, &string_hash, &val); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } break; } return NJS_OK; } static njs_int_t njs_key_ext_extractable(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { njs_webcrypto_key_t *key; key = njs_vm_external(vm, njs_webcrypto_crypto_key_proto_id, value); if (njs_slow_path(key == NULL)) { njs_value_undefined_set(retval); return NJS_DECLINED; } njs_value_boolean_set(retval, key->extractable); return NJS_OK; } static njs_int_t njs_key_ext_type(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { const char *type; njs_webcrypto_key_t *key; key = njs_vm_external(vm, njs_webcrypto_crypto_key_proto_id, value); if (njs_slow_path(key == NULL)) { njs_value_undefined_set(retval); return NJS_DECLINED; } if (key->alg->raw) { (void) njs_vm_value_string_create(vm, retval, (u_char *) "secret", njs_length("secret")); } else { type = key->u.a.privat ? "private": "public"; (void) njs_vm_value_string_create(vm, retval, (u_char *) type, key->u.a.privat ? 7 : 6); } return NJS_OK; } static njs_int_t njs_key_ext_usages(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { njs_webcrypto_key_t *key; key = njs_vm_external(vm, njs_webcrypto_crypto_key_proto_id, value); if (njs_slow_path(key == NULL)) { njs_value_undefined_set(retval); return NJS_DECLINED; } return njs_key_ops(vm, retval, key->usage); } static njs_int_t njs_ext_get_random_values(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_str_t fill; njs_value_t *buffer; buffer = njs_arg(args, nargs, 1); ret = njs_vm_value_to_bytes(vm, &fill, buffer); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } if (njs_slow_path(fill.length > 65536)) { njs_vm_type_error(vm, "requested length exceeds 65536 bytes"); return NJS_ERROR; } if (RAND_bytes(fill.start, fill.length) != 1) { njs_webcrypto_error(vm, "RAND_bytes() failed"); return NJS_ERROR; } njs_value_assign(retval, buffer); return NJS_OK; } static void njs_webcrypto_cleanup_pkey(void *data) { njs_webcrypto_key_t *key = data; if (!key->alg->raw) { EVP_PKEY_free(key->u.a.pkey); } } static njs_webcrypto_key_t * njs_webcrypto_key_alloc(njs_vm_t *vm, njs_webcrypto_algorithm_t *alg, unsigned usage, njs_bool_t extractable) { njs_mp_cleanup_t *cln; njs_webcrypto_key_t *key; key = njs_mp_zalloc(njs_vm_memory_pool(vm), sizeof(njs_webcrypto_key_t)); if (njs_slow_path(key == NULL)) { njs_vm_memory_error(vm); return NULL; } cln = njs_mp_cleanup_add(njs_vm_memory_pool(vm), 0); if (cln == NULL) { njs_vm_memory_error(vm); return NULL; } cln->handler = njs_webcrypto_cleanup_pkey; cln->data = key; key->alg = alg; key->usage = usage; key->extractable = extractable; return key; } static njs_webcrypto_key_format_t njs_key_format(njs_vm_t *vm, njs_value_t *value) { njs_int_t ret; njs_str_t format; njs_opaque_value_t string; njs_webcrypto_entry_t *e; ret = njs_value_to_string(vm, njs_value_arg(&string), value); if (njs_slow_path(ret != NJS_OK)) { return NJS_KEY_FORMAT_UNKNOWN; } njs_value_string_get(njs_value_arg(&string), &format); for (e = &njs_webcrypto_format[0]; e->name.length != 0; e++) { if (njs_strstr_eq(&format, &e->name)) { return e->value; } } njs_vm_type_error(vm, "unknown key format: \"%V\"", &format); return NJS_KEY_FORMAT_UNKNOWN; } static njs_str_t * njs_format_string(njs_webcrypto_key_format_t fmt) { njs_webcrypto_entry_t *e; for (e = &njs_webcrypto_format[0]; e->name.length != 0; e++) { if (fmt == e->value) { break; } } return &e->name; } static njs_int_t njs_key_usage_array_handler(njs_vm_t *vm, njs_iterator_args_t *args, njs_value_t *value, int64_t index, njs_value_t *retval) { unsigned *mask; njs_str_t u; njs_int_t ret; njs_opaque_value_t usage; njs_webcrypto_entry_t *e; njs_value_assign(&usage, value); ret = njs_value_to_string(vm, njs_value_arg(&usage), njs_value_arg(&usage)); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_value_string_get(njs_value_arg(&usage), &u); for (e = &njs_webcrypto_usage[0]; e->name.length != 0; e++) { if (njs_strstr_eq(&u, &e->name)) { mask = args->data; *mask |= e->value; return NJS_OK; } } njs_vm_type_error(vm, "unknown key usage: \"%V\"", &u); return NJS_ERROR; } static njs_int_t njs_key_usage(njs_vm_t *vm, njs_value_t *value, unsigned *mask) { int64_t length; njs_int_t ret; njs_opaque_value_t retval; njs_iterator_args_t args; if (!njs_value_is_array(value)) { njs_vm_type_error(vm, "\"keyUsages\" argument must be an Array"); return NJS_ERROR; } ret = njs_vm_array_length(vm, value, &length); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } *mask = 0; njs_value_assign(&args.value, value); args.from = 0; args.to = length; args.data = mask; return njs_vm_object_iterate(vm, &args, njs_key_usage_array_handler, njs_value_arg(&retval)); } static njs_int_t njs_key_ops(njs_vm_t *vm, njs_value_t *retval, unsigned mask) { njs_int_t ret; njs_value_t *value; njs_webcrypto_entry_t *e; ret = njs_vm_array_alloc(vm, retval, 4); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } for (e = &njs_webcrypto_usage[0]; e->name.length != 0; e++) { if (mask & e->value) { value = njs_vm_array_push(vm, retval); if (value == NULL) { return NJS_ERROR; } ret = njs_vm_value_string_create(vm, value, e->name.start, e->name.length); if (ret != NJS_OK) { return NJS_ERROR; } } } return NJS_OK; } static njs_webcrypto_algorithm_t * njs_key_algorithm(njs_vm_t *vm, njs_value_t *options) { njs_int_t ret; njs_str_t a; njs_value_t *val; njs_opaque_value_t name; njs_webcrypto_entry_t *e; njs_webcrypto_algorithm_t *alg; if (njs_value_is_object(options)) { val = njs_vm_object_prop(vm, options, &string_name, &name); if (njs_slow_path(val == NULL)) { njs_vm_type_error(vm, "algorithm name is not provided"); return NULL; } } else { njs_value_assign(&name, options); } ret = njs_value_to_string(vm, njs_value_arg(&name), njs_value_arg(&name)); if (njs_slow_path(ret != NJS_OK)) { return NULL; } njs_value_string_get(njs_value_arg(&name), &a); for (e = &njs_webcrypto_alg[0]; e->name.length != 0; e++) { if (njs_strstr_case_eq(&a, &e->name)) { alg = (njs_webcrypto_algorithm_t *) e->value; if (alg->usage & NJS_KEY_USAGE_UNSUPPORTED) { njs_vm_type_error(vm, "unsupported algorithm: \"%V\"", &a); return NULL; } return alg; } } njs_vm_type_error(vm, "unknown algorithm name: \"%V\"", &a); return NULL; } static njs_str_t * njs_algorithm_string(njs_webcrypto_algorithm_t *algorithm) { njs_webcrypto_entry_t *e; njs_webcrypto_algorithm_t *alg; for (e = &njs_webcrypto_alg[0]; e->name.length != 0; e++) { alg = (njs_webcrypto_algorithm_t *) e->value; if (alg->type == algorithm->type) { break; } } return &e->name; } static njs_int_t njs_algorithm_hash(njs_vm_t *vm, njs_value_t *options, njs_webcrypto_hash_t *hash) { njs_int_t ret; njs_str_t name; njs_value_t *val; njs_opaque_value_t value; njs_webcrypto_entry_t *e; if (njs_value_is_object(options)) { val = njs_vm_object_prop(vm, options, &string_hash, &value); if (njs_slow_path(val == NULL)) { njs_value_undefined_set(njs_value_arg(&value)); } } else { njs_value_assign(&value, options); } ret = njs_value_to_string(vm, njs_value_arg(&value), njs_value_arg(&value)); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_value_string_get(njs_value_arg(&value), &name); for (e = &njs_webcrypto_hash[0]; e->name.length != 0; e++) { if (njs_strstr_eq(&name, &e->name)) { *hash = e->value; return NJS_OK; } } njs_vm_type_error(vm, "unknown hash name: \"%V\"", &name); return NJS_ERROR; } static njs_str_t * njs_algorithm_hash_name(njs_webcrypto_hash_t hash) { njs_webcrypto_entry_t *e; for (e = &njs_webcrypto_hash[0]; e->name.length != 0; e++) { if (e->value == hash) { return &e->name; } } return &e->name; } static const EVP_MD * njs_algorithm_hash_digest(njs_webcrypto_hash_t hash) { switch (hash) { case NJS_HASH_SHA256: return EVP_sha256(); case NJS_HASH_SHA384: return EVP_sha384(); case NJS_HASH_SHA512: return EVP_sha512(); case NJS_HASH_SHA1: default: break; } return EVP_sha1(); } static njs_int_t njs_algorithm_curve(njs_vm_t *vm, njs_value_t *options, int *curve) { njs_int_t ret; njs_str_t name; njs_value_t *val; njs_opaque_value_t value; njs_webcrypto_entry_t *e; if (*curve != 0) { return NJS_OK; } val = njs_vm_object_prop(vm, options, &string_curve, &value); if (njs_slow_path(val == NULL)) { njs_value_undefined_set(njs_value_arg(&value)); } ret = njs_value_to_string(vm, njs_value_arg(&value), njs_value_arg(&value)); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_value_string_get(njs_value_arg(&value), &name); for (e = &njs_webcrypto_curve[0]; e->name.length != 0; e++) { if (njs_strstr_eq(&name, &e->name)) { *curve = e->value; return NJS_OK; } } njs_vm_type_error(vm, "unknown namedCurve: \"%V\"", &name); return NJS_ERROR; } static njs_str_t * njs_algorithm_curve_name(int curve) { njs_webcrypto_entry_t *e; for (e = &njs_webcrypto_curve[0]; e->name.length != 0; e++) { if (e->value == (uintptr_t) curve) { return &e->name; } } return &e->name; } static njs_int_t njs_promise_trampoline(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_function_t *callback; callback = njs_value_function(njs_argument(args, 1)); if (callback != NULL) { return njs_vm_invoke(vm, callback, njs_argument(args, 2), 1, retval); } return NJS_OK; } static njs_int_t njs_webcrypto_result(njs_vm_t *vm, njs_opaque_value_t *result, njs_int_t rc, njs_value_t *retval) { njs_int_t ret; njs_function_t *callback; njs_opaque_value_t promise, arguments[2]; ret = njs_vm_promise_create(vm, njs_value_arg(&promise), njs_value_arg(&arguments)); if (ret != NJS_OK) { goto error; } callback = njs_vm_function_alloc(vm, njs_promise_trampoline, 0, 0); if (callback == NULL) { goto error; } njs_value_assign(&arguments[0], &arguments[(rc != NJS_OK)]); if (rc != NJS_OK) { njs_vm_exception_get(vm, njs_value_arg(&arguments[1])); } else { njs_value_assign(&arguments[1], result); } ret = njs_vm_enqueue_job(vm, callback, njs_value_arg(&arguments), 2); if (ret == NJS_ERROR) { goto error; } njs_value_assign(retval, &promise); return NJS_OK; error: njs_vm_internal_error(vm, "cannot make webcrypto result"); return NJS_ERROR; } static njs_int_t njs_webcrypto_array_buffer(njs_vm_t *vm, njs_value_t *retval, u_char *start, size_t length) { u_char *dst; dst = njs_mp_alloc(njs_vm_memory_pool(vm), length); if (njs_slow_path(dst == NULL)) { njs_vm_memory_error(vm); return NJS_ERROR; } memcpy(dst, start, length); return njs_vm_value_array_buffer_set(vm, retval, dst, length); } static u_char * njs_cpystrn(u_char *dst, u_char *src, size_t n) { if (n == 0) { return dst; } while (--n) { *dst = *src; if (*dst == '\0') { return dst; } dst++; src++; } *dst = '\0'; return dst; } static void njs_webcrypto_error(njs_vm_t *vm, const char *fmt, ...) { int flags; u_char *p, *last; va_list args; const char *data; unsigned long n; u_char errstr[NJS_MAX_ERROR_STR]; last = &errstr[NJS_MAX_ERROR_STR]; va_start(args, fmt); p = njs_vsprintf(errstr, last - 1, fmt, args); va_end(args); if (ERR_peek_error()) { p = njs_cpystrn(p, (u_char *) " (SSL:", last - p); for ( ;; ) { n = ERR_peek_error_data(&data, &flags); if (n == 0) { break; } /* ERR_error_string_n() requires at least one byte */ if (p >= last - 1) { goto next; } *p++ = ' '; ERR_error_string_n(n, (char *) p, last - p); while (p < last && *p) { p++; } if (p < last && *data && (flags & ERR_TXT_STRING)) { *p++ = ':'; p = njs_cpystrn(p, (u_char *) data, last - p); } next: (void) ERR_get_error(); } if (p < last) { *p++ = ')'; } } njs_vm_error(vm, "%*s", p - errstr, errstr); } static njs_int_t njs_webcrypto_init(njs_vm_t *vm) { njs_int_t ret, proto_id; njs_str_t name; njs_opaque_value_t value; #if (OPENSSL_VERSION_NUMBER < 0x10100003L) OpenSSL_add_all_algorithms(); #endif njs_webcrypto_crypto_key_proto_id = njs_vm_external_prototype(vm, njs_ext_webcrypto_crypto_key, njs_nitems(njs_ext_webcrypto_crypto_key)); if (njs_slow_path(njs_webcrypto_crypto_key_proto_id < 0)) { return NJS_ERROR; } proto_id = njs_vm_external_prototype(vm, njs_ext_webcrypto, njs_nitems(njs_ext_webcrypto)); if (njs_slow_path(proto_id < 0)) { return NJS_ERROR; } ret = njs_vm_external_create(vm, njs_value_arg(&value), proto_id, NULL, 1); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } name.length = njs_length("crypto"); name.start = (u_char *) "crypto"; ret = njs_vm_bind(vm, &name, njs_value_arg(&value), 1); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } return NJS_OK; } njs-0.8.9/external/njs_xml_module.c000066400000000000000000001506421474132077100173450ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #include #include #include #include #include #include typedef struct { xmlDoc *doc; xmlParserCtxt *ctx; } njs_xml_doc_t; typedef enum { XML_NSET_TREE = 0, XML_NSET_TREE_NO_COMMENTS, XML_NSET_TREE_INVERT, } njs_xml_nset_type_t; typedef struct njs_xml_nset_s njs_xml_nset_t; struct njs_xml_nset_s { xmlNodeSet *nodes; xmlDoc *doc; njs_xml_nset_type_t type; njs_xml_nset_t *next; njs_xml_nset_t *prev; }; static njs_int_t njs_xml_ext_parse(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_xml_ext_canonicalization(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t magic, njs_value_t *retval); static njs_int_t njs_xml_doc_ext_prop_keys(njs_vm_t *vm, njs_value_t *value, njs_value_t *keys); static njs_int_t njs_xml_doc_ext_root(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *unused, njs_value_t *retval); static njs_int_t njs_xml_node_ext_prop_keys(njs_vm_t *vm, njs_value_t *value, njs_value_t *keys); static njs_int_t njs_xml_node_ext_prop_handler(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *unused, njs_value_t *retval); static njs_int_t njs_xml_attr_ext_prop_keys(njs_vm_t *vm, njs_value_t *value, njs_value_t *keys); static njs_int_t njs_xml_attr_ext_prop_handler(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *unused, njs_value_t *retval); static njs_int_t njs_xml_node_ext_add_child(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_xml_node_ext_attrs(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t njs_xml_node_ext_name(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t njs_xml_node_ext_ns(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t njs_xml_node_ext_parent(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t njs_xml_node_ext_remove_all_attributes(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_xml_node_ext_remove_attribute(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_xml_node_ext_remove_children(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_xml_node_ext_remove_text(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_xml_node_ext_set_attribute(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_xml_node_ext_set_text(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_xml_node_ext_tags(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t njs_xml_node_ext_text(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t njs_xml_node_attr_handler(njs_vm_t *vm, xmlNode *current, njs_str_t *name, njs_value_t *setval, njs_value_t *retval); static njs_int_t njs_xml_node_tag_remove(njs_vm_t *vm, xmlNode *current, njs_str_t *name); static njs_int_t njs_xml_node_tag_handler(njs_vm_t *vm, xmlNode *current, njs_str_t *name, njs_value_t *setval, njs_value_t *retval); static njs_int_t njs_xml_node_tags_handler(njs_vm_t *vm, xmlNode *current, njs_str_t *name, njs_value_t *setval, njs_value_t *retval); static xmlNode *njs_xml_external_node(njs_vm_t *vm, njs_value_t *value); static njs_int_t njs_xml_str_to_c_string(njs_vm_t *vm, njs_str_t *str, u_char *dst, size_t size); static const u_char *njs_xml_value_to_c_string(njs_vm_t *vm, njs_value_t *value, u_char *dst, size_t size); static njs_int_t njs_xml_encode_special_chars(njs_vm_t *vm, njs_str_t *src, njs_str_t *out); static njs_int_t njs_xml_replace_node(njs_vm_t *vm, xmlNode *old, xmlNode *current); static void njs_xml_node_cleanup(void *data); static void njs_xml_doc_cleanup(void *data); static njs_xml_nset_t *njs_xml_nset_create(njs_vm_t *vm, xmlDoc *doc, xmlNode *current, njs_xml_nset_type_t type); static njs_xml_nset_t *njs_xml_nset_add(njs_xml_nset_t *nset, njs_xml_nset_t *add); static void njs_xml_nset_cleanup(void *data); static void njs_xml_error(njs_vm_t *vm, njs_xml_doc_t *tree, const char *fmt, ...); static njs_int_t njs_xml_init(njs_vm_t *vm); static njs_external_t njs_ext_xml[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "xml", } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("parse"), .writable = 1, .configurable = 1, .u.method = { .native = njs_xml_ext_parse, .magic8 = 0, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("c14n"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_xml_ext_canonicalization, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("exclusiveC14n"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_xml_ext_canonicalization, .magic8 = 1, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("serialize"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_xml_ext_canonicalization, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("serializeToString"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_xml_ext_canonicalization, .magic8 = 3, } }, }; static njs_external_t njs_ext_xml_doc[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "XMLDoc", } }, { .flags = NJS_EXTERN_SELF, .u.object = { .enumerable = 1, .prop_handler = njs_xml_doc_ext_root, .keys = njs_xml_doc_ext_prop_keys, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("$root"), .enumerable = 1, .u.property = { .handler = njs_xml_doc_ext_root, .magic32 = 1, } }, }; static njs_external_t njs_ext_xml_node[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "XMLNode", } }, { .flags = NJS_EXTERN_SELF, .u.object = { .enumerable = 1, .writable = 1, .configurable = 1, .prop_handler = njs_xml_node_ext_prop_handler, .keys = njs_xml_node_ext_prop_keys, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("addChild"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_xml_node_ext_add_child, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("$attrs"), .enumerable = 1, .u.property = { .handler = njs_xml_node_ext_attrs, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("$name"), .enumerable = 1, .u.property = { .handler = njs_xml_node_ext_name, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("$ns"), .enumerable = 1, .u.property = { .handler = njs_xml_node_ext_ns, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("$parent"), .enumerable = 1, .u.property = { .handler = njs_xml_node_ext_parent, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("removeAllAttributes"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_xml_node_ext_remove_all_attributes, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("removeAttribute"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_xml_node_ext_remove_attribute, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("removeChildren"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_xml_node_ext_remove_children, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("removeText"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_xml_node_ext_remove_text, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("setAttribute"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_xml_node_ext_set_attribute, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("setText"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_xml_node_ext_set_text, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("$tags"), .enumerable = 1, .writable = 1, .configurable = 1, .u.property = { .handler = njs_xml_node_ext_tags, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("$text"), .enumerable = 1, .writable = 1, .u.property = { .handler = njs_xml_node_ext_text, } }, }; static njs_external_t njs_ext_xml_attr[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "XMLAttr", } }, { .flags = NJS_EXTERN_SELF, .u.object = { .enumerable = 1, .prop_handler = njs_xml_attr_ext_prop_handler, .keys = njs_xml_attr_ext_prop_keys, } }, }; njs_module_t njs_xml_module = { .name = njs_str("xml"), .preinit = NULL, .init = njs_xml_init, }; static njs_int_t njs_xml_doc_proto_id; static njs_int_t njs_xml_node_proto_id; static njs_int_t njs_xml_attr_proto_id; static njs_int_t njs_xml_ext_parse(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_str_t data; njs_xml_doc_t *tree; njs_mp_cleanup_t *cln; ret = njs_vm_value_to_bytes(vm, &data, njs_arg(args, nargs, 1)); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } tree = njs_mp_zalloc(njs_vm_memory_pool(vm), sizeof(njs_xml_doc_t)); if (njs_slow_path(tree == NULL)) { njs_vm_memory_error(vm); return NJS_ERROR; } tree->ctx = xmlNewParserCtxt(); if (njs_slow_path(tree->ctx == NULL)) { njs_vm_internal_error(vm, "xmlNewParserCtxt() failed"); return NJS_ERROR; } tree->doc = xmlCtxtReadMemory(tree->ctx, (char *) data.start, data.length, NULL, NULL, XML_PARSE_NOWARNING | XML_PARSE_NOERROR); if (njs_slow_path(tree->doc == NULL)) { njs_xml_error(vm, tree, "failed to parse XML"); return NJS_ERROR; } cln = njs_mp_cleanup_add(njs_vm_memory_pool(vm), 0); if (njs_slow_path(cln == NULL)) { njs_vm_memory_error(vm); return NJS_ERROR; } cln->handler = njs_xml_doc_cleanup; cln->data = tree; return njs_vm_external_create(vm, retval, njs_xml_doc_proto_id, tree, 0); } static njs_int_t njs_xml_doc_ext_prop_keys(njs_vm_t *vm, njs_value_t *value, njs_value_t *keys) { xmlNode *node; njs_int_t ret; njs_value_t *push; njs_xml_doc_t *tree; tree = njs_vm_external(vm, njs_xml_doc_proto_id, value); if (njs_slow_path(tree == NULL)) { njs_value_undefined_set(keys); return NJS_DECLINED; } ret = njs_vm_array_alloc(vm, keys, 2); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } for (node = xmlDocGetRootElement(tree->doc); node != NULL; node = node->next) { if (node->type != XML_ELEMENT_NODE) { continue; } push = njs_vm_array_push(vm, keys); if (njs_slow_path(push == NULL)) { return NJS_ERROR; } ret = njs_vm_value_string_create(vm, push, node->name, njs_strlen(node->name)); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } return NJS_OK; } static njs_int_t njs_xml_doc_ext_root(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *unused, njs_value_t *retval) { xmlNode *node; njs_int_t ret; njs_str_t name; njs_bool_t any; njs_xml_doc_t *tree; tree = njs_vm_external(vm, njs_xml_doc_proto_id, value); if (njs_slow_path(tree == NULL)) { njs_value_undefined_set(retval); return NJS_DECLINED; } any = njs_vm_prop_magic32(prop); if (!any) { ret = njs_vm_prop_name(vm, prop, &name); if (njs_slow_path(ret != NJS_OK)) { njs_value_undefined_set(retval); return NJS_DECLINED; } } else { /* To suppress warning. */ name.length = 0; name.start = NULL; } for (node = xmlDocGetRootElement(tree->doc); node != NULL; node = node->next) { if (node->type != XML_ELEMENT_NODE) { continue; } if (!any) { if (name.length != njs_strlen(node->name) || njs_strncmp(name.start, node->name, name.length) != 0) { continue; } } return njs_vm_external_create(vm, retval, njs_xml_node_proto_id, node, 0); } njs_value_undefined_set(retval); return NJS_DECLINED; } static njs_int_t njs_xml_node_ext_prop_keys(njs_vm_t *vm, njs_value_t *value, njs_value_t *keys) { xmlNode *node, *current; njs_int_t ret; njs_value_t *push; current = njs_vm_external(vm, njs_xml_node_proto_id, value); if (njs_slow_path(current == NULL)) { njs_value_undefined_set(keys); return NJS_DECLINED; } ret = njs_vm_array_alloc(vm, keys, 2); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } if (current->name != NULL && current->type == XML_ELEMENT_NODE) { push = njs_vm_array_push(vm, keys); if (njs_slow_path(push == NULL)) { return NJS_ERROR; } ret = njs_vm_value_string_create(vm, push, (u_char *) "$name", njs_length("$name")); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } if (current->ns != NULL) { push = njs_vm_array_push(vm, keys); if (njs_slow_path(push == NULL)) { return NJS_ERROR; } ret = njs_vm_value_string_create(vm, push, (u_char *) "$ns", njs_length("$ns")); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } if (current->properties != NULL) { push = njs_vm_array_push(vm, keys); if (njs_slow_path(push == NULL)) { return NJS_ERROR; } ret = njs_vm_value_string_create(vm, push, (u_char *) "$attrs", njs_length("$attrs")); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } if (current->children != NULL && current->children->content != NULL) { push = njs_vm_array_push(vm, keys); if (njs_slow_path(push == NULL)) { return NJS_ERROR; } ret = njs_vm_value_string_create(vm, push, (u_char *) "$text", njs_length("$text")); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } for (node = current->children; node != NULL; node = node->next) { if (node->type != XML_ELEMENT_NODE) { continue; } push = njs_vm_array_push(vm, keys); if (njs_slow_path(push == NULL)) { return NJS_ERROR; } ret = njs_vm_value_string_create(vm, push, (u_char *) "$tags", njs_length("$tags")); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } break; } return NJS_OK; } static njs_int_t njs_xml_node_ext_prop_handler(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { xmlNode *current; njs_int_t ret; njs_str_t name; /* * $tag$foo - the first tag child with the name "foo" * $tags$foo - the all children with the name "foo" as an array * $attr$foo - the attribute with the name "foo" * foo - the same as $tag$foo */ current = njs_vm_external(vm, njs_xml_node_proto_id, value); if (njs_slow_path(current == NULL)) { njs_value_undefined_set(retval); return NJS_DECLINED; } ret = njs_vm_prop_name(vm, prop, &name); if (njs_slow_path(ret != NJS_OK)) { njs_value_undefined_set(retval); return NJS_DECLINED; } if (name.length > 1 && name.start[0] == '$') { if (name.length > njs_length("$attr$") && njs_strncmp(&name.start[1], "attr$", njs_length("attr$")) == 0) { name.length -= njs_length("$attr$"); name.start += njs_length("$attr$"); return njs_xml_node_attr_handler(vm, current, &name, setval, retval); } if (name.length > njs_length("$tag$") && njs_strncmp(&name.start[1], "tag$", njs_length("tag$")) == 0) { name.length -= njs_length("$tag$"); name.start += njs_length("$tag$"); return njs_xml_node_tag_handler(vm, current, &name, setval, retval); } if (name.length >= njs_length("$tags$") && njs_strncmp(&name.start[1], "tags$", njs_length("tags$")) == 0) { name.length -= njs_length("$tags$"); name.start += njs_length("$tags$"); return njs_xml_node_tags_handler(vm, current, &name, setval, retval); } } return njs_xml_node_tag_handler(vm, current, &name, setval, retval); } static njs_int_t njs_xml_node_ext_add_child(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { xmlNode *current, *node, *copy, *rnode; njs_int_t ret; current = njs_vm_external(vm, njs_xml_node_proto_id, njs_argument(args, 0)); if (njs_slow_path(current == NULL)) { njs_vm_type_error(vm, "\"this\" is not a XMLNode object"); return NJS_ERROR; } node = njs_xml_external_node(vm, njs_arg(args, nargs, 1)); if (njs_slow_path(node == NULL)) { njs_vm_type_error(vm, "node is not a XMLNode object"); return NJS_ERROR; } copy = xmlDocCopyNode(current, current->doc, 1); if (njs_slow_path(copy == NULL)) { njs_vm_internal_error(vm, "xmlDocCopyNode() failed"); return NJS_ERROR; } node = xmlDocCopyNode(node, current->doc, 1); if (njs_slow_path(node == NULL)) { njs_vm_internal_error(vm, "xmlDocCopyNode() failed"); goto error; } rnode = xmlAddChild(copy, node); if (njs_slow_path(rnode == NULL)) { xmlFreeNode(node); njs_vm_internal_error(vm, "xmlAddChild() failed"); goto error; } ret = xmlReconciliateNs(current->doc, copy); if (njs_slow_path(ret == -1)) { njs_vm_internal_error(vm, "xmlReconciliateNs() failed"); goto error; } njs_value_undefined_set(retval); return njs_xml_replace_node(vm, current, copy); error: xmlFreeNode(copy); return NJS_ERROR; } static njs_int_t njs_xml_node_ext_attrs(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { xmlNode *current; current = njs_vm_external(vm, njs_xml_node_proto_id, value); if (njs_slow_path(current == NULL || current->properties == NULL)) { njs_value_undefined_set(retval); return NJS_DECLINED; } return njs_vm_external_create(vm, retval, njs_xml_attr_proto_id, current->properties, 0); } static njs_int_t njs_xml_node_ext_name(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { xmlNode *current; current = njs_vm_external(vm, njs_xml_node_proto_id, value); if (current == NULL || current->type != XML_ELEMENT_NODE) { njs_value_undefined_set(retval); return NJS_DECLINED; } return njs_vm_value_string_create(vm, retval, current->name, njs_strlen(current->name)); } static njs_int_t njs_xml_node_ext_ns(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { xmlNode *current; current = njs_vm_external(vm, njs_xml_node_proto_id, value); if (njs_slow_path(current == NULL || current->ns == NULL)) { njs_value_undefined_set(retval); return NJS_DECLINED; } return njs_vm_value_string_create(vm, retval, current->ns->href, njs_strlen(current->ns->href)); } static njs_int_t njs_xml_node_ext_parent(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { xmlNode *current; current = njs_vm_external(vm, njs_xml_node_proto_id, value); if (njs_slow_path(current == NULL || current->parent == NULL || current->parent->type != XML_ELEMENT_NODE)) { njs_value_undefined_set(retval); return NJS_DECLINED; } return njs_vm_external_create(vm, retval, njs_xml_node_proto_id, current->parent, 0); } static njs_int_t njs_xml_node_ext_remove_attribute(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { return njs_xml_node_ext_set_attribute(vm, args, nargs, 1, retval); } static njs_int_t njs_xml_node_ext_remove_all_attributes(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { xmlNode *current; current = njs_vm_external(vm, njs_xml_node_proto_id, njs_argument(args, 0)); if (njs_slow_path(current == NULL)) { njs_vm_type_error(vm, "\"this\" is not a XMLNode object"); return NJS_ERROR; } if (current->properties != NULL) { xmlFreePropList(current->properties); current->properties = NULL; } njs_value_undefined_set(retval); return NJS_OK; } static njs_int_t njs_xml_node_ext_remove_children(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { xmlNode *current, *copy; njs_str_t name; njs_value_t *selector; current = njs_vm_external(vm, njs_xml_node_proto_id, njs_argument(args, 0)); if (njs_slow_path(current == NULL)) { njs_vm_type_error(vm, "\"this\" is not a XMLNode object"); return NJS_ERROR; } selector = njs_arg(args, nargs, 1); njs_value_undefined_set(retval); if (!njs_value_is_null_or_undefined(selector)) { if (njs_slow_path(!njs_value_is_string(selector))) { njs_vm_type_error(vm, "selector is not a string"); return NJS_ERROR; } njs_value_string_get(selector, &name); return njs_xml_node_tag_remove(vm, current, &name); } /* all. */ copy = xmlDocCopyNode(current, current->doc, 1); if (njs_slow_path(copy == NULL)) { njs_vm_internal_error(vm, "xmlDocCopyNode() failed"); return NJS_ERROR; } if (copy->children != NULL) { xmlFreeNodeList(copy->children); copy->children = NULL; } return njs_xml_replace_node(vm, current, copy); } static njs_int_t njs_xml_node_ext_remove_text(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { return njs_xml_node_ext_text(vm, NULL, njs_argument(args, 0), NULL, NULL); } static njs_int_t njs_xml_node_ext_set_attribute(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t remove, njs_value_t *retval) { xmlNode *current; njs_str_t str; njs_value_t *name; current = njs_vm_external(vm, njs_xml_node_proto_id, njs_argument(args, 0)); if (njs_slow_path(current == NULL)) { njs_vm_type_error(vm, "\"this\" is not a XMLNode object"); return NJS_ERROR; } name = njs_arg(args, nargs, 1); if (njs_slow_path(!njs_value_is_string(name))) { njs_vm_type_error(vm, "name is not a string"); return NJS_ERROR; } njs_value_string_get(name, &str); return njs_xml_node_attr_handler(vm, current, &str, njs_arg(args, nargs, 2), !remove ? retval : NULL); } static njs_int_t njs_xml_node_ext_set_text(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { return njs_xml_node_ext_text(vm, NULL, njs_argument(args, 0), njs_arg(args, nargs, 1), retval); } static njs_int_t njs_xml_node_ext_tags(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { xmlNode *current; njs_str_t name; current = njs_vm_external(vm, njs_xml_node_proto_id, value); if (njs_slow_path(current == NULL || current->children == NULL)) { njs_value_undefined_set(retval); return NJS_DECLINED; } name.start = NULL; name.length = 0; return njs_xml_node_tags_handler(vm, current, &name, setval, retval); } static njs_int_t njs_xml_node_ext_text(njs_vm_t *vm, njs_object_prop_t *unused, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { u_char *text; xmlNode *current, *copy; njs_int_t ret; njs_str_t content, enc; current = njs_vm_external(vm, njs_xml_node_proto_id, value); if (njs_slow_path(current == NULL)) { njs_value_undefined_set(retval); return NJS_DECLINED; } if (retval != NULL && setval == NULL) { text = xmlNodeGetContent(current); ret = njs_vm_value_string_create(vm, retval, text, njs_strlen(text)); xmlFree(text); return ret; } /* set or delete. */ enc.start = NULL; enc.length = 0; if (retval != NULL && (setval != NULL && !njs_value_is_null_or_undefined(setval))) { if (njs_slow_path(!njs_value_is_string(setval))) { njs_vm_type_error(vm, "setval is not a string"); return NJS_ERROR; } njs_value_string_get(setval, &content); ret = njs_xml_encode_special_chars(vm, &content, &enc); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } copy = xmlDocCopyNode(current, current->doc, 1); if (njs_slow_path(copy == NULL)) { njs_vm_internal_error(vm, "xmlDocCopyNode() failed"); return NJS_ERROR; } xmlNodeSetContentLen(copy, enc.start, enc.length); if (retval != NULL) { njs_value_undefined_set(retval); } return njs_xml_replace_node(vm, current, copy); } static njs_int_t njs_xml_node_attr_handler(njs_vm_t *vm, xmlNode *current, njs_str_t *name, njs_value_t *setval, njs_value_t *retval) { size_t size; njs_int_t ret; xmlAttr *attr; const u_char *content, *value; u_char name_buf[512], value_buf[1024]; if (retval != NULL && setval == NULL) { /* get. */ for (attr = current->properties; attr != NULL; attr = attr->next) { if (attr->type != XML_ATTRIBUTE_NODE) { continue; } size = njs_strlen(attr->name); if (name->length != size || njs_strncmp(name->start, attr->name, size) != 0) { continue; } if (attr->children != NULL && attr->children->next == NULL && attr->children->type == XML_TEXT_NODE) { content = (const u_char *) attr->children->content; return njs_vm_value_string_create(vm, retval, content, njs_strlen(content)); } } njs_value_undefined_set(retval); return NJS_DECLINED; } /* set or delete. */ ret = njs_xml_str_to_c_string(vm, name, &name_buf[0], sizeof(name_buf)); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = xmlValidateQName(&name_buf[0], 0); if (njs_slow_path(ret != 0)) { njs_vm_type_error(vm, "attribute name \"%V\" is not valid", name); return NJS_ERROR; } if (retval == NULL || (setval != NULL && njs_value_is_null_or_undefined(setval))) { /* delete. */ attr = xmlHasProp(current, &name_buf[0]); if (attr != NULL) { xmlRemoveProp(attr); } return NJS_OK; } value = njs_xml_value_to_c_string(vm, setval, &value_buf[0], sizeof(value_buf)); if (njs_slow_path(value == NULL)) { return NJS_ERROR; } attr = xmlSetProp(current, &name_buf[0], value); if (njs_slow_path(attr == NULL)) { njs_vm_internal_error(vm, "xmlSetProp() failed"); return NJS_ERROR; } njs_value_undefined_set(retval); return NJS_OK; } static njs_int_t njs_xml_node_tag_remove(njs_vm_t *vm, xmlNode *current, njs_str_t *name) { size_t size; xmlNode *node, *next, *copy; njs_int_t ret; copy = xmlDocCopyNode(current, current->doc, 1); if (njs_slow_path(copy == NULL)) { njs_vm_internal_error(vm, "xmlDocCopyNode() failed"); return NJS_ERROR; } for (node = copy->children; node != NULL; node = next) { next = node->next; if (node->type != XML_ELEMENT_NODE) { continue; } size = njs_strlen(node->name); if (name->length != size || njs_strncmp(name->start, node->name, size) != 0) { continue; } ret = njs_xml_replace_node(vm, node, NULL); if (njs_slow_path(ret != NJS_OK)) { xmlFreeNode(copy); return NJS_ERROR; } } return njs_xml_replace_node(vm, current, copy); } static njs_int_t njs_xml_node_tag_handler(njs_vm_t *vm, xmlNode *current, njs_str_t *name, njs_value_t *setval, njs_value_t *retval) { size_t size; xmlNode *node; if (retval != NULL && setval == NULL) { /* get. */ for (node = current->children; node != NULL; node = node->next) { if (node->type != XML_ELEMENT_NODE) { continue; } size = njs_strlen(node->name); if (name->length != size || njs_strncmp(name->start, node->name, size) != 0) { continue; } return njs_vm_external_create(vm, retval, njs_xml_node_proto_id, node, 0); } njs_value_undefined_set(retval); return NJS_DECLINED; } if (retval != NULL) { njs_vm_type_error(vm, "XMLNode.$tag$xxx is not assignable, " "use addChild() or node.$tags = [node1, node2, ..] " "syntax"); return NJS_ERROR; } /* delete. */ return njs_xml_node_tag_remove(vm, current, name); } static njs_int_t njs_xml_node_tags_handler(njs_vm_t *vm, xmlNode *current, njs_str_t *name, njs_value_t *setval, njs_value_t *retval) { size_t size; int64_t i, length; xmlNode *node, *rnode, *copy; njs_int_t ret; njs_value_t *push; njs_opaque_value_t *start; if (retval != NULL && setval == NULL) { /* get. */ ret = njs_vm_array_alloc(vm, retval, 2); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } for (node = current->children; node != NULL; node = node->next) { if (node->type != XML_ELEMENT_NODE) { continue; } size = njs_strlen(node->name); if (name->length > 0 && (name->length != size || njs_strncmp(name->start, node->name, size) != 0)) { continue; } push = njs_vm_array_push(vm, retval); if (njs_slow_path(push == NULL)) { return NJS_ERROR; } ret = njs_vm_external_create(vm, push, njs_xml_node_proto_id, node, 0); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } return NJS_OK; } if (name->length > 0) { njs_vm_type_error(vm, "XMLNode $tags$xxx is not assignable, use " "addChild() or node.$tags = [node1, node2, ..] " "syntax"); return NJS_ERROR; } /* set or delete. */ copy = xmlDocCopyNode(current, current->doc, 2 /* copy properties and namespaces */); if (njs_slow_path(copy == NULL)) { njs_vm_internal_error(vm, "xmlDocCopyNode() failed"); return NJS_ERROR; } if (retval == NULL) { /* delete. */ return njs_xml_replace_node(vm, current, copy); } if (!njs_value_is_array(setval)) { njs_vm_type_error(vm, "setval is not an array"); goto error; } start = (njs_opaque_value_t *) njs_vm_array_start(vm, setval); if (njs_slow_path(start == NULL)) { goto error; } (void) njs_vm_array_length(vm, setval, &length); for (i = 0; i < length; i++) { node = njs_xml_external_node(vm, njs_value_arg(start++)); if (njs_slow_path(node == NULL)) { njs_vm_type_error(vm, "setval[%D] is not a XMLNode object", i); goto error; } node = xmlDocCopyNode(node, current->doc, 1); if (njs_slow_path(node == NULL)) { njs_vm_internal_error(vm, "xmlDocCopyNode() failed"); goto error; } rnode = xmlAddChild(copy, node); if (njs_slow_path(rnode == NULL)) { njs_vm_internal_error(vm, "xmlAddChild() failed"); xmlFreeNode(node); goto error; } } ret = xmlReconciliateNs(current->doc, copy); if (njs_slow_path(ret == -1)) { njs_vm_internal_error(vm, "xmlReconciliateNs() failed"); goto error; } njs_value_undefined_set(retval); return njs_xml_replace_node(vm, current, copy); error: xmlFreeNode(copy); return NJS_ERROR; } static xmlNode * njs_xml_external_node(njs_vm_t *vm, njs_value_t *value) { xmlNode *current; njs_xml_doc_t *tree; current = njs_vm_external(vm, njs_xml_node_proto_id, value); if (njs_slow_path(current == NULL)) { tree = njs_vm_external(vm, njs_xml_doc_proto_id, value); if (njs_slow_path(tree == NULL)) { njs_vm_type_error(vm, "\"this\" is not a XMLNode object"); return NULL; } current = xmlDocGetRootElement(tree->doc); if (njs_slow_path(current == NULL)) { njs_vm_type_error(vm, "\"this\" is not a XMLNode object"); return NULL; } } return current; } static njs_int_t njs_xml_str_to_c_string(njs_vm_t *vm, njs_str_t *str, u_char *dst, size_t size) { u_char *p; if (njs_slow_path(str->length > size - njs_length("\0"))) { njs_vm_internal_error(vm, "njs_xml_str_to_c_string() very long string, " "length >= %uz", size - njs_length("\0")); return NJS_ERROR; } p = njs_cpymem(dst, str->start, str->length); *p = '\0'; return NJS_OK; } static const u_char * njs_xml_value_to_c_string(njs_vm_t *vm, njs_value_t *value, u_char *dst, size_t size) { u_char *p; njs_str_t str; njs_int_t ret; ret = njs_vm_value_to_bytes(vm, &str, value); if (njs_slow_path(ret != NJS_OK)) { return NULL; } if (njs_fast_path(str.length + njs_length("\0") < size)) { ret = njs_xml_str_to_c_string(vm, &str, dst, size); if (njs_slow_path(ret != NJS_OK)) { return NULL; } return dst; } dst = njs_mp_alloc(njs_vm_memory_pool(vm), str.length + njs_length("\0")); if (njs_slow_path(dst == NULL)) { njs_vm_memory_error(vm); return NULL; } p = njs_cpymem(dst, str.start, str.length); *p = '\0'; return dst; } static njs_int_t njs_xml_encode_special_chars(njs_vm_t *vm, njs_str_t *src, njs_str_t *out) { u_char *p, *dst, *end; size_t len; len = 0; end = src->start + src->length; for (p = src->start; p < end; p++) { if (*p == '<' || *p == '>') { len += njs_length("<"); } if (*p == '&' || *p == '\r') { len += njs_length("&"); } if (*p == '"') { len += njs_length("""); } len += 1; } if (njs_fast_path(len == src->length)) { *out = *src; return NJS_OK; } out->start = njs_mp_alloc(njs_vm_memory_pool(vm), len); if (njs_slow_path(out->start == NULL)) { njs_vm_memory_error(vm); return NJS_ERROR; } dst = out->start; for (p = src->start; p < end; p++) { if (*p == '<') { *dst++ = '&'; *dst++ = 'l'; *dst++ = 't'; *dst++ = ';'; } else if (*p == '>') { *dst++ = '&'; *dst++ = 'g'; *dst++ = 't'; *dst++ = ';'; } else if (*p == '&') { *dst++ = '&'; *dst++ = 'a'; *dst++ = 'm'; *dst++ = 'p'; *dst++ = ';'; } else if (*p == '"') { *dst++ = '&'; *dst++ = 'q'; *dst++ = 'u'; *dst++ = 'o'; *dst++ = 't'; *dst++ = ';'; } else if (*p == '\r') { *dst++ = '&'; *dst++ = '#'; *dst++ = '1'; *dst++ = '3'; *dst++ = ';'; } else { *dst++ = *p; } } out->length = len; return NJS_OK; } static njs_int_t njs_xml_replace_node(njs_vm_t *vm, xmlNode *old, xmlNode *current) { njs_mp_cleanup_t *cln; if (current != NULL) { old = xmlReplaceNode(old, current); } else { xmlUnlinkNode(old); } cln = njs_mp_cleanup_add(njs_vm_memory_pool(vm), 0); if (njs_slow_path(cln == NULL)) { njs_vm_memory_error(vm); return NJS_ERROR; } cln->handler = njs_xml_node_cleanup; cln->data = old; return NJS_OK; } static void njs_xml_node_cleanup(void *data) { xmlNode *current = data; xmlFreeNode(current); } static void njs_xml_doc_cleanup(void *data) { njs_xml_doc_t *current = data; xmlFreeDoc(current->doc); xmlFreeParserCtxt(current->ctx); } static int njs_xml_buf_write_cb(void *context, const char *buffer, int len) { njs_chb_t *chain = context; njs_chb_append(chain, buffer, len); return chain->error ? -1 : len; } static int njs_xml_node_one_contains(njs_xml_nset_t *nset, xmlNode *node, xmlNode *parent) { int in; xmlNs ns; if (nset->type == XML_NSET_TREE_NO_COMMENTS && node->type == XML_COMMENT_NODE) { return 0; } in = 1; if (nset->nodes != NULL) { if (node->type != XML_NAMESPACE_DECL) { in = xmlXPathNodeSetContains(nset->nodes, node); } else { memcpy(&ns, node, sizeof(ns)); /* libxml2 workaround, check xpath.c for details */ if ((parent != NULL) && (parent->type == XML_ATTRIBUTE_NODE)) { ns.next = (xmlNs *) parent->parent; } else { ns.next = (xmlNs *) parent; } in = xmlXPathNodeSetContains(nset->nodes, (xmlNode *) &ns); } } switch (nset->type) { case XML_NSET_TREE: case XML_NSET_TREE_NO_COMMENTS: if (in != 0) { return 1; } if ((parent != NULL) && (parent->type == XML_ELEMENT_NODE)) { return njs_xml_node_one_contains(nset, parent, parent->parent); } return 0; case XML_NSET_TREE_INVERT: default: if (in != 0) { return 0; } if ((parent != NULL) && (parent->type == XML_ELEMENT_NODE)) { return njs_xml_node_one_contains(nset, parent, parent->parent); } } return 1; } static int njs_xml_c14n_visibility_cb(void *user_data, xmlNode *node, xmlNode *parent) { int status; njs_xml_nset_t *n, *nset; nset = user_data; if (nset == NULL) { return 1; } status = 1; n = nset; do { if (status && !njs_xml_node_one_contains(n, node, parent)) { status = 0; } n = n->next; } while (n != nset); return status; } static u_char ** njs_xml_parse_ns_list(njs_vm_t *vm, njs_str_t *src) { u_char *p, **buf, **n, **out; size_t size, idx; out = NULL; p = njs_mp_alloc(njs_vm_memory_pool(vm), src->length + 1); if (njs_slow_path(p == NULL)) { njs_vm_memory_error(vm); return NULL; } memcpy(p, src->start, src->length); p[src->length] = '\0'; size = 8; buf = njs_mp_alloc(njs_vm_memory_pool(vm), size * sizeof(char *)); if (njs_slow_path(buf == NULL)) { njs_vm_memory_error(vm); return NULL; } out = buf; while (*p != '\0') { idx = out - buf; if (idx >= size) { size *= 2; n = njs_mp_alloc(njs_vm_memory_pool(vm), size * sizeof(char *)); if (njs_slow_path(buf == NULL)) { njs_vm_memory_error(vm); return NULL; } memcpy(n, buf, size * sizeof(char *) / 2); buf = n; out = &buf[idx]; } *out++ = p; while (*p != ' ' && *p != '\0') { p++; } if (*p == ' ') { *p++ = '\0'; } } *out = NULL; return buf; } static njs_int_t njs_xml_ext_canonicalization(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t magic, njs_value_t *retval) { u_char **prefix_list; ssize_t size; xmlNode *node, *current; njs_int_t ret; njs_str_t data, string; njs_chb_t chain; njs_bool_t comments; njs_value_t *excluding, *prefixes; njs_xml_nset_t *nset, *children; xmlOutputBuffer *buf; current = njs_xml_external_node(vm, njs_argument(args, 1)); if (njs_slow_path(current == NULL)) { return NJS_ERROR; } comments = njs_value_bool(njs_arg(args, nargs, 3)); excluding = njs_arg(args, nargs, 2); if (!njs_value_is_null_or_undefined(excluding)) { node = njs_vm_external(vm, njs_xml_node_proto_id, excluding); if (njs_slow_path(node == NULL)) { njs_vm_type_error(vm, "\"excluding\" argument is not a XMLNode " "object"); return NJS_ERROR; } nset = njs_xml_nset_create(vm, current->doc, current, XML_NSET_TREE_NO_COMMENTS); if (njs_slow_path(nset == NULL)) { return NJS_ERROR; } children = njs_xml_nset_create(vm, node->doc, node, XML_NSET_TREE_INVERT); if (njs_slow_path(children == NULL)) { return NJS_ERROR; } nset = njs_xml_nset_add(nset, children); } else { nset = njs_xml_nset_create(vm, current->doc, current, comments ? XML_NSET_TREE : XML_NSET_TREE_NO_COMMENTS); if (njs_slow_path(nset == NULL)) { return NJS_ERROR; } } prefix_list = NULL; prefixes = njs_arg(args, nargs, 4); if (!njs_value_is_null_or_undefined(prefixes)) { if (!njs_value_is_string(prefixes)) { njs_vm_type_error(vm, "\"prefixes\" argument is not a string"); return NJS_ERROR; } ret = njs_vm_value_string(vm, &string, prefixes); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } prefix_list = njs_xml_parse_ns_list(vm, &string); if (njs_slow_path(prefix_list == NULL)) { return NJS_ERROR; } } NJS_CHB_MP_INIT(&chain, vm); buf = xmlOutputBufferCreateIO(njs_xml_buf_write_cb, NULL, &chain, NULL); if (njs_slow_path(buf == NULL)) { njs_vm_internal_error(vm, "xmlOutputBufferCreateIO() failed"); return NJS_ERROR; } ret = xmlC14NExecute(current->doc, njs_xml_c14n_visibility_cb, nset, magic & 0x1 ? XML_C14N_EXCLUSIVE_1_0 : XML_C14N_1_0, prefix_list, comments, buf); if (njs_slow_path(ret < 0)) { njs_vm_internal_error(vm, "xmlC14NExecute() failed"); ret = NJS_ERROR; goto error; } if (magic & 0x2) { ret = njs_vm_value_string_create_chb(vm, retval, &chain); } else { size = njs_chb_size(&chain); if (njs_slow_path(size < 0)) { njs_vm_memory_error(vm); ret = NJS_ERROR; goto error; } ret = njs_chb_join(&chain, &data); if (njs_slow_path(ret != NJS_OK)) { ret = NJS_ERROR; goto error; } ret = njs_vm_value_buffer_set(vm, retval, data.start, data.length); } error: (void) xmlOutputBufferClose(buf); njs_chb_destroy(&chain); return ret; } static njs_int_t njs_xml_attr_ext_prop_keys(njs_vm_t *vm, njs_value_t *value, njs_value_t *keys) { xmlAttr *node, *current; njs_int_t ret; njs_value_t *push; current = njs_vm_external(vm, njs_xml_attr_proto_id, value); if (njs_slow_path(current == NULL)) { njs_value_undefined_set(keys); return NJS_DECLINED; } ret = njs_vm_array_alloc(vm, keys, 2); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } for (node = current; node != NULL; node = node->next) { if (node->type != XML_ATTRIBUTE_NODE) { continue; } push = njs_vm_array_push(vm, keys); if (njs_slow_path(push == NULL)) { return NJS_ERROR; } ret = njs_vm_value_string_create(vm, push, node->name, njs_strlen(node->name)); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } return NJS_OK; } static njs_int_t njs_xml_attr_ext_prop_handler(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *unused, njs_value_t *retval) { size_t size; xmlAttr *node, *current; njs_int_t ret; njs_str_t name; current = njs_vm_external(vm, njs_xml_attr_proto_id, value); if (njs_slow_path(current == NULL)) { njs_value_undefined_set(retval); return NJS_DECLINED; } ret = njs_vm_prop_name(vm, prop, &name); if (njs_slow_path(ret != NJS_OK)) { njs_value_undefined_set(retval); return NJS_DECLINED; } for (node = current; node != NULL; node = node->next) { if (node->type != XML_ATTRIBUTE_NODE) { continue; } size = njs_strlen(node->name); if (name.length != size || njs_strncmp(name.start, node->name, size) != 0) { continue; } return njs_vm_value_string_create(vm, retval, node->children->content, njs_strlen(node->children->content)); } return NJS_OK; } static njs_xml_nset_t * njs_xml_nset_create(njs_vm_t *vm, xmlDoc *doc, xmlNode *current, njs_xml_nset_type_t type) { xmlNodeSet *nodes; njs_xml_nset_t *nset; njs_mp_cleanup_t *cln; nset = njs_mp_zalloc(njs_vm_memory_pool(vm), sizeof(njs_xml_nset_t)); if (njs_slow_path(nset == NULL)) { njs_vm_memory_error(vm); return NULL; } cln = njs_mp_cleanup_add(njs_vm_memory_pool(vm), 0); if (njs_slow_path(cln == NULL)) { njs_vm_memory_error(vm); return NULL; } nodes = xmlXPathNodeSetCreate(current); if (njs_slow_path(nodes == NULL)) { njs_vm_memory_error(vm); return NULL; } cln->handler = njs_xml_nset_cleanup; cln->data = nset; nset->doc = doc; nset->type = type; nset->nodes = nodes; nset->next = nset->prev = nset; return nset; } static njs_xml_nset_t * njs_xml_nset_add(njs_xml_nset_t *nset, njs_xml_nset_t *add) { if (nset == NULL) { return add; } add->next = nset; add->prev = nset->prev; nset->prev->next = add; nset->prev = add; return nset; } static void njs_xml_nset_cleanup(void *data) { njs_xml_nset_t *nset = data; if (nset->nodes != NULL) { xmlXPathFreeNodeSet(nset->nodes); } } static void njs_xml_error(njs_vm_t *vm, njs_xml_doc_t *current, const char *fmt, ...) { u_char *p, *last; va_list args; const xmlError *err; u_char errstr[NJS_MAX_ERROR_STR]; last = &errstr[NJS_MAX_ERROR_STR]; va_start(args, fmt); p = njs_vsprintf(errstr, last - 1, fmt, args); va_end(args); err = xmlCtxtGetLastError(current->ctx); if (err != NULL) { p = njs_sprintf(p, last - 1, " (libxml2: \"%*s\" at %d:%d)", njs_strlen(err->message) - 1, err->message, err->line, err->int2); } njs_vm_error(vm, "%*s", p - errstr, errstr); } static njs_int_t njs_xml_init(njs_vm_t *vm) { njs_int_t ret, proto_id; njs_mod_t *module; njs_opaque_value_t value; xmlInitParser(); njs_xml_doc_proto_id = njs_vm_external_prototype(vm, njs_ext_xml_doc, njs_nitems(njs_ext_xml_doc)); if (njs_slow_path(njs_xml_doc_proto_id < 0)) { return NJS_ERROR; } njs_xml_node_proto_id = njs_vm_external_prototype(vm, njs_ext_xml_node, njs_nitems(njs_ext_xml_node)); if (njs_slow_path(njs_xml_node_proto_id < 0)) { return NJS_ERROR; } njs_xml_attr_proto_id = njs_vm_external_prototype(vm, njs_ext_xml_attr, njs_nitems(njs_ext_xml_attr)); if (njs_slow_path(njs_xml_attr_proto_id < 0)) { return NJS_ERROR; } proto_id = njs_vm_external_prototype(vm, njs_ext_xml, njs_nitems(njs_ext_xml)); if (njs_slow_path(proto_id < 0)) { return NJS_ERROR; } ret = njs_vm_external_create(vm, njs_value_arg(&value), proto_id, NULL, 1); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } module = njs_vm_add_module(vm, &njs_str_value("xml"), njs_value_arg(&value)); if (njs_slow_path(module == NULL)) { return NJS_ERROR; } return NJS_OK; } njs-0.8.9/external/njs_zlib_module.c000066400000000000000000000365661474132077100175150ustar00rootroot00000000000000/* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #include #include #include #define NJS_ZLIB_CHUNK_SIZE 1024 static njs_int_t njs_zlib_ext_deflate(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_zlib_ext_inflate(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); njs_int_t njs_zlib_constant(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t njs_zlib_init(njs_vm_t *vm); static void *njs_zlib_alloc(void *opaque, u_int items, u_int size); static void njs_zlib_free(void *opaque, void *address); static njs_external_t njs_ext_zlib_constants[] = { { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("Z_NO_COMPRESSION"), .enumerable = 1, .u.property = { .handler = njs_zlib_constant, .magic32 = Z_NO_COMPRESSION, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("Z_BEST_SPEED"), .enumerable = 1, .u.property = { .handler = njs_zlib_constant, .magic32 = Z_BEST_SPEED, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("Z_BEST_COMPRESSION"), .enumerable = 1, .u.property = { .handler = njs_zlib_constant, .magic32 = Z_BEST_COMPRESSION, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("Z_FILTERED"), .enumerable = 1, .u.property = { .handler = njs_zlib_constant, .magic32 = Z_FILTERED, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("Z_HUFFMAN_ONLY"), .enumerable = 1, .u.property = { .handler = njs_zlib_constant, .magic32 = Z_HUFFMAN_ONLY, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("Z_RLE"), .enumerable = 1, .u.property = { .handler = njs_zlib_constant, .magic32 = Z_RLE, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("Z_FIXED"), .enumerable = 1, .u.property = { .handler = njs_zlib_constant, .magic32 = Z_FIXED, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("Z_DEFAULT_STRATEGY"), .enumerable = 1, .u.property = { .handler = njs_zlib_constant, .magic32 = Z_DEFAULT_STRATEGY, } }, }; static njs_external_t njs_ext_zlib[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "zlib", } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("deflateRawSync"), .writable = 1, .configurable = 1, .u.method = { .native = njs_zlib_ext_deflate, .magic8 = 1, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("deflateSync"), .writable = 1, .configurable = 1, .u.method = { .native = njs_zlib_ext_deflate, .magic8 = 0, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("inflateRawSync"), .writable = 1, .configurable = 1, .u.method = { .native = njs_zlib_ext_inflate, .magic8 = 1, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("inflateSync"), .writable = 1, .configurable = 1, .u.method = { .native = njs_zlib_ext_inflate, .magic8 = 0, } }, { .flags = NJS_EXTERN_OBJECT, .name.string = njs_str("constants"), .writable = 1, .configurable = 1, .u.object = { .properties = njs_ext_zlib_constants, .nproperties = njs_nitems(njs_ext_zlib_constants), } }, }; njs_module_t njs_zlib_module = { .name = njs_str("zlib"), .preinit = NULL, .init = njs_zlib_init, }; static njs_int_t njs_zlib_ext_deflate(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t raw, njs_value_t *retval) { int rc, level, mem_level, strategy, window_bits; u_char *buffer; size_t chunk_size; ssize_t size; njs_chb_t chain; z_stream stream; njs_int_t ret; njs_str_t data, dictionary; njs_value_t *options, *value; njs_opaque_value_t lvalue; static const njs_str_t chunk_size_key = njs_str("chunkSize"); static const njs_str_t dict_key = njs_str("dictionary"); static const njs_str_t level_key = njs_str("level"); static const njs_str_t mem_level_key = njs_str("memLevel"); static const njs_str_t strategy_key = njs_str("strategy"); static const njs_str_t window_bits_key = njs_str("windowBits"); ret = njs_vm_value_to_bytes(vm, &data, njs_arg(args, nargs, 1)); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } chunk_size = NJS_ZLIB_CHUNK_SIZE; dictionary.start = NULL; mem_level = 8; level = Z_DEFAULT_COMPRESSION; strategy = Z_DEFAULT_STRATEGY; window_bits = raw ? -MAX_WBITS : MAX_WBITS; options = njs_arg(args, nargs, 2); if (njs_value_is_object(options)) { value = njs_vm_object_prop(vm, options, &chunk_size_key, &lvalue); if (value != NULL) { chunk_size = njs_value_number(value); if (njs_slow_path(chunk_size < 64)) { njs_vm_range_error(vm, "chunkSize must be >= 64"); return NJS_ERROR; } } value = njs_vm_object_prop(vm, options, &level_key, &lvalue); if (value != NULL) { level = njs_value_number(value); if (njs_slow_path(level < Z_DEFAULT_COMPRESSION || level > Z_BEST_COMPRESSION)) { njs_vm_range_error(vm, "level must be in the range %d..%d", Z_DEFAULT_COMPRESSION, Z_BEST_COMPRESSION); return NJS_ERROR; } } value = njs_vm_object_prop(vm, options, &window_bits_key, &lvalue); if (value != NULL) { window_bits = njs_value_number(value); if (raw) { if (njs_slow_path(window_bits < -15 || window_bits > -9)) { njs_vm_range_error(vm, "windowBits must be in the range " "-15..-9"); return NJS_ERROR; } } else { if (njs_slow_path(window_bits < 9 || window_bits > 15)) { njs_vm_range_error(vm, "windowBits must be in the range " "9..15"); return NJS_ERROR; } } } value = njs_vm_object_prop(vm, options, &mem_level_key, &lvalue); if (value != NULL) { mem_level = njs_value_number(value); if (njs_slow_path(mem_level < 1 || mem_level > 9)) { njs_vm_range_error(vm, "memLevel must be in the range 0..9"); return NJS_ERROR; } } value = njs_vm_object_prop(vm, options, &strategy_key, &lvalue); if (value != NULL) { strategy = njs_value_number(value); switch (strategy) { case Z_FILTERED: case Z_HUFFMAN_ONLY: case Z_RLE: case Z_FIXED: case Z_DEFAULT_STRATEGY: break; default: njs_vm_type_error(vm, "unknown strategy: %d", strategy); return NJS_ERROR; } } value = njs_vm_object_prop(vm, options, &dict_key, &lvalue); if (value != NULL) { ret = njs_vm_value_to_bytes(vm, &dictionary, value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } } stream.next_in = data.start; stream.avail_in = data.length; stream.zalloc = njs_zlib_alloc; stream.zfree = njs_zlib_free; stream.opaque = njs_vm_memory_pool(vm); rc = deflateInit2(&stream, level, Z_DEFLATED, window_bits, mem_level, strategy); if (njs_slow_path(rc != Z_OK)) { njs_vm_internal_error(vm, "deflateInit2() failed"); return NJS_ERROR; } if (dictionary.start != NULL) { rc = deflateSetDictionary(&stream, dictionary.start, dictionary.length); if (njs_slow_path(rc != Z_OK)) { njs_vm_internal_error(vm, "deflateSetDictionary() failed"); return NJS_ERROR; } } NJS_CHB_MP_INIT(&chain, vm); do { stream.next_out = njs_chb_reserve(&chain, chunk_size); if (njs_slow_path(stream.next_out == NULL)) { njs_vm_memory_error(vm); goto fail; } stream.avail_out = chunk_size; rc = deflate(&stream, Z_FINISH); if (njs_slow_path(rc < 0)) { njs_vm_internal_error(vm, "failed to deflate the data: %s", stream.msg); goto fail; } njs_chb_written(&chain, chunk_size - stream.avail_out); } while (stream.avail_out == 0); deflateEnd(&stream); size = njs_chb_size(&chain); if (njs_slow_path(size < 0)) { njs_vm_memory_error(vm); return NJS_ERROR; } buffer = njs_mp_alloc(njs_vm_memory_pool(vm), size); if (njs_slow_path(buffer == NULL)) { return NJS_ERROR; } njs_chb_join_to(&chain, buffer); njs_chb_destroy(&chain); return njs_vm_value_buffer_set(vm, retval, buffer, size); fail: deflateEnd(&stream); njs_chb_destroy(&chain); return NJS_ERROR; } static njs_int_t njs_zlib_ext_inflate(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t raw, njs_value_t *retval) { int rc, window_bits; u_char *buffer; size_t chunk_size; ssize_t size; njs_chb_t chain; z_stream stream; njs_int_t ret; njs_str_t data, dictionary; njs_value_t *options, *value; njs_opaque_value_t lvalue; static const njs_str_t chunk_size_key = njs_str("chunkSize"); static const njs_str_t dict_key = njs_str("dictionary"); static const njs_str_t window_bits_key = njs_str("windowBits"); ret = njs_vm_value_to_bytes(vm, &data, njs_arg(args, nargs, 1)); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } chunk_size = NJS_ZLIB_CHUNK_SIZE; dictionary.start = NULL; window_bits = raw ? -MAX_WBITS : MAX_WBITS; options = njs_arg(args, nargs, 2); if (njs_value_is_object(options)) { value = njs_vm_object_prop(vm, options, &chunk_size_key, &lvalue); if (value != NULL) { chunk_size = njs_value_number(value); if (njs_slow_path(chunk_size < 64)) { njs_vm_range_error(vm, "chunkSize must be >= 64"); return NJS_ERROR; } } value = njs_vm_object_prop(vm, options, &window_bits_key, &lvalue); if (value != NULL) { window_bits = njs_value_number(value); if (raw) { if (njs_slow_path(window_bits < -15 || window_bits > -8)) { njs_vm_range_error(vm, "windowBits must be in the range " "-15..-8"); return NJS_ERROR; } } else { if (njs_slow_path(window_bits < 8 || window_bits > 15)) { njs_vm_range_error(vm, "windowBits must be in the range " "8..15"); return NJS_ERROR; } } } value = njs_vm_object_prop(vm, options, &dict_key, &lvalue); if (value != NULL) { ret = njs_vm_value_to_bytes(vm, &dictionary, value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } } stream.next_in = data.start; stream.avail_in = data.length; stream.zalloc = njs_zlib_alloc; stream.zfree = njs_zlib_free; stream.opaque = njs_vm_memory_pool(vm); rc = inflateInit2(&stream, window_bits); if (njs_slow_path(rc != Z_OK)) { njs_vm_internal_error(vm, "inflateInit2() failed"); return NJS_ERROR; } if (dictionary.start != NULL) { rc = inflateSetDictionary(&stream, dictionary.start, dictionary.length); if (njs_slow_path(rc != Z_OK)) { njs_vm_internal_error(vm, "deflateSetDictionary() failed"); return NJS_ERROR; } } NJS_CHB_MP_INIT(&chain, vm); while (rc != Z_STREAM_END) { stream.next_out = njs_chb_reserve(&chain, chunk_size); if (njs_slow_path(stream.next_out == NULL)) { njs_vm_memory_error(vm); goto fail; } stream.avail_out = chunk_size; rc = inflate(&stream, Z_NO_FLUSH); if (njs_slow_path(rc < 0)) { njs_vm_internal_error(vm, "failed to inflate the compressed " "data: %s", stream.msg); goto fail; } if (rc == Z_NEED_DICT) { njs_vm_type_error(vm, "failed to inflate, dictionary is required"); goto fail; } njs_chb_written(&chain, chunk_size - stream.avail_out); } rc = inflateEnd(&stream); if (njs_slow_path(rc != Z_OK)) { njs_vm_error(vm, "failed to end the inflate stream"); return NJS_ERROR; } size = njs_chb_size(&chain); if (njs_slow_path(size < 0)) { njs_vm_memory_error(vm); return NJS_ERROR; } buffer = njs_mp_alloc(njs_vm_memory_pool(vm), size); if (njs_slow_path(buffer == NULL)) { return NJS_ERROR; } njs_chb_join_to(&chain, buffer); njs_chb_destroy(&chain); return njs_vm_value_buffer_set(vm, retval, buffer, size); fail: inflateEnd(&stream); njs_chb_destroy(&chain); return NJS_ERROR; } njs_int_t njs_zlib_constant(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { njs_value_number_set(retval, njs_vm_prop_magic32(prop)); return NJS_OK; } static njs_int_t njs_zlib_init(njs_vm_t *vm) { njs_int_t ret, proto_id; njs_mod_t *module; njs_opaque_value_t value; proto_id = njs_vm_external_prototype(vm, njs_ext_zlib, njs_nitems(njs_ext_zlib)); if (njs_slow_path(proto_id < 0)) { return NJS_ERROR; } ret = njs_vm_external_create(vm, njs_value_arg(&value), proto_id, NULL, 1); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } module = njs_vm_add_module(vm, &njs_str_value("zlib"), njs_value_arg(&value)); if (njs_slow_path(module == NULL)) { return NJS_ERROR; } return NJS_OK; } static void * njs_zlib_alloc(void *opaque, u_int items, u_int size) { return njs_mp_alloc(opaque, items * size); } static void njs_zlib_free(void *opaque, void *address) { /* Do nothing. */ } njs-0.8.9/external/qjs_fs_module.c000066400000000000000000002332131474132077100171540ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) F5, Inc. */ #include #include #include #include #if (NJS_SOLARIS) #define DT_DIR 0 #define DT_REG 1 #define DT_CHR 2 #define DT_LNK 3 #define DT_BLK 4 #define DT_FIFO 5 #define DT_SOCK 6 #define QJS_DT_INVALID -1 #define qjs_dentry_type(_dentry) \ (QJS_DT_INVALID) #else #define QJS_DT_INVALID -1 #define qjs_dentry_type(_dentry) \ ((_dentry)->d_type) #endif #define qjs_fs_magic(calltype, mode) \ (((mode) << 2) | calltype) #define qjs_fs_magic2(field, type) \ (((type) << 4) | field) typedef enum { QJS_FS_DIRECT, QJS_FS_PROMISE, QJS_FS_CALLBACK, } qjs_fs_calltype_t; typedef enum { QJS_FTW_PHYS = 1, QJS_FTW_MOUNT = 2, QJS_FTW_DEPTH = 8, } qjs_ftw_flags_t; typedef enum { QJS_FTW_F, QJS_FTW_D, QJS_FTW_DNR, QJS_FTW_NS, QJS_FTW_SL, QJS_FTW_DP, QJS_FTW_SLN, } qjs_ftw_type_t; typedef enum { QJS_FS_TRUNC, QJS_FS_APPEND, } qjs_fs_writemode_t; typedef enum { QJS_FS_STAT, QJS_FS_LSTAT, QJS_FS_FSTAT, } njs_fs_statmode_t; typedef struct { long tv_sec; long tv_nsec; } qjs_timespec_t; typedef struct { uint64_t st_dev; uint64_t st_mode; uint64_t st_nlink; uint64_t st_uid; uint64_t st_gid; uint64_t st_rdev; uint64_t st_ino; uint64_t st_size; uint64_t st_blksize; uint64_t st_blocks; qjs_timespec_t st_atim; qjs_timespec_t st_mtim; qjs_timespec_t st_ctim; qjs_timespec_t st_birthtim; } qjs_stat_t; typedef enum { QJS_FS_STAT_DEV, QJS_FS_STAT_INO, QJS_FS_STAT_MODE, QJS_FS_STAT_NLINK, QJS_FS_STAT_UID, QJS_FS_STAT_GID, QJS_FS_STAT_RDEV, QJS_FS_STAT_SIZE, QJS_FS_STAT_BLKSIZE, QJS_FS_STAT_BLOCKS, QJS_FS_STAT_ATIME, QJS_FS_STAT_BIRTHTIME, QJS_FS_STAT_CTIME, QJS_FS_STAT_MTIME, } qjs_stat_prop_t; typedef struct { njs_str_t name; int value; } qjs_fs_entry_t; typedef int (*qjs_file_tree_walk_cb_t)(const char *, const struct stat *, qjs_ftw_type_t); static JSValue qjs_fs_access(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int calltype); static JSValue qjs_fs_exists_sync(JSContext *cx, JSValueConst this_val, int argc,JSValueConst *argv); static JSValue qjs_fs_close(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int calltype); static JSValue qjs_fs_mkdir(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int calltype); static JSValue qjs_fs_open(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int calltype); static JSValue qjs_fs_read(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int calltype); static JSValue qjs_fs_read_file(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int calltype); static JSValue qjs_fs_readlink(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int calltype); static JSValue qjs_fs_readdir(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int calltype); static JSValue qjs_fs_realpath(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int calltype); static JSValue qjs_fs_rename(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int calltype); static JSValue qjs_fs_rmdir(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int calltype); static JSValue qjs_fs_stat(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int magic); static JSValue qjs_fs_symlink(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int calltype); static JSValue qjs_fs_write(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int calltype); static JSValue qjs_fs_write_file(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int magic); static JSValue qjs_fs_unlink(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int calltype); static JSValue qjs_fs_stats_to_string_tag(JSContext *cx, JSValueConst this_val); static JSValue qjs_fs_stats_test(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int testtype); static int qjs_fs_stats_get_own_property(JSContext *cx, JSPropertyDescriptor *pdesc, JSValueConst obj, JSAtom prop); static int qjs_fs_stats_get_own_property_names(JSContext *cx, JSPropertyEnum **ptab, uint32_t *plen, JSValueConst obj); static void qjs_fs_stats_finalizer(JSRuntime *rt, JSValue val); static JSValue qjs_fs_dirent_to_string_tag(JSContext *cx, JSValueConst this_val); static JSValue qjs_fs_dirent_ctor(JSContext *cx, JSValueConst new_target, int argc, JSValueConst *argv); static JSValue qjs_fs_dirent_test(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int testtype); static JSValue qjs_fs_filehandle_to_string_tag(JSContext *cx, JSValueConst this_val); static JSValue qjs_fs_filehandle_fd(JSContext *cx, JSValueConst this_val); static JSValue qjs_fs_filehandle_value_of(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv); static void qjs_fs_filehandle_finalizer(JSRuntime *rt, JSValue val); static char *qjs_fs_path(JSContext *cx, char storage[NJS_MAX_PATH + 1], JSValue src, const char *prop_name); static JSValue qjs_fs_result(JSContext *cx, JSValue result, int calltype, JSValue callback); static JSValue qjs_fs_error(JSContext *cx, const char *syscall, const char *description, const char *path, int errn); static JSValue qjs_fs_encode(JSContext *cx, const qjs_buffer_encoding_t *encoding, njs_str_t *str); static int qjs_fs_flags(JSContext *cx, JSValue value, int default_flags); static mode_t qjs_fs_mode(JSContext *cx, JSValue value, mode_t default_mode); static JSModuleDef *qjs_fs_init(JSContext *cx, const char *name); static qjs_fs_entry_t qjs_flags_table[] = { { njs_str("a"), O_APPEND | O_CREAT | O_WRONLY }, { njs_str("a+"), O_APPEND | O_CREAT | O_RDWR }, { njs_str("as"), O_APPEND | O_CREAT | O_SYNC | O_WRONLY }, { njs_str("as+"), O_APPEND | O_CREAT | O_RDWR | O_SYNC }, { njs_str("ax"), O_APPEND | O_CREAT | O_EXCL | O_WRONLY }, { njs_str("ax+"), O_APPEND | O_CREAT | O_EXCL | O_RDWR }, { njs_str("r"), O_RDONLY }, { njs_str("r+"), O_RDWR }, { njs_str("rs+"), O_RDWR | O_SYNC }, { njs_str("w"), O_CREAT | O_TRUNC | O_WRONLY }, { njs_str("w+"), O_CREAT | O_TRUNC | O_RDWR }, { njs_str("wx"), O_CREAT | O_TRUNC | O_EXCL | O_WRONLY }, { njs_str("wx+"), O_CREAT | O_TRUNC | O_EXCL | O_RDWR }, { njs_null_str, 0 } }; static const JSCFunctionListEntry qjs_fs_stats_proto[] = { JS_CGETSET_DEF("[Symbol.toStringTag]", qjs_fs_stats_to_string_tag, NULL), JS_CFUNC_MAGIC_DEF("isBlockDevice", 0, qjs_fs_stats_test, DT_BLK), JS_CFUNC_MAGIC_DEF("isCharacterDevice", 0, qjs_fs_stats_test, DT_CHR), JS_CFUNC_MAGIC_DEF("isDirectory", 0, qjs_fs_stats_test, DT_DIR), JS_CFUNC_MAGIC_DEF("isFIFO", 0, qjs_fs_stats_test, DT_FIFO), JS_CFUNC_MAGIC_DEF("isFile", 0, qjs_fs_stats_test, DT_REG), JS_CFUNC_MAGIC_DEF("isSocket", 0, qjs_fs_stats_test, DT_SOCK), JS_CFUNC_MAGIC_DEF("isSymbolicLink", 0, qjs_fs_stats_test, DT_LNK), }; static const JSCFunctionListEntry qjs_fs_dirent_proto[] = { JS_CGETSET_DEF("[Symbol.toStringTag]", qjs_fs_dirent_to_string_tag, NULL), JS_CFUNC_MAGIC_DEF("isBlockDevice", 0, qjs_fs_dirent_test, DT_BLK), JS_CFUNC_MAGIC_DEF("isCharacterDevice", 0, qjs_fs_dirent_test, DT_CHR), JS_CFUNC_MAGIC_DEF("isDirectory", 0, qjs_fs_dirent_test, DT_DIR), JS_CFUNC_MAGIC_DEF("isFIFO", 0, qjs_fs_dirent_test, DT_FIFO), JS_CFUNC_MAGIC_DEF("isFile", 0, qjs_fs_dirent_test, DT_REG), JS_CFUNC_MAGIC_DEF("isSocket", 0, qjs_fs_dirent_test, DT_SOCK), JS_CFUNC_MAGIC_DEF("isSymbolicLink", 0, qjs_fs_dirent_test, DT_LNK), JS_CFUNC_SPECIAL_DEF("constructor", 1, constructor, qjs_fs_dirent_ctor), }; static const JSCFunctionListEntry qjs_fs_filehandle_proto[] = { JS_CGETSET_DEF("[Symbol.toStringTag]", qjs_fs_filehandle_to_string_tag, NULL), JS_CFUNC_MAGIC_DEF("close", 0, qjs_fs_close, QJS_FS_PROMISE), JS_CGETSET_DEF("fd", qjs_fs_filehandle_fd, NULL), JS_CFUNC_MAGIC_DEF("stat", 4, qjs_fs_stat, qjs_fs_magic(QJS_FS_PROMISE, QJS_FS_FSTAT)), JS_CFUNC_MAGIC_DEF("read", 4, qjs_fs_read, QJS_FS_PROMISE), JS_CFUNC_DEF("valueOf", 0, qjs_fs_filehandle_value_of), JS_CFUNC_MAGIC_DEF("write", 4, qjs_fs_write, QJS_FS_PROMISE), }; static const JSCFunctionListEntry qjs_fs_constants[] = { JS_PROP_INT32_DEF("F_OK", F_OK, JS_PROP_ENUMERABLE), JS_PROP_INT32_DEF("R_OK", R_OK, JS_PROP_ENUMERABLE), JS_PROP_INT32_DEF("W_OK", W_OK, JS_PROP_ENUMERABLE), JS_PROP_INT32_DEF("X_OK", X_OK, JS_PROP_ENUMERABLE), }; static const JSCFunctionListEntry qjs_fs_promises[] = { JS_CFUNC_MAGIC_DEF("access", 2, qjs_fs_access, QJS_FS_PROMISE), JS_CFUNC_MAGIC_DEF("appendFile", 3, qjs_fs_write_file, qjs_fs_magic(QJS_FS_PROMISE, QJS_FS_APPEND)), JS_CFUNC_MAGIC_DEF("fstat", 2, qjs_fs_stat, qjs_fs_magic(QJS_FS_PROMISE, QJS_FS_FSTAT)), JS_CFUNC_MAGIC_DEF("lstat", 2, qjs_fs_stat, qjs_fs_magic(QJS_FS_PROMISE, QJS_FS_LSTAT)), JS_CFUNC_MAGIC_DEF("mkdir", 2, qjs_fs_mkdir, QJS_FS_PROMISE), JS_CFUNC_MAGIC_DEF("open", 3, qjs_fs_open, QJS_FS_PROMISE), JS_CFUNC_MAGIC_DEF("readFile", 2, qjs_fs_read_file, QJS_FS_PROMISE), JS_CFUNC_MAGIC_DEF("realpath", 2, qjs_fs_realpath, QJS_FS_PROMISE), JS_CFUNC_MAGIC_DEF("readdir", 2, qjs_fs_readdir, QJS_FS_PROMISE), JS_CFUNC_MAGIC_DEF("readlink", 2, qjs_fs_readlink, QJS_FS_PROMISE), JS_CFUNC_MAGIC_DEF("rename", 2, qjs_fs_rename, QJS_FS_PROMISE), JS_CFUNC_MAGIC_DEF("rmdir", 2, qjs_fs_rmdir, QJS_FS_PROMISE), JS_CFUNC_MAGIC_DEF("stat", 2, qjs_fs_stat, qjs_fs_magic(QJS_FS_PROMISE, QJS_FS_STAT)), JS_CFUNC_MAGIC_DEF("symlink", 3, qjs_fs_symlink, QJS_FS_PROMISE), JS_CFUNC_MAGIC_DEF("writeFile", 3, qjs_fs_write_file, qjs_fs_magic(QJS_FS_PROMISE, QJS_FS_TRUNC)), JS_CFUNC_MAGIC_DEF("unlink", 1, qjs_fs_unlink, QJS_FS_PROMISE), }; static const JSCFunctionListEntry qjs_fs_export[] = { JS_OBJECT_DEF("constants", qjs_fs_constants, njs_nitems(qjs_fs_constants), JS_PROP_CONFIGURABLE), JS_OBJECT_DEF("promises", qjs_fs_promises, njs_nitems(qjs_fs_promises), JS_PROP_CONFIGURABLE), JS_CFUNC_MAGIC_DEF("access", 3, qjs_fs_access, QJS_FS_CALLBACK), JS_CFUNC_MAGIC_DEF("accessSync", 2, qjs_fs_access, QJS_FS_DIRECT), JS_CFUNC_MAGIC_DEF("appendFile", 4, qjs_fs_write_file, qjs_fs_magic(QJS_FS_CALLBACK, QJS_FS_APPEND)), JS_CFUNC_MAGIC_DEF("appendFileSync", 3, qjs_fs_write_file, qjs_fs_magic(QJS_FS_DIRECT, QJS_FS_APPEND)), JS_CFUNC_MAGIC_DEF("closeSync", 1, qjs_fs_close, QJS_FS_DIRECT), JS_CFUNC_SPECIAL_DEF("Dirent", 1, constructor, qjs_fs_dirent_ctor), JS_CFUNC_DEF("existsSync", 1, qjs_fs_exists_sync), JS_CFUNC_MAGIC_DEF("fstatSync", 2, qjs_fs_stat, qjs_fs_magic(QJS_FS_DIRECT, QJS_FS_FSTAT)), JS_CFUNC_MAGIC_DEF("lstat", 3, qjs_fs_stat, qjs_fs_magic(QJS_FS_CALLBACK, QJS_FS_LSTAT)), JS_CFUNC_MAGIC_DEF("lstatSync", 2, qjs_fs_stat, qjs_fs_magic(QJS_FS_DIRECT, QJS_FS_LSTAT)), JS_CFUNC_MAGIC_DEF("mkdir", 3, qjs_fs_mkdir, QJS_FS_CALLBACK), JS_CFUNC_MAGIC_DEF("mkdirSync", 2, qjs_fs_mkdir, QJS_FS_DIRECT), JS_CFUNC_MAGIC_DEF("openSync", 3, qjs_fs_open, QJS_FS_DIRECT), JS_CFUNC_MAGIC_DEF("readSync", 5, qjs_fs_read, QJS_FS_DIRECT), JS_CFUNC_MAGIC_DEF("readFile", 3, qjs_fs_read_file, QJS_FS_CALLBACK), JS_CFUNC_MAGIC_DEF("readFileSync", 2, qjs_fs_read_file, QJS_FS_DIRECT), JS_CFUNC_MAGIC_DEF("realpath", 3, qjs_fs_realpath, QJS_FS_CALLBACK), JS_CFUNC_MAGIC_DEF("realpathSync", 2, qjs_fs_realpath, QJS_FS_DIRECT), JS_CFUNC_MAGIC_DEF("readdir", 3, qjs_fs_readdir, QJS_FS_CALLBACK), JS_CFUNC_MAGIC_DEF("readdirSync", 2, qjs_fs_readdir, QJS_FS_DIRECT), JS_CFUNC_MAGIC_DEF("readlink", 3, qjs_fs_readlink, QJS_FS_CALLBACK), JS_CFUNC_MAGIC_DEF("readlinkSync", 2, qjs_fs_readlink, QJS_FS_DIRECT), JS_CFUNC_MAGIC_DEF("rename", 3, qjs_fs_rename, QJS_FS_CALLBACK), JS_CFUNC_MAGIC_DEF("renameSync", 2, qjs_fs_rename, QJS_FS_DIRECT), JS_CFUNC_MAGIC_DEF("rmdir", 3, qjs_fs_rmdir, QJS_FS_CALLBACK), JS_CFUNC_MAGIC_DEF("rmdirSync", 2, qjs_fs_rmdir, QJS_FS_DIRECT), JS_CFUNC_MAGIC_DEF("stat", 3, qjs_fs_stat, qjs_fs_magic(QJS_FS_CALLBACK, QJS_FS_STAT)), JS_CFUNC_MAGIC_DEF("statSync", 2, qjs_fs_stat, qjs_fs_magic(QJS_FS_DIRECT, QJS_FS_STAT)), JS_CFUNC_MAGIC_DEF("symlink", 4, qjs_fs_symlink, QJS_FS_CALLBACK), JS_CFUNC_MAGIC_DEF("symlinkSync", 3, qjs_fs_symlink, QJS_FS_DIRECT), JS_CFUNC_MAGIC_DEF("writeSync", 5, qjs_fs_write, QJS_FS_DIRECT), JS_CFUNC_MAGIC_DEF("writeFile", 4, qjs_fs_write_file, qjs_fs_magic(QJS_FS_CALLBACK, QJS_FS_TRUNC)), JS_CFUNC_MAGIC_DEF("writeFileSync", 3, qjs_fs_write_file, qjs_fs_magic(QJS_FS_DIRECT, QJS_FS_TRUNC)), JS_CFUNC_MAGIC_DEF("unlink", 2, qjs_fs_unlink, QJS_FS_CALLBACK), JS_CFUNC_MAGIC_DEF("unlinkSync", 1, qjs_fs_unlink, QJS_FS_DIRECT), }; static JSClassDef qjs_fs_stats_class = { "Stats", .finalizer = qjs_fs_stats_finalizer, .exotic = & (JSClassExoticMethods) { .get_own_property = qjs_fs_stats_get_own_property, .get_own_property_names = qjs_fs_stats_get_own_property_names, }, }; static JSClassDef qjs_fs_filehandle_class = { "FileHandle", .finalizer = qjs_fs_filehandle_finalizer, }; qjs_module_t qjs_fs_module = { .name = "fs", .init = qjs_fs_init, }; static JSValue qjs_fs_access(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int calltype) { int md, ret; JSValue callback, mode, result; const char *path; char path_buf[NJS_MAX_PATH + 1]; path = qjs_fs_path(cx, path_buf, argv[0], "path"); if (path == NULL) { return JS_EXCEPTION; } mode = argv[1]; callback = JS_UNDEFINED; if (calltype == QJS_FS_CALLBACK) { if (argc > 0) { callback = argv[njs_min(argc - 1, 2)]; } if (!JS_IsFunction(cx, callback)) { JS_ThrowTypeError(cx, "\"callback\" must be a function"); return JS_EXCEPTION; } if (JS_SameValue(cx, mode, callback)) { mode = JS_UNDEFINED; } } if (JS_IsNumber(mode)) { md = JS_VALUE_GET_INT(mode); } else if (JS_IsUndefined(mode)) { md = F_OK; } else { JS_ThrowTypeError(cx, "\"mode\" must be a number"); return JS_EXCEPTION; } result = JS_UNDEFINED; ret = access(path, md); if (ret != 0) { result = qjs_fs_error(cx, "access", strerror(errno), path, errno); } if (JS_IsException(result)) { return JS_EXCEPTION; } return qjs_fs_result(cx, result, calltype, callback); } static JSValue qjs_fs_exists_sync(JSContext *cx, JSValueConst this_val, int nargs, JSValueConst *args) { const char *path; char path_buf[NJS_MAX_PATH + 1]; path = qjs_fs_path(cx, path_buf, args[0], "path"); if (path == NULL) { return JS_EXCEPTION; } return (access(path, F_OK) == 0) ? JS_TRUE : JS_FALSE; } static JSValue qjs_fs_close(JSContext *cx, JSValueConst this_val, int nargs, JSValueConst *args, int calltype) { int fd; JSValue result; if (calltype == QJS_FS_DIRECT) { if (JS_ToInt32(cx, &fd, args[0]) < 0) { return JS_EXCEPTION; } } else { fd = (intptr_t) JS_GetOpaque(this_val, QJS_CORE_CLASS_ID_FS_FILEHANDLE); if (fd == -1) { JS_ThrowTypeError(cx, "file was already closed"); return JS_EXCEPTION; } JS_SetOpaque(this_val, (void *) -1); } result = JS_UNDEFINED; if (close(fd) != 0) { result = qjs_fs_error(cx, "close", strerror(errno), NULL, errno); goto done; } done: return qjs_fs_result(cx, result, calltype, JS_UNDEFINED); } static JSValue qjs_fs_make_path(JSContext *cx, char *path, mode_t md, int recursive) { int err; njs_int_t ret; const char *p, *prev, *end; struct stat sb; end = path + strlen(path); if (!recursive) { ret = mkdir(path, md); if (ret != 0) { err = errno; goto failed; } return JS_UNDEFINED; } p = path; prev = p; for ( ;; ) { p = strchr(prev + 1, '/'); if (p == NULL) { p = end; } if ((p - path) > NJS_MAX_PATH) { JS_ThrowInternalError(cx, "too large path"); return JS_EXCEPTION; } path[p - path] = '\0'; ret = mkdir(path, md); err = errno; switch (ret) { case 0: break; case EACCES: case ENOTDIR: case EPERM: goto failed; case EEXIST: default: ret = stat(path, &sb); if (ret == 0) { if (!S_ISDIR(sb.st_mode)) { err = ENOTDIR; goto failed; } break; } goto failed; } if (p == end) { break; } path[p - path] = '/'; prev = p; } return JS_UNDEFINED; failed: return qjs_fs_error(cx, "mkdir", strerror(err), path, err); } static JSValue qjs_fs_mkdir(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int calltype) { int recursive; char *path; mode_t md; JSValue callback, v, options, result; char path_buf[NJS_MAX_PATH + 1]; path = qjs_fs_path(cx, path_buf, argv[0], "path"); if (path == NULL) { return JS_EXCEPTION; } callback = JS_UNDEFINED; options = argv[1]; if (calltype == QJS_FS_CALLBACK) { if (argc > 0) { callback = argv[njs_min(argc - 1, 2)]; } if (!JS_IsFunction(cx, callback)) { JS_ThrowTypeError(cx, "\"callback\" must be a function"); return JS_EXCEPTION; } if (JS_SameValue(cx, options, callback)) { options = JS_UNDEFINED; } } md = 0777; recursive = 0; if (JS_IsNumber(options)) { md = JS_VALUE_GET_INT(options); } else if (!JS_IsUndefined(options)) { if (!JS_IsObject(options)) { JS_ThrowTypeError(cx, "Unknown options type (a number or object " "required)"); return JS_EXCEPTION; } v = JS_GetPropertyStr(cx, options, "mode"); if (!JS_IsUndefined(v) && !JS_IsException(v)) { md = qjs_fs_mode(cx, v, 0777); if (md == (mode_t) -1) { JS_FreeValue(cx, v); return JS_EXCEPTION; } } v = JS_GetPropertyStr(cx, options, "recursive"); if (!JS_IsUndefined(v) && !JS_IsException(v)) { recursive = JS_ToBool(cx, v); } } result = qjs_fs_make_path(cx, path, md, recursive); if (JS_IsException(result)) { return JS_EXCEPTION; } return qjs_fs_result(cx, result, calltype, callback); } static JSValue qjs_fs_open(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int calltype) { int fd, flags; mode_t md; JSValue result; const char *path; char path_buf[NJS_MAX_PATH + 1]; path = qjs_fs_path(cx, path_buf, argv[0], "path"); if (path == NULL) { return JS_EXCEPTION; } flags = qjs_fs_flags(cx, argv[1], O_RDONLY); if (flags == -1) { return JS_EXCEPTION; } md = qjs_fs_mode(cx, argv[2], 0666); if (md == (mode_t) -1) { return JS_EXCEPTION; } fd = open(path, flags, md); if (fd < 0) { result = qjs_fs_error(cx, "open", strerror(errno), path, errno); goto done; } if (calltype == QJS_FS_DIRECT) { /* Leaks fd if user does not close it. */ result = JS_NewInt32(cx, fd); } else { result = JS_NewObjectClass(cx, QJS_CORE_CLASS_ID_FS_FILEHANDLE); if (JS_IsException(result)) { (void) close(fd); goto done; } JS_SetOpaque(result, (void *) (intptr_t) fd); } done: if (JS_IsException(result)) { return JS_EXCEPTION; } return qjs_fs_result(cx, result, calltype, JS_UNDEFINED); } static JSValue qjs_fs_fd_read(JSContext *cx, int fd, njs_str_t *data) { u_char *p, *end, *start; size_t size; ssize_t n; size = data->length; if (size == 0) { size = 4096; } data->start = js_malloc(cx, size); if (data->start == NULL) { JS_ThrowOutOfMemory(cx); return JS_EXCEPTION; } p = data->start; end = p + size; for ( ;; ) { n = read(fd, p, end - p); if (n < 0) { js_free(cx, data->start); return JS_FALSE; } p += n; if (n == 0) { break; } if (end - p < 2048) { size *= 2; start = js_realloc(cx, data->start, size); if (start == NULL) { js_free(cx, data->start); JS_ThrowOutOfMemory(cx); return JS_EXCEPTION; } p = start + (p - data->start); end = start + size; data->start = start; } } data->length = p - data->start; return JS_TRUE; } static JSValue qjs_fs_read(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int calltype) { int fd, fd_offset; int64_t length, pos, offset; ssize_t n; JSValue ret, result; njs_str_t buffer; /* * fh.read(buffer, offset[, length[, position]]) * fs.readSync(fd, buffer, offset[, length[, position]]) */ if (calltype == QJS_FS_DIRECT) { fd_offset = 0; if (JS_ToInt32(cx, &fd, argv[0]) < 0) { return JS_EXCEPTION; } } else { fd_offset = -1; if (JS_ToInt32(cx, &fd, this_val) < 0) { return JS_EXCEPTION; } } ret = qjs_typed_array_data(cx, argv[fd_offset + 1], &buffer); if (JS_IsException(ret)) { return ret; } if (JS_ToInt64(cx, &offset, argv[fd_offset + 2]) < 0) { return JS_EXCEPTION; } if (offset < 0 || (size_t) offset > buffer.length) { JS_ThrowRangeError(cx, "offset is out of range (must be <= %zu)", buffer.length); return JS_EXCEPTION; } buffer.length -= offset; buffer.start += offset; if (!JS_IsUndefined(argv[fd_offset + 3])) { if (JS_ToInt64(cx, &length, argv[fd_offset + 3]) < 0) { return JS_EXCEPTION; } if (length < 0 || (size_t) length > buffer.length) { JS_ThrowRangeError(cx, "length is out of range (must be <= %zu)", buffer.length); return JS_EXCEPTION; } buffer.length = length; } pos = -1; if (!JS_IsNullOrUndefined(argv[fd_offset + 4])) { if (JS_ToInt64(cx, &pos, argv[fd_offset + 4]) < 0) { return JS_EXCEPTION; } } if (pos == -1) { n = read(fd, buffer.start, buffer.length); } else { n = pread(fd, buffer.start, buffer.length, pos); } if (n == -1) { result = qjs_fs_error(cx, "read", strerror(errno), NULL, errno); goto done; } if (calltype == QJS_FS_PROMISE) { result = JS_NewObject(cx); if (JS_IsException(result)) { goto done; } if (JS_DefinePropertyValueStr(cx, result, "bytesRead", JS_NewInt32(cx, n), JS_PROP_ENUMERABLE) < 0) { JS_FreeValue(cx, result); result = JS_EXCEPTION; goto done; } if (JS_DefinePropertyValueStr(cx, result, "buffer", JS_DupValue(cx, argv[fd_offset + 1]), JS_PROP_ENUMERABLE) < 0) { JS_FreeValue(cx, result); result = JS_EXCEPTION; goto done; } } else { result = JS_NewInt32(cx, n); } done: if (JS_IsException(result)) { return JS_EXCEPTION; } return qjs_fs_result(cx, result, calltype, JS_UNDEFINED); } static JSValue qjs_fs_read_file(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int calltype) { int fd, flags; njs_str_t str; JSValue callback, v, encode, options, result; const char *path; struct stat sb; const qjs_buffer_encoding_t *encoding; char path_buf[NJS_MAX_PATH + 1]; path = qjs_fs_path(cx, path_buf, argv[0], "path"); if (path == NULL) { return JS_EXCEPTION; } flags = O_RDONLY; options = argv[1]; encode = JS_UNDEFINED; callback = JS_UNDEFINED; if (calltype == QJS_FS_CALLBACK) { if (argc > 0) { callback = argv[njs_min(argc - 1, 2)]; } if (!JS_IsFunction(cx, callback)) { JS_ThrowTypeError(cx, "\"callback\" must be a function"); return JS_EXCEPTION; } if (JS_SameValue(cx, options, callback)) { options = JS_UNDEFINED; } } if (JS_IsString(options)) { encode = JS_DupValue(cx, options); } else if (!JS_IsUndefined(options)) { if (!JS_IsObject(options)) { JS_ThrowTypeError(cx, "Unknown options type (a string or object " "required)"); return JS_EXCEPTION; } v = JS_GetPropertyStr(cx, options, "flag"); if (!JS_IsUndefined(v) && !JS_IsException(v)) { flags = qjs_fs_flags(cx, v, O_RDONLY); if (flags == -1) { JS_FreeValue(cx, v); return JS_EXCEPTION; } } v = JS_GetPropertyStr(cx, options, "encoding"); if (!JS_IsUndefined(v) && !JS_IsException(v)) { encode = v; } } encoding = NULL; if (!JS_IsUndefined(encode)) { encoding = qjs_buffer_encoding(cx, encode, 1); if (encoding == NULL) { JS_FreeValue(cx, encode); return JS_EXCEPTION; } } JS_FreeValue(cx, encode); fd = open(path, flags); if (fd < 0) { result = qjs_fs_error(cx, "open", strerror(errno), path, errno); goto done; } if (fstat(fd, &sb) == -1) { result = qjs_fs_error(cx, "stat", strerror(errno), path, errno); goto done; } if (!S_ISREG(sb.st_mode)) { result = qjs_fs_error(cx, "stat", "File is not regular", path, 0); goto done; } str.start = NULL; str.length = sb.st_size; v = qjs_fs_fd_read(cx, fd, &str); if (!JS_SameValue(cx, v, JS_TRUE)) { if (JS_IsException(v)) { result = JS_EXCEPTION; } else { result = qjs_fs_error(cx, "read", strerror(errno), path, errno); } goto done; } result = qjs_fs_encode(cx, encoding, &str); js_free(cx, str.start); done: if (fd != -1) { (void) close(fd); } if (JS_IsException(result)) { return JS_EXCEPTION; } return qjs_fs_result(cx, result, calltype, callback); } static JSValue qjs_fs_readlink(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int calltype) { ssize_t n; JSValue callback, v, result, encode, options; njs_str_t str; const char *path, *enc; const qjs_buffer_encoding_t *encoding; char path_buf[NJS_MAX_PATH + 1], dst_buf[NJS_MAX_PATH + 1]; path = qjs_fs_path(cx, path_buf, argv[0], "path"); if (path == NULL) { return JS_EXCEPTION; } options = argv[1]; encode = JS_UNDEFINED; callback = JS_UNDEFINED; if (calltype == QJS_FS_CALLBACK) { callback = argv[njs_min(argc - 1, 2)]; if (!JS_IsFunction(cx, callback)) { JS_ThrowTypeError(cx, "\"callback\" must be a function"); return JS_EXCEPTION; } if (JS_SameValue(cx, options, callback)) { options = JS_UNDEFINED; } } if (JS_IsString(options)) { encode = JS_DupValue(cx, options); } else if (!JS_IsUndefined(options)) { if (!JS_IsObject(options)) { JS_ThrowTypeError(cx, "Unknown options type (a string or object " "required)"); return JS_EXCEPTION; } v = JS_GetPropertyStr(cx, options, "encoding"); if (!JS_IsUndefined(v) && !JS_IsException(v)) { encode = v; } } encoding = NULL; enc = JS_ToCString(cx, encode); if (enc == NULL) { JS_FreeValue(cx, encode); return JS_EXCEPTION; } if (strncmp(enc, "buffer", 6) != 0) { encoding = qjs_buffer_encoding(cx, encode, 1); if (encoding == NULL) { JS_FreeCString(cx, enc); JS_FreeValue(cx, encode); return JS_EXCEPTION; } } JS_FreeCString(cx, enc); JS_FreeValue(cx, encode); str.start = (u_char *) dst_buf; n = readlink(path, dst_buf, sizeof(dst_buf) - 1); if (n < 0) { result = qjs_fs_error(cx, "readlink", strerror(errno), path, errno); goto done; } str.length = n; result = qjs_fs_encode(cx, encoding, &str); done: if (JS_IsException(result)) { return JS_EXCEPTION; } return qjs_fs_result(cx, result, calltype, callback); } static JSValue qjs_fs_dirent_create(JSContext *cx, JSValueConst name, struct dirent *entry) { JSValue obj; obj = JS_NewObjectClass(cx, QJS_CORE_CLASS_ID_FS_DIRENT); if (JS_IsException(obj)) { return JS_EXCEPTION; } if (JS_DefinePropertyValueStr(cx, obj, "name", name, JS_PROP_ENUMERABLE) < 0) { JS_FreeValue(cx, obj); return JS_EXCEPTION; } if (entry != NULL) { if (JS_DefinePropertyValueStr(cx, obj, "type", JS_NewInt32(cx, qjs_dentry_type(entry)), 0) < 0) { JS_FreeValue(cx, obj); return JS_EXCEPTION; } } return obj; } static JSValue qjs_fs_dirent_ctor(JSContext *cx, JSValueConst new_target, int argc, JSValueConst *argv) { if (argc < 1) { JS_ThrowTypeError(cx, "name is required"); return JS_EXCEPTION; } return qjs_fs_dirent_create(cx, JS_DupValue(cx, argv[0]), NULL); } static JSValue qjs_fs_readdir(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int calltype) { DIR *dir; int types, idx; njs_str_t str; JSValue callback, v, result, encode, options, ename; const char *path, *enc; struct dirent *entry; const qjs_buffer_encoding_t *encoding; char path_buf[NJS_MAX_PATH + 1]; path = qjs_fs_path(cx, path_buf, argv[0], "path"); if (path == NULL) { return JS_EXCEPTION; } types = 0; options = argv[1]; encode = JS_UNDEFINED; callback = JS_UNDEFINED; if (calltype == QJS_FS_CALLBACK) { if (argc > 0) { callback = argv[njs_min(argc - 1, 2)]; } if (!JS_IsFunction(cx, callback)) { JS_ThrowTypeError(cx, "\"callback\" must be a function"); return JS_EXCEPTION; } if (JS_SameValue(cx, options, callback)) { options = JS_UNDEFINED; } } if (JS_IsString(options)) { encode = JS_DupValue(cx, options); } else if (!JS_IsUndefined(options)) { if (!JS_IsObject(options)) { JS_ThrowTypeError(cx, "Unknown options type (a string or object " "required)"); return JS_EXCEPTION; } v = JS_GetPropertyStr(cx, options, "encoding"); if (!JS_IsUndefined(v) && !JS_IsException(v)) { encode = v; } v = JS_GetPropertyStr(cx, options, "withFileTypes"); if (!JS_IsUndefined(v) && !JS_IsException(v)) { types = JS_ToBool(cx, v); } } encoding = NULL; enc = JS_ToCString(cx, encode); if (enc == NULL) { JS_FreeValue(cx, encode); return JS_EXCEPTION; } if (strncmp(enc, "buffer", 6) != 0) { encoding = qjs_buffer_encoding(cx, encode, 1); if (encoding == NULL) { JS_FreeCString(cx, enc); JS_FreeValue(cx, encode); return JS_EXCEPTION; } } JS_FreeCString(cx, enc); JS_FreeValue(cx, encode); idx = 0; dir = opendir(path); if (dir == NULL) { result = qjs_fs_error(cx, "opendir", strerror(errno), path, errno); goto done; } result = JS_NewArray(cx); if (JS_IsException(result)) { return JS_EXCEPTION; } for ( ;; ) { errno = 0; entry = readdir(dir); if (entry == NULL) { if (errno != 0) { JS_FreeValue(cx, result); result = qjs_fs_error(cx, "readdir", strerror(errno), path, errno); } break; } if (entry->d_name[0] == '.' && (entry->d_name[1] == '\0' || (entry->d_name[1] == '.' && entry->d_name[2] == '\0'))) { continue; } str.start = (u_char *) entry->d_name; str.length = strlen((const char *) str.start); if (str.length == 0) { continue; } ename = qjs_fs_encode(cx, encoding, &str); if (JS_IsException(ename)) { JS_FreeValue(cx, result); goto done; } if (!types) { if (JS_DefinePropertyValueUint32(cx, result, idx++, ename, 0) < 0) { JS_FreeValue(cx, ename); JS_FreeValue(cx, result); goto done; } } else { v = qjs_fs_dirent_create(cx, ename, entry); if (JS_IsException(v)) { JS_FreeValue(cx, ename); JS_FreeValue(cx, result); goto done; } if (JS_DefinePropertyValueUint32(cx, result, idx++, v, 0) < 0) { JS_FreeValue(cx, ename); JS_FreeValue(cx, result); goto done; } } } done: if (dir != NULL) { (void) closedir(dir); } if (JS_IsException(result)) { return JS_EXCEPTION; } return qjs_fs_result(cx, result, calltype, callback); } static JSValue qjs_fs_realpath(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int calltype) { JSValue callback, v, encode, options, result; njs_str_t str; const char *path, *enc; const qjs_buffer_encoding_t *encoding; char path_buf[NJS_MAX_PATH + 1], dst_buf[NJS_MAX_PATH + 1]; path = qjs_fs_path(cx, path_buf, argv[0], "path"); if (path == NULL) { return JS_EXCEPTION; } options = argv[1]; encode = JS_UNDEFINED; callback = JS_UNDEFINED; if (calltype == QJS_FS_CALLBACK) { if (argc > 0) { callback = argv[njs_min(argc - 1, 2)]; } if (!JS_IsFunction(cx, callback)) { JS_ThrowTypeError(cx, "\"callback\" must be a function"); return JS_EXCEPTION; } if (JS_SameValue(cx, options, callback)) { options = JS_UNDEFINED; } } if (JS_IsString(options)) { encode = JS_DupValue(cx, options); } else if (!JS_IsUndefined(options)) { if (!JS_IsObject(options)) { JS_ThrowTypeError(cx, "Unknown options type (a string or object " "required)"); return JS_EXCEPTION; } v = JS_GetPropertyStr(cx, options, "encoding"); if (!JS_IsUndefined(v) && !JS_IsException(v)) { encode = v; } } enc = JS_ToCString(cx, encode); if (enc == NULL) { JS_FreeValue(cx, encode); return JS_EXCEPTION; } encoding = NULL; if (strncmp(enc, "buffer", 6) != 0) { encoding = qjs_buffer_encoding(cx, encode, 1); if (encoding == NULL) { JS_FreeCString(cx, enc); JS_FreeValue(cx, encode); return JS_EXCEPTION; } } JS_FreeCString(cx, enc); JS_FreeValue(cx, encode); str.start = (u_char *) realpath(path, dst_buf); if (str.start == NULL) { result = qjs_fs_error(cx, "realpath", strerror(errno), path, errno); goto done; } str.length = strlen((const char *) str.start); result = qjs_fs_encode(cx, encoding, &str); done: if (JS_IsException(result)) { return JS_EXCEPTION; } return qjs_fs_result(cx, result, calltype, callback); } static JSValue qjs_fs_rename(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int calltype) { int ret; JSValue callback, result; const char *path, *newpath; char path_buf[NJS_MAX_PATH + 1], newpath_buf[NJS_MAX_PATH + 1]; path = qjs_fs_path(cx, path_buf, argv[0], "oldPath"); if (path == NULL) { return JS_EXCEPTION; } newpath = qjs_fs_path(cx, newpath_buf, argv[1], "newPath"); if (newpath == NULL) { return JS_EXCEPTION; } callback = JS_UNDEFINED; if (calltype == QJS_FS_CALLBACK) { callback = argv[2]; if (!JS_IsFunction(cx, callback)) { JS_ThrowTypeError(cx, "\"callback\" must be a function"); return JS_EXCEPTION; } } result = JS_UNDEFINED; ret = rename(path, newpath); if (ret != 0) { result = qjs_fs_error(cx, "rename", strerror(errno), NULL, errno); } if (JS_IsException(result)) { return JS_EXCEPTION; } return qjs_fs_result(cx, result, calltype, callback); } typedef struct qjs_ftw_trace_s qjs_ftw_trace_t; struct qjs_ftw_trace_s { struct qjs_ftw_trace_s *chain; dev_t dev; ino_t ino; }; static int qjs_ftw(char *path, qjs_file_tree_walk_cb_t cb, int fd_limit, qjs_ftw_flags_t flags, qjs_ftw_trace_t *parent) { int type, ret, dfd; DIR *d; size_t base, len, length; const char *d_name; struct stat st; struct dirent *entry; qjs_ftw_trace_t trace, *h; ret = (flags & QJS_FTW_PHYS) ? lstat(path, &st) : stat(path, &st); if (ret < 0) { if (!(flags & QJS_FTW_PHYS) && errno == ENOENT && !lstat(path, &st)) { type = QJS_FTW_SLN; } else if (errno != EACCES) { return -1; } else { type = QJS_FTW_NS; } } else if (S_ISDIR(st.st_mode)) { type = (flags & QJS_FTW_DEPTH) ? QJS_FTW_DP : QJS_FTW_D; } else if (S_ISLNK(st.st_mode)) { type = (flags & QJS_FTW_PHYS) ? QJS_FTW_SL : QJS_FTW_SLN; } else { type = QJS_FTW_F; } if ((flags & QJS_FTW_MOUNT) && parent != NULL && st.st_dev != parent->dev) { return 0; } for (h = parent; h != NULL; h = h->chain) { if (h->dev == st.st_dev && h->ino == st.st_ino) { return 0; } } len = strlen(path); base = len && (path[len - 1] == '/') ? len - 1 : len; trace.chain = parent; trace.dev = st.st_dev; trace.ino = st.st_ino; d = NULL; dfd = -1; if (type == QJS_FTW_D || type == QJS_FTW_DP) { dfd = open(path, O_RDONLY); if (dfd < 0) { if (errno != EACCES) { return -1; } type = QJS_FTW_DNR; } } if (!(flags & QJS_FTW_DEPTH)) { ret = cb(path, &st, type); if (ret != 0) { goto done; } } if (type == QJS_FTW_D || type == QJS_FTW_DP) { d = fdopendir(dfd); if (d == NULL) { ret = -1; goto done; } for ( ;; ) { entry = readdir(d); if (entry == NULL) { break; } d_name = entry->d_name; length = strlen(d_name); if ((length == 1 && d_name[0] == '.') || (length == 2 && (d_name[0] == '.' && d_name[1] == '.'))) { continue; } if (length >= (NJS_MAX_PATH - len)) { errno = ENAMETOOLONG; ret = -1; goto done; } path[base] = '/'; memcpy(&path[base + 1], d_name, length + njs_length("\0")); if (fd_limit != 0) { ret = qjs_ftw(path, cb, fd_limit - 1, flags, &trace); if (ret != 0) { goto done; } } } (void) closedir(d); d = NULL; dfd = -1; } path[len] = '\0'; if (flags & QJS_FTW_DEPTH) { ret = cb(path, &st, type); if (ret != 0) { return ret; } } ret = 0; done: if (d != NULL) { /* closedir() also closes underlying dfd. */ (void) closedir(d); } else if (dfd >= 0) { (void) close(dfd); } return ret; } static int qjs_file_tree_walk(const char *path, qjs_file_tree_walk_cb_t cb, int fd_limit, qjs_ftw_flags_t flags) { size_t len; char pathbuf[NJS_MAX_PATH + 1]; len = strlen(path); if (len > NJS_MAX_PATH) { errno = ENAMETOOLONG; return -1; } memcpy(pathbuf, path, len + 1); return qjs_ftw(pathbuf, cb, fd_limit, flags, NULL); } static int qjs_fs_rmtree_cb(const char *path, const struct stat *sb, qjs_ftw_type_t type) { njs_int_t ret; ret = remove(path); if (ret != 0) { return NJS_ERROR; } return NJS_OK; } static JSValue qjs_fs_rmtree(JSContext *cx, const char *path, int recursive) { njs_int_t ret; const char *description; ret = rmdir(path); if (ret == 0) { return JS_UNDEFINED; } description = strerror(errno); if (recursive && (errno == ENOTEMPTY || errno == EEXIST)) { ret = qjs_file_tree_walk(path, qjs_fs_rmtree_cb, 16, QJS_FTW_PHYS | QJS_FTW_MOUNT | QJS_FTW_DEPTH); if (ret == 0) { return JS_UNDEFINED; } description = strerror(errno); } return qjs_fs_error(cx, "rmdir", description, path, errno); } static JSValue qjs_fs_rmdir(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int calltype) { int recursive; JSValue callback, v, options, result; const char *path; char path_buf[NJS_MAX_PATH + 1]; path = qjs_fs_path(cx, path_buf, argv[0], "path"); if (path == NULL) { return JS_EXCEPTION; } recursive = 0; options = argv[1]; callback = JS_UNDEFINED; if (calltype == QJS_FS_CALLBACK) { callback = argv[njs_min(argc - 1, 2)]; if (!JS_IsFunction(cx, callback)) { JS_ThrowTypeError(cx, "\"callback\" must be a function"); return JS_EXCEPTION; } if (JS_SameValue(cx, options, callback)) { options = JS_UNDEFINED; } } if (!JS_IsUndefined(options)) { if (!JS_IsObject(options)) { JS_ThrowTypeError(cx, "Unknown options type (an object required)"); return JS_EXCEPTION; } v = JS_GetPropertyStr(cx, options, "recursive"); if (!JS_IsUndefined(v) && !JS_IsException(v)) { recursive = JS_ToBool(cx, v); } } result = qjs_fs_rmtree(cx, path, recursive); if (JS_IsException(result)) { return JS_EXCEPTION; } return qjs_fs_result(cx, result, calltype, callback); } static void qjs_fs_to_stat(qjs_stat_t *dst, struct stat *st) { dst->st_dev = st->st_dev; dst->st_mode = st->st_mode; dst->st_nlink = st->st_nlink; dst->st_uid = st->st_uid; dst->st_gid = st->st_gid; dst->st_rdev = st->st_rdev; dst->st_ino = st->st_ino; dst->st_size = st->st_size; dst->st_blksize = st->st_blksize; dst->st_blocks = st->st_blocks; #if (NJS_HAVE_STAT_ATIMESPEC) dst->st_atim.tv_sec = st->st_atimespec.tv_sec; dst->st_atim.tv_nsec = st->st_atimespec.tv_nsec; dst->st_mtim.tv_sec = st->st_mtimespec.tv_sec; dst->st_mtim.tv_nsec = st->st_mtimespec.tv_nsec; dst->st_ctim.tv_sec = st->st_ctimespec.tv_sec; dst->st_ctim.tv_nsec = st->st_ctimespec.tv_nsec; #elif (NJS_HAVE_STAT_ATIM) dst->st_atim.tv_sec = st->st_atim.tv_sec; dst->st_atim.tv_nsec = st->st_atim.tv_nsec; dst->st_mtim.tv_sec = st->st_mtim.tv_sec; dst->st_mtim.tv_nsec = st->st_mtim.tv_nsec; dst->st_ctim.tv_sec = st->st_ctim.tv_sec; dst->st_ctim.tv_nsec = st->st_ctim.tv_nsec; #if (NJS_HAVE_STAT_BIRTHTIM) dst->st_birthtim.tv_sec = st->st_birthtim.tv_sec; dst->st_birthtim.tv_nsec = st->st_birthtim.tv_nsec; #elif (NJS_HAVE__STAT_BIRTHTIM) dst->st_birthtim.tv_sec = st->__st_birthtim.tv_sec; dst->st_birthtim.tv_nsec = st->__st_birthtim.tv_nsec; #else dst->st_birthtim.tv_sec = st->st_ctim.tv_sec; dst->st_birthtim.tv_nsec = st->st_ctim.tv_nsec; #endif #else dst->st_atim.tv_sec = st->st_atime; dst->st_atim.tv_nsec = 0; dst->st_mtim.tv_sec = st->st_mtime; dst->st_mtim.tv_nsec = 0; dst->st_ctim.tv_sec = st->st_ctime; dst->st_ctim.tv_nsec = 0; dst->st_birthtim.tv_sec = st->st_ctime; dst->st_birthtim.tv_nsec = 0; #endif } static JSValue qjs_fs_stats_create(JSContext *cx, struct stat *st) { JSValue obj; qjs_stat_t *stat; stat = js_malloc(cx, sizeof(qjs_stat_t)); if (stat == NULL) { JS_ThrowOutOfMemory(cx); return JS_EXCEPTION; } qjs_fs_to_stat(stat, st); obj = JS_NewObjectClass(cx, QJS_CORE_CLASS_ID_FS_STATS); if (JS_IsException(obj)) { js_free(cx, stat); return JS_EXCEPTION; } JS_SetOpaque(obj, stat); return obj; } static JSValue qjs_fs_stat(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) { int ret, fd, fd_offset, throw, calltype; JSValue callback, result, options, value; const char *path; struct stat sb; char path_buf[NJS_MAX_PATH + 1]; fd = -1; path = NULL; calltype = magic & 3; if ((magic >> 2) != QJS_FS_FSTAT) { path = qjs_fs_path(cx, path_buf, argv[0], "path"); if (path == NULL) { return JS_EXCEPTION; } options = argv[1]; } else { if (calltype == QJS_FS_DIRECT) { fd_offset = 0; if (JS_ToInt32(cx, &fd, argv[fd_offset]) < 0) { return JS_EXCEPTION; } } else { fd_offset = -1; if (JS_ToInt32(cx, &fd, this_val) < 0) { return JS_EXCEPTION; } } options = argv[fd_offset + 1]; } callback = JS_UNDEFINED; if (calltype == QJS_FS_CALLBACK) { if (argc > 0) { callback = argv[njs_min(argc - 1, 2)]; } if (!JS_IsFunction(cx, callback)) { JS_ThrowTypeError(cx, "\"callback\" must be a function"); return JS_EXCEPTION; } if (JS_SameValue(cx, options, callback)) { options = JS_UNDEFINED; } } throw = 1; if (!JS_IsUndefined(options)) { if (!JS_IsObject(options)) { JS_ThrowTypeError(cx, "Unknown options type (an object required)"); return JS_EXCEPTION; } value = JS_GetPropertyStr(cx, options, "bigint"); if (!JS_IsUndefined(value)) { JS_ThrowTypeError(cx, "\"bigint\" is not supported"); return JS_EXCEPTION; } if (calltype == QJS_FS_DIRECT) { value = JS_GetPropertyStr(cx, options, "throwIfNoEntry"); if (!JS_IsUndefined(value)) { throw = JS_ToBool(cx, value); } } } switch (magic >> 2) { case QJS_FS_STAT: ret = stat(path, &sb); break; case QJS_FS_LSTAT: ret = lstat(path, &sb); break; case QJS_FS_FSTAT: default: ret = fstat(fd, &sb); break; } if (ret != 0) { if (errno != ENOENT || throw) { result = qjs_fs_error(cx, ((magic >> 2) == QJS_FS_STAT) ? "stat" : "lstat", strerror(errno), path, errno); if (JS_IsException(result)) { return JS_EXCEPTION; } } else { result = JS_UNDEFINED; } return qjs_fs_result(cx, result, calltype, callback); } result = qjs_fs_stats_create(cx, &sb); if (JS_IsException(result)) { return JS_EXCEPTION; } return qjs_fs_result(cx, result, calltype, callback); } static JSValue qjs_fs_symlink(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int calltype) { int ret; JSValue callback, result, type; const char *target, *path; char target_buf[NJS_MAX_PATH + 1], path_buf[NJS_MAX_PATH + 1]; target = qjs_fs_path(cx, target_buf, argv[0], "target"); if (target == NULL) { return JS_EXCEPTION; } path = qjs_fs_path(cx, path_buf, argv[1], "path"); if (path == NULL) { return JS_EXCEPTION; } callback = JS_UNDEFINED; type = argv[2]; if (calltype == QJS_FS_CALLBACK) { callback = argv[njs_min(argc - 1, 3)]; if (!JS_IsFunction(cx, callback)) { JS_ThrowTypeError(cx, "\"callback\" must be a function"); return JS_EXCEPTION; } if (JS_SameValue(cx, type, callback)) { type = JS_UNDEFINED; } } if (!JS_IsUndefined(type) && !JS_IsString(type)) { JS_ThrowTypeError(cx, "\"type\" must be a string"); return JS_EXCEPTION; } result = JS_UNDEFINED; ret = symlink(target, path); if (ret != 0) { result = qjs_fs_error(cx, "symlink", strerror(errno), path, errno); } if (JS_IsException(result)) { return JS_EXCEPTION; } return qjs_fs_result(cx, result, calltype, callback); } static JSValue qjs_fs_write(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int calltype) { int fd, fd_offset; u_char *to_free_content; int64_t length, pos, offset; ssize_t n; JSValue buffer, v, result; njs_str_t str, content; const qjs_buffer_encoding_t *encoding; if (calltype == QJS_FS_DIRECT) { fd_offset = 0; if (JS_ToInt32(cx, &fd, argv[0]) < 0) { return JS_EXCEPTION; } } else { fd_offset = -1; if (JS_ToInt32(cx, &fd, this_val) < 0) { return JS_EXCEPTION; } } pos = -1; encoding = NULL; str.start = NULL; to_free_content = NULL; buffer = argv[fd_offset + 1]; /* * fs.writeSync(fd, string[, position[, encoding]]) * fh.write(string[, position[, encoding]]) */ if (JS_IsString(buffer)) { v = argv[fd_offset + 2]; if (!JS_IsNullOrUndefined(v)) { if (JS_ToInt64(cx, &pos, v) < 0) { return JS_EXCEPTION; } } encoding = qjs_buffer_encoding(cx, argv[fd_offset + 3], 1); if (encoding == NULL) { return JS_EXCEPTION; } str.start = (u_char *) JS_ToCStringLen(cx, &str.length, buffer); if (str.start == NULL) { return JS_EXCEPTION; } if (encoding->decode_length != NULL) { content.length = encoding->decode_length(cx, &str); content.start = js_malloc(cx, content.length); if (content.start == NULL) { JS_FreeCString(cx, (const char *) str.start); JS_ThrowOutOfMemory(cx); return JS_EXCEPTION; } to_free_content = content.start; if (encoding->decode(cx, &str, &content) != 0) { JS_FreeCString(cx, (const char *) str.start); return JS_EXCEPTION; } } else { content.start = (u_char *) str.start; content.length = str.length; } goto process; } /* * fh.write(buffer, offset[, length[, position]]) * fs.writeSync(fd, buffer, offset[, length[, position]]) */ v = qjs_typed_array_data(cx, buffer, &content); if (JS_IsException(v)) { return JS_EXCEPTION; } if (JS_ToInt64(cx, &offset, argv[fd_offset + 2]) < 0) { return JS_EXCEPTION; } if (offset < 0 || (size_t) offset > content.length) { JS_ThrowRangeError(cx, "offset is out of range (must be <= %zu)", content.length); return JS_EXCEPTION; } content.length -= offset; content.start += offset; v = argv[fd_offset + 3]; if (!JS_IsNullOrUndefined(v)) { if (JS_ToInt64(cx, &length, v) < 0) { return JS_EXCEPTION; } if (length < 0 || (size_t) length > content.length) { JS_ThrowRangeError(cx, "length is out of range (must be <= %zu)", content.length); return JS_EXCEPTION; } content.length = length; } v = argv[fd_offset + 4]; if (!JS_IsNullOrUndefined(v)) { if (JS_ToInt64(cx, &pos, v) < 0) { return JS_EXCEPTION; } } process: if (pos == -1) { n = write(fd, content.start, content.length); } else { n = pwrite(fd, content.start, content.length, pos); } if (n == -1) { result = qjs_fs_error(cx, "write", strerror(errno), NULL, errno); goto done; } if ((size_t) n != content.length) { result = qjs_fs_error(cx, "write", "failed to write all the data", NULL, 0); goto done; } if (calltype == QJS_FS_PROMISE) { result = JS_NewObject(cx); if (JS_IsException(result)) { goto done; } if (JS_DefinePropertyValueStr(cx, result, "bytesWritten", JS_NewInt32(cx, n), JS_PROP_C_W_E) < 0) { JS_FreeValue(cx, result); result = JS_EXCEPTION; goto done; } buffer = JS_DupValue(cx, buffer); if (JS_DefinePropertyValueStr(cx, result, "buffer", buffer, JS_PROP_C_W_E) < 0) { JS_FreeValue(cx, result); JS_FreeValue(cx, buffer); result = JS_EXCEPTION; goto done; } } else { result = JS_NewInt32(cx, n); } done: if (str.start != NULL) { JS_FreeCString(cx, (const char *) str.start); } if (to_free_content != NULL) { js_free(cx, to_free_content); } return qjs_fs_result(cx, result, calltype, JS_UNDEFINED); } static JSValue qjs_fs_write_file(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) { int fd, flags, want_to_free_content; u_char *p, *end; mode_t md; ssize_t n; JSValue callback, data, v, encode, options, result; njs_str_t str, content; const char *path; qjs_fs_calltype_t calltype; const qjs_buffer_encoding_t *encoding; char path_buf[NJS_MAX_PATH + 1]; path = qjs_fs_path(cx, path_buf, argv[0], "path"); if (path == NULL) { return JS_EXCEPTION; } md = 0666; flags = O_CREAT | O_WRONLY; flags |= ((magic >> 2) == QJS_FS_APPEND) ? O_APPEND : O_TRUNC; calltype = magic & 3; encode = JS_UNDEFINED; callback = JS_UNDEFINED; options = argv[2]; if (calltype == QJS_FS_CALLBACK) { if (argc > 0) { callback = argv[njs_min(argc - 1, 3)]; } if (!JS_IsFunction(cx, callback)) { JS_ThrowTypeError(cx, "\"callback\" must be a function"); return JS_EXCEPTION; } if (JS_SameValue(cx, options, callback)) { options = JS_UNDEFINED; } } if (JS_IsString(options)) { encode = JS_DupValue(cx, options); } else if (!JS_IsUndefined(options)) { if (!JS_IsObject(options)) { JS_ThrowTypeError(cx, "Unknown options type (a string or object " "required)"); return JS_EXCEPTION; } v = JS_GetPropertyStr(cx, options, "flag"); if (!JS_IsUndefined(v) && !JS_IsException(v)) { flags = qjs_fs_flags(cx, v, O_CREAT | O_WRONLY); if (flags == -1) { JS_FreeValue(cx, v); return JS_EXCEPTION; } } v = JS_GetPropertyStr(cx, options, "mode"); if (!JS_IsUndefined(v) && !JS_IsException(v)) { md = qjs_fs_mode(cx, v, 0666); if (md == (mode_t) -1) { JS_FreeValue(cx, v); return JS_EXCEPTION; } } v = JS_GetPropertyStr(cx, options, "encoding"); if (!JS_IsUndefined(v) && !JS_IsException(v)) { encode = v; } } encoding = qjs_buffer_encoding(cx, encode, 1); if (encoding == NULL) { JS_FreeValue(cx, encode); return JS_EXCEPTION; } JS_FreeValue(cx, encode); data = argv[1]; str.start = NULL; want_to_free_content = 0; if (JS_IsString(data)) { goto decode; } v = qjs_typed_array_data(cx, data, &content); if (JS_IsException(v)) { decode: str.start = (u_char *) JS_ToCStringLen(cx, &str.length, data); if (str.start == NULL) { return JS_EXCEPTION; } if (encoding->decode_length != NULL) { content.length = encoding->decode_length(cx, &str); content.start = js_malloc(cx, content.length); if (content.start == NULL) { JS_FreeCString(cx, (const char *) str.start); JS_ThrowOutOfMemory(cx); return JS_EXCEPTION; } want_to_free_content = 1; if (encoding->decode(cx, &str, &content) != 0) { JS_FreeCString(cx, (const char *) str.start); return JS_EXCEPTION; } } else { content.start = (u_char *) str.start; content.length = str.length; } } fd = open(path, flags, md); if (fd < 0) { result = qjs_fs_error(cx, "open", strerror(errno), path, errno); goto done; } p = content.start; end = p + content.length; while (p < end) { n = write(fd, p, end - p); if (n == -1) { if (errno == EINTR) { continue; } result = qjs_fs_error(cx, "write", strerror(errno), path, errno); goto done; } p += n; } result = JS_UNDEFINED; done: if (fd != -1) { (void) close(fd); } if (str.start != NULL) { JS_FreeCString(cx, (const char *) str.start); } if (want_to_free_content) { js_free(cx, content.start); } if (JS_IsException(result)) { return JS_EXCEPTION; } return qjs_fs_result(cx, result, calltype, callback); } static JSValue qjs_fs_unlink(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int calltype) { int ret; JSValue callback, result; const char *path; char path_buf[NJS_MAX_PATH + 1]; path = qjs_fs_path(cx, path_buf, argv[0], "path"); if (path == NULL) { return JS_EXCEPTION; } callback = JS_UNDEFINED; if (calltype == QJS_FS_CALLBACK) { callback = argv[1]; if (!JS_IsFunction(cx, callback)) { JS_ThrowTypeError(cx, "\"callback\" must be a function"); return JS_EXCEPTION; } } result = JS_UNDEFINED; ret = unlink(path); if (ret != 0) { result = qjs_fs_error(cx, "unlink", strerror(errno), path, errno); } if (JS_IsException(result)) { return JS_EXCEPTION; } return qjs_fs_result(cx, result, calltype, callback); } static JSValue qjs_fs_stats_to_string_tag(JSContext *cx, JSValueConst this_val) { return JS_NewString(cx, "Stats"); } static JSValue qjs_fs_stats_test(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int testtype) { unsigned mask; qjs_stat_t *st; st = JS_GetOpaque2(cx, this_val, QJS_CORE_CLASS_ID_FS_STATS); if (st == NULL) { return JS_EXCEPTION; } switch (testtype) { case DT_DIR: mask = S_IFDIR; break; case DT_REG: mask = S_IFREG; break; case DT_CHR: mask = S_IFCHR; break; case DT_LNK: mask = S_IFLNK; break; case DT_BLK: mask = S_IFBLK; break; case DT_FIFO: mask = S_IFIFO; break; case DT_SOCK: default: mask = S_IFSOCK; } return JS_NewBool(cx, (st->st_mode & S_IFMT) == mask); } static int qjs_fs_stats_get_own_property(JSContext *cx, JSPropertyDescriptor *pdesc, JSValueConst obj, JSAtom prop) { JSValue value; njs_str_t name; qjs_stat_t *st; #define qjs_fs_time_ms(ts) ((ts)->tv_sec * 1000.0 + (ts)->tv_nsec / 1000000.0) st = JS_GetOpaque2(cx, obj, QJS_CORE_CLASS_ID_FS_STATS); if (st == NULL) { (void) JS_ThrowInternalError(cx, "\"this\" is not a Stats object"); return -1; } name.start = (u_char *) JS_AtomToCString(cx, prop); if (name.start == NULL) { return -1; } name.length = strlen((const char *) name.start); if (name.length < 3) { JS_FreeCString(cx, (const char *) name.start); return 0; } switch (name.start[0]) { case 'a': if (name.length == 5 && memcmp(name.start, "atime", 5) == 0) { value = JS_NewDate(cx, qjs_fs_time_ms(&st->st_atim)); goto done; } if (name.length == 7 && memcmp(name.start, "atimeMs", 7) == 0) { value = JS_NewFloat64(cx, qjs_fs_time_ms(&st->st_atim)); goto done; } break; case 'b': if (name.length == 6 && memcmp(name.start, "blocks", 6) == 0) { value = JS_NewFloat64(cx, st->st_blocks); goto done; } if (name.length == 7 && memcmp(name.start, "blksize", 7) == 0) { value = JS_NewFloat64(cx, st->st_blksize); goto done; } if (name.length == 9 && memcmp(name.start, "birthtime", 9) == 0) { value = JS_NewDate(cx, qjs_fs_time_ms(&st->st_birthtim)); goto done; } if (name.length == 11 && memcmp(name.start, "birthtimeMs", 11) == 0) { value = JS_NewFloat64(cx, qjs_fs_time_ms(&st->st_birthtim)); goto done; } break; case 'c': if (name.length == 5 && memcmp(name.start, "ctime", 5) == 0) { value = JS_NewDate(cx, qjs_fs_time_ms(&st->st_ctim)); goto done; } if (name.length == 7 && memcmp(name.start, "ctimeMs", 7) == 0) { value = JS_NewFloat64(cx, qjs_fs_time_ms(&st->st_ctim)); goto done; } break; case 'd': if (name.length == 3 && memcmp(name.start, "dev", 3) == 0) { value = JS_NewFloat64(cx, st->st_dev); goto done; } break; case 'g': if (name.length == 3 && memcmp(name.start, "gid", 3) == 0) { value = JS_NewFloat64(cx, st->st_gid); goto done; } break; case 'i': if (name.length == 3 && memcmp(name.start, "ino", 3) == 0) { value = JS_NewFloat64(cx, st->st_ino); goto done; } break; case 'm': if (name.length == 4 && memcmp(name.start, "mode", 4) == 0) { value = JS_NewFloat64(cx, st->st_mode); goto done; } if (name.length == 5 && memcmp(name.start, "mtime", 5) == 0) { value = JS_NewDate(cx, qjs_fs_time_ms(&st->st_mtim)); goto done; } if (name.length == 7 && memcmp(name.start, "mtimeMs", 7) == 0) { value = JS_NewFloat64(cx, qjs_fs_time_ms(&st->st_mtim)); goto done; } break; case 'n': if (name.length == 5 && memcmp(name.start, "nlink", 5) == 0) { value = JS_NewFloat64(cx, st->st_nlink); goto done; } break; case 'r': if (name.length == 4 && memcmp(name.start, "rdev", 4) == 0) { value = JS_NewFloat64(cx, st->st_rdev); goto done; } break; case 's': if (name.length == 4 && memcmp(name.start, "size", 4) == 0) { value = JS_NewFloat64(cx, st->st_size); goto done; } break; case 'u': if (name.length == 3 && memcmp(name.start, "uid", 3) == 0) { value = JS_NewFloat64(cx, st->st_uid); goto done; } break; } JS_FreeCString(cx, (const char *) name.start); return 0; done: JS_FreeCString(cx, (const char *) name.start); if (pdesc != NULL) { pdesc->flags = JS_PROP_ENUMERABLE | JS_PROP_CONFIGURABLE; pdesc->getter = JS_UNDEFINED; pdesc->setter = JS_UNDEFINED; pdesc->value = value; } return 1; } static int qjs_fs_stats_get_own_property_names(JSContext *cx, JSPropertyEnum **ptab, uint32_t *plen, JSValueConst obj) { int ret; JSValue keys; unsigned i; static const char *stat_props[] = { "atime", "atimeMs", "birthtime", "birthtimeMs", "blksize", "blocks", "ctime", "ctimeMs", "dev", "gid", "ino", "mode", "mtime", "mtimeMs", "nlink", "size", "rdev", "uid", }; keys = JS_NewObject(cx); if (JS_IsException(keys)) { return -1; } for (i = 0; i < njs_nitems(stat_props); i++) { if (JS_DefinePropertyValueStr(cx, keys, stat_props[i], JS_UNDEFINED, JS_PROP_C_W_E) < 0) { JS_FreeValue(cx, keys); return -1; } } ret = JS_GetOwnPropertyNames(cx, ptab, plen, keys, JS_GPN_STRING_MASK); JS_FreeValue(cx, keys); return ret; } static void qjs_fs_stats_finalizer(JSRuntime *rt, JSValue val) { qjs_stat_t *stat; stat = JS_GetOpaque(val, QJS_CORE_CLASS_ID_FS_STATS); if (stat != NULL) { js_free_rt(rt, stat); } } static JSValue qjs_fs_dirent_to_string_tag(JSContext *cx, JSValueConst this_val) { return JS_NewString(cx, "Dirent"); } static JSValue qjs_fs_dirent_test(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int testtype) { int value; JSValue type; type = JS_GetPropertyStr(cx, this_val, "type"); if (JS_IsException(type)) { return JS_EXCEPTION; } if (JS_VALUE_GET_TAG(type) != JS_TAG_INT) { JS_FreeValue(cx, type); return JS_FALSE; } value = JS_VALUE_GET_INT(type); JS_FreeValue(cx, type); if (value == QJS_DT_INVALID) { JS_ThrowInternalError(cx, "dentry type is not supported on this " "platform"); return JS_EXCEPTION; } return JS_NewBool(cx, testtype == value); } static JSValue qjs_fs_filehandle_to_string_tag(JSContext *cx, JSValueConst this_val) { return JS_NewString(cx, "FileHandle"); } static JSValue qjs_fs_filehandle_fd(JSContext *cx, JSValueConst thisval) { int fd; fd = (intptr_t) JS_GetOpaque2(cx, thisval, QJS_CORE_CLASS_ID_FS_FILEHANDLE); if (fd == -1) { return JS_ThrowTypeError(cx, "file was already closed"); } return JS_NewInt32(cx, fd); } static JSValue qjs_fs_filehandle_value_of(JSContext *cx, JSValueConst thisval, int argc, JSValueConst *argv) { int fd; fd = (intptr_t) JS_GetOpaque2(cx, thisval, QJS_CORE_CLASS_ID_FS_FILEHANDLE); if (fd == -1) { return JS_ThrowTypeError(cx, "file was already closed"); } return JS_NewInt32(cx, fd); } static void qjs_fs_filehandle_finalizer(JSRuntime *rt, JSValue val) { int fd; fd = (intptr_t) JS_GetOpaque(val, QJS_CORE_CLASS_ID_FS_FILEHANDLE); (void) close(fd); } static JSValue qjs_fs_promise_trampoline(JSContext *cx, int argc, JSValueConst *argv) { return JS_Call(cx, argv[0], JS_UNDEFINED, 1, &argv[1]); } static JSValue qjs_fs_result(JSContext *cx, JSValue result, int calltype, JSValue callback) { JS_BOOL is_error; JSValue promise, callbacks[2], arguments[2]; switch (calltype) { case QJS_FS_DIRECT: if (JS_IsError(cx, result)) { JS_Throw(cx, result); return JS_EXCEPTION; } return result; case QJS_FS_PROMISE: promise = JS_NewPromiseCapability(cx, callbacks); if (JS_IsException(promise)) { JS_FreeValue(cx, result); return JS_EXCEPTION; } is_error = !!JS_IsError(cx, result); arguments[0] = callbacks[is_error]; arguments[1] = result; JS_FreeValue(cx, callbacks[!is_error]); if (JS_EnqueueJob(cx, qjs_fs_promise_trampoline, 2, arguments) < 0) { JS_FreeValue(cx, promise); JS_FreeValue(cx, callbacks[is_error]); JS_FreeValue(cx, result); return JS_EXCEPTION; } JS_FreeValue(cx, arguments[0]); JS_FreeValue(cx, arguments[1]); return promise; case QJS_FS_CALLBACK: if (JS_IsError(cx, result)) { arguments[0] = result; arguments[1] = JS_UNDEFINED; } else { arguments[0] = JS_UNDEFINED; arguments[1] = result; } promise = JS_Call(cx, callback, JS_UNDEFINED, 2, arguments); JS_FreeValue(cx, arguments[0]); JS_FreeValue(cx, arguments[1]); if (JS_IsException(promise)) { return JS_EXCEPTION; } return JS_UNDEFINED; default: return JS_ThrowInternalError(cx, "unexpected calltype %d", calltype); } } static int qjs_fs_flags(JSContext *cx, JSValue value, int default_flags) { JSValue ret; njs_str_t flags; qjs_fs_entry_t *fl; if (JS_IsUndefined(value)) { return default_flags; } ret = JS_ToString(cx, value); if (JS_IsException(ret)) { return -1; } flags.start = (u_char *) JS_ToCStringLen(cx, &flags.length, ret); JS_FreeValue(cx, ret); if (flags.start == NULL) { return -1; } for (fl = &qjs_flags_table[0]; fl->name.length != 0; fl++) { if (njs_strstr_eq(&flags, &fl->name)) { JS_FreeCString(cx, (const char *) flags.start); return fl->value; } } JS_ThrowTypeError(cx, "Unknown file open flags: \"%s\"", flags.start); JS_FreeCString(cx, (const char *) flags.start); return -1; } static mode_t qjs_fs_mode(JSContext *cx, JSValue value, mode_t default_mode) { int64_t i64; /* GCC complains about uninitialized i64. */ i64 = 0; if (JS_IsUndefined(value)) { return default_mode; } if (JS_ToInt64(cx, &i64, value) < 0) { return (mode_t) -1; } return (mode_t) i64; } static JSValue qjs_fs_error(JSContext *cx, const char *syscall, const char *description, const char *path, int errn) { JSValue value; value = JS_NewError(cx); if (JS_IsException(value)) { return JS_EXCEPTION; } if (JS_SetPropertyStr(cx, value, "message", JS_NewString(cx, description)) < 0) { JS_FreeValue(cx, value); return JS_EXCEPTION; } if (errn != 0) { if (JS_SetPropertyStr(cx, value, "errno", JS_NewInt32(cx, errn)) < 0) { JS_FreeValue(cx, value); return JS_EXCEPTION; } if (JS_SetPropertyStr(cx, value, "code", JS_NewString(cx, njs_errno_string(errn))) < 0) { JS_FreeValue(cx, value); return JS_EXCEPTION; } } if (path != NULL) { if (JS_SetPropertyStr(cx, value, "path", JS_NewString(cx, path)) < 0) { JS_FreeValue(cx, value); return JS_EXCEPTION; } } if (syscall != NULL) { if (JS_SetPropertyStr(cx, value, "syscall", JS_NewString(cx, syscall)) < 0) { JS_FreeValue(cx, value); return JS_EXCEPTION; } } return value; } static JSValue qjs_fs_encode(JSContext *cx, const qjs_buffer_encoding_t *encoding, njs_str_t *str) { JSValue ret; njs_str_t data; if (encoding == NULL) { return qjs_buffer_create(cx, str->start, str->length); } else if (encoding->encode_length != NULL) { data.length = encoding->encode_length(cx, str); data.start = js_malloc(cx, data.length); if (data.start == NULL) { JS_ThrowOutOfMemory(cx); return JS_EXCEPTION; } if (encoding->encode(cx, str, &data) != 0) { js_free(cx, data.start); return JS_EXCEPTION; } ret = JS_NewStringLen(cx, (const char *) data.start, data.length); js_free(cx, data.start); return ret; } return JS_NewStringLen(cx, (const char *) str->start, str->length); } static char * qjs_fs_path(JSContext *cx, char storage[NJS_MAX_PATH + 1], JSValue src, const char *prop_name) { u_char *p; JSValue val; qjs_bytes_t bytes; if (!JS_IsString(src)) { val = JS_GetTypedArrayBuffer(cx, src, NULL, NULL, NULL); if (JS_IsException(val)) { JS_ThrowTypeError(cx, "\"%s\" must be a string or Buffer", prop_name); return NULL; } JS_FreeValue(cx, val); } if (qjs_to_bytes(cx, &bytes, src) != 0) { return NULL; } if (bytes.length > NJS_MAX_PATH - 1) { qjs_bytes_free(cx, &bytes); JS_ThrowRangeError(cx, "\"%s\" is too long >= %d", prop_name, NJS_MAX_PATH); return NULL; } if (memchr(bytes.start, '\0', bytes.length) != 0) { qjs_bytes_free(cx, &bytes); JS_ThrowTypeError(cx, "\"%s\" must be a Buffer without null bytes", prop_name); return NULL; } p = njs_cpymem(storage, bytes.start, bytes.length); *p++ = '\0'; qjs_bytes_free(cx, &bytes); return storage; } static int qjs_fs_module_init(JSContext *cx, JSModuleDef *m) { int rc; JSValue proto; proto = JS_NewObject(cx); JS_SetPropertyFunctionList(cx, proto, qjs_fs_export, njs_nitems(qjs_fs_export)); rc = JS_SetModuleExport(cx, m, "default", proto); if (rc != 0) { return -1; } return JS_SetModuleExportList(cx, m, qjs_fs_export, njs_nitems(qjs_fs_export)); } static JSModuleDef * qjs_fs_init(JSContext *cx, const char *name) { int rc; JSValue proto; JSModuleDef *m; if (!JS_IsRegisteredClass(JS_GetRuntime(cx), QJS_CORE_CLASS_ID_FS_STATS)) { if (JS_NewClass(JS_GetRuntime(cx), QJS_CORE_CLASS_ID_FS_STATS, &qjs_fs_stats_class) < 0) { return NULL; } proto = JS_NewObject(cx); if (JS_IsException(proto)) { return NULL; } JS_SetPropertyFunctionList(cx, proto, qjs_fs_stats_proto, njs_nitems(qjs_fs_stats_proto)); JS_SetClassProto(cx, QJS_CORE_CLASS_ID_FS_STATS, proto); proto = JS_NewObject(cx); if (JS_IsException(proto)) { return NULL; } JS_SetPropertyFunctionList(cx, proto, qjs_fs_dirent_proto, njs_nitems(qjs_fs_dirent_proto)); JS_SetClassProto(cx, QJS_CORE_CLASS_ID_FS_DIRENT, proto); if (JS_NewClass(JS_GetRuntime(cx), QJS_CORE_CLASS_ID_FS_FILEHANDLE, &qjs_fs_filehandle_class) < 0) { return NULL; } proto = JS_NewObject(cx); if (JS_IsException(proto)) { return NULL; } JS_SetPropertyFunctionList(cx, proto, qjs_fs_filehandle_proto, njs_nitems(qjs_fs_filehandle_proto)); JS_SetClassProto(cx, QJS_CORE_CLASS_ID_FS_FILEHANDLE, proto); } m = JS_NewCModule(cx, name, qjs_fs_module_init); if (m == NULL) { return NULL; } JS_AddModuleExport(cx, m, "default"); rc = JS_AddModuleExportList(cx, m, qjs_fs_export, njs_nitems(qjs_fs_export)); if (rc != 0) { return NULL; } return m; } njs-0.8.9/external/qjs_zlib_module.c000066400000000000000000000326031474132077100175040ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) F5, Inc. */ #include #include #define NJS_ZLIB_CHUNK_SIZE 1024 static JSValue qjs_zlib_ext_deflate(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int raw); static JSValue qjs_zlib_ext_inflate(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int raw); static JSModuleDef *qjs_zlib_init(JSContext *ctx, const char *name); static void *qjs_zlib_alloc(void *opaque, u_int items, u_int size); static void qjs_zlib_free(void *opaque, void *address); static const JSCFunctionListEntry qjs_zlib_constants[] = { JS_PROP_INT32_DEF("Z_NO_COMPRESSION", Z_NO_COMPRESSION, JS_PROP_ENUMERABLE), JS_PROP_INT32_DEF("Z_BEST_SPEED", Z_BEST_SPEED, JS_PROP_ENUMERABLE), JS_PROP_INT32_DEF("Z_BEST_COMPRESSION", Z_BEST_COMPRESSION, JS_PROP_ENUMERABLE), JS_PROP_INT32_DEF("Z_FILTERED", Z_FILTERED, JS_PROP_ENUMERABLE), JS_PROP_INT32_DEF("Z_HUFFMAN_ONLY", Z_HUFFMAN_ONLY, JS_PROP_ENUMERABLE), JS_PROP_INT32_DEF("Z_RLE", Z_RLE, JS_PROP_ENUMERABLE), JS_PROP_INT32_DEF("Z_FIXED", Z_FIXED, JS_PROP_ENUMERABLE), JS_PROP_INT32_DEF("Z_DEFAULT_STRATEGY", Z_DEFAULT_STRATEGY, JS_PROP_ENUMERABLE), }; static const JSCFunctionListEntry qjs_zlib_export[] = { JS_CFUNC_MAGIC_DEF("deflateRawSync", 2, qjs_zlib_ext_deflate, 1), JS_CFUNC_MAGIC_DEF("deflateSync", 2, qjs_zlib_ext_deflate, 0), JS_CFUNC_MAGIC_DEF("inflateRawSync", 2, qjs_zlib_ext_inflate, 1), JS_CFUNC_MAGIC_DEF("inflateSync", 2, qjs_zlib_ext_inflate, 0), JS_OBJECT_DEF("constants", qjs_zlib_constants, njs_nitems(qjs_zlib_constants), JS_PROP_CONFIGURABLE), }; qjs_module_t qjs_zlib_module = { .name = "zlib", .init = qjs_zlib_init, }; static JSValue qjs_zlib_ext_deflate(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int raw) { int rc, chunk_size, level, mem_level, strategy, window_bits; JSValue ret, options; z_stream stream; njs_chb_t chain; qjs_bytes_t bytes, dictionary; chunk_size = NJS_ZLIB_CHUNK_SIZE; mem_level = 8; level = Z_DEFAULT_COMPRESSION; strategy = Z_DEFAULT_STRATEGY; window_bits = raw ? -MAX_WBITS : MAX_WBITS; NJS_CHB_CTX_INIT(&chain, ctx); dictionary.start = NULL; dictionary.length = 0; stream.opaque = NULL; options = argv[1]; if (JS_IsObject(options)) { ret = JS_GetPropertyStr(ctx, options, "chunkSize"); if (JS_IsException(ret)) { return JS_EXCEPTION; } if (!JS_IsUndefined(ret)) { rc = JS_ToInt32(ctx, &chunk_size, ret); JS_FreeValue(ctx, ret); if (rc != 0) { return JS_EXCEPTION; } if (chunk_size < 64) { JS_ThrowRangeError(ctx, "chunkSize must be >= 64"); return JS_EXCEPTION; } } ret = JS_GetPropertyStr(ctx, options, "level"); if (JS_IsException(ret)) { return JS_EXCEPTION; } if (!JS_IsUndefined(ret)) { rc = JS_ToInt32(ctx, &level, ret); JS_FreeValue(ctx, ret); if (rc != 0) { return JS_EXCEPTION; } if (level < Z_DEFAULT_COMPRESSION || level > Z_BEST_COMPRESSION) { JS_ThrowRangeError(ctx, "level must be in the range %d..%d", Z_DEFAULT_COMPRESSION, Z_BEST_COMPRESSION); return JS_EXCEPTION; } } ret = JS_GetPropertyStr(ctx, options, "windowBits"); if (JS_IsException(ret)) { return JS_EXCEPTION; } if (!JS_IsUndefined(ret)) { rc = JS_ToInt32(ctx, &window_bits, ret); JS_FreeValue(ctx, ret); if (rc != 0) { return JS_EXCEPTION; } if (raw) { if (window_bits < -15 || window_bits > -9) { JS_ThrowRangeError(ctx, "windowBits must be in the range " "-15..-9"); return JS_EXCEPTION; } } else { if (window_bits < 9 || window_bits > 15) { JS_ThrowRangeError(ctx, "windowBits must be in the range " "9..15"); return JS_EXCEPTION; } } } ret = JS_GetPropertyStr(ctx, options, "memLevel"); if (JS_IsException(ret)) { return JS_EXCEPTION; } if (!JS_IsUndefined(ret)) { rc = JS_ToInt32(ctx, &mem_level, ret); JS_FreeValue(ctx, ret); if (rc != 0) { return JS_EXCEPTION; } if (mem_level < 1 || mem_level > 9) { JS_ThrowRangeError(ctx, "memLevel must be in the range 1..9"); return JS_EXCEPTION; } } ret = JS_GetPropertyStr(ctx, options, "strategy"); if (JS_IsException(ret)) { return JS_EXCEPTION; } if (!JS_IsUndefined(ret)) { rc = JS_ToInt32(ctx, &strategy, ret); JS_FreeValue(ctx, ret); if (rc != 0) { return JS_EXCEPTION; } switch (strategy) { case Z_FILTERED: case Z_HUFFMAN_ONLY: case Z_RLE: case Z_FIXED: case Z_DEFAULT_STRATEGY: break; default: JS_ThrowRangeError(ctx, "unknown strategy: %d", strategy); return JS_EXCEPTION; } } ret = JS_GetPropertyStr(ctx, options, "dictionary"); if (JS_IsException(ret)) { return JS_EXCEPTION; } if (!JS_IsUndefined(ret)) { rc = qjs_to_bytes(ctx, &dictionary, ret); JS_FreeValue(ctx, ret); if (rc != 0) { return JS_EXCEPTION; } } } rc = qjs_to_bytes(ctx, &bytes, argv[0]); if (rc != 0) { return JS_EXCEPTION; } stream.next_in = bytes.start; stream.avail_in = bytes.length; stream.zalloc = qjs_zlib_alloc; stream.zfree = qjs_zlib_free; stream.opaque = ctx; rc = deflateInit2(&stream, level, Z_DEFLATED, window_bits, mem_level, strategy); if (njs_slow_path(rc != Z_OK)) { JS_ThrowInternalError(ctx, "deflateInit2() failed"); goto fail; } if (dictionary.start != NULL) { rc = deflateSetDictionary(&stream, dictionary.start, dictionary.length); if (rc != Z_OK) { JS_ThrowInternalError(ctx, "deflateSetDictionary() failed"); goto fail; } } do { stream.next_out = njs_chb_reserve(&chain, chunk_size); if (njs_slow_path(stream.next_out == NULL)) { JS_ThrowOutOfMemory(ctx); goto fail; } stream.avail_out = chunk_size; rc = deflate(&stream, Z_FINISH); if (njs_slow_path(rc < 0)) { JS_ThrowInternalError(ctx, "failed to deflate the data: %s", stream.msg); goto fail; } njs_chb_written(&chain, chunk_size - stream.avail_out); } while (stream.avail_out == 0); deflateEnd(&stream); qjs_bytes_free(ctx, &bytes); if (dictionary.start != NULL) { qjs_bytes_free(ctx, &dictionary); } ret = qjs_buffer_chb_alloc(ctx, &chain); njs_chb_destroy(&chain); return ret; fail: qjs_bytes_free(ctx, &bytes); if (dictionary.start != NULL) { qjs_bytes_free(ctx, &dictionary); } if (stream.opaque != NULL) { deflateEnd(&stream); } if (chain.pool != NULL) { njs_chb_destroy(&chain); } return JS_EXCEPTION; } static JSValue qjs_zlib_ext_inflate(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int raw) { int rc, chunk_size, window_bits; JSValue ret, options; z_stream stream; njs_chb_t chain; qjs_bytes_t bytes, dictionary; chunk_size = NJS_ZLIB_CHUNK_SIZE; window_bits = raw ? -MAX_WBITS : MAX_WBITS; NJS_CHB_CTX_INIT(&chain, ctx); dictionary.start = NULL; dictionary.length = 0; stream.opaque = NULL; options = argv[1]; if (JS_IsObject(options)) { ret = JS_GetPropertyStr(ctx, options, "chunkSize"); if (JS_IsException(ret)) { return JS_EXCEPTION; } if (!JS_IsUndefined(ret)) { rc = JS_ToInt32(ctx, &chunk_size, ret); JS_FreeValue(ctx, ret); if (rc != 0) { return JS_EXCEPTION; } if (chunk_size < 64) { JS_ThrowRangeError(ctx, "chunkSize must be >= 64"); return JS_EXCEPTION; } } ret = JS_GetPropertyStr(ctx, options, "windowBits"); if (JS_IsException(ret)) { return JS_EXCEPTION; } if (!JS_IsUndefined(ret)) { rc = JS_ToInt32(ctx, &window_bits, ret); JS_FreeValue(ctx, ret); if (rc != 0) { return JS_EXCEPTION; } if (raw) { if (window_bits < -15 || window_bits > -8) { JS_ThrowRangeError(ctx, "windowBits must be in the range " "-15..-8"); return JS_EXCEPTION; } } else { if (window_bits < 8 || window_bits > 15) { JS_ThrowRangeError(ctx, "windowBits must be in the range " "8..15"); return JS_EXCEPTION; } } } ret = JS_GetPropertyStr(ctx, options, "dictionary"); if (JS_IsException(ret)) { return JS_EXCEPTION; } if (!JS_IsUndefined(ret)) { rc = qjs_to_bytes(ctx, &dictionary, ret); JS_FreeValue(ctx, ret); if (rc != 0) { return JS_EXCEPTION; } } } rc = qjs_to_bytes(ctx, &bytes, argv[0]); if (rc != 0) { return JS_EXCEPTION; } stream.next_in = bytes.start; stream.avail_in = bytes.length; stream.zalloc = qjs_zlib_alloc; stream.zfree = qjs_zlib_free; stream.opaque = ctx; rc = inflateInit2(&stream, window_bits); if (njs_slow_path(rc != Z_OK)) { JS_ThrowInternalError(ctx, "inflateInit2() failed"); goto fail; } if (dictionary.start != NULL) { rc = inflateSetDictionary(&stream, dictionary.start, dictionary.length); if (rc != Z_OK) { JS_ThrowInternalError(ctx, "inflateSetDictionary() failed"); goto fail; } } while (rc != Z_STREAM_END) { stream.next_out = njs_chb_reserve(&chain, chunk_size); if (njs_slow_path(stream.next_out == NULL)) { JS_ThrowOutOfMemory(ctx); goto fail; } stream.avail_out = chunk_size; rc = inflate(&stream, Z_NO_FLUSH); if (njs_slow_path(rc < 0)) { JS_ThrowInternalError(ctx, "failed to inflate the data: %s", stream.msg); goto fail; } njs_chb_written(&chain, chunk_size - stream.avail_out); } rc = inflateEnd(&stream); if (njs_slow_path(rc != Z_OK)) { JS_ThrowInternalError(ctx, "inflateEnd() failed"); goto fail; } qjs_bytes_free(ctx, &bytes); if (dictionary.start != NULL) { qjs_bytes_free(ctx, &dictionary); } ret = qjs_buffer_chb_alloc(ctx, &chain); njs_chb_destroy(&chain); return ret; fail: qjs_bytes_free(ctx, &bytes); if (dictionary.start != NULL) { qjs_bytes_free(ctx, &dictionary); } if (stream.opaque != NULL) { inflateEnd(&stream); } if (chain.pool != NULL) { njs_chb_destroy(&chain); } return JS_EXCEPTION; } static int qjs_zlib_module_init(JSContext *ctx, JSModuleDef *m) { int rc; JSValue proto; proto = JS_NewObject(ctx); JS_SetPropertyFunctionList(ctx, proto, qjs_zlib_export, njs_nitems(qjs_zlib_export)); rc = JS_SetModuleExport(ctx, m, "default", proto); if (rc != 0) { return -1; } return JS_SetModuleExportList(ctx, m, qjs_zlib_export, njs_nitems(qjs_zlib_export)); } static JSModuleDef * qjs_zlib_init(JSContext *ctx, const char *name) { int rc; JSModuleDef *m; m = JS_NewCModule(ctx, name, qjs_zlib_module_init); if (m == NULL) { return NULL; } JS_AddModuleExport(ctx, m, "default"); rc = JS_AddModuleExportList(ctx, m, qjs_zlib_export, njs_nitems(qjs_zlib_export)); if (rc != 0) { return NULL; } return m; } static void * qjs_zlib_alloc(void *opaque, u_int items, u_int size) { return js_malloc(opaque, items * size); } static void qjs_zlib_free(void *opaque, void *address) { js_free(opaque, address); } njs-0.8.9/nginx/000077500000000000000000000000001474132077100134535ustar00rootroot00000000000000njs-0.8.9/nginx/config000066400000000000000000000106321474132077100146450ustar00rootroot00000000000000ngx_addon_name="ngx_js_module" NJS_OPENSSL=${NJS_OPENSSL:-YES} NJS_LIBXSLT=${NJS_LIBXSLT:-YES} NJS_ZLIB=${NJS_ZLIB:-YES} NJS_QUICKJS=${NJS_QUICKJS:-YES} NJS_DEPS="$ngx_addon_dir/ngx_js.h \ $ngx_addon_dir/ngx_js_fetch.h \ $ngx_addon_dir/ngx_js_shared_dict.h" NJS_SRCS="$ngx_addon_dir/ngx_js.c \ $ngx_addon_dir/ngx_js_fetch.c \ $ngx_addon_dir/ngx_js_regex.c \ $ngx_addon_dir/ngx_js_shared_dict.c" QJS_DEPS="" QJS_SRCS="" NJS_OPENSSL_LIB= NJS_XSLT_LIB= NJS_ZLIB_LIB= NJS_QUICKJS_LIB= NJS_QUICKJS_INC= NJS_HAVE_QUICKJS= if [ $NJS_QUICKJS != NO ]; then ngx_feature="QuickJS library -lquickjs.lto" ngx_feature_name=NJS_HAVE_QUICKJS ngx_feature_run=yes ngx_feature_incs="#if defined(__GNUC__) && (__GNUC__ >= 8) #pragma GCC diagnostic push #pragma GCC diagnostic ignored \"-Wcast-function-type\" #endif #include " ngx_feature_path="" ngx_feature_libs="-lquickjs.lto -lm -ldl -lpthread" ngx_feature_test="JSRuntime *rt; rt = JS_NewRuntime(); (void) JS_GetClassID; JS_FreeRuntime(rt); return 0;" . auto/feature if [ $ngx_found = no ]; then ngx_feature="QuickJS library -lquickjs" ngx_feature_libs="-lquickjs -lm -ldl -lpthread" . auto/feature fi if [ $ngx_found = no ]; then ngx_feature="QuickJS library -I/usr/include/quickjs/ -L/usr/lib/quickjs/ -lquickjs.lto" ngx_feature_path="/usr/include/quickjs/" ngx_feature_libs="-L/usr/lib/quickjs/ -lquickjs.lto -lm -ldl -lpthread" . auto/feature fi if [ $ngx_found = no ]; then ngx_feature="QuickJS library -I/usr/include/quickjs/ -L/usr/lib/quickjs/ -lquickjs" ngx_feature_libs="-L/usr/lib/quickjs/ -lquickjs -lm -ldl -lpthread" . auto/feature fi if [ $ngx_found = yes ]; then ngx_feature="QuickJS JS_NewTypedArray()" ngx_feature_test="(void) JS_NewTypedArray; return 0;" . auto/feature if [ $ngx_found = yes ]; then have=NJS_HAVE_QUICKJS_NEW_TYPED_ARRAY . auto/have fi NJS_HAVE_QUICKJS=YES NJS_QUICKJS_LIB="$ngx_feature_libs" NJS_QUICKJS_INC="$ngx_feature_path" echo " enabled QuickJS engine" fi fi if [ $NJS_OPENSSL != NO ]; then NJS_OPENSSL_LIB=OPENSSL have=NJS_HAVE_OPENSSL . auto/have NJS_SRCS="$NJS_SRCS $ngx_addon_dir/../external/njs_webcrypto_module.c" echo " enabled webcrypto module" fi if [ $NJS_LIBXSLT != NO ]; then NJS_XSLT_LIB=LIBXSLT have=NJS_HAVE_XML . auto/have NJS_SRCS="$NJS_SRCS $ngx_addon_dir/../external/njs_xml_module.c" echo " enabled xml module" fi if [ $NJS_ZLIB != NO ]; then NJS_ZLIB_LIB=ZLIB have=NJS_HAVE_ZLIB . auto/have NJS_SRCS="$NJS_SRCS $ngx_addon_dir/../external/njs_zlib_module.c" if [ "$NJS_HAVE_QUICKJS" = "YES" ]; then NJS_SRCS="$NJS_SRCS $ngx_addon_dir/../external/qjs_zlib_module.c" fi echo " enabled zlib module" fi NJS_ENGINE_DEP="$ngx_addon_dir/../build/libnjs.a" NJS_ENGINE_LIB="$ngx_addon_dir/../build/libnjs.a" if [ "$NJS_HAVE_QUICKJS" = "YES" ]; then NJS_ENGINE_DEP="$ngx_addon_dir/../build/libqjs.a" NJS_ENGINE_LIB="$ngx_addon_dir/../build/libnjs.a $ngx_addon_dir/../build/libqjs.a" fi if [ $HTTP != NO ]; then ngx_module_type=HTTP_AUX_FILTER ngx_module_name=ngx_http_js_module ngx_module_incs="$ngx_addon_dir/../src $ngx_addon_dir/../build \ $NJS_QUICKJS_INC" ngx_module_deps="$NJS_ENGINE_DEP $NJS_DEPS $QJS_DEPS" ngx_module_srcs="$ngx_addon_dir/ngx_http_js_module.c $NJS_SRCS $QJS_SRCS" ngx_module_libs="PCRE $NJS_OPENSSL_LIB $NJS_XSLT_LIB $NJS_ZLIB_LIB \ $NJS_QUICKJS_LIB $NJS_ENGINE_LIB -lm" . auto/module if [ "$ngx_module_link" != DYNAMIC ]; then NJS_SRCS= fi fi if [ $STREAM != NO ]; then ngx_module_type=STREAM ngx_module_name=ngx_stream_js_module ngx_module_incs="$ngx_addon_dir/../src $ngx_addon_dir/../build \ $NJS_QUICKJS_INC" ngx_module_deps="$NJS_ENGINE_DEP $NJS_DEPS $QJS_DEPS" ngx_module_srcs="$ngx_addon_dir/ngx_stream_js_module.c $NJS_SRCS $QJS_SRCS" ngx_module_libs="PCRE $NJS_OPENSSL_LIB $NJS_XSLT_LIB $NJS_ZLIB_LIB \ $NJS_QUICKJS_LIB $NJS_ENGINE_LIB -lm" . auto/module fi njs-0.8.9/nginx/config.make000066400000000000000000000011321474132077100155540ustar00rootroot00000000000000cat << END >> $NGX_MAKEFILE $ngx_addon_dir/../build/libnjs.a: $NGX_MAKEFILE cd $ngx_addon_dir/.. \\ && if [ -f build/Makefile ]; then \$(MAKE) clean; fi \\ && CFLAGS="\$(CFLAGS)" CC="\$(CC)" ./configure --no-openssl \\ --no-libxml2 --no-zlib --no-pcre --no-quickjs \\ && \$(MAKE) libnjs $ngx_addon_dir/../build/libqjs.a: $NGX_MAKEFILE cd $ngx_addon_dir/.. \\ && if [ -f build/Makefile ]; then \$(MAKE) clean; fi \\ && CFLAGS="\$(CFLAGS)" CC="\$(CC)" ./configure --no-openssl \\ --no-libxml2 --no-zlib --no-pcre \\ && \$(MAKE) libnjs libqjs END njs-0.8.9/nginx/ngx_http_js_module.c000066400000000000000000006422561474132077100175320ustar00rootroot00000000000000 /* * Copyright (C) Roman Arutyunyan * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #include #include #include #include "ngx_js.h" typedef struct { NGX_JS_COMMON_LOC_CONF; ngx_str_t content; ngx_str_t header_filter; ngx_str_t body_filter; ngx_uint_t buffer_type; } ngx_http_js_loc_conf_t; typedef struct { ngx_http_conf_ctx_t *conf_ctx; ngx_connection_t *connection; uint8_t *worker_affinity; /** * fd is used for event debug and should be at the same position * as in ngx_connection_t: after a 3rd pointer. */ ngx_socket_t fd; ngx_str_t method; ngx_msec_t interval; ngx_msec_t jitter; ngx_log_t log; ngx_http_log_ctx_t log_ctx; ngx_event_t event; } ngx_js_periodic_t; #define NJS_HEADER_SEMICOLON 0x1 #define NJS_HEADER_SINGLE 0x2 #define NJS_HEADER_ARRAY 0x4 #define NJS_HEADER_GET 0x8 typedef struct ngx_http_js_ctx_s ngx_http_js_ctx_t; struct ngx_http_js_ctx_s { NGX_JS_COMMON_CTX; ngx_uint_t done; ngx_int_t status; njs_opaque_value_t rargs; njs_opaque_value_t request_body; njs_opaque_value_t response_body; ngx_str_t redirect_uri; ngx_int_t filter; ngx_buf_t *buf; ngx_chain_t **last_out; ngx_chain_t *free; ngx_chain_t *busy; ngx_int_t (*body_filter)(ngx_http_request_t *r, ngx_http_js_loc_conf_t *jlcf, ngx_http_js_ctx_t *ctx, ngx_chain_t *in); ngx_js_periodic_t *periodic; }; typedef struct { ngx_str_t name; unsigned flags; uintptr_t handler; } ngx_http_js_header_t; typedef njs_int_t (*njs_http_js_header_handler_t)(njs_vm_t *vm, ngx_http_request_t *r, unsigned flags, njs_str_t *name, njs_value_t *setval, njs_value_t *retval); typedef njs_int_t (*njs_http_js_header_handler122_t)(njs_vm_t *vm, ngx_http_request_t *r, ngx_list_t *headers, njs_str_t *name, njs_value_t *setval, njs_value_t *retval); #if (NJS_HAVE_QUICKJS) typedef int (*njs_http_qjs_header_handler_t)(JSContext *cx, ngx_http_request_t *r, ngx_str_t *name, JSPropertyDescriptor *pdesc, JSValue *value, unsigned flags); typedef struct { ngx_http_request_t *request; JSValue args; JSValue request_body; JSValue response_body; } ngx_http_qjs_request_t; #endif typedef struct { ngx_str_t name; ngx_uint_t value; } ngx_http_js_entry_t; static ngx_int_t ngx_http_js_content_handler(ngx_http_request_t *r); static void ngx_http_js_content_event_handler(ngx_http_request_t *r); static void ngx_http_js_content_write_event_handler(ngx_http_request_t *r); static void ngx_http_js_content_finalize(ngx_http_request_t *r, ngx_http_js_ctx_t *ctx); static ngx_int_t ngx_http_js_header_filter(ngx_http_request_t *r); static ngx_int_t ngx_http_js_variable_set(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_js_variable_var(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_js_init_vm(ngx_http_request_t *r, njs_int_t proto_id); static void ngx_http_js_cleanup_ctx(void *data); static njs_int_t ngx_http_js_ext_keys_header(njs_vm_t *vm, njs_value_t *value, njs_value_t *keys, ngx_list_t *headers); #if defined(nginx_version) && (nginx_version < 1023000) static ngx_table_elt_t *ngx_http_js_get_header(ngx_list_part_t *part, u_char *data, size_t len); #endif static njs_int_t ngx_http_js_ext_raw_header(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_http_js_ext_header_out(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); #if defined(nginx_version) && (nginx_version < 1023000) static njs_int_t ngx_http_js_header_single(njs_vm_t *vm, ngx_http_request_t *r, ngx_list_t *headers, njs_str_t *name, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_http_js_header_out_special(njs_vm_t *vm, ngx_http_request_t *r, njs_str_t *v, njs_value_t *setval, njs_value_t *retval, ngx_table_elt_t **hh); static njs_int_t ngx_http_js_header_array(njs_vm_t *vm, ngx_http_request_t *r, ngx_list_t *headers, njs_str_t *name, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_http_js_header_generic(njs_vm_t *vm, ngx_http_request_t *r, ngx_list_t *headers, njs_str_t *name, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_http_js_content_encoding122(njs_vm_t *vm, ngx_http_request_t *r, ngx_list_t *headers, njs_str_t *name, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_http_js_content_length122(njs_vm_t *vm, ngx_http_request_t *r, ngx_list_t *headers, njs_str_t *name, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_http_js_content_type122(njs_vm_t *vm, ngx_http_request_t *r, ngx_list_t *headers, njs_str_t *name, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_http_js_date122(njs_vm_t *vm, ngx_http_request_t *r, ngx_list_t *headers, njs_str_t *name, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_http_js_last_modified122(njs_vm_t *vm, ngx_http_request_t *r, ngx_list_t *headers, njs_str_t *name, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_http_js_location122(njs_vm_t *vm, ngx_http_request_t *r, ngx_list_t *headers, njs_str_t *name, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_http_js_server122(njs_vm_t *vm, ngx_http_request_t *r, ngx_list_t *headers, njs_str_t *name, njs_value_t *setval, njs_value_t *retval); #endif static njs_int_t ngx_http_js_ext_keys_header_out(njs_vm_t *vm, njs_value_t *value, njs_value_t *keys); static njs_int_t ngx_http_js_ext_status(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_http_js_ext_send_header(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t ngx_http_js_ext_send(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t ngx_http_js_ext_send_buffer(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t ngx_http_js_ext_set_return_value(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t ngx_http_js_ext_done(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t ngx_http_js_ext_finish(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t ngx_http_js_ext_return(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t ngx_http_js_ext_internal_redirect(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t ngx_http_js_ext_get_http_version(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_http_js_ext_internal(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_http_js_ext_get_remote_address(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_http_js_ext_get_args(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_http_js_ext_get_request_body(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_http_js_ext_header_in(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); #if defined(nginx_version) && (nginx_version < 1023000) static njs_int_t ngx_http_js_header_cookie(njs_vm_t *vm, ngx_http_request_t *r, ngx_list_t *headers, njs_str_t *name, njs_value_t *setval, njs_value_t *retval); #if (NGX_HTTP_X_FORWARDED_FOR) static njs_int_t ngx_http_js_header_x_forwarded_for(njs_vm_t *vm, ngx_http_request_t *r, ngx_list_t *headers, njs_str_t *name, njs_value_t *setval, njs_value_t *retval); #endif static njs_int_t ngx_http_js_header_in_array(njs_vm_t *vm, ngx_http_request_t *r, ngx_array_t *array, u_char sep, njs_value_t *retval); #endif static njs_int_t ngx_http_js_ext_keys_header_in(njs_vm_t *vm, njs_value_t *value, njs_value_t *keys); static njs_int_t ngx_http_js_ext_variables(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_http_js_periodic_session_variables(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_http_js_ext_subrequest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static ngx_int_t ngx_http_js_subrequest_done(ngx_http_request_t *r, void *data, ngx_int_t rc); static njs_int_t ngx_http_js_ext_get_parent(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_http_js_ext_get_response_body(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); #if defined(nginx_version) && (nginx_version >= 1023000) static njs_int_t ngx_http_js_header_in(njs_vm_t *vm, ngx_http_request_t *r, unsigned flags, njs_str_t *name, njs_value_t *retval); static njs_int_t ngx_http_js_header_out(njs_vm_t *vm, ngx_http_request_t *r, unsigned flags, njs_str_t *name, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_http_js_header_out_special(njs_vm_t *vm, ngx_http_request_t *r, njs_str_t *v, njs_value_t *setval, njs_value_t *retval, ngx_table_elt_t **hh); static njs_int_t ngx_http_js_header_generic(njs_vm_t *vm, ngx_http_request_t *r, ngx_list_t *headers, ngx_table_elt_t **ph, unsigned flags, njs_str_t *name, njs_value_t *retval); #endif static njs_int_t ngx_http_js_content_encoding(njs_vm_t *vm, ngx_http_request_t *r, unsigned flags, njs_str_t *name, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_http_js_content_length(njs_vm_t *vm, ngx_http_request_t *r, unsigned flags, njs_str_t *name, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_http_js_content_type(njs_vm_t *vm, ngx_http_request_t *r, unsigned flags, njs_str_t *name, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_http_js_date(njs_vm_t *vm, ngx_http_request_t *r, unsigned flags, njs_str_t *name, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_http_js_last_modified(njs_vm_t *vm, ngx_http_request_t *r, unsigned flags, njs_str_t *name, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_http_js_location(njs_vm_t *vm, ngx_http_request_t *r, unsigned flags, njs_str_t *name, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_http_js_server(njs_vm_t *vm, ngx_http_request_t *r, unsigned flags, njs_str_t *name, njs_value_t *setval, njs_value_t *retval); #if (NJS_HAVE_QUICKJS) static JSValue ngx_http_qjs_ext_to_string_tag(JSContext *cx, JSValueConst this_val); static JSValue ngx_http_qjs_ext_args(JSContext *cx, JSValueConst this_val); static JSValue ngx_http_qjs_ext_done(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue ngx_http_qjs_ext_finish(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue ngx_http_qjs_ext_headers_in(JSContext *cx, JSValueConst this_val); static JSValue ngx_http_qjs_ext_headers_out(JSContext *cx, JSValueConst this_val); static JSValue ngx_http_qjs_ext_http_version(JSContext *cx, JSValueConst this_val); static JSValue ngx_http_qjs_ext_internal(JSContext *cx, JSValueConst this_val); static JSValue ngx_http_qjs_ext_internal_redirect(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue ngx_http_qjs_ext_log(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int level); static JSValue ngx_http_qjs_ext_periodic_to_string_tag(JSContext *cx, JSValueConst this_val); static JSValue ngx_http_qjs_ext_periodic_variables(JSContext *cx, JSValueConst this_val, int type); static JSValue ngx_http_qjs_ext_parent(JSContext *cx, JSValueConst this_val); static JSValue ngx_http_qjs_ext_remote_address(JSContext *cx, JSValueConst this_val); static JSValue ngx_http_qjs_ext_request_body(JSContext *cx, JSValueConst this_val, int type); static JSValue ngx_http_qjs_ext_response_body(JSContext *cx, JSValueConst this_val, int type); static JSValue ngx_http_qjs_ext_return(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue ngx_http_qjs_ext_send(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue ngx_http_qjs_ext_send_buffer(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue ngx_http_qjs_ext_send_header(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue ngx_http_qjs_ext_set_return_value(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue ngx_http_qjs_ext_status_get(JSContext *cx, JSValueConst this_val); static JSValue ngx_http_qjs_ext_status_set(JSContext *cx, JSValueConst this_val, JSValueConst value); static JSValue ngx_http_qjs_ext_string(JSContext *cx, JSValueConst this_val, int offset); static JSValue ngx_http_qjs_ext_subrequest(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue ngx_http_qjs_ext_raw_headers(JSContext *cx, JSValueConst this_val, int out); static JSValue ngx_http_qjs_ext_variables(JSContext *cx, JSValueConst this_val, int type); static int ngx_http_qjs_variables_own_property(JSContext *cx, JSPropertyDescriptor *pdesc, JSValueConst obj, JSAtom prop); static int ngx_http_qjs_variables_set_property(JSContext *cx, JSValueConst obj, JSAtom atom, JSValueConst value, JSValueConst receiver, int flags); static int ngx_http_qjs_headers_in_own_property(JSContext *cx, JSPropertyDescriptor *pdesc, JSValueConst obj, JSAtom prop); static int ngx_http_qjs_headers_in_own_property_names(JSContext *ctx, JSPropertyEnum **ptab, uint32_t *plen, JSValueConst obj); static int ngx_http_qjs_headers_out_own_property(JSContext *cx, JSPropertyDescriptor *pdesc, JSValueConst obj, JSAtom prop); static int ngx_http_qjs_headers_out_own_property_names(JSContext *cx, JSPropertyEnum **ptab, uint32_t *plen, JSValueConst obj); static int ngx_http_qjs_headers_out_set_property(JSContext *cx, JSValueConst obj, JSAtom atom, JSValueConst value, JSValueConst receiver, int flags); static int ngx_http_qjs_headers_out_define_own_property(JSContext *cx, JSValueConst this_obj, JSAtom prop, JSValueConst val, JSValueConst getter, JSValueConst setter, int flags); static int ngx_http_qjs_headers_out_delete_property(JSContext *cx, JSValueConst obj, JSAtom prop); static ngx_http_request_t *ngx_http_qjs_request(JSValueConst val); static JSValue ngx_http_qjs_request_make(JSContext *cx, ngx_int_t proto_id, ngx_http_request_t *r); static void ngx_http_qjs_request_finalizer(JSRuntime *rt, JSValue val); #endif static ngx_pool_t *ngx_http_js_pool(ngx_http_request_t *r); static ngx_resolver_t *ngx_http_js_resolver(ngx_http_request_t *r); static ngx_msec_t ngx_http_js_resolver_timeout(ngx_http_request_t *r); static ngx_msec_t ngx_http_js_fetch_timeout(ngx_http_request_t *r); static size_t ngx_http_js_buffer_size(ngx_http_request_t *r); static size_t ngx_http_js_max_response_buffer_size(ngx_http_request_t *r); static void ngx_http_js_event_finalize(ngx_http_request_t *r, ngx_int_t rc); static ngx_js_ctx_t *ngx_http_js_ctx(ngx_http_request_t *r); static void ngx_http_js_periodic_handler(ngx_event_t *ev); static void ngx_http_js_periodic_shutdown_handler(ngx_event_t *ev); static void ngx_http_js_periodic_write_handler(ngx_event_t *ev); static void ngx_http_js_periodic_finalize(ngx_http_request_t *r, ngx_int_t rc); static void ngx_http_js_periodic_destroy(ngx_http_request_t *r, ngx_js_periodic_t *periodic); static njs_int_t ngx_js_http_init(njs_vm_t *vm); static ngx_int_t ngx_http_js_init(ngx_conf_t *cf); static ngx_int_t ngx_http_js_init_worker(ngx_cycle_t *cycle); static char *ngx_http_js_periodic(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 char *ngx_http_js_var(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_js_content(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_js_shared_dict_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_js_body_filter_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static ngx_int_t ngx_http_js_init_conf_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf); static void *ngx_http_js_create_main_conf(ngx_conf_t *cf); 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_ssl_t *ngx_http_js_ssl(ngx_http_request_t *r); static ngx_flag_t ngx_http_js_ssl_verify(ngx_http_request_t *r); static ngx_int_t ngx_http_js_parse_unsafe_uri(ngx_http_request_t *r, njs_str_t *uri, njs_str_t *args); static ngx_conf_bitmask_t ngx_http_js_engines[] = { { ngx_string("njs"), NGX_ENGINE_NJS }, #if (NJS_HAVE_QUICKJS) { ngx_string("qjs"), NGX_ENGINE_QJS }, #endif { ngx_null_string, 0 } }; #if (NGX_HTTP_SSL) static ngx_conf_bitmask_t ngx_http_js_ssl_protocols[] = { { ngx_string("TLSv1"), NGX_SSL_TLSv1 }, { ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 }, { ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 }, { ngx_string("TLSv1.3"), NGX_SSL_TLSv1_3 }, { ngx_null_string, 0 } }; #endif static ngx_command_t ngx_http_js_commands[] = { { ngx_string("js_engine"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_js_engine, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_js_loc_conf_t, type), &ngx_http_js_engines }, { ngx_string("js_context_reuse"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_size_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_js_loc_conf_t, reuse), NULL }, { ngx_string("js_import"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE13, ngx_js_import, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, { ngx_string("js_periodic"), NGX_HTTP_LOC_CONF|NGX_CONF_ANY, ngx_http_js_periodic, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, { ngx_string("js_preload_object"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE13, ngx_js_preload_object, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, { ngx_string("js_path"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_array_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_js_loc_conf_t, paths), NULL }, { ngx_string("js_set"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE23, ngx_http_js_set, 0, 0, NULL }, { ngx_string("js_var"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12, ngx_http_js_var, 0, 0, NULL }, { ngx_string("js_content"), NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE1, ngx_http_js_content, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, { ngx_string("js_header_filter"), NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_js_loc_conf_t, header_filter), NULL }, { ngx_string("js_body_filter"), NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE12, ngx_http_js_body_filter_set, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, { ngx_string("js_fetch_buffer_size"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_size_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_js_loc_conf_t, buffer_size), NULL }, { ngx_string("js_fetch_max_response_buffer_size"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_size_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_js_loc_conf_t, max_response_body_size), NULL }, { ngx_string("js_fetch_timeout"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_msec_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_js_loc_conf_t, timeout), NULL }, #if (NGX_HTTP_SSL) { ngx_string("js_fetch_ciphers"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_js_loc_conf_t, ssl_ciphers), NULL }, { ngx_string("js_fetch_protocols"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, ngx_conf_set_bitmask_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_js_loc_conf_t, ssl_protocols), &ngx_http_js_ssl_protocols }, { ngx_string("js_fetch_verify"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, ngx_conf_set_flag_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_js_loc_conf_t, ssl_verify), NULL }, { ngx_string("js_fetch_verify_depth"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_num_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_js_loc_conf_t, ssl_verify_depth), NULL }, { ngx_string("js_fetch_trusted_certificate"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_slot, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_js_loc_conf_t, ssl_trusted_certificate), NULL }, #endif { ngx_string("js_shared_dict_zone"), NGX_HTTP_MAIN_CONF|NGX_CONF_1MORE, ngx_http_js_shared_dict_zone, 0, 0, NULL }, ngx_null_command }; static ngx_http_module_t ngx_http_js_module_ctx = { NULL, /* preconfiguration */ ngx_http_js_init, /* postconfiguration */ ngx_http_js_create_main_conf, /* 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 */ ngx_http_js_init_worker, /* init process */ NULL, /* init thread */ NULL, /* exit thread */ NULL, /* exit process */ NULL, /* exit master */ NGX_MODULE_V1_PADDING }; static ngx_http_output_header_filter_pt ngx_http_next_header_filter; static ngx_http_output_body_filter_pt ngx_http_next_body_filter; static njs_int_t ngx_http_js_request_proto_id = 1; static njs_int_t ngx_http_js_periodic_session_proto_id = 2; static njs_external_t ngx_http_js_ext_request[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "Request", } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("args"), .enumerable = 1, .u.property = { .handler = ngx_http_js_ext_get_args, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("done"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_http_js_ext_done, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("error"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_js_ext_log, .magic8 = NGX_LOG_ERR, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("finish"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_http_js_ext_finish, } }, { .flags = NJS_EXTERN_OBJECT, .name.string = njs_str("headersIn"), .enumerable = 1, .u.object = { .enumerable = 1, .prop_handler = ngx_http_js_ext_header_in, .keys = ngx_http_js_ext_keys_header_in, } }, { .flags = NJS_EXTERN_OBJECT, .name.string = njs_str("headersOut"), .enumerable = 1, .u.object = { .writable = 1, .configurable = 1, .enumerable = 1, .prop_handler = ngx_http_js_ext_header_out, .keys = ngx_http_js_ext_keys_header_out, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("httpVersion"), .enumerable = 1, .u.property = { .handler = ngx_http_js_ext_get_http_version, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("internal"), .enumerable = 1, .u.property = { .handler = ngx_http_js_ext_internal, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("internalRedirect"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_http_js_ext_internal_redirect, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("log"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_js_ext_log, .magic8 = NGX_LOG_INFO, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("method"), .enumerable = 1, .u.property = { .handler = ngx_js_ext_string, .magic32 = offsetof(ngx_http_request_t, method_name), } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("parent"), .u.property = { .handler = ngx_http_js_ext_get_parent, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("rawHeadersIn"), .u.property = { .handler = ngx_http_js_ext_raw_header, .magic32 = 0, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("rawHeadersOut"), .u.property = { .handler = ngx_http_js_ext_raw_header, .magic32 = 1, } }, { .flags = NJS_EXTERN_OBJECT, .name.string = njs_str("rawVariables"), .u.object = { .writable = 1, .prop_handler = ngx_http_js_ext_variables, .magic32 = NGX_JS_BUFFER, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("remoteAddress"), .enumerable = 1, .u.property = { .handler = ngx_http_js_ext_get_remote_address, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("requestBuffer"), .u.property = { .handler = ngx_http_js_ext_get_request_body, .magic32 = NGX_JS_BUFFER, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("requestText"), .enumerable = 1, .u.property = { .handler = ngx_http_js_ext_get_request_body, .magic32 = NGX_JS_STRING, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("responseBuffer"), .u.property = { .handler = ngx_http_js_ext_get_response_body, .magic32 = NGX_JS_BUFFER, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("responseText"), .enumerable = 1, .u.property = { .handler = ngx_http_js_ext_get_response_body, .magic32 = NGX_JS_STRING, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("return"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_http_js_ext_return, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("send"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_http_js_ext_send, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("sendBuffer"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_http_js_ext_send_buffer, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("sendHeader"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_http_js_ext_send_header, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("setReturnValue"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_http_js_ext_set_return_value, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("status"), .writable = 1, .enumerable = 1, .u.property = { .handler = ngx_http_js_ext_status, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("subrequest"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_http_js_ext_subrequest, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("uri"), .enumerable = 1, .u.property = { .handler = ngx_js_ext_string, .magic32 = offsetof(ngx_http_request_t, uri), } }, { .flags = NJS_EXTERN_OBJECT, .name.string = njs_str("variables"), .u.object = { .writable = 1, .prop_handler = ngx_http_js_ext_variables, .magic32 = NGX_JS_STRING, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("warn"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_js_ext_log, .magic8 = NGX_LOG_WARN, } }, }; static njs_external_t ngx_http_js_ext_periodic_session[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "PeriodicSession", } }, { .flags = NJS_EXTERN_OBJECT, .name.string = njs_str("rawVariables"), .u.object = { .writable = 1, .prop_handler = ngx_http_js_periodic_session_variables, .magic32 = NGX_JS_BUFFER, } }, { .flags = NJS_EXTERN_OBJECT, .name.string = njs_str("variables"), .u.object = { .writable = 1, .prop_handler = ngx_http_js_periodic_session_variables, .magic32 = NGX_JS_STRING, } }, }; static uintptr_t ngx_http_js_uptr[] = { offsetof(ngx_http_request_t, connection), (uintptr_t) ngx_http_js_pool, (uintptr_t) ngx_http_js_resolver, (uintptr_t) ngx_http_js_resolver_timeout, (uintptr_t) ngx_http_js_event_finalize, (uintptr_t) ngx_http_js_ssl, (uintptr_t) ngx_http_js_ssl_verify, (uintptr_t) ngx_http_js_fetch_timeout, (uintptr_t) ngx_http_js_buffer_size, (uintptr_t) ngx_http_js_max_response_buffer_size, (uintptr_t) 0 /* main_conf ptr */, (uintptr_t) ngx_http_js_ctx, }; static njs_vm_meta_t ngx_http_js_metas = { .size = njs_nitems(ngx_http_js_uptr), .values = ngx_http_js_uptr }; njs_module_t ngx_js_http_module = { .name = njs_str("http"), .preinit = NULL, .init = ngx_js_http_init, }; njs_module_t *njs_http_js_addon_modules[] = { /* * Shared addons should be in the same order and the same positions * in all nginx modules. */ &ngx_js_ngx_module, &ngx_js_fetch_module, &ngx_js_shared_dict_module, #ifdef NJS_HAVE_OPENSSL &njs_webcrypto_module, #endif #ifdef NJS_HAVE_XML &njs_xml_module, #endif #ifdef NJS_HAVE_ZLIB &njs_zlib_module, #endif &ngx_js_http_module, NULL, }; static ngx_http_js_entry_t ngx_http_methods[] = { { ngx_string("GET"), NGX_HTTP_GET }, { ngx_string("POST"), NGX_HTTP_POST }, { ngx_string("HEAD"), NGX_HTTP_HEAD }, { ngx_string("OPTIONS"), NGX_HTTP_OPTIONS }, { ngx_string("PROPFIND"), NGX_HTTP_PROPFIND }, { ngx_string("PUT"), NGX_HTTP_PUT }, { ngx_string("MKCOL"), NGX_HTTP_MKCOL }, { ngx_string("DELETE"), NGX_HTTP_DELETE }, { ngx_string("COPY"), NGX_HTTP_COPY }, { ngx_string("MOVE"), NGX_HTTP_MOVE }, { ngx_string("PROPPATCH"), NGX_HTTP_PROPPATCH }, { ngx_string("LOCK"), NGX_HTTP_LOCK }, { ngx_string("UNLOCK"), NGX_HTTP_UNLOCK }, { ngx_string("PATCH"), NGX_HTTP_PATCH }, { ngx_string("TRACE"), NGX_HTTP_TRACE }, }; #if (NJS_HAVE_QUICKJS) static const JSCFunctionListEntry ngx_http_qjs_ext_request[] = { JS_CGETSET_DEF("[Symbol.toStringTag]", ngx_http_qjs_ext_to_string_tag, NULL), JS_CGETSET_DEF("args", ngx_http_qjs_ext_args, NULL), JS_CFUNC_DEF("done", 0, ngx_http_qjs_ext_done), JS_CFUNC_MAGIC_DEF("error", 1, ngx_http_qjs_ext_log, NGX_LOG_ERR), JS_CFUNC_DEF("finish", 0, ngx_http_qjs_ext_finish), JS_CGETSET_DEF("headersIn", ngx_http_qjs_ext_headers_in, NULL), JS_CGETSET_DEF("headersOut", ngx_http_qjs_ext_headers_out, NULL), JS_CGETSET_DEF("httpVersion", ngx_http_qjs_ext_http_version, NULL), JS_CGETSET_DEF("internal", ngx_http_qjs_ext_internal, NULL), JS_CFUNC_DEF("internalRedirect", 1, ngx_http_qjs_ext_internal_redirect), JS_CFUNC_MAGIC_DEF("log", 1, ngx_http_qjs_ext_log, NGX_LOG_INFO), JS_CGETSET_MAGIC_DEF("method", ngx_http_qjs_ext_string, NULL, offsetof(ngx_http_request_t, method_name)), JS_CGETSET_DEF("parent", ngx_http_qjs_ext_parent, NULL), JS_CGETSET_MAGIC_DEF("rawHeadersIn", ngx_http_qjs_ext_raw_headers, NULL, 0), JS_CGETSET_MAGIC_DEF("rawHeadersOut", ngx_http_qjs_ext_raw_headers, NULL, 1), JS_CGETSET_MAGIC_DEF("rawVariables", ngx_http_qjs_ext_variables, NULL, NGX_JS_BUFFER), JS_CGETSET_DEF("remoteAddress", ngx_http_qjs_ext_remote_address, NULL), JS_CGETSET_MAGIC_DEF("requestBuffer", ngx_http_qjs_ext_request_body, NULL, NGX_JS_BUFFER), JS_CGETSET_MAGIC_DEF("requestText", ngx_http_qjs_ext_request_body, NULL, NGX_JS_STRING), JS_CGETSET_MAGIC_DEF("responseBuffer", ngx_http_qjs_ext_response_body, NULL, NGX_JS_BUFFER), JS_CGETSET_MAGIC_DEF("responseText", ngx_http_qjs_ext_response_body, NULL, NGX_JS_STRING), JS_CFUNC_DEF("return", 2, ngx_http_qjs_ext_return), JS_CFUNC_DEF("send", 1, ngx_http_qjs_ext_send), JS_CFUNC_DEF("sendBuffer", 2, ngx_http_qjs_ext_send_buffer), JS_CFUNC_DEF("sendHeader", 0, ngx_http_qjs_ext_send_header), JS_CFUNC_DEF("setReturnValue", 1, ngx_http_qjs_ext_set_return_value), JS_CGETSET_DEF("status", ngx_http_qjs_ext_status_get, ngx_http_qjs_ext_status_set), JS_CFUNC_DEF("subrequest", 3, ngx_http_qjs_ext_subrequest), JS_CGETSET_MAGIC_DEF("uri", ngx_http_qjs_ext_string, NULL, offsetof(ngx_http_request_t, uri)), JS_CGETSET_MAGIC_DEF("variables", ngx_http_qjs_ext_variables, NULL, NGX_JS_STRING), JS_CFUNC_MAGIC_DEF("warn", 1, ngx_http_qjs_ext_log, NGX_LOG_WARN), }; static const JSCFunctionListEntry ngx_http_qjs_ext_periodic[] = { JS_CGETSET_DEF("[Symbol.toStringTag]", ngx_http_qjs_ext_periodic_to_string_tag, NULL), JS_CGETSET_MAGIC_DEF("rawVariables", ngx_http_qjs_ext_periodic_variables, NULL, NGX_JS_BUFFER), JS_CGETSET_MAGIC_DEF("variables", ngx_http_qjs_ext_periodic_variables, NULL, NGX_JS_STRING), }; static JSClassDef ngx_http_qjs_request_class = { "Request", .finalizer = ngx_http_qjs_request_finalizer, }; static JSClassDef ngx_http_qjs_periodic_class = { "PeriodicSession", .finalizer = NULL, }; static JSClassDef ngx_http_qjs_variables_class = { "Variables", .finalizer = NULL, .exotic = & (JSClassExoticMethods) { .get_own_property = ngx_http_qjs_variables_own_property, .set_property = ngx_http_qjs_variables_set_property, }, }; static JSClassDef ngx_http_qjs_headers_in_class = { "headersIn", .finalizer = NULL, .exotic = & (JSClassExoticMethods) { .get_own_property = ngx_http_qjs_headers_in_own_property, .get_own_property_names = ngx_http_qjs_headers_in_own_property_names, }, }; static JSClassDef ngx_http_qjs_headers_out_class = { "headersOut", .finalizer = NULL, .exotic = & (JSClassExoticMethods) { .get_own_property = ngx_http_qjs_headers_out_own_property, .get_own_property_names = ngx_http_qjs_headers_out_own_property_names, .set_property = ngx_http_qjs_headers_out_set_property, .define_own_property = ngx_http_qjs_headers_out_define_own_property, .delete_property = ngx_http_qjs_headers_out_delete_property, }, }; qjs_module_t *njs_http_qjs_addon_modules[] = { &ngx_qjs_ngx_module, &ngx_qjs_ngx_shared_dict_module, /* * Shared addons should be in the same order and the same positions * in all nginx modules. */ #ifdef NJS_HAVE_ZLIB &qjs_zlib_module, #endif NULL, }; #endif static ngx_int_t ngx_http_js_content_handler(ngx_http_request_t *r) { ngx_int_t rc; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http js content handler"); rc = ngx_http_read_client_request_body(r, ngx_http_js_content_event_handler); if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } return NGX_DONE; } static void ngx_http_js_content_event_handler(ngx_http_request_t *r) { ngx_int_t rc; ngx_http_js_ctx_t *ctx; ngx_http_js_loc_conf_t *jlcf; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http js content event handler"); rc = ngx_http_js_init_vm(r, ngx_http_js_request_proto_id); if (rc == NGX_ERROR || rc == NGX_DECLINED) { ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } jlcf = ngx_http_get_module_loc_conf(r, ngx_http_js_module); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http js content call \"%V\"" , &jlcf->content); ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); /* * status is expected to be overriden by finish(), return() or * internalRedirect() methods, otherwise the content handler is * considered invalid. */ ctx->status = NGX_HTTP_INTERNAL_SERVER_ERROR; rc = ctx->engine->call((ngx_js_ctx_t *) ctx, &jlcf->content, &ctx->args[0], 1); if (rc == NGX_ERROR) { ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } if (rc == NGX_AGAIN) { r->write_event_handler = ngx_http_js_content_write_event_handler; return; } ngx_http_js_content_finalize(r, ctx); } static void ngx_http_js_content_write_event_handler(ngx_http_request_t *r) { ngx_event_t *wev; ngx_connection_t *c; ngx_http_js_ctx_t *ctx; ngx_http_core_loc_conf_t *clcf; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http js content write event handler"); c = r->connection; ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); if (!ngx_js_ctx_pending(ctx)) { ngx_http_js_content_finalize(r, ctx); if (!c->buffered) { return; } } wev = c->write; if (wev->timedout) { ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out"); ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT); return; } if (ngx_http_output_filter(r, NULL) == NGX_ERROR) { ngx_http_finalize_request(r, NGX_ERROR); return; } clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module); if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) { ngx_http_finalize_request(r, NGX_ERROR); return; } if (!wev->delayed) { if (wev->active && !wev->ready) { ngx_add_timer(wev, clcf->send_timeout); } else if (wev->timer_set) { ngx_del_timer(wev); } } } static void ngx_http_js_content_finalize(ngx_http_request_t *r, ngx_http_js_ctx_t *ctx) { ngx_str_t args; ngx_int_t rc; ngx_uint_t flags; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http js content rc: %i", ctx->status); if (ctx->redirect_uri.len) { if (ctx->redirect_uri.data[0] == '@') { ngx_http_named_location(r, &ctx->redirect_uri); } else { ngx_str_null(&args); flags = NGX_HTTP_LOG_UNSAFE; rc = ngx_http_parse_unsafe_uri(r, &ctx->redirect_uri, &args, &flags); if (rc != NGX_OK) { ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } ngx_http_internal_redirect(r, &ctx->redirect_uri, &args); } } ngx_http_finalize_request(r, ctx->status); } static ngx_int_t ngx_http_js_header_filter(ngx_http_request_t *r) { ngx_int_t rc; njs_int_t pending; ngx_http_js_ctx_t *ctx; ngx_http_js_loc_conf_t *jlcf; jlcf = ngx_http_get_module_loc_conf(r, ngx_http_js_module); if (jlcf->header_filter.len == 0) { return ngx_http_next_header_filter(r); } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http js header filter"); rc = ngx_http_js_init_vm(r, ngx_http_js_request_proto_id); if (rc == NGX_ERROR || rc == NGX_DECLINED) { return NGX_ERROR; } ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); ctx->filter = 1; pending = ngx_js_ctx_pending(ctx); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http js header call \"%V\"", &jlcf->header_filter); rc = ctx->engine->call((ngx_js_ctx_t *) ctx, &jlcf->header_filter, &ctx->args[0], 1); if (rc == NGX_ERROR) { return NGX_ERROR; } if (!pending && rc == NGX_AGAIN) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "async operation inside \"%V\" header filter", &jlcf->header_filter); return NGX_ERROR; } return ngx_http_next_header_filter(r); } static ngx_int_t ngx_http_njs_body_filter(ngx_http_request_t *r, ngx_http_js_loc_conf_t *jlcf, ngx_http_js_ctx_t *ctx, ngx_chain_t *in) { size_t len; u_char *p; njs_vm_t *vm; ngx_int_t rc; njs_int_t ret, pending; ngx_buf_t *b; ngx_chain_t *cl; ngx_connection_t *c; njs_opaque_value_t last_key, last; njs_opaque_value_t arguments[3]; static const njs_str_t last_str = njs_str("last"); c = r->connection; vm = ctx->engine->u.njs.vm; njs_value_assign(&arguments[0], &ctx->args[0]); njs_vm_value_string_create(vm, njs_value_arg(&last_key), last_str.start, last_str.length); while (in != NULL) { ctx->buf = in->buf; b = ctx->buf; if (!ctx->done) { len = b->last - b->pos; p = ngx_pnalloc(r->pool, len); if (p == NULL) { njs_vm_memory_error(vm); return NJS_ERROR; } if (len) { ngx_memcpy(p, b->pos, len); } ret = ngx_js_prop(vm, jlcf->buffer_type, njs_value_arg(&arguments[1]), p, len); if (ret != NJS_OK) { return ret; } njs_value_boolean_set(njs_value_arg(&last), b->last_buf); ret = njs_vm_object_alloc(vm, njs_value_arg(&arguments[2]), njs_value_arg(&last_key), njs_value_arg(&last), NULL); if (ret != NJS_OK) { return ret; } pending = ngx_js_ctx_pending(ctx); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http js body call \"%V\"", &jlcf->body_filter); rc = ctx->engine->call((ngx_js_ctx_t *) ctx, &jlcf->body_filter, &arguments[0], 3); if (rc == NGX_ERROR) { return NGX_ERROR; } if (!pending && rc == NGX_AGAIN) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "async operation inside \"%V\" body filter", &jlcf->body_filter); return NGX_ERROR; } ctx->buf->pos = ctx->buf->last; } else { cl = ngx_alloc_chain_link(c->pool); if (cl == NULL) { return NGX_ERROR; } cl->buf = b; *ctx->last_out = cl; ctx->last_out = &cl->next; } in = in->next; } return NGX_OK; } static ngx_int_t ngx_http_js_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { ngx_int_t rc; ngx_chain_t *out; ngx_http_js_ctx_t *ctx; ngx_http_js_loc_conf_t *jlcf; jlcf = ngx_http_get_module_loc_conf(r, ngx_http_js_module); if (jlcf->body_filter.len == 0 || in == NULL) { return ngx_http_next_body_filter(r, in); } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http js body filter"); rc = ngx_http_js_init_vm(r, ngx_http_js_request_proto_id); if (rc == NGX_ERROR || rc == NGX_DECLINED) { return NGX_ERROR; } ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); if (ctx->done) { return ngx_http_next_body_filter(r, in); } ctx->filter = 1; ctx->last_out = &out; rc = ctx->body_filter(r, jlcf, ctx, in); if (rc != NGX_OK) { return NGX_ERROR; } *ctx->last_out = NULL; if (out != NULL || r->connection->buffered) { rc = ngx_http_next_body_filter(r, out); ngx_chain_update_chains(r->connection->pool, &ctx->free, &ctx->busy, &out, (ngx_buf_tag_t) &ngx_http_js_module); } else { rc = NGX_OK; } return rc; } static ngx_int_t ngx_http_js_variable_set(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { ngx_js_set_t *vdata = (ngx_js_set_t *) data; ngx_int_t rc; njs_int_t pending; ngx_str_t *fname, value; ngx_http_js_ctx_t *ctx; fname = &vdata->fname; rc = ngx_http_js_init_vm(r, ngx_http_js_request_proto_id); if (rc == NGX_ERROR) { return NGX_ERROR; } if (rc == NGX_DECLINED) { v->not_found = 1; return NGX_OK; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http js variable call \"%V\"", fname); ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); pending = ngx_js_ctx_pending(ctx); rc = ctx->engine->call((ngx_js_ctx_t *) ctx, fname, &ctx->args[0], 1); if (rc == NGX_ERROR) { v->not_found = 1; return NGX_OK; } if (!pending && rc == NGX_AGAIN) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "async operation inside \"%V\" variable handler", fname); return NGX_ERROR; } if (ctx->engine->string(ctx->engine, &ctx->retval, &value) != NGX_OK) { return NGX_ERROR; } v->len = value.len; v->valid = 1; v->no_cacheable = vdata->flags & NGX_NJS_VAR_NOCACHE; v->not_found = 0; v->data = value.data; return NGX_OK; } static ngx_int_t ngx_http_js_variable_var(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { ngx_http_complex_value_t *cv = (ngx_http_complex_value_t *) data; ngx_str_t value; if (cv != NULL) { if (ngx_http_complex_value(r, cv, &value) != NGX_OK) { return NGX_ERROR; } } else { ngx_str_null(&value); } v->len = value.len; v->valid = 1; v->no_cacheable = 0; v->not_found = 0; v->data = value.data; return NGX_OK; } static ngx_int_t ngx_http_js_init_vm(ngx_http_request_t *r, njs_int_t proto_id) { ngx_http_js_ctx_t *ctx; ngx_pool_cleanup_t *cln; ngx_http_js_loc_conf_t *jlcf; jlcf = ngx_http_get_module_loc_conf(r, ngx_http_js_module); if (jlcf->engine == NULL) { return NGX_DECLINED; } ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); if (ctx == NULL) { ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_js_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } ngx_js_ctx_init((ngx_js_ctx_t *) ctx, r->connection->log); ngx_http_set_ctx(r, ctx, ngx_http_js_module); } if (ctx->engine) { return NGX_OK; } ctx->engine = jlcf->engine->clone((ngx_js_ctx_t *) ctx, (ngx_js_loc_conf_t *) jlcf, proto_id, r); if (ctx->engine == NULL) { return NGX_ERROR; } ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "http js vm clone %s: %p from: %p", jlcf->engine->name, ctx->engine, jlcf->engine); cln = ngx_pool_cleanup_add(r->pool, 0); if (cln == NULL) { return NGX_ERROR; } cln->handler = ngx_http_js_cleanup_ctx; cln->data = ctx; return NGX_OK; } static void ngx_http_js_cleanup_ctx(void *data) { ngx_http_request_t *r; ngx_http_js_loc_conf_t *jlcf; ngx_http_js_ctx_t *ctx = data; if (ngx_js_ctx_pending(ctx)) { ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "pending events"); } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "http js vm destroy: %p", ctx->engine); r = ngx_js_ctx_external(ctx); /* * Restoring the original module context, because it can be reset * by internalRedirect() method. Proper ctx is required for * ngx_http_qjs_request_finalizer() to work correctly. */ ngx_http_set_ctx(r, ctx, ngx_http_js_module); jlcf = ngx_http_get_module_loc_conf(r, ngx_http_js_module); ngx_js_ctx_destroy((ngx_js_ctx_t *) ctx, (ngx_js_loc_conf_t *) jlcf); } static njs_int_t ngx_http_js_ext_keys_header(njs_vm_t *vm, njs_value_t *value, njs_value_t *keys, ngx_list_t *headers) { int64_t i, length; njs_int_t rc; njs_str_t hdr; ngx_uint_t item; njs_value_t *start; ngx_list_part_t *part; ngx_table_elt_t *header, *h; part = &headers->part; item = 0; length = 0; while (part) { if (item >= part->nelts) { part = part->next; item = 0; continue; } header = part->elts; h = &header[item++]; if (h->hash == 0) { continue; } start = njs_vm_array_start(vm, keys); for (i = 0; i < length; i++) { njs_value_string_get(njs_argument(start, i), &hdr); if (h->key.len == hdr.length && ngx_strncasecmp(h->key.data, hdr.start, hdr.length) == 0) { break; } } if (i == length) { value = njs_vm_array_push(vm, keys); if (value == NULL) { return NJS_ERROR; } rc = njs_vm_value_string_create(vm, value, h->key.data, h->key.len); if (rc != NJS_OK) { return NJS_ERROR; } length++; } } return NJS_OK; } #if defined(nginx_version) && (nginx_version < 1023000) 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; } #endif static njs_int_t ngx_http_js_ext_raw_header(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { njs_int_t rc; ngx_uint_t i; njs_value_t *array, *elem; ngx_list_part_t *part; ngx_list_t *headers; ngx_table_elt_t *header, *h; ngx_http_request_t *r; r = njs_vm_external(vm, ngx_http_js_request_proto_id, value); if (r == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } headers = (njs_vm_prop_magic32(prop) == 1) ? &r->headers_out.headers : &r->headers_in.headers; rc = njs_vm_array_alloc(vm, retval, 8); if (rc != NJS_OK) { return NJS_ERROR; } part = &headers->part; 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; } array = njs_vm_array_push(vm, retval); if (array == NULL) { return NJS_ERROR; } rc = njs_vm_array_alloc(vm, array, 2); if (rc != NJS_OK) { return NJS_ERROR; } elem = njs_vm_array_push(vm, array); if (elem == NULL) { return NJS_ERROR; } rc = njs_vm_value_string_create(vm, elem, h->key.data, h->key.len); if (rc != NJS_OK) { return NJS_ERROR; } elem = njs_vm_array_push(vm, array); if (elem == NULL) { return NJS_ERROR; } rc = njs_vm_value_string_create(vm, elem, h->value.data, h->value.len); if (rc != NJS_OK) { return NJS_ERROR; } } return NJS_OK; } static njs_int_t ngx_http_js_ext_header_out(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { njs_int_t rc; njs_str_t name; ngx_http_request_t *r; ngx_http_js_header_t *h; static ngx_http_js_header_t headers_out[] = { #if defined(nginx_version) && (nginx_version < 1023000) #define header(name, h) { njs_str(name), 0, (uintptr_t) h } header("Age", ngx_http_js_header_single), header("Content-Type", ngx_http_js_content_type122), header("Content-Length", ngx_http_js_content_length122), header("Content-Encoding", ngx_http_js_content_encoding122), header("Date", ngx_http_js_date122), header("Etag", ngx_http_js_header_single), header("Expires", ngx_http_js_header_single), header("Last-Modified", ngx_http_js_last_modified122), header("Location", ngx_http_js_location122), header("Server", ngx_http_js_server122), header("Set-Cookie", ngx_http_js_header_array), header("Retry-After", ngx_http_js_header_single), header("", ngx_http_js_header_generic), #undef header #else #define header(name, fl, h) { njs_str(name), fl, (uintptr_t) h } header("Age", NJS_HEADER_SINGLE, ngx_http_js_header_out), header("Content-Encoding", 0, ngx_http_js_content_encoding), header("Content-Length", 0, ngx_http_js_content_length), header("Content-Type", 0, ngx_http_js_content_type), header("Date", 0, ngx_http_js_date), header("Etag", NJS_HEADER_SINGLE, ngx_http_js_header_out), header("Expires", NJS_HEADER_SINGLE, ngx_http_js_header_out), header("Last-Modified", 0, ngx_http_js_last_modified), header("Location", 0, ngx_http_js_location), header("Server", 0, ngx_http_js_server), header("Set-Cookie", NJS_HEADER_ARRAY, ngx_http_js_header_out), header("Retry-After", NJS_HEADER_SINGLE, ngx_http_js_header_out), header("", 0, ngx_http_js_header_out), #undef header #endif }; r = njs_vm_external(vm, ngx_http_js_request_proto_id, value); if (r == NULL) { if (retval != NULL) { njs_value_undefined_set(retval); } return NJS_DECLINED; } rc = njs_vm_prop_name(vm, prop, &name); if (rc != NJS_OK) { if (retval != NULL) { njs_value_undefined_set(retval); } return NJS_DECLINED; } if (r->header_sent && setval != NULL) { ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "ignored setting of response header \"%V\" because" " headers were already sent", &name); } for (h = headers_out; h->name.len > 0; h++) { if (h->name.len == name.length && ngx_strncasecmp(h->name.data, name.start, name.length) == 0) { break; } } #if defined(nginx_version) && (nginx_version < 1023000) return ((njs_http_js_header_handler122_t) h->handler)(vm, r, &r->headers_out.headers, &name, setval, retval); #else return ((njs_http_js_header_handler_t) h->handler)(vm, r, h->flags, &name, setval, retval); #endif } #if defined(nginx_version) && (nginx_version < 1023000) static njs_int_t ngx_http_js_header_single(njs_vm_t *vm, ngx_http_request_t *r, ngx_list_t *headers, njs_str_t *name, njs_value_t *setval, njs_value_t *retval) { njs_int_t rc; ngx_table_elt_t *h; if (retval != NULL && setval == NULL) { h = ngx_http_js_get_header(&headers->part, name->start, name->length); if (h == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } rc = njs_vm_value_string_create(vm, retval, h->value.data, h->value.len); if (rc != NJS_OK) { return NJS_ERROR; } return NJS_OK; } return ngx_http_js_header_generic(vm, r, headers, name, setval, retval); } static njs_int_t ngx_http_js_header_out_special(njs_vm_t *vm, ngx_http_request_t *r, njs_str_t *v, njs_value_t *setval, njs_value_t *retval, ngx_table_elt_t **hh) { u_char *p; int64_t length; njs_int_t rc; njs_str_t s; ngx_list_t *headers; ngx_table_elt_t *h; njs_opaque_value_t lvalue; headers = &r->headers_out.headers; if (retval != NULL && setval == NULL) { return ngx_http_js_header_single(vm, r, headers, v, setval, retval); } if (setval != NULL && njs_value_is_array(setval)) { rc = njs_vm_array_length(vm, setval, &length); if (rc != NJS_OK) { return NJS_ERROR; } setval = njs_vm_array_prop(vm, setval, length - 1, &lvalue); } if (ngx_js_string(vm, setval, &s) != NGX_OK) { return NJS_ERROR; } h = ngx_http_js_get_header(&headers->part, v->start, v->length); if (h != NULL && s.length == 0) { h->hash = 0; h = NULL; } if (h == NULL && s.length != 0) { h = ngx_list_push(headers); if (h == NULL) { return NJS_ERROR; } p = ngx_pnalloc(r->pool, v->length); if (p == NULL) { h->hash = 0; return NJS_ERROR; } ngx_memcpy(p, v->start, v->length); h->key.data = p; h->key.len = v->length; } if (h != NULL) { p = ngx_pnalloc(r->pool, s.length); if (p == NULL) { h->hash = 0; return NJS_ERROR; } ngx_memcpy(p, s.start, s.length); h->value.data = p; h->value.len = s.length; h->hash = 1; } if (hh != NULL) { *hh = h; } return NJS_OK; } static njs_int_t ngx_http_js_header_array(njs_vm_t *vm, ngx_http_request_t *r, ngx_list_t *headers, njs_str_t *name, njs_value_t *setval, njs_value_t *retval) { size_t len; u_char *data; njs_int_t rc; ngx_uint_t i; njs_value_t *value; ngx_list_part_t *part; ngx_table_elt_t *header, *h; if (retval != NULL && setval == NULL) { rc = njs_vm_array_alloc(vm, retval, 4); if (rc != NJS_OK) { return NJS_ERROR; } len = name->length; data = name->start; part = &headers->part; 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 || h->key.len != len || ngx_strncasecmp(h->key.data, data, len) != 0) { continue; } value = njs_vm_array_push(vm, retval); if (value == NULL) { return NJS_ERROR; } rc = njs_vm_value_string_create(vm, value, h->value.data, h->value.len); if (rc != NJS_OK) { return NJS_ERROR; } } return NJS_OK; } return ngx_http_js_header_generic(vm, r, headers, name, setval, retval); } static njs_int_t ngx_http_js_header_generic(njs_vm_t *vm, ngx_http_request_t *r, ngx_list_t *headers, njs_str_t *name, njs_value_t *setval, njs_value_t *retval) { u_char *data, *p, *start, *end; size_t len; int64_t length; njs_value_t *array; njs_int_t rc; njs_str_t s; ngx_uint_t i; ngx_list_part_t *part; ngx_table_elt_t *header, *h; njs_opaque_value_t lvalue; part = &headers->part; if (retval != NULL && setval == NULL) { header = part->elts; p = NULL; start = NULL; end = NULL; 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 || h->key.len != name->length || ngx_strncasecmp(h->key.data, name->start, name->length) != 0) { continue; } if (p == NULL) { start = h->value.data; end = h->value.data + h->value.len; p = end; continue; } if (p + h->value.len + 1 > end) { len = njs_max(p + h->value.len + 1 - start, 2 * (end - start)); data = ngx_pnalloc(r->pool, len); if (data == NULL) { return NJS_ERROR; } p = ngx_cpymem(data, start, p - start); start = data; end = data + len; } *p++ = ','; p = ngx_cpymem(p, h->value.data, h->value.len); } if (p == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } return njs_vm_value_string_create(vm, retval, start, p - start); } 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 || h->key.len != name->length || ngx_strncasecmp(h->key.data, name->start, name->length) != 0) { continue; } h->hash = 0; } if (retval == NULL) { return NJS_OK; } if (njs_value_is_array(setval)) { array = setval; rc = njs_vm_array_length(vm, array, &length); if (rc != NJS_OK) { return NJS_ERROR; } if (length == 0) { return NJS_OK; } } else { array = NULL; length = 1; } for (i = 0; i < (ngx_uint_t) length; i++) { if (array != NULL) { setval = njs_vm_array_prop(vm, array, i, &lvalue); } if (ngx_js_string(vm, setval, &s) != NGX_OK) { return NJS_ERROR; } if (s.length == 0) { continue; } h = ngx_list_push(headers); if (h == NULL) { return NJS_ERROR; } p = ngx_pnalloc(r->pool, name->length); if (p == NULL) { h->hash = 0; return NJS_ERROR; } ngx_memcpy(p, name->start, name->length); h->key.data = p; h->key.len = name->length; p = ngx_pnalloc(r->pool, s.length); if (p == NULL) { h->hash = 0; return NJS_ERROR; } ngx_memcpy(p, s.start, s.length); h->value.data = p; h->value.len = s.length; h->hash = 1; } return NJS_OK; } static njs_int_t ngx_http_js_content_encoding122(njs_vm_t *vm, ngx_http_request_t *r, ngx_list_t *headers, njs_str_t *v, njs_value_t *setval, njs_value_t *retval) { return ngx_http_js_content_encoding(vm, r, 0, v, setval, retval); } static njs_int_t ngx_http_js_content_length122(njs_vm_t *vm, ngx_http_request_t *r, ngx_list_t *headers, njs_str_t *v, njs_value_t *setval, njs_value_t *retval) { return ngx_http_js_content_length(vm, r, 0, v, setval, retval); } static njs_int_t ngx_http_js_content_type122(njs_vm_t *vm, ngx_http_request_t *r, ngx_list_t *headers, njs_str_t *v, njs_value_t *setval, njs_value_t *retval) { return ngx_http_js_content_type(vm, r, 0, v, setval, retval); } static njs_int_t ngx_http_js_date122(njs_vm_t *vm, ngx_http_request_t *r, ngx_list_t *headers, njs_str_t *v, njs_value_t *setval, njs_value_t *retval) { return ngx_http_js_date(vm, r, 0, v, setval, retval); } static njs_int_t ngx_http_js_last_modified122(njs_vm_t *vm, ngx_http_request_t *r, ngx_list_t *headers, njs_str_t *v, njs_value_t *setval, njs_value_t *retval) { return ngx_http_js_last_modified(vm, r, 0, v, setval, retval); } static njs_int_t ngx_http_js_location122(njs_vm_t *vm, ngx_http_request_t *r, ngx_list_t *headers, njs_str_t *v, njs_value_t *setval, njs_value_t *retval) { return ngx_http_js_location(vm, r, 0, v, setval, retval); } static njs_int_t ngx_http_js_server122(njs_vm_t *vm, ngx_http_request_t *r, ngx_list_t *headers, njs_str_t *v, njs_value_t *setval, njs_value_t *retval) { return ngx_http_js_server(vm, r, 0, v, setval, retval); } #endif static njs_int_t ngx_http_js_ext_keys_header_out(njs_vm_t *vm, njs_value_t *value, njs_value_t *keys) { njs_int_t rc; ngx_http_request_t *r; rc = njs_vm_array_alloc(vm, keys, 8); if (rc != NJS_OK) { return NJS_ERROR; } r = njs_vm_external(vm, ngx_http_js_request_proto_id, value); if (r == NULL) { return NJS_OK; } if (r->headers_out.content_type.len) { value = njs_vm_array_push(vm, keys); if (value == NULL) { return NJS_ERROR; } rc = njs_vm_value_string_create(vm, value, (u_char *) "Content-Type", njs_length("Content-Type")); if (rc != NJS_OK) { return NJS_ERROR; } } if (r->headers_out.content_length == NULL && r->headers_out.content_length_n >= 0) { value = njs_vm_array_push(vm, keys); if (value == NULL) { return NJS_ERROR; } rc = njs_vm_value_string_create(vm, value, (u_char *) "Content-Length", njs_length("Content-Length")); if (rc != NJS_OK) { return NJS_ERROR; } } return ngx_http_js_ext_keys_header(vm, value, keys, &r->headers_out.headers); } static njs_int_t ngx_http_js_ext_status(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { ngx_int_t n; ngx_http_request_t *r; r = njs_vm_external(vm, ngx_http_js_request_proto_id, value); if (r == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } if (setval == NULL) { njs_value_number_set(retval, r->headers_out.status); return NJS_OK; } if (ngx_js_integer(vm, setval, &n) != NGX_OK) { return NJS_ERROR; } r->headers_out.status = n; r->headers_out.status_line.len = 0; njs_value_undefined_set(retval); return NJS_OK; } static njs_int_t ngx_http_js_ext_send_header(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { ngx_http_request_t *r; r = njs_vm_external(vm, ngx_http_js_request_proto_id, njs_argument(args, 0)); if (r == NULL) { njs_vm_error(vm, "\"this\" is not an external"); return NJS_ERROR; } if (ngx_http_set_content_type(r) != NGX_OK) { return NJS_ERROR; } if (ngx_http_send_header(r) == NGX_ERROR) { return NJS_ERROR; } njs_value_undefined_set(retval); return NJS_OK; } static njs_int_t ngx_http_js_ext_send(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_str_t s; ngx_buf_t *b; ngx_uint_t n; ngx_chain_t *out, *cl, **ll; ngx_http_js_ctx_t *ctx; ngx_http_request_t *r; r = njs_vm_external(vm, ngx_http_js_request_proto_id, njs_argument(args, 0)); if (r == NULL) { njs_vm_error(vm, "\"this\" is not an external"); return NJS_ERROR; } ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); if (ctx->filter) { njs_vm_error(vm, "cannot send while in body filter"); return NJS_ERROR; } out = NULL; ll = &out; for (n = 1; n < nargs; n++) { if (ngx_js_string(vm, njs_argument(args, n), &s) != NGX_OK) { return NJS_ERROR; } if (s.length == 0) { continue; } b = ngx_calloc_buf(r->pool); if (b == NULL) { return NJS_ERROR; } b->start = s.start; b->pos = b->start; b->end = s.start + s.length; 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; } njs_value_undefined_set(retval); return NJS_OK; } static njs_int_t ngx_http_js_ext_send_buffer(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { unsigned last_buf, flush; njs_str_t buffer; ngx_buf_t *b; ngx_chain_t *cl; njs_value_t *flags, *value; ngx_http_js_ctx_t *ctx; ngx_http_request_t *r; njs_opaque_value_t lvalue; static const njs_str_t last_key = njs_str("last"); static const njs_str_t flush_key = njs_str("flush"); r = njs_vm_external(vm, ngx_http_js_request_proto_id, njs_argument(args, 0)); if (r == NULL) { njs_vm_error(vm, "\"this\" is not an external"); return NJS_ERROR; } ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); if (!ctx->filter) { njs_vm_error(vm, "cannot send buffer while not filtering"); return NJS_ERROR; } if (ngx_js_string(vm, njs_arg(args, nargs, 1), &buffer) != NGX_OK) { njs_vm_error(vm, "failed to get buffer arg"); return NJS_ERROR; } flush = ctx->buf->flush; last_buf = ctx->buf->last_buf; flags = njs_arg(args, nargs, 2); if (njs_value_is_object(flags)) { value = njs_vm_object_prop(vm, flags, &flush_key, &lvalue); if (value != NULL) { flush = njs_value_bool(value); } value = njs_vm_object_prop(vm, flags, &last_key, &lvalue); if (value != NULL) { last_buf = njs_value_bool(value); } } cl = ngx_chain_get_free_buf(r->pool, &ctx->free); if (cl == NULL) { njs_vm_error(vm, "memory error"); return NJS_ERROR; } b = cl->buf; b->flush = flush; b->last_buf = last_buf; b->memory = (buffer.length ? 1 : 0); b->sync = (buffer.length ? 0 : 1); b->tag = (ngx_buf_tag_t) &ngx_http_js_module; b->start = buffer.start; b->end = buffer.start + buffer.length; b->pos = b->start; b->last = b->end; *ctx->last_out = cl; ctx->last_out = &cl->next; njs_value_undefined_set(retval); return NJS_OK; } static njs_int_t ngx_http_js_ext_set_return_value(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { ngx_http_js_ctx_t *ctx; ngx_http_request_t *r; r = njs_vm_external(vm, ngx_http_js_request_proto_id, njs_argument(args, 0)); if (r == NULL) { njs_vm_error(vm, "\"this\" is not an external"); return NJS_ERROR; } ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); njs_value_assign(&ctx->retval, njs_arg(args, nargs, 1)); njs_value_undefined_set(retval); return NJS_OK; } static njs_int_t ngx_http_js_ext_done(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { ngx_http_js_ctx_t *ctx; ngx_http_request_t *r; r = njs_vm_external(vm, ngx_http_js_request_proto_id, njs_argument(args, 0)); if (r == NULL) { njs_vm_error(vm, "\"this\" is not an external"); return NJS_ERROR; } ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); if (!ctx->filter) { njs_vm_error(vm, "cannot set done while not filtering"); return NJS_ERROR; } ctx->done = 1; njs_value_undefined_set(retval); return NJS_OK; } static njs_int_t ngx_http_js_ext_finish(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { ngx_http_js_ctx_t *ctx; ngx_http_request_t *r; r = njs_vm_external(vm, ngx_http_js_request_proto_id, njs_argument(args, 0)); if (r == NULL) { njs_vm_error(vm, "\"this\" is not an external"); return NJS_ERROR; } if (ngx_http_send_special(r, NGX_HTTP_LAST) == NGX_ERROR) { return NJS_ERROR; } ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); ctx->status = NGX_OK; njs_value_undefined_set(retval); return NJS_OK; } static njs_int_t ngx_http_js_ext_return(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_str_t text; ngx_int_t status; ngx_http_js_ctx_t *ctx; ngx_http_request_t *r; ngx_http_complex_value_t cv; r = njs_vm_external(vm, ngx_http_js_request_proto_id, njs_argument(args, 0)); if (r == NULL) { njs_vm_error(vm, "\"this\" is not an external"); return NJS_ERROR; } if (ngx_js_integer(vm, njs_arg(args, nargs, 1), &status) != NGX_OK) { return NJS_ERROR; } if (status < 0 || status > 999) { njs_vm_error(vm, "code is out of range"); return NJS_ERROR; } ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); if (status < NGX_HTTP_BAD_REQUEST || !njs_value_is_null_or_undefined(njs_arg(args, nargs, 2))) { if (ngx_js_string(vm, njs_arg(args, nargs, 2), &text) != NGX_OK) { njs_vm_error(vm, "failed to convert text"); return NJS_ERROR; } ngx_memzero(&cv, sizeof(ngx_http_complex_value_t)); cv.value.data = text.start; cv.value.len = text.length; ctx->status = ngx_http_send_response(r, status, NULL, &cv); if (ctx->status == NGX_ERROR) { njs_vm_error(vm, "failed to send response"); return NJS_ERROR; } } else { ctx->status = status; } njs_value_undefined_set(retval); return NJS_OK; } static njs_int_t ngx_http_js_ext_internal_redirect(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_str_t uri; ngx_http_js_ctx_t *ctx; ngx_http_request_t *r; r = njs_vm_external(vm, ngx_http_js_request_proto_id, njs_argument(args, 0)); if (r == NULL) { njs_vm_error(vm, "\"this\" is not an external"); return NJS_ERROR; } if (r->parent != NULL) { njs_vm_error(vm, "internalRedirect cannot be called from a subrequest"); return NJS_ERROR; } ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); if (ctx->filter) { njs_vm_error(vm, "internalRedirect cannot be called while filtering"); return NJS_ERROR; } if (ngx_js_string(vm, njs_arg(args, nargs, 1), &uri) != NGX_OK) { njs_vm_error(vm, "failed to convert uri arg"); return NJS_ERROR; } if (uri.length == 0) { njs_vm_error(vm, "uri is empty"); return NJS_ERROR; } ctx->redirect_uri.data = uri.start; ctx->redirect_uri.len = uri.length; ctx->status = NGX_DONE; njs_value_undefined_set(retval); return NJS_OK; } static njs_int_t ngx_http_js_ext_get_http_version(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { ngx_str_t v; ngx_http_request_t *r; r = njs_vm_external(vm, ngx_http_js_request_proto_id, value); if (r == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } 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; case NGX_HTTP_VERSION_11: ngx_str_set(&v, "1.1"); break; case NGX_HTTP_VERSION_20: ngx_str_set(&v, "2.0"); break; #if (NGX_HTTP_VERSION_30) case NGX_HTTP_VERSION_30: ngx_str_set(&v, "3.0"); break; #endif default: ngx_str_set(&v, ""); break; } return njs_vm_value_string_create(vm, retval, v.data, v.len); } static njs_int_t ngx_http_js_ext_internal(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { ngx_http_request_t *r; r = njs_vm_external(vm, ngx_http_js_request_proto_id, value); if (r == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } njs_value_boolean_set(retval, r->internal); return NJS_OK; } static njs_int_t ngx_http_js_ext_get_remote_address(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { ngx_connection_t *c; ngx_http_request_t *r; r = njs_vm_external(vm, ngx_http_js_request_proto_id, value); if (r == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } c = r->connection; return njs_vm_value_string_create(vm, retval, c->addr_text.data, c->addr_text.len); } static njs_int_t ngx_http_js_ext_get_args(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { u_char *data; njs_int_t ret; njs_value_t *args; ngx_http_js_ctx_t *ctx; ngx_http_request_t *r; r = njs_vm_external(vm, ngx_http_js_request_proto_id, value); if (r == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); args = njs_value_arg(&ctx->rargs); if (njs_value_is_null(args)) { data = (r->args.len != 0) ? r->args.data : (u_char *) ""; ret = njs_vm_query_string_parse(vm, data, data + r->args.len, args); if (ret == NJS_ERROR) { return NJS_ERROR; } } njs_value_assign(retval, args); return NJS_OK; } static njs_int_t ngx_http_js_ext_get_request_body(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { u_char *p, *body; size_t len; uint32_t buffer_type; ngx_buf_t *buf; njs_int_t ret; njs_value_t *request_body; ngx_chain_t *cl; ngx_http_js_ctx_t *ctx; ngx_http_request_t *r; r = njs_vm_external(vm, ngx_http_js_request_proto_id, value); if (r == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); request_body = (njs_value_t *) &ctx->request_body; buffer_type = ngx_js_buffer_type(njs_vm_prop_magic32(prop)); if (!njs_value_is_null(request_body)) { if ((buffer_type == NGX_JS_BUFFER) == (uint32_t) njs_value_is_buffer(request_body)) { njs_value_assign(retval, request_body); return NJS_OK; } } if (r->request_body == NULL || r->request_body->bufs == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } if (r->request_body->temp_file) { njs_vm_error(vm, "request body is in a file"); return NJS_ERROR; } cl = r->request_body->bufs; buf = cl->buf; if (cl->next == NULL) { len = buf->last - buf->pos; body = buf->pos; goto done; } len = buf->last - buf->pos; cl = cl->next; for ( /* void */ ; cl; cl = cl->next) { buf = cl->buf; len += buf->last - buf->pos; } p = ngx_pnalloc(r->pool, len); if (p == NULL) { njs_vm_memory_error(vm); return NJS_ERROR; } body = p; cl = r->request_body->bufs; for ( /* void */ ; cl; cl = cl->next) { buf = cl->buf; p = ngx_cpymem(p, buf->pos, buf->last - buf->pos); } done: ret = ngx_js_prop(vm, buffer_type, request_body, body, len); if (ret != NJS_OK) { return NJS_ERROR; } njs_value_assign(retval, request_body); return NJS_OK; } #if defined(nginx_version) && (nginx_version < 1023000) static njs_int_t ngx_http_js_ext_header_in(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { njs_int_t rc; njs_str_t name; ngx_http_request_t *r; ngx_http_js_header_t *h; static ngx_http_js_header_t headers_in[] = { #define header(name, h) { njs_str(name), 0, (uintptr_t) h } header("Content-Type", ngx_http_js_header_single), header("Cookie", ngx_http_js_header_cookie), header("ETag", ngx_http_js_header_single), header("From", ngx_http_js_header_single), header("Max-Forwards", ngx_http_js_header_single), header("Referer", ngx_http_js_header_single), header("Proxy-Authorization", ngx_http_js_header_single), header("User-Agent", ngx_http_js_header_single), #if (NGX_HTTP_X_FORWARDED_FOR) header("X-Forwarded-For", ngx_http_js_header_x_forwarded_for), #endif header("", ngx_http_js_header_generic), #undef header }; r = njs_vm_external(vm, ngx_http_js_request_proto_id, value); if (r == NULL) { if (retval != NULL) { njs_value_undefined_set(retval); } return NJS_DECLINED; } rc = njs_vm_prop_name(vm, prop, &name); if (rc != NJS_OK) { if (retval != NULL) { njs_value_undefined_set(retval); } return NJS_DECLINED; } for (h = headers_in; h->name.len > 0; h++) { if (h->name.len == name.length && ngx_strncasecmp(h->name.data, name.start, name.length) == 0) { break; } } return ((njs_http_js_header_handler122_t) h->handler)(vm, r, &r->headers_in.headers, &name, setval, retval); } static njs_int_t ngx_http_js_header_cookie(njs_vm_t *vm, ngx_http_request_t *r, ngx_list_t *headers, njs_str_t *name, njs_value_t *setval, njs_value_t *retval) { return ngx_http_js_header_in_array(vm, r, &r->headers_in.cookies, ';', retval); } #if (NGX_HTTP_X_FORWARDED_FOR) static njs_int_t ngx_http_js_header_x_forwarded_for(njs_vm_t *vm, ngx_http_request_t *r, ngx_list_t *headers, njs_str_t *name, njs_value_t *setval, njs_value_t *retval) { return ngx_http_js_header_in_array(vm, r, &r->headers_in.x_forwarded_for, ',', retval); } #endif static njs_int_t ngx_http_js_header_in_array(njs_vm_t *vm, ngx_http_request_t *r, ngx_array_t *array, u_char sep, njs_value_t *retval) { njs_chb_t chain; njs_int_t ret; ngx_uint_t i, n; ngx_table_elt_t **hh; n = array->nelts; hh = array->elts; if (n == 0) { njs_value_undefined_set(retval); return NJS_DECLINED; } if (n == 1) { return njs_vm_value_string_create(vm, retval, (*hh)->value.data, (*hh)->value.len); } NJS_CHB_MP_INIT(&chain, vm); for (i = 0; i < n; i++) { njs_chb_append(&chain, hh[i]->value.data, hh[i]->value.len); njs_chb_append(&chain, &sep, 1); } ret = njs_vm_value_string_create_chb(vm, retval, &chain); njs_chb_destroy(&chain); return ret; } #else static njs_int_t ngx_http_js_ext_header_in(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *unused, njs_value_t *retval) { unsigned flags; njs_int_t rc; njs_str_t name, *h; ngx_http_request_t *r; static njs_str_t single_headers_in[] = { njs_str("Content-Type"), njs_str("ETag"), njs_str("From"), njs_str("Max-Forwards"), njs_str("Referer"), njs_str("Proxy-Authorization"), njs_str("User-Agent"), njs_str(""), }; r = njs_vm_external(vm, ngx_http_js_request_proto_id, value); if (r == NULL) { if (retval != NULL) { njs_value_undefined_set(retval); } return NJS_DECLINED; } rc = njs_vm_prop_name(vm, prop, &name); if (rc != NJS_OK) { if (retval != NULL) { njs_value_undefined_set(retval); } return NJS_DECLINED; } flags = 0; for (h = single_headers_in; h->length > 0; h++) { if (h->length == name.length && ngx_strncasecmp(h->start, name.start, name.length) == 0) { flags |= NJS_HEADER_SINGLE; break; } } return ngx_http_js_header_in(vm, r, flags, &name, retval); } #endif static njs_int_t ngx_http_js_ext_keys_header_in(njs_vm_t *vm, njs_value_t *value, njs_value_t *keys) { njs_int_t rc; ngx_http_request_t *r; rc = njs_vm_array_alloc(vm, keys, 8); if (rc != NJS_OK) { return NJS_ERROR; } r = njs_vm_external(vm, ngx_http_js_request_proto_id, value); if (r == NULL) { return NJS_OK; } return ngx_http_js_ext_keys_header(vm, value, keys, &r->headers_in.headers); } static njs_int_t ngx_http_js_request_variables(njs_vm_t *vm, njs_object_prop_t *prop, ngx_http_request_t *r, njs_value_t *setval, njs_value_t *retval) { njs_int_t rc, is_capture, start, length; njs_str_t val, s; ngx_str_t name; ngx_uint_t i, key; ngx_http_variable_t *v; ngx_http_core_main_conf_t *cmcf; ngx_http_variable_value_t *vv; rc = njs_vm_prop_name(vm, prop, &val); if (rc != NJS_OK) { njs_value_undefined_set(retval); return NJS_DECLINED; } name.data = val.start; name.len = val.length; if (setval == NULL) { is_capture = 1; for (i = 0; i < name.len; i++) { if (name.data[i] < '0' || name.data[i] > '9') { is_capture = 0; break; } } if (is_capture) { key = ngx_atoi(name.data, name.len) * 2; if (r->captures == NULL || r->captures_data == NULL || r->ncaptures <= key) { njs_value_undefined_set(retval); return NJS_DECLINED; } start = r->captures[key]; length = r->captures[key + 1] - start; return ngx_js_prop(vm, njs_vm_prop_magic32(prop), retval, &r->captures_data[start], length); } /* Lookup the variable in nginx variables */ key = ngx_hash_strlow(name.data, name.data, name.len); vv = ngx_http_get_variable(r, &name, key); if (vv == NULL || vv->not_found) { njs_value_undefined_set(retval); return NJS_DECLINED; } return ngx_js_prop(vm, njs_vm_prop_magic32(prop), retval, vv->data, vv->len); } cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); key = ngx_hash_strlow(name.data, name.data, name.len); v = ngx_hash_find(&cmcf->variables_hash, key, name.data, name.len); if (v == NULL) { njs_vm_error(vm, "variable not found"); return NJS_ERROR; } if (ngx_js_string(vm, setval, &s) != NGX_OK) { return NJS_ERROR; } if (v->set_handler != NULL) { vv = ngx_pcalloc(r->pool, sizeof(ngx_http_variable_value_t)); if (vv == NULL) { njs_vm_error(vm, "internal error"); return NJS_ERROR; } vv->valid = 1; vv->not_found = 0; vv->data = s.start; vv->len = s.length; v->set_handler(r, vv, v->data); return NJS_OK; } if (!(v->flags & NGX_HTTP_VAR_INDEXED)) { njs_vm_error(vm, "variable is not writable"); return NJS_ERROR; } vv = &r->variables[v->index]; vv->valid = 1; vv->not_found = 0; vv->data = ngx_pnalloc(r->pool, s.length); if (vv->data == NULL) { vv->valid = 0; njs_vm_error(vm, "internal error"); return NJS_ERROR; } vv->len = s.length; ngx_memcpy(vv->data, s.start, vv->len); return NJS_OK; } static njs_int_t ngx_http_js_ext_variables(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { ngx_http_request_t *r; r = njs_vm_external(vm, ngx_http_js_request_proto_id, value); if (r == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } return ngx_http_js_request_variables(vm, prop, r, setval, retval); } static njs_int_t ngx_http_js_periodic_session_variables(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { ngx_http_request_t *r; r = njs_vm_external(vm, ngx_http_js_periodic_session_proto_id, value); if (r == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } return ngx_http_js_request_variables(vm, prop, r, setval, retval); } static njs_int_t ngx_http_js_ext_subrequest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { ngx_int_t rc, flags; njs_str_t uri_arg, args_arg, method_name, body_arg; ngx_str_t uri, rargs; ngx_uint_t method, methods_max, has_body, detached, promise; njs_value_t *value, *arg, *options, *callback; ngx_js_event_t *event; ngx_http_js_ctx_t *ctx; njs_opaque_value_t lvalue; ngx_http_request_t *r, *sr; ngx_http_request_body_t *rb; ngx_http_post_subrequest_t *ps; static const njs_str_t args_key = njs_str("args"); static const njs_str_t method_key = njs_str("method"); static const njs_str_t body_key = njs_str("body"); static const njs_str_t detached_key = njs_str("detached"); r = njs_vm_external(vm, ngx_http_js_request_proto_id, njs_argument(args, 0)); if (r == NULL) { njs_vm_error(vm, "\"this\" is not an external"); return NJS_ERROR; } ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); if (r->subrequest_in_memory) { njs_vm_error(vm, "subrequest can only be created for " "the primary request"); return NJS_ERROR; } if (ngx_js_string(vm, njs_arg(args, nargs, 1), &uri_arg) != NGX_OK) { njs_vm_error(vm, "failed to convert uri arg"); return NJS_ERROR; } if (uri_arg.length == 0) { njs_vm_error(vm, "uri is empty"); return NJS_ERROR; } options = NULL; callback = NULL; method = 0; methods_max = sizeof(ngx_http_methods) / sizeof(ngx_http_methods[0]); args_arg.length = 0; args_arg.start = NULL; has_body = 0; detached = 0; arg = njs_arg(args, nargs, 2); if (njs_value_is_string(arg)) { if (ngx_js_string(vm, arg, &args_arg) != NJS_OK) { njs_vm_error(vm, "failed to convert args"); return NJS_ERROR; } } else if (njs_value_is_function(arg)) { callback = arg; } else if (njs_value_is_object(arg)) { options = arg; } else if (!njs_value_is_null_or_undefined(arg)) { njs_vm_error(vm, "failed to convert args"); return NJS_ERROR; } if (options != NULL) { value = njs_vm_object_prop(vm, options, &args_key, &lvalue); if (value != NULL) { if (ngx_js_string(vm, value, &args_arg) != NGX_OK) { njs_vm_error(vm, "failed to convert options.args"); return NJS_ERROR; } } value = njs_vm_object_prop(vm, options, &detached_key, &lvalue); if (value != NULL) { detached = njs_value_bool(value); } value = njs_vm_object_prop(vm, options, &method_key, &lvalue); if (value != NULL) { if (ngx_js_string(vm, value, &method_name) != NGX_OK) { njs_vm_error(vm, "failed to convert options.method"); return NJS_ERROR; } while (method < methods_max) { if (method_name.length == ngx_http_methods[method].name.len && ngx_memcmp(method_name.start, ngx_http_methods[method].name.data, method_name.length) == 0) { break; } method++; } } value = njs_vm_object_prop(vm, options, &body_key, &lvalue); if (value != NULL) { if (ngx_js_string(vm, value, &body_arg) != NGX_OK) { njs_vm_error(vm, "failed to convert options.body"); return NJS_ERROR; } has_body = 1; } } if (ngx_http_js_parse_unsafe_uri(r, &uri_arg, &args_arg) != NGX_OK) { njs_vm_error(vm, "unsafe uri"); return NJS_ERROR; } arg = njs_arg(args, nargs, 3); if (callback == NULL && !njs_value_is_undefined(arg)) { if (!njs_value_is_function(arg)) { njs_vm_error(vm, "callback is not a function"); return NJS_ERROR; } else { callback = arg; } } if (detached && callback != NULL) { njs_vm_error(vm, "detached flag and callback are mutually exclusive"); return NJS_ERROR; } flags = NGX_HTTP_SUBREQUEST_BACKGROUND; njs_value_undefined_set(retval); if (!detached) { ps = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t)); if (ps == NULL) { njs_vm_memory_error(vm); return NJS_ERROR; } promise = !!(callback == NULL); event = njs_mp_zalloc(njs_vm_memory_pool(vm), sizeof(ngx_js_event_t) + promise * (sizeof(njs_opaque_value_t) * 2)); if (njs_slow_path(event == NULL)) { njs_vm_memory_error(vm); return NJS_ERROR; } event->fd = ctx->event_id++; if (promise) { event->args = (njs_opaque_value_t *) &event[1]; rc = njs_vm_promise_create(vm, retval, njs_value_arg(event->args)); if (rc != NJS_OK) { return NJS_ERROR; } callback = njs_value_arg(event->args); } njs_value_assign(&event->function, callback); ps->handler = ngx_http_js_subrequest_done; ps->data = event; flags |= NGX_HTTP_SUBREQUEST_IN_MEMORY; } else { ps = NULL; event = NULL; } uri.len = uri_arg.length; uri.data = uri_arg.start; rargs.len = args_arg.length; rargs.data = args_arg.start; if (ngx_http_subrequest(r, &uri, rargs.len ? &rargs : NULL, &sr, ps, flags) != NGX_OK) { njs_vm_error(vm, "subrequest creation failed"); return NJS_ERROR; } if (event != NULL) { ngx_js_add_event(ctx, event); } if (method != methods_max) { sr->method = ngx_http_methods[method].value; sr->method_name = ngx_http_methods[method].name; } else { sr->method = NGX_HTTP_UNKNOWN; sr->method_name.len = method_name.length; sr->method_name.data = method_name.start; } sr->header_only = (sr->method == NGX_HTTP_HEAD) || (callback == NULL); if (has_body) { rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); if (rb == NULL) { goto memory_error; } if (body_arg.length != 0) { rb->bufs = ngx_alloc_chain_link(r->pool); if (rb->bufs == NULL) { goto memory_error; } rb->bufs->next = NULL; rb->bufs->buf = ngx_calloc_buf(r->pool); if (rb->bufs->buf == NULL) { goto memory_error; } rb->bufs->buf->memory = 1; rb->bufs->buf->last_buf = 1; rb->bufs->buf->pos = body_arg.start; rb->bufs->buf->last = body_arg.start + body_arg.length; } sr->request_body = rb; sr->headers_in.content_length_n = body_arg.length; sr->headers_in.chunked = 0; } return NJS_OK; memory_error: njs_vm_error(vm, "internal error"); return NJS_ERROR; } static ngx_int_t ngx_http_js_subrequest_done(ngx_http_request_t *r, void *data, ngx_int_t rc) { ngx_js_event_t *event = data; njs_int_t ret; njs_vm_t *vm; ngx_http_js_ctx_t *ctx; njs_opaque_value_t reply; if (rc != NGX_OK || r->connection->error || r->buffered) { return rc; } ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); if (ctx && ctx->done) { return NGX_OK; } if (ctx == NULL) { ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_js_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } ngx_http_set_ctx(r, ctx, ngx_http_js_module); } ctx->done = 1; ctx = ngx_http_get_module_ctx(r->parent, ngx_http_js_module); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "js subrequest done s: %ui parent ctx: %p", r->headers_out.status, ctx); if (ctx == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "js subrequest: failed to get the parent context"); return NGX_ERROR; } vm = ctx->engine->u.njs.vm; ret = njs_vm_external_create(vm, njs_value_arg(&reply), ngx_http_js_request_proto_id, r, 0); if (ret != NJS_OK) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "js subrequest reply creation failed"); return NGX_ERROR; } rc = ngx_js_call(vm, njs_value_function(njs_value_arg(&event->function)), &reply, 1); ngx_js_del_event(ctx, event); ngx_http_js_event_finalize(r->parent, rc); return NGX_OK; } static njs_int_t ngx_http_js_ext_get_parent(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { ngx_http_js_ctx_t *ctx; ngx_http_request_t *r; r = njs_vm_external(vm, ngx_http_js_request_proto_id, value); if (r == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } ctx = r->parent ? ngx_http_get_module_ctx(r->parent, ngx_http_js_module) : NULL; if (ctx == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } njs_value_assign(retval, njs_value_arg(&ctx->args[0])); return NJS_OK; } static njs_int_t ngx_http_js_ext_get_response_body(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { size_t len; u_char *p; uint32_t buffer_type; njs_int_t ret; ngx_buf_t *b; njs_value_t *response_body; ngx_http_js_ctx_t *ctx; ngx_http_request_t *r; r = njs_vm_external(vm, ngx_http_js_request_proto_id, value); if (r == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); response_body = (njs_value_t *) &ctx->response_body; buffer_type = ngx_js_buffer_type(njs_vm_prop_magic32(prop)); if (!njs_value_is_null(response_body)) { if ((buffer_type == NGX_JS_BUFFER) == (uint32_t) njs_value_is_buffer(response_body)) { njs_value_assign(retval, response_body); return NJS_OK; } } b = r->out ? r->out->buf : NULL; if (b == NULL) { njs_value_undefined_set(retval); return NJS_OK; } len = b->last - b->pos; p = ngx_pnalloc(r->pool, len); if (p == NULL) { njs_vm_memory_error(vm); return NJS_ERROR; } if (len) { ngx_memcpy(p, b->pos, len); } ret = ngx_js_prop(vm, buffer_type, response_body, p, len); if (ret != NJS_OK) { return NJS_ERROR; } njs_value_assign(retval, response_body); return NJS_OK; } #if defined(nginx_version) && (nginx_version >= 1023000) static njs_int_t ngx_http_js_header_in(njs_vm_t *vm, ngx_http_request_t *r, unsigned flags, njs_str_t *name, njs_value_t *retval) { u_char *lowcase_key; ngx_uint_t hash; ngx_table_elt_t **ph; ngx_http_header_t *hh; ngx_http_core_main_conf_t *cmcf; if (retval == NULL) { return NJS_OK; } /* look up hashed headers */ lowcase_key = ngx_pnalloc(r->pool, name->length); if (lowcase_key == NULL) { njs_vm_memory_error(vm); return NJS_ERROR; } hash = ngx_hash_strlow(lowcase_key, name->start, name->length); cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); hh = ngx_hash_find(&cmcf->headers_in_hash, hash, lowcase_key, name->length); ph = NULL; if (hh) { if (hh->offset == offsetof(ngx_http_headers_in_t, cookie)) { flags |= NJS_HEADER_SEMICOLON; } ph = (ngx_table_elt_t **) ((char *) &r->headers_in + hh->offset); } return ngx_http_js_header_generic(vm, r, &r->headers_in.headers, ph, flags, name, retval); } static njs_int_t ngx_http_js_header_out(njs_vm_t *vm, ngx_http_request_t *r, unsigned flags, njs_str_t *name, njs_value_t *setval, njs_value_t *retval) { u_char *p; int64_t length; njs_value_t *array; njs_int_t rc; njs_str_t s; ngx_uint_t i; ngx_list_part_t *part; ngx_table_elt_t *header, *h, **ph; njs_opaque_value_t lvalue; if (retval != NULL && setval == NULL) { return ngx_http_js_header_generic(vm, r, &r->headers_out.headers, NULL, flags, name, retval); } part = &r->headers_out.headers.part; 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 || h->key.len != name->length || ngx_strncasecmp(h->key.data, name->start, name->length) != 0) { continue; } h->hash = 0; h->next = NULL; } if (retval == NULL) { return NJS_OK; } if (njs_value_is_array(setval)) { array = setval; rc = njs_vm_array_length(vm, array, &length); if (rc != NJS_OK) { return NJS_ERROR; } if (length == 0) { return NJS_OK; } } else { array = NULL; length = 1; } ph = &header; for (i = 0; i < (ngx_uint_t) length; i++) { if (array != NULL) { setval = njs_vm_array_prop(vm, array, i, &lvalue); } if (ngx_js_string(vm, setval, &s) != NGX_OK) { return NJS_ERROR; } if (s.length == 0) { continue; } h = ngx_list_push(&r->headers_out.headers); if (h == NULL) { njs_vm_memory_error(vm); return NJS_ERROR; } p = ngx_pnalloc(r->pool, name->length); if (p == NULL) { h->hash = 0; njs_vm_memory_error(vm); return NJS_ERROR; } ngx_memcpy(p, name->start, name->length); h->key.data = p; h->key.len = name->length; p = ngx_pnalloc(r->pool, s.length); if (p == NULL) { h->hash = 0; njs_vm_memory_error(vm); return NJS_ERROR; } ngx_memcpy(p, s.start, s.length); h->value.data = p; h->value.len = s.length; h->hash = 1; *ph = h; ph = &h->next; } *ph = NULL; return NJS_OK; } static njs_int_t ngx_http_js_header_out_special(njs_vm_t *vm, ngx_http_request_t *r, njs_str_t *v, njs_value_t *setval, njs_value_t *retval, ngx_table_elt_t **hh) { u_char *p; int64_t length; njs_int_t rc; njs_str_t s; ngx_uint_t i; ngx_list_t *headers; ngx_list_part_t *part; ngx_table_elt_t *header, *h; njs_opaque_value_t lvalue; headers = &r->headers_out.headers; if (retval != NULL && setval == NULL) { return ngx_http_js_header_out(vm, r, NJS_HEADER_SINGLE, v, setval, retval); } if (setval != NULL && njs_value_is_array(setval)) { rc = njs_vm_array_length(vm, setval, &length); if (rc != NJS_OK) { return NJS_ERROR; } setval = njs_vm_array_prop(vm, setval, length - 1, &lvalue); } if (ngx_js_string(vm, setval, &s) != NGX_OK) { return NJS_ERROR; } part = &headers->part; 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 == v->length && ngx_strncasecmp(h->key.data, v->start, v->length) == 0) { goto done; } } h = NULL; done: if (h != NULL && s.length == 0) { h->hash = 0; h = NULL; } if (h == NULL && s.length != 0) { h = ngx_list_push(headers); if (h == NULL) { njs_vm_memory_error(vm); return NJS_ERROR; } p = ngx_pnalloc(r->pool, v->length); if (p == NULL) { h->hash = 0; njs_vm_memory_error(vm); return NJS_ERROR; } ngx_memcpy(p, v->start, v->length); h->key.data = p; h->key.len = v->length; } if (h != NULL) { p = ngx_pnalloc(r->pool, s.length); if (p == NULL) { h->hash = 0; njs_vm_memory_error(vm); return NJS_ERROR; } ngx_memcpy(p, s.start, s.length); h->value.data = p; h->value.len = s.length; h->hash = 1; } if (hh != NULL) { *hh = h; } return NJS_OK; } static njs_int_t ngx_http_js_header_generic(njs_vm_t *vm, ngx_http_request_t *r, ngx_list_t *headers, ngx_table_elt_t **ph, unsigned flags, njs_str_t *name, njs_value_t *retval) { u_char sep; njs_chb_t chain; njs_int_t rc, ret; ngx_uint_t i; njs_value_t *value; ngx_list_part_t *part; ngx_table_elt_t *header, *h; if (ph == NULL) { /* iterate over all headers */ ph = &header; part = &headers->part; h = part->elts; for (i = 0; /* void */ ; i++) { if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; h = part->elts; i = 0; } if (h[i].hash == 0 || name->length != h[i].key.len || ngx_strncasecmp(name->start, h[i].key.data, name->length) != 0) { continue; } *ph = &h[i]; ph = &h[i].next; } *ph = NULL; ph = &header; } if (*ph == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } if (flags & NJS_HEADER_ARRAY) { rc = njs_vm_array_alloc(vm, retval, 4); if (rc != NJS_OK) { return NJS_ERROR; } for (h = *ph; h; h = h->next) { value = njs_vm_array_push(vm, retval); if (value == NULL) { return NJS_ERROR; } rc = njs_vm_value_string_create(vm, value, h->value.data, h->value.len); if (rc != NJS_OK) { return NJS_ERROR; } } return NJS_OK; } if ((*ph)->next == NULL || flags & NJS_HEADER_SINGLE) { return njs_vm_value_string_create(vm, retval, (*ph)->value.data, (*ph)->value.len); } NJS_CHB_MP_INIT(&chain, vm); sep = flags & NJS_HEADER_SEMICOLON ? ';' : ','; for (h = *ph; h; h = h->next) { njs_chb_append(&chain, h->value.data, h->value.len); njs_chb_append(&chain, &sep, 1); njs_chb_append_literal(&chain, " "); } ret = njs_vm_value_string_create_chb(vm, retval, &chain); njs_chb_destroy(&chain); return ret; } #endif static njs_int_t ngx_http_js_content_encoding(njs_vm_t *vm, ngx_http_request_t *r, unsigned flags, njs_str_t *v, njs_value_t *setval, njs_value_t *retval) { njs_int_t rc; ngx_table_elt_t *h; rc = ngx_http_js_header_out_special(vm, r, v, setval, retval, &h); if (rc == NJS_ERROR) { return NJS_ERROR; } if (setval != NULL || retval == NULL) { r->headers_out.content_encoding = h; } return NJS_OK; } static njs_int_t ngx_http_js_content_length(njs_vm_t *vm, ngx_http_request_t *r, unsigned flags, njs_str_t *v, njs_value_t *setval, njs_value_t *retval) { u_char *p; njs_int_t rc; ngx_int_t n; ngx_table_elt_t *h; u_char content_len[NGX_OFF_T_LEN]; if (retval != NULL && setval == NULL) { if (r->headers_out.content_length == NULL && r->headers_out.content_length_n >= 0) { p = ngx_sprintf(content_len, "%O", r->headers_out.content_length_n); return njs_vm_value_string_create(vm, retval, content_len, p - content_len); } } rc = ngx_http_js_header_out_special(vm, r, v, setval, retval, &h); if (rc == NJS_ERROR) { return NJS_ERROR; } if (setval != NULL || retval == NULL) { if (h != NULL) { n = ngx_atoi(h->value.data, h->value.len); if (n == NGX_ERROR) { h->hash = 0; njs_vm_error(vm, "failed converting argument " "to positive integer"); return NJS_ERROR; } r->headers_out.content_length = h; r->headers_out.content_length_n = n; } else { ngx_http_clear_content_length(r); } } return NJS_OK; } static njs_int_t ngx_http_js_content_type(njs_vm_t *vm, ngx_http_request_t *r, unsigned flags, njs_str_t *v, njs_value_t *setval, njs_value_t *retval) { int64_t length; njs_int_t rc; njs_str_t s; ngx_str_t *hdr; njs_opaque_value_t lvalue; if (retval != NULL && setval == NULL) { hdr = &r->headers_out.content_type; if (hdr->len == 0) { njs_value_undefined_set(retval); return NJS_OK; } return njs_vm_value_string_create(vm, retval, hdr->data, hdr->len); } if (setval != NULL && njs_value_is_array(setval)) { rc = njs_vm_array_length(vm, setval, &length); if (rc != NJS_OK) { return NJS_ERROR; } setval = njs_vm_array_prop(vm, setval, length - 1, &lvalue); } if (ngx_js_string(vm, setval, &s) != NGX_OK) { return NJS_ERROR; } r->headers_out.content_type.len = s.length; r->headers_out.content_type_len = r->headers_out.content_type.len; r->headers_out.content_type.data = s.start; r->headers_out.content_type_lowcase = NULL; return NJS_OK; } static njs_int_t ngx_http_js_date(njs_vm_t *vm, ngx_http_request_t *r, unsigned flags, njs_str_t *v, njs_value_t *setval, njs_value_t *retval) { njs_int_t rc; ngx_table_elt_t *h; rc = ngx_http_js_header_out_special(vm, r, v, setval, retval, &h); if (rc == NJS_ERROR) { return NJS_ERROR; } if (setval != NULL || retval == NULL) { r->headers_out.date = h; } return NJS_OK; } static njs_int_t ngx_http_js_last_modified(njs_vm_t *vm, ngx_http_request_t *r, unsigned flags, njs_str_t *v, njs_value_t *setval, njs_value_t *retval) { njs_int_t rc; ngx_table_elt_t *h; rc = ngx_http_js_header_out_special(vm, r, v, setval, retval, &h); if (rc == NJS_ERROR) { return NJS_ERROR; } if (setval != NULL || retval == NULL) { r->headers_out.last_modified = h; } return NJS_OK; } static njs_int_t ngx_http_js_location(njs_vm_t *vm, ngx_http_request_t *r, unsigned flags, njs_str_t *v, njs_value_t *setval, njs_value_t *retval) { njs_int_t rc; ngx_table_elt_t *h; rc = ngx_http_js_header_out_special(vm, r, v, setval, retval, &h); if (rc == NJS_ERROR) { return NJS_ERROR; } if (setval != NULL || retval == NULL) { r->headers_out.location = h; } return NJS_OK; } static njs_int_t ngx_http_js_server(njs_vm_t *vm, ngx_http_request_t *r, unsigned flags, njs_str_t *v, njs_value_t *setval, njs_value_t *retval) { njs_int_t rc; ngx_table_elt_t *h; rc = ngx_http_js_header_out_special(vm, r, v, setval, retval, &h); if (rc == NJS_ERROR) { return NJS_ERROR; } if (setval != NULL || retval == NULL) { r->headers_out.server = h; } return NJS_OK; } static void ngx_http_js_periodic_handler(ngx_event_t *ev) { ngx_int_t rc; ngx_msec_t timer; ngx_connection_t *c; ngx_js_periodic_t *periodic; ngx_http_js_ctx_t *ctx; ngx_http_request_t *r; ngx_http_connection_t hc; if (ngx_terminate || ngx_exiting) { return; } periodic = ev->data; timer = periodic->interval; if (periodic->jitter) { timer += (ngx_msec_t) ngx_random() % periodic->jitter; } ngx_add_timer(&periodic->event, timer); c = periodic->connection; if (c != NULL) { ngx_log_error(NGX_LOG_ERR, c->log, 0, "http js periodic \"%V\" is already running, killing " "previous instance", &periodic->method); ngx_http_js_periodic_finalize(c->data, NGX_ERROR); } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, &periodic->log, 0, "http js periodic handler: \"%V\"", &periodic->method); c = ngx_get_connection(0, &periodic->log); if (c == NULL) { return; } ngx_memzero(&hc, sizeof(ngx_http_connection_t)); hc.conf_ctx = periodic->conf_ctx; c->data = &hc; r = ngx_http_create_request(c); if (r == NULL) { ngx_free_connection(c); c->fd = (ngx_socket_t) -1; return; } c->data = r; c->destroyed = 0; c->pool = r->pool; c->read->log = &periodic->log; c->read->handler = ngx_http_js_periodic_shutdown_handler; c->write->log = &periodic->log; c->write->handler = ngx_http_js_periodic_write_handler; periodic->connection = c; periodic->log_ctx.request = r; periodic->log_ctx.connection = c; r->method = NGX_HTTP_GET; r->method_name = ngx_http_core_get_method; ngx_str_set(&r->uri, "/"); r->unparsed_uri = r->uri; r->valid_unparsed_uri = 1; r->health_check = 1; rc = ngx_http_js_init_vm(r, ngx_http_js_periodic_session_proto_id); if (rc != NGX_OK) { ngx_http_js_periodic_destroy(r, periodic); return; } ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); ctx->periodic = periodic; r->count++; rc = ctx->engine->call((ngx_js_ctx_t *) ctx, &periodic->method, &ctx->args[0], 1); if (rc == NGX_AGAIN) { rc = NGX_OK; } r->count--; ngx_http_js_periodic_finalize(r, rc); } static void ngx_http_js_periodic_write_handler(ngx_event_t *ev) { ngx_connection_t *c; ngx_http_js_ctx_t *ctx; ngx_http_request_t *r; c = ev->data; r = c->data; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http js periodic write handler"); ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); if (!ngx_js_ctx_pending(ctx)) { ngx_http_js_periodic_finalize(r, NGX_OK); return; } } static void ngx_http_js_periodic_shutdown_handler(ngx_event_t *ev) { ngx_connection_t *c; c = ev->data; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http js periodic shutdown handler"); if (c->close) { ngx_http_js_periodic_finalize(c->data, NGX_ERROR); return; } ngx_log_error(NGX_LOG_ERR, c->log, 0, "http js periodic shutdown handler " "while not closing"); } static void ngx_http_js_periodic_finalize(ngx_http_request_t *r, ngx_int_t rc) { ngx_http_js_ctx_t *ctx; ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http js periodic finalize: \"%V\" rc: %i c: %i pending: %i", &ctx->periodic->method, rc, r->count, ngx_js_ctx_pending(ctx)); if (r->count > 1 || (rc == NGX_OK && ngx_js_ctx_pending(ctx))) { return; } ngx_http_js_periodic_destroy(r, ctx->periodic); } static void ngx_http_js_periodic_destroy(ngx_http_request_t *r, ngx_js_periodic_t *periodic) { ngx_connection_t *c; c = r->connection; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http js periodic destroy: \"%V\"", &periodic->method); periodic->connection = NULL; r->logged = 1; ngx_http_free_request(r, NGX_OK); ngx_free_connection(c); c->fd = (ngx_socket_t) -1; c->pool = NULL; c->destroyed = 1; if (c->write->posted) { ngx_delete_posted_event(c->write); } } static ngx_int_t ngx_http_js_periodic_init(ngx_js_periodic_t *periodic) { ngx_log_t *log; ngx_msec_t jitter; ngx_http_core_loc_conf_t *clcf; clcf = ngx_http_get_module_loc_conf(periodic->conf_ctx, ngx_http_core_module); log = clcf->error_log; ngx_memcpy(&periodic->log, log, sizeof(ngx_log_t)); periodic->log.data = &periodic->log_ctx; periodic->connection = NULL; periodic->event.handler = ngx_http_js_periodic_handler; periodic->event.data = periodic; periodic->event.log = log; periodic->event.cancelable = 1; jitter = periodic->jitter ? (ngx_msec_t) ngx_random() % periodic->jitter : 0; ngx_add_timer(&periodic->event, jitter + 1); return NGX_OK; } static ngx_pool_t * ngx_http_js_pool(ngx_http_request_t *r) { return r->pool; } static ngx_resolver_t * ngx_http_js_resolver(ngx_http_request_t *r) { ngx_http_core_loc_conf_t *clcf; clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); return clcf->resolver; } static ngx_msec_t ngx_http_js_resolver_timeout(ngx_http_request_t *r) { ngx_http_core_loc_conf_t *clcf; clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); return clcf->resolver_timeout; } static ngx_msec_t ngx_http_js_fetch_timeout(ngx_http_request_t *r) { ngx_http_js_loc_conf_t *jlcf; jlcf = ngx_http_get_module_loc_conf(r, ngx_http_js_module); return jlcf->timeout; } static size_t ngx_http_js_buffer_size(ngx_http_request_t *r) { ngx_http_js_loc_conf_t *jlcf; jlcf = ngx_http_get_module_loc_conf(r, ngx_http_js_module); return jlcf->buffer_size; } static size_t ngx_http_js_max_response_buffer_size(ngx_http_request_t *r) { ngx_http_js_loc_conf_t *jlcf; jlcf = ngx_http_get_module_loc_conf(r, ngx_http_js_module); return jlcf->max_response_body_size; } static void ngx_http_js_event_finalize(ngx_http_request_t *r, ngx_int_t rc) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http js event finalize rc: %i", rc); if (rc == NGX_ERROR) { if (r->health_check) { ngx_http_js_periodic_finalize(r, NGX_ERROR); return; } ngx_http_finalize_request(r, NGX_ERROR); return; } if (rc == NGX_OK) { ngx_post_event(r->connection->write, &ngx_posted_events); } } static ngx_js_ctx_t * ngx_http_js_ctx(ngx_http_request_t *r) { return ngx_http_get_module_ctx(r, ngx_http_js_module); } static njs_int_t ngx_js_http_init(njs_vm_t *vm) { ngx_http_js_request_proto_id = njs_vm_external_prototype(vm, ngx_http_js_ext_request, njs_nitems(ngx_http_js_ext_request)); if (ngx_http_js_request_proto_id < 0) { return NJS_ERROR; } ngx_http_js_periodic_session_proto_id = njs_vm_external_prototype(vm, ngx_http_js_ext_periodic_session, njs_nitems(ngx_http_js_ext_periodic_session)); if (ngx_http_js_periodic_session_proto_id < 0) { return NJS_ERROR; } return NJS_OK; } static ngx_engine_t * ngx_engine_njs_clone(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *cf, njs_int_t proto_id, void *external) { njs_int_t rc; ngx_engine_t *engine; ngx_http_js_ctx_t *hctx; engine = ngx_njs_clone(ctx, cf, external); if (engine == NULL) { return NULL; } rc = njs_vm_external_create(engine->u.njs.vm, njs_value_arg(&ctx->args[0]), proto_id, njs_vm_external_ptr(engine->u.njs.vm), 0); if (rc != NJS_OK) { return NULL; } hctx = (ngx_http_js_ctx_t *) ctx; hctx->body_filter = ngx_http_njs_body_filter; return engine; } #if (NJS_HAVE_QUICKJS) static ngx_int_t ngx_http_qjs_query_string_decode(njs_chb_t *chain, const u_char *start, size_t size) { u_char *dst; uint32_t cp; const u_char *p, *end; njs_unicode_decode_t ctx; static const int8_t hex[256] njs_aligned(32) = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, }; njs_utf8_decode_init(&ctx); cp = 0; p = start; end = p + size; while (p < end) { if (*p == '%' && end - p > 2 && hex[p[1]] >= 0 && hex[p[2]] >= 0) { cp = njs_utf8_consume(&ctx, (hex[p[1]] << 4) | hex[p[2]]); p += 3; } else { if (*p == '+') { cp = ' '; p++; } else { cp = njs_utf8_decode(&ctx, &p, end); } } if (cp > NJS_UNICODE_MAX_CODEPOINT) { if (cp == NJS_UNICODE_CONTINUE) { continue; } cp = NJS_UNICODE_REPLACEMENT; } dst = njs_chb_reserve(chain, 4); if (dst == NULL) { return NGX_ERROR; } njs_chb_written(chain, njs_utf8_encode(dst, cp) - dst); } if (cp == NJS_UNICODE_CONTINUE) { dst = njs_chb_reserve(chain, 3); if (dst == NULL) { return NGX_ERROR; } njs_chb_written(chain, njs_utf8_encode(dst, NJS_UNICODE_REPLACEMENT) - dst); } return NGX_OK; } static JSValue ngx_http_qjs_ext_to_string_tag(JSContext *cx, JSValueConst this_val) { return JS_NewString(cx, "Request"); } static JSValue ngx_http_qjs_ext_args(JSContext *cx, JSValueConst this_val) { u_char *start, *end, *p, *v; uint32_t len; JSAtom key; JSValue args, val, prev, length, arr; njs_str_t decoded; njs_int_t ret; ngx_int_t rc; njs_chb_t chain; ngx_http_request_t *r; ngx_http_qjs_request_t *req; req = JS_GetOpaque(this_val, NGX_QJS_CLASS_ID_HTTP_REQUEST); if (req == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a request object"); } if (!JS_IsUndefined(req->args)) { return JS_DupValue(cx, req->args); } args = JS_NewObject(cx); if (JS_IsException(args)) { return JS_EXCEPTION; } NJS_CHB_CTX_INIT(&chain, cx); r = req->request; rc = ngx_http_qjs_query_string_decode(&chain, r->args.data, r->args.len); if (rc != NGX_OK) { njs_chb_destroy(&chain); return JS_ThrowOutOfMemory(cx); } ret = njs_chb_join(&chain, &decoded); njs_chb_destroy(&chain); if (ret != NJS_OK) { return JS_ThrowOutOfMemory(cx); } start = decoded.start; end = start + decoded.length; while (start < end) { p = ngx_strlchr(start, end, '&'); if (p == NULL) { p = end; } v = ngx_strlchr(start, p, '='); if (v == NULL) { v = p; } if (v == start) { start = p + 1; continue; } key = JS_NewAtomLen(cx, (const char *) start, v - start); if (key == JS_ATOM_NULL) { chain.free(cx, decoded.start); return JS_EXCEPTION; } val = qjs_string_create(cx, v + 1, p - v - 1); if (JS_IsException(val)) { chain.free(cx, decoded.start); JS_FreeAtom(cx, key); return JS_EXCEPTION; } prev = JS_GetProperty(cx, args, key); if (JS_IsException(prev)) { chain.free(cx, decoded.start); JS_FreeAtom(cx, key); JS_FreeValue(cx, val); return JS_EXCEPTION; } if (JS_IsUndefined(prev)) { if (JS_SetProperty(cx, args, key, val) < 0) { goto exception; } } else if (JS_IsArray(cx, prev)) { length = JS_GetPropertyStr(cx, prev, "length"); if (JS_ToUint32(cx, &len, length)) { goto exception; } JS_FreeValue(cx, length); if (JS_SetPropertyUint32(cx, prev, len, val) < 0) { goto exception; } JS_FreeValue(cx, prev); } else { arr = JS_NewArray(cx); if (JS_IsException(arr)) { goto exception; } if (JS_SetPropertyUint32(cx, arr, 0, prev) < 0) { goto exception; } if (JS_SetPropertyUint32(cx, arr, 1, val) < 0) { goto exception; } if (JS_SetProperty(cx, args, key, arr) < 0) { goto exception; } } JS_FreeAtom(cx, key); start = p + 1; } chain.free(cx, decoded.start); req->args = args; return JS_DupValue(cx, args); exception: chain.free(cx, decoded.start); JS_FreeAtom(cx, key); JS_FreeValue(cx, val); JS_FreeValue(cx, prev); return JS_EXCEPTION; } static JSValue ngx_http_qjs_ext_done(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv) { ngx_http_js_ctx_t *ctx; ngx_http_request_t *r; r = ngx_http_qjs_request(this_val); if (r == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a request object"); } ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); if (!ctx->filter) { return JS_ThrowTypeError(cx, "cannot set done while not filtering"); } ctx->done = 1; return JS_UNDEFINED; } static JSValue ngx_http_qjs_ext_finish(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv) { ngx_http_js_ctx_t *ctx; ngx_http_request_t *r; r = ngx_http_qjs_request(this_val); if (r == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a request object"); } if (ngx_http_send_special(r, NGX_HTTP_LAST) == NGX_ERROR) { return JS_ThrowInternalError(cx, "failed to send response"); } ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); ctx->status = NGX_OK; return JS_UNDEFINED; } static JSValue ngx_http_qjs_ext_headers_in(JSContext *cx, JSValueConst this_val) { JSValue obj; ngx_http_request_t *r; r = ngx_http_qjs_request(this_val); if (r == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a request object"); } obj = JS_NewObjectProtoClass(cx, JS_NULL, NGX_QJS_CLASS_ID_HTTP_HEADERS_IN); JS_SetOpaque(obj, r); return obj; } static JSValue ngx_http_qjs_ext_headers_out(JSContext *cx, JSValueConst this_val) { JSValue obj; ngx_http_request_t *r; r = ngx_http_qjs_request(this_val); if (r == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a request object"); } obj = JS_NewObjectProtoClass(cx, JS_NULL, NGX_QJS_CLASS_ID_HTTP_HEADERS_OUT); JS_SetOpaque(obj, r); return obj; } static JSValue ngx_http_qjs_ext_http_version(JSContext *cx, JSValueConst this_val) { ngx_str_t v; ngx_http_request_t *r; r = ngx_http_qjs_request(this_val); if (r == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a request object"); } 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; case NGX_HTTP_VERSION_11: ngx_str_set(&v, "1.1"); break; case NGX_HTTP_VERSION_20: ngx_str_set(&v, "2.0"); break; #if (NGX_HTTP_VERSION_30) case NGX_HTTP_VERSION_30: ngx_str_set(&v, "3.0"); break; #endif default: ngx_str_set(&v, ""); break; } return qjs_string_create(cx, v.data, v.len); } static JSValue ngx_http_qjs_ext_internal(JSContext *cx, JSValueConst this_val) { ngx_http_request_t *r; r = ngx_http_qjs_request(this_val); if (r == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a request object"); } return JS_NewBool(cx, r->internal); } static JSValue ngx_http_qjs_ext_internal_redirect(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv) { ngx_http_js_ctx_t *ctx; ngx_http_request_t *r; r = ngx_http_qjs_request(this_val); if (r == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a request object"); } if (r->parent != NULL) { return JS_ThrowTypeError(cx, "internalRedirect cannot be called from a subrequest"); } ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); if (ctx->filter) { return JS_ThrowTypeError(cx, "internalRedirect cannot be called while filtering"); } if (ngx_qjs_string(ctx->engine, argv[0], &ctx->redirect_uri) != NGX_OK) { return JS_EXCEPTION; } ctx->status = NGX_DONE; return JS_UNDEFINED; } static JSValue ngx_http_qjs_ext_log(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int level) { int n; const char *msg; ngx_http_request_t *r; r = ngx_http_qjs_request(this_val); if (r == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a request object"); } for (n = 0; n < argc; n++) { msg = JS_ToCString(cx, argv[n]); ngx_js_logger(r->connection, level, (u_char *) msg, ngx_strlen(msg)); JS_FreeCString(cx, msg); } return JS_UNDEFINED; } static JSValue ngx_http_qjs_ext_periodic_to_string_tag(JSContext *cx, JSValueConst this_val) { return JS_NewString(cx, "PeriodicSession"); } static JSValue ngx_http_qjs_ext_periodic_variables(JSContext *cx, JSValueConst this_val, int type) { JSValue obj; ngx_http_qjs_request_t *req; req = JS_GetOpaque(this_val, NGX_QJS_CLASS_ID_HTTP_PERIODIC); if (req == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a periodic object"); } obj = JS_NewObjectProtoClass(cx, JS_NULL, NGX_QJS_CLASS_ID_HTTP_VARS); /* * Using lowest bit of the pointer to store the buffer type. */ type = (type == NGX_JS_BUFFER) ? 1 : 0; JS_SetOpaque(obj, (void *) ((uintptr_t) req->request | (uintptr_t) type)); return obj; } static JSValue ngx_http_qjs_ext_parent(JSContext *cx, JSValueConst this_val) { ngx_http_js_ctx_t *ctx; ngx_http_request_t *r; r = ngx_http_qjs_request(this_val); if (r == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a request object"); } ctx = r->parent ? ngx_http_get_module_ctx(r->parent, ngx_http_js_module) : NULL; if (ctx == NULL) { return JS_UNDEFINED; } return JS_DupValue(cx, ngx_qjs_arg(ctx->args[0])); } static JSValue ngx_http_qjs_ext_remote_address(JSContext *cx, JSValueConst this_val) { ngx_connection_t *c; ngx_http_request_t *r; r = ngx_http_qjs_request(this_val); if (r == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a request object"); } c = r->connection; return qjs_string_create(cx, c->addr_text.data, c->addr_text.len); } static JSValue ngx_http_qjs_ext_response_body(JSContext *cx, JSValueConst this_val, int type) { u_char *p; size_t len; uint32_t buffer_type; ngx_buf_t *b; JSValue body; ngx_http_request_t *r; ngx_http_qjs_request_t *req; req = JS_GetOpaque(this_val, NGX_QJS_CLASS_ID_HTTP_REQUEST); if (req == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a request object"); } buffer_type = ngx_js_buffer_type(type); if (!JS_IsUndefined(req->response_body)) { if ((buffer_type == NGX_JS_STRING) == JS_IsString(req->response_body)) { return JS_DupValue(cx, req->response_body); } } r = req->request; b = r->out ? r->out->buf : NULL; if (b == NULL) { return JS_UNDEFINED; } len = b->last - b->pos; p = ngx_pnalloc(r->pool, len); if (p == NULL) { return JS_ThrowOutOfMemory(cx); } if (len) { ngx_memcpy(p, b->pos, len); } body = ngx_qjs_prop(cx, buffer_type, p, len); if (JS_IsException(body)) { return JS_EXCEPTION; } req->response_body = body; return JS_DupValue(cx, req->response_body); } static JSValue ngx_http_qjs_ext_request_body(JSContext *cx, JSValueConst this_val, int type) { u_char *p, *data; size_t len; JSValue body; uint32_t buffer_type; ngx_buf_t *buf; ngx_chain_t *cl; ngx_http_request_t *r; ngx_http_qjs_request_t *req; req = JS_GetOpaque(this_val, NGX_QJS_CLASS_ID_HTTP_REQUEST); if (req == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a request object"); } buffer_type = ngx_js_buffer_type(type); if (!JS_IsUndefined(req->request_body)) { if ((buffer_type == NGX_JS_STRING) == JS_IsString(req->request_body)) { return JS_DupValue(cx, req->request_body); } JS_FreeValue(cx, req->request_body); } r = req->request; if (r->request_body == NULL || r->request_body->bufs == NULL) { return JS_UNDEFINED; } if (r->request_body->temp_file) { return JS_ThrowTypeError(cx, "request body is in a file"); } cl = r->request_body->bufs; buf = cl->buf; if (cl->next == NULL) { len = buf->last - buf->pos; data = buf->pos; goto done; } len = buf->last - buf->pos; cl = cl->next; for ( /* void */ ; cl; cl = cl->next) { buf = cl->buf; len += buf->last - buf->pos; } p = ngx_pnalloc(r->pool, len); if (p == NULL) { return JS_ThrowOutOfMemory(cx); } data = p; cl = r->request_body->bufs; for ( /* void */ ; cl; cl = cl->next) { buf = cl->buf; p = ngx_cpymem(p, buf->pos, buf->last - buf->pos); } done: body = ngx_qjs_prop(cx, buffer_type, data, len); if (JS_IsException(body)) { return JS_EXCEPTION; } req->request_body = body; return JS_DupValue(cx, req->request_body); } static JSValue ngx_http_qjs_ext_return(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv) { ngx_str_t body; ngx_int_t status; ngx_http_js_ctx_t *ctx; ngx_http_request_t *r; ngx_http_complex_value_t cv; r = ngx_http_qjs_request(this_val); if (r == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a request object"); } if (ngx_qjs_integer(cx, argv[0], &status) != NGX_OK) { return JS_EXCEPTION; } if (status < 0 || status > 999) { return JS_ThrowRangeError(cx, "code is out of range"); } ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); if (status < NGX_HTTP_BAD_REQUEST || !JS_IsNullOrUndefined(argv[1])) { if (ngx_qjs_string(ctx->engine, argv[1], &body) != NGX_OK) { return JS_ThrowOutOfMemory(cx); } ngx_memzero(&cv, sizeof(ngx_http_complex_value_t)); cv.value.data = body.data; cv.value.len = body.len; ctx->status = ngx_http_send_response(r, status, NULL, &cv); if (ctx->status == NGX_ERROR) { return JS_ThrowTypeError(cx, "failed to send response"); } } else { ctx->status = status; } return JS_UNDEFINED; } static JSValue ngx_http_qjs_ext_status_get(JSContext *cx, JSValueConst this_val) { ngx_http_request_t *r; r = ngx_http_qjs_request(this_val); if (r == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a request object"); } return JS_NewInt32(cx, r->headers_out.status); } static JSValue ngx_http_qjs_ext_status_set(JSContext *cx, JSValueConst this_val, JSValueConst value) { ngx_int_t n; ngx_http_request_t *r; r = ngx_http_qjs_request(this_val); if (r == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a request object"); } if (ngx_qjs_integer(cx, value, &n) != NGX_OK) { return JS_EXCEPTION; } r->headers_out.status = n; r->headers_out.status_line.len = 0; return JS_UNDEFINED; } static JSValue ngx_http_qjs_ext_string(JSContext *cx, JSValueConst this_val, int offset) { ngx_str_t *field; ngx_http_request_t *r; r = ngx_http_qjs_request(this_val); if (r == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a request object"); } field = (ngx_str_t *) ((u_char *) r + offset); return qjs_string_create(cx, field->data, field->len); } static JSValue ngx_http_qjs_ext_send(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv) { ngx_str_t s; ngx_buf_t *b; ngx_uint_t n; ngx_chain_t *out, *cl, **ll; ngx_http_js_ctx_t *ctx; ngx_http_request_t *r; r = ngx_http_qjs_request(this_val); if (r == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a request object"); } ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); if (ctx->filter) { return JS_ThrowTypeError(cx, "cannot send while in body filter"); } out = NULL; ll = &out; for (n = 0; n < (ngx_uint_t) argc; n++) { if (ngx_qjs_string(ctx->engine, argv[n], &s) != NGX_OK) { return JS_ThrowTypeError(cx, "failed to convert arg"); } if (s.len == 0) { continue; } b = ngx_calloc_buf(r->pool); if (b == NULL) { return JS_ThrowInternalError(cx, "failed to allocate buffer"); } 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 JS_ThrowInternalError(cx, "failed to allocate chain link"); } cl->buf = b; *ll = cl; ll = &cl->next; } *ll = NULL; if (ngx_http_output_filter(r, out) == NGX_ERROR) { return JS_ThrowInternalError(cx, "failed to send response"); } return JS_UNDEFINED; } static JSValue ngx_http_qjs_ext_send_buffer(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv) { unsigned last_buf, flush; JSValue flags, value; ngx_str_t buffer; ngx_buf_t *b; ngx_chain_t *cl; ngx_http_js_ctx_t *ctx; ngx_http_request_t *r; r = ngx_http_qjs_request(this_val); if (r == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a request object"); } ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); if (!ctx->filter) { return JS_ThrowTypeError(cx, "cannot send buffer while not filtering"); } if (ngx_qjs_string(ctx->engine, argv[0], &buffer) != NGX_OK) { return JS_ThrowTypeError(cx, "failed get buffer arg"); } flush = ctx->buf->flush; last_buf = ctx->buf->last_buf; flags = argv[1]; if (JS_IsObject(flags)) { value = JS_GetPropertyStr(cx, flags, "flush"); if (JS_IsException(value)) { return JS_EXCEPTION; } flush = JS_ToBool(cx, value); JS_FreeValue(cx, value); value = JS_GetPropertyStr(cx, flags, "last"); if (JS_IsException(value)) { return JS_EXCEPTION; } last_buf = JS_ToBool(cx, value); JS_FreeValue(cx, value); } cl = ngx_chain_get_free_buf(r->pool, &ctx->free); if (cl == NULL) { return JS_ThrowOutOfMemory(cx); } b = cl->buf; b->flush = flush; b->last_buf = last_buf; b->memory = (buffer.len ? 1 : 0); b->sync = (buffer.len ? 0 : 1); b->tag = (ngx_buf_tag_t) &ngx_http_js_module; b->start = buffer.data; b->end = buffer.data + buffer.len; b->pos = b->start; b->last = b->end; *ctx->last_out = cl; ctx->last_out = &cl->next; return JS_UNDEFINED; } static JSValue ngx_http_qjs_ext_send_header(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv) { ngx_http_request_t *r; r = ngx_http_qjs_request(this_val); if (r == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a request object"); } if (ngx_http_set_content_type(r) != NGX_OK) { return JS_ThrowInternalError(cx, "failed to set content type"); } if (ngx_http_send_header(r) == NGX_ERROR) { return JS_ThrowInternalError(cx, "failed to send header"); } return JS_UNDEFINED; } static JSValue ngx_http_qjs_ext_set_return_value(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv) { ngx_js_ctx_t *ctx; ngx_http_request_t *r; r = ngx_http_qjs_request(this_val); if (r == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a request object"); } ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); JS_FreeValue(cx, ngx_qjs_arg(ctx->retval)); ngx_qjs_arg(ctx->retval) = JS_DupValue(cx, argv[0]); return JS_UNDEFINED; } static ngx_int_t ngx_http_qjs_subrequest_done(ngx_http_request_t *r, void *data, ngx_int_t rc) { ngx_qjs_event_t *event = data; JSValue reply; JSContext *cx; ngx_http_js_ctx_t *ctx, *sctx; if (rc != NGX_OK || r->connection->error || r->buffered) { return rc; } sctx = ngx_http_get_module_ctx(r, ngx_http_js_module); if (sctx && sctx->done) { return NGX_OK; } if (sctx == NULL) { sctx = ngx_pcalloc(r->pool, sizeof(ngx_http_js_ctx_t)); if (sctx == NULL) { return NGX_ERROR; } ngx_http_set_ctx(r, sctx, ngx_http_js_module); ngx_qjs_arg(sctx->response_body) = JS_UNDEFINED; } sctx->done = 1; ctx = ngx_http_get_module_ctx(r->parent, ngx_http_js_module); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "js subrequest done s: %ui parent ctx: %p", r->headers_out.status, ctx); if (ctx == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "js subrequest: failed to get the parent context"); return NGX_ERROR; } cx = ctx->engine->u.qjs.ctx; if (!JS_IsObject(ngx_qjs_arg(sctx->args[0]))) { reply = ngx_http_qjs_request_make(cx, NGX_QJS_CLASS_ID_HTTP_REQUEST, r); if (JS_IsException(reply)) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "js subrequest reply creation failed"); return NGX_ERROR; } } else { reply = JS_DupValue(cx, ngx_qjs_arg(sctx->args[0])); } rc = ngx_qjs_call((ngx_js_ctx_t *) ctx, event->function, &reply, 1); JS_FreeValue(cx, reply); ngx_js_del_event(ctx, event); ngx_http_js_event_finalize(r->parent, rc); return NGX_OK; } static void ngx_http_js_subrequest_event_destructor(ngx_qjs_event_t *event) { JSContext *cx; cx = event->ctx; JS_FreeValue(cx, event->function); JS_FreeValue(cx, event->args[0]); JS_FreeValue(cx, event->args[1]); } static JSValue ngx_http_qjs_ext_subrequest(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv) { JSValue arg, options, callback, value, retval; ngx_int_t rc; ngx_str_t uri, args, method_name, body_arg; ngx_uint_t method, methods_max, has_body, detached, flags, promise; ngx_qjs_event_t *event; ngx_http_js_ctx_t *ctx; ngx_http_request_t *r, *sr; ngx_http_request_body_t *rb; ngx_http_post_subrequest_t *ps; r = ngx_http_qjs_request(this_val); if (r == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a request object"); } ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); if (r->subrequest_in_memory) { return JS_ThrowTypeError(cx, "subrequest can only be created for " "the primary request"); } if (ngx_qjs_string(ctx->engine, argv[0], &uri) != NGX_OK) { return JS_ThrowTypeError(cx, "failed to convert uri arg"); } if (uri.len == 0) { return JS_ThrowTypeError(cx, "uri is empty"); } options = JS_UNDEFINED; callback = JS_UNDEFINED; method = 0; methods_max = sizeof(ngx_http_methods) / sizeof(ngx_http_methods[0]); args.len = 0; args.data = NULL; method_name.len = 0; method_name.data = NULL; has_body = 0; detached = 0; arg = argv[1]; if (JS_IsString(arg)) { if (ngx_qjs_string(ctx->engine, arg, &args) != NGX_OK) { return JS_ThrowTypeError(cx, "failed to convert args"); } } else if (JS_IsFunction(cx, arg)) { callback = arg; } else if (JS_IsObject(arg)) { options = arg; } else if (!JS_IsNullOrUndefined(arg)) { return JS_ThrowTypeError(cx, "failed to convert args"); } if (!JS_IsUndefined(options)) { value = JS_GetPropertyStr(cx, options, "args"); if (JS_IsException(value)) { return JS_EXCEPTION; } if (!JS_IsUndefined(value)) { rc = ngx_qjs_string(ctx->engine, value, &args); JS_FreeValue(cx, value); if (rc != NGX_OK) { return JS_ThrowTypeError(cx, "failed to convert options.args"); } } value = JS_GetPropertyStr(cx, options, "detached"); if (JS_IsException(value)) { return JS_EXCEPTION; } if (!JS_IsUndefined(value)) { detached = JS_ToBool(cx, value); JS_FreeValue(cx, value); } value = JS_GetPropertyStr(cx, options, "method"); if (JS_IsException(value)) { return JS_EXCEPTION; } if (!JS_IsUndefined(value)) { rc = ngx_qjs_string(ctx->engine, value, &method_name); JS_FreeValue(cx, value); if (rc != NGX_OK) { return JS_ThrowTypeError(cx, "failed to convert option.method"); } while (method < methods_max) { if (method_name.len == ngx_http_methods[method].name.len && ngx_memcmp(method_name.data, ngx_http_methods[method].name.data, method_name.len) == 0) { break; } method++; } } value = JS_GetPropertyStr(cx, options, "body"); if (JS_IsException(value)) { return JS_EXCEPTION; } if (!JS_IsUndefined(value)) { rc = ngx_qjs_string(ctx->engine, value, &body_arg); JS_FreeValue(cx, value); if (rc != NGX_OK) { return JS_ThrowTypeError(cx, "failed to convert option.body"); } has_body = 1; } } flags = NGX_HTTP_LOG_UNSAFE; if (ngx_http_parse_unsafe_uri(r, &uri, &args, &flags) != NGX_OK) { return JS_ThrowTypeError(cx, "unsafe uri"); } arg = argv[2]; if (JS_IsUndefined(callback) && !JS_IsNullOrUndefined(arg)) { if (!JS_IsFunction(cx, arg)) { return JS_ThrowTypeError(cx, "callback is not a function"); } callback = arg; } if (detached && !JS_IsUndefined(callback)) { return JS_ThrowTypeError(cx, "detached flag and callback are mutually " "exclusive"); } retval = JS_UNDEFINED; flags = NGX_HTTP_SUBREQUEST_BACKGROUND; if (!detached) { ps = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t)); if (ps == NULL) { return JS_ThrowOutOfMemory(cx); } promise = !!JS_IsUndefined(callback); event = ngx_pcalloc(r->pool, sizeof(ngx_qjs_event_t) + sizeof(JSValue) * 2); if (event == NULL) { return JS_ThrowOutOfMemory(cx); } event->ctx = cx; event->fd = ctx->event_id++; event->args = (JSValue *) &event[1]; event->destructor = ngx_http_js_subrequest_event_destructor; if (promise) { retval = JS_NewPromiseCapability(cx, &event->args[0]); if (JS_IsException(retval)) { return JS_EXCEPTION; } callback = event->args[0]; } else { event->args[0] = JS_UNDEFINED; event->args[1] = JS_UNDEFINED; } event->function = JS_DupValue(cx, callback); ps->handler = ngx_http_qjs_subrequest_done; ps->data = event; flags |= NGX_HTTP_SUBREQUEST_IN_MEMORY; } else { ps = NULL; event = NULL; } if (ngx_http_subrequest(r, &uri, args.len ? &args : NULL, &sr, ps, flags) != NGX_OK) { return JS_ThrowInternalError(cx, "subrequest creation failed"); } if (event != NULL) { ngx_js_add_event(ctx, event); } if (method != methods_max) { sr->method = ngx_http_methods[method].value; sr->method_name = ngx_http_methods[method].name; } else { sr->method = NGX_HTTP_UNKNOWN; sr->method_name = method_name; } sr->header_only = (sr->method == NGX_HTTP_HEAD) || JS_IsUndefined(callback); if (has_body) { rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); if (rb == NULL) { goto memory_error; } if (body_arg.len != 0) { rb->bufs = ngx_alloc_chain_link(r->pool); if (rb->bufs == NULL) { goto memory_error; } rb->bufs->next = NULL; rb->bufs->buf = ngx_calloc_buf(r->pool); if (rb->bufs->buf == NULL) { goto memory_error; } rb->bufs->buf->memory = 1; rb->bufs->buf->last_buf = 1; rb->bufs->buf->pos = body_arg.data; rb->bufs->buf->last = body_arg.data + body_arg.len; } sr->request_body = rb; sr->headers_in.content_length_n = body_arg.len; sr->headers_in.chunked = 0; } return retval; memory_error: return JS_ThrowOutOfMemory(cx); } static JSValue ngx_http_qjs_ext_raw_headers(JSContext *cx, JSValueConst this_val, int out) { JSValue array, elem, key, val; uint32_t idx; ngx_uint_t i; ngx_list_t *headers; ngx_list_part_t *part; ngx_table_elt_t *header, *h; ngx_http_request_t *r; r = ngx_http_qjs_request(this_val); if (r == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a request object"); } headers = (out) ? &r->headers_out.headers : &r->headers_in.headers; array = JS_NewArray(cx); if (JS_IsException(array)) { return JS_EXCEPTION; } idx = 0; part = &headers->part; 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; } elem = JS_NewArray(cx); if (JS_IsException(elem)) { JS_FreeValue(cx, array); return JS_EXCEPTION; } if (JS_DefinePropertyValueUint32(cx, array, idx++, elem, JS_PROP_C_W_E) < 0) { JS_FreeValue(cx, elem); JS_FreeValue(cx, array); return JS_EXCEPTION; } key = qjs_string_create(cx, h->key.data, h->key.len); if (JS_IsException(key)) { JS_FreeValue(cx, array); return JS_EXCEPTION; } if (JS_DefinePropertyValueUint32(cx, elem, 0, key, JS_PROP_C_W_E) < 0) { JS_FreeValue(cx, key); JS_FreeValue(cx, array); return JS_EXCEPTION; } val = qjs_string_create(cx, h->value.data, h->value.len); if (JS_IsException(val)) { JS_FreeValue(cx, array); return JS_EXCEPTION; } if (JS_DefinePropertyValueUint32(cx, elem, 1, val, JS_PROP_C_W_E) < 0) { JS_FreeValue(cx, val); JS_FreeValue(cx, array); return JS_EXCEPTION; } } return array; } static JSValue ngx_http_qjs_ext_variables(JSContext *cx, JSValueConst this_val, int type) { JSValue obj; ngx_http_request_t *r; r = ngx_http_qjs_request(this_val); if (r == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a request object"); } obj = JS_NewObjectProtoClass(cx, JS_NULL, NGX_QJS_CLASS_ID_HTTP_VARS); /* * Using lowest bit of the pointer to store the buffer type. */ type = (type == NGX_JS_BUFFER) ? 1 : 0; JS_SetOpaque(obj, (void *) ((uintptr_t) r | (uintptr_t) type)); return obj; } static int ngx_http_qjs_variables_own_property(JSContext *cx, JSPropertyDescriptor *pdesc, JSValueConst obj, JSAtom prop) { uint32_t buffer_type; ngx_str_t name; ngx_uint_t i, key, start, length, is_capture; ngx_http_request_t *r; ngx_http_variable_value_t *vv; r = JS_GetOpaque(obj, NGX_QJS_CLASS_ID_HTTP_VARS); buffer_type = ((uintptr_t) r & 1) ? NGX_JS_BUFFER : NGX_JS_STRING; r = (ngx_http_request_t *) ((uintptr_t) r & ~(uintptr_t) 1); if (r == NULL) { (void) JS_ThrowInternalError(cx, "\"this\" is not a request object"); return -1; } name.data = (u_char *) JS_AtomToCString(cx, prop); if (name.data == NULL) { return -1; } name.len = ngx_strlen(name.data); is_capture = 1; for (i = 0; i < name.len; i++) { if (name.data[i] < '0' || name.data[i] > '9') { is_capture = 0; break; } } if (is_capture) { key = ngx_atoi(name.data, name.len) * 2; JS_FreeCString(cx, (char *) name.data); if (r->captures == NULL || r->captures_data == NULL || r->ncaptures <= key) { return 0; } if (pdesc != NULL) { pdesc->flags = JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE; pdesc->getter = JS_UNDEFINED; pdesc->setter = JS_UNDEFINED; start = r->captures[key]; length = r->captures[key + 1] - start; pdesc->value = ngx_qjs_prop(cx, buffer_type, &r->captures_data[start], length); } return 1; } key = ngx_hash_strlow(name.data, name.data, name.len); vv = ngx_http_get_variable(r, &name, key); JS_FreeCString(cx, (char *) name.data); if (vv == NULL || vv->not_found) { return 0; } if (pdesc != NULL) { pdesc->flags = JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE; pdesc->getter = JS_UNDEFINED; pdesc->setter = JS_UNDEFINED; pdesc->value = ngx_qjs_prop(cx, buffer_type, vv->data, vv->len); } return 1; } static int ngx_http_qjs_variables_set_property(JSContext *cx, JSValueConst obj, JSAtom prop, JSValueConst value, JSValueConst receiver, int flags) { ngx_str_t name, s; ngx_uint_t key; ngx_http_js_ctx_t *ctx; ngx_http_request_t *r; ngx_http_variable_t *v; ngx_http_variable_value_t *vv; ngx_http_core_main_conf_t *cmcf; r = JS_GetOpaque(obj, NGX_QJS_CLASS_ID_HTTP_VARS); r = (ngx_http_request_t *) ((uintptr_t) r & ~(uintptr_t) 1); if (r == NULL) { (void) JS_ThrowInternalError(cx, "\"this\" is not a request object"); return -1; } name.data = (u_char *) JS_AtomToCString(cx, prop); if (name.data == NULL) { return -1; } name.len = ngx_strlen(name.data); key = ngx_hash_strlow(name.data, name.data, name.len); cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); v = ngx_hash_find(&cmcf->variables_hash, key, name.data, name.len); JS_FreeCString(cx, (char *) name.data); if (v == NULL) { (void) JS_ThrowInternalError(cx, "variable not found"); return -1; } ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); if (ngx_qjs_string(ctx->engine, value, &s) != NGX_OK) { return -1; } if (v->set_handler != NULL) { vv = ngx_pcalloc(r->pool, sizeof(ngx_http_variable_value_t)); if (vv == NULL) { (void) JS_ThrowOutOfMemory(cx); return -1; } vv->valid = 1; vv->not_found = 0; vv->data = s.data; vv->len = s.len; v->set_handler(r, vv, v->data); return 1; } if (!(v->flags & NGX_HTTP_VAR_INDEXED)) { (void) JS_ThrowTypeError(cx, "variable is not writable"); return -1; } vv = &r->variables[v->index]; vv->valid = 1; vv->not_found = 0; vv->data = ngx_pnalloc(r->pool, s.len); if (vv->data == NULL) { vv->valid = 0; (void) JS_ThrowOutOfMemory(cx); return -1; } vv->len = s.len; ngx_memcpy(vv->data, s.data, vv->len); return 1; } static int ngx_http_qjs_ext_keys_header(JSContext *cx, ngx_list_t *headers, JSValue keys, JSPropertyEnum **ptab, uint32_t *plen) { JSAtom key; ngx_uint_t item; ngx_list_part_t *part; ngx_table_elt_t *header, *h; part = &headers->part; item = 0; while (part) { if (item >= part->nelts) { part = part->next; item = 0; continue; } header = part->elts; h = &header[item++]; if (h->hash == 0) { continue; } key = JS_NewAtomLen(cx, (const char *) h->key.data, h->key.len); if (key == JS_ATOM_NULL) { return -1; } if (JS_DefinePropertyValue(cx, keys, key, JS_UNDEFINED, JS_PROP_ENUMERABLE) < 0) { JS_FreeAtom(cx, key); return -1; } JS_FreeAtom(cx, key); } return JS_GetOwnPropertyNames(cx, ptab, plen, keys, JS_GPN_STRING_MASK); } static int ngx_http_qjs_headers_in_own_property_names(JSContext *cx, JSPropertyEnum **ptab, uint32_t *plen, JSValueConst obj) { int ret; JSValue keys; ngx_http_request_t *r; r = JS_GetOpaque(obj, NGX_QJS_CLASS_ID_HTTP_HEADERS_IN); if (r == NULL) { (void) JS_ThrowInternalError(cx, "\"this\" is not a headers_in object"); return -1; } keys = JS_NewObject(cx); if (JS_IsException(keys)) { return -1; } ret = ngx_http_qjs_ext_keys_header(cx, &r->headers_in.headers, keys, ptab, plen); JS_FreeValue(cx, keys); return ret; } static njs_int_t ngx_http_qjs_header_generic(JSContext *cx, ngx_http_request_t *r, ngx_list_t *headers, ngx_table_elt_t **ph, ngx_str_t *name, JSPropertyDescriptor *pdesc, unsigned flags) { int ret; u_char sep; njs_chb_t chain; JSValue val; ngx_uint_t i; ngx_list_part_t *part; ngx_table_elt_t *header, *h; if (ph == NULL) { /* iterate over all headers */ ph = &header; part = &headers->part; h = part->elts; for (i = 0; /* void */ ; i++) { if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; h = part->elts; i = 0; } if (h[i].hash == 0 || name->len != h[i].key.len || ngx_strncasecmp(name->data, h[i].key.data, name->len) != 0) { continue; } *ph = &h[i]; ph = &h[i].next; } *ph = NULL; ph = &header; } if (*ph == NULL) { return 0; } if (flags & NJS_HEADER_ARRAY) { if (pdesc == NULL) { return 1; } pdesc->flags = JS_PROP_ENUMERABLE; pdesc->getter = JS_UNDEFINED; pdesc->setter = JS_UNDEFINED; pdesc->value = JS_NewArray(cx); if (JS_IsException(pdesc->value)) { return -1; } for (h = *ph, i = 0; h; h = h->next, i++) { val = qjs_string_create(cx, h->value.data, h->value.len); if (JS_IsException(val)) { JS_FreeValue(cx, pdesc->value); return -1; } if (JS_DefinePropertyValueUint32(cx, pdesc->value, i, val, JS_PROP_ENUMERABLE) < 0) { JS_FreeValue(cx, pdesc->value); return -1; } } return 1; } if ((*ph)->next == NULL || flags & NJS_HEADER_SINGLE) { if (pdesc != NULL) { pdesc->flags = JS_PROP_ENUMERABLE; pdesc->getter = JS_UNDEFINED; pdesc->setter = JS_UNDEFINED; pdesc->value = qjs_string_create(cx, (*ph)->value.data, (*ph)->value.len); if (JS_IsException(pdesc->value)) { return -1; } } return 1; } NJS_CHB_CTX_INIT(&chain, cx); sep = flags & NJS_HEADER_SEMICOLON ? ';' : ','; for (h = *ph; h; h = h->next) { njs_chb_append(&chain, h->value.data, h->value.len); njs_chb_append(&chain, &sep, 1); njs_chb_append_literal(&chain, " "); } ret = 1; if (pdesc != NULL) { pdesc->flags = JS_PROP_ENUMERABLE; pdesc->getter = JS_UNDEFINED; pdesc->setter = JS_UNDEFINED; pdesc->value = qjs_string_create_chb(cx, &chain); if (JS_IsException(pdesc->value)) { ret = -1; goto done; } } done: njs_chb_destroy(&chain); return ret; } static int ngx_http_qjs_header_in(JSContext *cx, ngx_http_request_t *r, unsigned flags, ngx_str_t *name, JSPropertyDescriptor *pdesc) { u_char *lowcase_key; ngx_uint_t hash; ngx_table_elt_t **ph; ngx_http_header_t *hh; ngx_http_core_main_conf_t *cmcf; /* look up hashed headers */ lowcase_key = ngx_pnalloc(r->pool, name->len); if (lowcase_key == NULL) { (void) JS_ThrowOutOfMemory(cx); return -1; } hash = ngx_hash_strlow(lowcase_key, name->data, name->len); cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); hh = ngx_hash_find(&cmcf->headers_in_hash, hash, lowcase_key, name->len); ph = NULL; if (hh) { if (hh->offset == offsetof(ngx_http_headers_in_t, cookie)) { flags |= NJS_HEADER_SEMICOLON; } ph = (ngx_table_elt_t **) ((char *) &r->headers_in + hh->offset); } return ngx_http_qjs_header_generic(cx, r, &r->headers_in.headers, ph, name, pdesc, flags); } static int ngx_http_qjs_headers_in_own_property(JSContext *cx, JSPropertyDescriptor *pdesc, JSValueConst obj, JSAtom prop) { int ret; unsigned flags; ngx_str_t name, *h; ngx_http_request_t *r; static ngx_str_t single_headers_in[] = { ngx_string("Content-Type"), ngx_string("ETag"), ngx_string("From"), ngx_string("Max-Forwards"), ngx_string("Referer"), ngx_string("Proxy-Authorization"), ngx_string("User-Agent"), ngx_string(""), }; r = JS_GetOpaque(obj, NGX_QJS_CLASS_ID_HTTP_HEADERS_IN); if (r == NULL) { (void) JS_ThrowInternalError(cx, "\"this\" is not a headers_in object"); return -1; } name.data = (u_char *) JS_AtomToCString(cx, prop); if (name.data == NULL) { return -1; } name.len = ngx_strlen(name.data); flags = 0; for (h = single_headers_in; h->len > 0; h++) { if (h->len == name.len && ngx_strncasecmp(h->data, name.data, name.len) == 0) { flags |= NJS_HEADER_SINGLE; break; } } ret = ngx_http_qjs_header_in(cx, r, flags, &name, pdesc); JS_FreeCString(cx, (char *) name.data); return ret; } static int ngx_http_qjs_headers_out_own_property_names(JSContext *cx, JSPropertyEnum **ptab, uint32_t *plen, JSValueConst obj) { int ret; JSAtom key; JSValue keys; ngx_http_request_t *r; r = JS_GetOpaque(obj, NGX_QJS_CLASS_ID_HTTP_HEADERS_OUT); if (r == NULL) { (void) JS_ThrowInternalError(cx, "\"this\" is not a headers_out" " object"); return -1; } keys = JS_NewObject(cx); if (JS_IsException(keys)) { return -1; } if (r->headers_out.content_type.len) { key = JS_NewAtomLen(cx, "Content-Type", njs_length("Content-Type")); if (key == JS_ATOM_NULL) { return -1; } if (JS_DefinePropertyValue(cx, keys, key, JS_UNDEFINED, JS_PROP_ENUMERABLE) < 0) { JS_FreeAtom(cx, key); return -1; } JS_FreeAtom(cx, key); } if (r->headers_out.content_length == NULL && r->headers_out.content_length_n >= 0) { key = JS_NewAtomLen(cx, "Content-Length", njs_length("Content-Length")); if (key == JS_ATOM_NULL) { return -1; } if (JS_DefinePropertyValue(cx, keys, key, JS_UNDEFINED, JS_PROP_ENUMERABLE) < 0) { JS_FreeAtom(cx, key); return -1; } JS_FreeAtom(cx, key); } ret = ngx_http_qjs_ext_keys_header(cx, &r->headers_out.headers, keys, ptab, plen); JS_FreeValue(cx, keys); return ret; } static int ngx_http_qjs_headers_out_handler(JSContext *cx, ngx_http_request_t *r, ngx_str_t *name, JSPropertyDescriptor *pdesc, JSValue *value, unsigned flags) { u_char *p; int64_t length; uint32_t i; ngx_int_t rc; ngx_str_t s; JSValue v; ngx_list_part_t *part; ngx_table_elt_t *header, *h, **ph; ngx_http_js_ctx_t *ctx; if (flags & NJS_HEADER_GET) { return ngx_http_qjs_header_generic(cx, r, &r->headers_out.headers, NULL, name, pdesc, flags); } part = &r->headers_out.headers.part; 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 || h->key.len != name->len || ngx_strncasecmp(h->key.data, name->data, name->len) != 0) { continue; } h->hash = 0; h->next = NULL; } if (value == NULL) { return 1; } if (JS_IsArray(cx, *value)) { v = JS_GetPropertyStr(cx, *value, "length"); if (JS_IsException(v)) { return -1; } if (JS_ToInt64(cx, &length, v) < 0) { JS_FreeValue(cx, v); return -1; } JS_FreeValue(cx, v); } else { v = *value; length = 1; } ph = &header; ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); for (i = 0; i < (uint32_t) length; i++) { if (JS_IsArray(cx, *value)) { v = JS_GetPropertyUint32(cx, *value, i); if (JS_IsException(v)) { return -1; } } rc = ngx_qjs_string(ctx->engine, v, &s); if (JS_IsArray(cx, *value)) { JS_FreeValue(cx, v); } if (rc != NGX_OK) { return -1; } if (s.len == 0) { continue; } h = ngx_list_push(&r->headers_out.headers); if (h == NULL) { (void) JS_ThrowOutOfMemory(cx); return -1; } p = ngx_pnalloc(r->pool, name->len); if (p == NULL) { h->hash = 0; (void) JS_ThrowOutOfMemory(cx); return -1; } ngx_memcpy(p, name->data, name->len); h->key.data = p; h->key.len = name->len; p = ngx_pnalloc(r->pool, s.len); if (p == NULL) { h->hash = 0; (void) JS_ThrowOutOfMemory(cx); return -1; } ngx_memcpy(p, s.data, s.len); h->value.data = p; h->value.len = s.len; h->hash = 1; *ph = h; ph = &h->next; } *ph = NULL; return NJS_OK; } static int ngx_http_qjs_headers_out_special_handler(JSContext *cx, ngx_http_request_t *r, ngx_str_t *name, JSPropertyDescriptor *pdesc, JSValue *value, unsigned flags, ngx_table_elt_t **hh) { u_char *p; uint32_t length; JSValue len, setval; ngx_str_t s; ngx_uint_t i, rc; ngx_list_t *headers; ngx_list_part_t *part; ngx_table_elt_t *header, *h; ngx_http_js_ctx_t *ctx; if (flags & NJS_HEADER_GET) { return ngx_http_qjs_headers_out_handler(cx, r, name, pdesc, NULL, flags | NJS_HEADER_SINGLE); } if (value != NULL) { if (JS_IsArray(cx, *value)) { len = JS_GetPropertyStr(cx, *value, "length"); if (JS_IsException(len)) { return -1; } if (JS_ToUint32(cx, &length, len) < 0) { JS_FreeValue(cx, len); return -1; } JS_FreeValue(cx, len); setval = JS_GetPropertyUint32(cx, *value, length - 1); if (JS_IsException(setval)) { return -1; } } else { setval = *value; } } else { setval = JS_UNDEFINED; } ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); rc = ngx_qjs_string(ctx->engine, setval, &s); if (value != NULL && JS_IsArray(cx, *value)) { JS_FreeValue(cx, setval); } if (rc != NGX_OK) { return -1; } headers = &r->headers_out.headers; part = &headers->part; 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 == name->len && ngx_strncasecmp(h->key.data, name->data, name->len) == 0) { goto done; } } h = NULL; done: if (h != NULL && s.len == 0) { h->hash = 0; h = NULL; } if (h == NULL && s.len != 0) { h = ngx_list_push(headers); if (h == NULL) { (void) JS_ThrowOutOfMemory(cx); return -1; } p = ngx_pnalloc(r->pool, name->len); if (p == NULL) { h->hash = 0; (void) JS_ThrowOutOfMemory(cx); return -1; } ngx_memcpy(p, name->data, name->len); h->key.data = p; h->key.len = name->len; } if (h != NULL) { p = ngx_pnalloc(r->pool, s.len); if (p == NULL) { h->hash = 0; (void) JS_ThrowOutOfMemory(cx); return -1; } ngx_memcpy(p, s.data, s.len); h->value.data = p; h->value.len = s.len; h->hash = 1; } if (hh != NULL) { *hh = h; } return 1; } static int ngx_http_qjs_headers_out_content_encoding(JSContext *cx, ngx_http_request_t *r, ngx_str_t *name, JSPropertyDescriptor *pdesc, JSValue *value, unsigned flags) { int ret; ngx_table_elt_t *h; ret = ngx_http_qjs_headers_out_special_handler(cx, r, name, pdesc, value, flags, &h); if (ret < 0) { return -1; } if (!(flags & NJS_HEADER_GET)) { r->headers_out.content_encoding = h; } return ret; } static int ngx_http_qjs_headers_out_content_length(JSContext *cx, ngx_http_request_t *r, ngx_str_t *name, JSPropertyDescriptor *pdesc, JSValue *value, unsigned flags) { int ret; u_char *p; ngx_int_t n; ngx_table_elt_t *h; u_char content_len[NGX_OFF_T_LEN]; if (flags & NJS_HEADER_GET) { if (r->headers_out.content_length == NULL && r->headers_out.content_length_n >= 0) { p = ngx_sprintf(content_len, "%O", r->headers_out.content_length_n); if (pdesc != NULL) { pdesc->flags = JS_PROP_C_W_E; pdesc->getter = JS_UNDEFINED; pdesc->setter = JS_UNDEFINED; pdesc->value = qjs_string_create(cx, content_len, p - content_len); if (JS_IsException(pdesc->value)) { return -1; } } return 1; } } ret = ngx_http_qjs_headers_out_special_handler(cx, r, name, pdesc, value, flags, &h); if (ret < 0) { return -1; } if (!(flags & NJS_HEADER_GET)) { if (h != NULL) { n = ngx_atoi(h->value.data, h->value.len); if (n == NGX_ERROR) { h->hash = 0; (void) JS_ThrowInternalError(cx, "failed converting argument " "to positive integer"); return -1; } r->headers_out.content_length = h; r->headers_out.content_length_n = n; } else { ngx_http_clear_content_length(r); } } return ret; } static int ngx_http_qjs_headers_out_content_type(JSContext *cx, ngx_http_request_t *r, ngx_str_t *name, JSPropertyDescriptor *pdesc, JSValue *value, unsigned flags) { uint32_t length; JSValue len, setval; ngx_int_t rc; ngx_str_t *hdr, s; ngx_http_js_ctx_t *ctx; if (flags & NJS_HEADER_GET) { hdr = &r->headers_out.content_type; if (pdesc != NULL) { pdesc->flags = JS_PROP_C_W_E; pdesc->getter = JS_UNDEFINED; pdesc->setter = JS_UNDEFINED; if (hdr->len == 0) { pdesc->value = JS_UNDEFINED; return 1; } pdesc->value = qjs_string_create(cx, hdr->data, hdr->len); if (JS_IsException(pdesc->value)) { return -1; } } return 1; } if (value == NULL) { r->headers_out.content_type.len = 0; r->headers_out.content_type_len = 0; r->headers_out.content_type.data = NULL; r->headers_out.content_type_lowcase = NULL; return 1; } if (JS_IsArray(cx, *value)) { len = JS_GetPropertyStr(cx, *value, "length"); if (JS_IsException(len)) { return -1; } if (JS_ToUint32(cx, &length, len) < 0) { JS_FreeValue(cx, len); return -1; } JS_FreeValue(cx, len); setval = JS_GetPropertyUint32(cx, *value, length - 1); if (JS_IsException(setval)) { return -1; } } else { setval = *value; } ctx = ngx_http_get_module_ctx(r, ngx_http_js_module); rc = ngx_qjs_string(ctx->engine, setval, &s); if (JS_IsArray(cx, *value)) { JS_FreeValue(cx, setval); } if (rc != NGX_OK) { return -1; } r->headers_out.content_type.len = s.len; r->headers_out.content_type_len = r->headers_out.content_type.len; r->headers_out.content_type.data = s.data; r->headers_out.content_type_lowcase = NULL; return 1; } static int ngx_http_qjs_headers_out_date(JSContext *cx, ngx_http_request_t *r, ngx_str_t *name, JSPropertyDescriptor *pdesc, JSValue *value, unsigned flags) { int ret; ngx_table_elt_t *h; ret = ngx_http_qjs_headers_out_special_handler(cx, r, name, pdesc, value, flags, &h); if (ret < 0) { return -1; } if (!(flags & NJS_HEADER_GET)) { r->headers_out.date = h; } return ret; } static int ngx_http_qjs_headers_out_last_modified(JSContext *cx, ngx_http_request_t *r, ngx_str_t *name, JSPropertyDescriptor *pdesc, JSValue *value, unsigned flags) { int ret; ngx_table_elt_t *h; ret = ngx_http_qjs_headers_out_special_handler(cx, r, name, pdesc, value, flags, &h); if (ret < 0) { return -1; } if (!(flags & NJS_HEADER_GET)) { r->headers_out.last_modified = h; } return ret; } static int ngx_http_qjs_headers_out_location(JSContext *cx, ngx_http_request_t *r, ngx_str_t *name, JSPropertyDescriptor *pdesc, JSValue *value, unsigned flags) { int ret; ngx_table_elt_t *h; ret = ngx_http_qjs_headers_out_special_handler(cx, r, name, pdesc, value, flags, &h); if (ret < 0) { return -1; } if (!(flags & NJS_HEADER_GET)) { r->headers_out.location = h; } return ret; } static int ngx_http_qjs_headers_out_server(JSContext *cx, ngx_http_request_t *r, ngx_str_t *name, JSPropertyDescriptor *pdesc, JSValue *value, unsigned flags) { int ret; ngx_table_elt_t *h; ret = ngx_http_qjs_headers_out_special_handler(cx, r, name, pdesc, value, flags, &h); if (ret < 0) { return -1; } if (!(flags & NJS_HEADER_GET)) { r->headers_out.server = h; } return ret; } static int ngx_http_qjs_headers_out(JSContext *cx, ngx_http_request_t *r, ngx_str_t *name, JSPropertyDescriptor *pdesc, JSValue *value, unsigned flags) { ngx_http_js_header_t *h; static ngx_http_js_header_t headers_out[] = { #define header(name, fl, h) { njs_str(name), fl, (uintptr_t) h } header("Age", NJS_HEADER_SINGLE, ngx_http_qjs_headers_out_handler), header("Content-Encoding", 0, ngx_http_qjs_headers_out_content_encoding), header("Content-Length", 0, ngx_http_qjs_headers_out_content_length), header("Content-Type", 0, ngx_http_qjs_headers_out_content_type), header("Date", 0, ngx_http_qjs_headers_out_date), header("Etag", NJS_HEADER_SINGLE, ngx_http_qjs_headers_out_handler), header("Expires", NJS_HEADER_SINGLE, ngx_http_qjs_headers_out_handler), header("Last-Modified", 0, ngx_http_qjs_headers_out_last_modified), header("Location", 0, ngx_http_qjs_headers_out_location), header("Server", 0, ngx_http_qjs_headers_out_server), header("Set-Cookie", NJS_HEADER_ARRAY, ngx_http_qjs_headers_out_handler), header("Retry-After", NJS_HEADER_SINGLE, ngx_http_qjs_headers_out_handler), header("", 0, ngx_http_qjs_headers_out_handler), #undef header }; for (h = headers_out; h->name.len > 0; h++) { if (h->name.len == name->len && ngx_strncasecmp(h->name.data, name->data, name->len) == 0) { break; } } return ((njs_http_qjs_header_handler_t) h->handler)(cx, r, name, pdesc, value, h->flags | flags); } static int ngx_http_qjs_headers_out_own_property(JSContext *cx, JSPropertyDescriptor *pdesc, JSValueConst obj, JSAtom prop) { int ret; ngx_str_t name; ngx_http_request_t *r; r = JS_GetOpaque(obj, NGX_QJS_CLASS_ID_HTTP_HEADERS_OUT); if (r == NULL) { (void) JS_ThrowInternalError(cx, "\"this\" is not a headers_out" " object"); return -1; } name.data = (u_char *) JS_AtomToCString(cx, prop); if (name.data == NULL) { return -1; } name.len = ngx_strlen(name.data); ret = ngx_http_qjs_headers_out(cx, r, &name, pdesc, NULL, NJS_HEADER_GET); JS_FreeCString(cx, (char *) name.data); return ret; } static int ngx_http_qjs_headers_out_set_property(JSContext *cx, JSValueConst obj, JSAtom atom, JSValueConst value, JSValueConst receiver, int flags) { return ngx_http_qjs_headers_out_define_own_property(cx, obj, atom, value, JS_UNDEFINED, JS_UNDEFINED, flags); } static int ngx_http_qjs_headers_out_define_own_property(JSContext *cx, JSValueConst obj, JSAtom prop, JSValueConst value, JSValueConst getter, JSValueConst setter, int flags) { int ret; ngx_str_t name; ngx_http_request_t *r; r = JS_GetOpaque(obj, NGX_QJS_CLASS_ID_HTTP_HEADERS_OUT); if (r == NULL) { (void) JS_ThrowInternalError(cx, "\"this\" is not a headers_out" " object"); return -1; } if (!JS_IsUndefined(setter) || !JS_IsUndefined(getter)) { (void) JS_ThrowTypeError(cx, "cannot define getter or setter"); return -1; } name.data = (u_char *) JS_AtomToCString(cx, prop); if (name.data == NULL) { return -1; } name.len = ngx_strlen(name.data); if (r->header_sent) { ngx_log_error(NGX_LOG_WARN, r->connection->log, 0, "ignored setting of response header \"%V\" because" " headers were already sent", &name); } ret = ngx_http_qjs_headers_out(cx, r, &name, NULL, &value, 0); JS_FreeCString(cx, (char *) name.data); return ret; } static int ngx_http_qjs_headers_out_delete_property(JSContext *cx, JSValueConst obj, JSAtom prop) { int ret; ngx_str_t name; ngx_http_request_t *r; r = JS_GetOpaque(obj, NGX_QJS_CLASS_ID_HTTP_HEADERS_OUT); if (r == NULL) { (void) JS_ThrowInternalError(cx, "\"this\" is not a headers_out" " object"); return -1; } name.data = (u_char *) JS_AtomToCString(cx, prop); if (name.data == NULL) { return -1; } name.len = ngx_strlen(name.data); ret = ngx_http_qjs_headers_out(cx, r, &name, NULL, NULL, 0); JS_FreeCString(cx, (char *) name.data); return ret; } static ngx_int_t ngx_http_qjs_body_filter(ngx_http_request_t *r, ngx_http_js_loc_conf_t *jlcf, ngx_http_js_ctx_t *ctx, ngx_chain_t *in) { size_t len; u_char *p; JSAtom last_key; JSValue arguments[3], last; ngx_int_t rc; njs_int_t pending; ngx_buf_t *b; ngx_chain_t *cl; JSContext *cx; ngx_connection_t *c; c = r->connection; cx = ctx->engine->u.qjs.ctx; arguments[0] = ngx_qjs_arg(ctx->args[0]); last_key = JS_NewAtom(cx, "last"); if (last_key == JS_ATOM_NULL) { return NGX_ERROR; } while (in != NULL) { ctx->buf = in->buf; b = ctx->buf; if (!ctx->done) { len = b->last - b->pos; p = ngx_pnalloc(r->pool, len); if (p == NULL) { return NJS_ERROR; } if (len) { ngx_memcpy(p, b->pos, len); } arguments[1] = ngx_qjs_prop(cx, jlcf->buffer_type, p, len); if (JS_IsException(arguments[1])) { JS_FreeAtom(cx, last_key); return NGX_ERROR; } last = JS_NewBool(cx, b->last_buf); arguments[2] = JS_NewObject(cx); if (JS_IsException(arguments[2])) { JS_FreeAtom(cx, last_key); JS_FreeValue(cx, arguments[1]); return NGX_ERROR; } if (JS_SetProperty(cx, arguments[2], last_key, last) < 0) { JS_FreeAtom(cx, last_key); JS_FreeValue(cx, arguments[1]); JS_FreeValue(cx, arguments[2]); return NGX_ERROR; } pending = ngx_js_ctx_pending(ctx); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http js body call \"%V\"", &jlcf->body_filter); rc = ctx->engine->call((ngx_js_ctx_t *) ctx, &jlcf->body_filter, (njs_opaque_value_t *) &arguments[0], 3); JS_FreeAtom(cx, last_key); JS_FreeValue(cx, arguments[1]); JS_FreeValue(cx, arguments[2]); if (rc == NGX_ERROR) { return NGX_ERROR; } if (!pending && rc == NGX_AGAIN) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "async operation inside \"%V\" body filter", &jlcf->body_filter); return NGX_ERROR; } ctx->buf->pos = ctx->buf->last; } else { cl = ngx_alloc_chain_link(c->pool); if (cl == NULL) { return NGX_ERROR; } cl->buf = b; *ctx->last_out = cl; ctx->last_out = &cl->next; } in = in->next; } return NGX_OK; } static ngx_http_request_t * ngx_http_qjs_request(JSValueConst val) { ngx_http_qjs_request_t *req; req = JS_GetOpaque(val, NGX_QJS_CLASS_ID_HTTP_REQUEST); if (req == NULL) { return NULL; } return req->request; } static JSValue ngx_http_qjs_request_make(JSContext *cx, ngx_int_t proto_id, ngx_http_request_t *r) { JSValue request; ngx_http_qjs_request_t *req; request = JS_NewObjectClass(cx, proto_id); if (JS_IsException(request)) { return JS_EXCEPTION; } req = js_malloc(cx, sizeof(ngx_http_qjs_request_t)); if (req == NULL) { return JS_ThrowOutOfMemory(cx); } req->request = r; req->args = JS_UNDEFINED; req->request_body = JS_UNDEFINED; req->response_body = JS_UNDEFINED; JS_SetOpaque(request, req); return request; } static void ngx_http_qjs_request_finalizer(JSRuntime *rt, JSValue val) { ngx_http_qjs_request_t *req; req = JS_GetOpaque(val, NGX_QJS_CLASS_ID_HTTP_REQUEST); if (req == NULL) { return; } JS_FreeValueRT(rt, req->args); JS_FreeValueRT(rt, req->request_body); JS_FreeValueRT(rt, req->response_body); js_free_rt(rt, req); } static ngx_engine_t * ngx_engine_qjs_clone(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *cf, njs_int_t proto_id, void *external) { JSValue proto; JSContext *cx; ngx_engine_t *engine; ngx_http_js_ctx_t *hctx; engine = ngx_qjs_clone(ctx, cf, external); if (engine == NULL) { return NULL; } cx = engine->u.qjs.ctx; if (!JS_IsRegisteredClass(JS_GetRuntime(cx), NGX_QJS_CLASS_ID_HTTP_REQUEST)) { if (JS_NewClass(JS_GetRuntime(cx), NGX_QJS_CLASS_ID_HTTP_REQUEST, &ngx_http_qjs_request_class) < 0) { return NULL; } proto = JS_NewObject(cx); if (JS_IsException(proto)) { return NULL; } JS_SetPropertyFunctionList(cx, proto, ngx_http_qjs_ext_request, njs_nitems(ngx_http_qjs_ext_request)); JS_SetClassProto(cx, NGX_QJS_CLASS_ID_HTTP_REQUEST, proto); if (JS_NewClass(JS_GetRuntime(cx), NGX_QJS_CLASS_ID_HTTP_PERIODIC, &ngx_http_qjs_periodic_class) < 0) { return NULL; } proto = JS_NewObject(cx); if (JS_IsException(proto)) { return NULL; } JS_SetPropertyFunctionList(cx, proto, ngx_http_qjs_ext_periodic, njs_nitems(ngx_http_qjs_ext_periodic)); JS_SetClassProto(cx, NGX_QJS_CLASS_ID_HTTP_PERIODIC, proto); if (JS_NewClass(JS_GetRuntime(cx), NGX_QJS_CLASS_ID_HTTP_VARS, &ngx_http_qjs_variables_class) < 0) { return NULL; } if (JS_NewClass(JS_GetRuntime(cx), NGX_QJS_CLASS_ID_HTTP_HEADERS_IN, &ngx_http_qjs_headers_in_class) < 0) { return NULL; } if (JS_NewClass(JS_GetRuntime(cx), NGX_QJS_CLASS_ID_HTTP_HEADERS_OUT, &ngx_http_qjs_headers_out_class) < 0) { return NULL; } } hctx = (ngx_http_js_ctx_t *) ctx; hctx->body_filter = ngx_http_qjs_body_filter; if (proto_id == ngx_http_js_request_proto_id) { proto_id = NGX_QJS_CLASS_ID_HTTP_REQUEST; } else if (proto_id == ngx_http_js_periodic_session_proto_id) { proto_id = NGX_QJS_CLASS_ID_HTTP_PERIODIC; } ngx_qjs_arg(hctx->args[0]) = ngx_http_qjs_request_make(cx, proto_id, external); if (JS_IsException(ngx_qjs_arg(hctx->args[0]))) { return NULL; } return engine; } #endif static ngx_int_t ngx_http_js_init_conf_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf) { ngx_engine_opts_t options; ngx_js_main_conf_t *jmcf; memset(&options, 0, sizeof(ngx_engine_opts_t)); options.engine = conf->type; jmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_js_module); ngx_http_js_uptr[NGX_JS_MAIN_CONF_INDEX] = (uintptr_t) jmcf; if (conf->type == NGX_ENGINE_NJS) { options.u.njs.metas = &ngx_http_js_metas; options.u.njs.addons = njs_http_js_addon_modules; options.clone = ngx_engine_njs_clone; } #if (NJS_HAVE_QUICKJS) else if (conf->type == NGX_ENGINE_QJS) { options.u.qjs.metas = ngx_http_js_uptr; options.u.qjs.addons = njs_http_qjs_addon_modules; options.clone = ngx_engine_qjs_clone; } #endif return ngx_js_init_conf_vm(cf, conf, &options); } static ngx_int_t ngx_http_js_init(ngx_conf_t *cf) { ngx_http_next_header_filter = ngx_http_top_header_filter; ngx_http_top_header_filter = ngx_http_js_header_filter; ngx_http_next_body_filter = ngx_http_top_body_filter; ngx_http_top_body_filter = ngx_http_js_body_filter; return NGX_OK; } static ngx_int_t ngx_http_js_init_worker(ngx_cycle_t *cycle) { ngx_uint_t i; ngx_js_periodic_t *periodics; ngx_js_main_conf_t *jmcf; if ((ngx_process != NGX_PROCESS_WORKER) && ngx_process != NGX_PROCESS_SINGLE) { return NGX_OK; } jmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_js_module); if (jmcf == NULL || jmcf->periodics == NULL) { return NGX_OK; } periodics = jmcf->periodics->elts; for (i = 0; i < jmcf->periodics->nelts; i++) { if (periodics[i].worker_affinity != NULL && !periodics[i].worker_affinity[ngx_worker]) { continue; } if (periodics[i].worker_affinity == NULL && ngx_worker != 0) { continue; } periodics[i].fd = 1000000 + i; if (ngx_http_js_periodic_init(&periodics[i]) != NGX_OK) { return NGX_ERROR; } } return NGX_OK; } static char * ngx_http_js_periodic(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { uint8_t *mask; ngx_str_t *value, s; ngx_msec_t interval, jitter; ngx_uint_t i; ngx_core_conf_t *ccf; ngx_js_periodic_t *periodic; ngx_js_main_conf_t *jmcf; if (cf->args->nelts < 2) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "method name is required"); return NGX_CONF_ERROR; } jmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_js_module); if (jmcf->periodics == NULL) { jmcf->periodics = ngx_array_create(cf->pool, 1, sizeof(ngx_js_periodic_t)); if (jmcf->periodics == NULL) { return NGX_CONF_ERROR; } } periodic = ngx_array_push(jmcf->periodics); if (periodic == NULL) { return NGX_CONF_ERROR; } ngx_memzero(periodic, sizeof(ngx_js_periodic_t)); mask = NULL; jitter = 0; interval = 5000; value = cf->args->elts; for (i = 2; i < cf->args->nelts; i++) { if (ngx_strncmp(value[i].data, "interval=", 9) == 0) { s.len = value[i].len - 9; s.data = value[i].data + 9; interval = ngx_parse_time(&s, 0); if (interval == (ngx_msec_t) NGX_ERROR || interval == 0) { goto invalid; } continue; } if (ngx_strncmp(value[i].data, "jitter=", 7) == 0) { s.len = value[i].len - 7; s.data = value[i].data + 7; jitter = ngx_parse_time(&s, 0); if (jitter == (ngx_msec_t) NGX_ERROR) { goto invalid; } continue; } if (ngx_strncmp(value[i].data, "worker_affinity=", 16) == 0) { s.len = value[i].len - 16; s.data = value[i].data + 16; ccf = (ngx_core_conf_t *) ngx_get_conf(cf->cycle->conf_ctx, ngx_core_module); if (ccf->worker_processes == NGX_CONF_UNSET) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"worker_affinity\" is not supported " "with unset \"worker_processes\" directive"); return NGX_CONF_ERROR; } mask = ngx_palloc(cf->pool, ccf->worker_processes); if (mask == NULL) { return NGX_CONF_ERROR; } if (ngx_strncmp(s.data, "all", 3) == 0) { memset(mask, 1, ccf->worker_processes); continue; } if ((size_t) ccf->worker_processes != s.len) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "the number of " "\"worker_processes\" is not equal to the " "size of \"worker_affinity\" mask"); return NGX_CONF_ERROR; } for (i = 0; i < s.len; i++) { if (s.data[i] == '0') { mask[i] = 0; continue; } if (s.data[i] == '1') { mask[i] = 1; continue; } ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid character \"%c\" in \"worker_affinity=\"", s.data[i]); return NGX_CONF_ERROR; } continue; } invalid: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"", &value[i]); return NGX_CONF_ERROR; } periodic->method = value[1]; periodic->interval = interval; periodic->jitter = jitter; periodic->worker_affinity = mask; periodic->conf_ctx = cf->ctx; return NGX_CONF_OK; } static char * ngx_http_js_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_str_t *value; ngx_js_set_t *data, *prev; 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; } data = ngx_palloc(cf->pool, sizeof(ngx_js_set_t)); if (data == NULL) { return NGX_CONF_ERROR; } data->fname = value[2]; data->flags = 0; if (v->get_handler == ngx_http_js_variable_set) { prev = (ngx_js_set_t *) v->data; if (data->fname.len != prev->fname.len || ngx_strncmp(data->fname.data, prev->fname.data, data->fname.len) != 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "variable \"%V\" is redeclared with " "different function name", &value[1]); return NGX_CONF_ERROR; } } if (cf->args->nelts == 4) { if (ngx_strcmp(value[3].data, "nocache") == 0) { data->flags |= NGX_NJS_VAR_NOCACHE; } else { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unrecognized flag \"%V\"", &value[3]); return NGX_CONF_ERROR; } } v->get_handler = ngx_http_js_variable_set; v->data = (uintptr_t) data; return NGX_CONF_OK; } static char * ngx_http_js_var(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_str_t *value; ngx_int_t index; ngx_http_variable_t *v; ngx_http_complex_value_t *cv; ngx_http_compile_complex_value_t ccv; 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; } index = ngx_http_get_variable_index(cf, &value[1]); if (index == NGX_ERROR) { return NGX_CONF_ERROR; } cv = NULL; if (cf->args->nelts == 3) { cv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t)); if (cv == NULL) { return NGX_CONF_ERROR; } ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); ccv.cf = cf; ccv.value = &value[2]; ccv.complex_value = cv; if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { return NGX_CONF_ERROR; } } v->get_handler = ngx_http_js_variable_var; v->data = (uintptr_t) cv; return NGX_CONF_OK; } static char * ngx_http_js_content(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; if (jlcf->content.data) { return "is duplicate"; } value = cf->args->elts; jlcf->content = value[1]; clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); clcf->handler = ngx_http_js_content_handler; return NGX_CONF_OK; } static char * ngx_http_js_shared_dict_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { return ngx_js_shared_dict_zone(cf, cmd, conf, &ngx_http_js_module); } static char * ngx_http_js_body_filter_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_js_loc_conf_t *jlcf = conf; ngx_str_t *value; if (jlcf->body_filter.data) { return "is duplicate"; } value = cf->args->elts; jlcf->body_filter = value[1]; jlcf->buffer_type = NGX_JS_STRING; if (cf->args->nelts == 3) { if (ngx_strncmp(value[2].data, "buffer_type=", 12) == 0) { if (ngx_strcmp(&value[2].data[12], "string") == 0) { jlcf->buffer_type = NGX_JS_STRING; } else if (ngx_strcmp(&value[2].data[12], "buffer") == 0) { jlcf->buffer_type = NGX_JS_BUFFER; } else { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid buffer_type value \"%V\", " "it must be \"string\" or \"buffer\"", &value[2]); return NGX_CONF_ERROR; } } else { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"", &value[2]); return NGX_CONF_ERROR; } } return NGX_CONF_OK; } static void * ngx_http_js_create_main_conf(ngx_conf_t *cf) { ngx_js_main_conf_t *jmcf; jmcf = ngx_pcalloc(cf->pool, sizeof(ngx_js_main_conf_t)); if (jmcf == NULL) { return NULL; } /* * set by ngx_pcalloc(): * * jmcf->dicts = NULL; * jmcf->periodics = NULL; */ return jmcf; } static void * ngx_http_js_create_loc_conf(ngx_conf_t *cf) { ngx_http_js_loc_conf_t *conf = (ngx_http_js_loc_conf_t *) ngx_js_create_conf( cf, sizeof(ngx_http_js_loc_conf_t)); if (conf == NULL) { return NULL; } #if (NGX_HTTP_SSL) conf->ssl_verify = NGX_CONF_UNSET; conf->ssl_verify_depth = NGX_CONF_UNSET; #endif 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; ngx_conf_merge_str_value(conf->content, prev->content, ""); ngx_conf_merge_str_value(conf->header_filter, prev->header_filter, ""); ngx_conf_merge_str_value(conf->body_filter, prev->body_filter, ""); ngx_conf_merge_uint_value(conf->buffer_type, prev->buffer_type, NGX_JS_STRING); return ngx_js_merge_conf(cf, parent, child, ngx_http_js_init_conf_vm); } static ngx_ssl_t * ngx_http_js_ssl(ngx_http_request_t *r) { #if (NGX_HTTP_SSL) ngx_http_js_loc_conf_t *jlcf; jlcf = ngx_http_get_module_loc_conf(r, ngx_http_js_module); return jlcf->ssl; #else return NULL; #endif } static ngx_flag_t ngx_http_js_ssl_verify(ngx_http_request_t *r) { #if (NGX_HTTP_SSL) ngx_http_js_loc_conf_t *jlcf; jlcf = ngx_http_get_module_loc_conf(r, ngx_http_js_module); return jlcf->ssl_verify; #else return 0; #endif } static ngx_int_t ngx_http_js_parse_unsafe_uri(ngx_http_request_t *r, njs_str_t *uri, njs_str_t *args) { ngx_str_t uri_arg, args_arg; ngx_uint_t flags; flags = NGX_HTTP_LOG_UNSAFE; uri_arg.data = uri->start; uri_arg.len = uri->length; args_arg.data = args->start; args_arg.len = args->length; if (ngx_http_parse_unsafe_uri(r, &uri_arg, &args_arg, &flags) != NGX_OK) { return NGX_ERROR; } uri->start = uri_arg.data; uri->length = uri_arg.len; args->start = args_arg.data; args->length = args_arg.len; return NGX_OK; } njs-0.8.9/nginx/ngx_js.c000066400000000000000000003274061474132077100151230ustar00rootroot00000000000000 /* * Copyright (C) Roman Arutyunyan * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #include #include #include #include "ngx_js.h" typedef struct { ngx_queue_t labels; } ngx_js_console_t; typedef struct { njs_str_t name; uint64_t time; ngx_queue_t queue; } ngx_js_timelabel_t; typedef struct { void *promise_obj; njs_opaque_value_t promise; njs_opaque_value_t message; } ngx_js_rejected_promise_t; #if defined(PATH_MAX) #define NGX_MAX_PATH PATH_MAX #else #define NGX_MAX_PATH 4096 #endif typedef struct { int fd; njs_str_t name; njs_str_t file; char path[NGX_MAX_PATH + 1]; } njs_module_info_t; static ngx_int_t ngx_engine_njs_init(ngx_engine_t *engine, ngx_engine_opts_t *opts); static ngx_int_t ngx_engine_njs_compile(ngx_js_loc_conf_t *conf, ngx_log_t *log, u_char *start, size_t size); static ngx_int_t ngx_engine_njs_call(ngx_js_ctx_t *ctx, ngx_str_t *fname, njs_opaque_value_t *args, njs_uint_t nargs); static void *ngx_engine_njs_external(ngx_engine_t *engine); static ngx_int_t ngx_engine_njs_pending(ngx_engine_t *engine); static ngx_int_t ngx_engine_njs_string(ngx_engine_t *e, njs_opaque_value_t *value, ngx_str_t *str); static void ngx_engine_njs_destroy(ngx_engine_t *e, ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *conf); static ngx_int_t ngx_js_init_preload_vm(njs_vm_t *vm, ngx_js_loc_conf_t *conf); #if (NJS_HAVE_QUICKJS) static ngx_int_t ngx_engine_qjs_init(ngx_engine_t *engine, ngx_engine_opts_t *opts); static ngx_int_t ngx_engine_qjs_compile(ngx_js_loc_conf_t *conf, ngx_log_t *log, u_char *start, size_t size); static ngx_int_t ngx_engine_qjs_call(ngx_js_ctx_t *ctx, ngx_str_t *fname, njs_opaque_value_t *args, njs_uint_t nargs); static void *ngx_engine_qjs_external(ngx_engine_t *engine); static ngx_int_t ngx_engine_qjs_pending(ngx_engine_t *engine); static ngx_int_t ngx_engine_qjs_string(ngx_engine_t *e, njs_opaque_value_t *value, ngx_str_t *str); static JSValue ngx_qjs_process_getter(JSContext *ctx, JSValueConst this_val); static JSValue ngx_qjs_ext_set_timeout(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int immediate); static JSValue ngx_qjs_ext_clear_timeout(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue ngx_qjs_ext_build(JSContext *cx, JSValueConst this_val); static JSValue ngx_qjs_ext_conf_file_path(JSContext *cx, JSValueConst this_val); static JSValue ngx_qjs_ext_conf_prefix(JSContext *cx, JSValueConst this_val); static JSValue ngx_qjs_ext_constant_integer(JSContext *cx, JSValueConst this_val, int magic); static JSValue ngx_qjs_ext_error_log_path(JSContext *cx, JSValueConst this_val); static JSValue ngx_qjs_ext_log(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int level); static JSValue ngx_qjs_ext_console_time(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue ngx_qjs_ext_console_time_end(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue ngx_qjs_ext_prefix(JSContext *cx, JSValueConst this_val); static JSValue ngx_qjs_ext_worker_id(JSContext *cx, JSValueConst this_val); static void ngx_qjs_console_finalizer(JSRuntime *rt, JSValue val); static JSModuleDef *ngx_qjs_module_loader(JSContext *ctx, const char *module_name, void *opaque); static int ngx_qjs_unhandled_rejection(ngx_js_ctx_t *ctx); static void ngx_qjs_rejection_tracker(JSContext *ctx, JSValueConst promise, JSValueConst reason, JS_BOOL is_handled, void *opaque); static JSValue ngx_qjs_value(JSContext *cx, const ngx_str_t *path); static ngx_int_t ngx_qjs_dump_obj(ngx_engine_t *e, JSValueConst val, ngx_str_t *dst); static JSModuleDef *ngx_qjs_core_init(JSContext *cx, const char *name); #endif static njs_int_t ngx_js_ext_build(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_js_ext_conf_file_path(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_js_ext_conf_prefix(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_js_ext_error_log_path(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_js_ext_prefix(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_js_ext_version(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_js_ext_worker_id(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_js_ext_console_time(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t ngx_js_ext_console_time_end(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_set_timeout(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_set_immediate(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_clear_timeout(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t ngx_js_unhandled_rejection(ngx_js_ctx_t *ctx); static void ngx_js_rejection_tracker(njs_vm_t *vm, njs_external_ptr_t unused, njs_bool_t is_handled, njs_value_t *promise, njs_value_t *reason); static njs_mod_t *ngx_js_module_loader(njs_vm_t *vm, njs_external_ptr_t external, njs_str_t *name); static njs_int_t ngx_js_module_lookup(ngx_js_loc_conf_t *conf, njs_module_info_t *info); static njs_int_t ngx_js_module_read(njs_mp_t *mp, int fd, njs_str_t *text); static njs_int_t ngx_js_set_cwd(njs_mp_t *mp, ngx_js_loc_conf_t *conf, njs_str_t *path); static void ngx_js_cleanup_vm(void *data); static njs_int_t ngx_js_core_init(njs_vm_t *vm); static uint64_t ngx_js_monotonic_time(void); static njs_external_t ngx_js_ext_global_shared[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "GlobalShared", } }, { .flags = NJS_EXTERN_SELF, .u.object = { .enumerable = 1, .prop_handler = njs_js_ext_global_shared_prop, .keys = njs_js_ext_global_shared_keys, } }, }; static njs_external_t ngx_js_ext_core[] = { { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("build"), .enumerable = 1, .u.property = { .handler = ngx_js_ext_build, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("conf_file_path"), .enumerable = 1, .u.property = { .handler = ngx_js_ext_conf_file_path, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("conf_prefix"), .enumerable = 1, .u.property = { .handler = ngx_js_ext_conf_prefix, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("ERR"), .u.property = { .handler = ngx_js_ext_constant, .magic32 = NGX_LOG_ERR, .magic16 = NGX_JS_NUMBER, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("error_log_path"), .enumerable = 1, .u.property = { .handler = ngx_js_ext_error_log_path, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("fetch"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_js_ext_fetch, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("INFO"), .u.property = { .handler = ngx_js_ext_constant, .magic32 = NGX_LOG_INFO, .magic16 = NGX_JS_NUMBER, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("log"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_js_ext_log, } }, { .flags = NJS_EXTERN_OBJECT, .name.string = njs_str("shared"), .enumerable = 1, .writable = 1, .u.object = { .enumerable = 1, .properties = ngx_js_ext_global_shared, .nproperties = njs_nitems(ngx_js_ext_global_shared), } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("prefix"), .enumerable = 1, .u.property = { .handler = ngx_js_ext_prefix, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("version"), .enumerable = 1, .u.property = { .handler = ngx_js_ext_version, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("version_number"), .enumerable = 1, .u.property = { .handler = ngx_js_ext_constant, .magic32 = nginx_version, .magic16 = NGX_JS_NUMBER, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("WARN"), .u.property = { .handler = ngx_js_ext_constant, .magic32 = NGX_LOG_WARN, .magic16 = NGX_JS_NUMBER, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("worker_id"), .enumerable = 1, .u.property = { .handler = ngx_js_ext_worker_id, } }, }; static njs_external_t ngx_js_ext_console[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "Console", } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("dump"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_js_ext_log, #define NGX_JS_LOG_DUMP 16 #define NGX_JS_LOG_MASK 15 .magic8 = NGX_LOG_INFO | NGX_JS_LOG_DUMP, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("error"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_js_ext_log, .magic8 = NGX_LOG_ERR, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("info"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_js_ext_log, .magic8 = NGX_LOG_INFO, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("log"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_js_ext_log, .magic8 = NGX_LOG_INFO, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("time"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_js_ext_console_time, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("timeEnd"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_js_ext_console_time_end, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("warn"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_js_ext_log, .magic8 = NGX_LOG_WARN, } }, }; njs_module_t ngx_js_ngx_module = { .name = njs_str("ngx"), .preinit = NULL, .init = ngx_js_core_init, }; njs_module_t *njs_js_addon_modules_shared[] = { &ngx_js_ngx_module, NULL, }; static njs_int_t ngx_js_console_proto_id; #if (NJS_HAVE_QUICKJS) static const JSCFunctionListEntry ngx_qjs_ext_ngx[] = { JS_CGETSET_DEF("build", ngx_qjs_ext_build, NULL), JS_CGETSET_DEF("conf_prefix", ngx_qjs_ext_conf_prefix, NULL), JS_CGETSET_DEF("conf_file_path", ngx_qjs_ext_conf_file_path, NULL), JS_CGETSET_MAGIC_DEF("ERR", ngx_qjs_ext_constant_integer, NULL, NGX_LOG_ERR), JS_CGETSET_DEF("error_log_path", ngx_qjs_ext_error_log_path, NULL), JS_CGETSET_MAGIC_DEF("INFO", ngx_qjs_ext_constant_integer, NULL, NGX_LOG_INFO), JS_CFUNC_MAGIC_DEF("log", 1, ngx_qjs_ext_log, 0), JS_CGETSET_DEF("prefix", ngx_qjs_ext_prefix, NULL), JS_PROP_STRING_DEF("version", NGINX_VERSION, JS_PROP_C_W_E), JS_PROP_INT32_DEF("version_number", nginx_version, JS_PROP_C_W_E), JS_CGETSET_MAGIC_DEF("WARN", ngx_qjs_ext_constant_integer, NULL, NGX_LOG_WARN), JS_CGETSET_DEF("worker_id", ngx_qjs_ext_worker_id, NULL), }; static const JSCFunctionListEntry ngx_qjs_ext_console[] = { JS_CFUNC_MAGIC_DEF("error", 1, ngx_qjs_ext_log, NGX_LOG_ERR), JS_CFUNC_MAGIC_DEF("info", 1, ngx_qjs_ext_log, NGX_LOG_INFO), JS_CFUNC_MAGIC_DEF("log", 1, ngx_qjs_ext_log, NGX_LOG_INFO), JS_CFUNC_DEF("time", 1, ngx_qjs_ext_console_time), JS_CFUNC_DEF("timeEnd", 1, ngx_qjs_ext_console_time_end), JS_CFUNC_MAGIC_DEF("warn", 1, ngx_qjs_ext_log, NGX_LOG_WARN), }; static const JSCFunctionListEntry ngx_qjs_ext_global[] = { JS_CGETSET_DEF("process", ngx_qjs_process_getter, NULL), JS_CFUNC_MAGIC_DEF("setTimeout", 1, ngx_qjs_ext_set_timeout, 0), JS_CFUNC_MAGIC_DEF("setImmediate", 1, ngx_qjs_ext_set_timeout, 1), JS_CFUNC_DEF("clearTimeout", 1, ngx_qjs_ext_clear_timeout), }; static JSClassDef ngx_qjs_console_class = { "Console", .finalizer = ngx_qjs_console_finalizer, }; qjs_module_t ngx_qjs_ngx_module = { .name = "ngx", .init = ngx_qjs_core_init, }; #endif static ngx_engine_t * ngx_create_engine(ngx_engine_opts_t *opts) { njs_mp_t *mp; ngx_int_t rc; ngx_engine_t *engine; mp = njs_mp_fast_create(2 * getpagesize(), 128, 512, 16); if (mp == NULL) { return NULL; } engine = njs_mp_zalloc(mp, sizeof(ngx_engine_t)); if (engine == NULL) { return NULL; } engine->pool = mp; engine->clone = opts->clone; switch (opts->engine) { case NGX_ENGINE_NJS: rc = ngx_engine_njs_init(engine, opts); if (rc != NGX_OK) { return NULL; } engine->name = "njs"; engine->type = NGX_ENGINE_NJS; engine->compile = ngx_engine_njs_compile; engine->call = ngx_engine_njs_call; engine->external = ngx_engine_njs_external; engine->pending = ngx_engine_njs_pending; engine->string = ngx_engine_njs_string; engine->destroy = opts->destroy ? opts->destroy : ngx_engine_njs_destroy; break; #if (NJS_HAVE_QUICKJS) case NGX_ENGINE_QJS: rc = ngx_engine_qjs_init(engine, opts); if (rc != NGX_OK) { return NULL; } engine->name = "QuickJS"; engine->type = NGX_ENGINE_QJS; engine->compile = ngx_engine_qjs_compile; engine->call = ngx_engine_qjs_call; engine->external = ngx_engine_qjs_external; engine->pending = ngx_engine_qjs_pending; engine->string = ngx_engine_qjs_string; engine->destroy = opts->destroy ? opts->destroy : ngx_engine_qjs_destroy; break; #endif default: return NULL; } return engine; } static ngx_int_t ngx_engine_njs_init(ngx_engine_t *engine, ngx_engine_opts_t *opts) { njs_vm_t *vm; ngx_int_t rc; njs_vm_opt_t vm_options; njs_vm_opt_init(&vm_options); vm_options.backtrace = 1; vm_options.addons = opts->u.njs.addons; vm_options.metas = opts->u.njs.metas; vm_options.file = opts->file; vm_options.argv = ngx_argv; vm_options.argc = ngx_argc; vm_options.init = 1; vm = njs_vm_create(&vm_options); if (vm == NULL) { return NGX_ERROR; } njs_vm_set_rejection_tracker(vm, ngx_js_rejection_tracker, NULL); rc = ngx_js_set_cwd(njs_vm_memory_pool(vm), opts->conf, &vm_options.file); if (rc != NGX_OK) { return NGX_ERROR; } njs_vm_set_module_loader(vm, ngx_js_module_loader, opts->conf); engine->u.njs.vm = vm; return NJS_OK; } static ngx_int_t ngx_engine_njs_compile(ngx_js_loc_conf_t *conf, ngx_log_t *log, u_char *start, size_t size) { u_char *end; njs_vm_t *vm; njs_int_t rc; njs_str_t text; ngx_uint_t i; njs_value_t *value; njs_opaque_value_t exception, lvalue; ngx_js_named_path_t *import; static const njs_str_t line_number_key = njs_str("lineNumber"); static const njs_str_t file_name_key = njs_str("fileName"); vm = conf->engine->u.njs.vm; if (conf->preload_objects != NGX_CONF_UNSET_PTR) { if (ngx_js_init_preload_vm(vm, conf) != NGX_OK) { ngx_log_error(NGX_LOG_EMERG, log, 0, "failed to initialize preload objects"); return NGX_ERROR; } } end = start + size; rc = njs_vm_compile(vm, &start, end); if (rc != NJS_OK) { njs_vm_exception_get(vm, njs_value_arg(&exception)); njs_vm_value_string(vm, &text, njs_value_arg(&exception)); value = njs_vm_object_prop(vm, njs_value_arg(&exception), &file_name_key, &lvalue); if (value == NULL) { value = njs_vm_object_prop(vm, njs_value_arg(&exception), &line_number_key, &lvalue); if (value != NULL) { i = njs_value_number(value) - 1; if (i < conf->imports->nelts) { import = conf->imports->elts; ngx_log_error(NGX_LOG_EMERG, log, 0, "%*s, included in %s:%ui", text.length, text.start, import[i].file, import[i].line); return NGX_ERROR; } } } ngx_log_error(NGX_LOG_EMERG, log, 0, "%*s", text.length, text.start); return NGX_ERROR; } if (start != end) { ngx_log_error(NGX_LOG_EMERG, log, 0, "extra characters in js script: \"%*s\"", end - start, start); return NGX_ERROR; } return NGX_OK; } ngx_engine_t * ngx_njs_clone(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *cf, void *external) { njs_vm_t *vm; ngx_str_t exception; ngx_engine_t *engine; njs_opaque_value_t retval; vm = njs_vm_clone(cf->engine->u.njs.vm, external); if (vm == NULL) { return NULL; } engine = njs_mp_alloc(njs_vm_memory_pool(vm), sizeof(ngx_engine_t)); if (engine == NULL) { return NULL; } memcpy(engine, cf->engine, sizeof(ngx_engine_t)); engine->pool = njs_vm_memory_pool(vm); engine->u.njs.vm = vm; if (njs_vm_start(vm, njs_value_arg(&retval)) == NJS_ERROR) { ngx_js_exception(vm, &exception); ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "js exception: %V", &exception); return NULL; } return engine; } static ngx_int_t ngx_engine_njs_call(ngx_js_ctx_t *ctx, ngx_str_t *fname, njs_opaque_value_t *args, njs_uint_t nargs) { njs_vm_t *vm; njs_int_t ret; njs_str_t name; ngx_str_t exception; njs_function_t *func; name.start = fname->data; name.length = fname->len; vm = ctx->engine->u.njs.vm; func = njs_vm_function(vm, &name); if (func == NULL) { ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "js function \"%V\" not found", fname); return NGX_ERROR; } ret = njs_vm_invoke(vm, func, njs_value_arg(args), nargs, njs_value_arg(&ctx->retval)); if (ret == NJS_ERROR) { ngx_js_exception(vm, &exception); ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "js exception: %V", &exception); return NGX_ERROR; } for ( ;; ) { ret = njs_vm_execute_pending_job(vm); if (ret <= NJS_OK) { if (ret == NJS_ERROR) { ngx_js_exception(vm, &exception); ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "js job exception: %V", &exception); return NGX_ERROR; } break; } } if (ngx_js_unhandled_rejection(ctx)) { ngx_js_exception(vm, &exception); ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "js exception: %V", &exception); return NGX_ERROR; } return njs_rbtree_is_empty(&ctx->waiting_events) ? NGX_OK : NGX_AGAIN; } static void * ngx_engine_njs_external(ngx_engine_t *engine) { return njs_vm_external_ptr(engine->u.njs.vm); } static ngx_int_t ngx_engine_njs_pending(ngx_engine_t *e) { return njs_vm_pending(e->u.njs.vm); } static ngx_int_t ngx_engine_njs_string(ngx_engine_t *e, njs_opaque_value_t *value, ngx_str_t *str) { ngx_int_t rc; njs_str_t s; rc = ngx_js_string(e->u.njs.vm, njs_value_arg(value), &s); str->data = s.start; str->len = s.length; return rc; } static void ngx_engine_njs_destroy(ngx_engine_t *e, ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *conf) { ngx_js_event_t *event; njs_rbtree_node_t *node; if (ctx != NULL) { node = njs_rbtree_min(&ctx->waiting_events); while (njs_rbtree_is_there_successor(&ctx->waiting_events, node)) { event = (ngx_js_event_t *) ((u_char *) node - offsetof(ngx_js_event_t, node)); if (event->destructor != NULL) { event->destructor(event); } node = njs_rbtree_node_successor(&ctx->waiting_events, node); } } njs_vm_destroy(e->u.njs.vm); /* * when ctx !=NULL e->pool is vm pool, in such case it is destroyed * by njs_vm_destroy(). */ if (ctx == NULL) { njs_mp_destroy(e->pool); } } #if (NJS_HAVE_QUICKJS) static ngx_int_t ngx_engine_qjs_init(ngx_engine_t *engine, ngx_engine_opts_t *opts) { JSRuntime *rt; rt = JS_NewRuntime(); if (rt == NULL) { return NGX_ERROR; } engine->u.qjs.ctx = qjs_new_context(rt, opts->u.qjs.addons); if (engine->u.qjs.ctx == NULL) { return NGX_ERROR; } JS_SetRuntimeOpaque(rt, opts->u.qjs.metas); JS_SetContextOpaque(engine->u.qjs.ctx, opts->u.qjs.addons); JS_SetModuleLoaderFunc(rt, NULL, ngx_qjs_module_loader, opts->conf); return NGX_OK; } static ngx_int_t ngx_engine_qjs_compile(ngx_js_loc_conf_t *conf, ngx_log_t *log, u_char *start, size_t size) { JSValue code; ngx_str_t text; JSContext *cx; ngx_engine_t *engine; ngx_js_code_entry_t *pc; engine = conf->engine; cx = engine->u.qjs.ctx; code = JS_Eval(cx, (char *) start, size, "
", JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); if (JS_IsException(code)) { ngx_qjs_exception(engine, &text); ngx_log_error(NGX_LOG_EMERG, log, 0, "js compile %V", &text); return NGX_ERROR; } pc = njs_arr_add(engine->precompiled); if (pc == NULL) { JS_FreeValue(cx, code); ngx_log_error(NGX_LOG_EMERG, log, 0, "njs_arr_add() failed"); return NGX_ERROR; } pc->code = JS_WriteObject(cx, &pc->code_size, code, JS_WRITE_OBJ_BYTECODE); if (pc->code == NULL) { JS_FreeValue(cx, code); ngx_log_error(NGX_LOG_EMERG, log, 0, "JS_WriteObject() failed"); return NGX_ERROR; } JS_FreeValue(cx, code); return NGX_OK; } static JSValue js_std_await(JSContext *ctx, JSValue obj) { int state, err; JSValue ret; JSContext *ctx1; for (;;) { state = JS_PromiseState(ctx, obj); if (state == JS_PROMISE_FULFILLED) { ret = JS_PromiseResult(ctx, obj); JS_FreeValue(ctx, obj); break; } else if (state == JS_PROMISE_REJECTED) { ret = JS_Throw(ctx, JS_PromiseResult(ctx, obj)); JS_FreeValue(ctx, obj); break; } else if (state == JS_PROMISE_PENDING) { err = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1); if (err < 0) { /* js_std_dump_error(ctx1); */ } } else { /* not a promise */ ret = obj; break; } } return ret; } ngx_engine_t * ngx_qjs_clone(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *cf, void *external) { JSValue rv; njs_mp_t *mp; uint32_t i, length; JSRuntime *rt; ngx_str_t exception; JSContext *cx; ngx_engine_t *engine; ngx_js_code_entry_t *pc; mp = njs_mp_fast_create(2 * getpagesize(), 128, 512, 16); if (mp == NULL) { return NULL; } engine = njs_mp_alloc(mp, sizeof(ngx_engine_t)); if (engine == NULL) { return NULL; } memcpy(engine, cf->engine, sizeof(ngx_engine_t)); engine->pool = mp; if (cf->reuse_queue != NULL) { engine->u.qjs.ctx = ngx_js_queue_pop(cf->reuse_queue); if (engine->u.qjs.ctx != NULL) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0, "js reused context: %p", engine->u.qjs.ctx); JS_SetContextOpaque(engine->u.qjs.ctx, external); return engine; } } rt = JS_NewRuntime(); if (rt == NULL) { return NULL; } JS_SetRuntimeOpaque(rt, JS_GetRuntimeOpaque( JS_GetRuntime(cf->engine->u.qjs.ctx))); cx = qjs_new_context(rt, JS_GetContextOpaque(cf->engine->u.qjs.ctx)); if (cx == NULL) { JS_FreeRuntime(rt); return NULL; } engine->u.qjs.ctx = cx; JS_SetContextOpaque(cx, external); JS_SetHostPromiseRejectionTracker(rt, ngx_qjs_rejection_tracker, ctx); rv = JS_UNDEFINED; pc = engine->precompiled->start; length = engine->precompiled->items; for (i = 0; i < length; i++) { rv = JS_ReadObject(cx, pc[i].code, pc[i].code_size, JS_READ_OBJ_BYTECODE); if (JS_IsException(rv)) { ngx_qjs_exception(engine, &exception); ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "js load module exception: %V", &exception); goto destroy; } } if (JS_ResolveModule(cx, rv) < 0) { ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "js resolve module failed"); goto destroy; } rv = JS_EvalFunction(cx, rv); if (JS_IsException(rv)) { ngx_qjs_exception(engine, &exception); ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "js eval exception: %V", &exception); goto destroy; } rv = js_std_await(cx, rv); if (JS_IsException(rv)) { ngx_qjs_exception(engine, &exception); ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "js eval exception: %V", &exception); goto destroy; } JS_FreeValue(cx, rv); return engine; destroy: JS_FreeContext(cx); JS_FreeRuntime(rt); njs_mp_destroy(mp); return NULL; } static ngx_int_t ngx_engine_qjs_call(ngx_js_ctx_t *ctx, ngx_str_t *fname, njs_opaque_value_t *args, njs_uint_t nargs) { int rc; JSValue fn, val; ngx_str_t exception; JSRuntime *rt; JSContext *cx, *cx1; cx = ctx->engine->u.qjs.ctx; fn = ngx_qjs_value(cx, fname); if (!JS_IsFunction(cx, fn)) { JS_FreeValue(cx, fn); ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "js function \"%V\" not found", fname); return NGX_ERROR; } val = JS_Call(cx, fn, JS_UNDEFINED, nargs, &ngx_qjs_arg(args[0])); JS_FreeValue(cx, fn); if (JS_IsException(val)) { ngx_qjs_exception(ctx->engine, &exception); ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "js call exception: %V", &exception); return NGX_ERROR; } JS_FreeValue(cx, ngx_qjs_arg(ctx->retval)); ngx_qjs_arg(ctx->retval) = val; rt = JS_GetRuntime(cx); for ( ;; ) { rc = JS_ExecutePendingJob(rt, &cx1); if (rc <= 0) { if (rc == -1) { ngx_qjs_exception(ctx->engine, &exception); ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "js job exception: %V", &exception); return NGX_ERROR; } break; } } if (ngx_qjs_unhandled_rejection(ctx)) { ngx_qjs_exception(ctx->engine, &exception); ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "js exception: %V", &exception); return NGX_ERROR; } return njs_rbtree_is_empty(&ctx->waiting_events) ? NGX_OK : NGX_AGAIN; } static void * ngx_engine_qjs_external(ngx_engine_t *e) { return JS_GetContextOpaque(e->u.qjs.ctx); } static ngx_int_t ngx_engine_qjs_pending(ngx_engine_t *e) { return JS_IsJobPending(JS_GetRuntime(e->u.qjs.ctx)); } static ngx_int_t ngx_engine_qjs_string(ngx_engine_t *e, njs_opaque_value_t *value, ngx_str_t *str) { return ngx_qjs_dump_obj(e, ngx_qjs_arg(*value), str); } static void ngx_js_cleanup_reuse_ctx(void *data) { JSRuntime *rt; JSContext *cx; ngx_js_queue_t *reuse = data; for ( ;; ) { cx = ngx_js_queue_pop(reuse); if (cx == NULL) { break; } rt = JS_GetRuntime(cx); JS_FreeContext(cx); JS_FreeRuntime(rt); } } void ngx_engine_qjs_destroy(ngx_engine_t *e, ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *conf) { uint32_t i, length; JSRuntime *rt; JSContext *cx; JSClassID class_id; ngx_qjs_event_t *event; ngx_js_opaque_t *opaque; njs_rbtree_node_t *node; ngx_pool_cleanup_t *cln; ngx_js_code_entry_t *pc; ngx_js_rejected_promise_t *rejected_promise; cx = e->u.qjs.ctx; if (ctx != NULL) { node = njs_rbtree_min(&ctx->waiting_events); while (njs_rbtree_is_there_successor(&ctx->waiting_events, node)) { event = (ngx_qjs_event_t *) ((u_char *) node - offsetof(ngx_qjs_event_t, node)); if (event->destructor != NULL) { event->destructor(event); } node = njs_rbtree_node_successor(&ctx->waiting_events, node); } if (ctx->rejected_promises != NULL) { rejected_promise = ctx->rejected_promises->start; for (i = 0; i < ctx->rejected_promises->items; i++) { JS_FreeValue(cx, ngx_qjs_arg(rejected_promise[i].promise)); JS_FreeValue(cx, ngx_qjs_arg(rejected_promise[i].message)); } } class_id = JS_GetClassID(ngx_qjs_arg(ctx->args[0])); opaque = JS_GetOpaque(ngx_qjs_arg(ctx->args[0]), class_id); opaque->external = NULL; JS_FreeValue(cx, ngx_qjs_arg(ctx->args[0])); JS_FreeValue(cx, ngx_qjs_arg(ctx->retval)); } else if (e->precompiled != NULL) { pc = e->precompiled->start; length = e->precompiled->items; for (i = 0; i < length; i++) { js_free(cx, pc[i].code); } } njs_mp_destroy(e->pool); if (conf != NULL && conf->reuse != 0) { if (conf->reuse_queue == NULL) { conf->reuse_queue = ngx_js_queue_create(ngx_cycle->pool, conf->reuse); if (conf->reuse_queue == NULL) { goto free_ctx; } cln = ngx_pool_cleanup_add(ngx_cycle->pool, 0); if (cln == NULL) { goto free_ctx; } cln->handler = ngx_js_cleanup_reuse_ctx; cln->data = conf->reuse_queue; } if (ngx_js_queue_push(conf->reuse_queue, cx) != NGX_OK) { goto free_ctx; } return; } free_ctx: rt = JS_GetRuntime(cx); JS_FreeContext(cx); JS_FreeRuntime(rt); } static JSValue ngx_qjs_value(JSContext *cx, const ngx_str_t *path) { u_char *start, *p, *end; JSAtom key; size_t size; JSValue value, rv; start = path->data; end = start + path->len; value = JS_GetGlobalObject(cx); for ( ;; ) { p = njs_strlchr(start, end, '.'); size = ((p != NULL) ? p : end) - start; if (size == 0) { JS_FreeValue(cx, value); return JS_ThrowTypeError(cx, "empty path element"); } key = JS_NewAtomLen(cx, (char *) start, size); if (key == JS_ATOM_NULL) { JS_FreeValue(cx, value); return JS_ThrowInternalError(cx, "could not create atom"); } rv = JS_GetProperty(cx, value, key); JS_FreeAtom(cx, key); if (JS_IsException(rv)) { JS_FreeValue(cx, value); return JS_EXCEPTION; } JS_FreeValue(cx, value); if (p == NULL) { break; } start = p + 1; value = rv; } return rv; } static ngx_int_t ngx_qjs_dump_obj(ngx_engine_t *e, JSValueConst val, ngx_str_t *dst) { size_t len, byte_offset, byte_length; u_char *start, *p; JSValue buffer, stack; ngx_str_t str, stack_str; JSContext *cx; if (JS_IsNullOrUndefined(val)) { dst->data = NULL; dst->len = 0; return NGX_OK; } cx = e->u.qjs.ctx; buffer = JS_GetTypedArrayBuffer(cx, val, &byte_offset, &byte_length, NULL); if (!JS_IsException(buffer)) { start = JS_GetArrayBuffer(cx, &dst->len, buffer); JS_FreeValue(cx, buffer); if (start != NULL) { start += byte_offset; dst->len = byte_length; dst->data = njs_mp_alloc(e->pool, dst->len); if (dst->data == NULL) { return NGX_ERROR; } memcpy(dst->data, start, dst->len); return NGX_OK; } } str.data = (u_char *) JS_ToCString(cx, val); if (str.data != NULL) { str.len = ngx_strlen(str.data); stack = JS_GetPropertyStr(cx, val, "stack"); stack_str.len = 0; stack_str.data = NULL; if (!JS_IsException(stack) && !JS_IsUndefined(stack)) { stack_str.data = (u_char *) JS_ToCString(cx, stack); if (stack_str.data != NULL) { stack_str.len = ngx_strlen(stack_str.data); } } len = str.len; if (stack_str.len != 0) { len += stack_str.len + njs_length("\n"); } start = njs_mp_alloc(e->pool, len); if (start == NULL) { JS_FreeCString(cx, (char *) str.data); JS_FreeValue(cx, stack); return NGX_ERROR; } p = ngx_cpymem(start, str.data, str.len); if (stack_str.len != 0) { *p++ = '\n'; (void) ngx_cpymem(p, stack_str.data, stack_str.len); JS_FreeCString(cx, (char *) stack_str.data); } JS_FreeCString(cx, (char *) str.data); JS_FreeValue(cx, stack); } else { len = njs_length("[exception]"); start = njs_mp_alloc(e->pool, len); if (start == NULL) { return NGX_ERROR; } memcpy(start, "[exception]", len); } dst->data = start; dst->len = len; return NGX_OK; } ngx_int_t ngx_qjs_call(ngx_js_ctx_t *ctx, JSValue fn, JSValue *argv, int argc) { int rc; JSValue ret; ngx_str_t exception; JSRuntime *rt; JSContext *cx, *cx1; cx = ctx->engine->u.qjs.ctx; ret = JS_Call(cx, fn, JS_UNDEFINED, argc, argv); if (JS_IsException(ret)) { ngx_qjs_exception(ctx->engine, &exception); ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "js call exception: %V", &exception); return NGX_ERROR; } JS_FreeValue(cx, ret); rt = JS_GetRuntime(cx); for ( ;; ) { rc = JS_ExecutePendingJob(rt, &cx1); if (rc <= 0) { if (rc == -1) { ngx_qjs_exception(ctx->engine, &exception); ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "js job exception: %V", &exception); return NGX_ERROR; } break; } } return NGX_OK; } ngx_int_t ngx_qjs_exception(ngx_engine_t *e, ngx_str_t *s) { JSValue exception; exception = JS_GetException(e->u.qjs.ctx); if (ngx_qjs_dump_obj(e, exception, s) != NGX_OK) { return NGX_ERROR; } JS_FreeValue(e->u.qjs.ctx, exception); return NGX_OK; } ngx_int_t ngx_qjs_integer(JSContext *cx, JSValueConst val, ngx_int_t *n) { double num; if (JS_ToFloat64(cx, &num, val)) { return NGX_ERROR; } if (isinf(num) || isnan(num)) { (void) JS_ThrowTypeError(cx, "invalid number"); return NGX_ERROR; } *n = num; return NGX_OK; } ngx_int_t ngx_qjs_string(ngx_engine_t *e, JSValueConst val, ngx_str_t *dst) { size_t len, byte_offset, byte_length; u_char *start; JSValue buffer; JSContext *cx; const char *str; if (JS_IsNullOrUndefined(val)) { dst->data = NULL; dst->len = 0; return NGX_OK; } cx = e->u.qjs.ctx; if (JS_IsString(val)) { goto string; } buffer = JS_GetTypedArrayBuffer(cx, val, &byte_offset, &byte_length, NULL); if (!JS_IsException(buffer)) { start = JS_GetArrayBuffer(cx, &dst->len, buffer); JS_FreeValue(cx, buffer); if (start != NULL) { start += byte_offset; dst->len = byte_length; dst->data = njs_mp_alloc(e->pool, dst->len); if (dst->data == NULL) { return NGX_ERROR; } memcpy(dst->data, start, dst->len); return NGX_OK; } } string: str = JS_ToCString(cx, val); if (str == NULL) { return NGX_ERROR; } len = strlen(str); start = njs_mp_alloc(e->pool, len); if (start == NULL) { JS_FreeCString(cx, str); return NGX_ERROR; } memcpy(start, str, len); JS_FreeCString(cx, str); dst->data = start; dst->len = len; return NGX_OK; } static void ngx_qjs_timer_handler(ngx_event_t *ev) { void *external; JSContext *cx; ngx_int_t rc; ngx_js_ctx_t *ctx; ngx_qjs_event_t *event; event = (ngx_qjs_event_t *) ((u_char *) ev - offsetof(ngx_qjs_event_t, ev)); cx = event->ctx; external = JS_GetContextOpaque(cx); ctx = ngx_qjs_external_ctx(cx, external); rc = ngx_qjs_call((ngx_js_ctx_t *) ctx, event->function, event->args, event->nargs); ngx_js_del_event(ctx, event); ngx_qjs_external_event_finalize(cx)(external, rc); } static void ngx_qjs_clear_timer(ngx_qjs_event_t *event) { int i; JSContext *cx; cx = event->ctx; if (event->ev.timer_set) { ngx_del_timer(&event->ev); } JS_FreeValue(cx, event->function); for (i = 0; i < (int) event->nargs; i++) { JS_FreeValue(cx, event->args[i]); } } static JSValue ngx_qjs_process_getter(JSContext *cx, JSValueConst this_val) { return qjs_process_object(cx, ngx_argc, (const char **) ngx_argv); } static JSValue ngx_qjs_ext_set_timeout(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int immediate) { int i, n; void *external; uint32_t delay; ngx_js_ctx_t *ctx; ngx_qjs_event_t *event; ngx_connection_t *c; if (!JS_IsFunction(cx, argv[0])) { return JS_ThrowTypeError(cx, "first arg must be a function"); } delay = 0; if (!immediate && argc >= 2) { if (JS_ToUint32(cx, &delay, argv[1]) < 0) { return JS_EXCEPTION; } } n = immediate ? 1 : 2; argc = (argc >= n) ? argc - n : 0; external = JS_GetContextOpaque(cx); ctx = ngx_qjs_external_ctx(cx, external); event = ngx_pcalloc(ngx_qjs_external_pool(cx, external), sizeof(ngx_qjs_event_t) + sizeof(JSValue) * argc); if (event == NULL) { return JS_ThrowOutOfMemory(cx); } event->ctx = cx; event->function = JS_DupValue(cx, argv[0]); event->nargs = argc; event->args = (JSValue *) &event[1]; event->destructor = ngx_qjs_clear_timer; event->fd = ctx->event_id++; c = ngx_qjs_external_connection(cx, external); event->ev.log = c->log; event->ev.data = event; event->ev.handler = ngx_qjs_timer_handler; if (event->nargs != 0) { for (i = 0; i < argc; i++) { event->args[i] = JS_DupValue(cx, argv[n + i]); } } ngx_js_add_event(ctx, event); ngx_add_timer(&event->ev, delay); return JS_NewInt32(cx, event->fd); } static JSValue ngx_qjs_ext_clear_timeout(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv) { uint32_t id; ngx_js_ctx_t *ctx; ngx_qjs_event_t event_lookup, *event; njs_rbtree_node_t *rb; if (JS_ToUint32(cx, &id, argv[0]) < 0) { return JS_EXCEPTION; } ctx = ngx_qjs_external_ctx(cx, JS_GetContextOpaque(cx)); event_lookup.fd = id; rb = njs_rbtree_find(&ctx->waiting_events, &event_lookup.node); if (rb == NULL) { return JS_ThrowReferenceError(cx, "failed to find timer"); } event = (ngx_qjs_event_t *) ((u_char *) rb - offsetof(ngx_qjs_event_t, node)); ngx_js_del_event(ctx, event); return JS_UNDEFINED; } static JSValue ngx_qjs_ext_build(JSContext *cx, JSValueConst this_val) { return JS_NewStringLen(cx, #ifdef NGX_BUILD (char *) NGX_BUILD, njs_strlen(NGX_BUILD) #else (char *) "", 0 #endif ); } static JSValue ngx_qjs_ext_conf_prefix(JSContext *cx, JSValueConst this_val) { return JS_NewStringLen(cx, (char *) ngx_cycle->prefix.data, ngx_cycle->prefix.len); } static JSValue ngx_qjs_ext_conf_file_path(JSContext *cx, JSValueConst this_val) { return JS_NewStringLen(cx, (char *) ngx_cycle->conf_file.data, ngx_cycle->conf_file.len); } static JSValue ngx_qjs_ext_constant_integer(JSContext *cx, JSValueConst this_val, int magic) { return JS_NewInt32(cx, magic); } static JSValue ngx_qjs_ext_error_log_path(JSContext *cx, JSValueConst this_val) { return JS_NewStringLen(cx, (char *) ngx_cycle->error_log.data, ngx_cycle->error_log.len); } static JSValue ngx_qjs_ext_prefix(JSContext *cx, JSValueConst this_val) { return JS_NewStringLen(cx, (char *) ngx_cycle->prefix.data, ngx_cycle->prefix.len); } static JSValue ngx_qjs_ext_worker_id(JSContext *cx, JSValueConst this_val) { return JS_NewInt32(cx, ngx_worker); } static void ngx_qjs_console_finalizer(JSRuntime *rt, JSValue val) { ngx_queue_t *labels, *q, *next; ngx_js_console_t *console; ngx_js_timelabel_t *label; console = JS_GetOpaque(val, NGX_QJS_CLASS_ID_CONSOLE); if (console == (void *) 1) { return; } labels = &console->labels; q = ngx_queue_head(labels); for ( ;; ) { if (q == ngx_queue_sentinel(labels)) { break; } next = ngx_queue_next(q); label = ngx_queue_data(q, ngx_js_timelabel_t, queue); ngx_queue_remove(&label->queue); js_free_rt(rt, label); q = next; } js_free_rt(rt, console); } static JSValue ngx_qjs_ext_log(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) { char *p; uint32_t level; ngx_str_t msg; ngx_js_ctx_t *ctx; ngx_connection_t *c; p = JS_GetContextOpaque(cx); if (p == NULL) { return JS_ThrowInternalError(cx, "external is not set"); } level = magic & NGX_JS_LOG_MASK; if (level == 0) { if (JS_ToUint32(cx, &level, argv[0]) < 0) { return JS_EXCEPTION; } argc--; argv++; } ctx = ngx_qjs_external_ctx(cx, p); c = ngx_qjs_external_connection(cx, p); for ( ; argc > 0; argc--, argv++) { if (ngx_qjs_dump_obj(ctx->engine, argv[0], &msg) != NGX_OK) { return JS_EXCEPTION; } ngx_js_logger(c, level, (u_char *) msg.data, msg.len); } return JS_UNDEFINED; } static JSValue ngx_qjs_ext_console_time(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv) { ngx_str_t name; ngx_queue_t *labels, *q; ngx_js_console_t *console; ngx_connection_t *c; ngx_js_timelabel_t *label; static const ngx_str_t default_label = ngx_string("default"); console = JS_GetOpaque(this_val, NGX_QJS_CLASS_ID_CONSOLE); if (console == NULL) { return JS_ThrowInternalError(cx, "this is not a console object"); } if (console == (void *) 1) { console = js_malloc(cx, sizeof(ngx_js_console_t)); if (console == NULL) { return JS_ThrowOutOfMemory(cx); } ngx_queue_init(&console->labels); JS_SetOpaque(this_val, console); } if (!JS_IsUndefined(argv[0])) { name.data = (u_char *) JS_ToCStringLen(cx, &name.len, argv[0]); if (name.data == NULL) { return JS_EXCEPTION; } } else { name = default_label; } labels = &console->labels; for (q = ngx_queue_head(labels); q != ngx_queue_sentinel(labels); q = ngx_queue_next(q)) { label = ngx_queue_data(q, ngx_js_timelabel_t, queue); if (name.len == label->name.length && ngx_strncmp(name.data, label->name.start, name.len) == 0) { c = ngx_qjs_external_connection(cx, JS_GetContextOpaque(cx)); ngx_log_error(NGX_LOG_INFO, c->log, 0, "js: Timer \"%V\" already" " exists", &name); goto done; } } label = js_malloc(cx, sizeof(ngx_js_timelabel_t) + name.len); if (label == NULL) { if (name.data != default_label.data) { JS_FreeCString(cx, (char *) name.data); } return JS_ThrowOutOfMemory(cx); } label->name.length = name.len; label->name.start = (u_char *) label + sizeof(ngx_js_timelabel_t); memcpy(label->name.start, name.data, name.len); label->time = ngx_js_monotonic_time(); ngx_queue_insert_tail(&console->labels, &label->queue); done: if (name.data != default_label.data) { JS_FreeCString(cx, (char *) name.data); } return JS_UNDEFINED; } static JSValue ngx_qjs_ext_console_time_end(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv) { uint64_t ns, ms; ngx_str_t name; ngx_queue_t *labels, *q; ngx_js_console_t *console; ngx_connection_t *c; ngx_js_timelabel_t *label; static const ngx_str_t default_label = ngx_string("default"); ns = ngx_js_monotonic_time(); console = JS_GetOpaque(this_val, NGX_QJS_CLASS_ID_CONSOLE); if (console == NULL) { return JS_ThrowInternalError(cx, "this is not a console object"); } if (!JS_IsUndefined(argv[0])) { name.data = (u_char *) JS_ToCStringLen(cx, &name.len, argv[0]); if (name.data == NULL) { return JS_EXCEPTION; } } else { name = default_label; } if (console == (void *) 1) { goto not_found; } labels = &console->labels; q = ngx_queue_head(labels); for ( ;; ) { if (q == ngx_queue_sentinel(labels)) { goto not_found; } label = ngx_queue_data(q, ngx_js_timelabel_t, queue); if (name.len == label->name.length && ngx_strncmp(name.data, label->name.start, name.len) == 0) { ngx_queue_remove(&label->queue); break; } q = ngx_queue_next(q); } ns = ns - label->time; js_free(cx, label); ms = ns / 1000000; ns = ns % 1000000; c = ngx_qjs_external_connection(cx, JS_GetContextOpaque(cx)); ngx_log_error(NGX_LOG_INFO, c->log, 0, "js: %V: %uL.%06uLms", &name, ms, ns); if (name.data != default_label.data) { JS_FreeCString(cx, (char *) name.data); } return JS_UNDEFINED; not_found: c = ngx_qjs_external_connection(cx, JS_GetContextOpaque(cx)); ngx_log_error(NGX_LOG_INFO, c->log, 0, "js: Timer \"%V\" doesn't exist", &name); if (name.data != default_label.data) { JS_FreeCString(cx, (char *) name.data); } return JS_UNDEFINED; } static JSModuleDef * ngx_qjs_module_loader(JSContext *cx, const char *module_name, void *opaque) { JSValue func_val; njs_int_t ret; njs_str_t text; JSModuleDef *m; njs_module_info_t info; ngx_js_loc_conf_t *conf; ngx_js_code_entry_t *pc; conf = opaque; njs_memzero(&info, sizeof(njs_module_info_t)); info.name.start = (u_char *) module_name; info.name.length = njs_strlen(module_name); errno = 0; ret = ngx_js_module_lookup(conf, &info); if (ret != NJS_OK) { if (errno != 0) { JS_ThrowReferenceError(cx, "Cannot load module \"%s\" " "(%s:%s)", module_name, ngx_js_errno_string(errno), strerror(errno)); } return NULL; } ret = ngx_js_module_read(conf->engine->pool, info.fd, &text); (void) close(info.fd); if (ret != NJS_OK) { JS_ThrowInternalError(cx, "while reading \"%*s\" module", (int) info.file.length, info.file.start); return NULL; } func_val = JS_Eval(cx, (char *) text.start, text.length, module_name, JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); njs_mp_free(conf->engine->pool, text.start); if (JS_IsException(func_val)) { return NULL; } if (conf->engine->precompiled == NULL) { conf->engine->precompiled = njs_arr_create(conf->engine->pool, 4, sizeof(ngx_js_code_entry_t)); if (conf->engine->precompiled == NULL) { JS_FreeValue(cx, func_val); JS_ThrowOutOfMemory(cx); return NULL; } } pc = njs_arr_add(conf->engine->precompiled); if (pc == NULL) { JS_FreeValue(cx, func_val); JS_ThrowOutOfMemory(cx); return NULL; } pc->code = JS_WriteObject(cx, &pc->code_size, func_val, JS_WRITE_OBJ_BYTECODE); if (pc->code == NULL) { JS_FreeValue(cx, func_val); JS_ThrowInternalError(cx, "could not write module bytecode"); return NULL; } m = JS_VALUE_GET_PTR(func_val); JS_FreeValue(cx, func_val); return m; } static int ngx_qjs_unhandled_rejection(ngx_js_ctx_t *ctx) { size_t len; uint32_t i; JSContext *cx; const char *str; ngx_js_rejected_promise_t *rejected_promise; if (ctx->rejected_promises == NULL || ctx->rejected_promises->items == 0) { return 0; } cx = ctx->engine->u.qjs.ctx; rejected_promise = ctx->rejected_promises->start; str = JS_ToCStringLen(cx, &len, ngx_qjs_arg(rejected_promise->message)); if (njs_slow_path(str == NULL)) { return -1; } JS_ThrowTypeError(cx, "unhandled promise rejection: %*s", (int) len, str); JS_FreeCString(cx, str); for (i = 0; i < ctx->rejected_promises->items; i++) { JS_FreeValue(cx, ngx_qjs_arg(rejected_promise[i].promise)); JS_FreeValue(cx, ngx_qjs_arg(rejected_promise[i].message)); } njs_arr_destroy(ctx->rejected_promises); ctx->rejected_promises = NULL; return 1; } static void ngx_qjs_rejection_tracker(JSContext *cx, JSValueConst promise, JSValueConst reason, JS_BOOL is_handled, void *opaque) { void *promise_obj; uint32_t i, length; ngx_js_ctx_t *ctx; ngx_js_rejected_promise_t *rejected_promise; ctx = opaque; if (is_handled && ctx->rejected_promises != NULL) { rejected_promise = ctx->rejected_promises->start; length = ctx->rejected_promises->items; promise_obj = JS_VALUE_GET_PTR(promise); for (i = 0; i < length; i++) { if (JS_VALUE_GET_PTR(ngx_qjs_arg(rejected_promise[i].promise)) == promise_obj) { JS_FreeValue(cx, ngx_qjs_arg(rejected_promise[i].promise)); JS_FreeValue(cx, ngx_qjs_arg(rejected_promise[i].message)); njs_arr_remove(ctx->rejected_promises, &rejected_promise[i]); break; } } return; } if (ctx->rejected_promises == NULL) { if (ctx->engine == NULL) { /* Do not track rejections during eval stage. The exception * is lifted by the ngx_qjs_clone() function manually. */ return; } ctx->rejected_promises = njs_arr_create(ctx->engine->pool, 4, sizeof(ngx_js_rejected_promise_t)); if (ctx->rejected_promises == NULL) { return; } } rejected_promise = njs_arr_add(ctx->rejected_promises); if (rejected_promise == NULL) { return; } ngx_qjs_arg(rejected_promise->promise) = JS_DupValue(cx, promise); ngx_qjs_arg(rejected_promise->message) = JS_DupValue(cx, reason); } static JSModuleDef * ngx_qjs_core_init(JSContext *cx, const char *name) { int ret; JSValue global_obj, proto, obj; JSModuleDef *m; if (!JS_IsRegisteredClass(JS_GetRuntime(cx), NGX_QJS_CLASS_ID_CONSOLE)) { if (JS_NewClass(JS_GetRuntime(cx), NGX_QJS_CLASS_ID_CONSOLE, &ngx_qjs_console_class) < 0) { return NULL; } proto = JS_NewObject(cx); if (JS_IsException(proto)) { return NULL; } JS_SetPropertyFunctionList(cx, proto, ngx_qjs_ext_console, njs_nitems(ngx_qjs_ext_console)); JS_SetClassProto(cx, NGX_QJS_CLASS_ID_CONSOLE, proto); } obj = JS_NewObject(cx); if (JS_IsException(obj)) { return NULL; } JS_SetPropertyFunctionList(cx, obj, ngx_qjs_ext_ngx, njs_nitems(ngx_qjs_ext_ngx)); global_obj = JS_GetGlobalObject(cx); JS_SetPropertyFunctionList(cx, global_obj, ngx_qjs_ext_global, njs_nitems(ngx_qjs_ext_global)); ret = JS_SetPropertyStr(cx, global_obj, "ngx", obj); if (ret < 0) { JS_FreeValue(cx, global_obj); return NULL; } obj = JS_NewObjectClass(cx, NGX_QJS_CLASS_ID_CONSOLE); if (JS_IsException(obj)) { JS_FreeValue(cx, global_obj); return NULL; } JS_SetOpaque(obj, (void *) 1); ret = JS_SetPropertyStr(cx, global_obj, "console", obj); if (ret < 0) { JS_FreeValue(cx, global_obj); return NULL; } JS_FreeValue(cx, global_obj); m = JS_NewCModule(cx, name, NULL); if (m == NULL) { return NULL; } return m; } #endif ngx_int_t ngx_js_call(njs_vm_t *vm, njs_function_t *func, njs_opaque_value_t *args, njs_uint_t nargs) { njs_int_t ret; ngx_str_t exception; ngx_connection_t *c; ret = njs_vm_call(vm, func, njs_value_arg(args), nargs); if (ret == NJS_ERROR) { ngx_js_exception(vm, &exception); c = ngx_external_connection(vm, njs_vm_external_ptr(vm)); ngx_log_error(NGX_LOG_ERR, c->log, 0, "js exception: %V", &exception); return NGX_ERROR; } for ( ;; ) { ret = njs_vm_execute_pending_job(vm); if (ret <= NJS_OK) { c = ngx_external_connection(vm, njs_vm_external_ptr(vm)); if (ret == NJS_ERROR) { ngx_js_exception(vm, &exception); ngx_log_error(NGX_LOG_ERR, c->log, 0, "js job exception: %V", &exception); return NGX_ERROR; } break; } } return NGX_OK; } ngx_int_t ngx_js_exception(njs_vm_t *vm, ngx_str_t *s) { njs_int_t ret; njs_str_t str; ret = njs_vm_exception_string(vm, &str); if (ret != NJS_OK) { return NGX_ERROR; } s->data = str.start; s->len = str.length; return NGX_OK; } ngx_int_t ngx_js_integer(njs_vm_t *vm, njs_value_t *value, ngx_int_t *n) { if (!njs_value_is_valid_number(value)) { njs_vm_error(vm, "is not a number"); return NGX_ERROR; } *n = njs_value_number(value); return NGX_OK; } ngx_int_t ngx_js_string(njs_vm_t *vm, njs_value_t *value, njs_str_t *str) { if (value != NULL && !njs_value_is_null_or_undefined(value)) { if (njs_vm_value_to_bytes(vm, str, value) == NJS_ERROR) { return NGX_ERROR; } } else { str->start = NULL; str->length = 0; } return NGX_OK; } static njs_int_t njs_function_bind(njs_vm_t *vm, const njs_str_t *name, njs_function_native_t native, njs_bool_t ctor) { njs_function_t *f; njs_opaque_value_t value; f = njs_vm_function_alloc(vm, native, 1, ctor); if (f == NULL) { return NJS_ERROR; } njs_value_function_set(njs_value_arg(&value), f); return njs_vm_bind(vm, name, njs_value_arg(&value), 1); } static intptr_t ngx_js_event_rbtree_compare(njs_rbtree_node_t *node1, njs_rbtree_node_t *node2) { ngx_js_event_t *ev1, *ev2; ev1 = (ngx_js_event_t *) ((u_char *) node1 - offsetof(ngx_js_event_t, node)); ev2 = (ngx_js_event_t *) ((u_char *) node2 - offsetof(ngx_js_event_t, node)); if (ev1->fd < ev2->fd) { return -1; } if (ev1->fd > ev2->fd) { return 1; } return 0; } void ngx_js_ctx_init(ngx_js_ctx_t *ctx, ngx_log_t *log) { ctx->log = log; ctx->event_id = 0; njs_rbtree_init(&ctx->waiting_events, ngx_js_event_rbtree_compare); } void ngx_js_ctx_destroy(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *conf) { ctx->engine->destroy(ctx->engine, ctx, conf); } static njs_int_t ngx_js_core_init(njs_vm_t *vm) { njs_int_t ret, proto_id; njs_str_t name; njs_opaque_value_t value; static const njs_str_t set_timeout = njs_str("setTimeout"); static const njs_str_t set_immediate = njs_str("setImmediate"); static const njs_str_t clear_timeout = njs_str("clearTimeout"); proto_id = njs_vm_external_prototype(vm, ngx_js_ext_core, njs_nitems(ngx_js_ext_core)); if (proto_id < 0) { return NJS_ERROR; } ret = njs_vm_external_create(vm, njs_value_arg(&value), proto_id, NULL, 1); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } name.length = 3; name.start = (u_char *) "ngx"; ret = njs_vm_bind(vm, &name, njs_value_arg(&value), 1); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ngx_js_console_proto_id = njs_vm_external_prototype(vm, ngx_js_ext_console, njs_nitems(ngx_js_ext_console)); if (ngx_js_console_proto_id < 0) { return NJS_ERROR; } ret = njs_vm_external_create(vm, njs_value_arg(&value), ngx_js_console_proto_id, NULL, 1); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } name.length = 7; name.start = (u_char *) "console"; ret = njs_vm_bind(vm, &name, njs_value_arg(&value), 1); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_function_bind(vm, &set_timeout, njs_set_timeout, 1); if (ret != NJS_OK) { return NJS_ERROR; } ret = njs_function_bind(vm, &set_immediate, njs_set_immediate, 1); if (ret != NJS_OK) { return NJS_ERROR; } ret = njs_function_bind(vm, &clear_timeout, njs_clear_timeout, 1); if (ret != NJS_OK) { return NJS_ERROR; } return NJS_OK; } njs_int_t ngx_js_ext_string(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { char *p; ngx_str_t *field; p = njs_vm_external(vm, NJS_PROTO_ID_ANY, value); if (p == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } field = (ngx_str_t *) (p + njs_vm_prop_magic32(prop)); return njs_vm_value_string_create(vm, retval, field->data, field->len); } njs_int_t ngx_js_ext_uint(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { char *p; ngx_uint_t field; p = njs_vm_external(vm, NJS_PROTO_ID_ANY, value); if (p == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } field = *(ngx_uint_t *) (p + njs_vm_prop_magic32(prop)); njs_value_number_set(retval, field); return NJS_OK; } njs_int_t ngx_js_ext_constant(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { uint32_t magic32; magic32 = njs_vm_prop_magic32(prop); switch (njs_vm_prop_magic16(prop)) { case NGX_JS_NUMBER: njs_value_number_set(retval, magic32); break; case NGX_JS_BOOLEAN: default: njs_value_boolean_set(retval, magic32); break; } return NJS_OK; } njs_int_t ngx_js_ext_flags(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { uintptr_t data; data = (uintptr_t) njs_vm_external(vm, NJS_PROTO_ID_ANY, value); if (data == 0) { njs_value_undefined_set(retval); return NJS_DECLINED; } data = data & (uintptr_t) njs_vm_prop_magic32(prop); switch (njs_vm_prop_magic16(prop)) { case NGX_JS_BOOLEAN: default: njs_value_boolean_set(retval, data); break; } return NJS_OK; } njs_int_t ngx_js_ext_build(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { return njs_vm_value_string_create(vm, retval, #ifdef NGX_BUILD (u_char *) NGX_BUILD, njs_strlen(NGX_BUILD) #else (u_char *) "", 0 #endif ); } njs_int_t ngx_js_ext_conf_file_path(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { return njs_vm_value_string_create(vm, retval, ngx_cycle->conf_file.data, ngx_cycle->conf_file.len); } njs_int_t ngx_js_ext_conf_prefix(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { return njs_vm_value_string_create(vm, retval, ngx_cycle->conf_prefix.data, ngx_cycle->conf_prefix.len); } njs_int_t ngx_js_ext_error_log_path(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { return njs_vm_value_string_create(vm, retval, ngx_cycle->error_log.data, ngx_cycle->error_log.len); } njs_int_t ngx_js_ext_prefix(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { return njs_vm_value_string_create(vm, retval, ngx_cycle->prefix.data, ngx_cycle->prefix.len); } njs_int_t ngx_js_ext_version(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { return njs_vm_value_string_create(vm, retval, (u_char *) NGINX_VERSION, njs_strlen(NGINX_VERSION)); } njs_int_t ngx_js_ext_worker_id(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { njs_value_number_set(retval, ngx_worker); return NJS_OK; } njs_int_t ngx_js_ext_log(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t magic, njs_value_t *retval) { char *p; ngx_int_t lvl; njs_str_t msg; njs_uint_t n, level; ngx_connection_t *c; p = njs_vm_external(vm, NJS_PROTO_ID_ANY, njs_argument(args, 0)); if (p == NULL) { njs_vm_error(vm, "\"this\" is not an external"); return NJS_ERROR; } level = magic & NGX_JS_LOG_MASK; if (level == 0) { if (ngx_js_integer(vm, njs_arg(args, nargs, 1), &lvl) != NGX_OK) { return NJS_ERROR; } level = lvl; n = 2; } else { n = 1; } c = ngx_external_connection(vm, p); for (; n < nargs; n++) { if (njs_vm_value_dump(vm, &msg, njs_argument(args, n), 1, !!(magic & NGX_JS_LOG_DUMP)) == NJS_ERROR) { return NJS_ERROR; } ngx_js_logger(c, level, msg.start, msg.length); } njs_value_undefined_set(retval); return NJS_OK; } static njs_int_t ngx_js_ext_console_time(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_str_t name; ngx_queue_t *labels, *q; njs_value_t *value, *this; ngx_js_console_t *console; ngx_js_timelabel_t *label; static const njs_str_t default_label = njs_str("default"); this = njs_argument(args, 0); if (njs_slow_path(!njs_value_is_external(this, ngx_js_console_proto_id))) { njs_vm_type_error(vm, "\"this\" is not a console external"); return NJS_ERROR; } name = default_label; value = njs_arg(args, nargs, 1); if (njs_slow_path(!njs_value_is_string(value))) { if (!njs_value_is_undefined(value)) { ret = njs_value_to_string(vm, value, value); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_value_string_get(value, &name); } } else { njs_value_string_get(value, &name); } console = njs_value_external(this); if (console == NULL) { console = njs_mp_alloc(njs_vm_memory_pool(vm), sizeof(ngx_js_console_t)); if (console == NULL) { njs_vm_memory_error(vm); return NJS_ERROR; } ngx_queue_init(&console->labels); njs_value_external_set(this, console); } labels = &console->labels; for (q = ngx_queue_head(labels); q != ngx_queue_sentinel(labels); q = ngx_queue_next(q)) { label = ngx_queue_data(q, ngx_js_timelabel_t, queue); if (njs_strstr_eq(&name, &label->name)) { ngx_js_log(vm, njs_vm_external_ptr(vm), NGX_LOG_INFO, "Timer \"%V\" already exists.", &name); njs_value_undefined_set(retval); return NJS_OK; } } label = njs_mp_alloc(njs_vm_memory_pool(vm), sizeof(ngx_js_timelabel_t) + name.length); if (njs_slow_path(label == NULL)) { njs_vm_memory_error(vm); return NJS_ERROR; } label->name.length = name.length; label->name.start = (u_char *) label + sizeof(ngx_js_timelabel_t); memcpy(label->name.start, name.start, name.length); label->time = ngx_js_monotonic_time(); ngx_queue_insert_tail(&console->labels, &label->queue); njs_value_undefined_set(retval); return NJS_OK; } static njs_int_t ngx_js_ext_console_time_end(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { uint64_t ns, ms; njs_int_t ret; njs_str_t name; ngx_queue_t *labels, *q; njs_value_t *value, *this; ngx_js_console_t *console; ngx_js_timelabel_t *label; static const njs_str_t default_label = njs_str("default"); ns = ngx_js_monotonic_time(); this = njs_argument(args, 0); if (njs_slow_path(!njs_value_is_external(this, ngx_js_console_proto_id))) { njs_vm_type_error(vm, "\"this\" is not a console external"); return NJS_ERROR; } name = default_label; value = njs_arg(args, nargs, 1); if (njs_slow_path(!njs_value_is_string(value))) { if (!njs_value_is_undefined(value)) { ret = njs_value_to_string(vm, value, value); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_value_string_get(value, &name); } } else { njs_value_string_get(value, &name); } console = njs_value_external(this); if (njs_slow_path(console == NULL)) { goto not_found; } labels = &console->labels; q = ngx_queue_head(labels); for ( ;; ) { if (q == ngx_queue_sentinel(labels)) { goto not_found; } label = ngx_queue_data(q, ngx_js_timelabel_t, queue); if (njs_strstr_eq(&name, &label->name)) { ngx_queue_remove(&label->queue); break; } q = ngx_queue_next(q); } ns = ns - label->time; ms = ns / 1000000; ns = ns % 1000000; ngx_js_log(vm, njs_vm_external_ptr(vm), NGX_LOG_INFO, "%V: %uL.%06uLms", &name, ms, ns); njs_value_undefined_set(retval); return NJS_OK; not_found: ngx_js_log(vm, njs_vm_external_ptr(vm), NGX_LOG_INFO, "Timer \"%V\" doesn't exist.", &name); njs_value_undefined_set(retval); return NJS_OK; } static void ngx_js_timer_handler(ngx_event_t *ev) { njs_vm_t *vm; ngx_int_t rc; ngx_js_ctx_t *ctx; ngx_js_event_t *event; event = (ngx_js_event_t *) ((u_char *) ev - offsetof(ngx_js_event_t, ev)); vm = event->ctx; rc = ngx_js_call(vm, njs_value_function(njs_value_arg(&event->function)), event->args, event->nargs); ctx = ngx_external_ctx(vm, njs_vm_external_ptr(vm)); ngx_js_del_event(ctx, event); ngx_external_event_finalize(vm)(njs_vm_external_ptr(vm), rc); } static void ngx_js_clear_timer(ngx_js_event_t *event) { if (event->ev.timer_set) { ngx_del_timer(&event->ev); } } static njs_int_t njs_set_timer(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_bool_t immediate, njs_value_t *retval) { uint64_t delay; njs_uint_t n; ngx_js_ctx_t *ctx; ngx_js_event_t *event; ngx_connection_t *c; if (njs_slow_path(nargs < 2)) { njs_vm_type_error(vm, "too few arguments"); return NJS_ERROR; } if (njs_slow_path(!njs_value_is_function(njs_argument(args, 1)))) { njs_vm_type_error(vm, "first arg must be a function"); return NJS_ERROR; } delay = 0; if (!immediate && nargs >= 3 && njs_value_is_number(njs_argument(args, 2))) { delay = njs_value_number(njs_argument(args, 2)); } n = immediate ? 2 : 3; nargs = (nargs >= n) ? nargs - n : 0; event = njs_mp_zalloc(njs_vm_memory_pool(vm), sizeof(ngx_js_event_t) + sizeof(njs_opaque_value_t) * nargs); if (njs_slow_path(event == NULL)) { njs_vm_memory_error(vm); return NJS_ERROR; } event->ctx = vm; njs_value_assign(&event->function, njs_argument(args, 1)); event->nargs = nargs; event->args = (njs_opaque_value_t *) &event[1]; event->destructor = ngx_js_clear_timer; ctx = ngx_external_ctx(vm, njs_vm_external_ptr(vm)); event->fd = ctx->event_id++; c = ngx_external_connection(vm, njs_vm_external_ptr(vm)); event->ev.log = c->log; event->ev.data = event; event->ev.handler = ngx_js_timer_handler; if (event->nargs != 0) { memcpy(event->args, njs_argument(args, n), sizeof(njs_opaque_value_t) * event->nargs); } ngx_js_add_event(ctx, event); ngx_add_timer(&event->ev, delay); njs_value_number_set(retval, event->fd); return NJS_OK; } static njs_int_t njs_set_timeout(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { return njs_set_timer(vm, args, nargs, unused, 0, retval); } static njs_int_t njs_set_immediate(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { return njs_set_timer(vm, args, nargs, unused, 1, retval); } static njs_int_t njs_clear_timeout(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { ngx_js_ctx_t *ctx; ngx_js_event_t event_lookup, *event; njs_rbtree_node_t *rb; if (nargs < 2 || !njs_value_is_number(njs_argument(args, 1))) { njs_value_undefined_set(retval); return NJS_OK; } ctx = ngx_external_ctx(vm, njs_vm_external_ptr(vm)); event_lookup.fd = njs_value_number(njs_argument(args, 1)); rb = njs_rbtree_find(&ctx->waiting_events, &event_lookup.node); if (njs_slow_path(rb == NULL)) { njs_vm_internal_error(vm, "failed to find timer"); return NJS_ERROR; } event = (ngx_js_event_t *) ((u_char *) rb - offsetof(ngx_js_event_t, node)); ngx_js_del_event(ctx, event); njs_value_undefined_set(retval); return NJS_OK; } void ngx_js_log(njs_vm_t *vm, njs_external_ptr_t external, ngx_uint_t level, const char *fmt, ...) { u_char *p; va_list args; ngx_connection_t *c; u_char buf[NGX_MAX_ERROR_STR]; va_start(args, fmt); p = njs_vsprintf(buf, buf + sizeof(buf), fmt, args); va_end(args); if (external != NULL) { c = ngx_external_connection(vm, external); } else { c = NULL; } ngx_js_logger(c, level, buf, p - buf); } void ngx_js_logger(ngx_connection_t *c, ngx_uint_t level, const u_char *start, size_t length) { ngx_log_t *log; ngx_log_handler_pt handler; handler = NULL; if (c != NULL) { log = c->log; handler = log->handler; log->handler = NULL; } else { /* Logger was called during init phase. */ log = ngx_cycle->log; } ngx_log_error(level, log, 0, "js: %*s", length, start); if (c != NULL) { log->handler = handler; } } char * ngx_js_import(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_js_loc_conf_t *jscf = conf; u_char *p, *end, c; ngx_int_t from; ngx_str_t *value, name, path; ngx_js_named_path_t *import; value = cf->args->elts; from = (cf->args->nelts == 4); if (from) { if (ngx_strcmp(value[2].data, "from") != 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"", &value[2]); return NGX_CONF_ERROR; } } name = value[1]; path = (from ? value[3] : value[1]); if (!from) { end = name.data + name.len; for (p = end - 1; p >= name.data; p--) { if (*p == '/') { break; } } name.data = p + 1; name.len = end - p - 1; if (name.len < 3 || ngx_memcmp(&name.data[name.len - 3], ".js", 3) != 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "cannot extract export name from file path " "\"%V\", use extended \"from\" syntax", &path); return NGX_CONF_ERROR; } name.len -= 3; } if (name.len == 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "empty export name"); return NGX_CONF_ERROR; } p = name.data; end = name.data + name.len; while (p < end) { c = ngx_tolower(*p); if (*p != '_' && (c < 'a' || c > 'z')) { if (p == name.data) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "cannot start " "with \"%c\" in export name \"%V\"", *p, &name); return NGX_CONF_ERROR; } if (*p < '0' || *p > '9') { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid character " "\"%c\" in export name \"%V\"", *p, &name); return NGX_CONF_ERROR; } } p++; } if (ngx_strchr(path.data, '\'') != NULL) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid character \"'\" " "in file path \"%V\"", &path); return NGX_CONF_ERROR; } if (jscf->imports == NGX_CONF_UNSET_PTR) { jscf->imports = ngx_array_create(cf->pool, 4, sizeof(ngx_js_named_path_t)); if (jscf->imports == NULL) { return NGX_CONF_ERROR; } } import = ngx_array_push(jscf->imports); if (import == NULL) { return NGX_CONF_ERROR; } import->name = name; import->path = path; import->file = cf->conf_file->file.name.data; import->line = cf->conf_file->line; return NGX_CONF_OK; } char * ngx_js_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { char *p = conf; ngx_str_t *value; ngx_uint_t *type, m; ngx_conf_bitmask_t *mask; type = (size_t *) (p + cmd->offset); if (*type != NGX_CONF_UNSET_UINT) { return "is duplicate"; } value = cf->args->elts; mask = cmd->post; for (m = 0; mask[m].name.len != 0; m++) { if (mask[m].name.len != value[1].len || ngx_strcasecmp(mask[m].name.data, value[1].data) != 0) { continue; } *type = mask[m].mask; break; } if (mask[m].name.len == 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid value \"%s\"", value[1].data); return NGX_CONF_ERROR; } return NGX_CONF_OK; } char * ngx_js_preload_object(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_js_loc_conf_t *jscf = conf; u_char *p, *end, c; ngx_int_t from; ngx_str_t *value, name, path; ngx_js_named_path_t *preload; value = cf->args->elts; from = (cf->args->nelts == 4); if (from) { if (ngx_strcmp(value[2].data, "from") != 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"", &value[2]); return NGX_CONF_ERROR; } } name = value[1]; path = (from ? value[3] : value[1]); if (!from) { end = name.data + name.len; for (p = end - 1; p >= name.data; p--) { if (*p == '/') { break; } } name.data = p + 1; name.len = end - p - 1; if (name.len < 5 || ngx_memcmp(&name.data[name.len - 5], ".json", 5) != 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "cannot extract export name from file path " "\"%V\", use extended \"from\" syntax", &path); return NGX_CONF_ERROR; } name.len -= 5; } if (name.len == 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "empty global name"); return NGX_CONF_ERROR; } p = name.data; end = name.data + name.len; while (p < end) { c = ngx_tolower(*p); if (*p != '_' && (c < 'a' || c > 'z')) { if (p == name.data) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "cannot start " "with \"%c\" in global name \"%V\"", *p, &name); return NGX_CONF_ERROR; } if (*p < '0' || *p > '9' || *p == '.') { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid character " "\"%c\" in global name \"%V\"", *p, &name); return NGX_CONF_ERROR; } } p++; } if (ngx_strchr(path.data, '\'') != NULL) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid character \"'\" " "in file path \"%V\"", &path); return NGX_CONF_ERROR; } if (jscf->preload_objects == NGX_CONF_UNSET_PTR) { jscf->preload_objects = ngx_array_create(cf->pool, 4, sizeof(ngx_js_named_path_t)); if (jscf->preload_objects == NULL) { return NGX_CONF_ERROR; } } preload = ngx_array_push(jscf->preload_objects); if (preload == NULL) { return NGX_CONF_ERROR; } preload->name = name; preload->path = path; preload->file = cf->conf_file->file.name.data; preload->line = cf->conf_file->line; return NGX_CONF_OK; } static ngx_int_t ngx_js_init_preload_vm(njs_vm_t *vm, ngx_js_loc_conf_t *conf) { u_char *p, *start; size_t size; njs_int_t ret; ngx_uint_t i; njs_opaque_value_t retval; ngx_js_named_path_t *preload; njs_str_t str = njs_str( "import __fs from 'fs';" "{ let g = (function (np, no, nf, nsp, r) {" "return function (n, p) {" "p = (p[0] == '/') ? p : ngx.conf_prefix + p;" "let o = r(p);" "globalThis[n] = np(" "o," "function (k, v) {" "if (v instanceof no) {" "nf(nsp(v, null));" "}" "return v;" "}" ");" "return;" "}" "})(JSON.parse,Object,Object.freeze," "Object.setPrototypeOf,__fs.readFileSync);\n" ); size = str.length; preload = conf->preload_objects->elts; for (i = 0; i < conf->preload_objects->nelts; i++) { size += sizeof("g('','');\n") - 1 + preload[i].name.len + preload[i].path.len; } size += sizeof("}\n") - 1; start = njs_mp_alloc(njs_vm_memory_pool(vm), size); if (start == NULL) { return NGX_ERROR; } p = ngx_cpymem(start, str.start, str.length); preload = conf->preload_objects->elts; for (i = 0; i < conf->preload_objects->nelts; i++) { p = ngx_cpymem(p, "g('", sizeof("g('") - 1); p = ngx_cpymem(p, preload[i].name.data, preload[i].name.len); p = ngx_cpymem(p, "','", sizeof("','") - 1); p = ngx_cpymem(p, preload[i].path.data, preload[i].path.len); p = ngx_cpymem(p, "');\n", sizeof("');\n") - 1); } p = ngx_cpymem(p, "}\n", sizeof("}\n") - 1); ret = njs_vm_compile(vm, &start, start + size); if (ret != NJS_OK) { return NGX_ERROR; } ret = njs_vm_start(vm, njs_value_arg(&retval)); if (ret != NJS_OK) { return NGX_ERROR; } ret = njs_vm_reuse(vm); if (ret != NJS_OK) { return NGX_ERROR; } return NGX_OK; } ngx_int_t ngx_js_merge_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf, ngx_js_loc_conf_t *prev, ngx_int_t (*init_vm) (ngx_conf_t *cf, ngx_js_loc_conf_t *conf)) { ngx_str_t *path, *s; ngx_uint_t i; ngx_array_t *imports, *preload_objects, *paths; ngx_js_named_path_t *import, *pi, *pij, *preload; if (prev->imports != NGX_CONF_UNSET_PTR && prev->engine == NULL) { /* * special handling to preserve conf->engine * in the "http" or "stream" section to inherit it to all servers */ if (init_vm(cf, (ngx_js_loc_conf_t *) prev) != NGX_OK) { return NGX_ERROR; } } if (conf->imports == NGX_CONF_UNSET_PTR && conf->type == prev->type && conf->paths == NGX_CONF_UNSET_PTR && conf->preload_objects == NGX_CONF_UNSET_PTR) { if (prev->engine != NULL) { conf->preload_objects = prev->preload_objects; conf->imports = prev->imports; conf->type = prev->type; conf->paths = prev->paths; conf->engine = prev->engine; return NGX_OK; } } if (prev->preload_objects != NGX_CONF_UNSET_PTR) { if (conf->preload_objects == NGX_CONF_UNSET_PTR) { conf->preload_objects = prev->preload_objects; } else { preload_objects = ngx_array_create(cf->pool, 4, sizeof(ngx_js_named_path_t)); if (preload_objects == NULL) { return NGX_ERROR; } pij = prev->preload_objects->elts; for (i = 0; i < prev->preload_objects->nelts; i++) { preload = ngx_array_push(preload_objects); if (preload == NULL) { return NGX_ERROR; } *preload = pij[i]; } pij = conf->preload_objects->elts; for (i = 0; i < conf->preload_objects->nelts; i++) { preload = ngx_array_push(preload_objects); if (preload == NULL) { return NGX_ERROR; } *preload = pij[i]; } conf->preload_objects = preload_objects; } } if (prev->imports != NGX_CONF_UNSET_PTR) { if (conf->imports == NGX_CONF_UNSET_PTR) { conf->imports = prev->imports; } else { imports = ngx_array_create(cf->pool, 4, sizeof(ngx_js_named_path_t)); if (imports == NULL) { return NGX_ERROR; } pi = prev->imports->elts; for (i = 0; i < prev->imports->nelts; i++) { import = ngx_array_push(imports); if (import == NULL) { return NGX_ERROR; } *import = pi[i]; } pi = conf->imports->elts; for (i = 0; i < conf->imports->nelts; i++) { import = ngx_array_push(imports); if (import == NULL) { return NGX_ERROR; } *import = pi[i]; } conf->imports = imports; } } if (prev->paths != NGX_CONF_UNSET_PTR) { if (conf->paths == NGX_CONF_UNSET_PTR) { conf->paths = prev->paths; } else { paths = ngx_array_create(cf->pool, 4, sizeof(ngx_str_t)); if (paths == NULL) { return NGX_ERROR; } s = prev->imports->elts; for (i = 0; i < prev->paths->nelts; i++) { path = ngx_array_push(paths); if (path == NULL) { return NGX_ERROR; } *path = s[i]; } s = conf->imports->elts; for (i = 0; i < conf->paths->nelts; i++) { path = ngx_array_push(paths); if (path == NULL) { return NGX_ERROR; } *path = s[i]; } conf->paths = paths; } } if (conf->imports == NGX_CONF_UNSET_PTR) { return NGX_OK; } return init_vm(cf, (ngx_js_loc_conf_t *) conf); } static void ngx_js_rejection_tracker(njs_vm_t *vm, njs_external_ptr_t unused, njs_bool_t is_handled, njs_value_t *promise, njs_value_t *reason) { void *promise_obj; uint32_t i, length; ngx_js_ctx_t *ctx; ngx_js_rejected_promise_t *rejected_promise; ctx = ngx_external_ctx(vm, njs_vm_external_ptr(vm)); if (is_handled && ctx->rejected_promises != NULL) { rejected_promise = ctx->rejected_promises->start; length = ctx->rejected_promises->items; promise_obj = njs_value_ptr(promise); for (i = 0; i < length; i++) { if (njs_value_ptr(njs_value_arg(&rejected_promise[i].promise)) == promise_obj) { njs_arr_remove(ctx->rejected_promises, &rejected_promise[i]); break; } } return; } if (ctx->rejected_promises == NULL) { ctx->rejected_promises = njs_arr_create(njs_vm_memory_pool(vm), 4, sizeof(ngx_js_rejected_promise_t)); if (njs_slow_path(ctx->rejected_promises == NULL)) { return; } } rejected_promise = njs_arr_add(ctx->rejected_promises); if (njs_slow_path(rejected_promise == NULL)) { return; } njs_value_assign(&rejected_promise->promise, promise); njs_value_assign(&rejected_promise->message, reason); } static njs_int_t ngx_js_module_path(const ngx_str_t *dir, njs_module_info_t *info) { char *p; size_t length; njs_bool_t trail; char src[NGX_MAX_PATH + 1]; trail = 0; length = info->name.length; if (dir != NULL) { length += dir->len; if (length == 0 || dir->len == 0) { return NJS_DECLINED; } trail = (dir->data[dir->len - 1] != '/'); if (trail) { length++; } } if (njs_slow_path(length > NGX_MAX_PATH)) { return NJS_ERROR; } p = &src[0]; if (dir != NULL) { p = (char *) njs_cpymem(p, dir->data, dir->len); if (trail) { *p++ = '/'; } } p = (char *) njs_cpymem(p, info->name.start, info->name.length); *p = '\0'; p = realpath(&src[0], &info->path[0]); if (p == NULL) { return NJS_DECLINED; } info->fd = open(&info->path[0], O_RDONLY); if (info->fd < 0) { return NJS_DECLINED; } info->file.start = (u_char *) &info->path[0]; info->file.length = njs_strlen(info->file.start); return NJS_OK; } static njs_int_t ngx_js_module_lookup(ngx_js_loc_conf_t *conf, njs_module_info_t *info) { njs_int_t ret; ngx_str_t *path; njs_uint_t i; if (info->name.start[0] == '/') { return ngx_js_module_path(NULL, info); } ret = ngx_js_module_path(&conf->cwd, info); if (ret != NJS_DECLINED) { return ret; } ret = ngx_js_module_path((const ngx_str_t *) &ngx_cycle->conf_prefix, info); if (ret != NJS_DECLINED) { return ret; } if (conf->paths == NGX_CONF_UNSET_PTR) { return NJS_DECLINED; } path = conf->paths->elts; for (i = 0; i < conf->paths->nelts; i++) { ret = ngx_js_module_path(&path[i], info); if (ret != NJS_DECLINED) { return ret; } } return NJS_DECLINED; } static njs_int_t ngx_js_module_read(njs_mp_t *mp, int fd, njs_str_t *text) { ssize_t n; struct stat sb; text->start = NULL; if (fstat(fd, &sb) == -1) { goto fail; } if (!S_ISREG(sb.st_mode)) { goto fail; } text->length = sb.st_size; text->start = njs_mp_alloc(mp, text->length + 1); if (text->start == NULL) { goto fail; } n = read(fd, text->start, sb.st_size); if (n < 0 || n != sb.st_size) { goto fail; } text->start[text->length] = '\0'; return NJS_OK; fail: if (text->start != NULL) { njs_mp_free(mp, text->start); } return NJS_ERROR; } static void ngx_js_file_dirname(const njs_str_t *path, ngx_str_t *name) { const u_char *p, *end; if (path->length == 0) { goto current_dir; } p = path->start + path->length - 1; /* Stripping basename. */ while (p >= path->start && *p != '/') { p--; } end = p + 1; if (end == path->start) { goto current_dir; } /* Stripping trailing slashes. */ while (p >= path->start && *p == '/') { p--; } p++; if (p == path->start) { p = end; } name->data = path->start; name->len = p - path->start; return; current_dir: ngx_str_set(name, "."); } static njs_int_t ngx_js_set_cwd(njs_mp_t *mp, ngx_js_loc_conf_t *conf, njs_str_t *path) { ngx_str_t cwd; ngx_js_file_dirname(path, &cwd); conf->cwd.data = njs_mp_alloc(mp, cwd.len); if (conf->cwd.data == NULL) { return NJS_ERROR; } memcpy(conf->cwd.data, cwd.data, cwd.len); conf->cwd.len = cwd.len; return NJS_OK; } static njs_mod_t * ngx_js_module_loader(njs_vm_t *vm, njs_external_ptr_t external, njs_str_t *name) { u_char *start; njs_int_t ret; njs_str_t text; ngx_str_t prev_cwd; njs_mod_t *module; ngx_js_loc_conf_t *conf; njs_module_info_t info; conf = external; njs_memzero(&info, sizeof(njs_module_info_t)); info.name = *name; errno = 0; ret = ngx_js_module_lookup(conf, &info); if (njs_slow_path(ret != NJS_OK)) { if (errno != 0) { njs_vm_ref_error(vm, "Cannot load module \"%V\" (%s:%s)", name, ngx_js_errno_string(errno), strerror(errno)); } return NULL; } ret = ngx_js_module_read(njs_vm_memory_pool(vm), info.fd, &text); (void) close(info.fd); if (ret != NJS_OK) { njs_vm_internal_error(vm, "while reading \"%V\" module", &info.file); return NULL; } prev_cwd = conf->cwd; ret = ngx_js_set_cwd(njs_vm_memory_pool(vm), conf, &info.file); if (ret != NJS_OK) { njs_vm_internal_error(vm, "while setting cwd for \"%V\" module", &info.file); return NULL; } start = text.start; module = njs_vm_compile_module(vm, &info.file, &start, &text.start[text.length]); njs_mp_free(njs_vm_memory_pool(vm), conf->cwd.data); conf->cwd = prev_cwd; njs_mp_free(njs_vm_memory_pool(vm), text.start); return module; } ngx_int_t ngx_js_init_conf_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf, ngx_engine_opts_t *options) { u_char *start, *p; size_t size; ngx_str_t *m, file; ngx_uint_t i; ngx_pool_cleanup_t *cln; ngx_js_named_path_t *import; if (ngx_set_environment(cf->cycle, NULL) == NULL) { return NGX_ERROR; } size = 0; import = conf->imports->elts; for (i = 0; i < conf->imports->nelts; i++) { /* import from ''; globalThis. = ; */ size += sizeof("import from '';") - 1 + import[i].name.len * 3 + import[i].path.len + sizeof(" globalThis. = ;\n") - 1; } start = ngx_pnalloc(cf->pool, size + 1); if (start == NULL) { return NGX_ERROR; } p = start; import = conf->imports->elts; for (i = 0; i < conf->imports->nelts; i++) { /* import from ''; globalThis. = ; */ p = ngx_cpymem(p, "import ", sizeof("import ") - 1); p = ngx_cpymem(p, import[i].name.data, import[i].name.len); p = ngx_cpymem(p, " from '", sizeof(" from '") - 1); p = ngx_cpymem(p, import[i].path.data, import[i].path.len); p = ngx_cpymem(p, "'; globalThis.", sizeof("'; globalThis.") - 1); p = ngx_cpymem(p, import[i].name.data, import[i].name.len); p = ngx_cpymem(p, " = ", sizeof(" = ") - 1); p = ngx_cpymem(p, import[i].name.data, import[i].name.len); p = ngx_cpymem(p, ";\n", sizeof(";\n") - 1); } *p = '\0'; file = ngx_cycle->conf_prefix; options->file.start = file.data; options->file.length = file.len; options->conf = conf; conf->engine = ngx_create_engine(options); if (conf->engine == NULL) { ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "failed to create js VM"); return NGX_ERROR; } ngx_log_error(NGX_LOG_NOTICE, cf->log, 0, "js vm init %s: %p", conf->engine->name, conf->engine); cln = ngx_pool_cleanup_add(cf->pool, 0); if (cln == NULL) { return NGX_ERROR; } cln->handler = ngx_js_cleanup_vm; cln->data = conf; if (conf->paths != NGX_CONF_UNSET_PTR) { m = conf->paths->elts; for (i = 0; i < conf->paths->nelts; i++) { if (ngx_conf_full_name(cf->cycle, &m[i], 1) != NGX_OK) { return NGX_ERROR; } } } return conf->engine->compile(conf, cf->log, start, size); } static njs_int_t ngx_js_unhandled_rejection(ngx_js_ctx_t *ctx) { njs_vm_t *vm; njs_int_t ret; njs_str_t message; ngx_js_rejected_promise_t *rejected_promise; if (ctx->rejected_promises == NULL || ctx->rejected_promises->items == 0) { return 0; } vm = ctx->engine->u.njs.vm; rejected_promise = ctx->rejected_promises->start; ret = njs_vm_value_to_string(vm, &message, njs_value_arg(&rejected_promise->message)); if (njs_slow_path(ret != NJS_OK)) { return -1; } njs_vm_error(vm, "unhandled promise rejection: %V", &message); njs_arr_destroy(ctx->rejected_promises); ctx->rejected_promises = NULL; return 1; } static void ngx_js_cleanup_vm(void *data) { ngx_js_loc_conf_t *jscf = data; jscf->engine->destroy(jscf->engine, NULL, NULL); } ngx_js_loc_conf_t * ngx_js_create_conf(ngx_conf_t *cf, size_t size) { ngx_js_loc_conf_t *conf; conf = ngx_pcalloc(cf->pool, size); if (conf == NULL) { return NULL; } /* * set by ngx_pcalloc(): * * conf->reuse_queue = NULL; */ conf->paths = NGX_CONF_UNSET_PTR; conf->type = NGX_CONF_UNSET_UINT; conf->imports = NGX_CONF_UNSET_PTR; conf->preload_objects = NGX_CONF_UNSET_PTR; conf->reuse = NGX_CONF_UNSET_SIZE; conf->buffer_size = NGX_CONF_UNSET_SIZE; conf->max_response_body_size = NGX_CONF_UNSET_SIZE; conf->timeout = NGX_CONF_UNSET_MSEC; return conf; } #if defined(NGX_HTTP_SSL) || defined(NGX_STREAM_SSL) static ngx_int_t ngx_js_merge_ssl(ngx_conf_t *cf, ngx_js_loc_conf_t *conf, ngx_js_loc_conf_t *prev) { ngx_uint_t preserve; if (conf->ssl_protocols == 0 && conf->ssl_ciphers.data == NULL && conf->ssl_verify == NGX_CONF_UNSET && conf->ssl_verify_depth == NGX_CONF_UNSET && conf->ssl_trusted_certificate.data == NULL) { if (prev->ssl) { conf->ssl = prev->ssl; return NGX_OK; } preserve = 1; } else { preserve = 0; } conf->ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t)); if (conf->ssl == NULL) { return NGX_ERROR; } conf->ssl->log = cf->log; /* * special handling to preserve conf->ssl * in the "http" section to inherit it to all servers */ if (preserve) { prev->ssl = conf->ssl; } return NGX_OK; } static char * ngx_js_set_ssl(ngx_conf_t *cf, ngx_js_loc_conf_t *conf) { ngx_ssl_t *ssl; ngx_pool_cleanup_t *cln; if (conf->ssl->ctx) { return NGX_OK; } ssl = conf->ssl; if (ngx_ssl_create(ssl, conf->ssl_protocols, NULL) != NGX_OK) { return NGX_CONF_ERROR; } cln = ngx_pool_cleanup_add(cf->pool, 0); if (cln == NULL) { ngx_ssl_cleanup_ctx(ssl); return NGX_CONF_ERROR; } cln->handler = ngx_ssl_cleanup_ctx; cln->data = ssl; if (ngx_ssl_ciphers(NULL, ssl, &conf->ssl_ciphers, 0) != NGX_OK) { return NGX_CONF_ERROR; } if (ngx_ssl_trusted_certificate(cf, ssl, &conf->ssl_trusted_certificate, conf->ssl_verify_depth) != NGX_OK) { return NGX_CONF_ERROR; } return NGX_CONF_OK; } #endif char * ngx_js_merge_conf(ngx_conf_t *cf, void *parent, void *child, ngx_int_t (*init_vm)(ngx_conf_t *cf, ngx_js_loc_conf_t *conf)) { ngx_js_loc_conf_t *prev = parent; ngx_js_loc_conf_t *conf = child; ngx_conf_merge_uint_value(conf->type, prev->type, NGX_ENGINE_NJS); if (prev->type == NGX_CONF_UNSET_UINT) { prev->type = NGX_ENGINE_NJS; } ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000); ngx_conf_merge_size_value(conf->reuse, prev->reuse, 128); ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, 16384); ngx_conf_merge_size_value(conf->max_response_body_size, prev->max_response_body_size, 1048576); if (ngx_js_merge_vm(cf, (ngx_js_loc_conf_t *) conf, (ngx_js_loc_conf_t *) prev, init_vm) != NGX_OK) { return NGX_CONF_ERROR; } #if defined(NGX_HTTP_SSL) || defined(NGX_STREAM_SSL) if (ngx_js_merge_ssl(cf, conf, prev) != NGX_OK) { return NGX_CONF_ERROR; } ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, "DEFAULT"); ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols, (NGX_CONF_BITMASK_SET|NGX_SSL_TLSv1 |NGX_SSL_TLSv1_1|NGX_SSL_TLSv1_2)); ngx_conf_merge_value(conf->ssl_verify, prev->ssl_verify, 1); ngx_conf_merge_value(conf->ssl_verify_depth, prev->ssl_verify_depth, 100); ngx_conf_merge_str_value(conf->ssl_trusted_certificate, prev->ssl_trusted_certificate, ""); return ngx_js_set_ssl(cf, conf); #else return NGX_CONF_OK; #endif } static uint64_t ngx_js_monotonic_time(void) { #if (NGX_HAVE_CLOCK_MONOTONIC) struct timespec ts; #if defined(CLOCK_MONOTONIC_FAST) clock_gettime(CLOCK_MONOTONIC_FAST, &ts); #else clock_gettime(CLOCK_MONOTONIC, &ts); #endif return (uint64_t) ts.tv_sec * 1000000000 + ts.tv_nsec; #else struct timeval tv; gettimeofday(&tv, NULL); return (uint64_t) tv.tv_sec * 1000000000 + tv.tv_usec * 1000; #endif } #define ngx_js_errno_case(e) \ case e: \ return #e; const char* ngx_js_errno_string(int errnum) { switch (errnum) { #ifdef EACCES ngx_js_errno_case(EACCES); #endif #ifdef EADDRINUSE ngx_js_errno_case(EADDRINUSE); #endif #ifdef EADDRNOTAVAIL ngx_js_errno_case(EADDRNOTAVAIL); #endif #ifdef EAFNOSUPPORT ngx_js_errno_case(EAFNOSUPPORT); #endif #ifdef EAGAIN ngx_js_errno_case(EAGAIN); #endif #ifdef EWOULDBLOCK #if EAGAIN != EWOULDBLOCK ngx_js_errno_case(EWOULDBLOCK); #endif #endif #ifdef EALREADY ngx_js_errno_case(EALREADY); #endif #ifdef EBADF ngx_js_errno_case(EBADF); #endif #ifdef EBADMSG ngx_js_errno_case(EBADMSG); #endif #ifdef EBUSY ngx_js_errno_case(EBUSY); #endif #ifdef ECANCELED ngx_js_errno_case(ECANCELED); #endif #ifdef ECHILD ngx_js_errno_case(ECHILD); #endif #ifdef ECONNABORTED ngx_js_errno_case(ECONNABORTED); #endif #ifdef ECONNREFUSED ngx_js_errno_case(ECONNREFUSED); #endif #ifdef ECONNRESET ngx_js_errno_case(ECONNRESET); #endif #ifdef EDEADLK ngx_js_errno_case(EDEADLK); #endif #ifdef EDESTADDRREQ ngx_js_errno_case(EDESTADDRREQ); #endif #ifdef EDOM ngx_js_errno_case(EDOM); #endif #ifdef EDQUOT ngx_js_errno_case(EDQUOT); #endif #ifdef EEXIST ngx_js_errno_case(EEXIST); #endif #ifdef EFAULT ngx_js_errno_case(EFAULT); #endif #ifdef EFBIG ngx_js_errno_case(EFBIG); #endif #ifdef EHOSTUNREACH ngx_js_errno_case(EHOSTUNREACH); #endif #ifdef EIDRM ngx_js_errno_case(EIDRM); #endif #ifdef EILSEQ ngx_js_errno_case(EILSEQ); #endif #ifdef EINPROGRESS ngx_js_errno_case(EINPROGRESS); #endif #ifdef EINTR ngx_js_errno_case(EINTR); #endif #ifdef EINVAL ngx_js_errno_case(EINVAL); #endif #ifdef EIO ngx_js_errno_case(EIO); #endif #ifdef EISCONN ngx_js_errno_case(EISCONN); #endif #ifdef EISDIR ngx_js_errno_case(EISDIR); #endif #ifdef ELOOP ngx_js_errno_case(ELOOP); #endif #ifdef EMFILE ngx_js_errno_case(EMFILE); #endif #ifdef EMLINK ngx_js_errno_case(EMLINK); #endif #ifdef EMSGSIZE ngx_js_errno_case(EMSGSIZE); #endif #ifdef EMULTIHOP ngx_js_errno_case(EMULTIHOP); #endif #ifdef ENAMETOOLONG ngx_js_errno_case(ENAMETOOLONG); #endif #ifdef ENETDOWN ngx_js_errno_case(ENETDOWN); #endif #ifdef ENETRESET ngx_js_errno_case(ENETRESET); #endif #ifdef ENETUNREACH ngx_js_errno_case(ENETUNREACH); #endif #ifdef ENFILE ngx_js_errno_case(ENFILE); #endif #ifdef ENOBUFS ngx_js_errno_case(ENOBUFS); #endif #ifdef ENODATA ngx_js_errno_case(ENODATA); #endif #ifdef ENODEV ngx_js_errno_case(ENODEV); #endif #ifdef ENOENT ngx_js_errno_case(ENOENT); #endif #ifdef ENOEXEC ngx_js_errno_case(ENOEXEC); #endif #ifdef ENOLINK ngx_js_errno_case(ENOLINK); #endif #ifdef ENOLCK #if ENOLINK != ENOLCK ngx_js_errno_case(ENOLCK); #endif #endif #ifdef ENOMEM ngx_js_errno_case(ENOMEM); #endif #ifdef ENOMSG ngx_js_errno_case(ENOMSG); #endif #ifdef ENOPROTOOPT ngx_js_errno_case(ENOPROTOOPT); #endif #ifdef ENOSPC ngx_js_errno_case(ENOSPC); #endif #ifdef ENOSR ngx_js_errno_case(ENOSR); #endif #ifdef ENOSTR ngx_js_errno_case(ENOSTR); #endif #ifdef ENOSYS ngx_js_errno_case(ENOSYS); #endif #ifdef ENOTCONN ngx_js_errno_case(ENOTCONN); #endif #ifdef ENOTDIR ngx_js_errno_case(ENOTDIR); #endif #ifdef ENOTEMPTY #if ENOTEMPTY != EEXIST ngx_js_errno_case(ENOTEMPTY); #endif #endif #ifdef ENOTSOCK ngx_js_errno_case(ENOTSOCK); #endif #ifdef ENOTSUP ngx_js_errno_case(ENOTSUP); #else #ifdef EOPNOTSUPP ngx_js_errno_case(EOPNOTSUPP); #endif #endif #ifdef ENOTTY ngx_js_errno_case(ENOTTY); #endif #ifdef ENXIO ngx_js_errno_case(ENXIO); #endif #ifdef EOVERFLOW ngx_js_errno_case(EOVERFLOW); #endif #ifdef EPERM ngx_js_errno_case(EPERM); #endif #ifdef EPIPE ngx_js_errno_case(EPIPE); #endif #ifdef EPROTO ngx_js_errno_case(EPROTO); #endif #ifdef EPROTONOSUPPORT ngx_js_errno_case(EPROTONOSUPPORT); #endif #ifdef EPROTOTYPE ngx_js_errno_case(EPROTOTYPE); #endif #ifdef ERANGE ngx_js_errno_case(ERANGE); #endif #ifdef EROFS ngx_js_errno_case(EROFS); #endif #ifdef ESPIPE ngx_js_errno_case(ESPIPE); #endif #ifdef ESRCH ngx_js_errno_case(ESRCH); #endif #ifdef ESTALE ngx_js_errno_case(ESTALE); #endif #ifdef ETIME ngx_js_errno_case(ETIME); #endif #ifdef ETIMEDOUT ngx_js_errno_case(ETIMEDOUT); #endif #ifdef ETXTBSY ngx_js_errno_case(ETXTBSY); #endif #ifdef EXDEV ngx_js_errno_case(EXDEV); #endif default: break; } return "UNKNOWN CODE"; } ngx_js_queue_t * ngx_js_queue_create(ngx_pool_t *pool, ngx_uint_t capacity) { ngx_js_queue_t *queue; queue = ngx_pcalloc(pool, sizeof(ngx_js_queue_t)); if (queue == NULL) { return NULL; } queue->data = ngx_pcalloc(pool, sizeof(void *) * capacity); if (queue->data == NULL) { return NULL; } queue->head = 0; queue->tail = 0; queue->size = 0; queue->capacity = capacity; return queue; } ngx_int_t ngx_js_queue_push(ngx_js_queue_t *queue, void *item) { if (queue->size >= queue->capacity) { return NGX_ERROR; } queue->data[queue->tail] = item; queue->tail = (queue->tail + 1) % queue->capacity; queue->size++; return NGX_OK; } void * ngx_js_queue_pop(ngx_js_queue_t *queue) { void *item; if (queue->size == 0) { return NULL; } item = queue->data[queue->head]; queue->head = (queue->head + 1) % queue->capacity; queue->size--; return item; } njs-0.8.9/nginx/ngx_js.h000066400000000000000000000423321474132077100151200ustar00rootroot00000000000000 /* * Copyright (C) Roman Arutyunyan * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #ifndef _NGX_JS_H_INCLUDED_ #define _NGX_JS_H_INCLUDED_ #include #include #include #include #include #include #include "ngx_js_fetch.h" #include "ngx_js_shared_dict.h" #if (NJS_HAVE_QUICKJS) #include #endif #define NGX_ENGINE_NJS 1 #define NGX_ENGINE_QJS 2 #define NGX_JS_UNSET 0 #define NGX_JS_DEPRECATED 1 #define NGX_JS_STRING 2 #define NGX_JS_BUFFER 4 #define NGX_JS_BOOLEAN 8 #define NGX_JS_NUMBER 16 #define NGX_JS_BOOL_FALSE 0 #define NGX_JS_BOOL_TRUE 1 #define NGX_JS_BOOL_UNSET 2 #define NGX_NJS_VAR_NOCACHE 1 #define ngx_js_buffer_type(btype) ((btype) & ~NGX_JS_DEPRECATED) /* * This static table solves the problem of a native QuickJS approach * which uses a static variables of type JSClassID and JS_NewClassID() to * allocate class ids for custom classes. The static variables approach * causes a problem when two modules linked with -Wl,-Bsymbolic-functions flag * are loaded dynamically. */ #define NGX_QJS_CLASS_ID_OFFSET (QJS_CORE_CLASS_ID_LAST) #define NGX_QJS_CLASS_ID_CONSOLE (NGX_QJS_CLASS_ID_OFFSET + 1) #define NGX_QJS_CLASS_ID_HTTP_REQUEST (NGX_QJS_CLASS_ID_OFFSET + 2) #define NGX_QJS_CLASS_ID_HTTP_PERIODIC (NGX_QJS_CLASS_ID_OFFSET + 3) #define NGX_QJS_CLASS_ID_HTTP_VARS (NGX_QJS_CLASS_ID_OFFSET + 4) #define NGX_QJS_CLASS_ID_HTTP_HEADERS_IN (NGX_QJS_CLASS_ID_OFFSET + 5) #define NGX_QJS_CLASS_ID_HTTP_HEADERS_OUT (NGX_QJS_CLASS_ID_OFFSET + 6) #define NGX_QJS_CLASS_ID_STREAM_SESSION (NGX_QJS_CLASS_ID_OFFSET + 7) #define NGX_QJS_CLASS_ID_STREAM_PERIODIC (NGX_QJS_CLASS_ID_OFFSET + 8) #define NGX_QJS_CLASS_ID_STREAM_FLAGS (NGX_QJS_CLASS_ID_OFFSET + 9) #define NGX_QJS_CLASS_ID_STREAM_VARS (NGX_QJS_CLASS_ID_OFFSET + 10) #define NGX_QJS_CLASS_ID_SHARED (NGX_QJS_CLASS_ID_OFFSET + 11) #define NGX_QJS_CLASS_ID_SHARED_DICT (NGX_QJS_CLASS_ID_OFFSET + 12) #define NGX_QJS_CLASS_ID_SHARED_DICT_ERROR (NGX_QJS_CLASS_ID_OFFSET + 13) typedef struct ngx_js_loc_conf_s ngx_js_loc_conf_t; typedef struct ngx_js_event_s ngx_js_event_t; typedef struct ngx_js_dict_s ngx_js_dict_t; typedef struct ngx_js_ctx_s ngx_js_ctx_t; typedef struct ngx_engine_s ngx_engine_t; typedef ngx_pool_t *(*ngx_external_pool_pt)(njs_external_ptr_t e); typedef void (*ngx_js_event_finalize_pt)(njs_external_ptr_t e, ngx_int_t rc); typedef ngx_resolver_t *(*ngx_external_resolver_pt)(njs_external_ptr_t e); typedef ngx_msec_t (*ngx_external_timeout_pt)(njs_external_ptr_t e); typedef ngx_flag_t (*ngx_external_flag_pt)(njs_external_ptr_t e); typedef ngx_flag_t (*ngx_external_size_pt)(njs_external_ptr_t e); typedef ngx_ssl_t *(*ngx_external_ssl_pt)(njs_external_ptr_t e); typedef ngx_js_ctx_t *(*ngx_js_external_ctx_pt)(njs_external_ptr_t e); typedef struct { ngx_str_t name; ngx_str_t path; u_char *file; ngx_uint_t line; } ngx_js_named_path_t; struct ngx_js_event_s { void *ctx; njs_opaque_value_t function; njs_opaque_value_t *args; ngx_socket_t fd; NJS_RBTREE_NODE (node); njs_uint_t nargs; void (*destructor)(ngx_js_event_t *event); ngx_event_t ev; void *data; }; typedef struct { void **data; ngx_uint_t head; ngx_uint_t tail; ngx_uint_t size; ngx_uint_t capacity; } ngx_js_queue_t; #define NGX_JS_COMMON_MAIN_CONF \ ngx_js_dict_t *dicts; \ ngx_array_t *periodics \ #define _NGX_JS_COMMON_LOC_CONF \ ngx_uint_t type; \ ngx_engine_t *engine; \ ngx_uint_t reuse; \ ngx_js_queue_t *reuse_queue; \ ngx_str_t cwd; \ ngx_array_t *imports; \ ngx_array_t *paths; \ \ ngx_array_t *preload_objects; \ \ size_t buffer_size; \ size_t max_response_body_size; \ ngx_msec_t timeout #if defined(NGX_HTTP_SSL) || defined(NGX_STREAM_SSL) #define NGX_JS_COMMON_LOC_CONF \ _NGX_JS_COMMON_LOC_CONF; \ \ ngx_ssl_t *ssl; \ ngx_str_t ssl_ciphers; \ ngx_uint_t ssl_protocols; \ ngx_flag_t ssl_verify; \ ngx_int_t ssl_verify_depth; \ ngx_str_t ssl_trusted_certificate #else #define NGX_JS_COMMON_LOC_CONF _NGX_JS_COMMON_LOC_CONF #endif #define NGX_JS_COMMON_CTX \ ngx_engine_t *engine; \ ngx_log_t *log; \ njs_opaque_value_t args[3]; \ njs_opaque_value_t retval; \ njs_arr_t *rejected_promises; \ njs_rbtree_t waiting_events; \ ngx_socket_t event_id #define ngx_js_add_event(ctx, event) \ njs_rbtree_insert(&(ctx)->waiting_events, &(event)->node) #define ngx_js_del_event(ctx, event) \ do { \ if ((event)->destructor) { \ (event)->destructor(event); \ } \ \ njs_rbtree_delete(&(ctx)->waiting_events, &(event)->node); \ } while (0) typedef struct { NGX_JS_COMMON_MAIN_CONF; } ngx_js_main_conf_t; struct ngx_js_loc_conf_s { NGX_JS_COMMON_LOC_CONF; }; typedef struct { ngx_str_t fname; unsigned flags; } ngx_js_set_t; struct ngx_js_ctx_s { NGX_JS_COMMON_CTX; }; typedef struct { void *external; } ngx_js_opaque_t; typedef struct ngx_engine_opts_s { unsigned engine; union { struct { njs_vm_meta_t *metas; njs_module_t **addons; } njs; #if (NJS_HAVE_QUICKJS) struct { uintptr_t *metas; qjs_module_t **addons; } qjs; #endif } u; njs_str_t file; ngx_js_loc_conf_t *conf; ngx_engine_t *(*clone)(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *cf, njs_int_t pr_id, void *external); void (*destroy)(ngx_engine_t *e, ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *conf); } ngx_engine_opts_t; typedef struct { u_char *code; size_t code_size; } ngx_js_code_entry_t; struct ngx_engine_s { union { struct { njs_vm_t *vm; } njs; #if (NJS_HAVE_QUICKJS) struct { JSContext *ctx; } qjs; #endif } u; ngx_int_t (*compile)(ngx_js_loc_conf_t *conf, ngx_log_t *lg, u_char *start, size_t size); ngx_int_t (*call)(ngx_js_ctx_t *ctx, ngx_str_t *fname, njs_opaque_value_t *args, njs_uint_t nargs); ngx_engine_t *(*clone)(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *cf, njs_int_t pr_id, void *external); void *(*external)(ngx_engine_t *e); ngx_int_t (*pending)(ngx_engine_t *e); ngx_int_t (*string)(ngx_engine_t *e, njs_opaque_value_t *value, ngx_str_t *str); void (*destroy)(ngx_engine_t *e, ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *conf); unsigned type; const char *name; njs_mp_t *pool; njs_arr_t *precompiled; }; #define ngx_external_connection(vm, e) \ (*((ngx_connection_t **) ((u_char *) (e) + njs_vm_meta(vm, 0)))) #define ngx_external_pool(vm, e) \ ((ngx_external_pool_pt) njs_vm_meta(vm, 1))(e) #define ngx_external_resolver(vm, e) \ ((ngx_external_resolver_pt) njs_vm_meta(vm, 2))(e) #define ngx_external_resolver_timeout(vm, e) \ ((ngx_external_timeout_pt) njs_vm_meta(vm, 3))(e) #define ngx_external_event_finalize(vm) \ ((ngx_js_event_finalize_pt) njs_vm_meta(vm, 4)) #define ngx_external_ssl(vm, e) \ ((ngx_external_ssl_pt) njs_vm_meta(vm, 5))(e) #define ngx_external_ssl_verify(vm, e) \ ((ngx_external_flag_pt) njs_vm_meta(vm, 6))(e) #define ngx_external_fetch_timeout(vm, e) \ ((ngx_external_timeout_pt) njs_vm_meta(vm, 7))(e) #define ngx_external_buffer_size(vm, e) \ ((ngx_external_size_pt) njs_vm_meta(vm, 8))(e) #define ngx_external_max_response_buffer_size(vm, e) \ ((ngx_external_size_pt) njs_vm_meta(vm, 9))(e) #define NGX_JS_MAIN_CONF_INDEX 10 #define ngx_main_conf(vm) \ ((ngx_js_main_conf_t *) njs_vm_meta(vm, NGX_JS_MAIN_CONF_INDEX)) #define ngx_external_ctx(vm, e) \ ((ngx_js_external_ctx_pt) njs_vm_meta(vm, 11))(e) #define ngx_js_prop(vm, type, value, start, len) \ ((type == NGX_JS_STRING) ? njs_vm_value_string_create(vm, value, start, len) \ : njs_vm_value_buffer_set(vm, value, start, len)) void ngx_js_ctx_init(ngx_js_ctx_t *ctx, ngx_log_t *log); #define ngx_js_ctx_pending(ctx) \ ((ctx)->engine->pending(ctx->engine) \ || !njs_rbtree_is_empty(&(ctx)->waiting_events)) #define ngx_js_ctx_external(ctx) \ ((ctx)->engine->external(ctx->engine)) void ngx_js_ctx_destroy(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *conf); ngx_int_t ngx_js_call(njs_vm_t *vm, njs_function_t *func, njs_opaque_value_t *args, njs_uint_t nargs); ngx_int_t ngx_js_exception(njs_vm_t *vm, ngx_str_t *s); ngx_engine_t *ngx_njs_clone(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *cf, void *external); #if (NJS_HAVE_QUICKJS) typedef struct ngx_qjs_event_s ngx_qjs_event_t; typedef union { njs_opaque_value_t opaque; JSValue value; } ngx_qjs_value_t; struct ngx_qjs_event_s { void *ctx; JSValue function; JSValue *args; ngx_socket_t fd; NJS_RBTREE_NODE (node); njs_uint_t nargs; void (*destructor)(ngx_qjs_event_t *event); ngx_event_t ev; void *data; }; #define ngx_qjs_arg(val) (((ngx_qjs_value_t *) &(val))->value) ngx_engine_t *ngx_qjs_clone(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *cf, void *external); void ngx_engine_qjs_destroy(ngx_engine_t *e, ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *conf); ngx_int_t ngx_qjs_call(ngx_js_ctx_t *ctx, JSValue function, JSValue *argv, int argc); ngx_int_t ngx_qjs_exception(ngx_engine_t *e, ngx_str_t *s); ngx_int_t ngx_qjs_integer(JSContext *cx, JSValueConst val, ngx_int_t *n); ngx_int_t ngx_qjs_string(ngx_engine_t *e, JSValueConst val, ngx_str_t *str); #define ngx_qjs_prop(cx, type, start, len) \ ((type == NGX_JS_STRING) ? qjs_string_create(cx, start, len) \ : qjs_buffer_create(cx, (u_char *) start, len)) #define ngx_qjs_meta(cx, i) \ ((uintptr_t *) JS_GetRuntimeOpaque(JS_GetRuntime(cx)))[i] #define ngx_qjs_external_connection(cx, e) \ (*((ngx_connection_t **) ((u_char *) (e) + ngx_qjs_meta(cx, 0)))) #define ngx_qjs_external_pool(cx, e) \ ((ngx_external_pool_pt) ngx_qjs_meta(cx, 1))(e) #define ngx_qjs_external_resolver(cx, e) \ ((ngx_external_resolver_pt) ngx_qjs_meta(vm, 2))(e) #define ngx_qjs_external_resolver_timeout(cx, e) \ ((ngx_external_timeout_pt) ngx_qjs_meta(cx, 3))(e) #define ngx_qjs_external_event_finalize(cx) \ ((ngx_js_event_finalize_pt) ngx_qjs_meta(cx, 4)) #define ngx_qjs_external_ssl(cx, e) \ ((ngx_external_ssl_pt) ngx_qjs_meta(cx, 5))(e) #define ngx_qjs_external_ssl_verify(cx, e) \ ((ngx_external_flag_pt) ngx_qjs_meta(cx, 6))(e) #define ngx_qjs_external_fetch_timeout(cx, e) \ ((ngx_external_timeout_pt) ngx_qjs_meta(cx, 7))(e) #define ngx_qjs_external_buffer_size(cx, e) \ ((ngx_external_size_pt) ngx_qjs_meta(cx, 8))(e) #define ngx_qjs_external_max_response_buffer_size(cx, e) \ ((ngx_external_size_pt) ngx_qjs_meta(cx, 9))(e) #define ngx_qjs_main_conf(cx) \ ((ngx_js_main_conf_t *) ngx_qjs_meta(cx, NGX_JS_MAIN_CONF_INDEX)) #define ngx_qjs_external_ctx(cx, e) \ ((ngx_js_external_ctx_pt) ngx_qjs_meta(cx, 11))(e) extern qjs_module_t qjs_zlib_module; extern qjs_module_t ngx_qjs_ngx_module; extern qjs_module_t ngx_qjs_ngx_shared_dict_module; #endif njs_int_t ngx_js_ext_log(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t level, njs_value_t *retval); void ngx_js_log(njs_vm_t *vm, njs_external_ptr_t external, ngx_uint_t level, const char *fmt, ...); void ngx_js_logger(ngx_connection_t *c, ngx_uint_t level, const u_char *start, size_t length); char * ngx_js_import(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); char * ngx_js_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); char * ngx_js_preload_object(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); ngx_int_t ngx_js_merge_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf, ngx_js_loc_conf_t *prev, ngx_int_t (*init_vm)(ngx_conf_t *cf, ngx_js_loc_conf_t *conf)); ngx_int_t ngx_js_init_conf_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf, ngx_engine_opts_t *opt); ngx_js_loc_conf_t *ngx_js_create_conf(ngx_conf_t *cf, size_t size); char * ngx_js_merge_conf(ngx_conf_t *cf, void *parent, void *child, ngx_int_t (*init_vm)(ngx_conf_t *cf, ngx_js_loc_conf_t *conf)); char *ngx_js_shared_dict_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf, void *tag); njs_int_t ngx_js_ext_string(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); njs_int_t ngx_js_ext_uint(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); njs_int_t ngx_js_ext_constant(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); njs_int_t ngx_js_ext_flags(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); ngx_int_t ngx_js_string(njs_vm_t *vm, njs_value_t *value, njs_str_t *str); ngx_int_t ngx_js_integer(njs_vm_t *vm, njs_value_t *value, ngx_int_t *n); const char *ngx_js_errno_string(int errnum); ngx_js_queue_t *ngx_js_queue_create(ngx_pool_t *pool, ngx_uint_t capacity); ngx_int_t ngx_js_queue_push(ngx_js_queue_t *queue, void *item); void *ngx_js_queue_pop(ngx_js_queue_t *queue); extern njs_module_t ngx_js_ngx_module; extern njs_module_t njs_webcrypto_module; extern njs_module_t njs_xml_module; extern njs_module_t njs_zlib_module; #endif /* _NGX_JS_H_INCLUDED_ */ njs-0.8.9/nginx/ngx_js_fetch.c000066400000000000000000003206301474132077100162640ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) hongzhidao * Copyright (C) Antoine Bonavita * Copyright (C) NGINX, Inc. */ #include #include #include #include #include "ngx_js.h" typedef struct ngx_js_http_s ngx_js_http_t; typedef struct { njs_str_t name; njs_int_t value; } ngx_js_entry_t; typedef struct { ngx_uint_t state; ngx_uint_t code; u_char *status_text; u_char *status_text_end; ngx_uint_t count; ngx_flag_t chunked; off_t content_length_n; u_char *header_name_start; u_char *header_name_end; u_char *header_start; u_char *header_end; } ngx_js_http_parse_t; typedef struct { u_char *pos; uint64_t chunk_size; uint8_t state; uint8_t last; } ngx_js_http_chunk_parse_t; typedef struct ngx_js_tb_elt_s ngx_js_tb_elt_t; struct ngx_js_tb_elt_s { ngx_uint_t hash; ngx_str_t key; ngx_str_t value; ngx_js_tb_elt_t *next; }; typedef struct { enum { GUARD_NONE = 0, GUARD_REQUEST, GUARD_IMMUTABLE, GUARD_RESPONSE, } guard; ngx_list_t header_list; ngx_js_tb_elt_t *content_type; } ngx_js_headers_t; typedef struct { enum { CACHE_MODE_DEFAULT = 0, CACHE_MODE_NO_STORE, CACHE_MODE_RELOAD, CACHE_MODE_NO_CACHE, CACHE_MODE_FORCE_CACHE, CACHE_MODE_ONLY_IF_CACHED, } cache_mode; enum { CREDENTIALS_SAME_ORIGIN = 0, CREDENTIALS_INCLUDE, CREDENTIALS_OMIT, } credentials; enum { MODE_NO_CORS = 0, MODE_SAME_ORIGIN, MODE_CORS, MODE_NAVIGATE, MODE_WEBSOCKET, } mode; njs_str_t url; njs_str_t method; u_char m[8]; uint8_t body_used; njs_str_t body; ngx_js_headers_t headers; njs_opaque_value_t header_value; } ngx_js_request_t; typedef struct { njs_str_t url; ngx_int_t code; njs_str_t status_text; uint8_t body_used; njs_chb_t chain; ngx_js_headers_t headers; njs_opaque_value_t header_value; } ngx_js_response_t; struct ngx_js_http_s { ngx_log_t *log; ngx_pool_t *pool; njs_vm_t *vm; ngx_js_event_t *event; ngx_resolver_ctx_t *ctx; ngx_addr_t addr; ngx_addr_t *addrs; ngx_uint_t naddrs; ngx_uint_t naddr; in_port_t port; ngx_peer_connection_t peer; ngx_msec_t timeout; ngx_int_t buffer_size; ngx_int_t max_response_body_size; unsigned header_only; #if (NGX_SSL) ngx_str_t tls_name; ngx_ssl_t *ssl; njs_bool_t ssl_verify; #endif ngx_buf_t *buffer; ngx_buf_t *chunk; njs_chb_t chain; ngx_js_response_t response; njs_opaque_value_t response_value; njs_opaque_value_t promise; njs_opaque_value_t promise_callbacks[2]; uint8_t done; ngx_js_http_parse_t http_parse; ngx_js_http_chunk_parse_t http_chunk_parse; ngx_int_t (*process)(ngx_js_http_t *http); }; #define ngx_js_http_error(http, err, fmt, ...) \ do { \ njs_vm_error((http)->vm, fmt, ##__VA_ARGS__); \ njs_vm_exception_get((http)->vm, \ njs_value_arg(&(http)->response_value)); \ ngx_js_http_fetch_done(http, &(http)->response_value, NJS_ERROR); \ } while (0) static njs_int_t ngx_js_method_process(njs_vm_t *vm, ngx_js_request_t *r); static njs_int_t ngx_js_headers_inherit(njs_vm_t *vm, ngx_js_headers_t *headers, ngx_js_headers_t *orig); static njs_int_t ngx_js_headers_fill(njs_vm_t *vm, ngx_js_headers_t *headers, njs_value_t *init); static ngx_js_http_t *ngx_js_http_alloc(njs_vm_t *vm, ngx_pool_t *pool, ngx_log_t *log); static void njs_js_http_destructor(ngx_js_event_t *event); static void ngx_js_resolve_handler(ngx_resolver_ctx_t *ctx); static njs_int_t ngx_js_fetch_promissified_result(njs_vm_t *vm, njs_value_t *result, njs_int_t rc, njs_value_t *retval); static void ngx_js_http_fetch_done(ngx_js_http_t *http, njs_opaque_value_t *retval, njs_int_t rc); static njs_int_t ngx_js_http_promise_trampoline(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static void ngx_js_http_connect(ngx_js_http_t *http); static void ngx_js_http_next(ngx_js_http_t *http); static void ngx_js_http_write_handler(ngx_event_t *wev); static void ngx_js_http_read_handler(ngx_event_t *rev); static njs_int_t ngx_js_request_constructor(njs_vm_t *vm, ngx_js_request_t *request, ngx_url_t *u, njs_external_ptr_t external, njs_value_t *args, njs_uint_t nargs); static njs_int_t ngx_js_headers_append(njs_vm_t *vm, ngx_js_headers_t *headers, u_char *name, size_t len, u_char *value, size_t vlen); static ngx_int_t ngx_js_http_process_status_line(ngx_js_http_t *http); static ngx_int_t ngx_js_http_process_headers(ngx_js_http_t *http); static ngx_int_t ngx_js_http_process_body(ngx_js_http_t *http); static ngx_int_t ngx_js_http_parse_status_line(ngx_js_http_parse_t *hp, ngx_buf_t *b); static ngx_int_t ngx_js_http_parse_header_line(ngx_js_http_parse_t *hp, ngx_buf_t *b); static ngx_int_t ngx_js_http_parse_chunked(ngx_js_http_chunk_parse_t *hcp, ngx_buf_t *b, njs_chb_t *chain); static void ngx_js_http_dummy_handler(ngx_event_t *ev); static njs_int_t ngx_headers_js_ext_append(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t ngx_headers_js_ext_delete(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t ngx_headers_js_ext_for_each(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t as_array, njs_value_t *retval); static njs_int_t ngx_headers_js_ext_get(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t as_array, njs_value_t *retval); static njs_int_t ngx_headers_js_ext_has(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t ngx_headers_js_ext_prop(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_headers_js_ext_keys(njs_vm_t *vm, njs_value_t *value, njs_value_t *keys); static njs_int_t ngx_headers_js_ext_set(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t ngx_request_js_ext_body(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t ngx_request_js_ext_body_used(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_request_js_ext_cache(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_request_js_ext_credentials(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_request_js_ext_headers(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_request_js_ext_mode(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_response_js_ext_status(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_response_js_ext_status_text(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_response_js_ext_ok(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_response_js_ext_body_used(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_response_js_ext_headers(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_response_js_ext_type(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_response_js_ext_body(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); #if (NGX_SSL) static void ngx_js_http_ssl_init_connection(ngx_js_http_t *http); static void ngx_js_http_ssl_handshake_handler(ngx_connection_t *c); static void ngx_js_http_ssl_handshake(ngx_js_http_t *http); static njs_int_t ngx_js_http_ssl_name(ngx_js_http_t *http); #endif static void ngx_js_http_trim(u_char **value, size_t *len, njs_bool_t trim_c0_control_or_space); static njs_int_t ngx_fetch_flag(njs_vm_t *vm, const ngx_js_entry_t *entries, njs_int_t value, njs_value_t *retval); static njs_int_t ngx_fetch_flag_set(njs_vm_t *vm, const ngx_js_entry_t *entries, njs_value_t *value, const char *type); static njs_int_t ngx_js_fetch_init(njs_vm_t *vm); static const ngx_js_entry_t ngx_js_fetch_credentials[] = { { njs_str("same-origin"), CREDENTIALS_SAME_ORIGIN }, { njs_str("omit"), CREDENTIALS_OMIT }, { njs_str("include"), CREDENTIALS_INCLUDE }, { njs_null_str, 0 }, }; static const ngx_js_entry_t ngx_js_fetch_cache_modes[] = { { njs_str("default"), CACHE_MODE_DEFAULT }, { njs_str("no-store"), CACHE_MODE_NO_STORE }, { njs_str("reload"), CACHE_MODE_RELOAD }, { njs_str("no-cache"), CACHE_MODE_NO_CACHE }, { njs_str("force-cache"), CACHE_MODE_FORCE_CACHE }, { njs_str("only-if-cached"), CACHE_MODE_ONLY_IF_CACHED }, { njs_null_str, 0 }, }; static const ngx_js_entry_t ngx_js_fetch_modes[] = { { njs_str("no-cors"), MODE_NO_CORS }, { njs_str("cors"), MODE_CORS }, { njs_str("same-origin"), MODE_SAME_ORIGIN }, { njs_str("navigate"), MODE_NAVIGATE }, { njs_str("websocket"), MODE_WEBSOCKET }, { njs_null_str, 0 }, }; static njs_external_t ngx_js_ext_http_headers[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "Headers", } }, { .flags = NJS_EXTERN_SELF, .u.object = { .enumerable = 1, .prop_handler = ngx_headers_js_ext_prop, .keys = ngx_headers_js_ext_keys, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("append"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_headers_js_ext_append, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("delete"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_headers_js_ext_delete, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("forEach"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_headers_js_ext_for_each, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("get"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_headers_js_ext_get, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("getAll"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_headers_js_ext_get, .magic8 = 1 } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("has"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_headers_js_ext_has, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("set"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_headers_js_ext_set, } }, }; static njs_external_t ngx_js_ext_http_request[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "Request", } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("arrayBuffer"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_request_js_ext_body, #define NGX_JS_BODY_ARRAY_BUFFER 0 #define NGX_JS_BODY_JSON 1 #define NGX_JS_BODY_TEXT 2 .magic8 = NGX_JS_BODY_ARRAY_BUFFER } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("bodyUsed"), .enumerable = 1, .u.property = { .handler = ngx_request_js_ext_body_used, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("cache"), .enumerable = 1, .u.property = { .handler = ngx_request_js_ext_cache, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("credentials"), .enumerable = 1, .u.property = { .handler = ngx_request_js_ext_credentials, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("json"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_request_js_ext_body, .magic8 = NGX_JS_BODY_JSON } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("headers"), .enumerable = 1, .u.property = { .handler = ngx_request_js_ext_headers, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("method"), .enumerable = 1, .u.property = { .handler = ngx_js_ext_string, .magic32 = offsetof(ngx_js_request_t, method), } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("mode"), .enumerable = 1, .u.property = { .handler = ngx_request_js_ext_mode, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("text"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_request_js_ext_body, .magic8 = NGX_JS_BODY_TEXT } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("url"), .enumerable = 1, .u.property = { .handler = ngx_js_ext_string, .magic32 = offsetof(ngx_js_request_t, url), } }, }; static njs_external_t ngx_js_ext_http_response[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "Response", } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("arrayBuffer"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_response_js_ext_body, .magic8 = NGX_JS_BODY_ARRAY_BUFFER } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("bodyUsed"), .enumerable = 1, .u.property = { .handler = ngx_response_js_ext_body_used, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("headers"), .enumerable = 1, .u.property = { .handler = ngx_response_js_ext_headers, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("json"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_response_js_ext_body, .magic8 = NGX_JS_BODY_JSON } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("ok"), .enumerable = 1, .u.property = { .handler = ngx_response_js_ext_ok, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("redirected"), .enumerable = 1, .u.property = { .handler = ngx_js_ext_constant, .magic32 = 0, .magic16 = NGX_JS_BOOLEAN, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("status"), .enumerable = 1, .u.property = { .handler = ngx_response_js_ext_status, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("statusText"), .enumerable = 1, .u.property = { .handler = ngx_response_js_ext_status_text, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("text"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_response_js_ext_body, .magic8 = NGX_JS_BODY_TEXT } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("type"), .enumerable = 1, .u.property = { .handler = ngx_response_js_ext_type, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("url"), .enumerable = 1, .u.property = { .handler = ngx_js_ext_string, .magic32 = offsetof(ngx_js_response_t, url), } }, }; static njs_int_t ngx_http_js_fetch_request_proto_id; static njs_int_t ngx_http_js_fetch_response_proto_id; static njs_int_t ngx_http_js_fetch_headers_proto_id; njs_module_t ngx_js_fetch_module = { .name = njs_str("fetch"), .preinit = NULL, .init = ngx_js_fetch_init, }; njs_int_t ngx_js_ext_fetch(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; ngx_url_t u; ngx_uint_t i; njs_bool_t has_host; ngx_pool_t *pool; njs_value_t *init, *value; ngx_js_http_t *http; ngx_list_part_t *part; ngx_js_tb_elt_t *h; ngx_js_request_t request; ngx_connection_t *c; ngx_resolver_ctx_t *ctx; njs_external_ptr_t external; njs_opaque_value_t lvalue; static const njs_str_t buffer_size_key = njs_str("buffer_size"); static const njs_str_t body_size_key = njs_str("max_response_body_size"); #if (NGX_SSL) static const njs_str_t verify_key = njs_str("verify"); #endif external = njs_vm_external_ptr(vm); c = ngx_external_connection(vm, external); pool = ngx_external_pool(vm, external); http = ngx_js_http_alloc(vm, pool, c->log); if (http == NULL) { return NJS_ERROR; } ret = ngx_js_request_constructor(vm, &request, &u, external, args, nargs); if (ret != NJS_OK) { goto fail; } http->response.url = request.url; http->timeout = ngx_external_fetch_timeout(vm, external); http->buffer_size = ngx_external_buffer_size(vm, external); http->max_response_body_size = ngx_external_max_response_buffer_size(vm, external); #if (NGX_SSL) if (u.default_port == 443) { http->ssl = ngx_external_ssl(vm, external); http->ssl_verify = ngx_external_ssl_verify(vm, external); } #endif init = njs_arg(args, nargs, 2); if (njs_value_is_object(init)) { value = njs_vm_object_prop(vm, init, &buffer_size_key, &lvalue); if (value != NULL && ngx_js_integer(vm, value, &http->buffer_size) != NGX_OK) { goto fail; } value = njs_vm_object_prop(vm, init, &body_size_key, &lvalue); if (value != NULL && ngx_js_integer(vm, value, &http->max_response_body_size) != NGX_OK) { goto fail; } #if (NGX_SSL) value = njs_vm_object_prop(vm, init, &verify_key, &lvalue); if (value != NULL) { http->ssl_verify = njs_value_bool(value); } #endif } http->header_only = njs_strstr_eq(&request.method, &njs_str_value("HEAD")); NJS_CHB_MP_INIT(&http->chain, vm); njs_chb_append(&http->chain, request.method.start, request.method.length); njs_chb_append_literal(&http->chain, " "); if (u.uri.len == 0 || u.uri.data[0] != '/') { njs_chb_append_literal(&http->chain, "/"); } njs_chb_append(&http->chain, u.uri.data, u.uri.len); njs_chb_append_literal(&http->chain, " HTTP/1.1" CRLF); has_host = 0; part = &request.headers.header_list.part; h = part->elts; for (i = 0; /* void */; i++) { if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; h = part->elts; i = 0; } if (h[i].hash == 0) { continue; } if (h[i].key.len == 4 && ngx_strncasecmp(h[i].key.data, (u_char *) "Host", 4) == 0) { has_host = 1; njs_chb_append_literal(&http->chain, "Host: "); njs_chb_append(&http->chain, h[i].value.data, h[i].value.len); njs_chb_append_literal(&http->chain, CRLF); break; } } if (!has_host) { njs_chb_append_literal(&http->chain, "Host: "); njs_chb_append(&http->chain, u.host.data, u.host.len); if (!u.no_port) { njs_chb_sprintf(&http->chain, 32, ":%d", u.port); } njs_chb_append_literal(&http->chain, CRLF); } part = &request.headers.header_list.part; h = part->elts; for (i = 0; /* void */; i++) { if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; h = part->elts; i = 0; } if (h[i].hash == 0) { continue; } if (h[i].key.len == 4 && ngx_strncasecmp(h[i].key.data, (u_char *) "Host", 4) == 0) { continue; } njs_chb_append(&http->chain, h[i].key.data, h[i].key.len); njs_chb_append_literal(&http->chain, ": "); njs_chb_append(&http->chain, h[i].value.data, h[i].value.len); njs_chb_append_literal(&http->chain, CRLF); } njs_chb_append_literal(&http->chain, "Connection: close" CRLF); #if (NGX_SSL) http->tls_name.data = u.host.data; http->tls_name.len = u.host.len; #endif if (request.body.length != 0) { njs_chb_sprintf(&http->chain, 32, "Content-Length: %uz" CRLF CRLF, request.body.length); njs_chb_append(&http->chain, request.body.start, request.body.length); } else { njs_chb_append_literal(&http->chain, CRLF); } if (u.addrs == NULL) { ctx = ngx_resolve_start(ngx_external_resolver(vm, external), NULL); if (ctx == NULL) { njs_vm_memory_error(vm); return NJS_ERROR; } if (ctx == NGX_NO_RESOLVER) { njs_vm_error(vm, "no resolver defined"); goto fail; } http->ctx = ctx; http->port = u.port; ctx->name = u.host; ctx->handler = ngx_js_resolve_handler; ctx->data = http; ctx->timeout = ngx_external_resolver_timeout(vm, external); ret = ngx_resolve_name(http->ctx); if (ret != NGX_OK) { http->ctx = NULL; njs_vm_memory_error(vm); return NJS_ERROR; } njs_value_assign(retval, njs_value_arg(&http->promise)); return NJS_OK; } http->naddrs = 1; ngx_memcpy(&http->addr, &u.addrs[0], sizeof(ngx_addr_t)); http->addrs = &http->addr; ngx_js_http_connect(http); njs_value_assign(retval, njs_value_arg(&http->promise)); return NJS_OK; fail: njs_vm_exception_get(vm, njs_value_arg(&lvalue)); ngx_js_http_fetch_done(http, &lvalue, NJS_ERROR); njs_value_assign(retval, njs_value_arg(&http->promise)); return NJS_OK; } static njs_int_t ngx_js_ext_headers_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { ngx_int_t rc; njs_int_t ret; njs_value_t *init; ngx_pool_t *pool; ngx_js_headers_t *headers; pool = ngx_external_pool(vm, njs_vm_external_ptr(vm)); headers = ngx_palloc(pool, sizeof(ngx_js_headers_t)); if (headers == NULL) { njs_vm_memory_error(vm); return NJS_ERROR; } headers->guard = GUARD_NONE; rc = ngx_list_init(&headers->header_list, pool, 4, sizeof(ngx_js_tb_elt_t)); if (rc != NGX_OK) { njs_vm_memory_error(vm); return NJS_ERROR; } init = njs_arg(args, nargs, 1); if (njs_value_is_object(init)) { ret = ngx_js_headers_fill(vm, headers, init); if (ret != NJS_OK) { return NJS_ERROR; } } return njs_vm_external_create(vm, retval, ngx_http_js_fetch_headers_proto_id, headers, 0); } static njs_int_t ngx_js_ext_request_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; ngx_url_t u; ngx_js_request_t *request; request = njs_mp_alloc(njs_vm_memory_pool(vm), sizeof(ngx_js_request_t)); if (request == NULL) { njs_vm_memory_error(vm); return NJS_ERROR; } ret = ngx_js_request_constructor(vm, request, &u, njs_vm_external_ptr(vm), args, nargs); if (ret != NJS_OK) { return NJS_ERROR; } return njs_vm_external_create(vm, retval, ngx_http_js_fetch_request_proto_id, request, 0); } static njs_int_t ngx_js_ext_response_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { u_char *p, *end; ngx_int_t rc; njs_int_t ret; njs_str_t bd; ngx_pool_t *pool; njs_value_t *body, *init, *value; ngx_js_response_t *response; njs_opaque_value_t lvalue; static const njs_str_t headers = njs_str("headers"); static const njs_str_t status = njs_str("status"); static const njs_str_t status_text = njs_str("statusText"); response = njs_mp_zalloc(njs_vm_memory_pool(vm), sizeof(ngx_js_response_t)); if (response == NULL) { njs_vm_memory_error(vm); return NJS_ERROR; } /* * set by njs_mp_zalloc(): * * request->url.length = 0; * request->status_text.length = 0; */ response->code = 200; response->headers.guard = GUARD_RESPONSE; pool = ngx_external_pool(vm, njs_vm_external_ptr(vm)); rc = ngx_list_init(&response->headers.header_list, pool, 4, sizeof(ngx_js_tb_elt_t)); if (rc != NGX_OK) { njs_vm_memory_error(vm); return NJS_ERROR; } init = njs_arg(args, nargs, 2); if (njs_value_is_object(init)) { value = njs_vm_object_prop(vm, init, &status, &lvalue); if (value != NULL) { if (ngx_js_integer(vm, value, &response->code) != NGX_OK) { njs_vm_error(vm, "invalid Response status"); return NJS_ERROR; } if (response->code < 200 || response->code > 599) { njs_vm_error(vm, "status provided (%i) is outside of " "[200, 599] range", response->code); return NJS_ERROR; } } value = njs_vm_object_prop(vm, init, &status_text, &lvalue); if (value != NULL) { if (ngx_js_string(vm, value, &response->status_text) != NGX_OK) { njs_vm_error(vm, "invalid Response statusText"); return NJS_ERROR; } p = response->status_text.start; end = p + response->status_text.length; while (p < end) { if (*p != '\t' && *p < ' ') { njs_vm_error(vm, "invalid Response statusText"); return NJS_ERROR; } p++; } } value = njs_vm_object_prop(vm, init, &headers, &lvalue); if (value != NULL) { if (!njs_value_is_object(value)) { njs_vm_error(vm, "Headers is not an object"); return NJS_ERROR; } ret = ngx_js_headers_fill(vm, &response->headers, value); if (ret != NJS_OK) { return NJS_ERROR; } } } NJS_CHB_MP_INIT(&response->chain, vm); body = njs_arg(args, nargs, 1); if (!njs_value_is_null_or_undefined(body)) { if (ngx_js_string(vm, body, &bd) != NGX_OK) { njs_vm_error(vm, "invalid Response body"); return NJS_ERROR; } njs_chb_append(&response->chain, bd.start, bd.length); if (njs_value_is_string(body)) { ret = ngx_js_headers_append(vm, &response->headers, (u_char *) "Content-Type", njs_length("Content-Type"), (u_char *) "text/plain;charset=UTF-8", njs_length("text/plain;charset=UTF-8")); if (ret != NJS_OK) { return NJS_ERROR; } } } return njs_vm_external_create(vm, retval, ngx_http_js_fetch_response_proto_id, response, 0); } static njs_int_t ngx_js_method_process(njs_vm_t *vm, ngx_js_request_t *request) { u_char *s, *p; const njs_str_t *m; static const njs_str_t forbidden[] = { njs_str("CONNECT"), njs_str("TRACE"), njs_str("TRACK"), njs_null_str, }; static const njs_str_t to_normalize[] = { njs_str("DELETE"), njs_str("GET"), njs_str("HEAD"), njs_str("OPTIONS"), njs_str("POST"), njs_str("PUT"), njs_null_str, }; for (m = &forbidden[0]; m->length != 0; m++) { if (njs_strstr_case_eq(&request->method, m)) { njs_vm_error(vm, "forbidden method: %V", m); return NJS_ERROR; } } for (m = &to_normalize[0]; m->length != 0; m++) { if (njs_strstr_case_eq(&request->method, m)) { s = &request->m[0]; p = m->start; while (*p != '\0') { *s++ = njs_upper_case(*p++); } request->method.start = &request->m[0]; request->method.length = m->length; break; } } return NJS_OK; } static njs_int_t ngx_js_headers_inherit(njs_vm_t *vm, ngx_js_headers_t *headers, ngx_js_headers_t *orig) { njs_int_t ret; ngx_uint_t i; ngx_list_part_t *part; ngx_js_tb_elt_t *h; part = &orig->header_list.part; h = part->elts; for (i = 0; /* void */; i++) { if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; h = part->elts; i = 0; } if (h[i].hash == 0) { continue; } ret = ngx_js_headers_append(vm, headers, h[i].key.data, h[i].key.len, h[i].value.data, h[i].value.len); if (ret != NJS_OK) { return NJS_ERROR; } } return NJS_OK; } static njs_int_t ngx_js_headers_fill(njs_vm_t *vm, ngx_js_headers_t *headers, njs_value_t *init) { int64_t i, len, length; njs_int_t ret; njs_str_t name, header; njs_value_t *keys, *value; ngx_js_headers_t *hh; njs_opaque_value_t *e, *start, lvalue; hh = njs_vm_external(vm, ngx_http_js_fetch_headers_proto_id, init); if (hh != NULL) { return ngx_js_headers_inherit(vm, headers, hh); } if (njs_value_is_array(init)) { start = (njs_opaque_value_t *) njs_vm_array_start(vm, init); if (start == NULL) { return NJS_ERROR; } (void) njs_vm_array_length(vm, init, &length); for (i = 0; i < length; i++) { e = (njs_opaque_value_t *) njs_vm_array_start(vm, njs_value_arg(start)); if (e == NULL) { return NJS_ERROR; } (void) njs_vm_array_length(vm, njs_value_arg(start), &len); start++; if (len != 2) { njs_vm_error(vm, "header does not contain exactly two items"); return NJS_ERROR; } if (ngx_js_string(vm, njs_value_arg(&e[0]), &name) != NGX_OK) { return NJS_ERROR; } if (ngx_js_string(vm, njs_value_arg(&e[1]), &header) != NGX_OK) { return NJS_ERROR; } ret = ngx_js_headers_append(vm, headers, name.start, name.length, header.start, header.length); if (ret != NJS_OK) { return NJS_ERROR; } } } else { keys = njs_vm_object_keys(vm, init, njs_value_arg(&lvalue)); if (keys == NULL) { return NJS_ERROR; } start = (njs_opaque_value_t *) njs_vm_array_start(vm, keys); if (start == NULL) { return NJS_ERROR; } (void) njs_vm_array_length(vm, keys, &length); for (i = 0; i < length; i++) { if (ngx_js_string(vm, njs_value_arg(start), &name) != NGX_OK) { return NJS_ERROR; } start++; value = njs_vm_object_prop(vm, init, &name, &lvalue); if (value == NULL) { return NJS_ERROR; } if (ngx_js_string(vm, value, &header) != NGX_OK) { return NJS_ERROR; } ret = ngx_js_headers_append(vm, headers, name.start, name.length, header.start, header.length); if (ret != NJS_OK) { return NJS_ERROR; } } } return NJS_OK; } static ngx_js_http_t * ngx_js_http_alloc(njs_vm_t *vm, ngx_pool_t *pool, ngx_log_t *log) { njs_int_t ret; ngx_js_ctx_t *ctx; ngx_js_http_t *http; ngx_js_event_t *event; njs_function_t *callback; http = ngx_pcalloc(pool, sizeof(ngx_js_http_t)); if (http == NULL) { goto failed; } http->pool = pool; http->log = log; http->vm = vm; http->timeout = 10000; http->http_parse.content_length_n = -1; ret = njs_vm_promise_create(vm, njs_value_arg(&http->promise), njs_value_arg(&http->promise_callbacks)); if (ret != NJS_OK) { goto failed; } callback = njs_vm_function_alloc(vm, ngx_js_http_promise_trampoline, 0, 0); if (callback == NULL) { goto failed; } event = njs_mp_zalloc(njs_vm_memory_pool(vm), sizeof(ngx_js_event_t)); if (njs_slow_path(event == NULL)) { goto failed; } ctx = ngx_external_ctx(vm, njs_vm_external_ptr(vm)); event->ctx = vm; njs_value_function_set(njs_value_arg(&event->function), callback); event->destructor = njs_js_http_destructor; event->fd = ctx->event_id++; event->data = http; ngx_js_add_event(ctx, event); http->event = event; ngx_log_debug1(NGX_LOG_DEBUG_EVENT, log, 0, "js fetch alloc:%p", http); return http; failed: njs_vm_error(vm, "internal error"); return NULL; } static void ngx_js_resolve_handler(ngx_resolver_ctx_t *ctx) { u_char *p; size_t len; socklen_t socklen; ngx_uint_t i; ngx_js_http_t *http; struct sockaddr *sockaddr; http = ctx->data; if (ctx->state) { ngx_js_http_error(http, 0, "\"%V\" could not be resolved (%i: %s)", &ctx->name, ctx->state, ngx_resolver_strerror(ctx->state)); return; } ngx_log_debug1(NGX_LOG_DEBUG_EVENT, http->log, 0, "http fetch resolved: \"%V\"", &ctx->name); #if (NGX_DEBUG) { u_char text[NGX_SOCKADDR_STRLEN]; ngx_str_t addr; ngx_uint_t i; addr.data = text; for (i = 0; i < ctx->naddrs; i++) { addr.len = ngx_sock_ntop(ctx->addrs[i].sockaddr, ctx->addrs[i].socklen, text, NGX_SOCKADDR_STRLEN, 0); ngx_log_debug1(NGX_LOG_DEBUG_EVENT, http->log, 0, "name was resolved to \"%V\"", &addr); } } #endif http->naddrs = ctx->naddrs; http->addrs = ngx_pcalloc(http->pool, http->naddrs * sizeof(ngx_addr_t)); if (http->addrs == NULL) { goto failed; } for (i = 0; i < ctx->naddrs; i++) { socklen = ctx->addrs[i].socklen; sockaddr = ngx_palloc(http->pool, socklen); if (sockaddr == NULL) { goto failed; } ngx_memcpy(sockaddr, ctx->addrs[i].sockaddr, socklen); ngx_inet_set_port(sockaddr, http->port); http->addrs[i].sockaddr = sockaddr; http->addrs[i].socklen = socklen; p = ngx_pnalloc(http->pool, NGX_SOCKADDR_STRLEN); if (p == NULL) { goto failed; } len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1); http->addrs[i].name.len = len; http->addrs[i].name.data = p; } ngx_resolve_name_done(ctx); http->ctx = NULL; ngx_js_http_connect(http); return; failed: ngx_js_http_error(http, 0, "memory error"); } static void ngx_js_http_close_connection(ngx_connection_t *c) { ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "js fetch close connection: %d", c->fd); #if (NGX_SSL) if (c->ssl) { c->ssl->no_wait_shutdown = 1; if (ngx_ssl_shutdown(c) == NGX_AGAIN) { c->ssl->handler = ngx_js_http_close_connection; return; } } #endif c->destroyed = 1; ngx_close_connection(c); } static void njs_js_http_destructor(ngx_js_event_t *event) { ngx_js_http_t *http; http = event->data; ngx_log_debug1(NGX_LOG_DEBUG_EVENT, http->log, 0, "js fetch destructor:%p", http); if (http->ctx != NULL) { ngx_resolve_name_done(http->ctx); http->ctx = NULL; } if (http->peer.connection != NULL) { ngx_js_http_close_connection(http->peer.connection); http->peer.connection = NULL; } } static njs_int_t ngx_js_fetch_promissified_result(njs_vm_t *vm, njs_value_t *result, njs_int_t rc, njs_value_t *retval) { njs_int_t ret; njs_function_t *callback; njs_opaque_value_t promise, arguments[2]; ret = njs_vm_promise_create(vm, njs_value_arg(&promise), njs_value_arg(&arguments)); if (ret != NJS_OK) { goto error; } callback = njs_vm_function_alloc(vm, ngx_js_http_promise_trampoline, 0, 0); if (callback == NULL) { goto error; } njs_value_assign(&arguments[0], &arguments[(rc != NJS_OK)]); if (rc != NJS_OK) { njs_vm_exception_get(vm, njs_value_arg(&arguments[1])); } else { njs_value_assign(&arguments[1], result); } ret = njs_vm_enqueue_job(vm, callback, njs_value_arg(&arguments), 2); if (ret == NJS_ERROR) { goto error; } njs_value_assign(retval, &promise); return NJS_OK; error: njs_vm_error(vm, "internal error"); return NJS_ERROR; } static void ngx_js_http_fetch_done(ngx_js_http_t *http, njs_opaque_value_t *retval, njs_int_t rc) { njs_vm_t *vm; ngx_js_ctx_t *ctx; ngx_js_event_t *event; njs_opaque_value_t arguments[2], *action; ngx_log_debug2(NGX_LOG_DEBUG_EVENT, http->log, 0, "js fetch done http:%p rc:%i", http, (ngx_int_t) rc); if (http->peer.connection != NULL) { ngx_js_http_close_connection(http->peer.connection); http->peer.connection = NULL; } if (http->event != NULL) { action = &http->promise_callbacks[(rc != NJS_OK)]; njs_value_assign(&arguments[0], action); njs_value_assign(&arguments[1], retval); vm = http->vm; event = http->event; rc = ngx_js_call(vm, njs_value_function(njs_value_arg(&event->function)), &arguments[0], 2); ctx = ngx_external_ctx(vm, njs_vm_external_ptr(vm)); ngx_js_del_event(ctx, event); ngx_external_event_finalize(vm)(njs_vm_external_ptr(vm), rc); } } static njs_int_t ngx_js_http_promise_trampoline(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_function_t *callback; callback = njs_value_function(njs_argument(args, 1)); if (callback != NULL) { return njs_vm_call(vm, callback, njs_argument(args, 2), 1); } return NJS_OK; } static void ngx_js_http_connect(ngx_js_http_t *http) { ngx_int_t rc; ngx_addr_t *addr; addr = &http->addrs[http->naddr]; ngx_log_debug2(NGX_LOG_DEBUG_EVENT, http->log, 0, "js fetch connect %ui/%ui", http->naddr, http->naddrs); http->peer.sockaddr = addr->sockaddr; http->peer.socklen = addr->socklen; http->peer.name = &addr->name; http->peer.get = ngx_event_get_peer; http->peer.log = http->log; http->peer.log_error = NGX_ERROR_ERR; rc = ngx_event_connect_peer(&http->peer); if (rc == NGX_ERROR) { ngx_js_http_error(http, 0, "connect failed"); return; } if (rc == NGX_BUSY || rc == NGX_DECLINED) { ngx_js_http_next(http); return; } http->peer.connection->data = http; http->peer.connection->pool = http->pool; http->peer.connection->write->handler = ngx_js_http_write_handler; http->peer.connection->read->handler = ngx_js_http_read_handler; http->process = ngx_js_http_process_status_line; ngx_add_timer(http->peer.connection->read, http->timeout); ngx_add_timer(http->peer.connection->write, http->timeout); #if (NGX_SSL) if (http->ssl != NULL && http->peer.connection->ssl == NULL) { ngx_js_http_ssl_init_connection(http); return; } #endif if (rc == NGX_OK) { ngx_js_http_write_handler(http->peer.connection->write); } } #if (NGX_SSL) static void ngx_js_http_ssl_init_connection(ngx_js_http_t *http) { ngx_int_t rc; ngx_connection_t *c; c = http->peer.connection; ngx_log_debug2(NGX_LOG_DEBUG_EVENT, http->log, 0, "js fetch secure connect %ui/%ui", http->naddr, http->naddrs); if (ngx_ssl_create_connection(http->ssl, c, NGX_SSL_BUFFER|NGX_SSL_CLIENT) != NGX_OK) { ngx_js_http_error(http, 0, "failed to create ssl connection"); return; } c->sendfile = 0; if (ngx_js_http_ssl_name(http) != NGX_OK) { ngx_js_http_error(http, 0, "failed to create ssl connection"); return; } c->log->action = "SSL handshaking to fetch target"; rc = ngx_ssl_handshake(c); if (rc == NGX_AGAIN) { c->data = http; c->ssl->handler = ngx_js_http_ssl_handshake_handler; return; } ngx_js_http_ssl_handshake(http); } static void ngx_js_http_ssl_handshake_handler(ngx_connection_t *c) { ngx_js_http_t *http; http = c->data; http->peer.connection->write->handler = ngx_js_http_write_handler; http->peer.connection->read->handler = ngx_js_http_read_handler; ngx_js_http_ssl_handshake(http); } static void ngx_js_http_ssl_handshake(ngx_js_http_t *http) { long rc; ngx_connection_t *c; c = http->peer.connection; if (c->ssl->handshaked) { if (http->ssl_verify) { rc = SSL_get_verify_result(c->ssl->connection); if (rc != X509_V_OK) { ngx_log_error(NGX_LOG_ERR, c->log, 0, "js fetch SSL certificate verify error: (%l:%s)", rc, X509_verify_cert_error_string(rc)); goto failed; } if (ngx_ssl_check_host(c, &http->tls_name) != NGX_OK) { ngx_log_error(NGX_LOG_ERR, c->log, 0, "js fetch SSL certificate does not match \"%V\"", &http->tls_name); goto failed; } } c->write->handler = ngx_js_http_write_handler; c->read->handler = ngx_js_http_read_handler; if (c->read->ready) { ngx_post_event(c->read, &ngx_posted_events); } http->process = ngx_js_http_process_status_line; ngx_js_http_write_handler(c->write); return; } failed: ngx_js_http_next(http); } static njs_int_t ngx_js_http_ssl_name(ngx_js_http_t *http) { #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME u_char *p; /* as per RFC 6066, literal IPv4 and IPv6 addresses are not permitted */ ngx_str_t *name = &http->tls_name; if (name->len == 0 || *name->data == '[') { goto done; } if (ngx_inet_addr(name->data, name->len) != INADDR_NONE) { goto done; } /* * SSL_set_tlsext_host_name() needs a null-terminated string, * hence we explicitly null-terminate name here */ p = ngx_pnalloc(http->pool, name->len + 1); if (p == NULL) { return NGX_ERROR; } (void) ngx_cpystrn(p, name->data, name->len + 1); name->data = p; ngx_log_debug1(NGX_LOG_DEBUG_EVENT, http->log, 0, "js fetch SSL server name: \"%s\"", name->data); if (SSL_set_tlsext_host_name(http->peer.connection->ssl->connection, (char *) name->data) == 0) { ngx_ssl_error(NGX_LOG_ERR, http->log, 0, "SSL_set_tlsext_host_name(\"%s\") failed", name->data); return NGX_ERROR; } #endif done: return NJS_OK; } #endif static void ngx_js_http_next(ngx_js_http_t *http) { ngx_log_debug0(NGX_LOG_DEBUG_EVENT, http->log, 0, "js fetch next addr"); if (++http->naddr >= http->naddrs) { ngx_js_http_error(http, 0, "connect failed"); return; } if (http->peer.connection != NULL) { ngx_js_http_close_connection(http->peer.connection); http->peer.connection = NULL; } http->buffer = NULL; ngx_js_http_connect(http); } static void ngx_js_http_write_handler(ngx_event_t *wev) { ssize_t n, size; ngx_buf_t *b; ngx_js_http_t *http; ngx_connection_t *c; c = wev->data; http = c->data; ngx_log_debug0(NGX_LOG_DEBUG_EVENT, wev->log, 0, "js fetch write handler"); if (wev->timedout) { ngx_js_http_error(http, NGX_ETIMEDOUT, "write timed out"); return; } #if (NGX_SSL) if (http->ssl != NULL && http->peer.connection->ssl == NULL) { ngx_js_http_ssl_init_connection(http); return; } #endif b = http->buffer; if (b == NULL) { size = njs_chb_size(&http->chain); if (size < 0) { ngx_js_http_error(http, 0, "memory error"); return; } b = ngx_create_temp_buf(http->pool, size); if (b == NULL) { ngx_js_http_error(http, 0, "memory error"); return; } njs_chb_join_to(&http->chain, b->last); b->last += size; http->buffer = b; } size = b->last - b->pos; n = c->send(c, b->pos, size); if (n == NGX_ERROR) { ngx_js_http_next(http); return; } if (n > 0) { b->pos += n; if (n == size) { wev->handler = ngx_js_http_dummy_handler; http->buffer = NULL; if (wev->timer_set) { ngx_del_timer(wev); } if (ngx_handle_write_event(wev, 0) != NGX_OK) { ngx_js_http_error(http, 0, "write failed"); } return; } } if (!wev->timer_set) { ngx_add_timer(wev, http->timeout); } } static void ngx_js_http_read_handler(ngx_event_t *rev) { ssize_t n, size; ngx_int_t rc; ngx_buf_t *b; ngx_js_http_t *http; ngx_connection_t *c; c = rev->data; http = c->data; ngx_log_debug0(NGX_LOG_DEBUG_EVENT, rev->log, 0, "js fetch read handler"); if (rev->timedout) { ngx_js_http_error(http, NGX_ETIMEDOUT, "read timed out"); return; } if (http->buffer == NULL) { b = ngx_create_temp_buf(http->pool, http->buffer_size); if (b == NULL) { ngx_js_http_error(http, 0, "memory error"); return; } http->buffer = b; } for ( ;; ) { b = http->buffer; size = b->end - b->last; n = c->recv(c, b->last, size); if (n > 0) { b->last += n; rc = http->process(http); if (rc == NGX_ERROR) { return; } continue; } if (n == NGX_AGAIN) { if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_js_http_error(http, 0, "read failed"); } return; } if (n == NGX_ERROR) { ngx_js_http_next(http); return; } break; } http->done = 1; rc = http->process(http); if (rc == NGX_DONE) { /* handler was called */ return; } if (rc == NGX_AGAIN) { ngx_js_http_error(http, 0, "prematurely closed connection"); } } static njs_int_t ngx_js_request_constructor(njs_vm_t *vm, ngx_js_request_t *request, ngx_url_t *u, njs_external_ptr_t external, njs_value_t *args, njs_uint_t nargs) { njs_int_t ret; ngx_uint_t rc; ngx_pool_t *pool; njs_value_t *input, *init, *value, *headers; ngx_js_request_t *orig; njs_opaque_value_t lvalue; static const njs_str_t body_key = njs_str("body"); static const njs_str_t cache_key = njs_str("cache"); static const njs_str_t cred_key = njs_str("credentials"); static const njs_str_t headers_key = njs_str("headers"); static const njs_str_t mode_key = njs_str("mode"); static const njs_str_t method_key = njs_str("method"); input = njs_arg(args, nargs, 1); if (njs_value_is_undefined(input)) { njs_vm_error(vm, "1st argument is required"); return NJS_ERROR; } /* * set by ngx_memzero(): * * request->url.length = 0; * request->body.length = 0; * request->cache_mode = CACHE_MODE_DEFAULT; * request->credentials = CREDENTIALS_SAME_ORIGIN; * request->mode = MODE_NO_CORS; * request->headers.content_type = NULL; */ ngx_memzero(request, sizeof(ngx_js_request_t)); request->method = njs_str_value("GET"); request->body = njs_str_value(""); request->headers.guard = GUARD_REQUEST; pool = ngx_external_pool(vm, external); rc = ngx_list_init(&request->headers.header_list, pool, 4, sizeof(ngx_js_tb_elt_t)); if (rc != NGX_OK) { njs_vm_memory_error(vm); return NJS_ERROR; } if (njs_value_is_string(input)) { ret = ngx_js_string(vm, input, &request->url); if (ret != NJS_OK) { njs_vm_error(vm, "failed to convert url arg"); return NJS_ERROR; } } else { orig = njs_vm_external(vm, ngx_http_js_fetch_request_proto_id, input); if (orig == NULL) { njs_vm_error(vm, "input is not string or a Request object"); return NJS_ERROR; } request->url = orig->url; request->method = orig->method; request->body = orig->body; request->body_used = orig->body_used; request->cache_mode = orig->cache_mode; request->credentials = orig->credentials; request->mode = orig->mode; ret = ngx_js_headers_inherit(vm, &request->headers, &orig->headers); if (ret != NJS_OK) { return NJS_ERROR; } } ngx_js_http_trim(&request->url.start, &request->url.length, 1); ngx_memzero(u, sizeof(ngx_url_t)); u->url.len = request->url.length; u->url.data = request->url.start; u->default_port = 80; u->uri_part = 1; u->no_resolve = 1; if (u->url.len > 7 && njs_strncasecmp(u->url.data, (u_char *) "http://", 7) == 0) { u->url.len -= 7; u->url.data += 7; #if (NGX_SSL) } else if (u->url.len > 8 && njs_strncasecmp(u->url.data, (u_char *) "https://", 8) == 0) { u->url.len -= 8; u->url.data += 8; u->default_port = 443; #endif } else { njs_vm_error(vm, "unsupported URL schema (only http or https are" " supported)"); return NJS_ERROR; } if (ngx_parse_url(pool, u) != NGX_OK) { njs_vm_error(vm, "invalid url"); return NJS_ERROR; } init = njs_arg(args, nargs, 2); if (njs_value_is_object(init)) { value = njs_vm_object_prop(vm, init, &method_key, &lvalue); if (value != NULL && ngx_js_string(vm, value, &request->method) != NGX_OK) { njs_vm_error(vm, "invalid Request method"); return NJS_ERROR; } ret = ngx_js_method_process(vm, request); if (ret != NJS_OK) { return NJS_ERROR; } value = njs_vm_object_prop(vm, init, &cache_key, &lvalue); if (value != NULL) { ret = ngx_fetch_flag_set(vm, ngx_js_fetch_cache_modes, value, "cache"); if (ret == NJS_ERROR) { return NJS_ERROR; } request->cache_mode = ret; } value = njs_vm_object_prop(vm, init, &cred_key, &lvalue); if (value != NULL) { ret = ngx_fetch_flag_set(vm, ngx_js_fetch_credentials, value, "credentials"); if (ret == NJS_ERROR) { return NJS_ERROR; } request->credentials = ret; } value = njs_vm_object_prop(vm, init, &mode_key, &lvalue); if (value != NULL) { ret = ngx_fetch_flag_set(vm, ngx_js_fetch_modes, value, "mode"); if (ret == NJS_ERROR) { return NJS_ERROR; } request->mode = ret; } headers = njs_vm_object_prop(vm, init, &headers_key, &lvalue); if (headers != NULL) { if (!njs_value_is_object(headers)) { njs_vm_error(vm, "Headers is not an object"); return NJS_ERROR; } /* * There are no API to reset or destroy ngx_list, * just allocating a new one. */ ngx_memset(&request->headers, 0, sizeof(ngx_js_headers_t)); request->headers.guard = GUARD_REQUEST; rc = ngx_list_init(&request->headers.header_list, pool, 4, sizeof(ngx_js_tb_elt_t)); if (rc != NGX_OK) { njs_vm_memory_error(vm); return NJS_ERROR; } ret = ngx_js_headers_fill(vm, &request->headers, headers); if (ret != NJS_OK) { return NJS_ERROR; } } value = njs_vm_object_prop(vm, init, &body_key, &lvalue); if (value != NULL) { if (ngx_js_string(vm, value, &request->body) != NGX_OK) { njs_vm_error(vm, "invalid Request body"); return NJS_ERROR; } if (request->headers.content_type == NULL && njs_value_is_string(value)) { ret = ngx_js_headers_append(vm, &request->headers, (u_char *) "Content-Type", njs_length("Content-Type"), (u_char *) "text/plain;charset=UTF-8", njs_length("text/plain;charset=UTF-8")); if (ret != NJS_OK) { return NJS_ERROR; } } } } return NJS_OK; } njs_inline njs_int_t ngx_js_http_whitespace(u_char c) { switch (c) { case 0x09: /* */ case 0x0A: /* */ case 0x0D: /* */ case 0x20: /* */ return 1; default: return 0; } } static void ngx_js_http_trim(u_char **value, size_t *len, njs_bool_t trim_c0_control_or_space) { u_char *start, *end; start = *value; end = start + *len; for ( ;; ) { if (start == end) { break; } if (ngx_js_http_whitespace(*start) || (trim_c0_control_or_space && *start <= ' ')) { start++; continue; } break; } for ( ;; ) { if (start == end) { break; } end--; if (ngx_js_http_whitespace(*end) || (trim_c0_control_or_space && *end <= ' ')) { continue; } end++; break; } *value = start; *len = end - start; } static const uint32_t token_map[] = { 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ 0x03ff6cfa, /* 0000 0011 1111 1111 0110 1100 1111 1010 */ /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ 0xc7fffffe, /* 1100 0111 1111 1111 1111 1111 1111 1110 */ /* ~}| {zyx wvut srqp onml kjih gfed cba` */ 0x57ffffff, /* 0101 0111 1111 1111 1111 1111 1111 1111 */ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ }; njs_inline njs_bool_t njs_is_token(uint32_t byte) { return ((token_map[byte >> 5] & ((uint32_t) 1 << (byte & 0x1f))) != 0); } static njs_int_t ngx_js_headers_append(njs_vm_t *vm, ngx_js_headers_t *headers, u_char *name, size_t len, u_char *value, size_t vlen) { u_char *p, *end; ngx_uint_t i; ngx_js_tb_elt_t *h, **ph; ngx_list_part_t *part; ngx_js_http_trim(&value, &vlen, 0); p = name; end = p + len; while (p < end) { if (!njs_is_token(*p)) { njs_vm_error(vm, "invalid header name"); return NJS_ERROR; } p++; } p = value; end = p + vlen; while (p < end) { if (*p == '\0') { njs_vm_error(vm, "invalid header value"); return NJS_ERROR; } p++; } if (headers->guard == GUARD_IMMUTABLE) { njs_vm_error(vm, "cannot append to immutable object"); return NJS_ERROR; } ph = NULL; part = &headers->header_list.part; h = part->elts; for (i = 0; /* void */; i++) { if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; h = part->elts; i = 0; } if (h[i].hash == 0) { continue; } if (len == h[i].key.len && (njs_strncasecmp(name, h[i].key.data, len) == 0)) { ph = &h[i].next; while (*ph) { ph = &(*ph)->next; } break; } } h = ngx_list_push(&headers->header_list); if (h == NULL) { njs_vm_memory_error(vm); return NJS_ERROR; } if (ph != NULL) { *ph = h; } h->hash = 1; h->key.data = name; h->key.len = len; h->value.data = value; h->value.len = vlen; h->next = NULL; if (len == njs_strlen("Content-Type") && ngx_strncasecmp(name, (u_char *) "Content-Type", len) == 0) { headers->content_type = h; } return NJS_OK; } static ngx_int_t ngx_js_http_process_status_line(ngx_js_http_t *http) { ngx_int_t rc; ngx_js_http_parse_t *hp; hp = &http->http_parse; rc = ngx_js_http_parse_status_line(hp, http->buffer); if (rc == NGX_OK) { ngx_log_debug1(NGX_LOG_DEBUG_EVENT, http->log, 0, "js fetch status %ui", hp->code); http->response.code = hp->code; http->response.status_text.start = hp->status_text; http->response.status_text.length = hp->status_text_end - hp->status_text; http->process = ngx_js_http_process_headers; return http->process(http); } if (rc == NGX_AGAIN) { return NGX_AGAIN; } /* rc == NGX_ERROR */ ngx_js_http_error(http, 0, "invalid fetch status line"); return NGX_ERROR; } static ngx_int_t ngx_js_http_process_headers(ngx_js_http_t *http) { size_t len, vlen; ngx_int_t rc; njs_int_t ret; ngx_js_http_parse_t *hp; ngx_log_debug0(NGX_LOG_DEBUG_EVENT, http->log, 0, "js fetch process headers"); hp = &http->http_parse; if (http->response.headers.header_list.size == 0) { rc = ngx_list_init(&http->response.headers.header_list, http->pool, 4, sizeof(ngx_js_tb_elt_t)); if (rc != NGX_OK) { ngx_js_http_error(http, 0, "alloc failed"); return NGX_ERROR; } } for ( ;; ) { rc = ngx_js_http_parse_header_line(hp, http->buffer); if (rc == NGX_OK) { len = hp->header_name_end - hp->header_name_start; vlen = hp->header_end - hp->header_start; ret = ngx_js_headers_append(http->vm, &http->response.headers, hp->header_name_start, len, hp->header_start, vlen); if (ret == NJS_ERROR) { ngx_js_http_error(http, 0, "cannot add respose header"); return NGX_ERROR; } ngx_log_debug4(NGX_LOG_DEBUG_EVENT, http->log, 0, "js fetch header \"%*s: %*s\"", len, hp->header_name_start, vlen, hp->header_start); if (len == njs_strlen("Transfer-Encoding") && vlen == njs_strlen("chunked") && ngx_strncasecmp(hp->header_name_start, (u_char *) "Transfer-Encoding", len) == 0 && ngx_strncasecmp(hp->header_start, (u_char *) "chunked", vlen) == 0) { hp->chunked = 1; } if (len == njs_strlen("Content-Length") && ngx_strncasecmp(hp->header_name_start, (u_char *) "Content-Length", len) == 0) { hp->content_length_n = ngx_atoof(hp->header_start, vlen); if (hp->content_length_n == NGX_ERROR) { ngx_js_http_error(http, 0, "invalid fetch content length"); return NGX_ERROR; } if (!http->header_only && hp->content_length_n > (off_t) http->max_response_body_size) { ngx_js_http_error(http, 0, "fetch content length is too large"); return NGX_ERROR; } } continue; } if (rc == NGX_DONE) { http->response.headers.guard = GUARD_IMMUTABLE; break; } if (rc == NGX_AGAIN) { return NGX_AGAIN; } /* rc == NGX_ERROR */ ngx_js_http_error(http, 0, "invalid fetch header"); return NGX_ERROR; } njs_chb_destroy(&http->chain); NJS_CHB_MP_INIT(&http->response.chain, http->vm); http->process = ngx_js_http_process_body; return http->process(http); } static ngx_int_t ngx_js_http_process_body(ngx_js_http_t *http) { ssize_t size, chsize, need; ngx_int_t rc; njs_int_t ret; ngx_buf_t *b; ngx_log_debug1(NGX_LOG_DEBUG_EVENT, http->log, 0, "js fetch process body done:%ui", (ngx_uint_t) http->done); if (http->done) { size = njs_chb_size(&http->response.chain); if (size < 0) { ngx_js_http_error(http, 0, "memory error"); return NGX_ERROR; } if (!http->header_only && http->http_parse.chunked && http->http_parse.content_length_n == -1) { ngx_js_http_error(http, 0, "invalid fetch chunked response"); return NGX_ERROR; } if (http->header_only || http->http_parse.content_length_n == -1 || size == http->http_parse.content_length_n) { ret = njs_vm_external_create(http->vm, njs_value_arg(&http->response_value), ngx_http_js_fetch_response_proto_id, &http->response, 0); if (ret != NJS_OK) { ngx_js_http_error(http, 0, "fetch response creation failed"); return NGX_ERROR; } ngx_js_http_fetch_done(http, &http->response_value, NJS_OK); return NGX_DONE; } if (size < http->http_parse.content_length_n) { return NGX_AGAIN; } ngx_js_http_error(http, 0, "fetch trailing data"); return NGX_ERROR; } b = http->buffer; if (http->http_parse.chunked) { rc = ngx_js_http_parse_chunked(&http->http_chunk_parse, b, &http->response.chain); if (rc == NGX_ERROR) { ngx_js_http_error(http, 0, "invalid fetch chunked response"); return NGX_ERROR; } size = njs_chb_size(&http->response.chain); if (rc == NGX_OK) { http->http_parse.content_length_n = size; } if (size > http->max_response_body_size * 10) { ngx_js_http_error(http, 0, "very large fetch chunked response"); return NGX_ERROR; } b->pos = http->http_chunk_parse.pos; } else { size = njs_chb_size(&http->response.chain); if (http->header_only) { need = 0; } else if (http->http_parse.content_length_n == -1) { need = http->max_response_body_size - size; } else { need = http->http_parse.content_length_n - size; } chsize = ngx_min(need, b->last - b->pos); if (size + chsize > http->max_response_body_size) { ngx_js_http_error(http, 0, "fetch response body is too large"); return NGX_ERROR; } if (chsize > 0) { njs_chb_append(&http->response.chain, b->pos, chsize); b->pos += chsize; } rc = (need > chsize) ? NGX_AGAIN : NGX_DONE; } if (b->pos == b->end) { if (http->chunk == NULL) { b = ngx_create_temp_buf(http->pool, http->buffer_size); if (b == NULL) { ngx_js_http_error(http, 0, "memory error"); return NGX_ERROR; } http->buffer = b; http->chunk = b; } else { b->last = b->start; b->pos = b->start; } } return rc; } static ngx_int_t ngx_js_http_parse_status_line(ngx_js_http_parse_t *hp, ngx_buf_t *b) { u_char ch; u_char *p; enum { sw_start = 0, sw_H, sw_HT, sw_HTT, sw_HTTP, sw_first_major_digit, sw_major_digit, sw_first_minor_digit, sw_minor_digit, sw_status, sw_space_after_status, sw_status_text, sw_almost_done } state; state = hp->state; for (p = b->pos; p < b->last; p++) { ch = *p; switch (state) { /* "HTTP/" */ case sw_start: switch (ch) { case 'H': state = sw_H; break; default: return NGX_ERROR; } break; case sw_H: switch (ch) { case 'T': state = sw_HT; break; default: return NGX_ERROR; } break; case sw_HT: switch (ch) { case 'T': state = sw_HTT; break; default: return NGX_ERROR; } break; case sw_HTT: switch (ch) { case 'P': state = sw_HTTP; break; default: return NGX_ERROR; } break; case sw_HTTP: switch (ch) { case '/': state = sw_first_major_digit; break; default: return NGX_ERROR; } break; /* the first digit of major HTTP version */ case sw_first_major_digit: if (ch < '1' || ch > '9') { return NGX_ERROR; } state = sw_major_digit; break; /* the major HTTP version or dot */ case sw_major_digit: if (ch == '.') { state = sw_first_minor_digit; break; } if (ch < '0' || ch > '9') { return NGX_ERROR; } break; /* the first digit of minor HTTP version */ case sw_first_minor_digit: if (ch < '0' || ch > '9') { return NGX_ERROR; } state = sw_minor_digit; break; /* the minor HTTP version or the end of the request line */ case sw_minor_digit: if (ch == ' ') { state = sw_status; break; } if (ch < '0' || ch > '9') { return NGX_ERROR; } break; /* HTTP status code */ case sw_status: if (ch == ' ') { break; } if (ch < '0' || ch > '9') { return NGX_ERROR; } hp->code = hp->code * 10 + (ch - '0'); if (++hp->count == 3) { state = sw_space_after_status; } break; /* space or end of line */ case sw_space_after_status: switch (ch) { case ' ': state = sw_status_text; break; case '.': /* IIS may send 403.1, 403.2, etc */ state = sw_status_text; break; case CR: break; case LF: goto done; default: return NGX_ERROR; } break; /* any text until end of line */ case sw_status_text: switch (ch) { case CR: hp->status_text_end = p; state = sw_almost_done; break; case LF: hp->status_text_end = p; goto done; } if (hp->status_text == NULL) { hp->status_text = p; } break; /* end of status line */ case sw_almost_done: switch (ch) { case LF: goto done; default: return NGX_ERROR; } } } b->pos = p; hp->state = state; return NGX_AGAIN; done: b->pos = p + 1; hp->state = sw_start; return NGX_OK; } static ngx_int_t ngx_js_http_parse_header_line(ngx_js_http_parse_t *hp, ngx_buf_t *b) { u_char c, ch, *p; enum { sw_start = 0, sw_name, sw_space_before_value, sw_value, sw_space_after_value, sw_almost_done, sw_header_almost_done } state; state = hp->state; for (p = b->pos; p < b->last; p++) { ch = *p; switch (state) { /* first char */ case sw_start: switch (ch) { case CR: hp->header_end = p; state = sw_header_almost_done; break; case LF: hp->header_end = p; goto header_done; default: state = sw_name; hp->header_name_start = p; c = (u_char) (ch | 0x20); if (c >= 'a' && c <= 'z') { break; } if (ch >= '0' && ch <= '9') { break; } return NGX_ERROR; } break; /* header name */ case sw_name: c = (u_char) (ch | 0x20); if (c >= 'a' && c <= 'z') { break; } if (ch == ':') { hp->header_name_end = p; state = sw_space_before_value; break; } if (ch == '-') { break; } if (ch >= '0' && ch <= '9') { break; } if (ch == CR) { hp->header_name_end = p; hp->header_start = p; hp->header_end = p; state = sw_almost_done; break; } if (ch == LF) { hp->header_name_end = p; hp->header_start = p; hp->header_end = p; goto done; } return NGX_ERROR; /* space* before header value */ case sw_space_before_value: switch (ch) { case ' ': break; case CR: hp->header_start = p; hp->header_end = p; state = sw_almost_done; break; case LF: hp->header_start = p; hp->header_end = p; goto done; default: hp->header_start = p; state = sw_value; break; } break; /* header value */ case sw_value: switch (ch) { case ' ': hp->header_end = p; state = sw_space_after_value; break; case CR: hp->header_end = p; state = sw_almost_done; break; case LF: hp->header_end = p; goto done; } break; /* space* before end of header line */ case sw_space_after_value: switch (ch) { case ' ': break; case CR: state = sw_almost_done; break; case LF: goto done; default: state = sw_value; break; } break; /* end of header line */ case sw_almost_done: switch (ch) { case LF: goto done; default: return NGX_ERROR; } /* end of header */ case sw_header_almost_done: switch (ch) { case LF: goto header_done; default: return NGX_ERROR; } } } b->pos = p; hp->state = state; return NGX_AGAIN; done: b->pos = p + 1; hp->state = sw_start; return NGX_OK; header_done: b->pos = p + 1; hp->state = sw_start; return NGX_DONE; } #define \ ngx_size_is_sufficient(cs) \ (cs < ((__typeof__(cs)) 1 << (sizeof(cs) * 8 - 4))) #define NGX_JS_HTTP_CHUNK_MIDDLE 0 #define NGX_JS_HTTP_CHUNK_ON_BORDER 1 #define NGX_JS_HTTP_CHUNK_END 2 static ngx_int_t ngx_js_http_chunk_buffer(ngx_js_http_chunk_parse_t *hcp, ngx_buf_t *b, njs_chb_t *chain) { size_t size; size = b->last - hcp->pos; if (hcp->chunk_size < size) { njs_chb_append(chain, hcp->pos, hcp->chunk_size); hcp->pos += hcp->chunk_size; return NGX_JS_HTTP_CHUNK_END; } njs_chb_append(chain, hcp->pos, size); hcp->pos += size; hcp->chunk_size -= size; if (hcp->chunk_size == 0) { return NGX_JS_HTTP_CHUNK_ON_BORDER; } return NGX_JS_HTTP_CHUNK_MIDDLE; } static ngx_int_t ngx_js_http_parse_chunked(ngx_js_http_chunk_parse_t *hcp, ngx_buf_t *b, njs_chb_t *chain) { u_char c, ch; ngx_int_t rc; enum { sw_start = 0, sw_chunk_size, sw_chunk_size_linefeed, sw_chunk_end_newline, sw_chunk_end_linefeed, sw_chunk, } state; state = hcp->state; hcp->pos = b->pos; while (hcp->pos < b->last) { /* * The sw_chunk state is tested outside the switch * to preserve hcp->pos and to not touch memory. */ if (state == sw_chunk) { rc = ngx_js_http_chunk_buffer(hcp, b, chain); if (rc == NGX_ERROR) { return rc; } if (rc == NGX_JS_HTTP_CHUNK_MIDDLE) { break; } state = sw_chunk_end_newline; if (rc == NGX_JS_HTTP_CHUNK_ON_BORDER) { break; } /* rc == NGX_JS_HTTP_CHUNK_END */ } ch = *hcp->pos++; switch (state) { case sw_start: state = sw_chunk_size; c = ch - '0'; if (c <= 9) { hcp->chunk_size = c; continue; } c = (ch | 0x20) - 'a'; if (c <= 5) { hcp->chunk_size = 0x0A + c; continue; } return NGX_ERROR; case sw_chunk_size: c = ch - '0'; if (c > 9) { c = (ch | 0x20) - 'a'; if (c <= 5) { c += 0x0A; } else if (ch == '\r') { state = sw_chunk_size_linefeed; continue; } else { return NGX_ERROR; } } if (ngx_size_is_sufficient(hcp->chunk_size)) { hcp->chunk_size = (hcp->chunk_size << 4) + c; continue; } return NGX_ERROR; case sw_chunk_size_linefeed: if (ch == '\n') { if (hcp->chunk_size != 0) { state = sw_chunk; continue; } hcp->last = 1; state = sw_chunk_end_newline; continue; } return NGX_ERROR; case sw_chunk_end_newline: if (ch == '\r') { state = sw_chunk_end_linefeed; continue; } return NGX_ERROR; case sw_chunk_end_linefeed: if (ch == '\n') { if (!hcp->last) { state = sw_start; continue; } return NGX_OK; } return NGX_ERROR; case sw_chunk: /* * This state is processed before the switch. * It added here just to suppress a warning. */ continue; } } hcp->state = state; return NGX_AGAIN; } static void ngx_js_http_dummy_handler(ngx_event_t *ev) { ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "js fetch dummy handler"); } static njs_int_t ngx_headers_js_get(njs_vm_t *vm, njs_value_t *value, njs_str_t *name, njs_value_t *retval, njs_bool_t as_array) { njs_int_t rc; njs_chb_t chain; ngx_uint_t i; ngx_js_tb_elt_t *h, *ph; ngx_list_part_t *part; ngx_js_headers_t *headers; headers = njs_vm_external(vm, ngx_http_js_fetch_headers_proto_id, value); if (headers == NULL) { njs_value_null_set(retval); return NJS_DECLINED; } if (as_array) { rc = njs_vm_array_alloc(vm, retval, 2); if (rc != NJS_OK) { return NJS_ERROR; } } part = &headers->header_list.part; h = part->elts; ph = NULL; for (i = 0; /* void */; i++) { if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; h = part->elts; i = 0; } if (h[i].hash == 0) { continue; } if (h[i].key.len == name->length && njs_strncasecmp(h[i].key.data, name->start, name->length) == 0) { ph = &h[i]; break; } } if (as_array) { while (ph != NULL) { value = njs_vm_array_push(vm, retval); if (value == NULL) { return NJS_ERROR; } rc = njs_vm_value_string_create(vm, value, ph->value.data, ph->value.len); if (rc != NJS_OK) { return NJS_ERROR; } ph = ph->next; } return NJS_OK; } if (ph == NULL) { njs_value_null_set(retval); return NJS_DECLINED; } NJS_CHB_MP_INIT(&chain, vm); h = ph; for ( ;; ) { njs_chb_append(&chain, h->value.data, h->value.len); if (h->next == NULL) { break; } njs_chb_append_literal(&chain, ", "); h = h->next; } rc = njs_vm_value_string_create_chb(vm, retval, &chain); njs_chb_destroy(&chain); return rc; } static njs_int_t ngx_headers_js_ext_append(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_str_t name, value; ngx_js_headers_t *headers; headers = njs_vm_external(vm, ngx_http_js_fetch_headers_proto_id, njs_argument(args, 0)); if (headers == NULL) { njs_vm_error(vm, "\"this\" is not fetch headers object"); return NJS_ERROR; } ret = ngx_js_string(vm, njs_arg(args, nargs, 1), &name); if (ret != NJS_OK) { return NJS_ERROR; } ret = ngx_js_string(vm, njs_arg(args, nargs, 2), &value); if (ret != NJS_OK) { return NJS_ERROR; } ret = ngx_js_headers_append(vm, headers, name.start, name.length, value.start, value.length); if (ret != NJS_OK) { return NJS_ERROR; } njs_value_undefined_set(retval); return NJS_OK; } static njs_int_t ngx_headers_js_ext_delete(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_str_t name; ngx_uint_t i; ngx_list_part_t *part; ngx_js_tb_elt_t *h; ngx_js_headers_t *headers; headers = njs_vm_external(vm, ngx_http_js_fetch_headers_proto_id, njs_argument(args, 0)); if (headers == NULL) { njs_vm_error(vm, "\"this\" is not fetch headers object"); return NJS_ERROR; } ret = ngx_js_string(vm, njs_arg(args, nargs, 1), &name); if (ret != NJS_OK) { return NJS_ERROR; } part = &headers->header_list.part; h = part->elts; for (i = 0; /* void */; i++) { if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; h = part->elts; i = 0; } if (h[i].hash == 0) { continue; } if (name.length == h[i].key.len && (njs_strncasecmp(name.start, h[i].key.data, name.length) == 0)) { h[i].hash = 0; } } if (name.length == njs_strlen("Content-Type") && ngx_strncasecmp(name.start, (u_char *) "Content-Type", name.length) == 0) { headers->content_type = NULL; } njs_value_undefined_set(retval); return NJS_OK; } static njs_int_t ngx_headers_js_ext_for_each(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t length; njs_int_t ret; njs_str_t name; njs_value_t *this, *callback; ngx_js_headers_t *headers; njs_opaque_value_t *k, *end, keys, arguments[2]; this = njs_argument(args, 0); headers = njs_vm_external(vm, ngx_http_js_fetch_headers_proto_id, this); if (headers == NULL) { njs_vm_error(vm, "\"this\" is not fetch headers object"); return NJS_ERROR; } callback = njs_arg(args, nargs, 1); if (!njs_value_is_function(callback)) { njs_vm_error(vm, "\"callback\" is not a function"); return NJS_ERROR; } ret = ngx_headers_js_ext_keys(vm, this, njs_value_arg(&keys)); if (ret != NJS_OK) { return NJS_ERROR; } (void) njs_vm_array_length(vm, njs_value_arg(&keys), &length); k = (njs_opaque_value_t *) njs_vm_array_start(vm, njs_value_arg(&keys)); end = k + length; for (; k < end; k++) { ret = ngx_js_string(vm, njs_value_arg(k), &name); if (ret != NJS_OK) { return NJS_ERROR; } ret = ngx_headers_js_get(vm, this, &name, njs_value_arg(&arguments[1]), 0); if (ret != NJS_OK) { return NJS_ERROR; } njs_value_assign(&arguments[0], k); ret = njs_vm_call(vm, njs_value_function(callback), njs_value_arg(&arguments[0]), 2); if (ret != NJS_OK) { return NJS_ERROR; } } return NJS_OK; } static njs_int_t ngx_headers_js_ext_get(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t as_array, njs_value_t *retval) { njs_int_t ret; njs_str_t name; ret = ngx_js_string(vm, njs_arg(args, nargs, 1), &name); if (ret != NJS_OK) { return NJS_ERROR; } ret = ngx_headers_js_get(vm, njs_argument(args, 0), &name, retval, as_array); return (ret != NJS_ERROR) ? NJS_OK : NJS_ERROR; } static njs_int_t ngx_headers_js_ext_has(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_str_t name; ret = ngx_js_string(vm, njs_arg(args, nargs, 1), &name); if (ret != NJS_OK) { return NJS_ERROR; } ret = ngx_headers_js_get(vm, njs_argument(args, 0), &name, retval, 0); if (ret == NJS_ERROR) { return NJS_ERROR; } njs_value_boolean_set(retval, ret == NJS_OK); return NJS_OK; } static njs_int_t ngx_headers_js_ext_prop(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { njs_int_t ret; njs_str_t name; ret = njs_vm_prop_name(vm, prop, &name); if (ret != NJS_OK) { return NJS_ERROR; } return ngx_headers_js_get(vm, value, &name, retval, 0); } static ngx_int_t ngx_string_compare(const void *s1, const void *s2) { return njs_vm_string_compare(s1, s2); } static njs_int_t ngx_headers_js_ext_keys(njs_vm_t *vm, njs_value_t *value, njs_value_t *keys) { njs_int_t rc; njs_str_t hdr; ngx_uint_t i, k, length; njs_value_t *start; ngx_list_part_t *part; ngx_js_tb_elt_t *h; ngx_js_headers_t *headers; headers = njs_vm_external(vm, ngx_http_js_fetch_headers_proto_id, value); if (headers == NULL) { njs_value_null_set(keys); return NJS_DECLINED; } rc = njs_vm_array_alloc(vm, keys, 8); if (rc != NJS_OK) { return NJS_ERROR; } length = 0; part = &headers->header_list.part; h = part->elts; for (i = 0; /* void */; i++) { if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; h = part->elts; i = 0; } if (h[i].hash == 0) { continue; } start = njs_vm_array_start(vm, keys); for (k = 0; k < length; k++) { njs_value_string_get(njs_argument(start, k), &hdr); if (h[i].key.len == hdr.length && njs_strncasecmp(h[i].key.data, hdr.start, hdr.length) == 0) { break; } } if (k == length) { value = njs_vm_array_push(vm, keys); if (value == NULL) { return NJS_ERROR; } rc = njs_vm_value_string_create(vm, value, h[i].key.data, h[i].key.len); if (rc != NJS_OK) { return NJS_ERROR; } length++; } } start = njs_vm_array_start(vm, keys); ngx_sort(start, (size_t) length, sizeof(njs_opaque_value_t), ngx_string_compare); return NJS_OK; } static njs_int_t ngx_headers_js_ext_set(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_str_t name, value; ngx_uint_t i; ngx_list_part_t *part; ngx_js_tb_elt_t *h, **ph, **pp; ngx_js_headers_t *headers; headers = njs_vm_external(vm, ngx_http_js_fetch_headers_proto_id, njs_argument(args, 0)); if (headers == NULL) { njs_vm_error(vm, "\"this\" is not fetch headers object"); return NJS_ERROR; } ret = ngx_js_string(vm, njs_arg(args, nargs, 1), &name); if (ret != NJS_OK) { return NJS_ERROR; } ret = ngx_js_string(vm, njs_arg(args, nargs, 2), &value); if (ret != NJS_OK) { return NJS_ERROR; } part = &headers->header_list.part; h = part->elts; for (i = 0; /* void */; i++) { if (i >= part->nelts) { if (part->next == NULL) { break; } part = part->next; h = part->elts; i = 0; } if (h[i].hash == 0) { continue; } if (name.length == h[i].key.len && (njs_strncasecmp(name.start, h[i].key.data, name.length) == 0)) { h[i].value.len = value.length; h[i].value.data = value.start; ph = &h[i].next; while (*ph) { pp = ph; ph = &(*ph)->next; *pp = NULL; } goto done; } } ret = ngx_js_headers_append(vm, headers, name.start, name.length, value.start, value.length); if (ret != NJS_OK) { return NJS_ERROR; } done: njs_value_undefined_set(retval); return NJS_OK; } static njs_int_t ngx_request_js_ext_body(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t type, njs_value_t *retval) { njs_int_t ret; ngx_js_request_t *request; njs_opaque_value_t result; request = njs_vm_external(vm, ngx_http_js_fetch_request_proto_id, njs_argument(args, 0)); if (request == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } if (request->body_used) { njs_vm_error(vm, "body stream already read"); return NJS_ERROR; } request->body_used = 1; switch (type) { case NGX_JS_BODY_ARRAY_BUFFER: ret = njs_vm_value_array_buffer_set(vm, njs_value_arg(&result), request->body.start, request->body.length); if (ret != NJS_OK) { njs_vm_memory_error(vm); return NJS_ERROR; } break; case NGX_JS_BODY_JSON: case NGX_JS_BODY_TEXT: default: ret = njs_vm_value_string_create(vm, njs_value_arg(&result), request->body.start, request->body.length); if (ret != NJS_OK) { njs_vm_memory_error(vm); return NJS_ERROR; } if (type == NGX_JS_BODY_JSON) { ret = njs_vm_json_parse(vm, njs_value_arg(&result), 1, njs_value_arg(&result)); } } return ngx_js_fetch_promissified_result(vm, njs_value_arg(&result), ret, retval); } static njs_int_t ngx_request_js_ext_body_used(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { ngx_js_request_t *request; request = njs_vm_external(vm, ngx_http_js_fetch_request_proto_id, value); if (request == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } njs_value_boolean_set(retval, request->body_used); return NJS_OK; } static njs_int_t ngx_request_js_ext_cache(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { ngx_js_request_t *request; request = njs_vm_external(vm, ngx_http_js_fetch_request_proto_id, value); if (request == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } return ngx_fetch_flag(vm, ngx_js_fetch_cache_modes, (njs_int_t) request->cache_mode, retval); } static njs_int_t ngx_request_js_ext_credentials(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { ngx_js_request_t *request; request = njs_vm_external(vm, ngx_http_js_fetch_request_proto_id, value); if (request == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } return ngx_fetch_flag(vm, ngx_js_fetch_credentials, (njs_int_t) request->credentials, retval); } static njs_int_t ngx_request_js_ext_headers(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { njs_int_t ret; ngx_js_request_t *request; request = njs_vm_external(vm, ngx_http_js_fetch_request_proto_id, value); if (request == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } if (njs_value_is_null(njs_value_arg(&request->header_value))) { ret = njs_vm_external_create(vm, njs_value_arg(&request->header_value), ngx_http_js_fetch_headers_proto_id, &request->headers, 0); if (ret != NJS_OK) { njs_vm_error(vm, "fetch header creation failed"); return NJS_ERROR; } } njs_value_assign(retval, &request->header_value); return NJS_OK; } static njs_int_t ngx_request_js_ext_mode(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { ngx_js_request_t *request; request = njs_vm_external(vm, ngx_http_js_fetch_request_proto_id, value); if (request == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } return ngx_fetch_flag(vm, ngx_js_fetch_modes, (njs_int_t) request->mode, retval); } static njs_int_t ngx_response_js_ext_body(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t type, njs_value_t *retval) { njs_int_t ret; njs_str_t string; ngx_js_response_t *response; njs_opaque_value_t result; response = njs_vm_external(vm, ngx_http_js_fetch_response_proto_id, njs_argument(args, 0)); if (response == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } if (response->body_used) { njs_vm_error(vm, "body stream already read"); return NJS_ERROR; } response->body_used = 1; ret = njs_chb_join(&response->chain, &string); if (ret != NJS_OK) { njs_vm_memory_error(vm); return NJS_ERROR; } switch (type) { case NGX_JS_BODY_ARRAY_BUFFER: ret = njs_vm_value_array_buffer_set(vm, njs_value_arg(&result), string.start, string.length); if (ret != NJS_OK) { njs_vm_memory_error(vm); return NJS_ERROR; } break; case NGX_JS_BODY_JSON: case NGX_JS_BODY_TEXT: default: ret = njs_vm_value_string_create(vm, njs_value_arg(&result), string.start, string.length); if (ret != NJS_OK) { njs_vm_memory_error(vm); return NJS_ERROR; } if (type == NGX_JS_BODY_JSON) { ret = njs_vm_json_parse(vm, njs_value_arg(&result), 1, retval); njs_value_assign(&result, retval); } } return ngx_js_fetch_promissified_result(vm, njs_value_arg(&result), ret, retval); } static njs_int_t ngx_response_js_ext_body_used(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { ngx_js_response_t *response; response = njs_vm_external(vm, ngx_http_js_fetch_response_proto_id, value); if (response == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } njs_value_boolean_set(retval, response->body_used); return NJS_OK; } static njs_int_t ngx_response_js_ext_headers(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { njs_int_t ret; ngx_js_response_t *response; response = njs_vm_external(vm, ngx_http_js_fetch_response_proto_id, value); if (response == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } if (njs_value_is_null(njs_value_arg(&response->header_value))) { ret = njs_vm_external_create(vm, njs_value_arg(&response->header_value), ngx_http_js_fetch_headers_proto_id, &response->headers, 0); if (ret != NJS_OK) { njs_vm_error(vm, "fetch header creation failed"); return NJS_ERROR; } } njs_value_assign(retval, &response->header_value); return NJS_OK; } static njs_int_t ngx_response_js_ext_ok(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { ngx_uint_t code; ngx_js_response_t *response; response = njs_vm_external(vm, ngx_http_js_fetch_response_proto_id, value); if (response == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } code = response->code; njs_value_boolean_set(retval, code >= 200 && code < 300); return NJS_OK; } static njs_int_t ngx_response_js_ext_status(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { ngx_js_response_t *response; response = njs_vm_external(vm, ngx_http_js_fetch_response_proto_id, value); if (response == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } njs_value_number_set(retval, response->code); return NJS_OK; } static njs_int_t ngx_response_js_ext_status_text(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { ngx_js_response_t *response; response = njs_vm_external(vm, ngx_http_js_fetch_response_proto_id, value); if (response == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } njs_vm_value_string_create(vm, retval, response->status_text.start, response->status_text.length); return NJS_OK; } static njs_int_t ngx_response_js_ext_type(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { ngx_js_response_t *response; response = njs_vm_external(vm, ngx_http_js_fetch_response_proto_id, value); if (response == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } return njs_vm_value_string_create(vm, retval, (u_char *) "basic", njs_length("basic")); } static njs_int_t ngx_fetch_flag(njs_vm_t *vm, const ngx_js_entry_t *entries, njs_int_t value, njs_value_t *retval) { const ngx_js_entry_t *e; for (e = entries; e->name.length != 0; e++) { if (e->value == value) { return njs_vm_value_string_create(vm, retval, e->name.start, e->name.length); } } return NJS_ERROR; } static njs_int_t ngx_fetch_flag_set(njs_vm_t *vm, const ngx_js_entry_t *entries, njs_value_t *value, const char *type) { njs_int_t ret; njs_str_t flag; const ngx_js_entry_t *e; ret = ngx_js_string(vm, value, &flag); if (ret != NJS_OK) { return NJS_ERROR; } for (e = entries; e->name.length != 0; e++) { if (njs_strstr_case_eq(&flag, &e->name)) { return e->value; } } njs_vm_error(vm, "unknown %s type: %V", type, &flag); return NJS_ERROR; } static njs_int_t ngx_js_fetch_function_bind(njs_vm_t *vm, const njs_str_t *name, njs_function_native_t native, njs_bool_t ctor) { njs_function_t *f; njs_opaque_value_t value; f = njs_vm_function_alloc(vm, native, 1, ctor); if (f == NULL) { return NJS_ERROR; } njs_value_function_set(njs_value_arg(&value), f); return njs_vm_bind(vm, name, njs_value_arg(&value), 1); } static njs_int_t ngx_js_fetch_init(njs_vm_t *vm) { njs_int_t ret; static const njs_str_t headers = njs_str("Headers"); static const njs_str_t request = njs_str("Request"); static const njs_str_t response = njs_str("Response"); ngx_http_js_fetch_headers_proto_id = njs_vm_external_prototype(vm, ngx_js_ext_http_headers, njs_nitems(ngx_js_ext_http_headers)); if (ngx_http_js_fetch_headers_proto_id < 0) { return NJS_ERROR; } ngx_http_js_fetch_request_proto_id = njs_vm_external_prototype(vm, ngx_js_ext_http_request, njs_nitems(ngx_js_ext_http_request)); if (ngx_http_js_fetch_request_proto_id < 0) { return NJS_ERROR; } ngx_http_js_fetch_response_proto_id = njs_vm_external_prototype(vm, ngx_js_ext_http_response, njs_nitems(ngx_js_ext_http_response)); if (ngx_http_js_fetch_response_proto_id < 0) { return NJS_ERROR; } ret = ngx_js_fetch_function_bind(vm, &headers, ngx_js_ext_headers_constructor, 1); if (ret != NJS_OK) { return NJS_ERROR; } ret = ngx_js_fetch_function_bind(vm, &request, ngx_js_ext_request_constructor, 1); if (ret != NJS_OK) { return NJS_ERROR; } ret = ngx_js_fetch_function_bind(vm, &response, ngx_js_ext_response_constructor, 1); if (ret != NJS_OK) { return NJS_ERROR; } return NJS_OK; } njs-0.8.9/nginx/ngx_js_fetch.h000066400000000000000000000005361474132077100162710ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #ifndef _NGX_JS_FETCH_H_INCLUDED_ #define _NGX_JS_FETCH_H_INCLUDED_ njs_int_t ngx_js_ext_fetch(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t level, njs_value_t *retval); extern njs_module_t ngx_js_fetch_module; #endif /* _NGX_JS_FETCH_H_INCLUDED_ */ njs-0.8.9/nginx/ngx_js_regex.c000066400000000000000000000002711474132077100163010ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #include #if (NGX_PCRE2) #define NJS_HAVE_PCRE2 1 #endif #include "../external/njs_regex.c" njs-0.8.9/nginx/ngx_js_shared_dict.c000066400000000000000000002360441474132077100174510ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #include #include #include "ngx_js.h" #include "ngx_js_shared_dict.h" typedef struct { ngx_rbtree_t rbtree; ngx_rbtree_node_t sentinel; ngx_atomic_t rwlock; ngx_rbtree_t rbtree_expire; ngx_rbtree_node_t sentinel_expire; } ngx_js_dict_sh_t; typedef struct { ngx_str_node_t sn; ngx_rbtree_node_t expire; union { ngx_str_t value; double number; } u; } ngx_js_dict_node_t; struct ngx_js_dict_s { ngx_shm_zone_t *shm_zone; ngx_js_dict_sh_t *sh; ngx_slab_pool_t *shpool; ngx_msec_t timeout; ngx_flag_t evict; #define NGX_JS_DICT_TYPE_STRING 0 #define NGX_JS_DICT_TYPE_NUMBER 1 ngx_uint_t type; ngx_js_dict_t *next; }; static njs_int_t njs_js_ext_shared_dict_capacity(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t njs_js_ext_shared_dict_clear(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t flags, njs_value_t *retval); static njs_int_t njs_js_ext_shared_dict_delete(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_js_ext_shared_dict_free_space(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_js_ext_shared_dict_get(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_js_ext_shared_dict_has(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_js_ext_shared_dict_keys(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_js_ext_shared_dict_incr(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_js_ext_shared_dict_items(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_js_ext_shared_dict_name(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t njs_js_ext_shared_dict_pop(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_js_ext_shared_dict_set(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t flags, njs_value_t *retval); static njs_int_t njs_js_ext_shared_dict_size(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_js_ext_shared_dict_type(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static ngx_js_dict_node_t *ngx_js_dict_lookup(ngx_js_dict_t *dict, njs_str_t *key); #define NGX_JS_DICT_FLAG_MUST_EXIST 1 #define NGX_JS_DICT_FLAG_MUST_NOT_EXIST 2 static ngx_int_t ngx_js_dict_set(njs_vm_t *vm, ngx_js_dict_t *dict, njs_str_t *key, njs_value_t *value, ngx_msec_t timeout, unsigned flags); static ngx_int_t ngx_js_dict_add(ngx_js_dict_t *dict, njs_str_t *key, njs_value_t *value, ngx_msec_t timeout, ngx_msec_t now); static ngx_int_t ngx_js_dict_update(ngx_js_dict_t *dict, ngx_js_dict_node_t *node, njs_value_t *value, ngx_msec_t timeout, ngx_msec_t now); static ngx_int_t ngx_js_dict_get(njs_vm_t *vm, ngx_js_dict_t *dict, njs_str_t *key, njs_value_t *retval); static ngx_int_t ngx_js_dict_incr(ngx_js_dict_t *dict, njs_str_t *key, njs_value_t *delta, njs_value_t *init, double *value, ngx_msec_t timeout); static ngx_int_t ngx_js_dict_delete(njs_vm_t *vm, ngx_js_dict_t *dict, njs_str_t *key, njs_value_t *retval); static ngx_int_t ngx_js_dict_copy_value_locked(njs_vm_t *vm, ngx_js_dict_t *dict, ngx_js_dict_node_t *node, njs_value_t *retval); static void ngx_js_dict_expire(ngx_js_dict_t *dict, ngx_msec_t now); static void ngx_js_dict_evict(ngx_js_dict_t *dict, ngx_int_t count); static njs_int_t ngx_js_dict_shared_error_name(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static ngx_int_t ngx_js_dict_init_zone(ngx_shm_zone_t *shm_zone, void *data); static njs_int_t ngx_js_shared_dict_preinit(njs_vm_t *vm); static njs_int_t ngx_js_shared_dict_init(njs_vm_t *vm); static void ngx_js_dict_node_free(ngx_js_dict_t *dict, ngx_js_dict_node_t *node); #if (NJS_HAVE_QUICKJS) static int ngx_qjs_shared_own_property(JSContext *cx, JSPropertyDescriptor *pdesc, JSValueConst obj, JSAtom prop); static int ngx_qjs_shared_own_property_names(JSContext *ctx, JSPropertyEnum **ptab, uint32_t *plen, JSValueConst obj); static JSValue ngx_qjs_ext_ngx_shared(JSContext *cx, JSValueConst this_val); static JSValue ngx_qjs_ext_shared_dict_capacity(JSContext *cx, JSValueConst this_val); static JSValue ngx_qjs_ext_shared_dict_clear(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue ngx_qjs_ext_shared_dict_delete(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue ngx_qjs_ext_shared_dict_free_space(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue ngx_qjs_ext_shared_dict_get(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue ngx_qjs_ext_shared_dict_has(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue ngx_qjs_ext_shared_dict_incr(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue ngx_qjs_ext_shared_dict_items(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue ngx_qjs_ext_shared_dict_keys(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue ngx_qjs_ext_shared_dict_name(JSContext *cx, JSValueConst this_val); static JSValue ngx_qjs_ext_shared_dict_pop(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue ngx_qjs_ext_shared_dict_set(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int flags); static JSValue ngx_qjs_ext_shared_dict_size(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue ngx_qjs_ext_shared_dict_tag(JSContext *cx, JSValueConst this_val); static JSValue ngx_qjs_ext_shared_dict_type(JSContext *cx, JSValueConst this_val); static JSValue ngx_qjs_dict_copy_value_locked(JSContext *cx, ngx_js_dict_t *dict, ngx_js_dict_node_t *node); static ngx_js_dict_node_t *ngx_qjs_dict_lookup(ngx_js_dict_t *dict, ngx_str_t *key); static ngx_int_t ngx_qjs_dict_add(JSContext *cx, ngx_js_dict_t *dict, ngx_str_t *key, JSValue value, ngx_msec_t timeout, ngx_msec_t now); static JSValue ngx_qjs_dict_delete(JSContext *cx, ngx_js_dict_t *dict, ngx_str_t *key, int retval); static JSValue ngx_qjs_dict_get(JSContext *cx, ngx_js_dict_t *dict, ngx_str_t *key); static JSValue ngx_qjs_dict_incr(JSContext *cx, ngx_js_dict_t *dict, ngx_str_t *key, double delta, double init, ngx_msec_t timeout); static JSValue ngx_qjs_dict_set(JSContext *cx, ngx_js_dict_t *dict, ngx_str_t *key, JSValue value, ngx_msec_t timeout, unsigned flags); static ngx_int_t ngx_qjs_dict_update(JSContext *cx, ngx_js_dict_t *dict, ngx_js_dict_node_t *node, JSValue value, ngx_msec_t timeout, ngx_msec_t now); static JSValue ngx_qjs_throw_shared_memory_error(JSContext *cx); static JSModuleDef *ngx_qjs_ngx_shared_dict_init(JSContext *cx, const char *name); #endif static njs_external_t ngx_js_ext_shared_dict[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "SharedDict", } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("add"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_js_ext_shared_dict_set, .magic8 = NGX_JS_DICT_FLAG_MUST_NOT_EXIST, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("capacity"), .enumerable = 1, .u.property = { .handler = njs_js_ext_shared_dict_capacity, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("clear"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_js_ext_shared_dict_clear, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("freeSpace"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_js_ext_shared_dict_free_space, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("delete"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_js_ext_shared_dict_delete, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("incr"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_js_ext_shared_dict_incr, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("items"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_js_ext_shared_dict_items, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("get"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_js_ext_shared_dict_get, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("has"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_js_ext_shared_dict_has, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("keys"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_js_ext_shared_dict_keys, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("name"), .enumerable = 1, .u.property = { .handler = njs_js_ext_shared_dict_name, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("pop"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_js_ext_shared_dict_pop, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("replace"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_js_ext_shared_dict_set, .magic8 = NGX_JS_DICT_FLAG_MUST_EXIST, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("set"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_js_ext_shared_dict_set, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("size"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_js_ext_shared_dict_size, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("type"), .enumerable = 1, .u.property = { .handler = njs_js_ext_shared_dict_type, } }, }; static njs_external_t ngx_js_ext_error_ctor_props[] = { { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("name"), .enumerable = 1, .u.property = { .handler = ngx_js_dict_shared_error_name, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("prototype"), .enumerable = 1, .u.property = { .handler = njs_object_prototype_create, } }, }; static njs_external_t ngx_js_ext_error_proto_props[] = { { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("name"), .enumerable = 1, .u.property = { .handler = ngx_js_dict_shared_error_name, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("prototype"), .enumerable = 1, .u.property = { .handler = njs_object_prototype_create, } }, }; static njs_int_t ngx_js_shared_dict_proto_id; static njs_int_t ngx_js_shared_dict_error_id; njs_module_t ngx_js_shared_dict_module = { .name = njs_str("shared_dict"), .preinit = ngx_js_shared_dict_preinit, .init = ngx_js_shared_dict_init, }; #if (NJS_HAVE_QUICKJS) static const JSCFunctionListEntry ngx_qjs_ext_ngx[] = { JS_CGETSET_DEF("shared", ngx_qjs_ext_ngx_shared, NULL), }; static const JSCFunctionListEntry ngx_qjs_ext_shared_dict[] = { JS_CGETSET_DEF("[Symbol.toStringTag]", ngx_qjs_ext_shared_dict_tag, NULL), JS_CFUNC_MAGIC_DEF("add", 3, ngx_qjs_ext_shared_dict_set, NGX_JS_DICT_FLAG_MUST_NOT_EXIST), JS_CGETSET_DEF("capacity", ngx_qjs_ext_shared_dict_capacity, NULL), JS_CFUNC_DEF("clear", 0, ngx_qjs_ext_shared_dict_clear), JS_CFUNC_DEF("delete", 1, ngx_qjs_ext_shared_dict_delete), JS_CFUNC_DEF("freeSpace", 0, ngx_qjs_ext_shared_dict_free_space), JS_CFUNC_DEF("get", 1, ngx_qjs_ext_shared_dict_get), JS_CFUNC_DEF("has", 1, ngx_qjs_ext_shared_dict_has), JS_CFUNC_DEF("incr", 3, ngx_qjs_ext_shared_dict_incr), JS_CFUNC_DEF("items", 0, ngx_qjs_ext_shared_dict_items), JS_CFUNC_DEF("keys", 0, ngx_qjs_ext_shared_dict_keys), JS_CGETSET_DEF("name", ngx_qjs_ext_shared_dict_name, NULL), JS_CFUNC_DEF("pop", 1, ngx_qjs_ext_shared_dict_pop), JS_CFUNC_MAGIC_DEF("replace", 3, ngx_qjs_ext_shared_dict_set, NGX_JS_DICT_FLAG_MUST_EXIST), JS_CFUNC_MAGIC_DEF("set", 3, ngx_qjs_ext_shared_dict_set, 0), JS_CFUNC_DEF("size", 0, ngx_qjs_ext_shared_dict_size), JS_CGETSET_DEF("type", ngx_qjs_ext_shared_dict_type, NULL), }; static const JSCFunctionListEntry ngx_qjs_ext_shared_dict_error[] = { JS_PROP_STRING_DEF("name", "SharedMemoryError", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE), JS_PROP_STRING_DEF("message", "", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE), }; static JSClassDef ngx_qjs_shared_dict_class = { "SharedDict", .finalizer = NULL, }; static JSClassDef ngx_qjs_shared_class = { "Shared", .finalizer = NULL, .exotic = & (JSClassExoticMethods) { .get_own_property = ngx_qjs_shared_own_property, .get_own_property_names = ngx_qjs_shared_own_property_names, }, }; qjs_module_t ngx_qjs_ngx_shared_dict_module = { .name = "shared_dict", .init = ngx_qjs_ngx_shared_dict_init, }; #endif njs_int_t njs_js_ext_global_shared_prop(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { njs_int_t ret; njs_str_t name; ngx_js_dict_t *dict; ngx_shm_zone_t *shm_zone; ngx_js_main_conf_t *conf; ret = njs_vm_prop_name(vm, prop, &name); if (ret != NJS_OK) { return NJS_ERROR; } conf = ngx_main_conf(vm); for (dict = conf->dicts; dict != NULL; dict = dict->next) { shm_zone = dict->shm_zone; if (shm_zone->shm.name.len == name.length && ngx_strncmp(shm_zone->shm.name.data, name.start, name.length) == 0) { ret = njs_vm_external_create(vm, retval, ngx_js_shared_dict_proto_id, shm_zone, 0); if (ret != NJS_OK) { njs_vm_internal_error(vm, "sharedDict creation failed"); return NJS_ERROR; } return NJS_OK; } } njs_value_null_set(retval); return NJS_DECLINED; } njs_int_t njs_js_ext_global_shared_keys(njs_vm_t *vm, njs_value_t *unused, njs_value_t *keys) { njs_int_t rc; njs_value_t *value; ngx_js_dict_t *dict; ngx_shm_zone_t *shm_zone; ngx_js_main_conf_t *conf; conf = ngx_main_conf(vm); rc = njs_vm_array_alloc(vm, keys, 4); if (rc != NJS_OK) { return NJS_ERROR; } for (dict = conf->dicts; dict != NULL; dict = dict->next) { shm_zone = dict->shm_zone; value = njs_vm_array_push(vm, keys); if (value == NULL) { return NJS_ERROR; } rc = njs_vm_value_string_create(vm, value, shm_zone->shm.name.data, shm_zone->shm.name.len); if (rc != NJS_OK) { return NJS_ERROR; } } return NJS_OK; } static njs_int_t njs_js_ext_shared_dict_capacity(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { ngx_shm_zone_t *shm_zone; shm_zone = njs_vm_external(vm, ngx_js_shared_dict_proto_id, value); if (shm_zone == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } njs_value_number_set(retval, shm_zone->shm.size); return NJS_OK; } static njs_int_t njs_js_ext_shared_dict_clear(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { ngx_rbtree_t *rbtree; ngx_js_dict_t *dict; ngx_shm_zone_t *shm_zone; ngx_rbtree_node_t *rn, *next; shm_zone = njs_vm_external(vm, ngx_js_shared_dict_proto_id, njs_argument(args, 0)); if (shm_zone == NULL) { njs_vm_type_error(vm, "\"this\" is not a shared dict"); return NJS_ERROR; } dict = shm_zone->data; ngx_rwlock_wlock(&dict->sh->rwlock); if (dict->timeout) { ngx_js_dict_evict(dict, 0x7fffffff /* INT_MAX */); } else { rbtree = &dict->sh->rbtree; if (rbtree->root == rbtree->sentinel) { goto done; } for (rn = ngx_rbtree_min(rbtree->root, rbtree->sentinel); rn != NULL; rn = next) { next = ngx_rbtree_next(rbtree, rn); ngx_rbtree_delete(rbtree, rn); ngx_js_dict_node_free(dict, (ngx_js_dict_node_t *) rn); } } done: ngx_rwlock_unlock(&dict->sh->rwlock); njs_value_undefined_set(retval); return NJS_OK; } static njs_int_t njs_js_ext_shared_dict_free_space(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { size_t bytes; ngx_js_dict_t *dict; ngx_shm_zone_t *shm_zone; shm_zone = njs_vm_external(vm, ngx_js_shared_dict_proto_id, njs_argument(args, 0)); if (shm_zone == NULL) { njs_vm_type_error(vm, "\"this\" is not a shared dict"); return NJS_ERROR; } dict = shm_zone->data; ngx_rwlock_rlock(&dict->sh->rwlock); bytes = dict->shpool->pfree * ngx_pagesize; ngx_rwlock_unlock(&dict->sh->rwlock); njs_value_number_set(retval, bytes); return NJS_OK; } static njs_int_t njs_js_ext_shared_dict_delete(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { ngx_int_t rc; njs_str_t key; ngx_shm_zone_t *shm_zone; shm_zone = njs_vm_external(vm, ngx_js_shared_dict_proto_id, njs_argument(args, 0)); if (shm_zone == NULL) { njs_vm_type_error(vm, "\"this\" is not a shared dict"); return NJS_ERROR; } if (ngx_js_string(vm, njs_arg(args, nargs, 1), &key) != NGX_OK) { return NJS_ERROR; } rc = ngx_js_dict_delete(vm, shm_zone->data, &key, NULL); njs_value_boolean_set(retval, rc == NGX_OK); return NJS_OK; } static njs_int_t njs_js_ext_shared_dict_get(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { ngx_int_t rc; njs_str_t key; ngx_shm_zone_t *shm_zone; shm_zone = njs_vm_external(vm, ngx_js_shared_dict_proto_id, njs_argument(args, 0)); if (shm_zone == NULL) { njs_vm_type_error(vm, "\"this\" is not a shared dict"); return NJS_ERROR; } if (ngx_js_string(vm, njs_arg(args, nargs, 1), &key) != NGX_OK) { return NJS_ERROR; } rc = ngx_js_dict_get(vm, shm_zone->data, &key, retval); if (njs_slow_path(rc == NGX_ERROR)) { njs_vm_error(vm, "failed to get value from shared dict"); return NJS_ERROR; } return NJS_OK; } static njs_int_t njs_js_ext_shared_dict_has(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_str_t key; ngx_msec_t now; ngx_time_t *tp; ngx_js_dict_t *dict; ngx_shm_zone_t *shm_zone; ngx_js_dict_node_t *node; shm_zone = njs_vm_external(vm, ngx_js_shared_dict_proto_id, njs_argument(args, 0)); if (shm_zone == NULL) { njs_vm_type_error(vm, "\"this\" is not a shared dict"); return NJS_ERROR; } if (ngx_js_string(vm, njs_arg(args, nargs, 1), &key) != NGX_OK) { return NJS_ERROR; } dict = shm_zone->data; ngx_rwlock_rlock(&dict->sh->rwlock); node = ngx_js_dict_lookup(dict, &key); if (node != NULL && dict->timeout) { tp = ngx_timeofday(); now = tp->sec * 1000 + tp->msec; if (now >= node->expire.key) { node = NULL; } } ngx_rwlock_unlock(&dict->sh->rwlock); njs_value_boolean_set(retval, node != NULL); return NJS_OK; } static njs_int_t njs_js_ext_shared_dict_keys(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t rc; ngx_int_t max_count; ngx_msec_t now; ngx_time_t *tp; njs_value_t *value; ngx_rbtree_t *rbtree; ngx_js_dict_t *dict; ngx_shm_zone_t *shm_zone; ngx_rbtree_node_t *rn; ngx_js_dict_node_t *node; shm_zone = njs_vm_external(vm, ngx_js_shared_dict_proto_id, njs_argument(args, 0)); if (shm_zone == NULL) { njs_vm_type_error(vm, "\"this\" is not a shared dict"); return NJS_ERROR; } dict = shm_zone->data; max_count = 1024; if (nargs > 1) { if (ngx_js_integer(vm, njs_arg(args, nargs, 1), &max_count) != NGX_OK) { return NJS_ERROR; } } rc = njs_vm_array_alloc(vm, retval, 8); if (rc != NJS_OK) { return NJS_ERROR; } ngx_rwlock_rlock(&dict->sh->rwlock); if (dict->timeout) { tp = ngx_timeofday(); now = tp->sec * 1000 + tp->msec; ngx_js_dict_expire(dict, now); } rbtree = &dict->sh->rbtree; if (rbtree->root == rbtree->sentinel) { goto done; } for (rn = ngx_rbtree_min(rbtree->root, rbtree->sentinel); rn != NULL; rn = ngx_rbtree_next(rbtree, rn)) { if (max_count-- == 0) { break; } node = (ngx_js_dict_node_t *) rn; value = njs_vm_array_push(vm, retval); if (value == NULL) { goto fail; } rc = njs_vm_value_string_create(vm, value, node->sn.str.data, node->sn.str.len); if (rc != NJS_OK) { goto fail; } } done: ngx_rwlock_unlock(&dict->sh->rwlock); return NJS_OK; fail: ngx_rwlock_unlock(&dict->sh->rwlock); return NJS_ERROR; } static njs_int_t njs_js_ext_shared_dict_incr(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double value; ngx_int_t rc; njs_str_t key; ngx_msec_t timeout; njs_value_t *delta, *init, *timeo; ngx_js_dict_t *dict; ngx_shm_zone_t *shm_zone; njs_opaque_value_t lvalue; shm_zone = njs_vm_external(vm, ngx_js_shared_dict_proto_id, njs_argument(args, 0)); if (shm_zone == NULL) { njs_vm_type_error(vm, "\"this\" is not a shared dict"); return NJS_ERROR; } dict = shm_zone->data; if (dict->type != NGX_JS_DICT_TYPE_NUMBER) { njs_vm_type_error(vm, "shared dict is not a number dict"); return NJS_ERROR; } if (ngx_js_string(vm, njs_arg(args, nargs, 1), &key) != NGX_OK) { return NJS_ERROR; } delta = njs_arg(args, nargs, 2); if (!njs_value_is_number(delta)) { njs_vm_type_error(vm, "delta is not a number"); return NJS_ERROR; } init = njs_lvalue_arg(njs_value_arg(&lvalue), args, nargs, 3); if (!njs_value_is_number(init) && !njs_value_is_undefined(init)) { njs_vm_type_error(vm, "init value is not a number"); return NJS_ERROR; } if (njs_value_is_undefined(init)) { njs_value_number_set(init, 0); } timeo = njs_arg(args, nargs, 4); if (!njs_value_is_undefined(timeo)) { if (!njs_value_is_number(timeo)) { njs_vm_type_error(vm, "timeout is not a number"); return NJS_ERROR; } if (!dict->timeout) { njs_vm_type_error(vm, "shared dict must be declared with timeout"); return NJS_ERROR; } timeout = (ngx_msec_t) njs_value_number(timeo); if (timeout < 1) { njs_vm_type_error(vm, "timeout must be greater than or equal to 1"); return NJS_ERROR; } } else { timeout = dict->timeout; } rc = ngx_js_dict_incr(shm_zone->data, &key, delta, init, &value, timeout); if (rc == NGX_ERROR) { njs_vm_error(vm, "failed to increment value in shared dict"); return NJS_ERROR; } njs_value_number_set(retval, value); return NJS_OK; } static njs_int_t njs_js_ext_shared_dict_items(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t rc; ngx_int_t max_count; ngx_msec_t now; ngx_time_t *tp; njs_value_t *value, *kv; ngx_rbtree_t *rbtree; ngx_js_dict_t *dict; ngx_shm_zone_t *shm_zone; ngx_rbtree_node_t *rn; ngx_js_dict_node_t *node; shm_zone = njs_vm_external(vm, ngx_js_shared_dict_proto_id, njs_argument(args, 0)); if (shm_zone == NULL) { njs_vm_type_error(vm, "\"this\" is not a shared dict"); return NJS_ERROR; } dict = shm_zone->data; max_count = 1024; if (nargs > 1) { if (ngx_js_integer(vm, njs_arg(args, nargs, 1), &max_count) != NGX_OK) { return NJS_ERROR; } } rc = njs_vm_array_alloc(vm, retval, 8); if (rc != NJS_OK) { return NJS_ERROR; } ngx_rwlock_rlock(&dict->sh->rwlock); if (dict->timeout) { tp = ngx_timeofday(); now = tp->sec * 1000 + tp->msec; ngx_js_dict_expire(dict, now); } rbtree = &dict->sh->rbtree; if (rbtree->root == rbtree->sentinel) { goto done; } for (rn = ngx_rbtree_min(rbtree->root, rbtree->sentinel); rn != NULL; rn = ngx_rbtree_next(rbtree, rn)) { if (max_count-- == 0) { break; } node = (ngx_js_dict_node_t *) rn; kv = njs_vm_array_push(vm, retval); if (kv == NULL) { goto fail; } rc = njs_vm_array_alloc(vm, kv, 2); if (rc != NJS_OK) { goto fail; } value = njs_vm_array_push(vm, kv); if (value == NULL) { goto fail; } rc = njs_vm_value_string_create(vm, value, node->sn.str.data, node->sn.str.len); if (rc != NJS_OK) { goto fail; } value = njs_vm_array_push(vm, kv); if (value == NULL) { goto fail; } rc = ngx_js_dict_copy_value_locked(vm, dict, node, value); if (rc != NJS_OK) { goto fail; } } done: ngx_rwlock_unlock(&dict->sh->rwlock); return NJS_OK; fail: ngx_rwlock_unlock(&dict->sh->rwlock); return NJS_ERROR; } static njs_int_t njs_js_ext_shared_dict_name(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { ngx_shm_zone_t *shm_zone; shm_zone = njs_vm_external(vm, ngx_js_shared_dict_proto_id, value); if (shm_zone == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } return njs_vm_value_string_create(vm, retval, shm_zone->shm.name.data, shm_zone->shm.name.len); } static njs_int_t njs_js_ext_shared_dict_pop(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { ngx_int_t rc; njs_str_t key; ngx_shm_zone_t *shm_zone; shm_zone = njs_vm_external(vm, ngx_js_shared_dict_proto_id, njs_argument(args, 0)); if (shm_zone == NULL) { njs_vm_type_error(vm, "\"this\" is not a shared dict"); return NJS_ERROR; } if (ngx_js_string(vm, njs_arg(args, nargs, 1), &key) != NGX_OK) { return NJS_ERROR; } rc = ngx_js_dict_delete(vm, shm_zone->data, &key, retval); if (rc == NGX_DECLINED) { njs_value_undefined_set(retval); } return (rc != NGX_ERROR) ? NJS_OK : NJS_ERROR; } static njs_int_t njs_js_ext_shared_dict_set(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t flags, njs_value_t *retval) { njs_str_t key; ngx_int_t rc; ngx_msec_t timeout; njs_value_t *value, *timeo; ngx_js_dict_t *dict; ngx_shm_zone_t *shm_zone; shm_zone = njs_vm_external(vm, ngx_js_shared_dict_proto_id, njs_argument(args, 0)); if (shm_zone == NULL) { njs_vm_type_error(vm, "\"this\" is not a shared dict"); return NJS_ERROR; } if (ngx_js_string(vm, njs_arg(args, nargs, 1), &key) != NGX_OK) { return NJS_ERROR; } dict = shm_zone->data; value = njs_arg(args, nargs, 2); if (dict->type == NGX_JS_DICT_TYPE_STRING) { if (!njs_value_is_string(value)) { njs_vm_type_error(vm, "string value is expected"); return NJS_ERROR; } } else { if (!njs_value_is_number(value)) { njs_vm_type_error(vm, "number value is expected"); return NJS_ERROR; } } timeo = njs_arg(args, nargs, 3); if (!njs_value_is_undefined(timeo)) { if (!njs_value_is_number(timeo)) { njs_vm_type_error(vm, "timeout is not a number"); return NJS_ERROR; } if (!dict->timeout) { njs_vm_type_error(vm, "shared dict must be declared with timeout"); return NJS_ERROR; } timeout = (ngx_msec_t) njs_value_number(timeo); if (timeout < 1) { njs_vm_type_error(vm, "timeout must be greater than or equal to 1"); return NJS_ERROR; } } else { timeout = dict->timeout; } rc = ngx_js_dict_set(vm, shm_zone->data, &key, value, timeout, flags); if (rc == NGX_ERROR) { return NJS_ERROR; } if (flags) { /* add() or replace(). */ njs_value_boolean_set(retval, rc == NGX_OK); } else { njs_value_assign(retval, njs_argument(args, 0)); } return NJS_OK; } static njs_int_t njs_js_ext_shared_dict_size(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t items; ngx_msec_t now; ngx_time_t *tp; ngx_rbtree_t *rbtree; ngx_js_dict_t *dict; ngx_shm_zone_t *shm_zone; ngx_rbtree_node_t *rn; shm_zone = njs_vm_external(vm, ngx_js_shared_dict_proto_id, njs_argument(args, 0)); if (shm_zone == NULL) { njs_vm_type_error(vm, "\"this\" is not a shared dict"); return NJS_ERROR; } dict = shm_zone->data; ngx_rwlock_rlock(&dict->sh->rwlock); if (dict->timeout) { tp = ngx_timeofday(); now = tp->sec * 1000 + tp->msec; ngx_js_dict_expire(dict, now); } rbtree = &dict->sh->rbtree; if (rbtree->root == rbtree->sentinel) { ngx_rwlock_unlock(&dict->sh->rwlock); njs_value_number_set(retval, 0); return NJS_OK; } items = 0; for (rn = ngx_rbtree_min(rbtree->root, rbtree->sentinel); rn != NULL; rn = ngx_rbtree_next(rbtree, rn)) { items++; } ngx_rwlock_unlock(&dict->sh->rwlock); njs_value_number_set(retval, items); return NJS_OK; } static njs_int_t njs_js_ext_shared_dict_type(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { njs_str_t type; ngx_js_dict_t *dict; ngx_shm_zone_t *shm_zone; shm_zone = njs_vm_external(vm, ngx_js_shared_dict_proto_id, value); if (shm_zone == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } dict = shm_zone->data; switch (dict->type) { case NGX_JS_DICT_TYPE_STRING: type = njs_str_value("string"); break; default: type = njs_str_value("number"); break; } return njs_vm_value_string_create(vm, retval, type.start, type.length); } static ngx_js_dict_node_t * ngx_js_dict_lookup(ngx_js_dict_t *dict, njs_str_t *key) { uint32_t hash; ngx_str_t k; ngx_rbtree_t *rbtree; rbtree = &dict->sh->rbtree; hash = ngx_crc32_long(key->start, key->length); k.data = key->start; k.len = key->length; return (ngx_js_dict_node_t *) ngx_str_rbtree_lookup(rbtree, &k, hash); } static void * ngx_js_dict_alloc(ngx_js_dict_t *dict, size_t n) { void *p; p = ngx_slab_alloc_locked(dict->shpool, n); if (p == NULL && dict->evict) { ngx_js_dict_evict(dict, 16); p = ngx_slab_alloc_locked(dict->shpool, n); } return p; } static void ngx_js_dict_node_free(ngx_js_dict_t *dict, ngx_js_dict_node_t *node) { ngx_slab_pool_t *shpool; shpool = dict->shpool; if (dict->type == NGX_JS_DICT_TYPE_STRING) { ngx_slab_free_locked(shpool, node->u.value.data); } ngx_slab_free_locked(shpool, node); } static ngx_int_t ngx_js_dict_set(njs_vm_t *vm, ngx_js_dict_t *dict, njs_str_t *key, njs_value_t *value, ngx_msec_t timeout, unsigned flags) { ngx_msec_t now; ngx_time_t *tp; ngx_js_dict_node_t *node; tp = ngx_timeofday(); now = tp->sec * 1000 + tp->msec; ngx_rwlock_wlock(&dict->sh->rwlock); node = ngx_js_dict_lookup(dict, key); if (node == NULL) { if (flags & NGX_JS_DICT_FLAG_MUST_EXIST) { ngx_rwlock_unlock(&dict->sh->rwlock); return NGX_DECLINED; } if (ngx_js_dict_add(dict, key, value, timeout, now) != NGX_OK) { goto memory_error; } } else { if (flags & NGX_JS_DICT_FLAG_MUST_NOT_EXIST) { if (!dict->timeout || now < node->expire.key) { ngx_rwlock_unlock(&dict->sh->rwlock); return NGX_DECLINED; } } if (ngx_js_dict_update(dict, node, value, timeout, now) != NGX_OK) { goto memory_error; } } ngx_rwlock_unlock(&dict->sh->rwlock); return NGX_OK; memory_error: ngx_rwlock_unlock(&dict->sh->rwlock); njs_vm_error3(vm, ngx_js_shared_dict_error_id, "", 0); return NGX_ERROR; } static ngx_int_t ngx_js_dict_add(ngx_js_dict_t *dict, njs_str_t *key, njs_value_t *value, ngx_msec_t timeout, ngx_msec_t now) { size_t n; uint32_t hash; njs_str_t string; ngx_js_dict_node_t *node; if (dict->timeout) { ngx_js_dict_expire(dict, now); } n = sizeof(ngx_js_dict_node_t) + key->length; hash = ngx_crc32_long(key->start, key->length); node = ngx_js_dict_alloc(dict, n); if (node == NULL) { return NGX_ERROR; } node->sn.str.data = (u_char *) node + sizeof(ngx_js_dict_node_t); if (dict->type == NGX_JS_DICT_TYPE_STRING) { njs_value_string_get(value, &string); node->u.value.data = ngx_js_dict_alloc(dict, string.length); if (node->u.value.data == NULL) { ngx_slab_free_locked(dict->shpool, node); return NGX_ERROR; } ngx_memcpy(node->u.value.data, string.start, string.length); node->u.value.len = string.length; } else { node->u.number = njs_value_number(value); } node->sn.node.key = hash; ngx_memcpy(node->sn.str.data, key->start, key->length); node->sn.str.len = key->length; ngx_rbtree_insert(&dict->sh->rbtree, &node->sn.node); if (dict->timeout) { node->expire.key = now + timeout; ngx_rbtree_insert(&dict->sh->rbtree_expire, &node->expire); } return NGX_OK; } static ngx_int_t ngx_js_dict_update(ngx_js_dict_t *dict, ngx_js_dict_node_t *node, njs_value_t *value, ngx_msec_t timeout, ngx_msec_t now) { u_char *p; njs_str_t string; if (dict->type == NGX_JS_DICT_TYPE_STRING) { njs_value_string_get(value, &string); p = ngx_js_dict_alloc(dict, string.length); if (p == NULL) { return NGX_ERROR; } ngx_slab_free_locked(dict->shpool, node->u.value.data); ngx_memcpy(p, string.start, string.length); node->u.value.data = p; node->u.value.len = string.length; } else { node->u.number = njs_value_number(value); } if (dict->timeout) { ngx_rbtree_delete(&dict->sh->rbtree_expire, &node->expire); node->expire.key = now + timeout; ngx_rbtree_insert(&dict->sh->rbtree_expire, &node->expire); } return NGX_OK; } static ngx_int_t ngx_js_dict_delete(njs_vm_t *vm, ngx_js_dict_t *dict, njs_str_t *key, njs_value_t *retval) { ngx_int_t rc; ngx_msec_t now; ngx_time_t *tp; ngx_js_dict_node_t *node; ngx_rwlock_wlock(&dict->sh->rwlock); node = ngx_js_dict_lookup(dict, key); if (node == NULL) { ngx_rwlock_unlock(&dict->sh->rwlock); return NGX_DECLINED; } if (dict->timeout) { ngx_rbtree_delete(&dict->sh->rbtree_expire, &node->expire); } ngx_rbtree_delete(&dict->sh->rbtree, (ngx_rbtree_node_t *) node); if (retval != NULL) { tp = ngx_timeofday(); now = tp->sec * 1000 + tp->msec; if (!dict->timeout || now < node->expire.key) { rc = ngx_js_dict_copy_value_locked(vm, dict, node, retval); } else { rc = NGX_DECLINED; } } else { rc = NGX_OK; } ngx_js_dict_node_free(dict, node); ngx_rwlock_unlock(&dict->sh->rwlock); return rc; } static ngx_int_t ngx_js_dict_incr(ngx_js_dict_t *dict, njs_str_t *key, njs_value_t *delta, njs_value_t *init, double *value, ngx_msec_t timeout) { ngx_msec_t now; ngx_time_t *tp; ngx_js_dict_node_t *node; tp = ngx_timeofday(); now = tp->sec * 1000 + tp->msec; ngx_rwlock_wlock(&dict->sh->rwlock); node = ngx_js_dict_lookup(dict, key); if (node == NULL) { njs_value_number_set(init, njs_value_number(init) + njs_value_number(delta)); if (ngx_js_dict_add(dict, key, init, timeout, now) != NGX_OK) { ngx_rwlock_unlock(&dict->sh->rwlock); return NGX_ERROR; } *value = njs_value_number(init); } else { node->u.number += njs_value_number(delta); *value = node->u.number; if (dict->timeout) { ngx_rbtree_delete(&dict->sh->rbtree_expire, &node->expire); node->expire.key = now + timeout; ngx_rbtree_insert(&dict->sh->rbtree_expire, &node->expire); } } ngx_rwlock_unlock(&dict->sh->rwlock); return NGX_OK; } static ngx_int_t ngx_js_dict_get(njs_vm_t *vm, ngx_js_dict_t *dict, njs_str_t *key, njs_value_t *retval) { ngx_int_t rc; ngx_msec_t now; ngx_time_t *tp; ngx_js_dict_node_t *node; ngx_rwlock_rlock(&dict->sh->rwlock); node = ngx_js_dict_lookup(dict, key); if (node == NULL) { goto not_found; } if (dict->timeout) { tp = ngx_timeofday(); now = tp->sec * 1000 + tp->msec; if (now >= node->expire.key) { goto not_found; } } rc = ngx_js_dict_copy_value_locked(vm, dict, node, retval); ngx_rwlock_unlock(&dict->sh->rwlock); return rc; not_found: ngx_rwlock_unlock(&dict->sh->rwlock); njs_value_undefined_set(retval); return NGX_OK; } static ngx_int_t ngx_js_dict_copy_value_locked(njs_vm_t *vm, ngx_js_dict_t *dict, ngx_js_dict_node_t *node, njs_value_t *retval) { njs_int_t ret; ngx_uint_t type; type = dict->type; if (type == NGX_JS_DICT_TYPE_STRING) { ret = njs_vm_value_string_create(vm, retval, node->u.value.data, node->u.value.len); if (ret != NJS_OK) { return NGX_ERROR; } } else { njs_value_number_set(retval, node->u.number); } return NGX_OK; } static void ngx_js_dict_expire(ngx_js_dict_t *dict, ngx_msec_t now) { ngx_rbtree_t *rbtree; ngx_rbtree_node_t *rn, *next; ngx_js_dict_node_t *node; rbtree = &dict->sh->rbtree_expire; if (rbtree->root == rbtree->sentinel) { return; } for (rn = ngx_rbtree_min(rbtree->root, rbtree->sentinel); rn != NULL; rn = next) { if (rn->key > now) { return; } node = (ngx_js_dict_node_t *) ((char *) rn - offsetof(ngx_js_dict_node_t, expire)); next = ngx_rbtree_next(rbtree, rn); ngx_rbtree_delete(rbtree, rn); ngx_rbtree_delete(&dict->sh->rbtree, (ngx_rbtree_node_t *) node); ngx_js_dict_node_free(dict, node); } } static void ngx_js_dict_evict(ngx_js_dict_t *dict, ngx_int_t count) { ngx_rbtree_t *rbtree; ngx_rbtree_node_t *rn, *next; ngx_js_dict_node_t *node; rbtree = &dict->sh->rbtree_expire; if (rbtree->root == rbtree->sentinel) { return; } for (rn = ngx_rbtree_min(rbtree->root, rbtree->sentinel); rn != NULL; rn = next) { if (count-- == 0) { return; } node = (ngx_js_dict_node_t *) ((char *) rn - offsetof(ngx_js_dict_node_t, expire)); next = ngx_rbtree_next(rbtree, rn); ngx_rbtree_delete(rbtree, rn); ngx_rbtree_delete(&dict->sh->rbtree, (ngx_rbtree_node_t *) node); ngx_js_dict_node_free(dict, node); } } static njs_int_t ngx_js_dict_shared_error_name(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { return njs_vm_value_string_create(vm, retval, (u_char *) "SharedMemoryError", 17); } static ngx_int_t ngx_js_dict_init_zone(ngx_shm_zone_t *shm_zone, void *data) { ngx_js_dict_t *prev = data; size_t len; ngx_js_dict_t *dict; dict = shm_zone->data; if (prev) { if (dict->timeout && !prev->timeout) { ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0, "js_shared_dict_zone \"%V\" uses timeout %M " "while previously it did not use timeout", &shm_zone->shm.name, dict->timeout); return NGX_ERROR; } if (dict->type != prev->type) { ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0, "js_shared_dict_zone \"%V\" had previously a " "different type", &shm_zone->shm.name, dict->timeout); return NGX_ERROR; } dict->sh = prev->sh; dict->shpool = prev->shpool; return NGX_OK; } dict->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; if (shm_zone->shm.exists) { dict->sh = dict->shpool->data; return NGX_OK; } dict->sh = ngx_slab_calloc(dict->shpool, sizeof(ngx_js_dict_sh_t)); if (dict->sh == NULL) { return NGX_ERROR; } dict->shpool->data = dict->sh; ngx_rbtree_init(&dict->sh->rbtree, &dict->sh->sentinel, ngx_str_rbtree_insert_value); if (dict->timeout) { ngx_rbtree_init(&dict->sh->rbtree_expire, &dict->sh->sentinel_expire, ngx_rbtree_insert_timer_value); } len = sizeof(" in js shared dict zone \"\"") + shm_zone->shm.name.len; dict->shpool->log_ctx = ngx_slab_alloc(dict->shpool, len); if (dict->shpool->log_ctx == NULL) { return NGX_ERROR; } ngx_sprintf(dict->shpool->log_ctx, " in js shared zone \"%V\"%Z", &shm_zone->shm.name); return NGX_OK; } char * ngx_js_shared_dict_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf, void *tag) { ngx_js_main_conf_t *jmcf = conf; u_char *p; ssize_t size; ngx_str_t *value, name, s; ngx_flag_t evict; ngx_msec_t timeout; ngx_uint_t i, type; ngx_js_dict_t *dict; ngx_shm_zone_t *shm_zone; size = 0; evict = 0; timeout = 0; name.len = 0; type = NGX_JS_DICT_TYPE_STRING; value = cf->args->elts; for (i = 1; i < cf->args->nelts; i++) { if (ngx_strncmp(value[i].data, "zone=", 5) == 0) { name.data = value[i].data + 5; p = (u_char *) ngx_strchr(name.data, ':'); if (p == NULL) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid zone size \"%V\"", &value[i]); return NGX_CONF_ERROR; } name.len = p - name.data; if (name.len == 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid zone name \"%V\"", &value[i]); return NGX_CONF_ERROR; } s.data = p + 1; s.len = value[i].data + value[i].len - s.data; size = ngx_parse_size(&s); if (size == NGX_ERROR) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid zone size \"%V\"", &value[i]); return NGX_CONF_ERROR; } if (size < (ssize_t) (8 * ngx_pagesize)) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "zone \"%V\" is too small", &value[i]); return NGX_CONF_ERROR; } continue; } if (ngx_strncmp(value[i].data, "evict", 5) == 0) { evict = 1; continue; } if (ngx_strncmp(value[i].data, "timeout=", 8) == 0) { s.data = value[i].data + 8; s.len = value[i].len - 8; timeout = ngx_parse_time(&s, 0); if (timeout == (ngx_msec_t) NGX_ERROR) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid timeout value \"%V\"", &value[i]); return NGX_CONF_ERROR; } continue; } if (ngx_strncmp(value[i].data, "type=", 5) == 0) { if (ngx_strcmp(&value[i].data[5], "string") == 0) { type = NGX_JS_DICT_TYPE_STRING; } else if (ngx_strcmp(&value[i].data[5], "number") == 0) { type = NGX_JS_DICT_TYPE_NUMBER; } else { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid dict type \"%s\"", &value[i].data[5]); return NGX_CONF_ERROR; } continue; } } if (name.len == 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"%V\" must have \"zone\" parameter", &cmd->name); return NGX_CONF_ERROR; } if (evict && timeout == 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "evict requires timeout="); return NGX_CONF_ERROR; } shm_zone = ngx_shared_memory_add(cf, &name, size, tag); if (shm_zone == NULL) { return NGX_CONF_ERROR; } if (shm_zone->data) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "duplicate zone \"%V\"", &name); return NGX_CONF_ERROR; } dict = ngx_pcalloc(cf->pool, sizeof(ngx_js_dict_t)); if (dict == NULL) { return NGX_CONF_ERROR; } dict->shm_zone = shm_zone; dict->next = jmcf->dicts; jmcf->dicts = dict; shm_zone->data = dict; shm_zone->init = ngx_js_dict_init_zone; dict->evict = evict; dict->timeout = timeout; dict->type = type; return NGX_CONF_OK; } static njs_int_t ngx_js_shared_dict_preinit(njs_vm_t *vm) { static const njs_str_t error_name = njs_str("SharedMemoryError"); ngx_js_shared_dict_error_id = njs_vm_external_constructor(vm, &error_name, njs_error_constructor, ngx_js_ext_error_ctor_props, njs_nitems(ngx_js_ext_error_ctor_props), ngx_js_ext_error_proto_props, njs_nitems(ngx_js_ext_error_ctor_props)); if (njs_slow_path(ngx_js_shared_dict_error_id < 0)) { return NJS_ERROR; } return NJS_OK; } static njs_int_t ngx_js_shared_dict_init(njs_vm_t *vm) { ngx_js_shared_dict_proto_id = njs_vm_external_prototype(vm, ngx_js_ext_shared_dict, njs_nitems(ngx_js_ext_shared_dict)); if (ngx_js_shared_dict_proto_id < 0) { return NJS_ERROR; } return NJS_OK; } #if (NJS_HAVE_QUICKJS) static int ngx_qjs_shared_own_property(JSContext *cx, JSPropertyDescriptor *pdesc, JSValueConst obj, JSAtom prop) { int ret; ngx_str_t name; ngx_js_dict_t *dict; ngx_shm_zone_t *shm_zone; ngx_js_main_conf_t *conf; name.data = (u_char *) JS_AtomToCString(cx, prop); if (name.data == NULL) { return -1; } name.len = ngx_strlen(name.data); ret = 0; conf = ngx_qjs_main_conf(cx); for (dict = conf->dicts; dict != NULL; dict = dict->next) { shm_zone = dict->shm_zone; if (shm_zone->shm.name.len == name.len && ngx_strncmp(shm_zone->shm.name.data, name.data, name.len) == 0) { if (pdesc != NULL) { pdesc->flags = JS_PROP_ENUMERABLE; pdesc->getter = JS_UNDEFINED; pdesc->setter = JS_UNDEFINED; pdesc->value = JS_NewObjectClass(cx, NGX_QJS_CLASS_ID_SHARED_DICT); if (JS_IsException(pdesc->value)) { ret = -1; break; } JS_SetOpaque(pdesc->value, shm_zone); } ret = 1; break; } } JS_FreeCString(cx, (char *) name.data); return ret; } static int ngx_qjs_shared_own_property_names(JSContext *cx, JSPropertyEnum **ptab, uint32_t *plen, JSValueConst obj) { int ret; JSAtom key; JSValue keys; ngx_js_dict_t *dict; ngx_shm_zone_t *shm_zone; ngx_js_main_conf_t *conf; keys = JS_NewObject(cx); if (JS_IsException(keys)) { return -1; } conf = ngx_qjs_main_conf(cx); for (dict = conf->dicts; dict != NULL; dict = dict->next) { shm_zone = dict->shm_zone; key = JS_NewAtomLen(cx, (const char *) shm_zone->shm.name.data, shm_zone->shm.name.len); if (key == JS_ATOM_NULL) { return -1; } if (JS_DefinePropertyValue(cx, keys, key, JS_UNDEFINED, JS_PROP_ENUMERABLE) < 0) { JS_FreeAtom(cx, key); return -1; } JS_FreeAtom(cx, key); } ret = JS_GetOwnPropertyNames(cx, ptab, plen, keys, JS_GPN_STRING_MASK); JS_FreeValue(cx, keys); return ret; } static JSValue ngx_qjs_ext_ngx_shared(JSContext *cx, JSValueConst this_val) { return JS_NewObjectProtoClass(cx, JS_NULL, NGX_QJS_CLASS_ID_SHARED); } static JSValue ngx_qjs_ext_shared_dict_capacity(JSContext *cx, JSValueConst this_val) { ngx_shm_zone_t *shm_zone; shm_zone = JS_GetOpaque(this_val, NGX_QJS_CLASS_ID_SHARED_DICT); if (shm_zone == NULL) { return JS_UNDEFINED; } return JS_NewInt32(cx, shm_zone->shm.size); } static JSValue ngx_qjs_ext_shared_dict_clear(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv) { ngx_rbtree_t *rbtree; ngx_js_dict_t *dict; ngx_shm_zone_t *shm_zone; ngx_rbtree_node_t *rn, *next; shm_zone = JS_GetOpaque(this_val, NGX_QJS_CLASS_ID_SHARED_DICT); if (shm_zone == NULL) { return JS_ThrowTypeError(cx, "\"this\" is not a shared dict"); } dict = shm_zone->data; ngx_rwlock_wlock(&dict->sh->rwlock); if (dict->timeout) { ngx_js_dict_evict(dict, 0x7fffffff /* INT_MAX */); } else { rbtree = &dict->sh->rbtree; if (rbtree->root == rbtree->sentinel) { goto done; } for (rn = ngx_rbtree_min(rbtree->root, rbtree->sentinel); rn != NULL; rn = next) { next = ngx_rbtree_next(rbtree, rn); ngx_rbtree_delete(rbtree, rn); ngx_js_dict_node_free(dict, (ngx_js_dict_node_t *) rn); } } done: ngx_rwlock_unlock(&dict->sh->rwlock); return JS_UNDEFINED; } static JSValue ngx_qjs_ext_shared_dict_delete(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv) { ngx_str_t key; ngx_js_ctx_t *ctx; ngx_shm_zone_t *shm_zone; shm_zone = JS_GetOpaque(this_val, NGX_QJS_CLASS_ID_SHARED_DICT); if (shm_zone == NULL) { return JS_ThrowTypeError(cx, "\"this\" is not a shared dict"); } ctx = ngx_qjs_external_ctx(cx, JS_GetContextOpaque(cx)); if (ngx_qjs_string(ctx->engine, argv[0], &key) != NGX_OK) { return JS_EXCEPTION; } return ngx_qjs_dict_delete(cx, shm_zone->data, &key, 0); } static JSValue ngx_qjs_ext_shared_dict_free_space(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv) { size_t bytes; ngx_js_dict_t *dict; ngx_shm_zone_t *shm_zone; shm_zone = JS_GetOpaque(this_val, NGX_QJS_CLASS_ID_SHARED_DICT); if (shm_zone == NULL) { return JS_ThrowTypeError(cx, "\"this\" is not a shared dict"); } dict = shm_zone->data; ngx_rwlock_rlock(&dict->sh->rwlock); bytes = dict->shpool->pfree * ngx_pagesize; ngx_rwlock_unlock(&dict->sh->rwlock); return JS_NewInt32(cx, bytes); } static JSValue ngx_qjs_ext_shared_dict_get(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv) { ngx_str_t key; ngx_js_ctx_t *ctx; ngx_shm_zone_t *shm_zone; shm_zone = JS_GetOpaque(this_val, NGX_QJS_CLASS_ID_SHARED_DICT); if (shm_zone == NULL) { return JS_ThrowTypeError(cx, "\"this\" is not a shared dict"); } ctx = ngx_qjs_external_ctx(cx, JS_GetContextOpaque(cx)); if (ngx_qjs_string(ctx->engine, argv[0], &key) != NGX_OK) { return JS_EXCEPTION; } return ngx_qjs_dict_get(cx, shm_zone->data, &key); } static JSValue ngx_qjs_ext_shared_dict_has(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv) { ngx_str_t key; ngx_msec_t now; ngx_time_t *tp; ngx_js_ctx_t *ctx; ngx_js_dict_t *dict; ngx_shm_zone_t *shm_zone; ngx_js_dict_node_t *node; shm_zone = JS_GetOpaque(this_val, NGX_QJS_CLASS_ID_SHARED_DICT); if (shm_zone == NULL) { return JS_ThrowTypeError(cx, "\"this\" is not a shared dict"); } ctx = ngx_qjs_external_ctx(cx, JS_GetContextOpaque(cx)); if (ngx_qjs_string(ctx->engine, argv[0], &key) != NGX_OK) { return JS_EXCEPTION; } dict = shm_zone->data; ngx_rwlock_rlock(&dict->sh->rwlock); node = ngx_qjs_dict_lookup(dict, &key); if (node != NULL && dict->timeout) { tp = ngx_timeofday(); now = tp->sec * 1000 + tp->msec; if (now >= node->expire.key) { node = NULL; } } ngx_rwlock_unlock(&dict->sh->rwlock); return JS_NewBool(cx, node != NULL); } static JSValue ngx_qjs_ext_shared_dict_incr(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv) { double delta, init; uint32_t timeout; ngx_str_t key; ngx_js_ctx_t *ctx; ngx_js_dict_t *dict; ngx_shm_zone_t *shm_zone; shm_zone = JS_GetOpaque(this_val, NGX_QJS_CLASS_ID_SHARED_DICT); if (shm_zone == NULL) { return JS_ThrowTypeError(cx, "\"this\" is not a shared dict"); } dict = shm_zone->data; if (dict->type != NGX_JS_DICT_TYPE_NUMBER) { return JS_ThrowTypeError(cx, "shared dict is not a number dict"); } ctx = ngx_qjs_external_ctx(cx, JS_GetContextOpaque(cx)); if (ngx_qjs_string(ctx->engine, argv[0], &key) != NGX_OK) { return JS_EXCEPTION; } if (JS_ToFloat64(cx, &delta, argv[1]) < 0) { return JS_EXCEPTION; } if (JS_ToFloat64(cx, &init, argv[2]) < 0) { return JS_EXCEPTION; } if (argc > 3) { if (JS_ToUint32(cx, &timeout, argv[3]) < 0) { return JS_EXCEPTION; } if (!dict->timeout) { return JS_ThrowTypeError(cx, "shared dict must be declared with timeout"); } if (timeout < 1) { return JS_ThrowRangeError(cx, "timeout must be greater than or equal to 1"); } } else { timeout = dict->timeout; } return ngx_qjs_dict_incr(cx, dict, &key, delta, init, timeout); } static JSValue ngx_qjs_ext_shared_dict_items(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv) { JSValue arr, kv, v; uint32_t max_count, i; ngx_msec_t now; ngx_time_t *tp; ngx_rbtree_t *rbtree; ngx_js_dict_t *dict; ngx_shm_zone_t *shm_zone; ngx_rbtree_node_t *rn; ngx_js_dict_node_t *node; shm_zone = JS_GetOpaque(this_val, NGX_QJS_CLASS_ID_SHARED_DICT); if (shm_zone == NULL) { return JS_ThrowTypeError(cx, "\"this\" is not a shared dict"); } dict = shm_zone->data; max_count = 1024; if (argc > 0) { if (JS_ToUint32(cx, &max_count, argv[0]) < 0) { return JS_EXCEPTION; } } rbtree = &dict->sh->rbtree; ngx_rwlock_rlock(&dict->sh->rwlock); if (dict->timeout) { tp = ngx_timeofday(); now = tp->sec * 1000 + tp->msec; ngx_js_dict_expire(dict, now); } if (rbtree->root == rbtree->sentinel) { ngx_rwlock_unlock(&dict->sh->rwlock); return JS_NewArray(cx); } arr = JS_NewArray(cx); if (JS_IsException(arr)) { ngx_rwlock_unlock(&dict->sh->rwlock); return JS_EXCEPTION; } i = 0; for (rn = ngx_rbtree_min(rbtree->root, rbtree->sentinel); rn != NULL; rn = ngx_rbtree_next(rbtree, rn)) { if (max_count-- == 0) { break; } node = (ngx_js_dict_node_t *) rn; kv = JS_NewArray(cx); if (JS_IsException(kv)) { ngx_rwlock_unlock(&dict->sh->rwlock); JS_FreeValue(cx, arr); return JS_EXCEPTION; } v = JS_NewStringLen(cx, (const char *) node->sn.str.data, node->sn.str.len); if (JS_IsException(v)) { ngx_rwlock_unlock(&dict->sh->rwlock); JS_FreeValue(cx, kv); JS_FreeValue(cx, arr); return JS_EXCEPTION; } if (JS_DefinePropertyValueUint32(cx, kv, 0, v, JS_PROP_C_W_E) < 0) { ngx_rwlock_unlock(&dict->sh->rwlock); JS_FreeValue(cx, v); JS_FreeValue(cx, kv); JS_FreeValue(cx, arr); return JS_EXCEPTION; } v = ngx_qjs_dict_copy_value_locked(cx, dict, node); if (JS_DefinePropertyValueUint32(cx, kv, 1, v, JS_PROP_C_W_E) < 0) { ngx_rwlock_unlock(&dict->sh->rwlock); JS_FreeValue(cx, v); JS_FreeValue(cx, kv); JS_FreeValue(cx, arr); return JS_EXCEPTION; } if (JS_DefinePropertyValueUint32(cx, arr, i++, kv, JS_PROP_C_W_E) < 0) { ngx_rwlock_unlock(&dict->sh->rwlock); JS_FreeValue(cx, kv); JS_FreeValue(cx, arr); return JS_EXCEPTION; } } ngx_rwlock_unlock(&dict->sh->rwlock); return arr; } static JSValue ngx_qjs_ext_shared_dict_keys(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv) { JSValue arr, key; uint32_t max_count, i; ngx_msec_t now; ngx_time_t *tp; ngx_rbtree_t *rbtree; ngx_js_dict_t *dict; ngx_shm_zone_t *shm_zone; ngx_rbtree_node_t *rn; ngx_js_dict_node_t *node; shm_zone = JS_GetOpaque(this_val, NGX_QJS_CLASS_ID_SHARED_DICT); if (shm_zone == NULL) { return JS_ThrowTypeError(cx, "\"this\" is not a shared dict"); } dict = shm_zone->data; max_count = 1024; if (argc > 0) { if (JS_ToUint32(cx, &max_count, argv[0]) < 0) { return JS_EXCEPTION; } } rbtree = &dict->sh->rbtree; ngx_rwlock_rlock(&dict->sh->rwlock); if (dict->timeout) { tp = ngx_timeofday(); now = tp->sec * 1000 + tp->msec; ngx_js_dict_expire(dict, now); } if (rbtree->root == rbtree->sentinel) { ngx_rwlock_unlock(&dict->sh->rwlock); return JS_NewArray(cx); } arr = JS_NewArray(cx); if (JS_IsException(arr)) { ngx_rwlock_unlock(&dict->sh->rwlock); return JS_EXCEPTION; } i = 0; for (rn = ngx_rbtree_min(rbtree->root, rbtree->sentinel); rn != NULL; rn = ngx_rbtree_next(rbtree, rn)) { if (max_count-- == 0) { break; } node = (ngx_js_dict_node_t *) rn; key = JS_NewStringLen(cx, (const char *) node->sn.str.data, node->sn.str.len); if (JS_IsException(key)) { ngx_rwlock_unlock(&dict->sh->rwlock); JS_FreeValue(cx, arr); return JS_EXCEPTION; } if (JS_DefinePropertyValueUint32(cx, arr, i++, key, JS_PROP_C_W_E) < 0) { ngx_rwlock_unlock(&dict->sh->rwlock); JS_FreeValue(cx, key); JS_FreeValue(cx, arr); return JS_EXCEPTION; } } ngx_rwlock_unlock(&dict->sh->rwlock); return arr; } static JSValue ngx_qjs_ext_shared_dict_name(JSContext *cx, JSValueConst this_val) { ngx_shm_zone_t *shm_zone; shm_zone = JS_GetOpaque(this_val, NGX_QJS_CLASS_ID_SHARED_DICT); if (shm_zone == NULL) { return JS_UNDEFINED; } return JS_NewStringLen(cx, (const char *) shm_zone->shm.name.data, shm_zone->shm.name.len); } static JSValue ngx_qjs_ext_shared_dict_pop(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv) { ngx_str_t key; ngx_js_ctx_t *ctx; ngx_shm_zone_t *shm_zone; shm_zone = JS_GetOpaque(this_val, NGX_QJS_CLASS_ID_SHARED_DICT); if (shm_zone == NULL) { return JS_ThrowTypeError(cx, "\"this\" is not a shared dict"); } ctx = ngx_qjs_external_ctx(cx, JS_GetContextOpaque(cx)); if (ngx_qjs_string(ctx->engine, argv[0], &key) != NGX_OK) { return JS_EXCEPTION; } return ngx_qjs_dict_delete(cx, shm_zone->data, &key, 1); } static JSValue ngx_qjs_ext_shared_dict_set(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int flags) { JSValue ret; uint32_t timeout; ngx_str_t key; ngx_js_ctx_t *ctx; ngx_js_dict_t *dict; ngx_shm_zone_t *shm_zone; shm_zone = JS_GetOpaque(this_val, NGX_QJS_CLASS_ID_SHARED_DICT); if (shm_zone == NULL) { return JS_ThrowTypeError(cx, "\"this\" is not a shared dict"); } ctx = ngx_qjs_external_ctx(cx, JS_GetContextOpaque(cx)); if (ngx_qjs_string(ctx->engine, argv[0], &key) != NGX_OK) { return JS_EXCEPTION; } dict = shm_zone->data; if (dict->type == NGX_JS_DICT_TYPE_STRING) { if (!JS_IsString(argv[1])) { return JS_ThrowTypeError(cx, "string value is expected"); } } else { if (!JS_IsNumber(argv[1])) { return JS_ThrowTypeError(cx, "number value is expected"); } } if (!JS_IsUndefined(argv[2])) { if (!JS_IsNumber(argv[2])) { return JS_ThrowTypeError(cx, "timeout is not a number"); } if (!dict->timeout) { return JS_ThrowTypeError(cx, "shared dict must be declared with timeout"); } if (JS_ToUint32(cx, &timeout, argv[2]) < 0) { return JS_EXCEPTION; } if (timeout < 1) { return JS_ThrowTypeError(cx, "timeout must be greater than or equal to 1"); } } else { timeout = dict->timeout; } ret = ngx_qjs_dict_set(cx, shm_zone->data, &key, argv[1], timeout, flags); if (JS_IsException(ret)) { return JS_EXCEPTION; } if (flags) { /* add() or replace(). */ return ret; } return JS_DupValue(cx, this_val); } static JSValue ngx_qjs_ext_shared_dict_size(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv) { njs_int_t items; ngx_msec_t now; ngx_time_t *tp; ngx_rbtree_t *rbtree; ngx_js_dict_t *dict; ngx_shm_zone_t *shm_zone; ngx_rbtree_node_t *rn; shm_zone = JS_GetOpaque(this_val, NGX_QJS_CLASS_ID_SHARED_DICT); if (shm_zone == NULL) { return JS_ThrowTypeError(cx, "\"this\" is not a shared dict"); } dict = shm_zone->data; ngx_rwlock_rlock(&dict->sh->rwlock); if (dict->timeout) { tp = ngx_timeofday(); now = tp->sec * 1000 + tp->msec; ngx_js_dict_expire(dict, now); } rbtree = &dict->sh->rbtree; if (rbtree->root == rbtree->sentinel) { ngx_rwlock_unlock(&dict->sh->rwlock); return JS_NewInt32(cx, 0); } items = 0; for (rn = ngx_rbtree_min(rbtree->root, rbtree->sentinel); rn != NULL; rn = ngx_rbtree_next(rbtree, rn)) { items++; } ngx_rwlock_unlock(&dict->sh->rwlock); return JS_NewInt32(cx, items); } static JSValue ngx_qjs_ext_shared_dict_tag(JSContext *cx, JSValueConst this_val) { return JS_NewString(cx, "SharedDict"); } static JSValue ngx_qjs_ext_shared_dict_type(JSContext *cx, JSValueConst this_val) { ngx_str_t type; ngx_js_dict_t *dict; ngx_shm_zone_t *shm_zone; shm_zone = JS_GetOpaque(this_val, NGX_QJS_CLASS_ID_SHARED_DICT); if (shm_zone == NULL) { return JS_UNDEFINED; } dict = shm_zone->data; switch (dict->type) { case NGX_JS_DICT_TYPE_STRING: ngx_str_set(&type, "string"); break; default: ngx_str_set(&type, "number"); break; } return JS_NewStringLen(cx, (const char *) type.data, type.len); } static JSValue ngx_qjs_dict_copy_value_locked(JSContext *cx, ngx_js_dict_t *dict, ngx_js_dict_node_t *node) { if (dict->type == NGX_JS_DICT_TYPE_STRING) { return JS_NewStringLen(cx, (const char *) node->u.value.data, node->u.value.len); } /* NGX_JS_DICT_TYPE_NUMBER */ return JS_NewFloat64(cx, node->u.number); } static ngx_js_dict_node_t * ngx_qjs_dict_lookup(ngx_js_dict_t *dict, ngx_str_t *key) { uint32_t hash; ngx_rbtree_t *rbtree; rbtree = &dict->sh->rbtree; hash = ngx_crc32_long(key->data, key->len); return (ngx_js_dict_node_t *) ngx_str_rbtree_lookup(rbtree, key, hash); } static ngx_int_t ngx_qjs_dict_add(JSContext *cx, ngx_js_dict_t *dict, ngx_str_t *key, JSValue value, ngx_msec_t timeout, ngx_msec_t now) { size_t n; uint32_t hash; ngx_str_t string; ngx_js_dict_node_t *node; if (dict->timeout) { ngx_js_dict_expire(dict, now); } n = sizeof(ngx_js_dict_node_t) + key->len; hash = ngx_crc32_long(key->data, key->len); node = ngx_js_dict_alloc(dict, n); if (node == NULL) { return NGX_ERROR; } node->sn.str.data = (u_char *) node + sizeof(ngx_js_dict_node_t); if (dict->type == NGX_JS_DICT_TYPE_STRING) { string.data = (u_char *) JS_ToCStringLen(cx, &string.len, value); if (string.data == NULL) { ngx_slab_free_locked(dict->shpool, node); return NGX_ERROR; } node->u.value.data = ngx_js_dict_alloc(dict, string.len); if (node->u.value.data == NULL) { ngx_slab_free_locked(dict->shpool, node); JS_FreeCString(cx, (char *) string.data); return NGX_ERROR; } ngx_memcpy(node->u.value.data, string.data, string.len); node->u.value.len = string.len; JS_FreeCString(cx, (char *) string.data); } else { if (JS_ToFloat64(cx, &node->u.number, value) < 0) { ngx_slab_free_locked(dict->shpool, node); return NGX_ERROR; } } node->sn.node.key = hash; ngx_memcpy(node->sn.str.data, key->data, key->len); node->sn.str.len = key->len; ngx_rbtree_insert(&dict->sh->rbtree, &node->sn.node); if (dict->timeout) { node->expire.key = now + timeout; ngx_rbtree_insert(&dict->sh->rbtree_expire, &node->expire); } return NGX_OK; } static JSValue ngx_qjs_dict_delete(JSContext *cx, ngx_js_dict_t *dict, ngx_str_t *key, int retval) { JSValue ret; ngx_msec_t now; ngx_time_t *tp; ngx_js_dict_node_t *node; ngx_rwlock_wlock(&dict->sh->rwlock); node = ngx_qjs_dict_lookup(dict, key); if (node == NULL) { ngx_rwlock_unlock(&dict->sh->rwlock); return JS_UNDEFINED; } if (dict->timeout) { ngx_rbtree_delete(&dict->sh->rbtree_expire, &node->expire); } ngx_rbtree_delete(&dict->sh->rbtree, (ngx_rbtree_node_t *) node); if (retval) { tp = ngx_timeofday(); now = tp->sec * 1000 + tp->msec; if (!dict->timeout || now < node->expire.key) { ret = ngx_qjs_dict_copy_value_locked(cx, dict, node); } else { ret = JS_UNDEFINED; } } else { ret = JS_TRUE; } ngx_js_dict_node_free(dict, node); ngx_rwlock_unlock(&dict->sh->rwlock); return ret; } static JSValue ngx_qjs_dict_get(JSContext *cx, ngx_js_dict_t *dict, ngx_str_t *key) { JSValue ret; ngx_msec_t now; ngx_time_t *tp; ngx_js_dict_node_t *node; ngx_rwlock_rlock(&dict->sh->rwlock); node = ngx_qjs_dict_lookup(dict, key); if (node == NULL) { goto not_found; } if (dict->timeout) { tp = ngx_timeofday(); now = tp->sec * 1000 + tp->msec; if (now >= node->expire.key) { goto not_found; } } ret = ngx_qjs_dict_copy_value_locked(cx, dict, node); ngx_rwlock_unlock(&dict->sh->rwlock); return ret; not_found: ngx_rwlock_unlock(&dict->sh->rwlock); return JS_UNDEFINED; } static JSValue ngx_qjs_dict_incr(JSContext *cx, ngx_js_dict_t *dict, ngx_str_t *key, double delta, double init, ngx_msec_t timeout) { JSValue value; ngx_msec_t now; ngx_time_t *tp; ngx_js_dict_node_t *node; tp = ngx_timeofday(); now = tp->sec * 1000 + tp->msec; ngx_rwlock_wlock(&dict->sh->rwlock); node = ngx_qjs_dict_lookup(dict, key); if (node == NULL) { value = JS_NewFloat64(cx, init + delta); if (ngx_qjs_dict_add(cx, dict, key, value, timeout, now) != NGX_OK) { ngx_rwlock_unlock(&dict->sh->rwlock); JS_FreeValue(cx, value); return ngx_qjs_throw_shared_memory_error(cx); } } else { node->u.number += delta; value = JS_NewFloat64(cx, node->u.number); if (dict->timeout) { ngx_rbtree_delete(&dict->sh->rbtree_expire, &node->expire); node->expire.key = now + timeout; ngx_rbtree_insert(&dict->sh->rbtree_expire, &node->expire); } } ngx_rwlock_unlock(&dict->sh->rwlock); return value; } static JSValue ngx_qjs_dict_set(JSContext *cx, ngx_js_dict_t *dict, ngx_str_t *key, JSValue value, ngx_msec_t timeout, unsigned flags) { ngx_msec_t now; ngx_time_t *tp; ngx_js_dict_node_t *node; tp = ngx_timeofday(); now = tp->sec * 1000 + tp->msec; ngx_rwlock_wlock(&dict->sh->rwlock); node = ngx_qjs_dict_lookup(dict, key); if (node == NULL) { if (flags & NGX_JS_DICT_FLAG_MUST_EXIST) { ngx_rwlock_unlock(&dict->sh->rwlock); return JS_FALSE; } if (ngx_qjs_dict_add(cx, dict, key, value, timeout, now) != NGX_OK) { goto memory_error; } ngx_rwlock_unlock(&dict->sh->rwlock); return JS_TRUE; } if (flags & NGX_JS_DICT_FLAG_MUST_NOT_EXIST) { if (!dict->timeout || now < node->expire.key) { ngx_rwlock_unlock(&dict->sh->rwlock); return JS_FALSE; } } if (ngx_qjs_dict_update(cx, dict, node, value, timeout, now) != NGX_OK) { goto memory_error; } ngx_rwlock_unlock(&dict->sh->rwlock); return JS_TRUE; memory_error: ngx_rwlock_unlock(&dict->sh->rwlock); return ngx_qjs_throw_shared_memory_error(cx); } static ngx_int_t ngx_qjs_dict_update(JSContext *cx, ngx_js_dict_t *dict, ngx_js_dict_node_t *node, JSValue value, ngx_msec_t timeout, ngx_msec_t now) { u_char *p; ngx_str_t string; if (dict->type == NGX_JS_DICT_TYPE_STRING) { string.data = (u_char *) JS_ToCStringLen(cx, &string.len, value); if (string.data == NULL) { return NGX_ERROR; } p = ngx_js_dict_alloc(dict, string.len); if (p == NULL) { JS_FreeCString(cx, (char *) string.data); return NGX_ERROR; } ngx_slab_free_locked(dict->shpool, node->u.value.data); ngx_memcpy(p, string.data, string.len); node->u.value.data = p; node->u.value.len = string.len; JS_FreeCString(cx, (char *) string.data); } else { if (JS_ToFloat64(cx, &node->u.number, value) < 0) { return NGX_ERROR; } } if (dict->timeout) { ngx_rbtree_delete(&dict->sh->rbtree_expire, &node->expire); node->expire.key = now + timeout; ngx_rbtree_insert(&dict->sh->rbtree_expire, &node->expire); } return NGX_OK; } static JSValue ngx_qjs_shared_dict_error_constructor(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv) { JSValue proto, error_ctor, result, global_obj; global_obj = JS_GetGlobalObject(cx); error_ctor = JS_GetPropertyStr(cx, global_obj, "Error"); if (JS_IsException(error_ctor)) { JS_FreeValue(cx, global_obj); return error_ctor; } result = JS_CallConstructor(cx, error_ctor, argc, argv); JS_FreeValue(cx, error_ctor); JS_FreeValue(cx, global_obj); if (!JS_IsException(result)) { proto = JS_GetClassProto(cx, NGX_QJS_CLASS_ID_SHARED_DICT_ERROR); if (JS_SetPrototype(cx, result, proto) < 0) { JS_FreeValue(cx, result); JS_FreeValue(cx, proto); return JS_EXCEPTION; } JS_FreeValue(cx, proto); } return result; } static JSValue ngx_qjs_throw_shared_memory_error(JSContext *cx) { JSValue ctor, global_obj, err; global_obj = JS_GetGlobalObject(cx); ctor = JS_GetPropertyStr(cx, global_obj, "SharedMemoryError"); JS_FreeValue(cx, global_obj); if (JS_IsException(ctor)) { return ctor; } err = JS_CallConstructor(cx, ctor, 0, NULL); JS_FreeValue(cx, ctor); return JS_Throw(cx, err); } static JSModuleDef * ngx_qjs_ngx_shared_dict_init(JSContext *cx, const char *name) { JSValue global_obj, ngx_obj, proto, error_ctor, error_proto, shared_ctor; if (JS_NewClass(JS_GetRuntime(cx), NGX_QJS_CLASS_ID_SHARED, &ngx_qjs_shared_class) < 0) { return NULL; } if (JS_NewClass(JS_GetRuntime(cx), NGX_QJS_CLASS_ID_SHARED_DICT, &ngx_qjs_shared_dict_class) < 0) { return NULL; } proto = JS_NewObject(cx); if (JS_IsException(proto)) { return NULL; } JS_SetPropertyFunctionList(cx, proto, ngx_qjs_ext_shared_dict, njs_nitems(ngx_qjs_ext_shared_dict)); JS_SetClassProto(cx, NGX_QJS_CLASS_ID_SHARED_DICT, proto); global_obj = JS_GetGlobalObject(cx); error_ctor = JS_GetPropertyStr(cx, global_obj, "Error"); if (JS_IsException(error_ctor)) { JS_FreeValue(cx, global_obj); return NULL; } error_proto = JS_GetPropertyStr(cx, error_ctor, "prototype"); if (JS_IsException(error_proto)) { JS_FreeValue(cx, error_ctor); JS_FreeValue(cx, global_obj); return NULL; } proto = JS_NewObjectProto(cx, error_proto); JS_FreeValue(cx, error_ctor); JS_FreeValue(cx, error_proto); if (JS_IsException(proto)) { JS_FreeValue(cx, global_obj); return NULL; } JS_SetPropertyFunctionList(cx, proto, ngx_qjs_ext_shared_dict_error, njs_nitems(ngx_qjs_ext_shared_dict_error)); JS_SetClassProto(cx, NGX_QJS_CLASS_ID_SHARED_DICT_ERROR, proto); shared_ctor = JS_NewCFunction2(cx, ngx_qjs_shared_dict_error_constructor, "SharedDictError", 1, JS_CFUNC_constructor, 0); if (JS_IsException(shared_ctor)) { JS_FreeValue(cx, global_obj); return NULL; } JS_SetConstructor(cx, shared_ctor, proto); if (JS_SetPropertyStr(cx, global_obj, "SharedMemoryError", shared_ctor) < 0) { JS_FreeValue(cx, shared_ctor); JS_FreeValue(cx, global_obj); return NULL; } ngx_obj = JS_GetPropertyStr(cx, global_obj, "ngx"); if (JS_IsException(ngx_obj)) { JS_FreeValue(cx, global_obj); return NULL; } JS_SetPropertyFunctionList(cx, ngx_obj, ngx_qjs_ext_ngx, njs_nitems(ngx_qjs_ext_ngx)); JS_FreeValue(cx, ngx_obj); JS_FreeValue(cx, global_obj); return JS_NewCModule(cx, name, NULL); } #endif njs-0.8.9/nginx/ngx_js_shared_dict.h000066400000000000000000000007561474132077100174550ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #ifndef _NGX_JS_SHARED_DICT_H_INCLUDED_ #define _NGX_JS_SHARED_DICT_H_INCLUDED_ njs_int_t njs_js_ext_global_shared_prop(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); njs_int_t njs_js_ext_global_shared_keys(njs_vm_t *vm, njs_value_t *value, njs_value_t *keys); extern njs_module_t ngx_js_shared_dict_module; #endif /* _NGX_JS_SHARED_DICT_H_INCLUDED_ */ njs-0.8.9/nginx/ngx_stream_js_module.c000066400000000000000000002710371474132077100200410ustar00rootroot00000000000000 /* * Copyright (C) Roman Arutyunyan * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #include #include #include #include "ngx_js.h" typedef struct ngx_stream_js_ctx_s ngx_stream_js_ctx_t; typedef struct { NGX_JS_COMMON_LOC_CONF; ngx_str_t access; ngx_str_t preread; ngx_str_t filter; } ngx_stream_js_srv_conf_t; typedef struct { njs_opaque_value_t function; ngx_uint_t data_type; } ngx_stream_js_ev_t; typedef struct { ngx_stream_conf_ctx_t *conf_ctx; ngx_connection_t *connection; uint8_t *worker_affinity; /** * fd is used for event debug and should be at the same position * as in ngx_connection_t: after a 3rd pointer. */ ngx_socket_t fd; ngx_str_t method; ngx_msec_t interval; ngx_msec_t jitter; ngx_log_t log; ngx_event_t event; } ngx_js_periodic_t; struct ngx_stream_js_ctx_s { NGX_JS_COMMON_CTX; ngx_buf_t *buf; ngx_chain_t **last_out; ngx_chain_t *free; ngx_chain_t *upstream_busy; ngx_chain_t *downstream_busy; ngx_int_t status; ngx_int_t (*run_event)(ngx_stream_session_t *s, ngx_stream_js_ctx_t *ctx, ngx_stream_js_ev_t *event, ngx_uint_t from_upstream); ngx_int_t (*body_filter)(ngx_stream_session_t *s, ngx_stream_js_ctx_t *ctx, ngx_chain_t *in, ngx_uint_t from_upstream); #define NGX_JS_EVENT_UPLOAD 0 #define NGX_JS_EVENT_DOWNLOAD 1 #define NGX_JS_EVENT_MAX 2 ngx_stream_js_ev_t events[NGX_JS_EVENT_MAX]; unsigned filter:1; unsigned in_progress:1; ngx_js_periodic_t *periodic; }; #if (NJS_HAVE_QUICKJS) typedef struct { ngx_str_t name; ngx_uint_t data_type; ngx_uint_t id; } ngx_stream_qjs_event_t; typedef struct { ngx_stream_session_t *session; JSValue callbacks[NGX_JS_EVENT_MAX]; } ngx_stream_qjs_session_t; #endif #define ngx_stream_pending(ctx) \ (ngx_js_ctx_pending(ctx) || ngx_stream_js_pending_events(ctx)) static ngx_int_t ngx_stream_js_access_handler(ngx_stream_session_t *s); static ngx_int_t ngx_stream_js_preread_handler(ngx_stream_session_t *s); static ngx_int_t ngx_stream_js_phase_handler(ngx_stream_session_t *s, ngx_str_t *name); static ngx_int_t ngx_stream_js_body_filter(ngx_stream_session_t *s, ngx_chain_t *in, ngx_uint_t from_upstream); static ngx_int_t ngx_stream_njs_body_filter(ngx_stream_session_t *s, ngx_stream_js_ctx_t *ctx, ngx_chain_t *in, ngx_uint_t from_upstream); static ngx_int_t ngx_stream_js_next_filter(ngx_stream_session_t *s, ngx_stream_js_ctx_t *ctx, ngx_chain_t *out, ngx_uint_t from_upstream); static ngx_int_t ngx_stream_js_variable_set(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_stream_js_variable_var(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_stream_js_init_vm(ngx_stream_session_t *s, njs_int_t proto_id); static ngx_int_t ngx_stream_js_pending_events(ngx_stream_js_ctx_t *ctx); static void ngx_stream_js_drop_events(ngx_stream_js_ctx_t *ctx); static void ngx_stream_js_cleanup(void *data); static ngx_int_t ngx_stream_js_run_event(ngx_stream_session_t *s, ngx_stream_js_ctx_t *ctx, ngx_stream_js_ev_t *event, ngx_uint_t from_upstream); static ngx_stream_js_ev_t *ngx_stream_js_event(ngx_stream_session_t *s, njs_str_t *event); static njs_int_t ngx_stream_js_ext_get_remote_address(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_stream_js_ext_done(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t ngx_stream_js_ext_on(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t ngx_stream_js_ext_off(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t ngx_stream_js_ext_send(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t from_upstream, njs_value_t *retval); static njs_int_t ngx_stream_js_ext_set_return_value(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t ngx_stream_js_ext_variables(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_stream_js_periodic_variables(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); #if (NJS_HAVE_QUICKJS) static JSValue ngx_stream_qjs_ext_to_string_tag(JSContext *cx, JSValueConst this_val); static JSValue ngx_stream_qjs_ext_done(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int magic); static JSValue ngx_stream_qjs_ext_log(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int level); static JSValue ngx_stream_qjs_ext_on(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue ngx_stream_qjs_ext_off(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue ngx_stream_qjs_ext_periodic_to_string_tag(JSContext *cx, JSValueConst this_val); static JSValue ngx_stream_qjs_ext_periodic_variables(JSContext *cx, JSValueConst this_val, int type); static JSValue ngx_stream_qjs_ext_remote_address(JSContext *cx, JSValueConst this_val); static JSValue ngx_stream_qjs_ext_send(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int from_upstream); static JSValue ngx_stream_qjs_ext_set_return_value(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue ngx_stream_qjs_ext_variables(JSContext *cx, JSValueConst this_val, int type); static JSValue ngx_stream_qjs_ext_uint(JSContext *cx, JSValueConst this_val, int offset); static JSValue ngx_stream_qjs_ext_flag(JSContext *cx, JSValueConst this_val, int mask); static int ngx_stream_qjs_variables_own_property(JSContext *cx, JSPropertyDescriptor *pdesc, JSValueConst obj, JSAtom prop); static int ngx_stream_qjs_variables_set_property(JSContext *cx, JSValueConst obj, JSAtom atom, JSValueConst value, JSValueConst receiver, int flags); static int ngx_stream_qjs_variables_define_own_property(JSContext *cx, JSValueConst obj, JSAtom prop, JSValueConst value, JSValueConst getter, JSValueConst setter, int flags); static ngx_int_t ngx_stream_qjs_run_event(ngx_stream_session_t *s, ngx_stream_js_ctx_t *ctx, ngx_stream_js_ev_t *event, ngx_uint_t from_upstream); static ngx_int_t ngx_stream_qjs_body_filter(ngx_stream_session_t *s, ngx_stream_js_ctx_t *ctx, ngx_chain_t *in, ngx_uint_t from_upstream); static ngx_stream_session_t *ngx_stream_qjs_session(JSValueConst val); static JSValue ngx_stream_qjs_session_make(JSContext *cx, ngx_int_t proto_id, ngx_stream_session_t *s); static void ngx_stream_qjs_session_finalizer(JSRuntime *rt, JSValue val); #endif static ngx_pool_t *ngx_stream_js_pool(ngx_stream_session_t *s); static ngx_resolver_t *ngx_stream_js_resolver(ngx_stream_session_t *s); static ngx_msec_t ngx_stream_js_resolver_timeout(ngx_stream_session_t *s); static ngx_msec_t ngx_stream_js_fetch_timeout(ngx_stream_session_t *s); static size_t ngx_stream_js_buffer_size(ngx_stream_session_t *s); static size_t ngx_stream_js_max_response_buffer_size(ngx_stream_session_t *s); static void ngx_stream_js_event_finalize(ngx_stream_session_t *s, ngx_int_t rc); static ngx_js_ctx_t *ngx_stream_js_ctx(ngx_stream_session_t *s); static void ngx_stream_js_periodic_handler(ngx_event_t *ev); static void ngx_stream_js_periodic_event_handler(ngx_event_t *ev); static void ngx_stream_js_periodic_finalize(ngx_stream_session_t *s, ngx_int_t rc); static void ngx_stream_js_periodic_destroy(ngx_stream_session_t *s, ngx_js_periodic_t *periodic); static njs_int_t ngx_js_stream_init(njs_vm_t *vm); static ngx_int_t ngx_stream_js_init(ngx_conf_t *cf); static ngx_int_t ngx_stream_js_init_worker(ngx_cycle_t *cycle); static char *ngx_stream_js_periodic(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_stream_js_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_stream_js_var(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static ngx_int_t ngx_stream_js_init_conf_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf); static void *ngx_stream_js_create_main_conf(ngx_conf_t *cf); static void *ngx_stream_js_create_srv_conf(ngx_conf_t *cf); static char *ngx_stream_js_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child); static char *ngx_stream_js_shared_dict_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static ngx_ssl_t *ngx_stream_js_ssl(ngx_stream_session_t *s); static ngx_flag_t ngx_stream_js_ssl_verify(ngx_stream_session_t *s); static ngx_conf_bitmask_t ngx_stream_js_engines[] = { { ngx_string("njs"), NGX_ENGINE_NJS }, #if (NJS_HAVE_QUICKJS) { ngx_string("qjs"), NGX_ENGINE_QJS }, #endif { ngx_null_string, 0 } }; #if (NGX_STREAM_SSL) static ngx_conf_bitmask_t ngx_stream_js_ssl_protocols[] = { { ngx_string("TLSv1"), NGX_SSL_TLSv1 }, { ngx_string("TLSv1.1"), NGX_SSL_TLSv1_1 }, { ngx_string("TLSv1.2"), NGX_SSL_TLSv1_2 }, { ngx_string("TLSv1.3"), NGX_SSL_TLSv1_3 }, { ngx_null_string, 0 } }; #endif static ngx_command_t ngx_stream_js_commands[] = { { ngx_string("js_engine"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE, ngx_js_engine, NGX_STREAM_SRV_CONF_OFFSET, offsetof(ngx_stream_js_srv_conf_t, type), &ngx_stream_js_engines }, { ngx_string("js_context_reuse"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_size_slot, NGX_STREAM_SRV_CONF_OFFSET, offsetof(ngx_stream_js_srv_conf_t, reuse), NULL }, { ngx_string("js_import"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE13, ngx_js_import, NGX_STREAM_SRV_CONF_OFFSET, 0, NULL }, { ngx_string("js_periodic"), NGX_STREAM_SRV_CONF|NGX_CONF_ANY, ngx_stream_js_periodic, NGX_STREAM_SRV_CONF_OFFSET, 0, NULL }, { ngx_string("js_preload_object"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE13, ngx_js_preload_object, NGX_STREAM_SRV_CONF_OFFSET, 0, NULL }, { ngx_string("js_path"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_array_slot, NGX_STREAM_SRV_CONF_OFFSET, offsetof(ngx_stream_js_srv_conf_t, paths), NULL }, { ngx_string("js_set"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE23, ngx_stream_js_set, 0, 0, NULL }, { ngx_string("js_var"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE12, ngx_stream_js_var, 0, 0, NULL }, { ngx_string("js_access"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_slot, NGX_STREAM_SRV_CONF_OFFSET, offsetof(ngx_stream_js_srv_conf_t, access), NULL }, { ngx_string("js_preread"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_slot, NGX_STREAM_SRV_CONF_OFFSET, offsetof(ngx_stream_js_srv_conf_t, preread), NULL }, { ngx_string("js_filter"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_slot, NGX_STREAM_SRV_CONF_OFFSET, offsetof(ngx_stream_js_srv_conf_t, filter), NULL }, { ngx_string("js_fetch_buffer_size"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_size_slot, NGX_STREAM_SRV_CONF_OFFSET, offsetof(ngx_stream_js_srv_conf_t, buffer_size), NULL }, { ngx_string("js_fetch_max_response_buffer_size"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_size_slot, NGX_STREAM_SRV_CONF_OFFSET, offsetof(ngx_stream_js_srv_conf_t, max_response_body_size), NULL }, { ngx_string("js_fetch_timeout"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_msec_slot, NGX_STREAM_SRV_CONF_OFFSET, offsetof(ngx_stream_js_srv_conf_t, timeout), NULL }, #if (NGX_STREAM_SSL) { ngx_string("js_fetch_ciphers"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_slot, NGX_STREAM_SRV_CONF_OFFSET, offsetof(ngx_stream_js_srv_conf_t, ssl_ciphers), NULL }, { ngx_string("js_fetch_protocols"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE, ngx_conf_set_bitmask_slot, NGX_STREAM_SRV_CONF_OFFSET, offsetof(ngx_stream_js_srv_conf_t, ssl_protocols), &ngx_stream_js_ssl_protocols }, { ngx_string("js_fetch_verify"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG, ngx_conf_set_flag_slot, NGX_STREAM_SRV_CONF_OFFSET, offsetof(ngx_stream_js_srv_conf_t, ssl_verify), NULL }, { ngx_string("js_fetch_verify_depth"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_num_slot, NGX_STREAM_SRV_CONF_OFFSET, offsetof(ngx_stream_js_srv_conf_t, ssl_verify_depth), NULL }, { ngx_string("js_fetch_trusted_certificate"), NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, ngx_conf_set_str_slot, NGX_STREAM_SRV_CONF_OFFSET, offsetof(ngx_stream_js_srv_conf_t, ssl_trusted_certificate), NULL }, #endif { ngx_string("js_shared_dict_zone"), NGX_STREAM_MAIN_CONF|NGX_CONF_1MORE, ngx_stream_js_shared_dict_zone, 0, 0, NULL }, ngx_null_command }; static ngx_stream_module_t ngx_stream_js_module_ctx = { NULL, /* preconfiguration */ ngx_stream_js_init, /* postconfiguration */ ngx_stream_js_create_main_conf, /* create main configuration */ NULL, /* init main configuration */ ngx_stream_js_create_srv_conf, /* create server configuration */ ngx_stream_js_merge_srv_conf, /* merge server configuration */ }; ngx_module_t ngx_stream_js_module = { NGX_MODULE_V1, &ngx_stream_js_module_ctx, /* module context */ ngx_stream_js_commands, /* module directives */ NGX_STREAM_MODULE, /* module type */ NULL, /* init master */ NULL, /* init module */ ngx_stream_js_init_worker, /* init process */ NULL, /* init thread */ NULL, /* exit thread */ NULL, /* exit process */ NULL, /* exit master */ NGX_MODULE_V1_PADDING }; static njs_external_t ngx_stream_js_ext_session[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "Stream Session", } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("allow"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_stream_js_ext_done, .magic8 = NGX_OK, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("decline"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_stream_js_ext_done, .magic8 = -NGX_DECLINED, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("deny"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_stream_js_ext_done, .magic8 = -NGX_DONE, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("done"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_stream_js_ext_done, .magic8 = NGX_OK, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("error"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_js_ext_log, .magic8 = NGX_LOG_ERR, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("log"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_js_ext_log, .magic8 = NGX_LOG_INFO, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("off"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_stream_js_ext_off, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("on"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_stream_js_ext_on, } }, { .flags = NJS_EXTERN_OBJECT, .name.string = njs_str("rawVariables"), .u.object = { .writable = 1, .prop_handler = ngx_stream_js_ext_variables, .magic32 = NGX_JS_BUFFER, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("remoteAddress"), .enumerable = 1, .u.property = { .handler = ngx_stream_js_ext_get_remote_address, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("send"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_stream_js_ext_send, .magic8 = NGX_JS_BOOL_UNSET, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("sendDownstream"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_stream_js_ext_send, .magic8 = NGX_JS_BOOL_TRUE, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("sendUpstream"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_stream_js_ext_send, .magic8 = NGX_JS_BOOL_FALSE, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("setReturnValue"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_stream_js_ext_set_return_value, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("status"), .enumerable = 1, .u.property = { .handler = ngx_js_ext_uint, .magic32 = offsetof(ngx_stream_session_t, status), } }, { .flags = NJS_EXTERN_OBJECT, .name.string = njs_str("variables"), .u.object = { .writable = 1, .prop_handler = ngx_stream_js_ext_variables, .magic32 = NGX_JS_STRING, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("warn"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = ngx_js_ext_log, .magic8 = NGX_LOG_WARN, } }, }; static njs_external_t ngx_stream_js_ext_periodic_session[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "PeriodicSession", } }, { .flags = NJS_EXTERN_OBJECT, .name.string = njs_str("rawVariables"), .u.object = { .writable = 1, .prop_handler = ngx_stream_js_periodic_variables, .magic32 = NGX_JS_BUFFER, } }, { .flags = NJS_EXTERN_OBJECT, .name.string = njs_str("variables"), .u.object = { .writable = 1, .prop_handler = ngx_stream_js_periodic_variables, .magic32 = NGX_JS_STRING, } }, }; static njs_external_t ngx_stream_js_ext_session_flags[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "Stream Flags", } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("from_upstream"), .enumerable = 1, .u.property = { .handler = ngx_js_ext_flags, .magic16 = NGX_JS_BOOLEAN, .magic32 = 0x00000002, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("last"), .enumerable = 1, .u.property = { .handler = ngx_js_ext_flags, .magic16 = NGX_JS_BOOLEAN, .magic32 = 0x00000001, } }, }; static uintptr_t ngx_stream_js_uptr[] = { offsetof(ngx_stream_session_t, connection), (uintptr_t) ngx_stream_js_pool, (uintptr_t) ngx_stream_js_resolver, (uintptr_t) ngx_stream_js_resolver_timeout, (uintptr_t) ngx_stream_js_event_finalize, (uintptr_t) ngx_stream_js_ssl, (uintptr_t) ngx_stream_js_ssl_verify, (uintptr_t) ngx_stream_js_fetch_timeout, (uintptr_t) ngx_stream_js_buffer_size, (uintptr_t) ngx_stream_js_max_response_buffer_size, (uintptr_t) 0 /* main_conf ptr */, (uintptr_t) ngx_stream_js_ctx, }; static njs_vm_meta_t ngx_stream_js_metas = { .size = njs_nitems(ngx_stream_js_uptr), .values = ngx_stream_js_uptr }; static ngx_stream_filter_pt ngx_stream_next_filter; static njs_int_t ngx_stream_js_session_proto_id = 1; static njs_int_t ngx_stream_js_periodic_session_proto_id = 2; static njs_int_t ngx_stream_js_session_flags_proto_id = 3; njs_module_t ngx_js_stream_module = { .name = njs_str("stream"), .preinit = NULL, .init = ngx_js_stream_init, }; njs_module_t *njs_stream_js_addon_modules[] = { /* * Shared addons should be in the same order and the same positions * in all nginx modules. */ &ngx_js_ngx_module, &ngx_js_fetch_module, &ngx_js_shared_dict_module, #ifdef NJS_HAVE_OPENSSL &njs_webcrypto_module, #endif #ifdef NJS_HAVE_XML &njs_xml_module, #endif #ifdef NJS_HAVE_ZLIB &njs_zlib_module, #endif &ngx_js_stream_module, NULL, }; #if (NJS_HAVE_QUICKJS) static const JSCFunctionListEntry ngx_stream_qjs_ext_session[] = { JS_CGETSET_DEF("[Symbol.toStringTag]", ngx_stream_qjs_ext_to_string_tag, NULL), JS_CFUNC_MAGIC_DEF("allow", 1, ngx_stream_qjs_ext_done, NGX_OK), JS_CFUNC_MAGIC_DEF("decline", 1, ngx_stream_qjs_ext_done, -NGX_DECLINED), JS_CFUNC_MAGIC_DEF("deny", 1, ngx_stream_qjs_ext_done, -NGX_DONE), JS_CFUNC_MAGIC_DEF("done", 1, ngx_stream_qjs_ext_done, NGX_OK), JS_CFUNC_MAGIC_DEF("error", 1, ngx_stream_qjs_ext_log, NGX_LOG_ERR), JS_CFUNC_MAGIC_DEF("log", 1, ngx_stream_qjs_ext_log, NGX_LOG_INFO), JS_CFUNC_DEF("on", 2, ngx_stream_qjs_ext_on), JS_CFUNC_DEF("off", 1, ngx_stream_qjs_ext_off), JS_CGETSET_MAGIC_DEF("rawVariables", ngx_stream_qjs_ext_variables, NULL, NGX_JS_BUFFER), JS_CGETSET_DEF("remoteAddress", ngx_stream_qjs_ext_remote_address, NULL), JS_CFUNC_MAGIC_DEF("send", 2, ngx_stream_qjs_ext_send, NGX_JS_BOOL_UNSET), JS_CFUNC_MAGIC_DEF("sendDownstream", 1, ngx_stream_qjs_ext_send, NGX_JS_BOOL_TRUE), JS_CFUNC_MAGIC_DEF("sendUpstream", 1, ngx_stream_qjs_ext_send, NGX_JS_BOOL_FALSE), JS_CFUNC_DEF("setReturnValue", 1, ngx_stream_qjs_ext_set_return_value), JS_CGETSET_MAGIC_DEF("status", ngx_stream_qjs_ext_uint, NULL, offsetof(ngx_stream_session_t, status)), JS_CGETSET_MAGIC_DEF("variables", ngx_stream_qjs_ext_variables, NULL, NGX_JS_STRING), JS_CFUNC_MAGIC_DEF("warn", 1, ngx_stream_qjs_ext_log, NGX_LOG_WARN), }; static const JSCFunctionListEntry ngx_stream_qjs_ext_periodic[] = { JS_CGETSET_DEF("[Symbol.toStringTag]", ngx_stream_qjs_ext_periodic_to_string_tag, NULL), JS_CGETSET_MAGIC_DEF("rawVariables", ngx_stream_qjs_ext_periodic_variables, NULL, NGX_JS_BUFFER), JS_CGETSET_MAGIC_DEF("variables", ngx_stream_qjs_ext_periodic_variables, NULL, NGX_JS_STRING), }; static const JSCFunctionListEntry ngx_stream_qjs_ext_flags[] = { JS_CGETSET_MAGIC_DEF("from_upstream", ngx_stream_qjs_ext_flag, NULL, 2), JS_CGETSET_MAGIC_DEF("last", ngx_stream_qjs_ext_flag, NULL, 1), }; static JSClassDef ngx_stream_qjs_session_class = { "Session", .finalizer = ngx_stream_qjs_session_finalizer, }; static JSClassDef ngx_stream_qjs_periodic_class = { "Periodic", .finalizer = NULL, }; static JSClassDef ngx_stream_qjs_flags_class = { "Stream Flags", .finalizer = NULL, }; static JSClassDef ngx_stream_qjs_variables_class = { "Variables", .finalizer = NULL, .exotic = & (JSClassExoticMethods) { .get_own_property = ngx_stream_qjs_variables_own_property, .set_property = ngx_stream_qjs_variables_set_property, .define_own_property = ngx_stream_qjs_variables_define_own_property, }, }; qjs_module_t *njs_stream_qjs_addon_modules[] = { &ngx_qjs_ngx_module, /* * Shared addons should be in the same order and the same positions * in all nginx modules. */ #ifdef NJS_HAVE_ZLIB &qjs_zlib_module, #endif NULL, }; #endif static ngx_int_t ngx_stream_js_access_handler(ngx_stream_session_t *s) { ngx_stream_js_srv_conf_t *jscf; ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "js access handler"); jscf = ngx_stream_get_module_srv_conf(s, ngx_stream_js_module); return ngx_stream_js_phase_handler(s, &jscf->access); } static ngx_int_t ngx_stream_js_preread_handler(ngx_stream_session_t *s) { ngx_stream_js_srv_conf_t *jscf; ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "js preread handler"); jscf = ngx_stream_get_module_srv_conf(s, ngx_stream_js_module); return ngx_stream_js_phase_handler(s, &jscf->preread); } static ngx_int_t ngx_stream_js_phase_handler(ngx_stream_session_t *s, ngx_str_t *name) { ngx_int_t rc; ngx_stream_js_ctx_t *ctx; if (name->len == 0) { return NGX_DECLINED; } ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "stream js phase handler"); rc = ngx_stream_js_init_vm(s, ngx_stream_js_session_proto_id); if (rc != NGX_OK) { return rc; } ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module); if (!ctx->in_progress) { /* * status is expected to be overriden by allow(), deny(), decline() or * done() methods. */ ctx->status = NGX_ERROR; ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ctx->log, 0, "stream js phase call \"%V\"", name); rc = ctx->engine->call((ngx_js_ctx_t *) ctx, name, &ctx->args[0], 1); if (rc == NGX_ERROR) { return rc; } } rc = ctx->run_event(s, ctx, &ctx->events[NGX_JS_EVENT_UPLOAD], 0); if (rc != NGX_OK) { return NGX_ERROR; } if (ngx_stream_pending(ctx)) { ctx->in_progress = 1; rc = (ctx->events[NGX_JS_EVENT_UPLOAD].data_type != NGX_JS_UNSET) ? NGX_AGAIN : NGX_DONE; } else { ctx->in_progress = 0; rc = ctx->status; } ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ctx->log, 0, "stream js phase rc: %i", rc); return rc; } #define ngx_stream_event(from_upstream) \ (from_upstream ? &ctx->events[NGX_JS_EVENT_DOWNLOAD] \ : &ctx->events[NGX_JS_EVENT_UPLOAD]) static ngx_int_t ngx_stream_js_body_filter(ngx_stream_session_t *s, ngx_chain_t *in, ngx_uint_t from_upstream) { ngx_int_t rc; ngx_chain_t *out; ngx_stream_js_ctx_t *ctx; ngx_stream_js_srv_conf_t *jscf; jscf = ngx_stream_get_module_srv_conf(s, ngx_stream_js_module); if (jscf->filter.len == 0) { return ngx_stream_next_filter(s, in, from_upstream); } ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "stream js filter u:%ui", from_upstream); rc = ngx_stream_js_init_vm(s, ngx_stream_js_session_proto_id); if (rc == NGX_ERROR) { return NGX_ERROR; } if (rc == NGX_DECLINED) { return ngx_stream_next_filter(s, in, from_upstream); } ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module); if (!ctx->filter) { ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "stream js filter call \"%V\"" , &jscf->filter); rc = ctx->engine->call((ngx_js_ctx_t *) ctx, &jscf->filter, &ctx->args[0], 1); if (rc == NGX_ERROR) { return rc; } } ctx->filter = 1; ctx->last_out = &out; rc = ctx->body_filter(s, ctx, in, from_upstream); if (rc != NGX_OK) { return NGX_ERROR; } ctx->buf = NULL; *ctx->last_out = NULL; return ngx_stream_js_next_filter(s, ctx, out, from_upstream); } static ngx_int_t ngx_stream_js_next_filter(ngx_stream_session_t *s, ngx_stream_js_ctx_t *ctx, ngx_chain_t *out, ngx_uint_t from_upstream) { ngx_int_t rc; ngx_chain_t **busy; ngx_connection_t *c, *dst; c = s->connection; if (from_upstream) { dst = c; busy = &ctx->downstream_busy; } else { dst = s->upstream ? s->upstream->peer.connection : NULL; busy = &ctx->upstream_busy; } if (out != NULL || dst == NULL || dst->buffered) { rc = ngx_stream_next_filter(s, out, from_upstream); ngx_chain_update_chains(c->pool, &ctx->free, busy, &out, (ngx_buf_tag_t) &ngx_stream_js_module); } else { rc = NGX_OK; } return rc; } static ngx_int_t ngx_stream_js_variable_set(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data) { ngx_js_set_t *vdata = (ngx_js_set_t *) data; ngx_int_t rc; njs_int_t pending; ngx_str_t *fname, value; ngx_stream_js_ctx_t *ctx; fname = &vdata->fname; rc = ngx_stream_js_init_vm(s, ngx_stream_js_session_proto_id); if (rc == NGX_ERROR) { return NGX_ERROR; } if (rc == NGX_DECLINED) { v->not_found = 1; return NGX_OK; } ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "stream js variable call \"%V\"", fname); ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module); pending = ngx_stream_pending(ctx); rc = ctx->engine->call((ngx_js_ctx_t *) ctx, fname, &ctx->args[0], 1); if (rc == NGX_ERROR) { v->not_found = 1; return NGX_OK; } if (!pending && rc == NGX_AGAIN) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "async operation inside \"%V\" variable handler", fname); return NGX_ERROR; } if (ctx->engine->string(ctx->engine, &ctx->retval, &value) != NGX_OK) { return NGX_ERROR; } v->len = value.len; v->valid = 1; v->no_cacheable = vdata->flags & NGX_NJS_VAR_NOCACHE; v->not_found = 0; v->data = value.data; return NGX_OK; } static ngx_int_t ngx_stream_js_variable_var(ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data) { ngx_stream_complex_value_t *cv = (ngx_stream_complex_value_t *) data; ngx_str_t value; if (cv != NULL) { if (ngx_stream_complex_value(s, cv, &value) != NGX_OK) { return NGX_ERROR; } } else { ngx_str_null(&value); } v->len = value.len; v->valid = 1; v->no_cacheable = 0; v->not_found = 0; v->data = value.data; return NGX_OK; } static ngx_int_t ngx_stream_js_init_vm(ngx_stream_session_t *s, njs_int_t proto_id) { ngx_pool_cleanup_t *cln; ngx_stream_js_ctx_t *ctx; ngx_stream_js_srv_conf_t *jscf; jscf = ngx_stream_get_module_srv_conf(s, ngx_stream_js_module); if (jscf->engine == NULL) { return NGX_DECLINED; } ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module); if (ctx == NULL) { ctx = ngx_pcalloc(s->connection->pool, sizeof(ngx_stream_js_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } ngx_js_ctx_init((ngx_js_ctx_t *) ctx, s->connection->log); ngx_stream_set_ctx(s, ctx, ngx_stream_js_module); } if (ctx->engine) { return NGX_OK; } ctx->engine = jscf->engine->clone((ngx_js_ctx_t *) ctx, (ngx_js_loc_conf_t *) jscf, proto_id, s); if (ctx->engine == NULL) { return NGX_ERROR; } ngx_log_debug3(NGX_LOG_DEBUG_STREAM, ctx->log, 0, "stream js vm clone %s: %p from: %p", jscf->engine->name, ctx->engine, jscf->engine); cln = ngx_pool_cleanup_add(s->connection->pool, 0); if (cln == NULL) { return NGX_ERROR; } cln->handler = ngx_stream_js_cleanup; cln->data = s; return NGX_OK; } static ngx_int_t ngx_stream_js_pending_events(ngx_stream_js_ctx_t *ctx) { ngx_uint_t i; for (i = 0; i < NGX_JS_EVENT_MAX; i++) { if (ctx->events[i].data_type != NGX_JS_UNSET) { return 1; } } return 0; } static void ngx_stream_js_drop_events(ngx_stream_js_ctx_t *ctx) { ngx_uint_t i; for (i = 0; i < NGX_JS_EVENT_MAX; i++) { /* * event[i].data_type = NGX_JS_UNSET * event[i].function = JS_NULL */ memset(&ctx->events[i], 0, sizeof(ngx_stream_js_ev_t)); } } static void ngx_stream_js_cleanup(void *data) { ngx_stream_js_ctx_t *ctx; ngx_stream_js_srv_conf_t *jscf; ngx_stream_session_t *s = data; ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module); if (ngx_js_ctx_pending(ctx)) { ngx_log_error(NGX_LOG_ERR, ctx->log, 0, "pending events"); } ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ctx->log, 0, "stream js vm destroy: %p", ctx->engine); jscf = ngx_stream_get_module_srv_conf(s, ngx_stream_js_module); ngx_js_ctx_destroy((ngx_js_ctx_t *) ctx, (ngx_js_loc_conf_t *) jscf); } static ngx_int_t ngx_stream_js_run_event(ngx_stream_session_t *s, ngx_stream_js_ctx_t *ctx, ngx_stream_js_ev_t *event, ngx_uint_t from_upstream) { size_t len; u_char *p; njs_vm_t *vm; njs_int_t ret; ngx_str_t exception; ngx_buf_t *b; uintptr_t flags; ngx_connection_t *c; if (!njs_value_is_function(njs_value_arg(&event->function))) { return NGX_OK; } c = s->connection; b = ctx->filter ? ctx->buf : c->buffer; len = b ? b->last - b->pos : 0; vm = ctx->engine->u.njs.vm; p = ngx_pnalloc(c->pool, len); if (p == NULL) { njs_vm_memory_error(vm); goto error; } if (len) { ngx_memcpy(p, b->pos, len); } ret = ngx_js_prop(vm, event->data_type, njs_value_arg(&ctx->args[1]), p, len); if (ret != NJS_OK) { goto error; } flags = from_upstream << 1 | (uintptr_t) (b && b->last_buf); ret = njs_vm_external_create(vm, njs_value_arg(&ctx->args[2]), ngx_stream_js_session_flags_proto_id, (void *) flags, 0); if (ret != NJS_OK) { goto error; } ret = ngx_js_call(vm, njs_value_function(njs_value_arg(&event->function)), &ctx->args[1], 2); if (ret == NJS_ERROR) { error: ngx_js_exception(vm, &exception); ngx_log_error(NGX_LOG_ERR, c->log, 0, "js exception: %V", &exception); return NGX_ERROR; } return NGX_OK; } static ngx_int_t ngx_stream_njs_body_filter(ngx_stream_session_t *s, ngx_stream_js_ctx_t *ctx, ngx_chain_t *in, ngx_uint_t from_upstream) { ngx_int_t rc; ngx_chain_t *cl; ngx_stream_js_ev_t *event; while (in) { ctx->buf = in->buf; event = ngx_stream_event(from_upstream); if (njs_value_is_function(njs_value_arg(&event->function))) { rc = ngx_stream_js_run_event(s, ctx, event, from_upstream); if (rc != NGX_OK) { return NGX_ERROR; } ctx->buf->pos = ctx->buf->last; } else { cl = ngx_alloc_chain_link(s->connection->pool); if (cl == NULL) { return NGX_ERROR; } cl->buf = ctx->buf; *ctx->last_out = cl; ctx->last_out = &cl->next; } in = in->next; } return NGX_OK; } static ngx_stream_js_ev_t * ngx_stream_js_event(ngx_stream_session_t *s, njs_str_t *event) { ngx_uint_t i, n, type; ngx_stream_js_ctx_t *ctx; static const struct { ngx_str_t name; ngx_uint_t data_type; ngx_uint_t id; } events[] = { { ngx_string("upload"), NGX_JS_STRING, NGX_JS_EVENT_UPLOAD, }, { ngx_string("download"), NGX_JS_STRING, NGX_JS_EVENT_DOWNLOAD, }, { ngx_string("upstream"), NGX_JS_BUFFER, NGX_JS_EVENT_UPLOAD, }, { ngx_string("downstream"), NGX_JS_BUFFER, NGX_JS_EVENT_DOWNLOAD, }, }; ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module); i = 0; n = sizeof(events) / sizeof(events[0]); while (i < n) { if (event->length == events[i].name.len && ngx_memcmp(event->start, events[i].name.data, event->length) == 0) { break; } i++; } if (i == n) { njs_vm_error(ctx->engine->u.njs.vm, "unknown event \"%V\"", event); return NULL; } ctx->events[events[i].id].data_type = events[i].data_type; for (n = 0; n < NGX_JS_EVENT_MAX; n++) { type = ctx->events[n].data_type; if (type != NGX_JS_UNSET && type != events[i].data_type) { njs_vm_error(ctx->engine->u.njs.vm, "mixing string and buffer" " events is not allowed"); return NULL; } } return &ctx->events[events[i].id]; } static njs_int_t ngx_stream_js_ext_get_remote_address(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { ngx_connection_t *c; ngx_stream_session_t *s; s = njs_vm_external(vm, ngx_stream_js_session_proto_id, value); if (s == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } c = s->connection; return njs_vm_value_string_create(vm, retval, c->addr_text.data, c->addr_text.len); } static njs_int_t ngx_stream_js_ext_done(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t magic, njs_value_t *retval) { ngx_int_t status; njs_value_t *code; ngx_stream_js_ctx_t *ctx; ngx_stream_session_t *s; s = njs_vm_external(vm, ngx_stream_js_session_proto_id, njs_argument(args, 0)); if (s == NULL) { njs_vm_error(vm, "\"this\" is not an external"); return NJS_ERROR; } status = (ngx_int_t) magic; status = -status; if (status == NGX_DONE) { status = NGX_STREAM_FORBIDDEN; } code = njs_arg(args, nargs, 1); if (!njs_value_is_undefined(code)) { if (ngx_js_integer(vm, code, &status) != NGX_OK) { return NJS_ERROR; } if (status < NGX_ABORT || status > NGX_STREAM_SERVICE_UNAVAILABLE) { njs_vm_error(vm, "code is out of range"); return NJS_ERROR; } } ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module); if (ctx->filter) { njs_vm_error(vm, "should not be called while filtering"); return NJS_ERROR; } ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "stream js set status: %i", status); ctx->status = status; ngx_stream_js_drop_events(ctx); njs_value_undefined_set(retval); return NJS_OK; } static njs_int_t ngx_stream_js_ext_on(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_str_t name; njs_value_t *callback; ngx_stream_js_ev_t *event; ngx_stream_session_t *s; s = njs_vm_external(vm, ngx_stream_js_session_proto_id, njs_argument(args, 0)); if (s == NULL) { njs_vm_error(vm, "\"this\" is not an external"); return NJS_ERROR; } if (ngx_js_string(vm, njs_arg(args, nargs, 1), &name) == NJS_ERROR) { njs_vm_error(vm, "failed to convert event arg"); return NJS_ERROR; } callback = njs_arg(args, nargs, 2); if (!njs_value_is_function(callback)) { njs_vm_error(vm, "callback is not a function"); return NJS_ERROR; } event = ngx_stream_js_event(s, &name); if (event == NULL) { return NJS_ERROR; } if (njs_value_is_function(njs_value_arg(&event->function))) { njs_vm_error(vm, "event handler \"%V\" is already set", &name); return NJS_ERROR; } njs_value_assign(&event->function, callback); njs_value_undefined_set(retval); return NJS_OK; } static njs_int_t ngx_stream_js_ext_off(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_str_t name; ngx_stream_js_ev_t *event; ngx_stream_session_t *s; s = njs_vm_external(vm, ngx_stream_js_session_proto_id, njs_argument(args, 0)); if (s == NULL) { njs_vm_error(vm, "\"this\" is not an external"); return NJS_ERROR; } if (ngx_js_string(vm, njs_arg(args, nargs, 1), &name) == NJS_ERROR) { njs_vm_error(vm, "failed to convert event arg"); return NJS_ERROR; } event = ngx_stream_js_event(s, &name); if (event == NULL) { return NJS_ERROR; } njs_value_null_set(njs_value_arg(&event->function)); event->data_type = NGX_JS_UNSET; njs_value_undefined_set(retval); return NJS_OK; } static njs_int_t ngx_stream_js_ext_send(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t from_upstream, njs_value_t *retval) { unsigned last_buf, flush; njs_str_t buffer; ngx_buf_t *b; njs_value_t *flags, *value; ngx_chain_t *cl; ngx_connection_t *c; njs_opaque_value_t lvalue; ngx_stream_js_ctx_t *ctx; ngx_stream_session_t *s; static const njs_str_t last_key = njs_str("last"); static const njs_str_t flush_key = njs_str("flush"); static const njs_str_t from_key = njs_str("from_upstream"); s = njs_vm_external(vm, ngx_stream_js_session_proto_id, njs_argument(args, 0)); if (s == NULL) { njs_vm_error(vm, "\"this\" is not an external"); return NJS_ERROR; } c = s->connection; ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module); if (!ctx->filter) { njs_vm_error(vm, "cannot send buffer in this handler"); return NJS_ERROR; } if (ngx_js_string(vm, njs_arg(args, nargs, 1), &buffer) != NGX_OK) { njs_vm_error(vm, "failed to get buffer arg"); return NJS_ERROR; } /* * ctx->buf != NULL when s.send() is called while processing incoming * data chunks, otherwise s.send() is called asynchronously */ if (ctx->buf != NULL) { flush = ctx->buf->flush; last_buf = ctx->buf->last_buf; } else { flush = 0; last_buf = 0; } flags = njs_arg(args, nargs, 2); if (njs_value_is_object(flags)) { value = njs_vm_object_prop(vm, flags, &flush_key, &lvalue); if (value != NULL) { flush = njs_value_bool(value); } value = njs_vm_object_prop(vm, flags, &last_key, &lvalue); if (value != NULL) { last_buf = njs_value_bool(value); } if (from_upstream == NGX_JS_BOOL_UNSET) { value = njs_vm_object_prop(vm, flags, &from_key, &lvalue); if (value != NULL) { from_upstream = njs_value_bool(value); } if (value == NULL && ctx->buf == NULL) { goto exception; } } } cl = ngx_chain_get_free_buf(c->pool, &ctx->free); if (cl == NULL) { njs_vm_error(vm, "memory error"); return NJS_ERROR; } b = cl->buf; b->flush = flush; b->last_buf = last_buf; b->memory = (buffer.length ? 1 : 0); b->sync = (buffer.length ? 0 : 1); b->tag = (ngx_buf_tag_t) &ngx_stream_js_module; b->start = buffer.start; b->end = buffer.start + buffer.length; b->pos = b->start; b->last = b->end; if (from_upstream == NGX_JS_BOOL_UNSET) { *ctx->last_out = cl; ctx->last_out = &cl->next; } else { if (ngx_stream_js_next_filter(s, ctx, cl, from_upstream) == NGX_ERROR) { njs_vm_error(vm, "ngx_stream_js_next_filter() failed"); return NJS_ERROR; } } njs_value_undefined_set(retval); return NJS_OK; exception: njs_vm_error(vm, "\"from_upstream\" flag is expected when" "called asynchronously"); return NJS_ERROR; } static njs_int_t ngx_stream_js_ext_set_return_value(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { ngx_stream_js_ctx_t *ctx; ngx_stream_session_t *s; s = njs_vm_external(vm, ngx_stream_js_session_proto_id, njs_argument(args, 0)); if (s == NULL) { njs_vm_error(vm, "\"this\" is not an external"); return NJS_ERROR; } ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module); njs_value_assign(&ctx->retval, njs_arg(args, nargs, 1)); njs_value_undefined_set(retval); return NJS_OK; } static njs_int_t ngx_stream_js_session_variables(njs_vm_t *vm, njs_object_prop_t *prop, ngx_stream_session_t *s, njs_value_t *setval, njs_value_t *retval) { njs_int_t rc; njs_str_t val; ngx_str_t name; ngx_uint_t key; ngx_stream_variable_t *v; ngx_stream_core_main_conf_t *cmcf; ngx_stream_variable_value_t *vv; rc = njs_vm_prop_name(vm, prop, &val); if (rc != NJS_OK) { njs_value_undefined_set(retval); return NJS_DECLINED; } name.data = val.start; name.len = val.length; if (setval == NULL) { key = ngx_hash_strlow(name.data, name.data, name.len); vv = ngx_stream_get_variable(s, &name, key); if (vv == NULL || vv->not_found) { njs_value_undefined_set(retval); return NJS_DECLINED; } return ngx_js_prop(vm, njs_vm_prop_magic32(prop), retval, vv->data, vv->len); } cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module); key = ngx_hash_strlow(name.data, name.data, name.len); v = ngx_hash_find(&cmcf->variables_hash, key, name.data, name.len); if (v == NULL) { njs_vm_error(vm, "variable not found"); return NJS_ERROR; } if (ngx_js_string(vm, setval, &val) != NGX_OK) { return NJS_ERROR; } if (v->set_handler != NULL) { vv = ngx_pcalloc(s->connection->pool, sizeof(ngx_stream_variable_value_t)); if (vv == NULL) { return NJS_ERROR; } vv->valid = 1; vv->not_found = 0; vv->data = val.start; vv->len = val.length; v->set_handler(s, vv, v->data); return NJS_OK; } if (!(v->flags & NGX_STREAM_VAR_INDEXED)) { njs_vm_error(vm, "variable is not writable"); return NJS_ERROR; } vv = &s->variables[v->index]; vv->valid = 1; vv->not_found = 0; vv->data = ngx_pnalloc(s->connection->pool, val.length); if (vv->data == NULL) { return NJS_ERROR; } vv->len = val.length; ngx_memcpy(vv->data, val.start, vv->len); return NJS_OK; } static njs_int_t ngx_stream_js_ext_variables(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { ngx_stream_session_t *s; s = njs_vm_external(vm, ngx_stream_js_session_proto_id, value); if (s == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } return ngx_stream_js_session_variables(vm, prop, s, setval, retval); } static njs_int_t ngx_stream_js_periodic_variables(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { ngx_stream_session_t *s; s = njs_vm_external(vm, ngx_stream_js_periodic_session_proto_id, value); if (s == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } return ngx_stream_js_session_variables(vm, prop, s, setval, retval); } static ngx_pool_t * ngx_stream_js_pool(ngx_stream_session_t *s) { return s->connection->pool; } static ngx_resolver_t * ngx_stream_js_resolver(ngx_stream_session_t *s) { ngx_stream_core_srv_conf_t *cscf; cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module); return cscf->resolver; } static ngx_msec_t ngx_stream_js_resolver_timeout(ngx_stream_session_t *s) { ngx_stream_core_srv_conf_t *cscf; cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module); return cscf->resolver_timeout; } static ngx_msec_t ngx_stream_js_fetch_timeout(ngx_stream_session_t *s) { ngx_stream_js_srv_conf_t *jscf; jscf = ngx_stream_get_module_srv_conf(s, ngx_stream_js_module); return jscf->timeout; } static size_t ngx_stream_js_buffer_size(ngx_stream_session_t *s) { ngx_stream_js_srv_conf_t *jscf; jscf = ngx_stream_get_module_srv_conf(s, ngx_stream_js_module); return jscf->buffer_size; } static size_t ngx_stream_js_max_response_buffer_size(ngx_stream_session_t *s) { ngx_stream_js_srv_conf_t *jscf; jscf = ngx_stream_get_module_srv_conf(s, ngx_stream_js_module); return jscf->max_response_body_size; } static void ngx_stream_js_event_finalize(ngx_stream_session_t *s, ngx_int_t rc) { ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "http js event finalize rc: %i", rc); if (rc == NGX_ERROR) { if (s->health_check) { ngx_stream_js_periodic_finalize(s, NGX_ERROR); return; } ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); return; } if (rc == NGX_OK) { ngx_post_event(s->connection->read, &ngx_posted_events); } } static ngx_js_ctx_t * ngx_stream_js_ctx(ngx_stream_session_t *s) { return ngx_stream_get_module_ctx(s, ngx_stream_js_module); } static njs_int_t ngx_js_stream_init(njs_vm_t *vm) { ngx_stream_js_session_proto_id = njs_vm_external_prototype(vm, ngx_stream_js_ext_session, njs_nitems(ngx_stream_js_ext_session)); if (ngx_stream_js_session_proto_id < 0) { return NJS_ERROR; } ngx_stream_js_periodic_session_proto_id = njs_vm_external_prototype(vm, ngx_stream_js_ext_periodic_session, njs_nitems(ngx_stream_js_ext_periodic_session)); if (ngx_stream_js_periodic_session_proto_id < 0) { return NJS_ERROR; } ngx_stream_js_session_flags_proto_id = njs_vm_external_prototype(vm, ngx_stream_js_ext_session_flags, njs_nitems(ngx_stream_js_ext_session_flags)); if (ngx_stream_js_session_flags_proto_id < 0) { return NJS_ERROR; } return NJS_OK; } static ngx_engine_t * ngx_engine_njs_clone(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *cf, njs_int_t proto_id, void *external) { njs_int_t rc; ngx_engine_t *engine; ngx_stream_js_ctx_t *sctx; engine = ngx_njs_clone(ctx, cf, external); if (engine == NULL) { return NULL; } sctx = (ngx_stream_js_ctx_t *) ctx; sctx->run_event = ngx_stream_js_run_event; sctx->body_filter = ngx_stream_njs_body_filter; rc = njs_vm_external_create(engine->u.njs.vm, njs_value_arg(&ctx->args[0]), proto_id, njs_vm_external_ptr(engine->u.njs.vm), 0); if (rc != NJS_OK) { return NULL; } return engine; } #if (NJS_HAVE_QUICKJS) static JSValue ngx_stream_qjs_ext_to_string_tag(JSContext *cx, JSValueConst this_val) { return JS_NewString(cx, "Stream Session"); } static JSValue ngx_stream_qjs_ext_done(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) { ngx_int_t status; ngx_stream_js_ctx_t *ctx; ngx_stream_session_t *s; s = ngx_stream_qjs_session(this_val); if (s == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a session object"); } status = (ngx_int_t) magic; status = -status; if (status == NGX_DONE) { status = NGX_STREAM_FORBIDDEN; } if (!JS_IsUndefined(argv[0])) { if (ngx_qjs_integer(cx, argv[0], &status) != NGX_OK) { return JS_EXCEPTION; } if (status < NGX_ABORT || status > NGX_STREAM_SERVICE_UNAVAILABLE) { return JS_ThrowInternalError(cx, "code is out of range"); } } ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module); if (ctx->filter) { return JS_ThrowInternalError(cx, "should not be called while " "filtering"); } ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "stream js set status: %i", status); ctx->status = status; ngx_stream_js_drop_events(ctx); return JS_UNDEFINED; } static JSValue ngx_stream_qjs_ext_log(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int level) { int n; const char *msg; ngx_stream_session_t *s; s = ngx_stream_qjs_session(this_val); if (s == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a session object"); } for (n = 0; n < argc; n++) { msg = JS_ToCString(cx, argv[n]); ngx_js_logger(s->connection, level, (u_char *) msg, ngx_strlen(msg)); JS_FreeCString(cx, msg); } return JS_UNDEFINED; } static const ngx_stream_qjs_event_t * ngx_stream_qjs_event(ngx_stream_session_t *s, JSContext *cx, ngx_str_t *event) { ngx_uint_t i, n, type; ngx_stream_js_ctx_t *ctx; static const ngx_stream_qjs_event_t events[] = { { ngx_string("upload"), NGX_JS_STRING, NGX_JS_EVENT_UPLOAD, }, { ngx_string("download"), NGX_JS_STRING, NGX_JS_EVENT_DOWNLOAD, }, { ngx_string("upstream"), NGX_JS_BUFFER, NGX_JS_EVENT_UPLOAD, }, { ngx_string("downstream"), NGX_JS_BUFFER, NGX_JS_EVENT_DOWNLOAD, }, }; ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module); i = 0; n = sizeof(events) / sizeof(events[0]); while (i < n) { if (event->len == events[i].name.len && ngx_memcmp(event->data, events[i].name.data, event->len) == 0) { break; } i++; } if (i == n) { (void) JS_ThrowInternalError(cx, "unknown event \"%*s\"", (int) event->len, event->data); return NULL; } ctx->events[events[i].id].data_type = events[i].data_type; for (n = 0; n < NGX_JS_EVENT_MAX; n++) { type = ctx->events[n].data_type; if (type != NGX_JS_UNSET && type != events[i].data_type) { (void) JS_ThrowInternalError(cx, "mixing string and buffer" " events is not allowed"); return NULL; } } return &events[i]; } static JSValue ngx_stream_qjs_ext_on(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv) { ngx_str_t name; ngx_stream_js_ctx_t *ctx; ngx_stream_qjs_session_t *ses; const ngx_stream_qjs_event_t *e; ses = JS_GetOpaque(this_val, NGX_QJS_CLASS_ID_STREAM_SESSION); if (ses == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a session object"); } ctx = ngx_stream_get_module_ctx(ses->session, ngx_stream_js_module); if (ngx_qjs_string(ctx->engine, argv[0], &name) != NGX_OK) { return JS_EXCEPTION; } e = ngx_stream_qjs_event(ses->session, cx, &name); if (e == NULL) { return JS_EXCEPTION; } if (JS_IsFunction(cx, ngx_qjs_arg(ctx->events[e->id].function))) { return JS_ThrowInternalError(cx, "event handler \"%s\" is already set", name.data); } if (!JS_IsFunction(cx, argv[1])) { return JS_ThrowTypeError(cx, "callback is not a function"); } ngx_qjs_arg(ctx->events[e->id].function) = argv[1]; JS_FreeValue(cx, ses->callbacks[e->id]); ses->callbacks[e->id] = JS_DupValue(cx, argv[1]); return JS_UNDEFINED; } static JSValue ngx_stream_qjs_ext_off(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv) { ngx_str_t name; ngx_stream_js_ctx_t *ctx; ngx_stream_session_t *s; const ngx_stream_qjs_event_t *e; s = ngx_stream_qjs_session(this_val); if (s == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a session object"); } ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module); if (ngx_qjs_string(ctx->engine, argv[0], &name) != NGX_OK) { return JS_EXCEPTION; } e = ngx_stream_qjs_event(s, cx, &name); if (e == NULL) { return JS_EXCEPTION; } ngx_qjs_arg(ctx->events[e->id].function) = JS_NULL; ctx->events[e->id].data_type = NGX_JS_UNSET; return JS_UNDEFINED; } static JSValue ngx_stream_qjs_ext_periodic_to_string_tag(JSContext *cx, JSValueConst this_val) { return JS_NewString(cx, "PeriodicSession"); } static JSValue ngx_stream_qjs_ext_periodic_variables(JSContext *cx, JSValueConst this_val, int type) { JSValue obj; ngx_stream_qjs_session_t *ses; ses = JS_GetOpaque(this_val, NGX_QJS_CLASS_ID_STREAM_PERIODIC); if (ses == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a periodic object"); } obj = JS_NewObjectProtoClass(cx, JS_NULL, NGX_QJS_CLASS_ID_STREAM_VARS); /* * Using lowest bit of the pointer to store the buffer type. */ type = (type == NGX_JS_BUFFER) ? 1 : 0; JS_SetOpaque(obj, (void *) ((uintptr_t) ses->session | (uintptr_t) type)); return obj; } static JSValue ngx_stream_qjs_ext_remote_address(JSContext *cx, JSValueConst this_val) { ngx_connection_t *c; ngx_stream_session_t *s; s = ngx_stream_qjs_session(this_val); if (s == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a session object"); } c = s->connection; return qjs_string_create(cx, c->addr_text.data, c->addr_text.len); } static JSValue ngx_stream_qjs_ext_send(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv, int from_upstream) { JSValue val; unsigned last_buf, flush; ngx_str_t buffer; ngx_buf_t *b; ngx_chain_t *cl; ngx_connection_t *c; ngx_stream_js_ctx_t *ctx; ngx_stream_session_t *s; s = ngx_stream_qjs_session(this_val); if (s == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a session object"); } c = s->connection; ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module); if (!ctx->filter) { return JS_ThrowInternalError(cx, "cannot send buffer in this handler"); } if (ngx_qjs_string(ctx->engine, argv[0], &buffer) != NGX_OK) { return JS_EXCEPTION; } /* * ctx->buf != NULL when s.send() is called while processing incoming * data chunks, otherwise s.send() is called asynchronously */ if (ctx->buf != NULL) { flush = ctx->buf->flush; last_buf = ctx->buf->last_buf; } else { flush = 0; last_buf = 0; } if (JS_IsObject(argv[1])) { val = JS_GetPropertyStr(cx, argv[1], "flush"); if (JS_IsException(val)) { return JS_EXCEPTION; } if (!JS_IsUndefined(val)) { flush = JS_ToBool(cx, val); JS_FreeValue(cx, val); } val = JS_GetPropertyStr(cx, argv[1], "last"); if (JS_IsException(val)) { return JS_EXCEPTION; } if (!JS_IsUndefined(val)) { last_buf = JS_ToBool(cx, val); JS_FreeValue(cx, val); } if (from_upstream == NGX_JS_BOOL_UNSET) { val = JS_GetPropertyStr(cx, argv[1], "from_upstream"); if (JS_IsException(val)) { return JS_EXCEPTION; } if (!JS_IsUndefined(val)) { from_upstream = JS_ToBool(cx, val); JS_FreeValue(cx, val); } if (from_upstream == NGX_JS_BOOL_UNSET && ctx->buf == NULL) { return JS_ThrowInternalError(cx, "from_upstream flag is " "expected when called " "asynchronously"); } } } cl = ngx_chain_get_free_buf(c->pool, &ctx->free); if (cl == NULL) { return JS_ThrowInternalError(cx, "memory error"); } b = cl->buf; b->flush = flush; b->last_buf = last_buf; b->memory = (buffer.len ? 1 : 0); b->sync = (buffer.len ? 0 : 1); b->tag = (ngx_buf_tag_t) &ngx_stream_js_module; b->start = buffer.data; b->end = buffer.data + buffer.len; b->pos = b->start; b->last = b->end; if (from_upstream == NGX_JS_BOOL_UNSET) { *ctx->last_out = cl; ctx->last_out = &cl->next; } else { if (ngx_stream_js_next_filter(s, ctx, cl, from_upstream) == NGX_ERROR) { return JS_ThrowInternalError(cx, "ngx_stream_js_next_filter() " "failed"); } } return JS_UNDEFINED; } static JSValue ngx_stream_qjs_ext_set_return_value(JSContext *cx, JSValueConst this_val, int argc, JSValueConst *argv) { ngx_js_ctx_t *ctx; ngx_stream_session_t *s; s = ngx_stream_qjs_session(this_val); if (s == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a session object"); } ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module); JS_FreeValue(cx, ngx_qjs_arg(ctx->retval)); ngx_qjs_arg(ctx->retval) = JS_DupValue(cx, argv[0]); return JS_UNDEFINED; } static JSValue ngx_stream_qjs_ext_variables(JSContext *cx, JSValueConst this_val, int type) { JSValue obj; ngx_stream_session_t *s; s = ngx_stream_qjs_session(this_val); if (s == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a session object"); } obj = JS_NewObjectProtoClass(cx, JS_NULL, NGX_QJS_CLASS_ID_STREAM_VARS); /* * Using lowest bit of the pointer to store the buffer type. */ type = (type == NGX_JS_BUFFER) ? 1 : 0; JS_SetOpaque(obj, (void *) ((uintptr_t) s | (uintptr_t) type)); return obj; } static JSValue ngx_stream_qjs_ext_uint(JSContext *cx, JSValueConst this_val, int offset) { ngx_uint_t *field; ngx_stream_session_t *s; s = ngx_stream_qjs_session(this_val); if (s == NULL) { return JS_ThrowInternalError(cx, "\"this\" is not a session object"); } field = (ngx_uint_t *) ((u_char *) s + offset); return JS_NewUint32(cx, *field); } static JSValue ngx_stream_qjs_ext_flag(JSContext *cx, JSValueConst this_val, int mask) { uintptr_t flags; flags = (uintptr_t) JS_GetOpaque(this_val, NGX_QJS_CLASS_ID_STREAM_FLAGS); return JS_NewBool(cx, flags & mask); } static int ngx_stream_qjs_variables_own_property(JSContext *cx, JSPropertyDescriptor *pdesc, JSValueConst obj, JSAtom prop) { uint32_t buffer_type; ngx_str_t name; ngx_uint_t key; ngx_stream_session_t *s; ngx_stream_variable_value_t *vv; s = JS_GetOpaque(obj, NGX_QJS_CLASS_ID_STREAM_VARS); buffer_type = ((uintptr_t) s & 1) ? NGX_JS_BUFFER : NGX_JS_STRING; s = (ngx_stream_session_t *) ((uintptr_t) s & ~(uintptr_t) 1); if (s == NULL) { (void) JS_ThrowInternalError(cx, "\"this\" is not a session object"); return -1; } name.data = (u_char *) JS_AtomToCString(cx, prop); if (name.data == NULL) { return -1; } name.len = ngx_strlen(name.data); key = ngx_hash_strlow(name.data, name.data, name.len); vv = ngx_stream_get_variable(s, &name, key); JS_FreeCString(cx, (char *) name.data); if (vv == NULL || vv->not_found) { return 0; } if (pdesc != NULL) { pdesc->flags = JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE; pdesc->getter = JS_UNDEFINED; pdesc->setter = JS_UNDEFINED; pdesc->value = ngx_qjs_prop(cx, buffer_type, vv->data, vv->len); } return 1; } static int ngx_stream_qjs_variables_set_property(JSContext *cx, JSValueConst obj, JSAtom prop, JSValueConst value, JSValueConst receiver, int flags) { ngx_str_t name, val; ngx_uint_t key; ngx_js_ctx_t *ctx; ngx_stream_session_t *s; ngx_stream_variable_t *v; ngx_stream_variable_value_t *vv; ngx_stream_core_main_conf_t *cmcf; s = JS_GetOpaque(obj, NGX_QJS_CLASS_ID_STREAM_VARS); s = (ngx_stream_session_t *) ((uintptr_t) s & ~(uintptr_t) 1); if (s == NULL) { (void) JS_ThrowInternalError(cx, "\"this\" is not a session object"); return -1; } name.data = (u_char *) JS_AtomToCString(cx, prop); if (name.data == NULL) { return -1; } name.len = ngx_strlen(name.data); key = ngx_hash_strlow(name.data, name.data, name.len); cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module); v = ngx_hash_find(&cmcf->variables_hash, key, name.data, name.len); JS_FreeCString(cx, (char *) name.data); if (v == NULL) { (void) JS_ThrowInternalError(cx, "variable not found"); return -1; } ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module); if (ngx_qjs_string(ctx->engine, value, &val) != NGX_OK) { return -1; } if (v->set_handler != NULL) { vv = ngx_pcalloc(s->connection->pool, sizeof(ngx_stream_variable_value_t)); if (vv == NULL) { (void) JS_ThrowOutOfMemory(cx); return -1; } vv->valid = 1; vv->not_found = 0; vv->data = val.data; vv->len = val.len; v->set_handler(s, vv, v->data); return 1; } if (!(v->flags & NGX_STREAM_VAR_INDEXED)) { (void) JS_ThrowTypeError(cx, "variable is not writable"); return -1; } vv = &s->variables[v->index]; vv->valid = 1; vv->not_found = 0; vv->data = ngx_pnalloc(s->connection->pool, val.len); if (vv->data == NULL) { vv->valid = 0; (void) JS_ThrowOutOfMemory(cx); return -1; } vv->len = val.len; ngx_memcpy(vv->data, val.data, vv->len); return 1; } static int ngx_stream_qjs_variables_define_own_property(JSContext *cx, JSValueConst obj, JSAtom prop, JSValueConst value, JSValueConst getter, JSValueConst setter, int flags) { if (!JS_IsUndefined(setter) || !JS_IsUndefined(getter)) { (void) JS_ThrowTypeError(cx, "cannot define getter or setter"); return -1; } return ngx_stream_qjs_variables_set_property(cx, obj, prop, value, obj, flags); } static ngx_int_t ngx_stream_qjs_run_event(ngx_stream_session_t *s, ngx_stream_js_ctx_t *ctx, ngx_stream_js_ev_t *event, ngx_uint_t from_upstream) { size_t len; u_char *p; JSContext *cx; ngx_int_t rc; ngx_str_t exception; ngx_buf_t *b; uintptr_t flags; ngx_connection_t *c; JSValue argv[2]; cx = ctx->engine->u.qjs.ctx; if (!JS_IsFunction(cx, ngx_qjs_arg(event->function))) { return NGX_OK; } c = s->connection; b = ctx->filter ? ctx->buf : c->buffer; len = b ? b->last - b->pos : 0; p = ngx_pnalloc(c->pool, len); if (p == NULL) { (void) JS_ThrowOutOfMemory(cx); goto error; } if (len) { ngx_memcpy(p, b->pos, len); } argv[0] = ngx_qjs_prop(cx, event->data_type, p, len); if (JS_IsException(argv[0])) { goto error; } argv[1] = JS_NewObjectClass(cx, NGX_QJS_CLASS_ID_STREAM_FLAGS); if (JS_IsException(argv[1])) { JS_FreeValue(cx, argv[0]); goto error; } flags = from_upstream << 1 | (uintptr_t) (b && b->last_buf); JS_SetOpaque(argv[1], (void *) flags); rc = ngx_qjs_call((ngx_js_ctx_t *) ctx, ngx_qjs_arg(event->function), &argv[0], 2); JS_FreeValue(cx, argv[0]); JS_FreeValue(cx, argv[1]); if (rc == NGX_ERROR) { error: ngx_qjs_exception(ctx->engine, &exception); ngx_log_error(NGX_LOG_ERR, c->log, 0, "js exception: %V", &exception); return NGX_ERROR; } return NGX_OK; } static ngx_int_t ngx_stream_qjs_body_filter(ngx_stream_session_t *s, ngx_stream_js_ctx_t *ctx, ngx_chain_t *in, ngx_uint_t from_upstream) { ngx_int_t rc; JSContext *cx; ngx_chain_t *cl; ngx_stream_js_ev_t *event; cx = ctx->engine->u.qjs.ctx; while (in != NULL) { ctx->buf = in->buf; event = ngx_stream_event(from_upstream); if (JS_IsFunction(cx, ngx_qjs_arg(event->function))) { rc = ngx_stream_qjs_run_event(s, ctx, event, from_upstream); if (rc != NGX_OK) { return NGX_ERROR; } ctx->buf->pos = ctx->buf->last; } else { cl = ngx_alloc_chain_link(s->connection->pool); if (cl == NULL) { return NGX_ERROR; } cl->buf = ctx->buf; *ctx->last_out = cl; ctx->last_out = &cl->next; } in = in->next; } return NGX_OK; } static ngx_stream_session_t * ngx_stream_qjs_session(JSValueConst val) { ngx_stream_qjs_session_t *ses; ses = JS_GetOpaque(val, NGX_QJS_CLASS_ID_STREAM_SESSION); if (ses == NULL) { return NULL; } return ses->session; } static JSValue ngx_stream_qjs_session_make(JSContext *cx, ngx_int_t proto_id, ngx_stream_session_t *s) { JSValue session; ngx_uint_t i; ngx_stream_qjs_session_t *ses; session = JS_NewObjectClass(cx, proto_id); if (JS_IsException(session)) { return JS_EXCEPTION; } ses = js_malloc(cx, sizeof(ngx_stream_qjs_session_t)); if (ses == NULL) { return JS_ThrowOutOfMemory(cx); } ses->session = s; for (i = 0; i < NGX_JS_EVENT_MAX; i++) { ses->callbacks[i] = JS_UNDEFINED; } JS_SetOpaque(session, ses); return session; } static void ngx_stream_qjs_session_finalizer(JSRuntime *rt, JSValue val) { ngx_uint_t i; ngx_stream_qjs_session_t *ses; ses = JS_GetOpaque(val, NGX_QJS_CLASS_ID_STREAM_SESSION); if (ses == NULL) { return; } for (i = 0; i < NGX_JS_EVENT_MAX; i++) { JS_FreeValueRT(rt, ses->callbacks[i]); } js_free_rt(rt, ses); } static ngx_engine_t * ngx_engine_qjs_clone(ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *cf, njs_int_t proto_id, void *external) { JSValue proto; JSContext *cx; ngx_engine_t *engine; ngx_stream_js_ctx_t *sctx; engine = ngx_qjs_clone(ctx, cf, external); if (engine == NULL) { return NULL; } cx = engine->u.qjs.ctx; if (!JS_IsRegisteredClass(JS_GetRuntime(cx), NGX_QJS_CLASS_ID_STREAM_SESSION)) { if (JS_NewClass(JS_GetRuntime(cx), NGX_QJS_CLASS_ID_STREAM_SESSION, &ngx_stream_qjs_session_class) < 0) { return NULL; } proto = JS_NewObject(cx); if (JS_IsException(proto)) { return NULL; } JS_SetPropertyFunctionList(cx, proto, ngx_stream_qjs_ext_session, njs_nitems(ngx_stream_qjs_ext_session)); JS_SetClassProto(cx, NGX_QJS_CLASS_ID_STREAM_SESSION, proto); if (JS_NewClass(JS_GetRuntime(cx), NGX_QJS_CLASS_ID_STREAM_PERIODIC, &ngx_stream_qjs_periodic_class) < 0) { return NULL; } proto = JS_NewObject(cx); if (JS_IsException(proto)) { return NULL; } JS_SetPropertyFunctionList(cx, proto, ngx_stream_qjs_ext_periodic, njs_nitems(ngx_stream_qjs_ext_periodic)); JS_SetClassProto(cx, NGX_QJS_CLASS_ID_STREAM_PERIODIC, proto); if (JS_NewClass(JS_GetRuntime(cx), NGX_QJS_CLASS_ID_STREAM_FLAGS, &ngx_stream_qjs_flags_class) < 0) { return NULL; } proto = JS_NewObject(cx); if (JS_IsException(proto)) { return NULL; } JS_SetPropertyFunctionList(cx, proto, ngx_stream_qjs_ext_flags, njs_nitems(ngx_stream_qjs_ext_flags)); JS_SetClassProto(cx, NGX_QJS_CLASS_ID_STREAM_FLAGS, proto); if (JS_NewClass(JS_GetRuntime(cx), NGX_QJS_CLASS_ID_STREAM_VARS, &ngx_stream_qjs_variables_class) < 0) { return NULL; } } sctx = (ngx_stream_js_ctx_t *) ctx; sctx->run_event = ngx_stream_qjs_run_event; sctx->body_filter = ngx_stream_qjs_body_filter; if (proto_id == ngx_stream_js_session_proto_id) { proto_id = NGX_QJS_CLASS_ID_STREAM_SESSION; } else if (proto_id == ngx_stream_js_periodic_session_proto_id) { proto_id = NGX_QJS_CLASS_ID_STREAM_PERIODIC; } ngx_qjs_arg(ctx->args[0]) = ngx_stream_qjs_session_make(cx, proto_id, external); if (JS_IsException(ngx_qjs_arg(ctx->args[0]))) { return NULL; } return engine; } static void ngx_stream_qjs_destroy(ngx_engine_t *e, ngx_js_ctx_t *ctx, ngx_js_loc_conf_t *conf) { ngx_uint_t i; JSValue cb; ngx_stream_qjs_session_t *ses; if (ctx != NULL) { /* * explicitly freeing the callback functions * to avoid circular references with the session object. */ ses = JS_GetOpaque(ngx_qjs_arg(ctx->args[0]), NGX_QJS_CLASS_ID_STREAM_SESSION); if (ses != NULL) { for (i = 0; i < NGX_JS_EVENT_MAX; i++) { cb = ses->callbacks[i]; ses->callbacks[i] = JS_UNDEFINED; JS_FreeValue(e->u.qjs.ctx, cb); } } } ngx_engine_qjs_destroy(e, ctx, conf); } #endif static ngx_int_t ngx_stream_js_init_conf_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf) { ngx_engine_opts_t options; ngx_js_main_conf_t *jmcf; memset(&options, 0, sizeof(ngx_engine_opts_t)); options.engine = conf->type; jmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_js_module); ngx_stream_js_uptr[NGX_JS_MAIN_CONF_INDEX] = (uintptr_t) jmcf; if (conf->type == NGX_ENGINE_NJS) { options.u.njs.metas = &ngx_stream_js_metas; options.u.njs.addons = njs_stream_js_addon_modules; options.clone = ngx_engine_njs_clone; } #if (NJS_HAVE_QUICKJS) else if (conf->type == NGX_ENGINE_QJS) { options.u.qjs.metas = ngx_stream_js_uptr; options.u.qjs.addons = njs_stream_qjs_addon_modules; options.clone = ngx_engine_qjs_clone; options.destroy = ngx_stream_qjs_destroy; } #endif return ngx_js_init_conf_vm(cf, conf, &options); } static void ngx_stream_js_periodic_handler(ngx_event_t *ev) { ngx_int_t rc; ngx_msec_t timer; ngx_js_periodic_t *periodic; ngx_connection_t *c; ngx_stream_js_ctx_t *ctx; ngx_stream_session_t *s; ngx_stream_core_main_conf_t *cmcf; if (ngx_terminate || ngx_exiting) { return; } periodic = ev->data; timer = periodic->interval; if (periodic->jitter) { timer += (ngx_msec_t) ngx_random() % periodic->jitter; } ngx_add_timer(&periodic->event, timer); c = periodic->connection; if (c != NULL) { ngx_log_error(NGX_LOG_ERR, c->log, 0, "stream js periodic \"%V\" is already running, killing " "previous instance", &periodic->method); ngx_stream_js_periodic_finalize(c->data, NGX_ERROR); } ngx_log_debug1(NGX_LOG_DEBUG_STREAM, &periodic->log, 0, "stream js periodic handler: \"%V\"", &periodic->method); c = ngx_get_connection(0, &periodic->log); if (c == NULL) { return; } c->pool = ngx_create_pool(1024, c->log); if (c->pool == NULL) { goto free_connection; } s = ngx_pcalloc(c->pool, sizeof(ngx_stream_session_t)); if (s == NULL) { goto free_pool; } s->main_conf = periodic->conf_ctx->main_conf; s->srv_conf = periodic->conf_ctx->srv_conf; s->ctx = ngx_pcalloc(c->pool, sizeof(void *) * ngx_stream_max_module); if (s->ctx == NULL) { goto free_pool; } cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module); s->variables = ngx_pcalloc(c->pool, cmcf->variables.nelts * sizeof(ngx_stream_variable_value_t)); if (s->variables == NULL) { goto free_pool; } c->data = s; c->destroyed = 0; c->read->log = &periodic->log; c->read->handler = ngx_stream_js_periodic_event_handler; s->received = 1; s->connection = c; s->signature = NGX_STREAM_MODULE; s->health_check = 1; rc = ngx_stream_js_init_vm(s, ngx_stream_js_periodic_session_proto_id); if (rc != NGX_OK) { ngx_stream_js_periodic_destroy(s, periodic); return; } periodic->connection = c; ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module); ctx->periodic = periodic; s->received++; rc = ctx->engine->call((ngx_js_ctx_t *) ctx, &periodic->method, &ctx->args[0], 1); if (rc == NGX_AGAIN) { rc = NGX_OK; } s->received--; ngx_stream_js_periodic_finalize(s, rc); return; free_pool: ngx_destroy_pool(c->pool); free_connection: ngx_close_connection(c); } static void ngx_stream_js_periodic_event_handler(ngx_event_t *ev) { ngx_connection_t *c; ngx_stream_js_ctx_t *ctx; ngx_stream_session_t *s; c = ev->data; ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, "stream js periodic event handler"); if (c->close) { ngx_stream_js_periodic_finalize(c->data, NGX_ERROR); return; } s = c->data; ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module); if (!ngx_js_ctx_pending(ctx)) { ngx_stream_js_periodic_finalize(s, NGX_OK); return; } } static void ngx_stream_js_periodic_finalize(ngx_stream_session_t *s, ngx_int_t rc) { ngx_stream_js_ctx_t *ctx; ctx = ngx_stream_get_module_ctx(s, ngx_stream_js_module); ngx_log_debug4(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, "stream js periodic finalize: \"%V\" rc: %i c: %i " "pending: %i", &ctx->periodic->method, rc, s->received, ngx_js_ctx_pending(ctx)); if (s->received > 1 || (rc == NGX_OK && ngx_js_ctx_pending(ctx))) { return; } ngx_stream_js_periodic_destroy(s, ctx->periodic); } static void ngx_stream_js_periodic_destroy(ngx_stream_session_t *s, ngx_js_periodic_t *periodic) { ngx_connection_t *c; c = s->connection; ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, "stream js periodic request destroy: \"%V\"", &periodic->method); periodic->connection = NULL; ngx_free_connection(c); ngx_destroy_pool(c->pool); c->fd = (ngx_socket_t) -1; c->pool = NULL; c->destroyed = 1; if (c->read->posted) { ngx_delete_posted_event(c->read); } } static ngx_int_t ngx_stream_js_periodic_init(ngx_js_periodic_t *periodic) { ngx_log_t *log; ngx_msec_t jitter; ngx_stream_core_srv_conf_t *cscf; cscf = ngx_stream_get_module_srv_conf(periodic->conf_ctx, ngx_stream_core_module); log = cscf->error_log; ngx_memcpy(&periodic->log, log, sizeof(ngx_log_t)); periodic->connection = NULL; periodic->event.handler = ngx_stream_js_periodic_handler; periodic->event.data = periodic; periodic->event.log = log; periodic->event.cancelable = 1; jitter = periodic->jitter ? (ngx_msec_t) ngx_random() % periodic->jitter : 0; ngx_add_timer(&periodic->event, jitter + 1); return NGX_OK; } static ngx_int_t ngx_stream_js_init_worker(ngx_cycle_t *cycle) { ngx_uint_t i; ngx_js_periodic_t *periodics; ngx_js_main_conf_t *jmcf; if ((ngx_process != NGX_PROCESS_WORKER) && ngx_process != NGX_PROCESS_SINGLE) { return NGX_OK; } jmcf = ngx_stream_cycle_get_module_main_conf(cycle, ngx_stream_js_module); if (jmcf == NULL || jmcf->periodics == NULL) { return NGX_OK; } periodics = jmcf->periodics->elts; for (i = 0; i < jmcf->periodics->nelts; i++) { if (periodics[i].worker_affinity != NULL && !periodics[i].worker_affinity[ngx_worker]) { continue; } if (periodics[i].worker_affinity == NULL && ngx_worker != 0) { continue; } periodics[i].fd = 1000000 + i; if (ngx_stream_js_periodic_init(&periodics[i]) != NGX_OK) { return NGX_ERROR; } } return NGX_OK; } static char * ngx_stream_js_periodic(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { uint8_t *mask; ngx_str_t *value, s; ngx_msec_t interval, jitter; ngx_uint_t i; ngx_core_conf_t *ccf; ngx_js_periodic_t *periodic; ngx_js_main_conf_t *jmcf; if (cf->args->nelts < 2) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "method name is required"); return NGX_CONF_ERROR; } jmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_js_module); if (jmcf->periodics == NULL) { jmcf->periodics = ngx_array_create(cf->pool, 1, sizeof(ngx_js_periodic_t)); if (jmcf->periodics == NULL) { return NGX_CONF_ERROR; } } periodic = ngx_array_push(jmcf->periodics); if (periodic == NULL) { return NGX_CONF_ERROR; } ngx_memzero(periodic, sizeof(ngx_js_periodic_t)); mask = NULL; jitter = 0; interval = 5000; value = cf->args->elts; for (i = 2; i < cf->args->nelts; i++) { if (ngx_strncmp(value[i].data, "interval=", 9) == 0) { s.len = value[i].len - 9; s.data = value[i].data + 9; interval = ngx_parse_time(&s, 0); if (interval == (ngx_msec_t) NGX_ERROR || interval == 0) { goto invalid; } continue; } if (ngx_strncmp(value[i].data, "jitter=", 7) == 0) { s.len = value[i].len - 7; s.data = value[i].data + 7; jitter = ngx_parse_time(&s, 0); if (jitter == (ngx_msec_t) NGX_ERROR) { goto invalid; } continue; } if (ngx_strncmp(value[i].data, "worker_affinity=", 16) == 0) { s.len = value[i].len - 16; s.data = value[i].data + 16; ccf = (ngx_core_conf_t *) ngx_get_conf(cf->cycle->conf_ctx, ngx_core_module); if (ccf->worker_processes == NGX_CONF_UNSET) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"worker_affinity\" is not supported " "with unset \"worker_processes\" directive"); return NGX_CONF_ERROR; } mask = ngx_palloc(cf->pool, ccf->worker_processes); if (mask == NULL) { return NGX_CONF_ERROR; } if (ngx_strncmp(s.data, "all", 3) == 0) { memset(mask, 1, ccf->worker_processes); continue; } if ((size_t) ccf->worker_processes != s.len) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "the number of " "\"worker_processes\" is not equal to the " "size of \"worker_affinity\" mask"); return NGX_CONF_ERROR; } for (i = 0; i < s.len; i++) { if (s.data[i] == '0') { mask[i] = 0; continue; } if (s.data[i] == '1') { mask[i] = 1; continue; } ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid character \"%c\" in \"worker_affinity=\"", s.data[i]); return NGX_CONF_ERROR; } continue; } invalid: ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"", &value[i]); return NGX_CONF_ERROR; } periodic->method = value[1]; periodic->interval = interval; periodic->jitter = jitter; periodic->worker_affinity = mask; periodic->conf_ctx = cf->ctx; return NGX_CONF_OK; } static char * ngx_stream_js_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_str_t *value; ngx_js_set_t *data, *prev; ngx_stream_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_stream_add_variable(cf, &value[1], NGX_STREAM_VAR_CHANGEABLE); if (v == NULL) { return NGX_CONF_ERROR; } data = ngx_palloc(cf->pool, sizeof(ngx_js_set_t)); if (data == NULL) { return NGX_CONF_ERROR; } data->fname = value[2]; if (v->get_handler == ngx_stream_js_variable_set) { prev = (ngx_js_set_t *) v->data; if (data->fname.len != prev->fname.len || ngx_strncmp(data->fname.data, prev->fname.data, data->fname.len) != 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "variable \"%V\" is redeclared with " "different function name", &value[1]); return NGX_CONF_ERROR; } } if (cf->args->nelts == 4) { if (ngx_strcmp(value[3].data, "nocache") == 0) { data->flags |= NGX_NJS_VAR_NOCACHE; } else { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unrecognized flag \"%V\"", &value[3]); return NGX_CONF_ERROR; } } v->get_handler = ngx_stream_js_variable_set; v->data = (uintptr_t) data; return NGX_CONF_OK; } static char * ngx_stream_js_var(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_str_t *value; ngx_int_t index; ngx_stream_variable_t *v; ngx_stream_complex_value_t *cv; ngx_stream_compile_complex_value_t ccv; 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_stream_add_variable(cf, &value[1], NGX_STREAM_VAR_CHANGEABLE); if (v == NULL) { return NGX_CONF_ERROR; } index = ngx_stream_get_variable_index(cf, &value[1]); if (index == NGX_ERROR) { return NGX_CONF_ERROR; } cv = NULL; if (cf->args->nelts == 3) { cv = ngx_palloc(cf->pool, sizeof(ngx_stream_complex_value_t)); if (cv == NULL) { return NGX_CONF_ERROR; } ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t)); ccv.cf = cf; ccv.value = &value[2]; ccv.complex_value = cv; if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) { return NGX_CONF_ERROR; } } v->get_handler = ngx_stream_js_variable_var; v->data = (uintptr_t) cv; return NGX_CONF_OK; } static void * ngx_stream_js_create_main_conf(ngx_conf_t *cf) { ngx_js_main_conf_t *jmcf; jmcf = ngx_pcalloc(cf->pool, sizeof(ngx_js_main_conf_t)); if (jmcf == NULL) { return NULL; } /* * set by ngx_pcalloc(): * * jmcf->dicts = NULL; * jmcf->periodics = NULL; */ return jmcf; } static void * ngx_stream_js_create_srv_conf(ngx_conf_t *cf) { ngx_stream_js_srv_conf_t *conf = (ngx_stream_js_srv_conf_t *) ngx_js_create_conf( cf, sizeof(ngx_stream_js_srv_conf_t)); if (conf == NULL) { return NULL; } #if (NGX_STREAM_SSL) conf->ssl_verify = NGX_CONF_UNSET; conf->ssl_verify_depth = NGX_CONF_UNSET; #endif return conf; } static char * ngx_stream_js_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) { ngx_stream_js_srv_conf_t *prev = parent; ngx_stream_js_srv_conf_t *conf = child; ngx_conf_merge_str_value(conf->access, prev->access, ""); ngx_conf_merge_str_value(conf->preread, prev->preread, ""); ngx_conf_merge_str_value(conf->filter, prev->filter, ""); return ngx_js_merge_conf(cf, parent, child, ngx_stream_js_init_conf_vm); } static char * ngx_stream_js_shared_dict_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { return ngx_js_shared_dict_zone(cf, cmd, conf, &ngx_stream_js_module); } static ngx_int_t ngx_stream_js_init(ngx_conf_t *cf) { ngx_stream_handler_pt *h; ngx_stream_core_main_conf_t *cmcf; ngx_stream_next_filter = ngx_stream_top_filter; ngx_stream_top_filter = ngx_stream_js_body_filter; cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module); h = ngx_array_push(&cmcf->phases[NGX_STREAM_ACCESS_PHASE].handlers); if (h == NULL) { return NGX_ERROR; } *h = ngx_stream_js_access_handler; h = ngx_array_push(&cmcf->phases[NGX_STREAM_PREREAD_PHASE].handlers); if (h == NULL) { return NGX_ERROR; } *h = ngx_stream_js_preread_handler; return NGX_OK; } static ngx_ssl_t * ngx_stream_js_ssl(ngx_stream_session_t *s) { #if (NGX_STREAM_SSL) ngx_stream_js_srv_conf_t *jscf; jscf = ngx_stream_get_module_srv_conf(s, ngx_stream_js_module); return jscf->ssl; #else return NULL; #endif } static ngx_flag_t ngx_stream_js_ssl_verify(ngx_stream_session_t *s) { #if (NGX_STREAM_SSL) ngx_stream_js_srv_conf_t *jscf; jscf = ngx_stream_get_module_srv_conf(s, ngx_stream_js_module); return jscf->ssl_verify; #else return 0; #endif } njs-0.8.9/nginx/t/000077500000000000000000000000001474132077100137165ustar00rootroot00000000000000njs-0.8.9/nginx/t/README000066400000000000000000000005441474132077100146010ustar00rootroot00000000000000Test suite for nginx JavaScript module. This test suite relies on nginx-tests repository for the test library. Use prove to run tests as one usually do for perl tests. Individual tests may be run as well. Usage: $ TEST_NGINX_BINARY=/path/to/nginx prove -r -I /path/to/nginx-tests/lib/ nginx/t Refer to nginx-tests documentation for more details. njs-0.8.9/nginx/t/js.t000066400000000000000000000227271474132077100145310ustar00rootroot00000000000000#!/usr/bin/perl # (C) Roman Arutyunyan # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for http njs module. ############################################################################### use warnings; use strict; use Test::More; use Socket qw/ CRLF /; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http rewrite/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_set $test_method test.method; js_set $test_version test.version; js_set $test_addr test.addr; js_set $test_uri test.uri; js_set $test_var test.variable; js_set $test_type test.type; js_set $test_global test.global_obj; js_set $test_log test.log; js_set $test_internal test.sub_internal; js_set $buffer test.buffer; js_set $test_except test.except; js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /njs { js_content test.njs; } location /method { return 200 $test_method; } location /version { return 200 $test_version; } location /addr { return 200 $test_addr; } location /uri { return 200 $test_uri; } location /var { return 200 $test_var; } location /global { return 200 $test_global; } location /body { js_content test.request_body; } location /in_file { client_body_in_file_only on; js_content test.request_body; } location /status { js_content test.status; } location /buffer_variable { js_content test.buffer_variable; } location /request_body { js_content test.request_body; } location /request_body_cache { js_content test.request_body_cache; } location /send { js_content test.send; } location /send_buffer { js_content test.send_buffer; } location /return_method { js_content test.return_method; } location /type { js_content test.type; } location /log { return 200 $test_log; } location /internal { js_content test.internal; } location /sub_internal { internal; return 200 $test_internal; } location /except { return 200 $test_except; } location /content_except { js_content test.content_except; } location /content_empty { js_content test.content_empty; } } } EOF $t->write_file('test.js', < a[v], r); var typ = Buffer.isBuffer(p) ? 'buffer' : (typeof p); r.return(200, `type: \${typ}`); } function log(r) { r.log('SEE-LOG'); } function buffer_variable(r) { r.return(200, r.rawVariables.buffer.toString('hex')); } async function internal(r) { let reply = await r.subrequest('/sub_internal'); r.return(200, `parent: \${r.internal} sub: \${reply.responseText}`); } function sub_internal(r) { return r.internal; } function except(r) { decodeURI("%E0"); } function content_except(r) { return {}.a.a; } function content_empty(r) { } export default {njs:test_njs, method, version, addr, uri, buffer, variable, global_obj, status, request_body, internal, request_body_cache, send, return_method, sub_internal, type, log, buffer_variable, except, content_except, content_empty, send_buffer}; EOF $t->try_run('no njs available')->plan(29); ############################################################################### like(http_get('/method'), qr/method=GET/, 'r.method'); like(http_get('/version'), qr/version=1.0/, 'r.httpVersion'); like(http_get('/addr'), qr/addr=127.0.0.1/, 'r.remoteAddress'); like(http_get('/uri'), qr/uri=\/uri/, 'r.uri'); like(http_get('/status'), qr/204 No Content/, 'r.status'); like(http_post('/body'), qr/REQ-BODY/, 'request body'); like(http_post('/in_file'), qr/request body is in a file/, 'request body in file'); like(http_post_big('/body'), qr/200.*^(1234567890){1024}$/ms, 'request body big'); like(http_get('/send?foo=12345&n=11&foo-2=bar&ndd=&foo-3=z'), qr/n=foo, v=12 n=foo-2, v=ba n=foo-3, v=z/, 'r.send'); TODO: { local $TODO = 'not yet' unless has_version('0.8.4'); like(http_get('/send_buffer'), qr/send_buffer/, 'r.send accepts buffer'); } like(http_get('/return_method?c=200'), qr/200 OK.*\x0d\x0a?\x0d\x0a?$/s, 'return code'); like(http_get('/return_method?c=200&t=SEE-THIS'), qr/200 OK.*^SEE-THIS$/ms, 'return text'); like(http_get('/return_method?c=301&t=path'), qr/ 301 .*Location: path/s, 'return redirect'); like(http_get('/return_method?c=404'), qr/404 Not.*html/s, 'return error page'); like(http_get('/return_method?c=inv'), qr/ 500 /, 'return invalid'); like(http_get('/type?path=variables.host'), qr/200 OK.*type: string$/s, 'variables type'); like(http_get('/type?path=rawVariables.host'), qr/200 OK.*type: buffer$/s, 'rawVariables type'); like(http_post('/type?path=requestText'), qr/200 OK.*type: string$/s, 'requestText type'); like(http_post('/type?path=requestBuffer'), qr/200 OK.*type: buffer$/s, 'requestBuffer type'); like(http_post('/request_body_cache'), qr/requestText:string requestBuffer:buffer$/s, 'request body cache'); like(http_get('/var'), qr/variable=127.0.0.1/, 'r.variables'); like(http_get('/global'), qr/global=njs/, 'global code'); like(http_get('/log'), qr/200 OK/, 'r.log'); TODO: { local $TODO = 'not yet' unless has_version('0.7.7'); like(http_get('/internal'), qr/parent: false sub: true/, 'r.internal'); } TODO: { local $TODO = 'not yet' unless has_version('0.8.3'); like(http_get('/buffer_variable'), qr/aabbccdd/, 'buffer variable'); } http_get('/except'); http_get('/content_except'); like(http_get('/content_empty'), qr/500 Internal Server Error/, 'empty handler'); $t->stop(); ok(index($t->read_file('error.log'), 'SEE-LOG') > 0, 'log js'); ok(index($t->read_file('error.log'), 'at decodeURI') > 0, 'js_set backtrace'); ok(index($t->read_file('error.log'), 'at content_except') > 0, 'js_content backtrace'); ############################################################################### sub has_version { my $need = shift; http_get('/njs') =~ /^([.0-9]+)$/m; my @v = split(/\./, $1); my ($n, $v); for $n (split(/\./, $need)) { $v = shift @v || 0; return 0 if $n > $v; return 1 if $v > $n; } return 1; } ############################################################################### sub http_get_hdr { my ($url, %extra) = @_; return http(< "JSON::PP not installed") if $@; my $t = Test::Nginx->new()->has(qw/http/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; js_set $test_iter test.iter; server { listen 127.0.0.1:8080; server_name localhost; location /njs { js_content test.njs; } location /iter { return 200 $test_iter; } location /keys { js_content test.keys; } location /object { js_content test.object; } } } EOF $t->write_file('test.js', <try_run('no njs')->plan(15); ############################################################################### sub recode { my $json; eval { $json = JSON::PP::decode_json(shift) }; if ($@) { return ""; } JSON::PP->new()->canonical()->encode($json); } sub get_json { http_get(shift) =~ /\x0d\x0a?\x0d\x0a?(.*)/ms; recode($1); } ############################################################################### local $TODO = 'not yet' unless has_version('0.7.6'); like(http_get('/iter?foo=12345&foo2=bar&nn=22&foo-3=z'), qr/12345barz/, 'r.args iteration'); like(http_get('/iter?foo=123&foo2=&foo3&foo4=456'), qr/123456/, 'r.args iteration 2'); like(http_get('/iter?foo=123&foo2=&foo3'), qr/123/, 'r.args iteration 3'); like(http_get('/iter?foo=123&foo2='), qr/123/, 'r.args iteration 4'); like(http_get('/iter?foo=1&foo=2'), qr/1,2/m, 'r.args iteration 5'); like(http_get('/keys?b=1&c=2&a=5'), qr/a,b,c/m, 'r.args sorted keys'); like(http_get('/keys?b=1&b=2'), qr/b/m, 'r.args duplicate keys'); like(http_get('/keys?b=1&a&c='), qr/a,b,c/m, 'r.args empty value'); is(get_json('/object'), '{}', 'empty object'); is(get_json('/object?a=1&b=2&c=3'), '{"a":"1","b":"2","c":"3"}', 'ordinary object'); is(get_json('/object?a=1&A=2'), '{"A":"2","a":"1"}', 'case sensitive object'); is(get_json('/object?a=1&A=2&a=3'), '{"A":"2","a":["1","3"]}', 'duplicate keys object'); is(get_json('/object?%61=1&a=2'), '{"a":["1","2"]}', 'keys percent-encoded object'); is(get_json('/object?a=%62%63&b=%63%64'), '{"a":"bc","b":"cd"}', 'values percent-encoded object'); is(get_json('/object?a=%6&b=%&c=%%&d=%zz'), '{"a":"%6","b":"%","c":"%%","d":"%zz"}', 'values percent-encoded broken object'); ############################################################################### sub has_version { my $need = shift; http_get('/njs') =~ /^([.0-9]+)$/m; my @v = split(/\./, $1); my ($n, $v); for $n (split(/\./, $need)) { $v = shift @v || 0; return 0 if $n > $v; return 1 if $v > $n; } return 1; } ############################################################################### njs-0.8.9/nginx/t/js_async.t000066400000000000000000000121711474132077100157160ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Async tests for http njs module. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http rewrite/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_set $test_async test.set_timeout; js_set $context_var test.context_var; js_set $test_set_rv_var test.set_rv_var; js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /njs { js_content test.njs; } location /async_var { return 200 $test_async; } location /shared_ctx { add_header H $context_var; js_content test.shared_ctx; } location /set_timeout { js_content test.set_timeout; } location /set_timeout_many { js_content test.set_timeout_many; } location /set_timeout_data { postpone_output 0; js_content test.set_timeout_data; } location /limit_rate { postpone_output 0; sendfile_max_chunk 5; js_content test.limit_rate; } location /async_content { js_content test.async_content; } location /set_rv_var { return 200 $test_set_rv_var; } } } EOF $t->write_file('test.js', < {resolve(x)}).then(v => v).then(v => v); } async function async_content(r) { const a1 = await pr('A'); const a2 = await pr('B'); r.return(200, `retval: \${a1 + a2}`); } async function set_rv_var(r) { const a1 = await pr(10); const a2 = await pr(20); r.setReturnValue(`retval: \${a1 + a2}`); } export default {njs:test_njs, set_timeout, set_timeout_data, set_timeout_many, context_var, shared_ctx, limit_rate, async_content, set_rv_var}; EOF $t->try_run('no njs available')->plan(9); ############################################################################### like(http_get('/set_timeout'), qr/Content-Type: foo/, 'setTimeout'); like(http_get('/set_timeout_many'), qr/Content-Type: reply/, 'setTimeout many'); like(http_get('/set_timeout_data'), qr/123456789/, 'setTimeout data'); like(http_get('/shared_ctx?a=xxx'), qr/H: xxx/, 'shared context'); like(http_get('/limit_rate'), qr/A{50}/, 'limit_rate'); like(http_get('/async_content'), qr/retval: AB/, 'async content'); like(http_get('/set_rv_var'), qr/retval: 30/, 'set return value variable'); http_get('/async_var'); $t->stop(); ok(index($t->read_file('error.log'), 'pending events') > 0, 'pending js events'); ok(index($t->read_file('error.log'), 'async operation inside') > 0, 'async op in var handler'); ############################################################################### njs-0.8.9/nginx/t/js_body_filter.t000066400000000000000000000106651474132077100171110ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for http njs module, body filter. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http proxy/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /njs { js_content test.njs; } location /append { js_body_filter test.append; proxy_pass http://127.0.0.1:8081/source; } location /buffer_type { js_body_filter test.buffer_type buffer_type=buffer; proxy_pass http://127.0.0.1:8081/source; } location /buffer_type_nonutf8 { js_body_filter test.buffer_type buffer_type=buffer; proxy_pass http://127.0.0.1:8081/nonutf8_source; } location /forward { js_body_filter test.forward buffer_type=string; proxy_pass http://127.0.0.1:8081/source; } location /filter { proxy_buffering off; js_body_filter test.filter; proxy_pass http://127.0.0.1:8081/source; } location /prepend { js_body_filter test.prepend; proxy_pass http://127.0.0.1:8081/source; } } server { listen 127.0.0.1:8081; server_name localhost; location /source { postpone_output 1; js_content test.source; } location /nonutf8_source { postpone_output 1; js_content test.nonutf8_source; } } } EOF $t->write_file('test.js', <Buffer.from(v, 'hex')); chunks.delay = 5; chunks.r = r; chunks.chain = chain; r.status = 200; r.sendHeader(); chain(chunks, 0); } function filter(r, data, flags) { if (flags.last || data.length >= Number(r.args.len)) { r.sendBuffer(`\${data}|`, flags); if (r.args.dup && !flags.last) { r.sendBuffer(data, flags); } } } function forward(r, data, flags) { r.sendBuffer(data, flags); } function prepend(r, data, flags) { r.sendBuffer("XXX"); r.sendBuffer(data, flags); r.done(); } export default {njs: test_njs, append, buffer_type, filter, forward, prepend, source, nonutf8_source}; EOF $t->try_run('no njs body filter')->plan(7); ############################################################################### like(http_get('/append'), qr/AAABBCDDDDXXX/, 'append'); like(http_get('/buffer_type'), qr/AAABBCDDDD/, 'buffer type'); like(http_get('/buffer_type_nonutf8'), qr/\xaa\xaa\xbb\xcc\xdd\xdd/, 'buffer type nonutf8'); like(http_get('/forward'), qr/AAABBCDDDD/, 'forward'); like(http_get('/filter?len=3'), qr/AAA|DDDD|/, 'filter 3'); like(http_get('/filter?len=2&dup=1'), qr/AAA|AAABB|BBDDDD|DDDD/, 'filter 2 dup'); like(http_get('/prepend'), qr/XXXAAABBCDDDD/, 'prepend'); ############################################################################### njs-0.8.9/nginx/t/js_body_filter_if.t000066400000000000000000000051051474132077100175600ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for http njs module, body filter, if context. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http proxy rewrite/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /njs { js_content test.njs; } location /filter { if ($arg_name ~ "prepend") { js_body_filter test.prepend; } if ($arg_name ~ "append") { js_body_filter test.append; } js_body_filter test.should_not_be_called; proxy_pass http://127.0.0.1:8081/source; } } server { listen 127.0.0.1:8081; server_name localhost; location /source { postpone_output 1; js_content test.source; } } } EOF $t->write_file('test.js', <try_run('no njs body filter')->plan(2); ############################################################################### like(http_get('/filter?name=append'), qr/AAABBCDDDDXXX/, 'append'); like(http_get('/filter?name=prepend'), qr/XXXAAABBCDDDD/, 'prepend'); ############################################################################### njs-0.8.9/nginx/t/js_buffer.t000066400000000000000000000100701474132077100160460ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for http njs module, buffer properties. ############################################################################### use warnings; use strict; use Test::More; use Socket qw/ CRLF /; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; eval { require JSON::PP; }; plan(skip_all => "JSON::PP not installed") if $@; my $t = Test::Nginx->new()->has(qw/http rewrite proxy/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /njs { js_content test.njs; } location /return { js_content test.return; } location /req_body { js_content test.req_body; } location /res_body { js_content test.res_body; } location /res_text { js_content test.res_text; } location /binary_var { js_content test.binary_var; } location /p/ { proxy_pass http://127.0.0.1:8081/; } } server { listen 127.0.0.1:8081; server_name localhost; location /sub1 { return 200 '{"a": {"b": 1}}'; } } } EOF $t->write_file('test.js', < { var body = reply.responseBuffer; var view = new DataView(body.buffer); view.setInt8(2, 'c'.charCodeAt(0)); body = JSON.parse(body); body.type = type(reply.responseBuffer); r.return(200, JSON.stringify(body)); }) } function res_text(r) { r.subrequest('/p/sub1') .then(reply => { var body = JSON.parse(reply.responseText); body.type = type(reply.responseText); r.return(200, JSON.stringify(body)); }) } function binary_var(r) { var test = r.rawVariables.binary_remote_addr .equals(Buffer.from([127,0,0,1])); r.return(200, test); } export default {njs: test_njs, return: test_return, req_body, res_body, res_text, binary_var}; EOF $t->try_run('no njs buffer')->plan(5); ############################################################################### like(http_get('/return?text=FOO'), qr/200 OK.*body: FOO$/s, 'return buffer'); like(http_post('/req_body'), qr/200 OK.*BAR$/s, 'request buffer'); is(get_json('/res_body'), '{"c":{"b":1},"type":"buffer"}', 'response buffer'); is(get_json('/res_text'), '{"a":{"b":1},"type":"string"}', 'response text'); like(http_get('/binary_var'), qr/200 OK.*true$/s, 'binary var'); ############################################################################### sub recode { my $json; eval { $json = JSON::PP::decode_json(shift) }; if ($@) { return ""; } JSON::PP->new()->canonical()->encode($json); } sub get_json { http_get(shift) =~ /\x0d\x0a?\x0d\x0a?(.*)/ms; recode($1); } sub http_post { my ($url, %extra) = @_; my $p = "POST $url HTTP/1.0" . CRLF . "Host: localhost" . CRLF . "Content-Length: 17" . CRLF . CRLF . "{\"a\":{\"b\":\"BAR\"}}"; return http($p, %extra); } ############################################################################### njs-0.8.9/nginx/t/js_capture_variables.t000066400000000000000000000037341474132077100203010ustar00rootroot00000000000000#!/usr/bin/perl # (C) Thomas P. # Tests for http njs module, reading location capture variables. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /njs { js_content test.njs; } location ~ /(.+)/(.+) { js_content test.variables; } } } EOF $t->write_file('test.js', <try_run('no njs capture variables')->plan(4); ############################################################################### TODO: { local $TODO = 'not yet' unless has_version('0.8.6'); like(http_get('/test/hello?index=0'), qr/"\/test\/hello"/, 'global capture'); like(http_get('/test/hello?index=1'), qr/"test"/, 'local capture 1'); like(http_get('/test/hello?index=2'), qr/"hello"/, 'local capture 2'); like(http_get('/test/hello?index=3'), qr/"undefined"/, 'undefined capture'); } ############################################################################### sub has_version { my $need = shift; http_get('/njs') =~ /^([.0-9]+)$/m; my @v = split(/\./, $1); my ($n, $v); for $n (split(/\./, $need)) { $v = shift @v || 0; return 0 if $n > $v; return 1 if $v > $n; } return 1; } ############################################################################### njs-0.8.9/nginx/t/js_console.t000066400000000000000000000066531474132077100162530ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for http njs module, console object. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /engine { js_content test.engine; } location /dump { js_content test.dump; } location /error { js_content test.error; } location /info { js_content test.info; } location /log { js_content test.log; } location /time { js_content test.time; } location /time_test { js_content test.time_test; } location /warn { js_content test.warn; } } } EOF $t->write_file('test.js', <try_run('no njs console')->plan(7); ############################################################################### my $engine = http_get('/engine'); http_get('/dump?data=eyJhIjpbMiwzXX0'); http_get('/error?data=IldBS0Ei'); http_get('/info?data=IkJBUiI'); http_get('/log?data=eyJhIjpbIkIiLCJDIl19'); http_get('/time?delay=7&timer=foo'); http_get('/time_test'); http_get('/warn?data=IkZPTyI'); $t->stop(); like($t->read_file('error.log'), qr/\[error\].*js: WAKA/, 'console.error'); like($t->read_file('error.log'), qr/\[info\].*js: BAR/, 'console.info'); SKIP: { skip "QuickJS has no console.dump() method.", 1 if $engine =~ /QuickJS$/m; like($t->read_file('error.log'), qr/\[info\].*js: \{a:\['B','C'\]\}/, 'console.log with object'); } like($t->read_file('error.log'), qr/\[warn\].*js: FOO/, 'console.warn'); like($t->read_file('error.log'), qr/\[info\].*js: foo: \d+\.\d\d\d\d\d\dms/, 'console.time foo'); like($t->read_file('error.log'), qr/\[info\].*js: Timer \"default\" already/, 'console.time already started'); like($t->read_file('error.log'), qr/\[info\].*js: Timer \"test\" doesn't/, 'console.time not started'); ############################################################################### njs-0.8.9/nginx/t/js_dump.t000066400000000000000000000051611474132077100155470ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for http njs module, request object dump. ############################################################################### use warnings; use strict; use Test::More; use Socket qw/ CRLF /; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http rewrite/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /engine { js_content test.engine; } location /dump { js_content test.dump; } location /stringify { js_content test.stringify; } location /stringify_subrequest { js_content test.stringify_subrequest; } location /js_sub { return 201 '{$request_method}'; } } } EOF $t->write_file('test.js', < { r.return(200, JSON.stringify(reply)) }); } export default {engine, dump, stringify, stringify_subrequest}; EOF $t->try_run('no njs dump')->plan(3); ############################################################################### SKIP: { skip "QuickJS has no njs.dump() method.", 1 if http_get('/engine') =~ /QuickJS$/m; like(http( 'GET /dump?v=1&t=x HTTP/1.0' . CRLF . 'Foo: bar' . CRLF . 'Foo2: bar2' . CRLF . 'Host: localhost' . CRLF . CRLF ), qr/method:'GET'/, 'njs.dump(r)'); } TODO: { local $TODO = 'in QuickJS these are non-enumerable getter/setter props' if http_get('/engine') =~ /^(QuickJS)$/m; like(http( 'GET /stringify?v=1&t=x HTTP/1.0' . CRLF . 'Foo: bar' . CRLF . 'Foo2: bar2' . CRLF . 'Host: localhost' . CRLF . CRLF ), qr/headersOut":\{"baz":"bar"}/, 'JSON.stringify(r)'); like(http( 'GET /stringify_subrequest HTTP/1.0' . CRLF . 'Host: localhost' . CRLF . CRLF ), qr/"status":201/, 'JSON.stringify(reply)'); } ############################################################################### njs-0.8.9/nginx/t/js_dup_set.t000066400000000000000000000025421474132077100162450ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for http njs module, duplicate identical js_set directives. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /set1 { js_set $test test.foo; return 200 set1:$test; } location /set2 { js_set $test test.foo; return 200 set2:$test; } } } EOF $t->write_file('test.js', <try_run('no njs')->plan(2); ############################################################################### like(http_get('/set1'), qr/set1:42/, '/set1 location'); like(http_get('/set2'), qr/set2:42/, '/set2 location'); ############################################################################### njs-0.8.9/nginx/t/js_engine.t000066400000000000000000000050621474132077100160470ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for http njs module, js_engine directive. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http proxy/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /njs { js_content test.njs; } location /njs/ { proxy_pass http://127.0.0.1:8081/; } location /qjs/ { proxy_pass http://127.0.0.1:8082/; } } server { listen 127.0.0.1:8081; server_name localhost; js_engine njs; location /test { js_content test.test; } location /override { js_engine qjs; js_content test.test; } } server { listen 127.0.0.1:8082; server_name localhost; js_engine qjs; location /test { js_content test.test; } location /override { js_engine njs; js_content test.test; } } } EOF $t->write_file('test.js', <try_run('no njs js_engine')->plan(4); ############################################################################### TODO: { local $TODO = 'not yet' unless has_version('0.8.6'); like(http_get('/njs/test'), qr/njs/, 'js_engine njs server'); like(http_get('/njs/override'), qr/QuickJS/, 'js_engine override'); like(http_get('/qjs/test'), qr/QuickJS/, 'js_engine qjs server'); like(http_get('/qjs/override'), qr/njs/, 'js_engine override'); } $t->stop(); ############################################################################### sub has_version { my $need = shift; http_get('/njs') =~ /^([.0-9]+)$/m; my @v = split(/\./, $1); my ($n, $v); for $n (split(/\./, $need)) { $v = shift @v || 0; return 0 if $n > $v; return 1 if $v > $n; } return 1; } ############################################################################### njs-0.8.9/nginx/t/js_fetch.t000066400000000000000000000445361474132077100157040ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for http njs module, fetch method. ############################################################################### use warnings; use strict; use Test::More; use Socket qw/ CRLF /; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; eval { require JSON::PP; }; plan(skip_all => "JSON::PP not installed") if $@; my $t = Test::Nginx->new()->has(qw/http/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; js_fetch_max_response_buffer_size 128k; location /njs { js_content test.njs; } location /engine { js_content test.engine; } location /broken { js_content test.broken; } location /broken_response { js_content test.broken_response; } location /body { js_content test.body; } location /body_special { js_content test.body_special; } location /chain { js_content test.chain; } location /chunked_ok { js_content test.chunked_ok; } location /chunked_fail { js_content test.chunked_fail; } location /header { js_content test.header; } location /host_header { js_content test.host_header; } location /header_iter { js_content test.header_iter; } location /multi { js_content test.multi; } location /property { js_content test.property; } location /loc { js_content test.loc; } location /json { } } server { listen 127.0.0.1:8081; server_name localhost; location /loc { js_content test.loc; } location /host { return 200 $http_host; } } } EOF my $p0 = port(8080); my $p1 = port(8081); my $p2 = port(8082); $t->write_file('json', '{"a":[1,2], "b":{"c":"FIELD"}}'); $t->write_file('test.js', < a[v], obj); } return JSON.stringify(retval); } ngx.fetch(`http://127.0.0.1:$p0/\${loc}`) .then(reply => reply[getter]()) .then(data => r.return(200, query(data))) .catch(e => r.return(501, e.message)) } function property(r) { var opts = {headers:{}}; if (r.args.code) { opts.headers.code = r.args.code; } var p = ngx.fetch('http://127.0.0.1:$p0/loc', opts) if (r.args.readBody) { p = p.then(rep => rep.text().then(body => {rep.text = body; return rep;})) } p.then(reply => r.return(200, reply[r.args.pr])) .catch(e => r.return(501, e.message)) } function process_errors(r, tests) { var results = []; tests.forEach(args => { ngx.fetch.apply(r, args) .then(reply => { r.return(400, '["unexpected then"]'); }) .catch(e => { results.push(e.message); if (results.length == tests.length) { results.sort(); r.return(200, JSON.stringify(results)); } }) }) } function broken(r) { var tests = [ ['http://127.0.0.1:1/loc'], ['http://127.0.0.1:80800/loc'], [Symbol.toStringTag], ]; return process_errors(r, tests); } function broken_response(r) { var tests = [ ['http://127.0.0.1:$p2/status_line'], ['http://127.0.0.1:$p2/length'], ['http://127.0.0.1:$p2/header'], ['http://127.0.0.1:$p2/headers'], ['http://127.0.0.1:$p2/content_length'], ]; return process_errors(r, tests); } function chain(r) { var results = []; var reqs = [ ['http://127.0.0.1:$p0/loc'], ['http://127.0.0.1:$p1/loc'], ]; function next(reply) { if (reqs.length == 0) { r.return(200, "SUCCESS"); return; } ngx.fetch.apply(r, reqs.pop()) .then(next) .catch(e => r.return(400, e.message)) } next(); } function chunked_ok(r) { var results = []; var tests = [ ['http://127.0.0.1:$p2/big/ok', {max_response_body_size:128000}], ['http://127.0.0.1:$p2/chunked/ok'], ['http://127.0.0.1:$p2/chunked/big'], ]; function collect(v) { results.push(v); if (results.length == tests.length) { r.return(200); } } tests.forEach(args => { ngx.fetch.apply(r, args) .then(reply => reply.text()) .then(body => collect(body.length)) }) } function chunked_fail(r) { var results = []; var tests = [ ['http://127.0.0.1:$p2/big', {max_response_body_size:128000}], ['http://127.0.0.1:$p2/chunked'], ['http://127.0.0.1:$p2/chunked/big', {max_response_body_size:128}], ]; function collect(v) { results.push(v); if (results.length == tests.length) { r.return(200); } } tests.forEach(args => { ngx.fetch.apply(r, args) .then(reply => reply.text()) .catch(e => collect(e.message)) }) } function header(r) { var url = `http://127.0.0.1:$p2/\${r.args.loc}`; var method = r.args.method ? r.args.method : 'get'; var p = ngx.fetch(url) if (r.args.readBody) { p = p.then(rep => rep.text().then(body => {rep.text = body; return rep;})) } p.then(reply => { var h = reply.headers[method](r.args.h); r.return(200, njs.dump(h)); }) .catch(e => r.return(501, e.message)) } async function host_header(r) { const reply = await ngx.fetch(`http://127.0.0.1:$p1/host`, {headers: {Host: r.args.host}}); const body = await reply.text(); r.return(200, body); } async function body_special(r) { let opts = {}; if (r.args.method) { opts.method = r.args.method; } let reply = await ngx.fetch(`http://127.0.0.1:$p2/\${r.args.loc}`, opts); let body = await reply.text(); r.return(200, body != '' ? body : ''); } async function header_iter(r) { let url = `http://127.0.0.1:$p2/\${r.args.loc}`; let response = await ngx.fetch(url); let headers = response.headers; let out = []; for (let key in response.headers) { if (key != 'Connection') { out.push(`\${key}:\${headers.get(key)}`); } } r.return(200, njs.dump(out)); } function multi(r) { var results = []; var tests = [ [ 'http://127.0.0.1:$p0/loc', { headers: {Code: 201}}, ], [ 'http://127.0.0.1:$p0/loc', { method:'POST', headers: {Code: 401}, body: 'OK'}, ], [ 'http://127.0.0.1:$p1/loc', { method:'PATCH', headers: {bar:'xxx'}}, ], ]; function cmp(a,b) { if (a.b > b.b) {return 1;} if (a.b < b.b) {return -1;} return 0 } tests.forEach(args => { ngx.fetch.apply(r, args) .then(rep => rep.text().then(body => {rep.text = body; return rep;})) .then(rep => { results.push({b:rep.text, c:rep.status, u:rep.url}); if (results.length == tests.length) { results.sort(cmp); r.return(200, JSON.stringify(results)); } }) .catch(e => { r.return(400, `["\${e.message}"]`); throw e; }) }) if (r.args.throw) { throw 'Oops'; } } function str(v) { return v ? v : ''}; function loc(r) { var v = r.variables; var body = str(r.requestText); var bar = str(r.headersIn.bar); var c = r.headersIn.code ? Number(r.headersIn.code) : 200; r.return(c, `\${v.request_method}:\${bar}:\${body}`); } export default {njs: test_njs, body, broken, broken_response, body_special, chain, chunked_ok, chunked_fail, header, header_iter, host_header, multi, loc, property, engine}; EOF $t->try_run('no njs.fetch'); plan(skip_all => 'not yet') if http_get('/engine') =~ /QuickJS$/m; $t->plan(36); $t->run_daemon(\&http_daemon, port(8082)); $t->waitforsocket('127.0.0.1:' . port(8082)); ############################################################################### like(http_get('/body?getter=arrayBuffer&loc=loc'), qr/200 OK.*"GET::"$/s, 'fetch body arrayBuffer'); like(http_get('/body?getter=text&loc=loc'), qr/200 OK.*"GET::"$/s, 'fetch body text'); like(http_get('/body?getter=json&loc=json&path=b.c'), qr/200 OK.*"FIELD"$/s, 'fetch body json'); like(http_get('/body?getter=json&loc=loc'), qr/501/s, 'fetch body json invalid'); like(http_get('/body_special?loc=parted'), qr/200 OK.*X{32000}$/s, 'fetch body parted'); like(http_get('/property?pr=bodyUsed'), qr/false$/s, 'fetch bodyUsed false'); like(http_get('/property?pr=bodyUsed&readBody=1'), qr/true$/s, 'fetch bodyUsed true'); like(http_get('/property?pr=ok'), qr/200 OK.*true$/s, 'fetch ok true'); like(http_get('/property?pr=ok&code=401'), qr/200 OK.*false$/s, 'fetch ok false'); like(http_get('/property?pr=redirected'), qr/200 OK.*false$/s, 'fetch redirected false'); like(http_get('/property?pr=statusText'), qr/200 OK.*OK$/s, 'fetch statusText OK'); like(http_get('/property?pr=statusText&code=403'), qr/200 OK.*Forbidden$/s, 'fetch statusText Forbidden'); like(http_get('/property?pr=type'), qr/200 OK.*basic$/s, 'fetch type'); like(http_get('/header?loc=duplicate_header&h=BAR'), qr/200 OK.*c$/s, 'fetch header'); like(http_get('/header?loc=duplicate_header&h=BARR'), qr/200 OK.*null$/s, 'fetch no header'); like(http_get('/header?loc=duplicate_header&h=foo'), qr/200 OK.*a, ?b$/s, 'fetch header duplicate'); like(http_get('/header?loc=duplicate_header&h=BAR&method=getAll'), qr/200 OK.*\['c']$/s, 'fetch getAll header'); like(http_get('/header?loc=duplicate_header&h=BARR&method=getAll'), qr/200 OK.*\[]$/s, 'fetch getAll no header'); like(http_get('/header?loc=duplicate_header&h=FOO&method=getAll'), qr/200 OK.*\['a','b']$/s, 'fetch getAll duplicate'); like(http_get('/header?loc=duplicate_header&h=bar&method=has'), qr/200 OK.*true$/s, 'fetch header has'); like(http_get('/header?loc=duplicate_header&h=buz&method=has'), qr/200 OK.*false$/s, 'fetch header does not have'); like(http_get('/header?loc=chunked/big&h=BAR&readBody=1'), qr/200 OK.*xxx$/s, 'fetch chunked header'); is(get_json('/multi'), '[{"b":"GET::","c":201,"u":"http://127.0.0.1:'.$p0.'/loc"},' . '{"b":"PATCH:xxx:","c":200,"u":"http://127.0.0.1:'.$p1.'/loc"},' . '{"b":"POST::OK","c":401,"u":"http://127.0.0.1:'.$p0.'/loc"}]', 'fetch multi'); like(http_get('/multi?throw=1'), qr/500/s, 'fetch destructor'); like(http_get('/broken'), qr/200/s, 'fetch broken'); like(http_get('/broken_response'), qr/200/s, 'fetch broken response'); like(http_get('/chunked_ok'), qr/200/s, 'fetch chunked ok'); like(http_get('/chunked_fail'), qr/200/s, 'fetch chunked fail'); like(http_get('/chain'), qr/200 OK.*SUCCESS$/s, 'fetch chain'); like(http_get('/header_iter?loc=duplicate_header_large'), qr/\['A:a','B:a','C:a','D:a','E:a','F:a','G:a','H:a','Moo:a, ?b']$/s, 'fetch header duplicate large'); TODO: { local $TODO = 'not yet' unless has_version('0.7.7'); like(http_get('/body_special?loc=no_content_length'), qr/200 OK.*CONTENT-BODY$/s, 'fetch body without content-length'); like(http_get('/body_special?loc=no_content_length/parted'), qr/200 OK.*X{32000}$/s, 'fetch body without content-length parted'); } TODO: { local $TODO = 'not yet' unless has_version('0.7.8'); like(http_get('/body_special?loc=head&method=HEAD'), qr/200 OK.*$/s, 'fetch head method'); like(http_get('/body_special?loc=length&method=head'), qr/200 OK.*$/s, 'fetch head method lower case'); } TODO: { local $TODO = 'not yet' unless has_version('0.8.0'); like(http_get('/host_header?host=FOOBAR'), qr/200 OK.*FOOBAR$/s, 'fetch host header'); } TODO: { local $TODO = 'not yet' unless has_version('0.8.2'); like(http_get('/body_special?loc=head/large&method=HEAD'), qr/200 OK.*$/s, 'fetch head method large content-length'); } ############################################################################### sub has_version { my $need = shift; http_get('/njs') =~ /^([.0-9]+)$/m; my @v = split(/\./, $1); my ($n, $v); for $n (split(/\./, $need)) { $v = shift @v || 0; return 0 if $n > $v; return 1 if $v > $n; } return 1; } ############################################################################### sub recode { my $json; eval { $json = JSON::PP::decode_json(shift) }; if ($@) { return ""; } JSON::PP->new()->canonical()->encode($json); } sub get_json { http_get(shift) =~ /\x0d\x0a?\x0d\x0a?(.*)/ms; recode($1); } ############################################################################### sub http_daemon { my $port = shift; my $server = IO::Socket::INET->new( Proto => 'tcp', LocalAddr => '127.0.0.1:' . $port, Listen => 5, Reuse => 1 ) or die "Can't create listening socket: $!\n"; local $SIG{PIPE} = 'IGNORE'; while (my $client = $server->accept()) { $client->autoflush(1); my $headers = ''; my $uri = ''; while (<$client>) { $headers .= $_; last if (/^\x0d?\x0a?$/); } $uri = $1 if $headers =~ /^\S+\s+([^ ]+)\s+HTTP/i; if ($uri eq '/status_line') { print $client "HTTP/1.1 2A"; } elsif ($uri eq '/content_length') { print $client "HTTP/1.1 200 OK" . CRLF . "Content-Length: " . CRLF . "Connection: close" . CRLF . CRLF; } elsif ($uri eq '/header') { print $client "HTTP/1.1 200 OK" . CRLF . "@#" . CRLF . "Connection: close" . CRLF . CRLF; } elsif ($uri eq '/duplicate_header') { print $client "HTTP/1.1 200 OK" . CRLF . "Foo: a" . CRLF . "bar: c" . CRLF . "Foo: b" . CRLF . "Connection: close" . CRLF . CRLF; } elsif ($uri eq '/duplicate_header_large') { print $client "HTTP/1.1 200 OK" . CRLF . "A: a" . CRLF . "B: a" . CRLF . "C: a" . CRLF . "D: a" . CRLF . "E: a" . CRLF . "F: a" . CRLF . "G: a" . CRLF . "H: a" . CRLF . "Moo: a" . CRLF . "Moo: b" . CRLF . "Connection: close" . CRLF . CRLF; } elsif ($uri eq '/headers') { print $client "HTTP/1.1 200 OK" . CRLF . "Connection: close" . CRLF; } elsif ($uri eq '/length') { print $client "HTTP/1.1 200 OK" . CRLF . "Content-Length: 100" . CRLF . "Connection: close" . CRLF . CRLF . "unfinished" . CRLF; } elsif ($uri eq '/head') { print $client "HTTP/1.1 200 OK" . CRLF . "Content-Length: 100" . CRLF . "Connection: close" . CRLF . CRLF; } elsif ($uri eq '/head/large') { print $client "HTTP/1.1 200 OK" . CRLF . "Content-Length: 1000000" . CRLF . "Connection: close" . CRLF . CRLF; } elsif ($uri eq '/parted') { print $client "HTTP/1.1 200 OK" . CRLF . "Content-Length: 32000" . CRLF . "Connection: close" . CRLF . CRLF; for (1 .. 4) { select undef, undef, undef, 0.01; print $client "X" x 8000; } } elsif ($uri eq '/no_content_length') { print $client "HTTP/1.1 200 OK" . CRLF . "Connection: close" . CRLF . CRLF . "CONTENT-BODY"; } elsif ($uri eq '/no_content_length/parted') { print $client "HTTP/1.1 200 OK" . CRLF . "Connection: close" . CRLF . CRLF; for (1 .. 4) { select undef, undef, undef, 0.01; print $client "X" x 8000; } } elsif ($uri eq '/big') { print $client "HTTP/1.1 200 OK" . CRLF . "Content-Length: 100100" . CRLF . "Connection: close" . CRLF . CRLF; for (1 .. 1000) { print $client ("X" x 98) . CRLF; } print $client "unfinished" . CRLF; } elsif ($uri eq '/big/ok') { print $client "HTTP/1.1 200 OK" . CRLF . "Content-Length: 100010" . CRLF . "Connection: close" . CRLF . CRLF; for (1 .. 1000) { print $client ("X" x 98) . CRLF; } print $client "finished" . CRLF; } elsif ($uri eq '/chunked') { print $client "HTTP/1.1 200 OK" . CRLF . "Transfer-Encoding: chunked" . CRLF . "Connection: close" . CRLF . CRLF . "ff" . CRLF . "unfinished" . CRLF; } elsif ($uri eq '/chunked/ok') { print $client "HTTP/1.1 200 OK" . CRLF . "Transfer-Encoding: chunked" . CRLF . "Connection: close" . CRLF . CRLF . "a" . CRLF . "finished" . CRLF . CRLF . "0" . CRLF . CRLF; } elsif ($uri eq '/chunked/big') { print $client "HTTP/1.1 200 OK" . CRLF . "Transfer-Encoding: chunked" . CRLF . "Bar: xxx" . CRLF . "Connection: close" . CRLF . CRLF; for (1 .. 100) { print $client "ff" . CRLF . ("X" x 255) . CRLF; } print $client "0" . CRLF . CRLF; } } } ############################################################################### njs-0.8.9/nginx/t/js_fetch_https.t000066400000000000000000000161131474132077100171140ustar00rootroot00000000000000#!/usr/bin/perl # (C) Antoine Bonavita # (C) Nginx, Inc. # Tests for http njs module, fetch method, https support. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http http_ssl rewrite/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; resolver 127.0.0.1:%%PORT_8981_UDP%%; resolver_timeout 1s; location /njs { js_content test.njs; } location /engine { js_content test.engine; } location /https { js_content test.https; } location /https.myca { js_content test.https; js_fetch_ciphers HIGH:!aNull:!MD5; js_fetch_protocols TLSv1.1 TLSv1.2; js_fetch_trusted_certificate myca.crt; } location /https.myca.short { js_content test.https; js_fetch_verify_depth 0; js_fetch_trusted_certificate myca.crt; } } server { listen 127.0.0.1:8081 ssl default; server_name default.example.com; ssl_certificate default.example.com.chained.crt; ssl_certificate_key default.example.com.key; location /loc { return 200 "You are at default.example.com."; } } server { listen 127.0.0.1:8081 ssl; server_name 1.example.com; ssl_certificate 1.example.com.chained.crt; ssl_certificate_key 1.example.com.key; location /loc { return 200 "You are at 1.example.com."; } } } EOF my $p1 = port(8081); $t->write_file('test.js', < reply.text()) .then(body => r.return(200, body)) .catch(e => r.return(501, e.message)) } export default {njs: test_njs, https, engine}; EOF my $d = $t->testdir(); $t->write_file('openssl.conf', <write_file('myca.conf', <>$d/openssl.out 2>&1") == 0 or die "Can't create self-signed certificate for CA: $!\n"; foreach my $name ('intermediate', 'default.example.com', '1.example.com') { system("openssl req -new " . "-config $d/openssl.conf -subj /CN=$name/ " . "-out $d/$name.csr -keyout $d/$name.key " . ">>$d/openssl.out 2>&1") == 0 or die "Can't create certificate signing req for $name: $!\n"; } $t->write_file('certserial', '1000'); $t->write_file('certindex', ''); system("openssl ca -batch -config $d/myca.conf " . "-keyfile $d/myca.key -cert $d/myca.crt " . "-subj /CN=intermediate/ -in $d/intermediate.csr " . "-out $d/intermediate.crt " . ">>$d/openssl.out 2>&1") == 0 or die "Can't sign certificate for intermediate: $!\n"; foreach my $name ('default.example.com', '1.example.com') { system("openssl ca -batch -config $d/myca.conf " . "-keyfile $d/intermediate.key -cert $d/intermediate.crt " . "-subj /CN=$name/ -in $d/$name.csr -out $d/$name.crt " . ">>$d/openssl.out 2>&1") == 0 or die "Can't sign certificate for $name $!\n"; $t->write_file("$name.chained.crt", $t->read_file("$name.crt") . $t->read_file('intermediate.crt')); } $t->try_run('no njs.fetch'); plan(skip_all => 'not yet') if http_get('/engine') =~ /QuickJS$/m; $t->plan(7); $t->run_daemon(\&dns_daemon, port(8981), $t); $t->waitforfile($t->testdir . '/' . port(8981)); ############################################################################### like(http_get('/https?domain=default.example.com&verify=false'), qr/You are at default.example.com.$/s, 'fetch https'); like(http_get('/https?domain=127.0.0.1&verify=false'), qr/You are at default.example.com.$/s, 'fetch https by IP'); like(http_get('/https?domain=1.example.com&verify=false'), qr/You are at 1.example.com.$/s, 'fetch tls extension'); like(http_get('/https.myca?domain=default.example.com'), qr/You are at default.example.com.$/s, 'fetch https trusted certificate'); like(http_get('/https.myca?domain=localhost'), qr/connect failed/s, 'fetch https wrong CN certificate'); like(http_get('/https?domain=default.example.com'), qr/connect failed/s, 'fetch https non trusted CA'); like(http_get('/https.myca.short?domain=default.example.com'), qr/connect failed/s, 'fetch https CA too far'); ############################################################################### sub reply_handler { my ($recv_data, $port, %extra) = @_; my (@name, @rdata); use constant NOERROR => 0; use constant A => 1; use constant IN => 1; # default values my ($hdr, $rcode, $ttl) = (0x8180, NOERROR, 3600); # decode name my ($len, $offset) = (undef, 12); while (1) { $len = unpack("\@$offset C", $recv_data); last if $len == 0; $offset++; push @name, unpack("\@$offset A$len", $recv_data); $offset += $len; } $offset -= 1; my ($id, $type, $class) = unpack("n x$offset n2", $recv_data); my $name = join('.', @name); if ($type == A) { push @rdata, rd_addr($ttl, '127.0.0.1'); } $len = @name; pack("n6 (C/a*)$len x n2", $id, $hdr | $rcode, 1, scalar @rdata, 0, 0, @name, $type, $class) . join('', @rdata); } sub rd_addr { my ($ttl, $addr) = @_; my $code = 'split(/\./, $addr)'; return pack 'n3N', 0xc00c, A, IN, $ttl if $addr eq ''; pack 'n3N nC4', 0xc00c, A, IN, $ttl, eval "scalar $code", eval($code); } sub dns_daemon { my ($port, $t) = @_; my ($data, $recv_data); my $socket = IO::Socket::INET->new( LocalAddr => '127.0.0.1', LocalPort => $port, Proto => 'udp', ) or die "Can't create listening socket: $!\n"; local $SIG{PIPE} = 'IGNORE'; # signal we are ready open my $fh, '>', $t->testdir() . '/' . $port; close $fh; while (1) { $socket->recv($recv_data, 65536); $data = reply_handler($recv_data, $port); $socket->send($data); } } ############################################################################### njs-0.8.9/nginx/t/js_fetch_objects.t000066400000000000000000000431141474132077100174040ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for http njs module, fetch objects. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http rewrite/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /njs { js_content test.njs; } location /engine { js_content test.engine; } location /headers { js_content test.headers; } location /request { js_content test.request; } location /response { js_content test.response; } location /fetch { js_content test.fetch; } location /fetch_multi_header { js_content test.fetch_multi_header; } location /method { return 200 $request_method; } location /header { return 200 $http_a; } location /body { js_content test.body; } } } EOF my $p0 = port(8080); $t->write_file('test.js', < { var h = new Headers(); return h.get('a'); }, null], ['normal', () => { var h = new Headers({a: 'X', b: 'Z'}); return `\${h.get('a')} \${h.get('B')}`; }, 'X Z'], ['trim value', () => { var h = new Headers({a: ' X '}); return h.get('a'); }, 'X'], ['invalid header name', () => { const valid = "!#\$\%&'*+-.^_`|~0123456789"; for (var i = 0; i < 128; i++) { var c = String.fromCodePoint(i); if (valid.indexOf(c) != -1 || /[a-zA-Z]+/.test(c)) { continue; } try { new Headers([[c, 'a']]); throw new Error( `header with "\${c}" (\${i}) should throw`); } catch (e) { if (e.message != 'invalid header name') { throw e; } } } return 'OK'; }, 'OK'], ['invalid header value', () => { var h = new Headers({A: 'aa\x00a'}); }, 'invalid header value'], ['combine', () => { var h = new Headers({a: 'X', A: 'Z'}); return h.get('a'); }, 'X, Z'], ['combine2', () => { var h = new Headers([['A', 'x'], ['a', 'z']]); return h.get('a'); }, 'x, z'], ['combine3', () => { var h = new Headers(); h.append('a', 'A'); h.append('a', 'B'); h.append('a', 'C'); h.append('a', 'D'); h.append('a', 'E'); h.append('a', 'F'); return h.get('a'); }, 'A, B, C, D, E, F'], ['getAll', () => { var h = new Headers({a: 'X', A: 'Z'}); return njs.dump(h.getAll('a')); }, "['X','Z']"], ['inherit', () => { var h = new Headers({a: 'X', b: 'Y'}); var h2 = new Headers(h); h2.append('c', 'Z'); return h2.has('a') && h2.has('B') && h2.has('c'); }, true], ['delete', () => { var h = new Headers({a: 'X', b: 'Z'}); h.delete('b'); return h.get('a') && !h.get('b'); }, true], ['forEach', () => { var r = []; var h = new Headers({a: '0', b: '1', c: '2'}); h.delete('b'); h.append('z', '3'); h.append('a', '4'); h.append('q', '5'); h.forEach((v, k) => { r.push(`\${v}:\${k}`)}) return r.join('|'); }, 'a:0, 4|c:2|q:5|z:3'], ['set', () => { var h = new Headers([['A', 'x'], ['a', 'y'], ['a', 'z']]); h.set('a', '#'); return h.get('a'); }, '#'], ['set on empty', () => { var h = new Headers([]); h.set('x-test', '1234'); return h.get('x-test'); }, '1234'], ]; run(r, tests); } async function request(r) { const tests = [ ['empty', () => { try { new Request(); throw new Error(`Request() should throw`); } catch (e) { if (e.message != '1st argument is required') { throw e; } } return 'OK'; }, 'OK'], ['normal', () => { var r = new Request("http://nginx.org", {headers: {a: 'X', b: 'Y'}}); return `\${r.url}: \${r.method} \${r.headers.a}`; }, 'http://nginx.org: GET X'], ['url trim', () => { var r = new Request("\\x00\\x01\\x02\\x03\\x05\\x06\\x07\\x08" + "\\x09\\x0a\\x0b\\x0c\\x0d\\x0e\\x0f" + "\\x10\\x11\\x12\\x13\\x14\\x15\\x16" + "\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d" + "\\x1e\\x1f\\x20http://nginx.org\\x00" + "\\x01\\x02\\x03\\x05\\x06\\x07\\x08" + "\\x09\\x0a\\x0b\\x0c\\x0d\\x0e\\x0f" + "\\x10\\x11\\x12\\x13\\x14\\x15\\x16" + "\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d" + "\\x1e\\x1f\\x20"); return r.url; }, 'http://nginx.org'], ['read only', () => { var r = new Request("http://nginx.org"); const props = ['bodyUsed', 'cache', 'credentials', 'headers', 'method', 'mode', 'url']; try { props.forEach(prop => { r[prop] = 1; throw new Error( `setting read-only \${prop} should throw`); }) } catch (e) { if (!e.message.startsWith('Cannot assign to read-only p')) { throw e; } } return 'OK'; }, 'OK'], ['cache', () => { const props = ['default', 'no-cache', 'no-store', 'reload', 'force-cache', 'only-if-cached', '#']; try { props.forEach(cv => { var r = new Request("http://nginx.org", {cache: cv}); if (r.cache != cv) { throw new Error(`r.cache != \${cv}`); } }) } catch (e) { if (!e.message.startsWith('unknown cache type: #')) { throw e; } } return 'OK'; }, 'OK'], ['credentials', () => { const props = ['omit', 'include', 'same-origin', '#']; try { props.forEach(cr => { var r = new Request("http://nginx.org", {credentials: cr}); if (r.credentials != cr) { throw new Error(`r.credentials != \${cr}`); } }) } catch (e) { if (!e.message.startsWith('unknown credentials type: #')) { throw e; } } return 'OK'; }, 'OK'], ['method', () => { const methods = ['get', 'hEad', 'Post', 'OPTIONS', 'PUT', 'DELETE', 'CONNECT']; try { methods.forEach(m => { var r = new Request("http://nginx.org", {method: m}); if (r.method != m.toUpperCase()) { throw new Error(`r.method != \${m}`); } }) } catch (e) { if (!e.message.startsWith('forbidden method: CONNECT')) { throw e; } } return 'OK'; }, 'OK'], ['mode', () => { const props = ['same-origin', 'cors', 'no-cors', 'navigate', 'websocket', '#']; try { props.forEach(m => { var r = new Request("http://nginx.org", {mode: m}); if (r.mode != m) { throw new Error(`r.mode != \${m}`); } }) } catch (e) { if (!e.message.startsWith('unknown mode type: #')) { throw e; } } return 'OK'; }, 'OK'], ['inherit', () => { var r = new Request("http://nginx.org", {headers: {a: 'X', b: 'Y'}}); var r2 = new Request(r); r2.headers.append('a', 'Z') return `\${r2.url}: \${r2.headers.get('a')}`; }, 'http://nginx.org: X, Z'], ['inherit2', () => { var r = new Request("http://nginx.org", {headers: {a: 'X', b: 'Y'}}); var r2 = new Request(r); r2.headers.append('a', 'Z') return `\${r.url}: \${r.headers.get('a')}`; }, 'http://nginx.org: X'], ['inherit3', () => { var h = new Headers(); h.append('a', 'X'); h.append('a', 'Z'); var r = new Request("http://nginx.org", {headers: h}); return `\${r.url}: \${r.headers.get('a')}`; }, 'http://nginx.org: X, Z'], ['content type', async () => { var r = new Request("http://nginx.org", {body: 'ABC', method: 'POST'}); var body = await r.text(); return `\${body}: \${r.headers.get('Content-Type')}`; }, 'ABC: text/plain;charset=UTF-8'], ['user content type', async () => { var r = new Request("http://nginx.org", {body: 'ABC', headers: {'Content-Type': 'text/html'}}); return r.headers.get('Content-Type'); }, 'text/html'], ['user content type from Headers()', async () => { var h = new Headers(); h.append('Content-Type', 'text/html'); var r = new Request("http://nginx.org", {body: 'ABC', headers: h}); return r.headers.get('Content-Type'); }, 'text/html'], ['user content type deleted', async () => { var h = new Headers(); h.append('Content-Type', 'text/html'); h.delete('Content-Type'); var r = new Request("http://nginx.org", {body: 'ABC', headers: h}); return r.headers.get('Content-Type'); }, 'text/plain;charset=UTF-8'], ['GET body', () => { try { var r = new Request("http://nginx.org", {body: 'ABC'}); } catch (e) { if (!e.message.startsWith('Request body incompatible w')) { throw e; } } return 'OK'; }, 'OK'], ]; run(r, tests); } async function response(r) { const tests = [ ['empty', async () => { var r = new Response(); var body = await r.text(); return `\${r.url}: \${r.status} \${body} \${r.headers.get('a')}`; }, ': 200 null'], ['normal', async () => { var r = new Response("ABC", {headers: {a: 'X', b: 'Y'}}); var body = await r.text(); return `\${r.url}: \${r.status} \${body} \${r.headers.get('a')}`; }, ': 200 ABC X'], ['headers', async () => { var r = new Response(null, {headers: new Headers({a: 'X', b: 'Y'})}); var body = await r.text(); return `\${r.url}: \${body} \${r.headers.get('b')}`; }, ': Y'], ['json', async () => { var r = new Response('{"a": {"b": 42}}'); var json = await r.json(); return json.a.b; }, 42], ['statusText', () => { const statuses = ['status text', 'aa\\u0000a']; try { statuses.forEach(s => { var r = new Response(null, {statusText: s}); if (r.statusText != s) { throw new Error(`r.statusText != \${s}`); } }) } catch (e) { if (!e.message.startsWith('invalid Response statusText')) { throw e; } } return 'OK'; }, 'OK'], ]; run(r, tests); } async function fetch(r) { const tests = [ ['method', async () => { var req = new Request("http://127.0.0.1:$p0/method", {method: 'PUT'}); var r = await ngx.fetch(req); var body = await r.text(); return `\${r.url}: \${r.status} \${body} \${r.headers.get('a')}`; }, 'http://127.0.0.1:$p0/method: 200 PUT null'], ['request body', async () => { var req = new Request("http://127.0.0.1:$p0/body", {body: 'foo'}); var r = await ngx.fetch(req); var body = await r.text(); return `\${r.url}: \${r.status} \${body}`; }, 'http://127.0.0.1:$p0/body: 201 foo'], ]; run(r, tests); } async function fetch_multi_header(r) { const tests = [ ['request multi header', async () => { var h = new Headers({a: 'X'}); h.append('a', 'Z'); var req = new Request("http://127.0.0.1:$p0/header", {headers: h}); var r = await ngx.fetch(req); var body = await r.text(); return `\${r.url}: \${r.status} \${body}`; }, 'http://127.0.0.1:$p0/header: 200 X, Z'], ]; run(r, tests); } export default {njs: test_njs, engine, body, headers, request, response, fetch, fetch_multi_header}; EOF $t->try_run('no njs'); plan(skip_all => 'not yet') if http_get('/engine') =~ /QuickJS$/m; $t->plan(5); ############################################################################### local $TODO = 'not yet' unless has_version('0.7.10'); like(http_get('/headers'), qr/200 OK/s, 'headers tests'); like(http_get('/request'), qr/200 OK/s, 'request tests'); like(http_get('/response'), qr/200 OK/s, 'response tests'); like(http_get('/fetch'), qr/200 OK/s, 'fetch tests'); TODO: { local $TODO = 'not yet' unless $t->has_version('1.23.0'); like(http_get('/fetch_multi_header'), qr/200 OK/s, 'fetch multi header tests'); } ############################################################################### sub has_version { my $need = shift; http_get('/njs') =~ /^([.0-9]+)$/m; my @v = split(/\./, $1); my ($n, $v); for $n (split(/\./, $need)) { $v = shift @v || 0; return 0 if $n > $v; return 1 if $v > $n; } return 1; } ############################################################################### njs-0.8.9/nginx/t/js_fetch_resolver.t000066400000000000000000000143511474132077100176150ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for http njs module, fetch method, dns support. ############################################################################### use warnings; use strict; use Test::More; use IO::Select; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; plan(skip_all => '127.0.0.2 local address required') unless defined IO::Socket::INET->new( LocalAddr => '127.0.0.2' ); my $t = Test::Nginx->new()->has(qw/http/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /njs { js_content test.njs; } location /engine { js_content test.engine; } location /dns { js_content test.dns; resolver 127.0.0.1:%%PORT_8981_UDP%%; resolver_timeout 1s; } } server { listen 127.0.0.1:8080; server_name aaa; location /loc { js_content test.loc; } location /host { return 200 8080:$http_host; } } server { listen 127.0.0.1:8081; server_name aaa; location /loc { js_content test.loc; } location /host { return 200 8081:$http_host; } } server { listen 127.0.0.1:8080; server_name many; location /loc { js_content test.loc; } } } EOF my $p0 = port(8080); my $p1 = port(8081); $t->write_file('test.js', <try_run('no njs.fetch'); plan(skip_all => 'not yet') if http_get('/engine') =~ /QuickJS$/m; $t->plan(5); $t->run_daemon(\&dns_daemon, port(8981), $t); $t->waitforfile($t->testdir . '/' . port(8981)); ############################################################################### like(http_get('/dns?domain=aaa'), qr/aaa:GET:::$/s, 'fetch dns aaa'); like(http_get('/dns?domain=many'), qr/many:GET:::$/s, 'fetch dns many'); like(http_get('/dns?domain=unknown'), qr/"unknown" could not be resolved/s, 'fetch dns unknown'); TODO: { local $TODO = 'not yet' unless has_version('0.8.5'); like(http_get('/dns?domain=aaa&port=p0&loc=host'), qr/8080:aaa:$p0$/s, 'fetch aaa:8080'); like(http_get('/dns?domain=aaa&port=p1&loc=host'), qr/8081:aaa:$p1$/s, 'fetch aaa:8081'); } ############################################################################### sub has_version { my $need = shift; http_get('/njs') =~ /^([.0-9]+)$/m; my @v = split(/\./, $1); my ($n, $v); for $n (split(/\./, $need)) { $v = shift @v || 0; return 0 if $n > $v; return 1 if $v > $n; } return 1; } ############################################################################### sub reply_handler { my ($recv_data, $port, %extra) = @_; my (@name, @rdata); use constant NOERROR => 0; use constant FORMERR => 1; use constant SERVFAIL => 2; use constant NXDOMAIN => 3; use constant A => 1; use constant IN => 1; # default values my ($hdr, $rcode, $ttl) = (0x8180, NOERROR, 3600); # decode name my ($len, $offset) = (undef, 12); while (1) { $len = unpack("\@$offset C", $recv_data); last if $len == 0; $offset++; push @name, unpack("\@$offset A$len", $recv_data); $offset += $len; } $offset -= 1; my ($id, $type, $class) = unpack("n x$offset n2", $recv_data); my $name = join('.', @name); if ($name eq 'aaa' && $type == A) { push @rdata, rd_addr($ttl, '127.0.0.1'); } elsif ($name eq 'many' && $type == A) { push @rdata, rd_addr($ttl, '127.0.0.2'); push @rdata, rd_addr($ttl, '127.0.0.1'); } $len = @name; pack("n6 (C/a*)$len x n2", $id, $hdr | $rcode, 1, scalar @rdata, 0, 0, @name, $type, $class) . join('', @rdata); } sub rd_addr { my ($ttl, $addr) = @_; my $code = 'split(/\./, $addr)'; return pack 'n3N', 0xc00c, A, IN, $ttl if $addr eq ''; pack 'n3N nC4', 0xc00c, A, IN, $ttl, eval "scalar $code", eval($code); } sub dns_daemon { my ($port, $t, %extra) = @_; my ($data, $recv_data); my $socket = IO::Socket::INET->new( LocalAddr => '127.0.0.1', LocalPort => $port, Proto => 'udp', ) or die "Can't create listening socket: $!\n"; my $sel = IO::Select->new($socket); local $SIG{PIPE} = 'IGNORE'; # signal we are ready open my $fh, '>', $t->testdir() . '/' . $port; close $fh; while (my @ready = $sel->can_read) { foreach my $fh (@ready) { if ($socket == $fh) { $fh->recv($recv_data, 65536); $data = reply_handler($recv_data, $port); $fh->send($data); } else { $fh->recv($recv_data, 65536); unless (length $recv_data) { $sel->remove($fh); $fh->close; next; } again: my $len = unpack("n", $recv_data); $data = substr $recv_data, 2, $len; $data = reply_handler($data, $port, tcp => 1); $data = pack("n", length $data) . $data; $fh->send($data); $recv_data = substr $recv_data, 2 + $len; goto again if length $recv_data; } } } } ############################################################################### njs-0.8.9/nginx/t/js_fetch_timeout.t000066400000000000000000000054011474132077100174360ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for http njs module, fetch method timeout. ############################################################################### use warnings; use strict; use Test::More; use Socket qw/ CRLF /; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /njs { js_content test.njs; } location /engine { js_content test.engine; } location /normal_timeout { js_content test.timeout_test; } location /short_timeout { js_fetch_timeout 200ms; js_content test.timeout_test; } } server { listen 127.0.0.1:8081; server_name localhost; location /normal_reply { js_content test.normal_reply; } location /delayed_reply { js_content test.delayed_reply; } } } EOF my $p1 = port(8081); $t->write_file('test.js', < ngx.fetch(v))); let bs = rs.map(v => ({s: v.status, v: v.value ? v.value.headers.X : v.reason})); r.return(200, njs.dump(bs)); } function normal_reply(r) { r.headersOut.X = 'N'; r.return(200); } function delayed_reply(r) { r.headersOut.X = 'D'; setTimeout((r) => { r.return(200); }, 250, r, 0); } export default {njs: test_njs, engine, timeout_test, normal_reply, delayed_reply}; EOF $t->try_run('no js_fetch_timeout'); plan(skip_all => 'not yet') if http_get('/engine') =~ /QuickJS$/m; $t->plan(2); ############################################################################### like(http_get('/normal_timeout'), qr/\[\{s:'fulfilled',v:'N'},\{s:'fulfilled',v:'D'}]$/s, 'normal timeout'); like(http_get('/short_timeout'), qr/\[\{s:'fulfilled',v:'N'},\{s:'rejected',v:Error: read timed out}]$/s, 'short timeout'); ############################################################################### njs-0.8.9/nginx/t/js_fetch_verify.t000066400000000000000000000102561474132077100172600ustar00rootroot00000000000000#!/usr/bin/perl # (C) Sergey Kandaurov # (C) Nginx, Inc. # Tests for http njs module, fetch method, backend certificate verification. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http http_ssl/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; resolver 127.0.0.1:%%PORT_8981_UDP%%; resolver_timeout 1s; location /njs { js_content test.njs; } location /engine { js_content test.engine; } location /https { js_content test.https; } location /https.verify_off { js_content test.https; js_fetch_verify off; } } server { listen 127.0.0.1:8081 ssl; server_name localhost; ssl_certificate localhost.crt; ssl_certificate_key localhost.key; } } EOF my $p1 = port(8081); $t->write_file('test.js', < reply.text()) .then(body => r.return(200, body)) .catch(e => r.return(501, e.message)); } export default {njs: test_njs, engine, https}; EOF $t->write_file('openssl.conf', <testdir(); foreach my $name ('localhost') { system('openssl req -x509 -new ' . "-config $d/openssl.conf -subj /CN=$name/ " . "-out $d/$name.crt -keyout $d/$name.key " . ">>$d/openssl.out 2>&1") == 0 or die "Can't create certificate for $name: $!\n"; } $t->try_run('no js_fetch_verify'); plan(skip_all => 'not yet') if http_get('/engine') =~ /QuickJS$/m; $t->plan(2); $t->run_daemon(\&dns_daemon, port(8981), $t); $t->waitforfile($t->testdir . '/' . port(8981)); ############################################################################### like(http_get('/https'), qr/connect failed/, 'fetch verify error'); like(http_get('/https.verify_off'), qr/200 OK/, 'fetch verify off'); ############################################################################### sub reply_handler { my ($recv_data, $port, %extra) = @_; my (@name, @rdata); use constant NOERROR => 0; use constant A => 1; use constant IN => 1; # default values my ($hdr, $rcode, $ttl) = (0x8180, NOERROR, 3600); # decode name my ($len, $offset) = (undef, 12); while (1) { $len = unpack("\@$offset C", $recv_data); last if $len == 0; $offset++; push @name, unpack("\@$offset A$len", $recv_data); $offset += $len; } $offset -= 1; my ($id, $type, $class) = unpack("n x$offset n2", $recv_data); my $name = join('.', @name); if ($type == A) { push @rdata, rd_addr($ttl, '127.0.0.1'); } $len = @name; pack("n6 (C/a*)$len x n2", $id, $hdr | $rcode, 1, scalar @rdata, 0, 0, @name, $type, $class) . join('', @rdata); } sub rd_addr { my ($ttl, $addr) = @_; my $code = 'split(/\./, $addr)'; return pack 'n3N', 0xc00c, A, IN, $ttl if $addr eq ''; pack 'n3N nC4', 0xc00c, A, IN, $ttl, eval "scalar $code", eval($code); } sub dns_daemon { my ($port, $t) = @_; my ($data, $recv_data); my $socket = IO::Socket::INET->new( LocalAddr => '127.0.0.1', LocalPort => $port, Proto => 'udp', ) or die "Can't create listening socket: $!\n"; local $SIG{PIPE} = 'IGNORE'; # signal we are ready open my $fh, '>', $t->testdir() . '/' . $port; close $fh; while (1) { $socket->recv($recv_data, 65536); $data = reply_handler($recv_data, $port); $socket->send($data); } } ############################################################################### njs-0.8.9/nginx/t/js_header_filter.t000066400000000000000000000035331474132077100174000ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for http njs module, header filter. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http proxy rewrite/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /njs { js_content test.njs; } location /filter/ { js_header_filter test.filter; proxy_pass http://127.0.0.1:8081/; } } server { listen 127.0.0.1:8081; server_name localhost; location / { add_header Set-Cookie "BB"; add_header Set-Cookie "CCCC"; return 200; } } } EOF $t->write_file('test.js', <v.length > len); } export default {njs: test_njs, filter}; EOF $t->try_run('no njs header filter')->plan(2); ############################################################################### like(http_get('/filter/?len=1'), qr/Set-Cookie: BB.*Set-Cookie: CCCC.*/ms, 'all');; unlike(http_get('/filter/?len=3'), qr/Set-Cookie: BB/, 'filter'); ############################################################################### njs-0.8.9/nginx/t/js_header_filter_if.t000066400000000000000000000033761474132077100200630ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for http njs module, header filter, if context. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http proxy rewrite/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /njs { js_content test.njs; } location / { if ($arg_name ~ "add") { js_header_filter test.add; } js_header_filter test.add2; proxy_pass http://127.0.0.1:8081/; } } server { listen 127.0.0.1:8081; server_name localhost; location / { return 200; } } } EOF $t->write_file('test.js', <try_run('no njs header filter')->plan(2); ############################################################################### like(http_get('/?name=add'), qr/Foo: bar/, 'header filter if'); like(http_get('/'), qr/Bar: xxx/, 'header filter'); ############################################################################### njs-0.8.9/nginx/t/js_headers.t000066400000000000000000000426311474132077100162200ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for http njs module, working with headers. ############################################################################### use warnings; use strict; use Test::More; use Socket qw/ CRLF /; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http charset/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_set $test_foo_in test.foo_in; js_set $test_ifoo_in test.ifoo_in; js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /njs { js_content test.njs; } location /content_length { js_content test.content_length; } location /content_length_arr { js_content test.content_length_arr; } location /content_length_keys { js_content test.content_length_keys; } location /content_type { charset windows-1251; default_type text/plain; js_content test.content_type; } location /content_type_arr { charset windows-1251; default_type text/plain; js_content test.content_type_arr; } location /content_encoding { js_content test.content_encoding; } location /content_encoding_arr { js_content test.content_encoding_arr; } location /date { js_content test.date; } location /last_modified { js_content test.last_modified; } location /location { js_content test.location; } location /location_sr { js_content test.location_sr; } location /_redirect { return 307 $request_uri; } location /headers_list { js_content test.headers_list; } location /foo_in { return 200 $test_foo_in; } location /ifoo_in { return 200 $test_ifoo_in; } location /hdr_in { js_content test.hdr_in; } location /raw_hdr_in { js_content test.raw_hdr_in; } location /hdr_out { js_content test.hdr_out; } location /raw_hdr_out { js_content test.raw_hdr_out; } location /hdr_out_array { js_content test.hdr_out_array; } location /hdr_out_set_cookie { js_content test.hdr_out_set_cookie; } location /hdr_out_single { js_content test.hdr_out_single; } location /ihdr_out { js_content test.ihdr_out; } location /hdr_sorted_keys { js_content test.hdr_sorted_keys; } location /hdr_out_special_set { js_content test.hdr_out_special_set; } location /copy_subrequest_hdrs { js_content test.copy_subrequest_hdrs; } location /server { js_content test.server; } location = /subrequest { internal; js_content test.subrequest; } } } EOF $t->write_file('test.js', <= 0x000705) { var clength = r.headersOut['Content-Length']; if (clength !== undefined) { r.return(500, `Content-Length "\${clength}" is not empty`); return; } } delete r.headersOut['Content-Length']; r.headersOut['Content-Length'] = ''; r.headersOut['Content-Length'] = 3; delete r.headersOut['Content-Length']; r.headersOut['Content-Length'] = 3; r.sendHeader(); r.send('XXX'); r.finish(); } function content_length_arr(r) { r.headersOut['Content-Length'] = [5]; r.headersOut['Content-Length'] = []; r.headersOut['Content-Length'] = [4,3]; r.sendHeader(); r.send('XXX'); r.finish(); } function content_length_keys(r) { r.headersOut['Content-Length'] = 3; var in_keys = Object.keys(r.headersOut).some(v=>v=='Content-Length'); r.return(200, `B:\${in_keys}`); } function content_type(r) { if (njs.version_number >= 0x000705) { var ctype = r.headersOut['Content-Type']; if (ctype !== undefined) { r.return(500, `Content-Type "\${ctype}" is not empty`); return; } } delete r.headersOut['Content-Type']; r.headersOut['Content-Type'] = 'text/xml'; r.headersOut['Content-Type'] = ''; r.headersOut['Content-Type'] = 'text/xml; charset='; delete r.headersOut['Content-Type']; r.headersOut['Content-Type'] = 'text/xml; charset=utf-8'; r.headersOut['Content-Type'] = 'text/xml; charset="utf-8"'; var in_keys = Object.keys(r.headersOut).some(v=>v=='Content-Type'); r.return(200, `B:\${in_keys}`); } function content_type_arr(r) { r.headersOut['Content-Type'] = ['text/html']; r.headersOut['Content-Type'] = []; r.headersOut['Content-Type'] = [ 'text/xml', 'text/html']; r.return(200); } function content_encoding(r) { if (njs.version_number >= 0x000705) { var ce = r.headersOut['Content-Encoding']; if (ce !== undefined) { r.return(500, `Content-Encoding "\${ce}" is not empty`); return; } } delete r.headersOut['Content-Encoding']; r.headersOut['Content-Encoding'] = ''; r.headersOut['Content-Encoding'] = 'test'; delete r.headersOut['Content-Encoding']; r.headersOut['Content-Encoding'] = 'gzip'; r.return(200); } function date(r) { r.headersOut['Date'] = 'Sun, 09 Sep 2001 01:46:40 GMT'; r.return(200); } function last_modified(r) { r.headersOut['Last-Modified'] = 'Sun, 09 Sep 2001 01:46:40 GMT'; r.return(200); } function location(r) { if (njs.version_number >= 0x000705) { var lc = r.headersOut['Location']; if (lc !== undefined) { r.return(500, `Location "\${lc}" is not empty`); return; } } delete r.headersOut['Location']; r.headersOut['Location'] = ''; r.headersOut['Location'] = 'test'; delete r.headersOut['Location']; r.headersOut['Location'] = 'loc'; r.return(200); } async function location_sr(r) { let resp = await r.subrequest('/_redirect'); r.headersOut['Location'] = resp.headersOut['Location']; r.return(resp.status, 'loc'); } function content_encoding_arr(r) { r.headersOut['Content-Encoding'] = 'test'; r.headersOut['Content-Encoding'] = []; r.headersOut['Content-Encoding'] = ['test', 'gzip']; r.return(200); } function headers_list(r) { for (var h in {a:1, b:2, c:3}) { r.headersOut[h] = h; } delete r.headersOut.b; r.headersOut.d = 'd'; var out = ""; for (var h in r.headersOut) { out += h + ":"; } r.return(200, out); } function hdr_in(r) { var s = '', h; for (h in r.headersIn) { s += `\${h.toLowerCase()}: \${r.headersIn[h]}\n`; } r.return(200, s); } function raw_hdr_in(r) { var filtered = r.rawHeadersIn .filter(v=>v[0].toLowerCase() == r.args.filter); r.return(200, 'raw:' + filtered.map(v=>v[1]).join('|')); } function hdr_sorted_keys(r) { var s = ''; var hdr = r.args.in ? r.headersIn : r.headersOut; if (!r.args.in) { r.headersOut.b = 'b'; r.headersOut.c = 'c'; r.headersOut.a = 'a'; } r.return(200, Object.keys(hdr).sort()); } function foo_in(r) { return 'hdr=' + r.headersIn.foo; } function ifoo_in(r) { var s = '', h; for (h in r.headersIn) { if (h.substr(0, 3) == 'foo') { s += r.headersIn[h]; } } return s; } function hdr_out(r) { r.status = 200; r.headersOut['Foo'] = r.args.foo; if (r.args.bar) { r.headersOut['Bar'] = r.headersOut[(r.args.bar == 'empty' ? 'Baz' :'Foo')] } r.sendHeader(); r.finish(); } function raw_hdr_out(r) { r.headersOut.a = ['foo', 'bar']; r.headersOut.b = 'b'; var filtered = r.rawHeadersOut .filter(v=>v[0].toLowerCase() == r.args.filter); r.return(200, 'raw:' + filtered.map(v=>v[1]).join('|')); } function hdr_out_array(r) { if (!r.args.hidden) { r.headersOut['Foo'] = [r.args.foo]; r.headersOut['Foo'] = []; r.headersOut['Foo'] = ['bar', r.args.foo]; } if (r.args.scalar_set) { r.headersOut['Foo'] = 'xxx'; } r.return(200, `B:\${r.headersOut.foo}`); } function hdr_out_single(r) { r.headersOut.ETag = ['a', 'b']; r.return(200, `B:\${r.headersOut.etag}`); } function hdr_out_set_cookie(r) { r.headersOut['Set-Cookie'] = []; r.headersOut['Set-Cookie'] = ['a', 'b']; delete r.headersOut['Set-Cookie']; r.headersOut['Set-Cookie'] = 'e'; r.headersOut['Set-Cookie'] = ['c', '', null, 'd', 'f']; var cookies = r.headersOut['Set-Cookie']; r.return(200, `B:\${cookies} \${Array.isArray(cookies)}`); } function ihdr_out(r) { r.status = 200; r.headersOut['a'] = r.args.a; r.headersOut['b'] = r.args.b; var s = '', h; for (h in r.headersOut) { s += r.headersOut[h]; } r.sendHeader(); r.send(s); r.finish(); } function hdr_out_special_set(r) { r.headersOut['Foo'] = "xxx"; r.headersOut['Content-Encoding'] = 'abc'; let ce = r.headersOut['Content-Encoding']; r.return(200, `CE: \${ce}`); } async function copy_subrequest_hdrs(r) { let resp = await r.subrequest("/subrequest"); for (const h in resp.headersOut) { r.headersOut[h] = resp.headersOut[h]; } r.return(200, resp.responseText); } function server(r) { r.headersOut['Server'] = 'Foo'; r.return(200); } function subrequest(r) { r.headersOut['A'] = 'a'; r.headersOut['Content-Encoding'] = 'ce'; r.headersOut['B'] = 'b'; r.headersOut['C'] = 'c'; r.headersOut['D'] = 'd'; r.headersOut['Set-Cookie'] = ['A', 'BB']; r.headersOut['Content-Length'] = 3; r.headersOut['Content-Type'] = 'ct'; r.sendHeader(); r.send('XXX'); r.finish(); } export default {njs:test_njs, content_length, content_length_arr, content_length_keys, content_type, content_type_arr, content_encoding, content_encoding_arr, headers_list, hdr_in, raw_hdr_in, hdr_sorted_keys, foo_in, ifoo_in, hdr_out, raw_hdr_out, hdr_out_array, hdr_out_single, hdr_out_set_cookie, ihdr_out, hdr_out_special_set, copy_subrequest_hdrs, subrequest, date, last_modified, location, location_sr, server}; EOF $t->try_run('no njs')->plan(49); ############################################################################### like(http_get('/content_length'), qr/Content-Length: 3/, 'set Content-Length'); like(http_get('/content_type'), qr/Content-Type: text\/xml; charset="utf-8"\r/, 'set Content-Type'); unlike(http_get('/content_type'), qr/Content-Type: text\/plain/, 'set Content-Type 2'); like(http_get('/content_encoding'), qr/Content-Encoding: gzip/, 'set Content-Encoding'); like(http_get('/headers_list'), qr/a:c:d/, 'headers list'); like(http_get('/ihdr_out?a=12&b=34'), qr/^1234$/m, 'r.headersOut iteration'); like(http_get('/ihdr_out'), qr/\x0d\x0a?\x0d\x0a?$/m, 'r.send zero'); like(http_get('/hdr_out?foo=12345'), qr/Foo: 12345/, 'r.headersOut'); like(http_get('/hdr_out?foo=123&bar=copy'), qr/Bar: 123/, 'r.headersOut get'); unlike(http_get('/hdr_out?bar=empty'), qr/Bar:/, 'r.headersOut empty'); unlike(http_get('/hdr_out?foo='), qr/Foo:/, 'r.headersOut no value'); unlike(http_get('/hdr_out?foo'), qr/Foo:/, 'r.headersOut no value 2'); like(http_get('/content_length_keys'), qr/B:true/, 'Content-Length in keys'); like(http_get('/content_length_arr'), qr/Content-Length: 3/, 'set Content-Length arr'); like(http_get('/content_type'), qr/B:true/, 'Content-Type in keys'); like(http_get('/content_type_arr'), qr/Content-Type: text\/html/, 'set Content-Type arr'); like(http_get('/content_encoding_arr'), qr/Content-Encoding: gzip/, 'set Content-Encoding arr'); like(http_get('/hdr_out_array?foo=12345'), qr/Foo: bar\r\nFoo: 12345/, 'r.headersOut arr'); like(http_get('/hdr_out_array'), qr/Foo: bar/, 'r.headersOut arr last is empty'); like(http_get('/hdr_out_array?foo=abc'), qr/B:bar,\s?abc/, 'r.headersOut get'); like(http_get('/hdr_out_array'), qr/B:bar/, 'r.headersOut get2'); like(http_get('/hdr_out_array?hidden=1'), qr/B:undefined/, 'r.headersOut get3'); like(http_get('/hdr_out_array?scalar_set=1'), qr/B:xxx/, 'r.headersOut scalar set'); like(http_get('/hdr_out_single'), qr/ETag: a\r\nETag: b/, 'r.headersOut single'); like(http_get('/hdr_out_single'), qr/B:a/, 'r.headersOut single get'); like(http_get('/hdr_out_set_cookie'), qr/Set-Cookie: c\r\nSet-Cookie: d/, 'set_cookie'); like(http_get('/hdr_out_set_cookie'), qr/B:c,d,f true/, 'set_cookie2'); unlike(http_get('/hdr_out_set_cookie'), qr/Set-Cookie: [abe]/, 'set_cookie3'); like(http( 'GET /hdr_in HTTP/1.0' . CRLF . 'Cookie: foo' . CRLF . 'Host: localhost' . CRLF . CRLF ), qr/cookie: foo/, 'r.headersIn cookie'); like(http( 'GET /hdr_in HTTP/1.0' . CRLF . 'X-Forwarded-For: foo' . CRLF . 'Host: localhost' . CRLF . CRLF ), qr/x-forwarded-for: foo/, 'r.headersIn xff'); like(http( 'GET /hdr_in HTTP/1.0' . CRLF . 'Cookie: foo1' . CRLF . 'Cookie: foo2' . CRLF . 'Host: localhost' . CRLF . CRLF ), qr/cookie: foo1;\s?foo2/, 'r.headersIn cookie2'); like(http( 'GET /hdr_in HTTP/1.0' . CRLF . 'X-Forwarded-For: foo1' . CRLF . 'X-Forwarded-For: foo2' . CRLF . 'Host: localhost' . CRLF . CRLF ), qr/x-forwarded-for: foo1,\s?foo2/, 'r.headersIn xff2'); like(http( 'GET /hdr_in HTTP/1.0' . CRLF . 'ETag: bar1' . CRLF . 'ETag: bar2' . CRLF . 'Host: localhost' . CRLF . CRLF ), qr/etag: bar1(?!,\s?bar2)/, 'r.headersIn duplicate single'); like(http( 'GET /hdr_in HTTP/1.0' . CRLF . 'Content-Type: bar1' . CRLF . 'Content-Type: bar2' . CRLF . 'Host: localhost' . CRLF . CRLF ), qr/content-type: bar1(?!,\s?bar2)/, 'r.headersIn duplicate single 2'); like(http( 'GET /hdr_in HTTP/1.0' . CRLF . 'Foo: bar1' . CRLF . 'Foo: bar2' . CRLF . 'Host: localhost' . CRLF . CRLF ), qr/foo: bar1,\s?bar2/, 'r.headersIn duplicate generic'); like(http( 'GET /raw_hdr_in?filter=foo HTTP/1.0' . CRLF . 'foo: bar1' . CRLF . 'Foo: bar2' . CRLF . 'Host: localhost' . CRLF . CRLF ), qr/raw: bar1|bar2/, 'r.rawHeadersIn'); like(http_get('/raw_hdr_out?filter=a'), qr/raw: foo|bar/, 'r.rawHeadersOut'); like(http( 'GET /hdr_sorted_keys?in=1 HTTP/1.0' . CRLF . 'Cookie: foo1' . CRLF . 'Accept: */*' . CRLF . 'Cookie: foo2' . CRLF . 'Host: localhost' . CRLF . CRLF ), qr/Accept,Cookie,Host/, 'r.headersIn sorted keys'); like(http( 'GET /hdr_sorted_keys HTTP/1.0' . CRLF . 'Host: localhost' . CRLF . CRLF ), qr/a,b,c/, 'r.headersOut sorted keys'); TODO: { local $TODO = 'not yet' unless has_version('0.7.6'); like(http_get('/hdr_out_special_set'), qr/CE: abc/, 'r.headerOut special set'); like(http_get('/copy_subrequest_hdrs'), qr/A: a.*B: b.*C: c.*D: d.*Set-Cookie: A.*Set-Cookie: BB/s, 'subrequest copy'); like(http_get('/copy_subrequest_hdrs'), qr/Content-Type: ct.*Content-Encoding: ce.*Content-Length: 3/s, 'subrequest copy special'); } TODO: { local $TODO = 'not yet' unless has_version('0.8.0'); like(http_get('/location'), qr/Location: loc/, 'set location'); unlike(http_get('/location_sr'), qr/Location: \/location_sr/, 'location redirect'); } TODO: { local $TODO = 'not yet' unless has_version('0.8.1'); like(http_get('/date'), qr/Date: Sun, 09 Sep 2001 01:46:40 GMT/, 'set date'); like(http_get('/last_modified'), qr/Last-Modified: Sun, 09 Sep 2001 01:46:40 GMT/, 'set Last-Modified'); } TODO: { local $TODO = 'not yet' unless has_version('0.8.4'); like(http_get('/date'), qr/Server: nginx/, 'normal server'); like(http_get('/server'), qr/Server: Foo/, 'set server'); unlike(http_get('/server'), qr/Server: nginx/, 'set server 2'); } ############################################################################### sub has_version { my $need = shift; http_get('/njs') =~ /^([.0-9]+)$/m; my @v = split(/\./, $1); my ($n, $v); for $n (split(/\./, $need)) { $v = shift @v || 0; return 0 if $n > $v; return 1 if $v > $n; } return 1; } ############################################################################### njs-0.8.9/nginx/t/js_import.t000066400000000000000000000040171474132077100161130ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (c) Nginx, Inc. # Tests for http njs module, js_import directive. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_set $test foo.bar.p; js_import lib.js; js_import fun.js; js_import foo from ./main.js; server { listen 127.0.0.1:8080; server_name localhost; location /njs { js_content foo.version; } location /test_foo { js_content foo.test; } location /test_lib { js_content lib.test; } location /test_fun { js_content fun; } location /test_var { return 200 $test; } } } EOF $t->write_file('lib.js', <write_file('fun.js', <write_file('main.js', <try_run('no njs available')->plan(4); ############################################################################### like(http_get('/test_foo'), qr/MAIN-TEST/s, 'foo.test'); like(http_get('/test_lib'), qr/LIB-TEST/s, 'lib.test'); like(http_get('/test_fun'), qr/FUN-TEST/s, 'fun'); like(http_get('/test_var'), qr/P-TEST/s, 'foo.bar.p'); ############################################################################### njs-0.8.9/nginx/t/js_import2.t000066400000000000000000000052761474132077100162050ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (c) Nginx, Inc. # Tests for http njs module, js_import directive in server | location contexts. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http proxy rewrite/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% server { listen 127.0.0.1:8080; server_name localhost; js_set $test foo.bar.p; # context 1 js_import foo from main.js; location /njs { js_content foo.version; } location /test_foo { js_content foo.test; } location /test_lib { # context 2 js_import lib.js; js_content lib.test; } location /test_fun { # context 3 js_import fun.js; js_content fun; } location /test_var { return 200 $test; } location /proxy { proxy_pass http://127.0.0.1:8081/; } } server { listen 127.0.0.1:8081; server_name localhost; location /test_fun { # context 4 js_import fun.js; js_content fun; } } } EOF $t->write_file('lib.js', <write_file('fun.js', <write_file('main.js', <try_run('no njs available')->plan(6); ############################################################################### like(http_get('/test_foo'), qr/MAIN-TEST/s, 'foo.test'); like(http_get('/test_lib'), qr/LIB-TEST/s, 'lib.test'); like(http_get('/test_fun'), qr/FUN-TEST/s, 'fun'); like(http_get('/proxy/test_fun'), qr/FUN-TEST/s, 'proxy fun'); like(http_get('/test_var'), qr/P-TEST/s, 'foo.bar.p'); $t->stop(); my $content = $t->read_file('error.log'); my $count = () = $content =~ m/js vm init/g; ok($count == 4, 'uniq js vm contexts'); ############################################################################### njs-0.8.9/nginx/t/js_import_relative.t000066400000000000000000000035171474132077100200120ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (c) Nginx, Inc. # Tests for http njs module, js_import directive, importing relative paths. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import main from lib/main.js; server { listen 127.0.0.1:8080; server_name localhost; location /local { js_content main.test_local; } location /top { js_content main.test_top; } } } EOF my $d = $t->testdir(); mkdir("$d/lib"); mkdir("$d/lib/sub"); $t->write_file('lib/main.js', <write_file('lib/sub/foo.js', <write_file('lib/foo.js', <write_file('foo.js', <try_run('no njs available')->plan(2); ############################################################################### like(http_get('/local'), qr/LOCAL/s, 'local relative import'); like(http_get('/top'), qr/TOP/s, 'local relative import 2'); ############################################################################### njs-0.8.9/nginx/t/js_internal_redirect.t000066400000000000000000000043671474132077100203060ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for http njs module, internalRedirect method. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http rewrite/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /njs { js_content test.njs; } location /test { js_content test.redirect; } location /redirect { internal; return 200 redirect$arg_b; } location @named { return 200 named; } } } EOF $t->write_file('test.js', <try_run('no njs available')->plan(5); ############################################################################### like(http_get('/test'), qr/redirect/s, 'redirect'); like(http_get('/test?a=A'), qr/redirectA/s, 'redirect with args'); like(http_get('/test?dest=named'), qr/named/s, 'redirect to named location'); like(http_get('/test?unsafe=1'), qr/500 Internal Server/s, 'unsafe redirect'); like(http_get('/test?quoted=1'), qr/200 .*redirect/s, 'quoted redirect'); ############################################################################### njs-0.8.9/nginx/t/js_merge_location_blocks.t000066400000000000000000000027611474132077100211310ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (c) Nginx, Inc. # Tests for http njs module, check for proper location blocks merging. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import main.js; server { listen 127.0.0.1:8080; server_name localhost; location /a { js_content main.version; } location /b { js_content main.version; } location /c { js_content main.version; } location /d { js_content main.version; } } } EOF $t->write_file('main.js', <try_run('no njs available')->plan(1); ############################################################################### $t->stop(); my $content = $t->read_file('error.log'); my $count = () = $content =~ m/ js vm init/g; ok($count == 1, 'http js block imported once'); ############################################################################### njs-0.8.9/nginx/t/js_merge_server_blocks.t000066400000000000000000000025421474132077100206240ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (c) Nginx, Inc. # Tests for http njs module, check for proper server blocks merging. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import main.js; server { listen 127.0.0.1:8080; } server { listen 127.0.0.1:8081; } server { listen 127.0.0.1:8082; } server { listen 127.0.0.1:8083; } } EOF $t->write_file('main.js', <try_run('no njs available')->plan(1); ############################################################################### $t->stop(); my $content = $t->read_file('error.log'); my $count = () = $content =~ m/ js vm init/g; ok($count == 1, 'http js block imported once'); ############################################################################### njs-0.8.9/nginx/t/js_modules.t000066400000000000000000000027671474132077100162630ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for http njs module, ES6 import, export. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /test { js_content test.test; } } } EOF $t->write_file('test.js', <write_file('module.js', <try_run('no njs modules')->plan(2); ############################################################################### like(http_get('/test?fun=sum&a=3&b=4'), qr/7/s, 'test sum'); like(http_get('/test?fun=prod&a=3&b=4'), qr/12/s, 'test prod'); ############################################################################### njs-0.8.9/nginx/t/js_ngx.t000066400000000000000000000036171474132077100154020ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for http njs module, ngx object. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /njs { js_content test.njs; } location /log { js_content test.log; } } } EOF $t->write_file('test.js', <try_run('no njs ngx')->plan(3); ############################################################################### http_get('/log?level=INFO&text=FOO'); http_get('/log?level=WARN&text=BAR'); http_get('/log?level=ERR&text=BAZ'); $t->stop(); like($t->read_file('error.log'), qr/\[info\].*ngx.log:FOO/, 'ngx.log info'); like($t->read_file('error.log'), qr/\[warn\].*ngx.log:BAR/, 'ngx.log warn'); like($t->read_file('error.log'), qr/\[error\].*ngx.log:BAZ/, 'ngx.log err'); ############################################################################### njs-0.8.9/nginx/t/js_object.t000066400000000000000000000067001474132077100160500ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for http njs module, request object. ############################################################################### use warnings; use strict; use Test::More; use Socket qw/ CRLF /; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /engine { js_content test.engine; } location /to_string { js_content test.to_string; } location /define_prop { js_content test.define_prop; } location /in_operator { js_content test.in_operator; } location /redefine_bind { js_content test.redefine_bind; } location /redefine_proxy { js_content test.redefine_proxy; } location /redefine_proto { js_content test.redefine_proto; } location /get_own_prop_descs { js_content test.get_own_prop_descs; } } } EOF $t->write_file('test.js', <v in r.headersIn) .toString() === 'true,false'); } function redefine_bind(r) { r.return = r.return.bind(r, 200); r.return('redefine_bind'); } function redefine_proxy(r) { r.return_orig = r.return; r.return = function (body) { this.return_orig(200, body);} r.return('redefine_proxy'); } function redefine_proto(r) { r[0] = 'a'; r[1] = 'b'; r.length = 2; Object.setPrototypeOf(r, Array.prototype); r.return(200, r.join('|')); } function get_own_prop_descs(r) { r.return(200, Object.getOwnPropertyDescriptors(r)['log'].value === r.log); } export default {engine, to_string, define_prop, in_operator, redefine_bind, redefine_proxy, redefine_proto, get_own_prop_descs}; EOF $t->try_run('no njs request object')->plan(7); ############################################################################### like(http_get('/to_string'), qr/\[object Request\]/, 'toString'); like(http_get('/define_prop'), qr/Foo: bar/, 'define_prop'); like(http( 'GET /in_operator HTTP/1.0' . CRLF . 'Foo: foo' . CRLF . 'Host: localhost' . CRLF . CRLF ), qr/true/, 'in_operator'); like(http_get('/redefine_bind'), qr/redefine_bind/, 'redefine_bind'); like(http_get('/redefine_proxy'), qr/redefine_proxy/, 'redefine_proxy'); like(http_get('/redefine_proto'), qr/a|b/, 'redefine_proto'); SKIP: { skip "In QuickJS methods are in the prototype", 1 if http_get('/engine') =~ /QuickJS$/m; like(http_get('/get_own_prop_descs'), qr/true/, 'get_own_prop_descs'); } ############################################################################### njs-0.8.9/nginx/t/js_paths.t000066400000000000000000000041711474132077100157210ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for http njs module, js_path directive. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_path "%%TESTDIR%%/lib1"; js_path "lib2"; js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /test { js_content test.test; } location /test2 { js_content test.test2; } } } EOF $t->write_file('test.js', <testdir(); mkdir("$d/lib1"); mkdir("$d/lib2"); $t->write_file('lib1/module1.js', <write_file('lib2/module2.js', <try_run('no njs available')->plan(4); ############################################################################### like(http_get('/test?fun=sum&a=3&b=4'), qr/7/s, 'test sum'); like(http_get('/test?fun=prod&a=3&b=4'), qr/12/s, 'test prod'); like(http_get('/test2?a=3&b=4'), qr/34/s, 'test2'); like(http_get('/test2?a=A&b=B'), qr/AB/s, 'test2 relative'); ############################################################################### njs-0.8.9/nginx/t/js_periodic.t000066400000000000000000000157641474132077100164120ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for js_periodic directive. ############################################################################### use warnings; use strict; use Test::More; use Socket qw/ CRLF /; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http rewrite/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; worker_processes 4; events { } worker_shutdown_timeout 100ms; http { %%TEST_GLOBALS_HTTP%% js_import test.js; js_shared_dict_zone zone=nums:32k type=number; js_shared_dict_zone zone=strings:32k; js_shared_dict_zone zone=workers:32k type=number; js_set $js_set test.js_set; js_var $js_var JS-VAR; map _ $map_var { default "MAP-VAR"; } server { listen 127.0.0.1:8080; server_name localhost; location @periodic { js_periodic test.tick interval=30ms jitter=1ms; js_periodic test.timer interval=1s worker_affinity=all; js_periodic test.overrun interval=30ms; js_periodic test.file interval=1s; js_periodic test.fetch interval=40ms; js_periodic test.multiple_fetches interval=1s; js_periodic test.affinity interval=50ms worker_affinity=0101; js_periodic test.vars interval=10s; js_periodic test.fetch_exception interval=1s; js_periodic test.tick_exception interval=1s; js_periodic test.timer_exception interval=1s; js_periodic test.timeout_exception interval=30ms; } location /engine { js_content test.engine; } location /fetch_ok { return 200 'ok'; } location /fetch_foo { return 200 'foo'; } location /test_affinity { js_content test.test_affinity; } location /test_fetch { js_content test.test_fetch; } location /test_file { js_content test.test_file; } location /test_multiple_fetches { js_content test.test_multiple_fetches; } location /test_tick { js_content test.test_tick; } location /test_timer { js_content test.test_timer; } location /test_timeout_exception { js_content test.test_timeout_exception; } location /test_vars { js_content test.test_vars; } } } EOF my $p0 = port(8080); $t->write_file('test.js', < {}, 100000); } function tick() { ngx.shared.nums.incr('tick', 1); } function tick_exception() { throw new Error("EXCEPTION"); } function timer() { if (ngx.worker_id != 0) { return; } setTimeout(() => {ngx.shared.nums.set('timer', 1)}, 10); } function timer_exception() { setTimeout(() => {ngx.log(ngx.ERR, 'should not be seen')}, 10); throw new Error("EXCEPTION"); } function timeout_exception() { setTimeout(() => { var v = ngx.shared.nums.get('timeout_exception') || 0; if (v == 0) { ngx.shared.nums.set('timeout_exception', 1); throw new Error("EXCEPTION"); return; } ngx.shared.nums.incr('timeout_exception', 1); }, 1); } function vars(s) { var v = s.variables; ngx.shared.strings.set('vars', `\${v.js_var}|\${v.js_set}|\${v.map_var}`); } function test_affinity(r) { r.return(200, `[\${ngx.shared.workers.keys().toSorted()}]`); } function test_fetch(r) { r.return(200, ngx.shared.strings.get('fetch').startsWith('okok')); } function test_file(r) { r.return(200, fs.readFileSync(ngx.conf_prefix + 'file').toString() == 'abc'); } function test_multiple_fetches(r) { r.return(200, ngx.shared.strings.get('multiple_fetches') .startsWith('ok\@foo')); } function test_tick(r) { r.return(200, ngx.shared.nums.get('tick') >= 3); } function test_timer(r) { r.return(200, ngx.shared.nums.get('timer') == 1); } function test_timeout_exception(r) { r.return(200, ngx.shared.nums.get('timeout_exception') >= 2); } function test_vars(r) { r.return(200, ngx.shared.strings.get('vars')); } export default { affinity, fetch, fetch_exception, file, js_set, multiple_fetches, overrun, vars, test_affinity, test_fetch, test_file, test_multiple_fetches, test_tick, test_timeout_exception, test_timer, test_vars, tick, tick_exception, timer, timer_exception, timeout_exception, engine }; EOF $t->try_run('no js_periodic'); plan(skip_all => 'not yet') if http_get('/engine') =~ /QuickJS$/m; $t->plan(9); ############################################################################### select undef, undef, undef, 0.1; like(http_get('/test_affinity'), qr/\[1,3]/, 'affinity test'); like(http_get('/test_tick'), qr/true/, '3x tick test'); like(http_get('/test_timer'), qr/true/, 'timer test'); like(http_get('/test_file'), qr/true/, 'file test'); like(http_get('/test_fetch'), qr/true/, 'periodic fetch test'); like(http_get('/test_multiple_fetches'), qr/true/, 'multiple fetch test'); like(http_get('/test_timeout_exception'), qr/true/, 'timeout exception test'); like(http_get('/test_vars'), qr/JS-VAR\|JS-SET\|MAP-VAR/, 'vars test'); $t->stop(); unlike($t->read_file('error.log'), qr/\[error\].*should not be seen/, 'check for not discadred events'); njs-0.8.9/nginx/t/js_preload_object.t000066400000000000000000000112121474132077100175500ustar00rootroot00000000000000#!/usr/bin/perl # (C) Vadim Zhestikov # (C) Nginx, Inc. # Tests for http njs module, js_preload_object directive. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http rewrite/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_preload_object g1 from g.json; js_preload_object ga from ga.json; server { listen 127.0.0.1:8080; server_name localhost; js_import lib.js; js_preload_object lx from l.json; location /engine { js_content lib.engine; } location /test { js_content lib.test; } location /test_query { js_import lib1.js; js_content lib1.query; } location /test_query_preloaded { js_import lib1.js; js_preload_object l.json; js_content lib1.query; } location /test_var { js_set $test_var lib.test_var; return 200 $test_var; } location /test_mutate { js_content lib.mutate; } location /test_no_suffix { js_preload_object gg from no_suffix; js_content lib.suffix; } } } EOF $t->write_file('lib.js', <write_file('lib1.js', < a[v], globalThis); } catch (e) { res = e.message; } r.return(200, njs.dump(res)); } export default {query}; EOF $t->write_file('g.json', '{"a":1, "b":[1,2,"element",4,5], "c":{"prop":[{"a":2}]}}'); $t->write_file('ga.json', '"ga loaded"'); $t->write_file('l.json', '"l loaded"'); $t->write_file('no_suffix', '"no_suffix loaded"'); $t->try_run('no js_preload_object available'); plan(skip_all => 'not yet') if http_get('/engine') =~ /QuickJS$/m; $t->plan(12); ############################################################################### like(http_get('/test'), qr/ga loaded 2 l loaded/s, 'direct query'); like(http_get('/test_query?path=l'), qr/undefined/s, 'unreferenced'); like(http_get('/test_query_preloaded?path=l'), qr/l loaded/s, 'reference preload'); like(http_get('/test_query?path=g1.b.1'), qr/2/s, 'complex query'); like(http_get('/test_var'), qr/element/s, 'var reference'); like(http_get('/test_mutate?method=set_obj'), qr/Cannot assign to read-only/s, 'preload_object props are const (object)'); like(http_get('/test_mutate?method=set_arr'), qr/Cannot assign to read-only/s, 'preload_object props are const (array)'); like(http_get('/test_mutate?method=add_obj'), qr/Cannot add property "xxx"/s, 'preload_object props are not extensible (object)'); like(http_get('/test_mutate?method=add_arr'), qr/Cannot add property "10"/s, 'preload_object props are not extensible (array)'); like(http_get('/test_mutate?method=del_obj'), qr/Cannot delete property "a"/s, 'preload_object props are not deletable (object)'); like(http_get('/test_mutate?method=del_arr'), qr/Cannot delete property "0"/s, 'preload_object props are not deletable (array)'); like(http_get('/test_no_suffix'), qr/no_suffix loaded/s, 'load without suffix'); ############################################################################### njs-0.8.9/nginx/t/js_process.t000066400000000000000000000031741474132077100162620ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for http njs module, process object. ############################################################################### use warnings; use strict; use Test::More; use Socket qw/ CRLF /; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } env FOO=bar; env BAR=baz; http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /argv { js_content test.argv; } location /env { js_content test.env; } } } EOF $t->write_file('test.js', <= 0}`); } function env(r) { var e = process.env[r.args.var]; r.return(200, e ? e : 'undefined'); } export default { argv, env }; EOF $t->try_run('no njs process object')->plan(4); ############################################################################### like(http_get('/argv'), qr/true true/, 'argv'); like(http_get('/env?var=FOO'), qr/bar/, 'env FOO'); like(http_get('/env?var=BAR'), qr/baz/, 'env BAR'); like(http_get('/env?var=HOME'), qr/undefined/, 'env HOME'); ############################################################################### njs-0.8.9/nginx/t/js_promise.t000066400000000000000000000114531474132077100162610ustar00rootroot00000000000000#!/usr/bin/perl # (C) Nginx, Inc. # Promise tests for http njs module. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /njs { js_content test.njs; } location /promise { js_content test.promise; } location /promise_throw { js_content test.promise_throw; } location /promise_pure { js_content test.promise_pure; } location /timeout { js_content test.timeout; } location /sub_token { js_content test.sub_token; } } } EOF $t->write_file('test.js', < { var data = JSON.parse(reply.responseText); if (data['token'] !== "a") { throw new Error('token is not "a"'); } return data['token']; }) .then(token => { promisified_subrequest(r, '/sub_token', 'code=200&token=b') .then(reply => { var data = JSON.parse(reply.responseText); r.return(200, '{"token": "' + data['token'] + '"}'); }) .catch(() => { throw new Error("failed promise() test"); }); }) .catch(() => { r.return(500); }); } function promise_throw(r) { promisified_subrequest(r, '/sub_token', 'code=200&token=x') .then(reply => { var data = JSON.parse(reply.responseText); if (data['token'] !== "a") { throw data['token']; } return data['token']; }) .then(() => { r.return(500); }) .catch(token => { r.return(200, '{"token": "' + token + '"}'); }); } function promise_pure(r) { var count = 0; Promise.resolve(true) .then(() => count++) .then(() => not_exist_ref) .finally(() => count++) .catch(() => { r.return((count != 2) ? 500 : 200); }); } function timeout(r) { promisified_subrequest(r, '/sub_token', 'code=200&token=R') .then(reply => JSON.parse(reply.responseText)) .then(data => { setTimeout(timeout_cb, 50, r, '/sub_token', 'code=200&token=T'); return data; }) .then(data => { setTimeout(timeout_cb, 1, r, '/sub_token', 'code=200&token=' + data['token']); }) .catch(() => { r.return(500); }); } function timeout_cb(r, url, args) { promisified_subrequest(r, url, args) .then(reply => { if (global_token == '') { var data = JSON.parse(reply.responseText); global_token = data['token']; r.return(200, '{"token": "' + data['token'] + '"}'); } }) .catch(() => { r.return(500); }); } function promisified_subrequest(r, uri, args) { return new Promise((resolve, reject) => { r.subrequest(uri, args, (reply) => { if (reply.status < 400) { resolve(reply); } else { reject(reply); } }); }) } function sub_token(r) { var code = r.variables.arg_code; var token = r.variables.arg_token; r.return(parseInt(code), '{"token": "'+ token +'"}'); } export default {njs:test_njs, promise, promise_throw, promise_pure, timeout, sub_token}; EOF $t->try_run('no njs available')->plan(4); ############################################################################### like(http_get('/promise'), qr/{"token": "b"}/, "Promise"); like(http_get('/promise_throw'), qr/{"token": "x"}/, "Promise throw and catch"); like(http_get('/timeout'), qr/{"token": "R"}/, "Promise with timeout"); like(http_get('/promise_pure'), qr/200 OK/, "events handling"); ############################################################################### njs-0.8.9/nginx/t/js_request_body.t000066400000000000000000000040771474132077100173140ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for http njs module, r.requestText method. ############################################################################### use warnings; use strict; use Test::More; use Socket qw/ CRLF /; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /body { js_content test.body; } location /in_file { client_body_in_file_only on; js_content test.body; } } } EOF $t->write_file('test.js', <try_run('no njs request body')->plan(3); ############################################################################### like(http_post('/body'), qr/REQ-BODY/, 'request body'); like(http_post('/in_file'), qr/request body is in a file/, 'request body in file'); like(http_post_big('/body'), qr/200.*^(1234567890){1024}$/ms, 'request body big'); ############################################################################### sub http_post { my ($url, %extra) = @_; my $p = "POST $url HTTP/1.0" . CRLF . "Host: localhost" . CRLF . "Content-Length: 8" . CRLF . CRLF . "REQ-BODY"; return http($p, %extra); } sub http_post_big { my ($url, %extra) = @_; my $p = "POST $url HTTP/1.0" . CRLF . "Host: localhost" . CRLF . "Content-Length: 10240" . CRLF . CRLF . ("1234567890" x 1024); return http($p, %extra); } ############################################################################### njs-0.8.9/nginx/t/js_return.t000066400000000000000000000046771474132077100161340ustar00rootroot00000000000000#!/usr/bin/perl # (C) Sergey Kandaurov # (C) Nginx, Inc. # Tests for http njs module, return method. ############################################################################### use warnings; use strict; use Test::More; use Config; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location / { js_content test.returnf; } location /limit { sendfile_max_chunk 5; js_content test.returnf; } location /njs { js_content test.njs; } } } EOF $t->write_file('test.js', <try_run('no njs return')->plan(7); ############################################################################### like(http_get('/?c=200'), qr/200 OK.*\x0d\x0a?\x0d\x0a?$/s, 'return code'); like(http_get('/?c=200&t=SEE-THIS'), qr/200 OK.*^SEE-THIS$/ms, 'return text'); like(http_get('/?c=301&t=path'), qr/ 301 .*Location: path/s, 'return redirect'); like(http_get('/?c=404'), qr/404 Not.*html/s, 'return error page'); like(http_get('/?c=inv'), qr/ 500 /, 'return invalid'); TODO: { local $TODO = 'not yet' unless has_version('0.8.6'); unlike(http_get('/?c=404&t='), qr/Not.*html/s, 'return empty body'); } TODO: { local $TODO = 'not yet' unless has_version('0.8.8'); like(http_get('/limit?c=200&t=X&repeat=50'), qr/200 OK.*X{50}/s, 'return limited'); } ############################################################################### sub has_version { my $need = shift; http_get('/njs') =~ /^([.0-9]+)$/m; my @v = split(/\./, $1); my ($n, $v); for $n (split(/\./, $need)) { $v = shift @v || 0; return 0 if $n > $v; return 1 if $v > $n; } return 1; } ############################################################################### njs-0.8.9/nginx/t/js_shared_dict.t000066400000000000000000000301621474132077100170520ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for js_shared_dict_zone directive. ############################################################################### use warnings; use strict; use Test::More; use Socket qw/ CRLF /; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; js_shared_dict_zone zone=foo:32k timeout=2s evict; js_shared_dict_zone zone=bar:64k type=string; js_shared_dict_zone zone=waka:32k timeout=1000s type=number; js_shared_dict_zone zone=no_timeout:32k; js_shared_dict_zone zone=overflow:32k; server { listen 127.0.0.1:8080; server_name localhost; location /njs { js_content test.njs; } location /engine { js_content test.engine; } location /add { js_content test.add; } location /capacity { js_content test.capacity; } location /chain { js_content test.chain; } location /clear { js_content test.clear; } location /delete { js_content test.del; } location /free_space { js_content test.free_space; } location /get { js_content test.get; } location /has { js_content test.has; } location /incr { js_content test.incr; } location /items { js_content test.items; } location /keys { js_content test.keys; } location /name { js_content test.name; } location /overflow { js_content test.overflow; } location /pop { js_content test.pop; } location /replace { js_content test.replace; } location /set { js_content test.set; } location /set_clear { js_content test.set_clear; } location /size { js_content test.size; } location /zones { js_content test.zones; } } } EOF $t->write_file('test.js', <<'EOF'); function test_njs(r) { r.return(200, njs.version); } function engine(r) { r.return(200, njs.engine); } function convertToValue(dict, v) { if (dict.type == 'number') { return parseInt(v); } else if (v == 'empty') { v = ''; } return v; } function add(r) { var dict = ngx.shared[r.args.dict]; var value = convertToValue(dict, r.args.value); if (r.args.timeout) { var timeout = Number(r.args.timeout); r.return(200, dict.add(r.args.key, value, timeout)); } else { r.return(200, dict.add(r.args.key, value)); } } function capacity(r) { var dict = ngx.shared[r.args.dict]; r.return(200, dict.capacity); } function chain(r) { var dict = ngx.shared[r.args.dict]; var val = dict.set(r.args.key, r.args.value).get(r.args.key); r.return(200, val); } function clear(r) { var dict = ngx.shared[r.args.dict]; var result = dict.clear(); r.return(200, result === undefined ? 'undefined' : result); } function del(r) { var dict = ngx.shared[r.args.dict]; r.return(200, dict.delete(r.args.key)); } function free_space(r) { var dict = ngx.shared[r.args.dict]; var free_space = dict.freeSpace(); r.return(200, free_space >= 0 && free_space <= dict.capacity); } function get(r) { var dict = ngx.shared[r.args.dict]; var val = dict.get(r.args.key); if (val == '') { val = 'empty'; } else if (val === undefined) { val = 'undefined'; } r.return(200, val); } function has(r) { var dict = ngx.shared[r.args.dict]; r.return(200, dict.has(r.args.key)); } function incr(r) { var dict = ngx.shared[r.args.dict]; var def = r.args.def ? parseInt(r.args.def) : 0; if (r.args.timeout) { var timeout = Number(r.args.timeout); var val = dict.incr(r.args.key, parseInt(r.args.by), def, timeout); r.return(200, val); } else { var val = dict.incr(r.args.key, parseInt(r.args.by), def); r.return(200, val); } } function keys(r) { var ks; if (r.args.max) { ks = ngx.shared[r.args.dict].keys(parseInt(r.args.max)); } else { ks = ngx.shared[r.args.dict].keys(); } var sorted = ks.toSorted(); r.return(200, (sorted.length) ? sorted.join(",") : "empty"); } function items(r) { var kvs; if (r.args.max) { kvs = ngx.shared[r.args.dict].items(parseInt(r.args.max)); } else { kvs = ngx.shared[r.args.dict].items(); } r.return(200, kvs.toSorted().join("|")); } function name(r) { r.return(200, ngx.shared[r.args.dict].name); } function replace(r) { var dict = ngx.shared[r.args.dict]; var value = convertToValue(dict, r.args.value); r.return(200, dict.replace(r.args.key, value)); } function overflow(r) { var dict = ngx.shared.overflow; var v = 'x'.repeat(1024); try { for (var i = 0; i < 1024; i++) { dict.set('key' + i, v); } } catch (e) { if (e instanceof SharedMemoryError) { r.return(200, 'SharedMemoryError'); return; } r.return(200, e.toString()); return; } r.return(200, 'no exception'); } function pop(r) { var dict = ngx.shared[r.args.dict]; var val = dict.pop(r.args.key); if (val == '') { val = 'empty'; } else if (val === undefined) { val = 'undefined'; } r.return(200, val); } function set(r) { var dict = ngx.shared[r.args.dict]; var value = convertToValue(dict, r.args.value); if (r.args.timeout) { var timeout = Number(r.args.timeout); r.return(200, dict.set(r.args.key, value, timeout) === dict); } else { r.return(200, dict.set(r.args.key, value) === dict); } } function size(r) { var dict = ngx.shared[r.args.dict]; r.return(200, `size: ${dict.size()}`); } function set_clear(r) { var dict = ngx.shared.no_timeout; dict.clear(); dict.set("test", "test value"); dict.set("test1", "test1 value"); dict.clear(); r.return(200, `size: ${dict.size()}`); } function zones(r) { r.return(200, Object.keys(ngx.shared).sort()); } export default { add, capacity, chain, clear, del, free_space, get, has, incr, items, keys, name, njs: test_njs, pop, replace, set, set_clear, size, zones, engine, overflow }; EOF $t->try_run('no js_shared_dict_zone'); $t->plan(52); ############################################################################### like(http_get('/zones'), qr/bar,foo/, 'available zones'); like(http_get('/capacity?dict=foo'), qr/32768/, 'foo capacity'); like(http_get('/capacity?dict=bar'), qr/65536/, 'bar capacity'); like(http_get('/free_space?dict=foo'), qr/true/, 'foo free space'); like(http_get('/name?dict=foo'), qr/foo/, 'foo name'); like(http_get('/size?dict=foo'), qr/size: 0/, 'no of items in foo'); like(http_get('/add?dict=foo&key=FOO&value=xxx'), qr/true/, 'add foo.FOO'); like(http_get('/add?dict=foo&key=FOO&value=xxx'), qr/false/, 'failed add foo.FOO'); like(http_get('/set?dict=foo&key=FOO2&value=yyy'), qr/true/, 'set foo.FOO2'); like(http_get('/set?dict=foo&key=FOO3&value=empty'), qr/true/, 'set foo.FOO3'); like(http_get('/set?dict=bar&key=FOO&value=zzz'), qr/true/, 'set bar.FOO'); like(http_get('/set?dict=waka&key=FOO&value=42'), qr/true/, 'set waka.FOO'); like(http_get('/chain?dict=bar&key=FOO2&value=aaa'), qr/aaa/, 'chain bar.FOO2'); like(http_get('/incr?dict=waka&key=FOO&by=5'), qr/47/, 'incr waka.FOO'); like(http_get('/incr?dict=waka&key=FOO2&by=7777'), qr/7777/, 'incr waka.FOO2'); like(http_get('/incr?dict=waka&key=FOO2&by=2'), qr/7779/, 'incr waka.FOO2'); like(http_get('/incr?dict=waka&key=FOO3&by=3333&def=5'), qr/3338/, 'incr waka.FOO3'); like(http_get('/has?dict=foo&key=FOO'), qr/true/, 'has foo.FOO'); like(http_get('/has?dict=foo&key=NOT_EXISTING'), qr/false/, 'failed has foo.NOT_EXISTING'); like(http_get('/has?dict=waka&key=FOO'), qr/true/, 'has waka.FOO'); $t->reload(); like(http_get('/keys?dict=foo'), qr/FOO\,FOO2\,FOO3/, 'foo keys'); like(http_get('/keys?dict=foo&max=2'), qr/FOO\,FOO3/, 'foo keys max 2'); like(http_get('/size?dict=foo'), qr/size: 3/, 'no of items in foo'); like(http_get('/get?dict=foo&key=FOO2'), qr/yyy/, 'get foo.FOO2'); like(http_get('/get?dict=bar&key=FOO'), qr/zzz/, 'get bar.FOO'); like(http_get('/get?dict=foo&key=FOO'), qr/xxx/, 'get foo.FOO'); like(http_get('/get?dict=waka&key=FOO'), qr/47/, 'get waka.FOO'); like(http_get('/delete?dict=foo&key=FOO'), qr/true/, 'delete foo.FOO'); like(http_get('/get?dict=foo&key=FOO'), qr/undefined/, 'get foo.FOO'); like(http_get('/get?dict=foo&key=FOO3'), qr/empty/, 'get foo.FOO3'); like(http_get('/replace?dict=foo&key=FOO2&value=aaa'), qr/true/, 'replace foo.FOO2'); like(http_get('/replace?dict=foo&key=NOT_EXISTING&value=aaa'), qr/false/, 'failed replace foo.NOT_EXISTING'); select undef, undef, undef, 2.1; like(http_get('/get?dict=foo&key=FOO'), qr/undefined/, 'get expired foo.FOO'); like(http_get('/pop?dict=foo&key=FOO'), qr/undefined/, 'pop expired foo.FOO'); TODO: { local $TODO = 'not yet' unless has_version('0.8.1'); like(http_get('/keys?dict=foo'), qr/empty/, 'foo keys after expire'); like(http_get('/keys?dict=bar'), qr/FOO\,FOO2/, 'bar keys after a delay'); like(http_get('/size?dict=foo'), qr/size: 0/, 'no of items in foo after expire'); like(http_get('/items?dict=bar'), qr/FOO,zzz|FOO2,aaa/, 'bar items'); like(http_get('/items?dict=waka'), qr/FOO,47|FOO2,7779|FOO3,3338/, 'waka items'); } TODO: { local $TODO = 'not yet' unless has_version('0.8.5'); http_get('/clear?dict=waka'); like(http_get('/set?dict=waka&key=BAR&value=1&timeout=1'), qr/true/, 'set waka.BAR'); like(http_get('/add?dict=waka&key=BAR2&value=1&timeout=1'), qr/true/, 'add waka.BAR2'); like(http_get('/incr?dict=waka&key=BAR3&by=42&timeout=1'), qr/42/, 'incr waka.BAR3'); like(http_get('/set?dict=waka&key=FOO&value=42&timeout=1000'), qr/true/, 'set waka.FOO'); like(http_get('/add?dict=waka&key=FOO2&value=42&timeout=1000'), qr/true/, 'add waka.FOO2'); like(http_get('/incr?dict=waka&key=FOO3&by=42&timeout=1000'), qr/42/, 'incr waka.FOO3'); like(http_get('/keys?dict=waka'), qr/FOO\,FOO2\,FOO3/, 'waka keys'); } like(http_get('/pop?dict=bar&key=FOO'), qr/zzz/, 'pop bar.FOO'); like(http_get('/pop?dict=bar&key=FOO'), qr/undefined/, 'pop deleted bar.FOO'); http_get('/set?dict=foo&key=BAR&value=xxx'); like(http_get('/clear?dict=foo'), qr/undefined/, 'clear foo'); like(http_get('/size?dict=foo'), qr/size: 0/, 'no of items in foo after clear'); like(http_get('/set_clear'), qr/size: 0/, 'no of items in no_timeout after clear'); TODO: { local $TODO = 'not yet' unless has_version('0.8.8'); like(http_get('/overflow'), qr/SharedMemoryError/, 'overflow exception'); } ############################################################################### sub has_version { my $need = shift; http_get('/njs') =~ /^([.0-9]+)$/m; my @v = split(/\./, $1); my ($n, $v); for $n (split(/\./, $need)) { $v = shift @v || 0; return 0 if $n > $v; return 1 if $v > $n; } return 1; } ############################################################################### njs-0.8.9/nginx/t/js_subrequests.t000066400000000000000000000425031474132077100171700ustar00rootroot00000000000000#!/usr/bin/perl # # (C) Dmitry Volyntsev. # (C) Nginx, Inc. # Tests for subrequests in http njs module. ############################################################################### use warnings; use strict; use Test::More; use Socket qw/ CRLF /; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; eval { require JSON::PP; }; plan(skip_all => "JSON::PP not installed") if $@; my $t = Test::Nginx->new()->has(qw/http rewrite proxy cache/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% proxy_cache_path %%TESTDIR%%/cache1 keys_zone=ON:1m use_temp_path=on; js_import test.js; js_set $async_var test.async_var; js_set $subrequest_var test.subrequest_var; server { listen 127.0.0.1:8080; server_name localhost; location /njs { js_content test.njs; } location /sr { js_content test.sr; } location /sr_pr { js_content test.sr_pr; } location /sr_args { js_content test.sr_args; } location /sr_options_args { js_content test.sr_options_args; } location /sr_options_args_pr { js_content test.sr_options_args_pr; } location /sr_options_method { js_content test.sr_options_method; } location /sr_options_method_pr { js_content test.sr_options_method_pr; } location /sr_options_body { js_content test.sr_options_body; } location /sr_options_method_head { js_content test.sr_options_method_head; } location /sr_body { js_content test.sr_body; } location /sr_body_pr { js_content test.sr_body_pr; } location /sr_body_special { js_content test.sr_body_special; } location /sr_in_variable_handler { set $_ $async_var; js_content test.sr_in_variable_handler; } location /sr_detached_in_variable_handler { return 200 $subrequest_var; } location /sr_async_var { set $_ $async_var; error_page 404 /return; return 404; } location /sr_error_page { js_content test.sr_error_page; } location /sr_js_in_subrequest { js_content test.sr_js_in_subrequest; } location /sr_js_in_subrequest_pr { js_content test.sr_js_in_subrequest_pr; } location /sr_file { js_content test.sr_file; } location /sr_cache { js_content test.sr_cache; } location /sr_limit { sendfile_max_chunk 5; js_content test.sr_limit; } location /sr_unavail { js_content test.sr_unavail; } location /sr_unavail_pr { js_content test.sr_unavail_pr; } location /sr_unsafe { js_content test.sr_unsafe; } location /sr_broken { js_content test.sr_broken; } location /sr_too_large { js_content test.sr_too_large; } location /sr_out_of_order { js_content test.sr_out_of_order; } location /sr_except_not_a_func { js_content test.sr_except_not_a_func; } location /sr_except_failed_to_convert_options_arg { js_content test.sr_except_failed_to_convert_options_arg; } location /sr_except_invalid_options_header_only { js_content test.sr_except_invalid_options_header_only; } location /sr_in_sr_callback { js_content test.sr_in_sr_callback; } location /sr_error_in_callback { js_content test.sr_error_in_callback; } location /sr_uri_except { js_content test.sr_uri_except; } location /file/ { alias %%TESTDIR%%/; } location /p/ { proxy_cache $arg_c; proxy_pass http://127.0.0.1:8081/; } location /daemon/ { proxy_pass http://127.0.0.1:8082/; } location /too_large/ { subrequest_output_buffer_size 3; proxy_pass http://127.0.0.1:8081/; } location /sr_in_sr { js_content test.sr_in_sr; } location /unavail { proxy_pass http://127.0.0.1:8084/; } location /sr_parent { js_content test.sr_parent; } location /js_sub { js_content test.js_sub; } location /return { return 200 '["$request_method"]'; } location /error_page_404 { return 404; error_page 404 /404.html; } } server { listen 127.0.0.1:8081; server_name localhost; location /sub1 { add_header H $arg_h; return 206 '{"a": {"b": 1}}'; } location /sub2 { return 404 '{"e": "msg"}'; } location /method { return 200 '["$request_method"]'; } location /body { js_content test.body; } location /detached { js_content test.detached; } location /delayed { js_content test.delayed; } } server { listen 127.0.0.1:8084; server_name localhost; return 444; } } EOF $t->write_file('test.js', < r.return(200, JSON.stringify({h:reply.headersOut.h}))) } function sr_args(r) { r.subrequest('/p/sub1', 'h=xxx', reply => { r.return(200, JSON.stringify({h:reply.headersOut.h})); }); } function sr_options_args(r) { r.subrequest('/p/sub1', {args:'h=xxx'}, reply => { r.return(200, JSON.stringify({h:reply.headersOut.h})); }); } function sr_options_args_pr(r) { r.subrequest('/p/sub1', {args:'h=xxx'}) .then(reply => r.return(200, JSON.stringify({h:reply.headersOut.h}))) } function sr_options_method(r) { r.subrequest('/p/method', {method:r.args.m}, body_fwd_cb); } function sr_options_method_pr(r) { r.subrequest('/p/method', {method:r.args.m}) .then(body_fwd_cb); } function sr_options_body(r) { r.subrequest('/p/body', {method:'POST', body:'["REQ-BODY"]'}, body_fwd_cb); } function sr_options_method_head(r) { r.subrequest('/p/method', {method:'HEAD'}, reply => { r.return(200, JSON.stringify({c:reply.status})); }); } function sr_body(r) { r.subrequest('/p/sub1', body_fwd_cb); } function sr_body_pr(r) { r.subrequest('/p/sub1') .then(body_fwd_cb); } function sr_body_special(r) { r.subrequest('/p/sub2', body_fwd_cb); } function body(r) { r.return(200, r.variables.request_body); } function delayed(r) { setTimeout(r => r.return(200), 100, r); } function detached(r) { var method = r.variables.request_method; r.log(`DETACHED: \${method} args: \${r.variables.args}`); r.return(200); } function sr_in_variable_handler(r) { } function async_var(r) { r.subrequest('/p/delayed', reply => { r.return(200, JSON.stringify(["CB-VAR"])); }); return ""; } function sr_error_page(r) { r.subrequest('/error_page_404') .then(reply => {r.return(200, `reply.status:\${reply.status}`)}); } function subrequest_var(r) { r.subrequest('/p/detached', {detached:true}); r.subrequest('/p/detached', {detached:true, args:'a=yyy', method:'POST'}); return "subrequest_var"; } function sr_file(r) { r.subrequest('/file/t', body_fwd_cb); } function sr_cache(r) { r.subrequest('/p/t', body_fwd_cb); } function sr_limit(r) { r.subrequest('/file/t', function (reply) { r.return(200, "x".repeat(100)); }); } function sr_unavail(req) { subrequest_fn(req, ['/unavail'], ['status']); } function sr_unavail_pr(req) { subrequest_fn_pr(req, ['/unavail'], ['status']); } function sr_unsafe(r) { r.subrequest('../dir'); r.return(200); } function sr_broken(r) { r.subrequest('/daemon/unfinished', reply => { r.return(200, JSON.stringify({code:reply.status})); }); } function sr_too_large(r) { r.subrequest('/too_large/t', body_fwd_cb); } function sr_in_sr(r) { r.subrequest('/sr', body_fwd_cb); } function sr_js_in_subrequest(r) { r.subrequest('/js_sub', body_fwd_cb); } function sr_js_in_subrequest_pr(r) { r.subrequest('/js_sub') .then(body_fwd_cb); } function sr_error_in_callback(r) { r.subrequest("/sub1", () => {}); r.subrequest("/sub1", () => { throw "Oops!"; }); r.return(200); } function sr_in_sr_callback(r) { r.subrequest('/return', function (reply) { try { reply.subrequest('/return'); } catch (err) { r.return(200, JSON.stringify({e:err.message})); return; } r.return(200); }); } function sr_parent(r) { try { var parent = r.parent; } catch (err) { r.return(200, JSON.stringify({e:err.message})); return; } r.return(200); } function sr_out_of_order(r) { subrequest_fn(r, ['/p/delayed', '/p/sub1', '/unknown'], ['status']); } function collect(replies, props, total, reply) { reply.log(`subrequest handler: \${reply.uri} status: \${reply.status}`) var rep = {}; props.push('uri'); props.forEach(p => {rep[p] = reply[p]}); replies.push(rep); if (replies.length == total) { replies.sort((a, b) => a.uri < b.uri ? -1 : 1); reply.parent.return(200, JSON.stringify(replies)); } } function subrequest_fn(r, subs, props) { var replies = []; subs.forEach(sr => r.subrequest(sr, collect.bind(null, replies, props, subs.length))); } function subrequest_fn_pr(r, subs, props) { var replies = []; subs.forEach(sr => r.subrequest(sr) .then(collect.bind(null, replies, props, subs.length))); } function sr_except_not_a_func(r) { r.subrequest('/sub1', 'a=1', 'b'); } let Failed = {get toConvert() { return {toString(){return {};}}}}; function sr_except_failed_to_convert_options_arg(r) { r.subrequest('/sub1', {args:Failed.toConvert}, ()=>{}); } function sr_uri_except(r) { r.subrequest(Failed.toConvert, 'a=1', 'b'); } function body_fwd_cb(r) { r.parent.return(200, JSON.stringify(JSON.parse(r.responseText))); } function js_sub(r) { r.return(200, '["JS-SUB"]'); } export default {njs:test_njs, sr, sr_pr, sr_args, sr_options_args, sr_options_args_pr, sr_options_method, sr_options_method_pr, sr_options_method_head, sr_options_body, sr_body, sr_body_pr, sr_body_special, body, delayed, detached, sr_in_variable_handler, async_var, sr_error_page, subrequest_var, sr_file, sr_cache, sr_unavail, sr_parent, sr_unavail_pr, sr_broken, sr_too_large, sr_in_sr, sr_js_in_subrequest, sr_js_in_subrequest_pr, js_sub, sr_in_sr_callback, sr_out_of_order, sr_except_not_a_func, sr_uri_except, sr_except_failed_to_convert_options_arg, sr_unsafe, sr_error_in_callback, sr_limit}; EOF $t->write_file('t', '["SEE-THIS"]'); $t->try_run('no njs available')->plan(34); $t->run_daemon(\&http_daemon); ############################################################################### is(get_json('/sr'), '[{"status":404,"uri":"/p/sub2"}]', 'sr'); is(get_json('/sr_args'), '{"h":"xxx"}', 'sr_args'); is(get_json('/sr_options_args'), '{"h":"xxx"}', 'sr_options_args'); is(get_json('/sr_options_method?m=POST'), '["POST"]', 'sr method POST'); is(get_json('/sr_options_method?m=PURGE'), '["PURGE"]', 'sr method PURGE'); is(get_json('/sr_options_body'), '["REQ-BODY"]', 'sr_options_body'); is(get_json('/sr_options_method_head'), '{"c":200}', 'sr_options_method_head'); is(get_json('/sr_body'), '{"a":{"b":1}}', 'sr_body'); is(get_json('/sr_body_special'), '{"e":"msg"}', 'sr_body_special'); is(get_json('/sr_in_variable_handler'), '["CB-VAR"]', 'sr_in_variable_handler'); is(get_json('/sr_file'), '["SEE-THIS"]', 'sr_file'); is(get_json('/sr_cache?c=1'), '["SEE-THIS"]', 'sr_cache'); is(get_json('/sr_cache?c=1'), '["SEE-THIS"]', 'sr_cached'); is(get_json('/sr_js_in_subrequest'), '["JS-SUB"]', 'sr_js_in_subrequest'); is(get_json('/sr_unavail'), '[{"status":502,"uri":"/unavail"}]', 'sr_unavail'); is(get_json('/sr_out_of_order'), '[{"status":200,"uri":"/p/delayed"},' . '{"status":206,"uri":"/p/sub1"},' . '{"status":404,"uri":"/unknown"}]', 'sr_multi'); is(get_json('/sr_pr'), '{"h":"xxx"}', 'sr_promise'); is(get_json('/sr_options_args_pr'), '{"h":"xxx"}', 'sr_options_args_pr'); is(get_json('/sr_options_method_pr?m=PUT'), '["PUT"]', 'sr method PUT'); is(get_json('/sr_body_pr'), '{"a":{"b":1}}', 'sr_body_pr'); is(get_json('/sr_js_in_subrequest_pr'), '["JS-SUB"]', 'sr_js_in_subrequest_pr'); is(get_json('/sr_unavail_pr'), '[{"status":502,"uri":"/unavail"}]', 'sr_unavail_pr'); like(http_get('/sr_detached_in_variable_handler'), qr/subrequest_var/, 'sr_detached_in_variable_handler'); like(http_get('/sr_error_page'), qr/reply\.status:404/, 'sr_error_page'); http_get('/sr_broken'); http_get('/sr_in_sr'); http_get('/sr_in_variable_handler'); http_get('/sr_async_var'); http_get('/sr_too_large'); http_get('/sr_except_not_a_func'); http_get('/sr_except_failed_to_convert_options_arg'); http_get('/sr_uri_except'); is(get_json('/sr_in_sr_callback'), '{"e":"subrequest can only be created for the primary request"}', 'subrequest for non-primary request'); TODO: { local $TODO = 'not yet' unless has_version('0.8.4'); like(http_get('/sr_unsafe'), qr/500/s, 'unsafe subrequest uri'); } TODO: { local $TODO = 'not yet' unless has_version('0.8.5'); http_get('/sr_error_in_callback'); ok(index($t->read_file('error.log'), 'subrequest can only be created for') > 0, 'subrequest creation failed'); } TODO: { local $TODO = 'not yet' unless has_version('0.8.8'); like(http_get('/sr_limit'), qr/x{100}/, 'sr_limit'); } $t->stop(); ok(index($t->read_file('error.log'), 'callback is not a function') > 0, 'subrequest cb exception'); ok(index($t->read_file('error.log'), 'failed to convert uri arg') > 0, 'subrequest uri exception'); ok(index($t->read_file('error.log'), 'failed to convert options.args') > 0, 'subrequest invalid args exception'); ok(index($t->read_file('error.log'), 'too big subrequest response') > 0, 'subrequest too large body'); ok(index($t->read_file('error.log'), 'js subrequest: failed to get the parent context') > 0, 'zero parent ctx'); ok(index($t->read_file('error.log'), 'DETACHED') > 0, 'detached subrequest'); ############################################################################### sub recode { my $json; eval { $json = JSON::PP::decode_json(shift) }; if ($@) { return ""; } JSON::PP->new()->canonical()->encode($json); } sub get_json { http_get(shift) =~ /\x0d\x0a?\x0d\x0a?(.*)/ms; recode($1); } ############################################################################### sub http_daemon { my $server = IO::Socket::INET->new( Proto => 'tcp', LocalAddr => '127.0.0.1:' . port(8082), Listen => 5, Reuse => 1 ) or die "Can't create listening socket: $!\n"; local $SIG{PIPE} = 'IGNORE'; while (my $client = $server->accept()) { $client->autoflush(1); my $headers = ''; my $uri = ''; while (<$client>) { $headers .= $_; last if (/^\x0d?\x0a?$/); } $uri = $1 if $headers =~ /^\S+\s+([^ ]+)\s+HTTP/i; if ($uri eq '/unfinished') { print $client "HTTP/1.1 200 OK" . CRLF . "Transfer-Encoding: chunked" . CRLF . "Content-Length: 100" . CRLF . CRLF . "unfinished" . CRLF; close($client); } } } ############################################################################### sub has_version { my $need = shift; http_get('/njs') =~ /^([.0-9]+)$/m; my @v = split(/\./, $1); my ($n, $v); for $n (split(/\./, $need)) { $v = shift @v || 0; return 0 if $n > $v; return 1 if $v > $n; } return 1; } ############################################################################### njs-0.8.9/nginx/t/js_var.t000066400000000000000000000034011474132077100153650ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for http njs module, js_var directive. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http rewrite/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; js_var $foo; js_var $bar a:$arg_a; server { listen 127.0.0.1:8080; server_name localhost; location /test { js_content test.test; } location /sub { return 200 DONE; } location /dest { return 200 $bar; } } } EOF $t->write_file('test.js', < { r.variables.bar = reply.responseText; r.internalRedirect('/dest'); }); return; } r.return(200, `V:\${r.variables[r.args.var]}`); } export default {test}; EOF $t->try_run('no njs js_var')->plan(3); ############################################################################### like(http_get('/test?var=bar&a=qq'), qr/200 OK.*V:a:qq$/s, 'default value'); like(http_get('/test?var=foo'), qr/200 OK.*V:$/s, 'default empty value'); like(http_get('/test?sub=1&var=bar&a=qq'), qr/200 OK.*DONE$/s, 'value set'); ############################################################################### njs-0.8.9/nginx/t/js_var2.t000066400000000000000000000034531474132077100154560ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for http njs module, js_var directive in server | location contexts. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http rewrite/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; js_var $foo; location /test { js_content test.test; } location /sub { return 200 DONE; } location /dest { js_var $bar a:$arg_a; return 200 $bar; } } } EOF $t->write_file('test.js', < { r.variables.bar = reply.responseText; r.internalRedirect('/dest'); }); return; } r.return(200, `V:\${r.variables[r.args.var]}`); } export default {test}; EOF $t->try_run('no njs js_var')->plan(3); ############################################################################### like(http_get('/test?var=bar&a=qq'), qr/200 OK.*V:a:qq$/s, 'default value'); like(http_get('/test?var=foo'), qr/200 OK.*V:$/s, 'default empty value'); like(http_get('/test?sub=1&var=bar&a=qq'), qr/200 OK.*DONE$/s, 'value set'); ############################################################################### njs-0.8.9/nginx/t/js_variables.t000066400000000000000000000036311474132077100165520ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for http njs module, setting nginx variables. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http rewrite/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_set $test_var test.variable; js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; set $foo test.foo_orig; location /var_set { return 200 $test_var$foo; } location /content_set { js_content test.content_set; } location /not_found_set { js_content test.not_found_set; } } } EOF $t->write_file('test.js', <try_run('no njs')->plan(3); ############################################################################### like(http_get('/var_set?a=bar'), qr/test_varbar/, 'var set'); like(http_get('/content_set?a=bar'), qr/bar/, 'content set'); like(http_get('/not_found_set'), qr/variable not found/, 'not found exception'); ############################################################################### njs-0.8.9/nginx/t/js_variables_nocache.t000066400000000000000000000044511474132077100202330ustar00rootroot00000000000000#!/usr/bin/perl # (C) Thomas P. # Tests for http njs module, setting non-cacheable nginx variables. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http rewrite/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_set $nocache_var test.variable nocache; js_set $default_var test.variable; js_set $callcount_var test.callCount nocache; js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /default_var { set $a $default_var; set $b $default_var; return 200 '"$a/$b"'; } location /nocache_var { set $a $nocache_var; set $b $nocache_var; return 200 '"$a/$b"'; } location /callcount_var { set $a $callcount_var; set $b $callcount_var; return 200 '"$a/$b"'; } } } EOF $t->write_file('test.js', <try_run('no nocache njs variables')->plan(3); ############################################################################### # We use backreferences to make sure the same value was returned for the two uses like(http_get('/default_var'), qr/"(.+)\/\1"/, 'cached variable'); # Negative lookaheads don't capture, hence the .+ after it like(http_get('/nocache_var'), qr/"(.+)\/(?!\1).+"/, 'noncacheable variable'); TODO: { local $TODO = "Needs upstream nginx patch https://mailman.nginx.org/pipermail/nginx-devel/2024-August/N7VFIYUKSZFUIAO24OJODKQGTP63R5NV.html"; # Without the patch, this will give 2/4 (calls are duplicated) like(http_get('/callcount_var'), qr/"1\/2"/, 'callcount variable'); } ############################################################################### njs-0.8.9/nginx/t/stream_js.t000066400000000000000000000273611474132077100161030ustar00rootroot00000000000000#!/usr/bin/perl # (C) Andrey Zelenkov # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for stream njs module. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; use Test::Nginx::Stream qw/ dgram stream /; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http proxy rewrite stream stream_return udp/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8079; server_name localhost; location /njs { js_content test.njs; } location /p/ { proxy_pass http://127.0.0.1:8095/; } location /return { return 200 $http_foo; } } } stream { %%TEST_GLOBALS_STREAM%% js_set $js_addr test.addr; js_set $js_var test.variable; js_set $js_log test.log; js_set $js_unk test.unk; js_set $js_req_line test.req_line; js_set $js_sess_unk test.sess_unk; js_set $js_async test.asyncf; js_set $js_buffer test.buffer; js_import test.js; log_format status $server_port:$status; server { listen 127.0.0.1:8080; return $js_addr; } server { listen 127.0.0.1:8081; return $js_log; } server { listen 127.0.0.1:8082; return $js_var; } server { listen 127.0.0.1:8083; return $js_unk; } server { listen 127.0.0.1:8084; return $js_sess_unk; } server { listen 127.0.0.1:%%PORT_8985_UDP%% udp; return $js_addr; } server { listen 127.0.0.1:8086; js_access test.access_step; js_preread test.preread_step; js_filter test.filter_step; proxy_pass 127.0.0.1:8090; } server { listen 127.0.0.1:8087; js_access test.access_undecided; return OK; access_log %%TESTDIR%%/status.log status; } server { listen 127.0.0.1:8088; js_access test.access_allow; return OK; access_log %%TESTDIR%%/status.log status; } server { listen 127.0.0.1:8089; js_access test.access_deny; return OK; access_log %%TESTDIR%%/status.log status; } server { listen 127.0.0.1:8091; js_preread test.preread_async; proxy_pass 127.0.0.1:8090; } server { listen 127.0.0.1:8092; js_preread test.preread_data; proxy_pass 127.0.0.1:8090; } server { listen 127.0.0.1:8093; js_preread test.preread_req_line; return $js_req_line; } server { listen 127.0.0.1:8094; js_filter test.filter_empty; proxy_pass 127.0.0.1:8090; } server { listen 127.0.0.1:8095; js_filter test.filter_header_inject; proxy_pass 127.0.0.1:8079; } server { listen 127.0.0.1:8096; js_filter test.filter_search; proxy_pass 127.0.0.1:8090; } server { listen 127.0.0.1:8097; js_access test.access_except; proxy_pass 127.0.0.1:8090; } server { listen 127.0.0.1:8098; js_preread test.preread_except; proxy_pass 127.0.0.1:8090; } server { listen 127.0.0.1:8099; js_filter test.filter_except; proxy_pass 127.0.0.1:8090; } server { listen 127.0.0.1:8100; return $js_async; } server { listen 127.0.0.1:8101; return $js_buffer; } } EOF $t->write_file('test.js', < { if (!res || res[res.length - 1] != v) res += v }; function access_step(s) { step(1); setTimeout(function() { if (s.remoteAddress.match('127.0.0.1')) { s.allow(); } }, 1); } function preread_step(s) { s.on('upload', function (data) { step(2); if (data.length > 0) { s.done(); } }); } function filter_step(s) { s.on('upload', function(data, flags) { step(3); s.send(data); }); s.on('download', function(data, flags) { if (!flags.last) { step(4); s.send(data); } else { step(5); s.send(res, {last:1}); s.off('download'); } }); } function access_undecided(s) { s.decline(); } function access_allow(s) { if (s.remoteAddress.match('127.0.0.1')) { s.done(); return; } s.deny(); } function access_deny(s) { if (s.remoteAddress.match('127.0.0.1')) { s.deny(); return; } s.allow(); } function preread_async(s) { setTimeout(function() { s.done(); }, 1); } function preread_data(s) { s.on('upload', function (data, flags) { if (data.indexOf('z') != -1) { s.done(); } }); } var line = ''; function preread_req_line(s) { s.on('upload', function (data, flags) { var n = data.indexOf('\\n'); if (n != -1) { line = data.substr(0, n); s.done(); } }); } function req_line(s) { return line; } function filter_empty(s) { } function filter_header_inject(s) { var req = ''; s.on('upload', function(data, flags) { req += data; var n = req.search('\\n'); if (n != -1) { var rest = req.substr(n + 1); req = req.substr(0, n + 1); s.send(req + 'Foo: foo' + '\\r\\n' + rest, flags); s.off('upload'); } }); } function filter_search(s) { s.on('download', function(data, flags) { var n = data.search('y'); if (n != -1) { s.send('z'); } }); s.on('upload', function(data, flags) { var n = data.search('x'); if (n != -1) { s.send('y'); } }); } function access_except(s) { function done() {return s.a.a}; setTimeout(done, 1); setTimeout(done, 2); } function preread_except(s) { decodeURI("%E0"); } function filter_except(s) { s.on('unknown', function() {}); } function pr(x) { return new Promise(resolve => {resolve(x)}).then(v => v).then(v => v); } async function asyncf(s) { const a1 = await pr(10); const a2 = await pr(20); s.setReturnValue(`retval: \${a1 + a2}`); } export default {njs:test_njs, addr, variable, sess_unk, log, access_step, preread_step, filter_step, access_undecided, access_allow, access_deny, preread_async, preread_data, preread_req_line, req_line, filter_empty, filter_header_inject, filter_search, access_except, preread_except, filter_except, asyncf, buffer}; EOF $t->run_daemon(\&stream_daemon, port(8090)); $t->try_run('no stream njs available')->plan(25); $t->waitforsocket('127.0.0.1:' . port(8090)); ############################################################################### is(stream('127.0.0.1:' . port(8080))->read(), 'addr=127.0.0.1', 's.remoteAddress'); is(dgram('127.0.0.1:' . port(8985))->io('.'), 'addr=127.0.0.1', 's.remoteAddress udp'); is(stream('127.0.0.1:' . port(8081))->read(), 'log', 's.log'); is(stream('127.0.0.1:' . port(8082))->read(), 'variable=127.0.0.1', 's.variables'); is(stream('127.0.0.1:' . port(8083))->read(), '', 'stream js unknown function'); is(stream('127.0.0.1:' . port(8084))->read(), 'sess_unk=undefined', 's.unk'); is(stream('127.0.0.1:' . port(8086))->io('0'), '012345', 'async handlers order'); is(stream('127.0.0.1:' . port(8087))->io('#'), 'OK', 'access_undecided'); is(stream('127.0.0.1:' . port(8088))->io('#'), 'OK', 'access_allow'); is(stream('127.0.0.1:' . port(8089))->io('#'), '', 'access_deny'); is(stream('127.0.0.1:' . port(8091))->io('#'), '#', 'preread_async'); is(stream('127.0.0.1:' . port(8092))->io('#z'), '#z', 'preread_async_data'); is(stream('127.0.0.1:' . port(8093))->io("xy\na"), 'xy', 'preread_req_line'); is(stream('127.0.0.1:' . port(8094))->io('x'), 'x', 'filter_empty'); like(get('/p/return'), qr/foo/, 'filter_injected_header'); is(stream('127.0.0.1:' . port(8096))->io('x'), 'z', 'filter_search'); stream('127.0.0.1:' . port(8097))->io('x'); stream('127.0.0.1:' . port(8098))->io('x'); stream('127.0.0.1:' . port(8099))->io('x'); is(stream('127.0.0.1:' . port(8100))->read(), 'retval: 30', 'asyncf'); TODO: { local $TODO = 'not yet' unless has_version('0.8.3'); like(stream('127.0.0.1:' . port(8101))->read(), qr/\xaa\xbb\xcc\xdd/, 'buffer variable'); } $t->stop(); ok(index($t->read_file('error.log'), 'SEE-THIS') > 0, 'stream js log'); ok(index($t->read_file('error.log'), 'at decodeURI') > 0, 'stream js_preread backtrace'); ok(index($t->read_file('error.log'), 'at filter_except') > 0, 'stream js_filter backtrace'); my @p = (port(8087), port(8088), port(8089)); like($t->read_file('status.log'), qr/$p[0]:200/, 'status undecided'); like($t->read_file('status.log'), qr/$p[1]:200/, 'status allow'); like($t->read_file('status.log'), qr/$p[2]:403/, 'status deny'); my $content = $t->read_file('error.log'); my $count = () = $content =~ m/ js vm init/g; ok($count == 2, 'http and stream js blocks imported once each'); ############################################################################### sub has_version { my $need = shift; get('/njs') =~ /^([.0-9]+)$/m; my @v = split(/\./, $1); my ($n, $v); for $n (split(/\./, $need)) { $v = shift @v || 0; return 0 if $n > $v; return 1 if $v > $n; } return 1; } ############################################################################### sub stream_daemon { my $server = IO::Socket::INET->new( Proto => 'tcp', LocalAddr => '127.0.0.1:' . port(8090), Listen => 5, Reuse => 1 ) or die "Can't create listening socket: $!\n"; local $SIG{PIPE} = 'IGNORE'; while (my $client = $server->accept()) { $client->autoflush(1); log2c("(new connection $client)"); $client->sysread(my $buffer, 65536) or next; log2i("$client $buffer"); log2o("$client $buffer"); $client->syswrite($buffer); close $client; } } sub log2i { Test::Nginx::log_core('|| <<', @_); } sub log2o { Test::Nginx::log_core('|| >>', @_); } sub log2c { Test::Nginx::log_core('||', @_); } sub get { my ($url, %extra) = @_; my $s = IO::Socket::INET->new( Proto => 'tcp', PeerAddr => '127.0.0.1:' . port(8079) ) or die "Can't connect to nginx: $!\n"; return http_get($url, socket => $s); } ############################################################################### njs-0.8.9/nginx/t/stream_js_buffer.t000066400000000000000000000075071474132077100174340ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for stream njs module, buffer properties. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; use Test::Nginx::Stream qw/ stream /; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http proxy rewrite stream stream_return/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /njs { js_content test.njs; } location /p/ { proxy_pass http://127.0.0.1:8085/; } location /return { return 200 'RETURN:$http_foo'; } } } stream { %%TEST_GLOBALS_STREAM%% js_import test.js; js_set $type test.type; js_set $binary_var test.binary_var; server { listen 127.0.0.1:8081; return $type; } server { listen 127.0.0.1:8082; return $binary_var; } server { listen 127.0.0.1:8083; js_preread test.cb_mismatch; proxy_pass 127.0.0.1:8090; } server { listen 127.0.0.1:8084; js_preread test.cb_mismatch2; proxy_pass 127.0.0.1:8090; } server { listen 127.0.0.1:8085; js_filter test.header_inject; proxy_pass 127.0.0.1:8080; } } EOF $t->write_file('test.js', < {}); s.on('downstream', () => {}); } catch (e) { throw new Error(`cb_mismatch:\${e.message}`) } } function cb_mismatch2(s) { try { s.on('upstream', () => {}); s.on('download', () => {}); } catch (e) { throw new Error(`cb_mismatch2:\${e.message}`) } } function header_inject(s) { var req = Buffer.from([]); s.on('upstream', function(data, flags) { req = Buffer.concat([req, data]); var n = req.indexOf('\\n'); if (n != -1) { var rest = req.slice(n + 1); req = req.slice(0, n + 1); s.send(req, flags); s.send('Foo: foo\\r\\n', flags); s.send(rest, flags); s.off('upstream'); } }); } export default {njs: test_njs, type, binary_var, cb_mismatch, cb_mismatch2, header_inject}; EOF $t->try_run('no njs ngx')->plan(5); ############################################################################### is(stream('127.0.0.1:' . port(8081))->read(), 'buffer', 'var type'); is(stream('127.0.0.1:' . port(8082))->read(), 'true', 'binary var'); stream('127.0.0.1:' . port(8083))->io('x'); stream('127.0.0.1:' . port(8084))->io('x'); like(http_get('/p/return'), qr/RETURN:foo/, 'injected header'); $t->stop(); ok(index($t->read_file('error.log'), 'cb_mismatch:mixing string and buffer') > 0, 'cb mismatch'); ok(index($t->read_file('error.log'), 'cb_mismatch2:mixing string and buffer') > 0, 'cb mismatch'); ############################################################################### njs-0.8.9/nginx/t/stream_js_console.t000066400000000000000000000067071474132077100176260ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for stream njs module, console object. ############################################################################### use warnings; use strict; use Test::More; use Socket qw/ CRLF /; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; use Test::Nginx::Stream qw/ stream /; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/stream/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /engine { js_content test.engine; } } } stream { %%TEST_GLOBALS_STREAM%% js_import test.js; server { listen 127.0.0.1:8081; js_preread test.log; proxy_pass 127.0.0.1:8090; } server { listen 127.0.0.1:8082; js_preread test.timer; proxy_pass 127.0.0.1:8090; } } EOF $t->write_file('test.js', < 0) { s.off('upload'); data = Buffer.from(data, 'base64'); const object = JSON.parse(data); console.log(object); s.allow(); } }); } function timer(s) { s.on('upload', function (data) { if (data.length > 0) { s.off('upload'); console.time('foo'); setTimeout(function() { console.timeEnd('foo'); s.allow(); }, 7); } }); } export default { engine, log, timer }; EOF $t->run_daemon(\&stream_daemon, port(8090)); $t->try_run('no njs console')->plan(4); $t->waitforsocket('127.0.0.1:' . port(8090)); ############################################################################### my $engine = http_get('/engine'); is(stream('127.0.0.1:' . port(8081))->io('eyJhIjpbIkIiLCJDIl19'), 'eyJhIjpbIkIiLCJDIl19', 'log test'); is(stream('127.0.0.1:' . port(8082))->io('timer'), 'timer', 'timer test'); $t->stop(); SKIP: { skip "QuickJS has no console.dump() method.", 1 if $engine =~ /QuickJS$/m; like($t->read_file('error.log'), qr/\[info\].*js: \{a:\['B','C'\]\}/, 'console.log with object'); } like($t->read_file('error.log'), qr/\[info\].*js: foo: \d+\.\d\d\d\d\d\dms/, 'console.time foo'); ############################################################################### sub stream_daemon { my $server = IO::Socket::INET->new( Proto => 'tcp', LocalAddr => '127.0.0.1:' . port(8090), Listen => 5, Reuse => 1 ) or die "Can't create listening socket: $!\n"; local $SIG{PIPE} = 'IGNORE'; while (my $client = $server->accept()) { $client->autoflush(1); log2c("(new connection $client)"); $client->sysread(my $buffer, 65536) or next; log2i("$client $buffer"); log2o("$client $buffer"); $client->syswrite($buffer); close $client; } } sub log2i { Test::Nginx::log_core('|| <<', @_); } sub log2o { Test::Nginx::log_core('|| >>', @_); } sub log2c { Test::Nginx::log_core('||', @_); } ############################################################################### njs-0.8.9/nginx/t/stream_js_dup_set.t000066400000000000000000000025731474132077100176240ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for stream njs module, duplicate identical js_set directives. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; use Test::Nginx::Stream qw/ stream /; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/stream stream_return/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } stream { %%TEST_GLOBALS_STREAM%% js_import test.js; server { listen 127.0.0.1:8081; js_set $test test.foo; return 8081:$test; } server { listen 127.0.0.1:8082; js_set $test test.foo; return 8082:$test; } } EOF $t->write_file('test.js', <try_run('no njs available')->plan(2); ############################################################################### is(stream('127.0.0.1:' . port(8081))->read(), '8081:42', '8081 server'); is(stream('127.0.0.1:' . port(8082))->read(), '8082:42', '8082 server'); ############################################################################### njs-0.8.9/nginx/t/stream_js_exit.t000066400000000000000000000065471474132077100171370ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for stream njs module, exit hook. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; use Test::Nginx::Stream qw/ stream /; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http stream/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /njs { js_content test.njs; } location /engine { js_content test.engine; } } } stream { %%TEST_GLOBALS_STREAM%% js_import test.js; server { listen 127.0.0.1:8081; js_access test.access; js_filter test.filter; proxy_pass 127.0.0.1:8090; } server { listen 127.0.0.1:8082; js_access test.access; proxy_pass 127.0.0.1:1; } } EOF $t->write_file('test.js', < { var v = s.variables; var c = `\${v.bytes_received}/\${v.bytes_sent}`; var u = `\${v.upstream_bytes_received}/\${v.upstream_bytes_sent}`; s.error(`s:\${s.status} C: \${c} U: \${u}`); }); s.allow(); } function filter(s) { s.on('upload', (data, flags) => { s.send(`@\${data}`, flags); }); s.on('download', (data, flags) => { s.send(data.slice(2), flags); }); } export default {njs: test_njs, engine, access, filter}; EOF $t->try_run('no stream njs available'); plan(skip_all => 'not yet') if http_get('/engine') =~ /QuickJS$/m; $t->plan(2); $t->run_daemon(\&stream_daemon, port(8090)); $t->waitforsocket('127.0.0.1:' . port(8090)); ############################################################################### stream('127.0.0.1:' . port(8081))->io('###'); stream('127.0.0.1:' . port(8082))->io('###'); $t->stop(); ok(index($t->read_file('error.log'), 's:200 C: 3/6 U: 8/4') > 0, 'normal'); ok(index($t->read_file('error.log'), 's:502 C: 0/0 U: 0/0') > 0, 'failed conn'); ############################################################################### sub stream_daemon { my $server = IO::Socket::INET->new( Proto => 'tcp', LocalAddr => '127.0.0.1:' . port(8090), Listen => 5, Reuse => 1 ) or die "Can't create listening socket: $!\n"; local $SIG{PIPE} = 'IGNORE'; while (my $client = $server->accept()) { $client->autoflush(1); log2c("(new connection $client)"); $client->sysread(my $buffer, 65536) or next; log2i("$client $buffer"); $buffer = $buffer . $buffer; log2o("$client $buffer"); $client->syswrite($buffer); close $client; } } sub log2i { Test::Nginx::log_core('|| <<', @_); } sub log2o { Test::Nginx::log_core('|| >>', @_); } sub log2c { Test::Nginx::log_core('||', @_); } ############################################################################### njs-0.8.9/nginx/t/stream_js_fetch.t000066400000000000000000000145351474132077100172530ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for stream njs module, fetch method. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; use Test::Nginx::Stream qw/ stream /; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http stream/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /njs { js_content test.njs; } location /engine { js_content test.engine; } location /validate { js_content test.validate; } location /success { return 200; } location /fail { return 403; } } } stream { %%TEST_GLOBALS_STREAM%% js_import test.js; server { listen 127.0.0.1:8081; js_preread test.preread_verify; proxy_pass 127.0.0.1:8090; } server { listen 127.0.0.1:8082; js_filter test.filter_verify; proxy_pass 127.0.0.1:8091; } server { listen 127.0.0.1:8083; js_access test.access_ok; proxy_pass 127.0.0.1:8090; } server { listen 127.0.0.1:8084; js_access test.access_nok; proxy_pass 127.0.0.1:8090; } } EOF my $p = port(8080); $t->write_file('test.js', <= 4 && collect.readUInt16BE(0) == 0xabcd) { s.off('upstream'); let reply = await ngx.fetch('http://127.0.0.1:$p/validate', {body: collect.slice(2,4)}); (reply.status == 200) ? s.done(): s.deny(); } else if (collect.length) { s.deny(); } }); } function filter_verify(s) { var collect = Buffer.from([]); s.on('upstream', async function (data, flags) { collect = Buffer.concat([collect, data]); if (collect.length >= 4 && collect.readUInt16BE(0) == 0xabcd) { s.off('upstream'); let reply = await ngx.fetch('http://127.0.0.1:$p/validate', {body: collect.slice(2,4)}); if (reply.status == 200) { s.send(collect.slice(4), flags); } else { s.send("__CLOSE__", flags); } } }); } async function access_ok(s) { let reply = await ngx.fetch('http://127.0.0.1:$p/success'); (reply.status == 200) ? s.allow(): s.deny(); } async function access_nok(s) { let reply = await ngx.fetch('http://127.0.0.1:$p/fail'); (reply.status == 200) ? s.allow(): s.deny(); } export default {njs: test_njs, validate, preread_verify, filter_verify, access_ok, access_nok, engine}; EOF $t->try_run('no stream njs available'); plan(skip_all => 'not yet') if http_get('/engine') =~ /QuickJS$/m; $t->plan(9); $t->run_daemon(\&stream_daemon, port(8090), port(8091)); $t->waitforsocket('127.0.0.1:' . port(8090)); $t->waitforsocket('127.0.0.1:' . port(8091)); ############################################################################### is(stream('127.0.0.1:' . port(8081))->io('###'), '', 'preread not enough'); is(stream('127.0.0.1:' . port(8081))->io("\xAB\xCDQZ##"), "\xAB\xCDQZ##", 'preread validated'); is(stream('127.0.0.1:' . port(8081))->io("\xAC\xCDQZ##"), '', 'preread invalid magic'); is(stream('127.0.0.1:' . port(8081))->io("\xAB\xCDQQ##"), '', 'preread validation failed'); TODO: { todo_skip 'leaves coredump', 3 unless $ENV{TEST_NGINX_UNSAFE} or has_version('0.7.7'); my $s = stream('127.0.0.1:' . port(8082)); is($s->io("\xAB\xCDQZ##", read => 1), '##', 'filter validated'); is($s->io("@@", read => 1), '@@', 'filter off'); is(stream('127.0.0.1:' . port(8082))->io("\xAB\xCDQQ##"), '', 'filter validation failed'); } is(stream('127.0.0.1:' . port(8083))->io('ABC'), 'ABC', 'access fetch ok'); is(stream('127.0.0.1:' . port(8084))->io('ABC'), '', 'access fetch nok'); ############################################################################### sub has_version { my $need = shift; http_get('/njs') =~ /^([.0-9]+)$/m; my @v = split(/\./, $1); my ($n, $v); for $n (split(/\./, $need)) { $v = shift @v || 0; return 0 if $n > $v; return 1 if $v > $n; } return 1; } ############################################################################### sub stream_daemon { my (@ports) = @_; my (@socks, @clients); for my $port (@ports) { my $server = IO::Socket::INET->new( Proto => 'tcp', LocalAddr => "127.0.0.1:$port", Listen => 5, Reuse => 1 ) or die "Can't create listening socket: $!\n"; push @socks, $server; } my $sel = IO::Select->new(@socks); local $SIG{PIPE} = 'IGNORE'; while (my @ready = $sel->can_read) { foreach my $fh (@ready) { if (grep $_ == $fh, @socks) { my $new = $fh->accept; $new->autoflush(1); $sel->add($new); } elsif (stream_handle_client($fh) || $fh->sockport() == port(8090)) { $sel->remove($fh); $fh->close; } } } } sub stream_handle_client { my ($client) = @_; log2c("(new connection $client)"); $client->sysread(my $buffer, 65536) or return 1; log2i("$client $buffer"); if ($buffer eq "__CLOSE__") { return 1; } log2o("$client $buffer"); $client->syswrite($buffer); return 0; } sub log2i { Test::Nginx::log_core('|| <<', @_); } sub log2o { Test::Nginx::log_core('|| >>', @_); } sub log2c { Test::Nginx::log_core('||', @_); } ############################################################################### njs-0.8.9/nginx/t/stream_js_fetch_https.t000066400000000000000000000227101474132077100204670ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for stream njs module, fetch method, https support. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; use Test::Nginx::Stream qw/ stream /; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new() ->has(qw/http http_ssl rewrite stream stream_return socket_ssl/) ->has_daemon('openssl') ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /njs { js_content test.njs; } location /engine { js_content test.engine; } } server { listen 127.0.0.1:8081 ssl default; server_name default.example.com; ssl_certificate default.example.com.chained.crt; ssl_certificate_key default.example.com.key; location /loc { return 200 "You are at default.example.com."; } location /success { return 200; } location /fail { return 403; } location /backend { return 200 "BACKEND OK"; } } server { listen 127.0.0.1:8081 ssl; server_name 1.example.com; ssl_certificate 1.example.com.chained.crt; ssl_certificate_key 1.example.com.key; location /loc { return 200 "You are at 1.example.com."; } } } stream { %%TEST_GLOBALS_STREAM%% js_import test.js; js_var $message; resolver 127.0.0.1:%%PORT_8981_UDP%%; resolver_timeout 1s; server { listen 127.0.0.1:8082; js_preread test.preread; return "default CA $message"; } server { listen 127.0.0.1:8083; js_preread test.preread; return "my CA $message"; js_fetch_ciphers HIGH:!aNull:!MD5; js_fetch_protocols TLSv1.1 TLSv1.2; js_fetch_trusted_certificate myca.crt; } server { listen 127.0.0.1:8084; js_preread test.preread; return "my CA with verify_depth=0 $message"; js_fetch_verify_depth 0; js_fetch_trusted_certificate myca.crt; } server { listen 127.0.0.1:8085; js_access test.access_ok; ssl_preread on; js_fetch_ciphers HIGH:!aNull:!MD5; js_fetch_protocols TLSv1.1 TLSv1.2; js_fetch_trusted_certificate myca.crt; proxy_pass 127.0.0.1:8081; } server { listen 127.0.0.1:8086; js_access test.access_nok; ssl_preread on; js_fetch_ciphers HIGH:!aNull:!MD5; js_fetch_protocols TLSv1.1 TLSv1.2; js_fetch_trusted_certificate myca.crt; proxy_pass 127.0.0.1:8081; } } EOF my $p1 = port(8081); my $p2 = port(8082); my $p3 = port(8083); my $p4 = port(8084); $t->write_file('test.js', < { s.variables.message = 'https OK - ' + reply.status; s.done(); }) .catch(e => { s.variables.message = 'https NOK - ' + e.message; s.done(); }) } else if (data.length) { s.deny(); } }); } async function access_ok(s) { let r = await ngx.fetch('https://default.example.com:$p1/success', {body: s.remoteAddress}); (r.status == 200) ? s.allow(): s.deny(); } async function access_nok(s) { let r = await ngx.fetch('https://default.example.com:$p1/fail', {body: s.remoteAddress}); (r.status == 200) ? s.allow(): s.deny(); } export default {njs: test_njs, engine, preread, access_ok, access_nok}; EOF my $d = $t->testdir(); $t->write_file('openssl.conf', <write_file('myca.conf', <>$d/openssl.out 2>&1") == 0 or die "Can't create self-signed certificate for CA: $!\n"; foreach my $name ('intermediate', 'default.example.com', '1.example.com') { system("openssl req -new " . "-config $d/openssl.conf -subj /CN=$name/ " . "-out $d/$name.csr -keyout $d/$name.key " . ">>$d/openssl.out 2>&1") == 0 or die "Can't create certificate signing req for $name: $!\n"; } $t->write_file('certserial', '1000'); $t->write_file('certindex', ''); system("openssl ca -batch -config $d/myca.conf " . "-keyfile $d/myca.key -cert $d/myca.crt " . "-subj /CN=intermediate/ -in $d/intermediate.csr " . "-out $d/intermediate.crt " . ">>$d/openssl.out 2>&1") == 0 or die "Can't sign certificate for intermediate: $!\n"; foreach my $name ('default.example.com', '1.example.com') { system("openssl ca -batch -config $d/myca.conf " . "-keyfile $d/intermediate.key -cert $d/intermediate.crt " . "-subj /CN=$name/ -in $d/$name.csr -out $d/$name.crt " . ">>$d/openssl.out 2>&1") == 0 or die "Can't sign certificate for $name $!\n"; $t->write_file("$name.chained.crt", $t->read_file("$name.crt") . $t->read_file('intermediate.crt')); } $t->try_run('no njs.fetch'); plan(skip_all => 'not yet') if http_get('/engine') =~ /QuickJS$/m; $t->plan(6); $t->run_daemon(\&dns_daemon, port(8981), $t); $t->waitforfile($t->testdir . '/' . port(8981)); ############################################################################### like(stream("127.0.0.1:$p2")->io('GOdefault.example.com'), qr/connect failed/s, 'stream non trusted CA'); like(stream("127.0.0.1:$p3")->io('GOdefault.example.com'), qr/https OK/s, 'stream trusted CA'); like(stream("127.0.0.1:$p3")->io('GOlocalhost'), qr/connect failed/s, 'stream wrong CN'); like(stream("127.0.0.1:$p4")->io('GOdefaul.example.com'), qr/connect failed/s, 'stream verify_depth too small'); like(https_get('default.example.com', port(8085), '/backend'), qr!BACKEND OK!, 'access https fetch'); is(https_get('default.example.com', port(8086), '/backend'), '', 'access https fetch not'); ############################################################################### sub get_ssl_socket { my ($host, $port) = @_; my $s; eval { local $SIG{ALRM} = sub { die "timeout\n" }; local $SIG{PIPE} = sub { die "sigpipe\n" }; alarm(8); $s = IO::Socket::SSL->new( Proto => 'tcp', PeerAddr => '127.0.0.1:' . $port, SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE(), SSL_error_trap => sub { die $_[1] } ); alarm(0); }; alarm(0); if ($@) { log_in("died: $@"); return undef; } return $s; } sub https_get { my ($host, $port, $url) = @_; my $s = get_ssl_socket($host, $port); if (!$s) { return ''; } return http(< $s); GET $url HTTP/1.0 Host: $host EOF } ############################################################################### sub reply_handler { my ($recv_data, $port, %extra) = @_; my (@name, @rdata); use constant NOERROR => 0; use constant A => 1; use constant IN => 1; # default values my ($hdr, $rcode, $ttl) = (0x8180, NOERROR, 3600); # decode name my ($len, $offset) = (undef, 12); while (1) { $len = unpack("\@$offset C", $recv_data); last if $len == 0; $offset++; push @name, unpack("\@$offset A$len", $recv_data); $offset += $len; } $offset -= 1; my ($id, $type, $class) = unpack("n x$offset n2", $recv_data); my $name = join('.', @name); if ($type == A) { push @rdata, rd_addr($ttl, '127.0.0.1'); } $len = @name; pack("n6 (C/a*)$len x n2", $id, $hdr | $rcode, 1, scalar @rdata, 0, 0, @name, $type, $class) . join('', @rdata); } sub rd_addr { my ($ttl, $addr) = @_; my $code = 'split(/\./, $addr)'; return pack 'n3N', 0xc00c, A, IN, $ttl if $addr eq ''; pack 'n3N nC4', 0xc00c, A, IN, $ttl, eval "scalar $code", eval($code); } sub dns_daemon { my ($port, $t) = @_; my ($data, $recv_data); my $socket = IO::Socket::INET->new( LocalAddr => '127.0.0.1', LocalPort => $port, Proto => 'udp', ) or die "Can't create listening socket: $!\n"; local $SIG{PIPE} = 'IGNORE'; # signal we are ready open my $fh, '>', $t->testdir() . '/' . $port; close $fh; while (1) { $socket->recv($recv_data, 65536); $data = reply_handler($recv_data, $port); $socket->send($data); } } ############################################################################### njs-0.8.9/nginx/t/stream_js_fetch_init.t000066400000000000000000000061231474132077100202700ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for stream njs module, Response prototype reinitialization. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; use Test::Nginx::Stream qw/ stream /; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http rewrite stream/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } stream { %%TEST_GLOBALS_STREAM%% js_import test.js; server { listen 127.0.0.1:8081; js_access test.access_ok; proxy_pass 127.0.0.1:8090; } } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /njs { js_content test.njs; } location /engine { js_content test.engine; } location /success { return 200; } } } EOF my $p = port(8080); $t->write_file('test.js', <try_run('no stream njs available'); plan(skip_all => 'not yet') if http_get('/engine') =~ /QuickJS$/m; $t->plan(1); $t->run_daemon(\&stream_daemon, port(8090)); $t->waitforsocket('127.0.0.1:' . port(8090)); ############################################################################### local $TODO = 'not yet' unless has_version('0.7.9'); is(stream('127.0.0.1:' . port(8081))->io('ABC'), 'ABC', 'access fetch ok'); ############################################################################### sub has_version { my $need = shift; http_get('/njs') =~ /^([.0-9]+)$/m; my @v = split(/\./, $1); my ($n, $v); for $n (split(/\./, $need)) { $v = shift @v || 0; return 0 if $n > $v; return 1 if $v > $n; } return 1; } ############################################################################### sub stream_daemon { my $server = IO::Socket::INET->new( Proto => 'tcp', LocalAddr => '127.0.0.1:' . port(8090), Listen => 5, Reuse => 1 ) or die "Can't create listening socket: $!\n"; local $SIG{PIPE} = 'IGNORE'; while (my $client = $server->accept()) { $client->autoflush(1); log2c("(new connection $client)"); $client->sysread(my $buffer, 65536) or next; log2i("$client $buffer"); log2o("$client $buffer"); $client->syswrite($buffer); close $client; } } sub log2i { Test::Nginx::log_core('|| <<', @_); } sub log2o { Test::Nginx::log_core('|| >>', @_); } sub log2c { Test::Nginx::log_core('||', @_); } ############################################################################### njs-0.8.9/nginx/t/stream_js_import2.t000066400000000000000000000022641474132077100175520ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for stream njs module, js_import directive in server context. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; use Test::Nginx::Stream qw/ stream /; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/stream stream_return/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } stream { %%TEST_GLOBALS_STREAM%% server { listen 127.0.0.1:8081; js_import foo from ./main.js; js_set $test foo.bar.p; return $test; } } EOF $t->write_file('main.js', <try_run('no njs available')->plan(1); ############################################################################### is(stream('127.0.0.1:' . port(8081))->read(), 'P-TEST', 'foo.bar.p'); ############################################################################### njs-0.8.9/nginx/t/stream_js_ngx.t000066400000000000000000000034651474132077100167560ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for stream njs module, ngx object. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; use Test::Nginx::Stream qw/ stream /; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/stream stream_return/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /njs { js_content test.njs; } } } stream { %%TEST_GLOBALS_STREAM%% js_import test.js; js_set $log test.log; server { listen 127.0.0.1:8081; return $log; } } EOF $t->write_file('test.js', <try_run('no njs ngx')->plan(4); ############################################################################### is(stream('127.0.0.1:' . port(8081))->read(), 'OK', 'log var'); $t->stop(); like($t->read_file('error.log'), qr/\[info\].*ngx.log:FOO/, 'ngx.log info'); like($t->read_file('error.log'), qr/\[warn\].*ngx.log:BAR/, 'ngx.log warn'); like($t->read_file('error.log'), qr/\[error\].*ngx.log:BAZ/, 'ngx.log err'); ############################################################################### njs-0.8.9/nginx/t/stream_js_object.t000066400000000000000000000061401474132077100174210ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for stream njs module, stream session object. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; use Test::Nginx::Stream qw/ stream /; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/stream stream_return/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /engine { js_content test.engine; } } } stream { %%TEST_GLOBALS_STREAM%% js_import test.js; js_set $to_string test.to_string; js_set $define_prop test.define_prop; js_set $in_operator test.in_operator; js_set $redefine_proto test.redefine_proto; js_set $get_own_prop_descs test.get_own_prop_descs; server { listen 127.0.0.1:8081; return $to_string; } server { listen 127.0.0.1:8082; return $define_prop$status; } server { listen 127.0.0.1:8083; return $in_operator; } server { listen 127.0.0.1:8084; return $redefine_proto; } server { listen 127.0.0.1:8085; return $get_own_prop_descs; } } EOF $t->write_file('test.js', <v in s.variables).toString(); } function redefine_proto(s) { s[0] = 'a'; s[1] = 'b'; s.length = 2; Object.setPrototypeOf(s, Array.prototype); return s.join('|') === 'a|b'; } function get_own_prop_descs(s) { return Object.getOwnPropertyDescriptors(s)['on'].value === s.on; } export default { engine, to_string, define_prop, in_operator, redefine_proto, get_own_prop_descs }; EOF $t->try_run('no njs stream session object')->plan(5); ############################################################################### is(stream('127.0.0.1:' . port(8081))->read(), '[object Stream Session]', 'to_string'); is(stream('127.0.0.1:' . port(8082))->read(), '400400', 'define_prop'); is(stream('127.0.0.1:' . port(8083))->read(), 'true,false', 'in_operator'); is(stream('127.0.0.1:' . port(8084))->read(), 'true', 'redefine_proto'); SKIP: { skip "In QuickJS methods are in the prototype", 1 if http_get('/engine') =~ /QuickJS$/m; is(stream('127.0.0.1:' . port(8085))->read(), 'true', 'get_own_prop_descs'); } ############################################################################### njs-0.8.9/nginx/t/stream_js_periodic.t000066400000000000000000000213611474132077100177530ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for stream njs module, js_periodic directive. ############################################################################### use warnings; use strict; use Test::More; use Socket qw/ CRLF /; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; use Test::Nginx::Stream qw/ stream /; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http rewrite stream/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; worker_processes 4; events { } worker_shutdown_timeout 100ms; stream { %%TEST_GLOBALS_STREAM%% js_import test.js; js_shared_dict_zone zone=nums:32k type=number; js_shared_dict_zone zone=strings:32k; js_shared_dict_zone zone=workers:32k type=number; js_set $js_set test.js_set; js_var $js_var JS-VAR; map _ $map_var { default "MAP-VAR"; } server { listen 127.0.0.1:8081; js_periodic test.tick interval=30ms jitter=1ms; js_periodic test.timer interval=1s worker_affinity=all; js_periodic test.overrun interval=30ms; js_periodic test.file interval=1s; js_periodic test.fetch interval=40ms; js_periodic test.multiple_fetches interval=1s; js_periodic test.affinity interval=50ms worker_affinity=0101; js_periodic test.vars interval=10s; js_periodic test.fetch_exception interval=1s; js_periodic test.tick_exception interval=1s; js_periodic test.timer_exception interval=1s; js_periodic test.timeout_exception interval=30ms; js_preread test.test; proxy_pass 127.0.0.1:8090; } } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /engine { js_content test.engine; } location /fetch_ok { return 200 'ok'; } location /fetch_foo { return 200 'foo'; } } } EOF my $p1 = port(8080); $t->write_file('test.js', < {}, 100000); } function tick() { ngx.shared.nums.incr('tick', 1); } function tick_exception() { throw new Error("EXCEPTION"); } function timer() { if (ngx.worker_id != 0) { return; } setTimeout(() => {ngx.shared.nums.set('timer', 1)}, 10); } function timer_exception() { setTimeout(() => {ngx.log(ngx.ERR, 'should not be seen')}, 10); throw new Error("EXCEPTION"); } function timeout_exception() { setTimeout(() => { var v = ngx.shared.nums.get('timeout_exception') || 0; if (v == 0) { ngx.shared.nums.set('timeout_exception', 1); throw new Error("EXCEPTION"); return; } ngx.shared.nums.incr('timeout_exception', 1); }, 1); } function vars(s) { var v = s.variables; ngx.shared.strings.set('vars', `\${v.js_var}|\${v.js_set}|\${v.map_var}`); } function test(s) { s.on('upload', function (data) { if (data.length > 0) { switch (data) { case 'affinity': if (ngx.shared.workers.keys().toSorted().toString() == '1,3') { s.done(); return; } break; case 'fetch': if (ngx.shared.strings.get('fetch').startsWith('okok')) { s.done(); return; } break; case 'multiple_fetches': if (ngx.shared.strings.get('multiple_fetches') .startsWith('ok\@foo')) { s.done(); return; } break; case 'file': let file_data = fs.readFileSync(ngx.conf_prefix + 'file') .toString(); if (file_data == 'abc') { s.done(); return; } break; case 'tick': if (ngx.shared.nums.get('tick') >= 3) { s.done(); return; } break; case 'timeout_exception': if (ngx.shared.nums.get('timeout_exception') >= 2) { s.done(); return; } break; case 'timer': if (ngx.shared.nums.get('timer') == 1) { s.done(); return; } break; case 'vars': var vars = ngx.shared.strings.get('vars'); if (vars === 'JS-VAR|JS-SET|MAP-VAR') { s.done(); return; } break; default: throw new Error(`Unknown test "\${data}"`); } throw new Error(`Test "\${data}" failed`); } }); } export default { affinity, fetch, fetch_exception, js_set, multiple_fetches, file, overrun, test, tick, tick_exception, timer, timer_exception, timeout_exception, vars, engine }; EOF $t->run_daemon(\&stream_daemon, port(8090)); $t->try_run('no js_periodic'); plan(skip_all => 'not yet') if http_get('/engine') =~ /QuickJS$/m; $t->plan(9); $t->waitforsocket('127.0.0.1:' . port(8090)); ############################################################################### select undef, undef, undef, 0.2; is(stream('127.0.0.1:' . port(8081))->io('affinity'), 'affinity', 'affinity test'); is(stream('127.0.0.1:' . port(8081))->io('tick'), 'tick', '3x tick test'); is(stream('127.0.0.1:' . port(8081))->io('timer'), 'timer', 'timer test'); is(stream('127.0.0.1:' . port(8081))->io('file'), 'file', 'file test'); is(stream('127.0.0.1:' . port(8081))->io('fetch'), 'fetch', 'fetch test'); is(stream('127.0.0.1:' . port(8081))->io('multiple_fetches'), 'multiple_fetches', 'muliple fetches test'); is(stream('127.0.0.1:' . port(8081))->io('timeout_exception'), 'timeout_exception', 'timeout exception test'); is(stream('127.0.0.1:' . port(8081))->io('vars'), 'vars', 'vars test'); $t->stop(); unlike($t->read_file('error.log'), qr/\[error\].*should not be seen/, 'check for not discadred events'); ############################################################################### sub stream_daemon { my $server = IO::Socket::INET->new( Proto => 'tcp', LocalAddr => '127.0.0.1:' . port(8090), Listen => 5, Reuse => 1 ) or die "Can't create listening socket: $!\n"; local $SIG{PIPE} = 'IGNORE'; while (my $client = $server->accept()) { $client->autoflush(1); log2c("(new connection $client)"); $client->sysread(my $buffer, 65536) or next; log2i("$client $buffer"); log2o("$client $buffer"); $client->syswrite($buffer); close $client; } } sub log2i { Test::Nginx::log_core('|| <<', @_); } sub log2o { Test::Nginx::log_core('|| >>', @_); } sub log2c { Test::Nginx::log_core('||', @_); } ############################################################################### njs-0.8.9/nginx/t/stream_js_preload_object.t000066400000000000000000000054561474132077100211400ustar00rootroot00000000000000#!/usr/bin/perl # (C) Vadim Zhestikov # (C) Nginx, Inc. # Tests for stream njs module, js_preload_object directive. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; use Test::Nginx::Stream qw/ stream /; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/stream stream_return/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import main.js; server { listen 127.0.0.1:8080; server_name localhost; location /engine { js_content main.engine; } } } stream { %%TEST_GLOBALS_STREAM%% js_preload_object g1 from g.json; js_set $test foo.bar.p; js_import lib.js; js_import foo from ./main.js; server { listen 127.0.0.1:8081; return $test; } server { listen 127.0.0.1:8082; js_access lib.access; js_preread lib.preread; js_filter lib.filter; proxy_pass 127.0.0.1:8083; } server { listen 127.0.0.1:8083; return "x"; } } EOF $t->write_file('lib.js', < 0) { s.done(); } }); } function filter(s) { s.on('upload', function(data, flags) { fup = g1.c.prop[0].a; s.send(data); }); s.on('download', function(data, flags) { fdown = g1.b[3]; s.send(data); if (flags.last) { s.send(`\${acc}\${pup}\${fup}\${fdown}`, flags); s.off('download'); } }); } export default {access, preread, filter}; EOF $t->write_file('main.js', <write_file('g.json', '{"a":1, "b":[1,2,"element",4,5], "c":{"prop":[{"a":3}]}}'); $t->try_run('no js_preload_object available'); plan(skip_all => 'not yet') if http_get('/engine') =~ /QuickJS$/m; $t->plan(2); ############################################################################### is(stream('127.0.0.1:' . port(8081))->read(), 'element', 'foo.bar.p'); is(stream('127.0.0.1:' . port(8082))->io('0'), 'x1234', 'filter chain'); ############################################################################### njs-0.8.9/nginx/t/stream_js_process.t000066400000000000000000000042451474132077100176350ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for stream njs module, process object. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; use Test::Nginx::Stream qw/ stream /; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/stream stream_return/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } env FOO=bar; env BAR=baz; stream { %%TEST_GLOBALS_STREAM%% js_import test.js; js_set $env_foo test.env_foo; js_set $env_bar test.env_bar; js_set $env_home test.env_home; js_set $argv test.argv; server { listen 127.0.0.1:8081; return $env_foo; } server { listen 127.0.0.1:8082; return $env_bar; } server { listen 127.0.0.1:8083; return $env_home; } server { listen 127.0.0.1:8084; return $argv; } } EOF $t->write_file('test.js', <= 0}`; } export default { env_foo, env_bar, env_home, argv }; EOF $t->try_run('no njs stream session object')->plan(4); ############################################################################### is(stream('127.0.0.1:' . port(8081))->read(), 'bar', 'env.FOO'); is(stream('127.0.0.1:' . port(8082))->read(), 'baz', 'env.BAR'); is(stream('127.0.0.1:' . port(8083))->read(), 'undefined', 'env HOME'); is(stream('127.0.0.1:' . port(8084))->read(), 'true true', 'argv'); ############################################################################### njs-0.8.9/nginx/t/stream_js_send.t000066400000000000000000000077031474132077100171120ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for s.send() in stream njs module. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; use Test::Nginx::Stream qw/ stream /; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http stream/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% js_import test.js; server { listen 127.0.0.1:8080; server_name localhost; location /njs { js_content test.njs; } } } stream { %%TEST_GLOBALS_STREAM%% js_import test.js; server { listen 127.0.0.1:8081; js_filter test.filter; proxy_pass 127.0.0.1:8090; } server { listen 127.0.0.1:8082; js_filter test.filter_direct; proxy_pass 127.0.0.1:8090; } } EOF $t->write_file('test.js', < { s.send("__HANDSHAKE__", flags); const p = new Promise((resolve, reject) => { s.on("download", (data, flags) => { s.off("download"); resolve(data); }); }); s.off("upload"); const handshakeResponse = await p; if (handshakeResponse != '__HANDSHAKE_RESPONSE__') { throw `Handshake failed: \${handshakeResponse}`; } s.send(data, flags); }); } function filter_direct(s) { s.on("upload", async (data, flags) => { s.sendUpstream("__HANDSHAKE__", flags); const p = new Promise((resolve, reject) => { s.on("download", (data, flags) => { s.off("download"); resolve(data); }); }); s.off("upload"); const handshakeResponse = await p; if (handshakeResponse != '__HANDSHAKE_RESPONSE__') { throw `Handshake failed: \${handshakeResponse}`; } s.sendDownstream('xxx', flags); s.sendUpstream(data, flags); }); } export default {njs:test_njs, filter, filter_direct}; EOF $t->run_daemon(\&stream_daemon, port(8090)); $t->try_run('no stream njs available')->plan(2); $t->waitforsocket('127.0.0.1:' . port(8090)); ############################################################################### is(stream('127.0.0.1:' . port(8081))->io('abc'), 'ABC', 'async filter');; is(stream('127.0.0.1:' . port(8082))->io('abc'), 'xxxABC', 'async filter direct'); $t->stop(); ############################################################################### sub stream_daemon { my $server = IO::Socket::INET->new( Proto => 'tcp', LocalAddr => '127.0.0.1:' . port(8090), Listen => 5, Reuse => 1 ) or die "Can't create listening socket: $!\n"; local $SIG{PIPE} = 'IGNORE'; while (my $client = $server->accept()) { $client->autoflush(1); log2c("(new connection $client)"); $client->sysread(my $buffer, 65536) or next; log2i("$client $buffer"); if ($buffer ne "__HANDSHAKE__") { $buffer = "__HANDSHAKE_INVALID__"; log2o("$client $buffer"); $client->syswrite($buffer); close $client; } $buffer = "__HANDSHAKE_RESPONSE__"; log2o("$client $buffer"); $client->syswrite($buffer); $client->sysread($buffer, 65536) or next; $buffer = uc($buffer); log2o("$client $buffer"); $client->syswrite($buffer); close $client; } } sub log2i { Test::Nginx::log_core('|| <<', @_); } sub log2o { Test::Nginx::log_core('|| >>', @_); } sub log2c { Test::Nginx::log_core('||', @_); } ############################################################################### njs-0.8.9/nginx/t/stream_js_shared_dict.t000066400000000000000000000110231474132077100204200ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for js_shared_dict_zone directive. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; use Test::Nginx::Stream qw/ stream /; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http rewrite stream/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } http { %%TEST_GLOBALS_HTTP%% server { listen 127.0.0.1:8080; server_name localhost; location / { return 200; } location /engine { js_content test.engine; } } } stream { %%TEST_GLOBALS_STREAM%% js_import test.js; js_shared_dict_zone zone=foo:32k; server { listen 127.0.0.1:8081; js_preread test.preread_verify; proxy_pass 127.0.0.1:8090; } server { listen 127.0.0.1:8082; js_preread test.control_access; proxy_pass 127.0.0.1:8080; } } EOF $t->write_file('test.js', <= 4 && collect.readUInt16BE(0) == 0xabcd) { let id = collect.slice(2,4); ngx.shared.foo.get(id) ? s.done(): s.deny(); } else if (collect.length) { s.deny(); } }); } function control_access(s) { var req = ''; s.on('upload', function(data, flags) { req += data; var n = req.search('\\n'); if (n != -1) { var params = req.substr(0, n).split(' ')[1].split('?')[1]; var args = qs.parse(params); switch (args.action) { case 'set': ngx.shared.foo.set(args.key, args.value); break; case 'del': ngx.shared.foo.delete(args.key); break; } s.done(); } }); } export default { engine, preread_verify, control_access }; EOF $t->try_run('no js_shared_dict_zone'); $t->plan(9); $t->run_daemon(\&stream_daemon, port(8090)); $t->waitforsocket('127.0.0.1:' . port(8090)); ############################################################################### is(stream('127.0.0.1:' . port(8081))->io("\xAB\xCDQZ##"), "", 'access failed, QZ is not in the shared dict'); is(stream('127.0.0.1:' . port(8081))->io("\xAB\xCDQQ##"), "", 'access failed, QQ is not in the shared dict'); like(get('/?action=set&key=QZ&value=1'), qr/200/, 'set foo.QZ'); is(stream('127.0.0.1:' . port(8081))->io("\xAB\xCDQZ##"), "\xAB\xCDQZ##", 'access granted'); is(stream('127.0.0.1:' . port(8081))->io("\xAB\xCDQQ##"), "", 'access failed, QQ is not in the shared dict'); like(get('/?action=del&key=QZ'), qr/200/, 'del foo.QZ'); like(get('/?action=set&key=QQ&value=1'), qr/200/, 'set foo.QQ'); is(stream('127.0.0.1:' . port(8081))->io("\xAB\xCDQZ##"), "", 'access failed, QZ is not in the shared dict'); is(stream('127.0.0.1:' . port(8081))->io("\xAB\xCDQQ##"), "\xAB\xCDQQ##", 'access granted'); ############################################################################### sub stream_daemon { my $server = IO::Socket::INET->new( Proto => 'tcp', LocalAddr => '127.0.0.1:' . port(8090), Listen => 5, Reuse => 1 ) or die "Can't create listening socket: $!\n"; local $SIG{PIPE} = 'IGNORE'; while (my $client = $server->accept()) { $client->autoflush(1); log2c("(new connection $client)"); $client->sysread(my $buffer, 65536) or next; log2i("$client $buffer"); log2o("$client $buffer"); $client->syswrite($buffer); close $client; } } sub log2i { Test::Nginx::log_core('|| <<', @_); } sub log2o { Test::Nginx::log_core('|| >>', @_); } sub log2c { Test::Nginx::log_core('||', @_); } sub get { my ($url, %extra) = @_; my $s = IO::Socket::INET->new( Proto => 'tcp', PeerAddr => '127.0.0.1:' . port(8082) ) or die "Can't connect to nginx: $!\n"; return http_get($url, socket => $s); } ############################################################################### njs-0.8.9/nginx/t/stream_js_var.t000066400000000000000000000026321474132077100167450ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for stream njs module, js_var directive. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; use Test::Nginx::Stream qw/ stream /; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/stream stream_return/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } stream { %%TEST_GLOBALS_STREAM%% js_import test.js; js_var $foo; js_var $bar a:$remote_addr; js_set $var test.varr; server { listen 127.0.0.1:8081; return $bar$foo; } server { listen 127.0.0.1:8082; return $var$foo; } } EOF $t->write_file('test.js', <try_run('no stream js_var')->plan(2); ############################################################################### is(stream('127.0.0.1:' . port(8081))->io('###'), 'a:127.0.0.1', 'default value'); is(stream('127.0.0.1:' . port(8082))->io('###'), 'xxx', 'value set'); ############################################################################### njs-0.8.9/nginx/t/stream_js_var2.t000066400000000000000000000026641474132077100170340ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for stream njs module, js_var directive in server context. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; use Test::Nginx::Stream qw/ stream /; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/stream stream_return/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } stream { %%TEST_GLOBALS_STREAM%% js_import test.js; js_var $foo; server { listen 127.0.0.1:8081; js_var $bar a:$remote_addr; return $bar$foo; } server { listen 127.0.0.1:8082; js_set $var test.varr; return $var$foo; } } EOF $t->write_file('test.js', <try_run('no stream js_var')->plan(2); ############################################################################### is(stream('127.0.0.1:' . port(8081))->io('###'), 'a:127.0.0.1', 'default value'); is(stream('127.0.0.1:' . port(8082))->io('###'), 'xxx', 'value set'); ############################################################################### njs-0.8.9/nginx/t/stream_js_variables.t000066400000000000000000000031751474132077100201300ustar00rootroot00000000000000#!/usr/bin/perl # (C) Dmitry Volyntsev # (C) Nginx, Inc. # Tests for stream njs module, setting nginx variables. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; use Test::Nginx::Stream qw/ stream /; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/stream stream_return/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } stream { %%TEST_GLOBALS_STREAM%% js_set $test_var test.variable; js_set $test_not_found test.not_found; js_import test.js; server { listen 127.0.0.1:8081; return $test_var$status; } server { listen 127.0.0.1:8082; return $test_not_found; } } EOF $t->write_file('test.js', <try_run('no stream njs available')->plan(2); ############################################################################### is(stream('127.0.0.1:' . port(8081))->read(), 'test_var400', 'var set'); is(stream('127.0.0.1:' . port(8082))->read(), 'not_found', 'not found set'); $t->stop(); ############################################################################### njs-0.8.9/nginx/t/stream_js_variables_nocache.t000066400000000000000000000041501474132077100216020ustar00rootroot00000000000000#!/usr/bin/perl # (C) Thomas P. # Tests for stream njs module, setting non-cacheable nginx variables. ############################################################################### use warnings; use strict; use Test::More; BEGIN { use FindBin; chdir($FindBin::Bin); } use lib 'lib'; use Test::Nginx; use Test::Nginx::Stream qw/ stream /; ############################################################################### select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/stream stream_return/) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% daemon off; events { } stream { %%TEST_GLOBALS_STREAM%% js_set $nocache_var test.variable nocache; js_set $default_var test.variable; js_set $callcount_var test.callCount nocache; js_import test.js; server { listen 127.0.0.1:8081; set $a $default_var; set $b $default_var; return '"$a/$b"'; } server { listen 127.0.0.1:8082; set $a $nocache_var; set $b $nocache_var; return '"$a/$b"'; } server { listen 127.0.0.1:8083; set $a $callcount_var; set $b $callcount_var; return '"$a/$b"'; } } EOF $t->write_file('test.js', <try_run('no nocache stream njs variables')->plan(3); ############################################################################### # We use backreferences to make sure the same value was returned for the two uses like(stream('127.0.0.1:' . port(8081))->read(), qr/"(.+)\/\1"/, 'cached variable'); # Negative lookaheads don't capture, hence the .+ after it like(stream('127.0.0.1:' . port(8082))->read(), qr/"(.+)\/(?!\1).+"/, 'noncacheable variable'); like(stream('127.0.0.1:' . port(8083))->read(), qr/"1\/2"/, 'callcount variable'); $t->stop(); ############################################################################### njs-0.8.9/src/000077500000000000000000000000001474132077100131175ustar00rootroot00000000000000njs-0.8.9/src/njs.h000066400000000000000000000505171474132077100140720ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. * * njs public header. */ #ifndef _NJS_H_INCLUDED_ #define _NJS_H_INCLUDED_ #include #define NJS_VERSION "0.8.9" #define NJS_VERSION_NUMBER 0x000809 #include #include #include #include #include #include #include #include #include typedef uintptr_t njs_index_t; typedef struct njs_vm_s njs_vm_t; typedef struct njs_mod_s njs_mod_t; typedef union njs_value_s njs_value_t; typedef struct njs_function_s njs_function_t; typedef struct njs_vm_shared_s njs_vm_shared_t; typedef struct njs_object_init_s njs_object_init_t; typedef struct njs_object_prop_s njs_object_prop_t; typedef struct njs_object_type_init_s njs_object_type_init_t; typedef struct njs_external_s njs_external_t; /* * njs_opaque_value_t is the external storage type for native njs_value_t type. * sizeof(njs_opaque_value_t) == sizeof(njs_value_t). */ typedef struct { uint64_t filler[2]; } njs_opaque_value_t; /* sizeof(njs_value_t) is 16 bytes. */ #define njs_argument(args, n) \ (njs_value_t *) ((u_char *) args + (n) * 16) extern const njs_value_t njs_value_undefined; #define njs_arg(args, nargs, n) \ ((n < nargs) ? njs_argument(args, n) \ : (njs_value_t *) &njs_value_undefined) #define njs_value_assign(dst, src) \ memcpy(dst, src, sizeof(njs_opaque_value_t)) #define njs_value_arg(val) ((njs_value_t *) val) #define njs_lvalue_arg(lvalue, args, nargs, n) \ ((n < nargs) ? njs_argument(args, n) \ : (njs_value_assign(lvalue, &njs_value_undefined), lvalue)) #define njs_vm_error(vm, fmt, ...) \ njs_vm_error2(vm, 0, fmt, ##__VA_ARGS__) #define njs_vm_internal_error(vm, fmt, ...) \ njs_vm_error2(vm, 2, fmt, ##__VA_ARGS__) #define njs_vm_range_error(vm, fmt, ...) \ njs_vm_error2(vm, 3, fmt, ##__VA_ARGS__) #define njs_vm_ref_error(vm, fmt, ...) \ njs_vm_error2(vm, 4, fmt, ##__VA_ARGS__) #define njs_vm_syntax_error(vm, fmt, ...) \ njs_vm_error2(vm, 5, fmt, ##__VA_ARGS__) #define njs_vm_type_error(vm, fmt, ...) \ njs_vm_error2(vm, 6, fmt, ##__VA_ARGS__) #define njs_deprecated(vm, text) \ do { \ static njs_bool_t reported; \ \ if (!reported) { \ njs_vm_warn(vm, text " is deprecated " \ "and will be removed in the future"); \ reported = 1; \ } \ } while(0) /* * njs_prop_handler_t operates as a property getter/setter or delete handler. * - retval != NULL && setval == NULL - GET context. * - retval != NULL && setval != NULL - SET context. * - retval == NULL - DELETE context. * * njs_prop_handler_t is expected to return: * NJS_OK - handler executed successfully; * NJS_DECLINED - handler was applied to inappropriate object, retval * contains undefined value; * NJS_ERROR - some error, njs_vm_exception_get(vm) can be used to get * the exception value. */ typedef njs_int_t (*njs_prop_handler_t) (njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); typedef njs_int_t (*njs_exotic_keys_t)(njs_vm_t *vm, njs_value_t *value, njs_value_t *retval); typedef njs_int_t (*njs_function_native_t) (njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t magic8, njs_value_t *retval); typedef enum { NJS_SYMBOL_INVALID, NJS_SYMBOL_ASYNC_ITERATOR, NJS_SYMBOL_HAS_INSTANCE, NJS_SYMBOL_IS_CONCAT_SPREADABLE, NJS_SYMBOL_ITERATOR, NJS_SYMBOL_MATCH, NJS_SYMBOL_MATCH_ALL, NJS_SYMBOL_REPLACE, NJS_SYMBOL_SEARCH, NJS_SYMBOL_SPECIES, NJS_SYMBOL_SPLIT, NJS_SYMBOL_TO_PRIMITIVE, NJS_SYMBOL_TO_STRING_TAG, NJS_SYMBOL_UNSCOPABLES, NJS_SYMBOL_KNOWN_MAX, } njs_wellknown_symbol_t; typedef enum { #define njs_object_enum_kind(flags) (flags & 7) NJS_ENUM_KEYS = 1, NJS_ENUM_VALUES = 2, NJS_ENUM_BOTH = 4, #define njs_object_enum(flags) (flags & (NJS_ENUM_STRING | NJS_ENUM_SYMBOL)) NJS_ENUM_STRING = 8, NJS_ENUM_SYMBOL = 16, NJS_ENUM_ENUMERABLE_ONLY = 32, NJS_ENUM_NON_SHARED_ONLY = 64, } njs_object_enum_t; typedef enum { /* * Extern property type. */ NJS_EXTERN_PROPERTY = 0, NJS_EXTERN_METHOD = 1, NJS_EXTERN_OBJECT = 2, NJS_EXTERN_SELF = 3, #define NJS_EXTERN_TYPE_MASK 3 /* * Extern property flags. */ NJS_EXTERN_SYMBOL = 4, } njs_extern_flag_t; typedef enum { NJS_EXTERN_TYPE_INT = 0, NJS_EXTERN_TYPE_UINT = 1, NJS_EXTERN_TYPE_VALUE = 2, } njs_extern_type_t; struct njs_external_s { njs_extern_flag_t flags; union { njs_str_t string; uint32_t symbol; } name; unsigned writable; unsigned configurable; unsigned enumerable; union { struct { const char value[15]; /* NJS_STRING_SHORT + 1. */ njs_prop_handler_t handler; uint16_t magic16; uint32_t magic32; } property; struct { njs_function_native_t native; uint8_t magic8; uint8_t ctor; } method; struct { njs_external_t *properties; njs_uint_t nproperties; unsigned writable; unsigned configurable; unsigned enumerable; njs_prop_handler_t prop_handler; uint32_t magic32; njs_exotic_keys_t keys; } object; } u; }; typedef void * njs_external_ptr_t; typedef njs_mod_t *(*njs_module_loader_t)(njs_vm_t *vm, njs_external_ptr_t external, njs_str_t *name); typedef void (*njs_rejection_tracker_t)(njs_vm_t *vm, njs_external_ptr_t external, njs_bool_t is_handled, njs_value_t *promise, njs_value_t *reason); typedef struct { size_t size; uintptr_t *values; } njs_vm_meta_t; typedef njs_int_t (*njs_addon_init_pt)(njs_vm_t *vm); typedef struct { njs_str_t name; njs_addon_init_pt preinit; njs_addon_init_pt init; } njs_module_t; typedef struct { njs_external_ptr_t external; njs_vm_shared_t *shared; njs_vm_meta_t *metas; njs_module_t **addons; njs_str_t file; char **argv; njs_uint_t argc; njs_uint_t max_stack_size; /* * interactive - enables "interactive" mode. * (REPL). Allows starting parent VM without cloning. * disassemble - enables disassemble. * backtrace - enables backtraces. * quiet - removes filenames from backtraces. To produce comparable test262 diffs. * sandbox - "sandbox" mode. Disables file access. * unsafe - enables unsafe language features: * - Function constructors. * module - ES6 "module" mode. Script mode is default. * ast - print AST. */ uint8_t interactive; /* 1 bit */ uint8_t trailer; /* 1 bit */ uint8_t init; /* 1 bit */ uint8_t disassemble; /* 1 bit */ uint8_t backtrace; /* 1 bit */ uint8_t quiet; /* 1 bit */ uint8_t sandbox; /* 1 bit */ uint8_t unsafe; /* 1 bit */ uint8_t module; /* 1 bit */ uint8_t ast; /* 1 bit */ #ifdef NJS_DEBUG_OPCODE uint8_t opcode_debug; /* 1 bit */ #endif #ifdef NJS_DEBUG_GENERATOR uint8_t generator_debug; /* 1 bit */ #endif } njs_vm_opt_t; typedef struct { njs_function_t *function; njs_opaque_value_t argument; njs_opaque_value_t value; void *data; int64_t from; int64_t to; } njs_iterator_args_t; typedef njs_int_t (*njs_iterator_handler_t)(njs_vm_t *vm, njs_iterator_args_t *args, njs_value_t *entry, int64_t n, njs_value_t *retval); NJS_EXPORT void njs_vm_opt_init(njs_vm_opt_t *options); NJS_EXPORT njs_vm_t *njs_vm_create(njs_vm_opt_t *options); NJS_EXPORT void njs_vm_destroy(njs_vm_t *vm); NJS_EXPORT njs_int_t njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end); NJS_EXPORT void njs_vm_set_module_loader(njs_vm_t *vm, njs_module_loader_t module_loader, void *opaque); NJS_EXPORT njs_mod_t *njs_vm_add_module(njs_vm_t *vm, njs_str_t *name, njs_value_t *value); NJS_EXPORT njs_mod_t *njs_vm_compile_module(njs_vm_t *vm, njs_str_t *name, u_char **start, u_char *end); NJS_EXPORT njs_int_t njs_vm_reuse(njs_vm_t *vm); NJS_EXPORT njs_vm_t *njs_vm_clone(njs_vm_t *vm, njs_external_ptr_t external); NJS_EXPORT njs_int_t njs_vm_enqueue_job(njs_vm_t *vm, njs_function_t *function, const njs_value_t *args, njs_uint_t nargs); /* * Executes a single pending job. * 1 successful run. * NJS_OK pending job was not found. * NJS_ERROR some exception or internal error happens. */ NJS_EXPORT njs_int_t njs_vm_execute_pending_job(njs_vm_t *vm); NJS_EXPORT njs_int_t njs_vm_pending(njs_vm_t *vm); NJS_EXPORT void njs_vm_set_rejection_tracker(njs_vm_t *vm, njs_rejection_tracker_t rejection_tracker, void *opaque); /* * Runs the specified function with provided arguments. * NJS_OK successful run. * NJS_ERROR some exception or internal error happens. * * njs_vm_exception_get(vm) can be used to get the exception value. */ NJS_EXPORT njs_int_t njs_vm_call(njs_vm_t *vm, njs_function_t *function, const njs_value_t *args, njs_uint_t nargs); NJS_EXPORT njs_int_t njs_vm_invoke(njs_vm_t *vm, njs_function_t *function, const njs_value_t *args, njs_uint_t nargs, njs_value_t *retval); /* * Runs the global code. * NJS_OK successful run. * NJS_ERROR some exception or internal error happens. * * njs_vm_exception_get(vm) can be used to get the exception value. */ NJS_EXPORT njs_int_t njs_vm_start(njs_vm_t *vm, njs_value_t *retval); #define NJS_PROTO_ID_ANY (-1) NJS_EXPORT njs_int_t njs_vm_external_prototype(njs_vm_t *vm, const njs_external_t *definition, njs_uint_t n); NJS_EXPORT njs_int_t njs_vm_external_constructor(njs_vm_t *vm, const njs_str_t *name, njs_function_native_t native, const njs_external_t *ctor_props, njs_uint_t ctor_nprops, const njs_external_t *proto_props, njs_uint_t proto_nprops); NJS_EXPORT njs_int_t njs_vm_external_create(njs_vm_t *vm, njs_value_t *value, njs_int_t proto_id, njs_external_ptr_t external, njs_bool_t shared); NJS_EXPORT njs_external_ptr_t njs_vm_external(njs_vm_t *vm, njs_int_t proto_id, const njs_value_t *value); NJS_EXPORT njs_int_t njs_external_property(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); NJS_EXPORT njs_int_t njs_value_property(njs_vm_t *vm, njs_value_t *value, njs_value_t *key, njs_value_t *retval); NJS_EXPORT njs_int_t njs_value_property_set(njs_vm_t *vm, njs_value_t *value, njs_value_t *key, njs_value_t *setval); NJS_EXPORT uintptr_t njs_vm_meta(njs_vm_t *vm, njs_uint_t index); NJS_EXPORT njs_vm_opt_t *njs_vm_options(njs_vm_t *vm); NJS_EXPORT njs_int_t njs_error_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t type, njs_value_t *retval); NJS_EXPORT njs_int_t njs_object_prototype_create_constructor(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); NJS_EXPORT njs_int_t njs_object_prototype_create(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); NJS_EXPORT njs_function_t *njs_vm_function_alloc(njs_vm_t *vm, njs_function_native_t native, njs_bool_t shared, njs_bool_t ctor); NJS_EXPORT void njs_disassembler(njs_vm_t *vm); NJS_EXPORT njs_int_t njs_vm_bind(njs_vm_t *vm, const njs_str_t *var_name, const njs_value_t *value, njs_bool_t shared); njs_int_t njs_vm_bind_handler(njs_vm_t *vm, const njs_str_t *var_name, njs_prop_handler_t handler, uint16_t magic16, uint32_t magic32, njs_bool_t shared); NJS_EXPORT njs_int_t njs_vm_global(njs_vm_t *vm, njs_value_t *retval); NJS_EXPORT njs_int_t njs_vm_value(njs_vm_t *vm, const njs_str_t *path, njs_value_t *retval); NJS_EXPORT njs_function_t *njs_vm_function(njs_vm_t *vm, const njs_str_t *name); NJS_EXPORT njs_bool_t njs_vm_constructor(njs_vm_t *vm); NJS_EXPORT njs_int_t njs_vm_prototype(njs_vm_t *vm, njs_value_t *value, njs_value_t *retval); NJS_EXPORT void njs_vm_throw(njs_vm_t *vm, const njs_value_t *value); NJS_EXPORT void njs_vm_error2(njs_vm_t *vm, unsigned error_type, const char *fmt, ...); NJS_EXPORT void njs_vm_error3(njs_vm_t *vm, unsigned type, const char *fmt, ...); NJS_EXPORT void njs_vm_exception_get(njs_vm_t *vm, njs_value_t *retval); NJS_EXPORT njs_mp_t *njs_vm_memory_pool(njs_vm_t *vm); NJS_EXPORT njs_external_ptr_t njs_vm_external_ptr(njs_vm_t *vm); NJS_EXPORT njs_int_t njs_value_to_integer(njs_vm_t *vm, njs_value_t *value, int64_t *dst); /* Gets string value, no copy. */ NJS_EXPORT void njs_value_string_get(njs_value_t *value, njs_str_t *dst); NJS_EXPORT njs_int_t njs_vm_value_string_create(njs_vm_t *vm, njs_value_t *value, const u_char *start, uint32_t size); NJS_EXPORT njs_int_t njs_vm_value_string_create_chb(njs_vm_t *vm, njs_value_t *value, njs_chb_t *chain); NJS_EXPORT njs_int_t njs_vm_string_compare(const njs_value_t *v1, const njs_value_t *v2); NJS_EXPORT njs_int_t njs_vm_value_array_buffer_set(njs_vm_t *vm, njs_value_t *value, const u_char *start, uint32_t size); NJS_EXPORT njs_int_t njs_value_buffer_get(njs_vm_t *vm, njs_value_t *value, njs_str_t *dst); /* * Sets a Buffer value. * start data is not copied and should not be freed. */ NJS_EXPORT njs_int_t njs_vm_value_buffer_set(njs_vm_t *vm, njs_value_t *value, const u_char *start, uint32_t size); NJS_EXPORT njs_int_t njs_value_to_string(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value); /* * Converts a value to bytes. */ NJS_EXPORT njs_int_t njs_vm_value_to_bytes(njs_vm_t *vm, njs_str_t *dst, njs_value_t *src); /* * Converts a value to string. */ NJS_EXPORT njs_int_t njs_vm_value_to_string(njs_vm_t *vm, njs_str_t *dst, njs_value_t *src); /* * Calls njs_vm_value_to_string(), if exception was thrown adds backtrace. */ NJS_EXPORT njs_int_t njs_vm_value_string(njs_vm_t *vm, njs_str_t *dst, njs_value_t *src); /* * If string value is null-terminated the corresponding C string * is returned as is, otherwise the new copy is allocated with * the terminating zero byte. */ NJS_EXPORT const char *njs_vm_value_to_c_string(njs_vm_t *vm, njs_value_t *value); NJS_EXPORT njs_int_t njs_vm_exception_string(njs_vm_t *vm, njs_str_t *dst); NJS_EXPORT njs_int_t njs_vm_value_dump(njs_vm_t *vm, njs_str_t *dst, njs_value_t *value, njs_uint_t console, njs_uint_t indent); NJS_EXPORT void njs_vm_memory_error(njs_vm_t *vm); NJS_EXPORT void njs_value_undefined_set(njs_value_t *value); NJS_EXPORT void njs_value_null_set(njs_value_t *value); NJS_EXPORT void njs_value_invalid_set(njs_value_t *value); NJS_EXPORT void njs_value_boolean_set(njs_value_t *value, int yn); NJS_EXPORT void njs_value_number_set(njs_value_t *value, double num); NJS_EXPORT void njs_value_function_set(njs_value_t *value, njs_function_t *function); NJS_EXPORT void njs_value_external_set(njs_value_t *value, njs_external_ptr_t external); NJS_EXPORT uint8_t njs_value_bool(const njs_value_t *value); NJS_EXPORT double njs_value_number(const njs_value_t *value); NJS_EXPORT njs_function_t *njs_value_function(const njs_value_t *value); NJS_EXPORT njs_function_native_t njs_value_native_function( const njs_value_t *value); NJS_EXPORT void *njs_value_ptr(const njs_value_t *value); njs_external_ptr_t njs_value_external(const njs_value_t *value); NJS_EXPORT njs_int_t njs_value_external_tag(const njs_value_t *value); NJS_EXPORT uint16_t njs_vm_prop_magic16(njs_object_prop_t *prop); NJS_EXPORT uint32_t njs_vm_prop_magic32(njs_object_prop_t *prop); NJS_EXPORT njs_int_t njs_vm_prop_name(njs_vm_t *vm, njs_object_prop_t *prop, njs_str_t *dst); NJS_EXPORT njs_int_t njs_value_is_null(const njs_value_t *value); NJS_EXPORT njs_int_t njs_value_is_undefined(const njs_value_t *value); NJS_EXPORT njs_int_t njs_value_is_null_or_undefined(const njs_value_t *value); NJS_EXPORT njs_int_t njs_value_is_valid(const njs_value_t *value); NJS_EXPORT njs_int_t njs_value_is_boolean(const njs_value_t *value); NJS_EXPORT njs_int_t njs_value_is_number(const njs_value_t *value); NJS_EXPORT njs_int_t njs_value_is_valid_number(const njs_value_t *value); NJS_EXPORT njs_int_t njs_value_is_string(const njs_value_t *value); NJS_EXPORT njs_int_t njs_value_is_object(const njs_value_t *value); NJS_EXPORT njs_int_t njs_value_is_error(const njs_value_t *value); NJS_EXPORT njs_int_t njs_value_is_external(const njs_value_t *value, njs_int_t proto_id); NJS_EXPORT njs_int_t njs_value_is_array(const njs_value_t *value); NJS_EXPORT njs_int_t njs_value_is_function(const njs_value_t *value); NJS_EXPORT njs_int_t njs_value_is_buffer(const njs_value_t *value); NJS_EXPORT njs_int_t njs_value_is_data_view(const njs_value_t *value); NJS_EXPORT njs_int_t njs_vm_object_alloc(njs_vm_t *vm, njs_value_t *retval, ...); NJS_EXPORT njs_value_t *njs_vm_object_keys(njs_vm_t *vm, njs_value_t *value, njs_value_t *retval); NJS_EXPORT njs_value_t *njs_vm_value_enumerate(njs_vm_t *vm, njs_value_t *value, uint32_t flags, njs_value_t *retval); NJS_EXPORT njs_value_t *njs_vm_value_own_enumerate(njs_vm_t *vm, njs_value_t *value, uint32_t flags, njs_value_t *retval); NJS_EXPORT njs_value_t *njs_vm_object_prop(njs_vm_t *vm, njs_value_t *value, const njs_str_t *key, njs_opaque_value_t *retval); NJS_EXPORT njs_int_t njs_vm_object_prop_set(njs_vm_t *vm, njs_value_t *value, const njs_str_t *prop, njs_opaque_value_t *setval); NJS_EXPORT njs_int_t njs_vm_object_iterate(njs_vm_t *vm, njs_iterator_args_t *args, njs_iterator_handler_t handler, njs_value_t *retval); NJS_EXPORT njs_int_t njs_vm_array_alloc(njs_vm_t *vm, njs_value_t *retval, uint32_t spare); NJS_EXPORT njs_int_t njs_vm_array_length(njs_vm_t *vm, njs_value_t *value, int64_t *length); NJS_EXPORT njs_value_t *njs_vm_array_start(njs_vm_t *vm, njs_value_t *value); NJS_EXPORT njs_value_t *njs_vm_array_prop(njs_vm_t *vm, njs_value_t *value, int64_t index, njs_opaque_value_t *retval); NJS_EXPORT njs_value_t *njs_vm_array_push(njs_vm_t *vm, njs_value_t *value); NJS_EXPORT njs_int_t njs_vm_date_alloc(njs_vm_t *vm, njs_value_t *retval, double time); NJS_EXPORT njs_int_t njs_vm_json_parse(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_value_t *retval); NJS_EXPORT njs_int_t njs_vm_json_stringify(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_value_t *retval); NJS_EXPORT njs_int_t njs_vm_query_string_parse(njs_vm_t *vm, u_char *start, u_char *end, njs_value_t *retval); NJS_EXPORT njs_int_t njs_vm_promise_create(njs_vm_t *vm, njs_value_t *retval, njs_value_t *callbacks); #endif /* _NJS_H_INCLUDED_ */ njs-0.8.9/src/njs.pc.in000066400000000000000000000002511474132077100146400ustar00rootroot00000000000000prefix=@PREFIX@ libdir=@LIBDIR@ Name: njs Description: library to embed njs scripting language Version: @VERSION@ Libs: -L${libdir} -lnjs @EXTRA_LIBS@ Cflags: @CFLAGS@ njs-0.8.9/src/njs_addr2line.c000066400000000000000000000152031474132077100160020ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. * * addr2line impementaton based upon the work by Jeff Muizelaar. * * A hacky replacement for backtrace_symbols in glibc * * backtrace_symbols in glibc looks up symbols using dladdr which is limited in * the symbols that it sees. libbacktracesymbols opens the executable and * shared libraries using libbfd and will look up backtrace information using * the symbol table and the dwarf line information. * * Derived from addr2line.c from GNU Binutils by Jeff Muizelaar * * Copyright 2007 Jeff Muizelaar * * addr2line.c -- convert addresses to line number and function name * Copyright 1997, 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. * Contributed by Ulrich Lauther * * This file was part of GNU Binutils. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define _GNU_SOURCE #include #include #include #include typedef struct { const char *file; ElfW(Addr) address; ElfW(Addr) base; void *hdr; } njs_file_match_t; typedef struct { bfd_vma pc; const char *filename; const char *functionname; unsigned int line; njs_bool_t found; asymbol **syms; } njs_translate_address_t; static u_char *njs_process_file(u_char *buf, u_char *end, bfd_vma *addr, const char *file_name); static long njs_read_symtab(bfd *abfd, asymbol ***syms); static u_char *njs_translate_address(u_char *buf, u_char *end, bfd_vma *addr, bfd *abfd, asymbol **syms); static void njs_find_address_in_section(bfd *abfd, asection *section, void *data); static int njs_find_matching_file(struct dl_phdr_info *info, size_t size, void *data); u_char * _njs_addr2line(u_char *buf, u_char *end, void *address) { bfd_vma addr; const char *fname; njs_file_match_t match = { .address = (ElfW(Addr)) address }; bfd_init(); dl_iterate_phdr(njs_find_matching_file, &match); fname = "/proc/self/exe"; if (match.file != NULL && njs_strlen(match.file)) { fname = match.file; } addr = (ElfW(Addr)) address - match.base; return njs_process_file(buf, end, &addr, fname); } static u_char * njs_process_file(u_char *buf, u_char *end, bfd_vma *addr, const char *file_name) { bfd *abfd; char **matching; u_char *p; asymbol **syms; abfd = bfd_openr(file_name, NULL); if (abfd == NULL) { njs_stderror("%s: failed to open while looking for addr2line", file_name); return NULL; } if (bfd_check_format(abfd, bfd_archive)) { njs_stderror("%s: can not get addresses from archive", file_name); return NULL; } if (!bfd_check_format_matches(abfd, bfd_object, &matching)) { njs_stderror("%s: bfd_check_format_matches() failed", bfd_get_filename(abfd)); return NULL; } if (njs_read_symtab(abfd, &syms) <= 0) { njs_stderror("%s: njs_read_symtab() failed", bfd_get_filename(abfd)); return NULL; } p = njs_translate_address(buf, end, addr, abfd, syms); if (syms != NULL) { free(syms); syms = NULL; } bfd_close(abfd); return p; } static long njs_read_symtab(bfd *abfd, asymbol ***syms) { long symcount; unsigned size; if ((bfd_get_file_flags(abfd) & HAS_SYMS) == 0) { return 0; } symcount = bfd_read_minisymbols(abfd, 0, (PTR) syms, &size); if (symcount == 0) { symcount = bfd_read_minisymbols(abfd, 1 /* dynamic */, (PTR) syms, &size); } return symcount; } static u_char * njs_translate_address(u_char *buf, u_char *end, bfd_vma *addr, bfd *abfd, asymbol **syms) { char *h; const char *name; njs_translate_address_t ctx; ctx.pc = *addr; ctx.found = 0; ctx.syms = syms; bfd_map_over_sections(abfd, njs_find_address_in_section, &ctx); if (!ctx.found) { return njs_sprintf(buf, end, "\?\? \t\?\?:0 [0x%p]", addr); } name = ctx.functionname; if (name == NULL || *name == '\0') { name = "??"; } if (ctx.filename != NULL) { h = strrchr(ctx.filename, '/'); if (h != NULL) { ctx.filename = h + 1; } } return njs_sprintf(buf, end, "%s() %s:%ud [0x%p]", name, ctx.filename ? ctx.filename : "??", ctx.line, addr); } static void njs_find_address_in_section(bfd *abfd, asection *section, void *data) { bfd_vma vma; bfd_size_type size; njs_translate_address_t *ctx; ctx = data; if (ctx->found) { return; } if ((bfd_section_flags(section) & SEC_ALLOC) == 0) { return; } vma = bfd_section_vma(section); if (ctx->pc < vma) { return; } size = bfd_section_size(section); if (ctx->pc >= vma + size) { return; } ctx->found = bfd_find_nearest_line(abfd, section, ctx->syms, ctx->pc - vma, &ctx->filename, &ctx->functionname, &ctx->line); } static int njs_find_matching_file(struct dl_phdr_info *info, size_t size, void *data) { long n; const ElfW(Phdr) *phdr; ElfW(Addr) load_base = info->dlpi_addr; njs_file_match_t *match = data; /* * This code is modeled from Gfind_proc_info-lsb.c:callback() * from libunwind. */ phdr = info->dlpi_phdr; for (n = info->dlpi_phnum; --n >= 0; phdr++) { if (phdr->p_type == PT_LOAD) { ElfW(Addr) vaddr = phdr->p_vaddr + load_base; if (match->address >= vaddr && match->address < vaddr + phdr->p_memsz) { /* we found a match */ match->file = info->dlpi_name; match->base = info->dlpi_addr; } } } return 0; } njs-0.8.9/src/njs_addr2line.h000066400000000000000000000010531474132077100160050ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) Nginx, Inc. */ #ifndef _NJS_ADDR2LINE_H_INCLUDED_ #define _NJS_ADDR2LINE_H_INCLUDED_ u_char *_njs_addr2line(u_char *buf, u_char *end, void *address); #if defined(NJS_HAVE_LIBBFD) && defined(NJS_HAVE_DL_ITERATE_PHDR) #define NJS_HAVE_ADDR2LINE 1 #define njs_addr2line(buf, end, addr) _njs_addr2line(buf, end, addr) #else #define njs_addr2line(buf, end, addr) \ njs_sprintf(buf, end, "\?\?() \?\?:0 [0x%p]", addr) #endif #endif /* _NJS_ADDR2LINE_H_INCLUDED_ */ njs-0.8.9/src/njs_arr.c000066400000000000000000000055171474132077100147310ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include njs_arr_t * njs_arr_create(njs_mp_t *mp, njs_uint_t n, size_t size) { njs_arr_t *arr; arr = njs_mp_alloc(mp, sizeof(njs_arr_t) + n * size); if (njs_slow_path(arr == NULL)) { return NULL; } arr->start = (char *) arr + sizeof(njs_arr_t); arr->items = 0; arr->item_size = size; arr->available = n; arr->pointer = 1; arr->separate = 0; arr->mem_pool = mp; return arr; } void * njs_arr_init(njs_mp_t *mp, njs_arr_t *arr, void *start, njs_uint_t n, size_t size) { arr->start = start; arr->items = n; arr->item_size = size; arr->available = n; arr->pointer = 0; arr->separate = 0; arr->mem_pool = mp; if (arr->start == NULL) { arr->separate = 1; arr->items = 0; arr->start = njs_mp_alloc(mp, n * size); } return arr->start; } void njs_arr_destroy(njs_arr_t *arr) { if (arr->separate) { njs_mp_free(arr->mem_pool, arr->start); #if (NJS_DEBUG) arr->start = NULL; arr->items = 0; arr->available = 0; #endif } if (arr->pointer) { njs_mp_free(arr->mem_pool, arr); } } void * njs_arr_add(njs_arr_t *arr) { return njs_arr_add_multiple(arr, 1); } void * njs_arr_add_multiple(njs_arr_t *arr, njs_uint_t items) { void *item, *start, *old; uint32_t n; n = arr->available; items += arr->items; if (items >= n) { if (n < 16) { /* Allocate new arr twice as much as current. */ n *= 2; } else { /* Allocate new arr half as much as current. */ n += n / 2; } if (n < items) { n = items; } start = njs_mp_alloc(arr->mem_pool, n * arr->item_size); if (njs_slow_path(start == NULL)) { return NULL; } arr->available = n; old = arr->start; arr->start = start; memcpy(start, old, arr->items * arr->item_size); if (arr->separate == 0) { arr->separate = 1; } else { njs_mp_free(arr->mem_pool, old); } } item = (char *) arr->start + arr->items * arr->item_size; arr->items = items; return item; } void * njs_arr_zero_add(njs_arr_t *arr) { void *item; item = njs_arr_add(arr); if (njs_fast_path(item != NULL)) { njs_memzero(item, arr->item_size); } return item; } void njs_arr_remove(njs_arr_t *arr, void *item) { u_char *next, *last, *end; uint32_t item_size; item_size = arr->item_size; end = (u_char *) arr->start + item_size * arr->items; last = end - item_size; if (item != last) { next = (u_char *) item + item_size; memmove(item, next, end - next); } arr->items--; } njs-0.8.9/src/njs_arr.h000066400000000000000000000033611474132077100147310ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_ARR_H_INCLUDED_ #define _NJS_ARR_H_INCLUDED_ typedef struct { void *start; /* * A array can hold no more than 2**32 items. * the item size is no more than 64K. */ uint32_t items; uint32_t available; uint16_t item_size; uint8_t pointer; uint8_t separate; njs_mp_t *mem_pool; } njs_arr_t; NJS_EXPORT njs_arr_t *njs_arr_create(njs_mp_t *mp, njs_uint_t n, size_t size); NJS_EXPORT void *njs_arr_init(njs_mp_t *mp, njs_arr_t *arr, void *start, njs_uint_t n, size_t size); NJS_EXPORT void njs_arr_destroy(njs_arr_t *arr); NJS_EXPORT void *njs_arr_add(njs_arr_t *arr); NJS_EXPORT void *njs_arr_add_multiple(njs_arr_t *arr, njs_uint_t n); NJS_EXPORT void *njs_arr_zero_add(njs_arr_t *arr); NJS_EXPORT void njs_arr_remove(njs_arr_t *arr, void *item); #define njs_arr_item(arr, i) \ ((void *) ((char *) (arr)->start + (arr)->item_size * (i))) #define njs_arr_last(arr) \ ((void *) \ ((char *) (arr)->start \ + (arr)->item_size * ((arr)->items - 1))) #define njs_arr_reset(arr) \ (arr)->items = 0; #define njs_arr_is_empty(arr) \ ((arr)->items == 0) njs_inline void * njs_arr_remove_last(njs_arr_t *arr) { arr->items--; return (char *) arr->start + arr->item_size * arr->items; } #endif /* _NJS_ARR_H_INCLUDED_ */ njs-0.8.9/src/njs_array.c000066400000000000000000002366701474132077100152710ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include #define njs_array_func(type) \ ((type << 1) | NJS_ARRAY_FUNC) #define njs_array_arg(type) \ ((type << 1) | NJS_ARRAY_ARG) #define njs_array_type(magic) (magic >> 1) #define njs_array_arg1(magic) (magic & 0x1) typedef enum { NJS_ARRAY_EVERY = 0, NJS_ARRAY_SOME, NJS_ARRAY_INCLUDES, NJS_ARRAY_INDEX_OF, NJS_ARRAY_FOR_EACH, NJS_ARRAY_FIND, NJS_ARRAY_FIND_INDEX, NJS_ARRAY_REDUCE, NJS_ARRAY_FILTER, NJS_ARRAY_MAP, } njs_array_iterator_fun_t; typedef enum { NJS_ARRAY_LAST_INDEX_OF = 0, NJS_ARRAY_REDUCE_RIGHT, } njs_array_reverse_iterator_fun_t; typedef enum { NJS_ARRAY_FUNC = 0, NJS_ARRAY_ARG } njs_array_iterator_arg_t; static njs_int_t njs_array_prototype_slice_copy(njs_vm_t *vm, njs_value_t *this, int64_t start, int64_t length, njs_value_t *retval); njs_array_t * njs_array_alloc(njs_vm_t *vm, njs_bool_t flat, uint64_t length, uint32_t spare) { uint64_t size; njs_int_t ret; njs_array_t *array; njs_value_t value; if (njs_slow_path(length > UINT32_MAX)) { goto overflow; } array = njs_mp_alloc(vm->mem_pool, sizeof(njs_array_t)); if (njs_slow_path(array == NULL)) { goto memory_error; } size = length + spare; if (flat || size <= NJS_ARRAY_LARGE_OBJECT_LENGTH) { array->data = njs_mp_align(vm->mem_pool, sizeof(njs_value_t), size * sizeof(njs_value_t)); if (njs_slow_path(array->data == NULL)) { goto memory_error; } } else { array->data = NULL; } array->start = array->data; njs_lvlhsh_init(&array->object.hash); array->object.shared_hash = vm->shared->array_instance_hash; array->object.__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_ARRAY); array->object.slots = NULL; array->object.type = NJS_ARRAY; array->object.shared = 0; array->object.extensible = 1; array->object.error_data = 0; array->object.fast_array = (array->data != NULL); if (njs_fast_path(array->object.fast_array)) { array->size = size; array->length = length; } else { array->size = 0; array->length = 0; njs_set_array(&value, array); ret = njs_array_length_redefine(vm, &value, length, 1); if (njs_slow_path(ret != NJS_OK)) { return NULL; } } return array; memory_error: njs_memory_error(vm); return NULL; overflow: njs_range_error(vm, "Invalid array length"); return NULL; } void njs_array_destroy(njs_vm_t *vm, njs_array_t *array) { if (array->data != NULL) { njs_mp_free(vm->mem_pool, array->data); } /* TODO: destroy keys. */ njs_mp_free(vm->mem_pool, array); } njs_int_t njs_array_convert_to_slow_array(njs_vm_t *vm, njs_array_t *array) { uint32_t i, length; njs_value_t index, value; njs_object_prop_t *prop; if (njs_slow_path(!array->object.fast_array)) { return NJS_OK; } njs_set_array(&value, array); array->object.fast_array = 0; length = array->length; for (i = 0; i < length; i++) { if (njs_is_valid(&array->start[i])) { njs_uint32_to_string(&index, i); prop = njs_object_property_add(vm, &value, &index, 0); if (njs_slow_path(prop == NULL)) { return NJS_ERROR; } njs_value_assign(njs_prop_value(prop), &array->start[i]); } } njs_mp_free(vm->mem_pool, array->data); array->start = NULL; return NJS_OK; } njs_int_t njs_array_length_redefine(njs_vm_t *vm, njs_value_t *value, uint32_t length, int writable) { njs_object_prop_t *prop; static const njs_value_t string_length = njs_string("length"); if (njs_slow_path(!njs_is_array(value))) { njs_internal_error(vm, "njs_array_length_redefine() " "applied to non-array"); return NJS_ERROR; } prop = njs_object_property_add(vm, value, njs_value_arg(&string_length), 1); if (njs_slow_path(prop == NULL)) { njs_internal_error(vm, "njs_array_length_redefine() " "cannot redefine \"length\""); return NJS_ERROR; } prop->writable = writable; prop->enumerable = 0; prop->configurable = 0; njs_value_number_set(njs_prop_value(prop), length); return NJS_OK; } njs_int_t njs_array_length_set(njs_vm_t *vm, njs_value_t *value, njs_object_prop_t *prev, njs_value_t *setval) { double num, idx; int64_t prev_length; uint32_t i, length; njs_int_t ret; njs_array_t *keys; ret = njs_value_to_number(vm, setval, &num); if (njs_slow_path(ret != NJS_OK)) { return ret; } length = (uint32_t) njs_number_to_length(num); if ((double) length != num) { njs_range_error(vm, "Invalid array length"); return NJS_ERROR; } ret = njs_value_to_length(vm, njs_prop_value(prev), &prev_length); if (njs_slow_path(ret != NJS_OK)) { return ret; } keys = NULL; if (length < prev_length) { keys = njs_array_indices(vm, value); if (njs_slow_path(keys == NULL)) { return NJS_ERROR; } if (keys->length != 0) { i = keys->length - 1; do { idx = njs_string_to_index(&keys->start[i]); if (idx >= length) { ret = njs_value_property_delete(vm, value, &keys->start[i], NULL, 1); if (njs_slow_path(ret == NJS_ERROR)) { goto done; } } } while (i-- != 0); } } ret = njs_array_length_redefine(vm, value, length, prev->writable); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = NJS_OK; done: if (keys != NULL) { njs_array_destroy(vm, keys); } return ret; } static njs_int_t njs_array_copy_within(njs_vm_t *vm, njs_value_t *array, int64_t to_pos, int64_t from_pos, int64_t count, njs_bool_t forward) { int64_t i, from, to, len; njs_int_t ret; njs_array_t *arr; njs_value_t value; njs_assert(to_pos >= 0); njs_assert(from_pos >= 0); if (njs_fast_path(njs_is_fast_array(array) && count > 0)) { arr = njs_array(array); len = arr->length; if (to_pos + count < len && from_pos + count < len) { memmove(&arr->start[to_pos], &arr->start[from_pos], count * sizeof(njs_value_t)); return NJS_OK; } } if (!forward) { from_pos += count - 1; to_pos += count - 1; } for (i = 0; i < count; i++) { if (forward) { from = from_pos + i; to = to_pos + i; } else { from = from_pos - i; to = to_pos - i; } ret = njs_value_property_i64(vm, array, from, &value); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } if (ret == NJS_OK) { ret = njs_value_property_i64_set(vm, array, to, &value); } else { ret = njs_value_property_i64_delete(vm, array, to, NULL); } if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } } return NJS_OK; } njs_int_t njs_array_add(njs_vm_t *vm, njs_array_t *array, njs_value_t *value) { njs_int_t ret; ret = njs_array_expand(vm, array, 0, 1); if (njs_fast_path(ret == NJS_OK)) { array->start[array->length++] = *value; } return ret; } njs_int_t njs_array_string_add(njs_vm_t *vm, njs_array_t *array, const u_char *start, size_t size, size_t length) { njs_int_t ret; ret = njs_array_expand(vm, array, 0, 1); if (njs_fast_path(ret == NJS_OK)) { return njs_string_new(vm, &array->start[array->length++], start, size, length); } return ret; } njs_int_t njs_array_expand(njs_vm_t *vm, njs_array_t *array, uint32_t prepend, uint32_t append) { uint32_t free_before, free_after; uint64_t size; njs_value_t *start, *old; njs_assert(array->object.fast_array); free_before = array->start - array->data; free_after = array->size - array->length - free_before; if (njs_fast_path(free_before >= prepend && free_after >= append)) { return NJS_OK; } size = (uint64_t) prepend + array->length + append; if (size < 16) { size *= 2; } else { size += size / 2; } if (njs_slow_path(size > (UINT32_MAX / sizeof(njs_value_t)))) { goto memory_error; } start = njs_mp_align(vm->mem_pool, sizeof(njs_value_t), size * sizeof(njs_value_t)); if (njs_slow_path(start == NULL)) { goto memory_error; } array->size = size; old = array->data; array->data = start; start += prepend; if (array->length != 0) { memcpy(start, array->start, array->length * sizeof(njs_value_t)); } array->start = start; if (old != NULL) { njs_mp_free(vm->mem_pool, old); } return NJS_OK; memory_error: njs_memory_error(vm); return NJS_ERROR; } static njs_int_t njs_array_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double num; uint32_t size; njs_value_t *value; njs_array_t *array; args = &args[1]; size = nargs - 1; if (size == 1 && njs_is_number(&args[0])) { num = njs_number(&args[0]); size = (uint32_t) njs_number_to_length(num); if ((double) size != num) { njs_range_error(vm, "Invalid array length"); return NJS_ERROR; } args = NULL; } array = njs_array_alloc(vm, size <= NJS_ARRAY_FLAT_MAX_LENGTH, size, NJS_ARRAY_SPARE); if (njs_fast_path(array != NULL)) { if (array->object.fast_array) { value = array->start; if (args == NULL) { while (size != 0) { njs_set_invalid(value); value++; size--; } } else { while (size != 0) { *value++ = *args++; size--; } } } njs_set_array(retval, array); return NJS_OK; } return NJS_ERROR; } static njs_int_t njs_array_from(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t length, i; njs_int_t ret; njs_array_t *array; njs_value_t *this, *items, *mapfn; njs_value_t arguments[3], value, result; njs_function_t *function; mapfn = njs_arg(args, nargs, 2); if (njs_slow_path(!njs_is_function_or_undefined(mapfn))) { njs_type_error(vm, "\"mapfn\" argument is not callable"); return NJS_ERROR; } function = NULL; if (njs_is_function(mapfn)) { function = njs_function(mapfn); } items = njs_arg(args, nargs, 1); ret = njs_value_to_object(vm, items); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_object_length(vm, items, &length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } this = njs_argument(args, 0); if (njs_is_constructor(this)) { njs_set_number(&arguments[0], length); ret = njs_value_construct(vm, this, arguments, 1, &value); if (njs_slow_path(ret != NJS_OK)) { return ret; } } else { array = njs_array_alloc(vm, 1, length, 0); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } njs_set_array(&value, array); } arguments[0] = *njs_arg(args, nargs, 3); for (i = 0; i < length; i++) { ret = njs_value_property_i64(vm, items, i, &result); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } if (function != NULL) { njs_value_assign(&arguments[1], &result); njs_set_number(&arguments[2], i); ret = njs_function_apply(vm, function, arguments, 3, &result); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } ret = njs_value_create_data_prop_i64(vm, &value, i, &result, 0); if (njs_slow_path(ret != NJS_OK)) { return ret; } } ret = njs_object_length_set(vm, &value, length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } njs_value_assign(retval, &value); return NJS_OK; } static njs_int_t njs_array_is_array(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_set_boolean(retval, nargs > 1 && njs_is_array(&args[1])); return NJS_OK; } static njs_int_t njs_array_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { uint32_t length, i; njs_array_t *array; length = nargs > 1 ? nargs - 1 : 0; array = njs_array_alloc(vm, 0, length, NJS_ARRAY_SPARE); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } if (array->object.fast_array) { for (i = 0; i < length; i++) { array->start[i] = args[i + 1]; } } njs_set_array(retval, array); return NJS_OK; } static const njs_object_prop_t njs_array_constructor_properties[] = { NJS_DECLARE_PROP_LENGTH(1), NJS_DECLARE_PROP_NAME("Array"), NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), NJS_DECLARE_PROP_NATIVE("from", njs_array_from, 1, 0), NJS_DECLARE_PROP_NATIVE("isArray", njs_array_is_array, 1, 0), NJS_DECLARE_PROP_NATIVE("of", njs_array_of, 0, 0), }; const njs_object_init_t njs_array_constructor_init = { njs_array_constructor_properties, njs_nitems(njs_array_constructor_properties), }; static njs_int_t njs_array_length(njs_vm_t *vm,njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { double num; int64_t size; uint32_t length; njs_int_t ret; njs_value_t *val; njs_array_t *array; njs_object_t *proto; proto = njs_object(value); if (njs_fast_path(setval == NULL)) { array = njs_object_proto_lookup(proto, NJS_ARRAY, njs_array_t); if (njs_slow_path(array == NULL)) { njs_set_undefined(retval); return NJS_DECLINED; } njs_set_number(retval, array->length); return NJS_OK; } if (proto->type != NJS_ARRAY) { njs_set_undefined(retval); return NJS_DECLINED; } if (njs_slow_path(!njs_is_valid(setval))) { return NJS_DECLINED; } ret = njs_value_to_number(vm, setval, &num); if (njs_slow_path(ret != NJS_OK)) { return ret; } length = (uint32_t) njs_number_to_length(num); if ((double) length != num) { njs_range_error(vm, "Invalid array length"); return NJS_ERROR; } array = (njs_array_t *) proto; if (njs_fast_path(array->object.fast_array)) { if (njs_fast_path(length <= NJS_ARRAY_LARGE_OBJECT_LENGTH)) { size = (int64_t) length - array->length; if (size > 0) { ret = njs_array_expand(vm, array, 0, size); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } val = &array->start[array->length]; do { njs_set_invalid(val); val++; size--; } while (size != 0); } array->length = length; njs_value_assign(retval, setval); return NJS_OK; } ret = njs_array_convert_to_slow_array(vm, array); if (njs_slow_path(ret != NJS_OK)) { return ret; } } prop->type = NJS_PROPERTY; njs_set_number(njs_prop_value(prop), length); njs_value_assign(retval, setval); return NJS_OK; } static njs_int_t njs_array_prototype_slice(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t start, end, length, object_length; njs_int_t ret; njs_value_t *this; this = njs_argument(args, 0); ret = njs_value_to_object(vm, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_object_length(vm, this, &object_length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } length = object_length; ret = njs_value_to_integer(vm, njs_arg(args, nargs, 1), &start); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (start < 0) { start += length; if (start < 0) { start = 0; } } if (start >= length) { start = 0; length = 0; } else { if (njs_is_defined(njs_arg(args, nargs, 2))) { ret = njs_value_to_integer(vm, njs_argument(args, 2), &end); if (njs_slow_path(ret != NJS_OK)) { return ret; } } else { end = length; } if (end < 0) { end += length; } if (length >= end) { length = end - start; if (length < 0) { start = 0; length = 0; } } else { length -= start; } } return njs_array_prototype_slice_copy(vm, this, start, length, retval); } static njs_int_t njs_array_prototype_slice_copy(njs_vm_t *vm, njs_value_t *this, int64_t start, int64_t length, njs_value_t *retval) { size_t size; u_char *dst; uint32_t n; njs_int_t ret; njs_array_t *array, *keys; njs_value_t *value, *last, val, self; const u_char *src, *end; njs_slice_prop_t string_slice; njs_string_prop_t string; keys = NULL; array = njs_array_alloc(vm, 0, length, NJS_ARRAY_SPARE); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } if (njs_slow_path(length == 0)) { ret = NJS_OK; goto done; } n = 0; if (njs_fast_path(array->object.fast_array)) { if (njs_is_string(this) || njs_is_object_string(this)) { if (njs_is_object_string(this)) { this = njs_object_value(this); } string_slice.start = start; string_slice.length = length; string_slice.string_length = njs_string_prop(&string, this); njs_string_slice_string_prop(&string, &string, &string_slice); src = string.start; end = src + string.size; do { value = &array->start[n++]; dst = njs_string_short_start(value); dst = njs_utf8_copy(dst, &src, end); size = dst - njs_string_short_start(value); njs_string_short_set(value, size, 1); length--; } while (length != 0); } else if (njs_is_object(this)) { last = &array->start[length]; for (value = array->start; value < last; value++, start++) { ret = njs_value_property_i64(vm, this, start, value); if (njs_slow_path(ret != NJS_OK)) { if (ret == NJS_ERROR) { return NJS_ERROR; } njs_set_invalid(value); } } } else { /* Primitive types. */ value = array->start; do { njs_set_invalid(value++); length--; } while (length != 0); } ret = NJS_OK; goto done; } njs_set_array(&self, array); if (njs_fast_object(length)) { do { ret = njs_value_property_i64(vm, this, start++, &val); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } if (ret == NJS_OK) { ret = njs_value_property_i64_set(vm, &self, start, &val); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } } length--; } while (length != 0); ret = NJS_OK; goto done; } keys = njs_array_indices(vm, this); if (njs_slow_path(keys == NULL)) { return NJS_ERROR; } for (n = 0; n < keys->length; n++) { ret = njs_value_property(vm, this, &keys->start[n], &val); if (njs_slow_path(ret == NJS_ERROR)) { goto done; } ret = njs_value_property_set(vm, &self, &keys->start[n], &val); if (njs_slow_path(ret == NJS_ERROR)) { goto done; } } ret = NJS_OK; done: if (keys != NULL) { njs_array_destroy(vm, keys); } njs_set_array(retval, array); return ret; } static njs_int_t njs_array_prototype_push(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t length; njs_int_t ret; njs_uint_t i; njs_array_t *array; njs_value_t *this; length = 0; this = njs_argument(args, 0); ret = njs_value_to_object(vm, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_is_fast_array(this)) { array = njs_array(this); if (nargs != 0) { ret = njs_array_expand(vm, array, 0, nargs); if (njs_slow_path(ret != NJS_OK)) { return ret; } for (i = 1; i < nargs; i++) { array->start[array->length++] = args[i]; } } njs_set_number(retval, array->length); return NJS_OK; } ret = njs_object_length(vm, this, &length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (njs_slow_path((length + nargs - 1) > NJS_MAX_LENGTH)) { njs_type_error(vm, "Invalid length"); return NJS_ERROR; } for (i = 1; i < nargs; i++) { ret = njs_value_property_i64_set(vm, this, length++, &args[i]); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } } ret = njs_object_length_set(vm, this, length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } njs_set_number(retval, length); return NJS_OK; } static njs_int_t njs_array_prototype_pop(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t length; njs_int_t ret; njs_value_t *this; this = njs_argument(args, 0); ret = njs_value_to_object(vm, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_object_length(vm, this, &length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (length == 0) { ret = njs_object_length_set(vm, this, length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } njs_set_undefined(retval); return NJS_OK; } ret = njs_value_property_i64(vm, this, --length, retval); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (njs_is_fast_array(this)) { njs_array(this)->length--; } else { ret = njs_value_property_i64_delete(vm, this, length, NULL); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } ret = njs_object_length_set(vm, this, length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } } return NJS_OK; } static njs_int_t njs_array_prototype_unshift(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double idx; int64_t from, to, length; njs_int_t ret; njs_uint_t n; njs_array_t *array, *keys; njs_value_t *this, entry; this = njs_argument(args, 0); length = 0; n = nargs - 1; ret = njs_value_to_object(vm, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_fast_path(njs_is_fast_array(this))) { array = njs_array(this); if (n != 0) { ret = njs_array_expand(vm, array, n, 0); if (njs_slow_path(ret != NJS_OK)) { return ret; } array->length += n; n = nargs; do { n--; array->start--; array->start[0] = args[n]; } while (n > 1); } njs_set_number(retval, array->length); return NJS_OK; } ret = njs_object_length(vm, this, &length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (n == 0) { goto done; } if (njs_slow_path((length + n) > NJS_MAX_LENGTH)) { njs_type_error(vm, "Invalid length"); return NJS_ERROR; } if (!njs_fast_object(length)) { keys = njs_array_indices(vm, this); if (njs_slow_path(keys == NULL)) { return NJS_ERROR; } from = keys->length; while (from > 0) { ret = njs_value_property_delete(vm, this, &keys->start[--from], &entry, 1); if (njs_slow_path(ret == NJS_ERROR)) { njs_array_destroy(vm, keys); return ret; } if (ret == NJS_OK) { idx = njs_string_to_index(&keys->start[from]) + n; ret = njs_value_property_i64_set(vm, this, idx, &entry); if (njs_slow_path(ret == NJS_ERROR)) { njs_array_destroy(vm, keys); return ret; } } } njs_array_destroy(vm, keys); length += n; goto copy; } from = length; length += n; to = length; while (from > 0) { ret = njs_value_property_i64_delete(vm, this, --from, &entry); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } to--; if (ret == NJS_OK) { ret = njs_value_property_i64_set(vm, this, to, &entry); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } } } copy: for (n = 1; n < nargs; n++) { ret = njs_value_property_i64_set(vm, this, n - 1, &args[n]); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } } done: ret = njs_object_length_set(vm, this, length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } njs_set_number(retval, length); return NJS_OK; } static njs_int_t njs_array_prototype_shift(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t i, length; njs_int_t ret; njs_array_t *array; njs_value_t *this, entry; this = njs_argument(args, 0); ret = njs_value_to_object(vm, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_object_length(vm, this, &length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (length == 0) { ret = njs_object_length_set(vm, this, length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } njs_set_undefined(retval); return NJS_OK; } ret = njs_value_property_i64(vm, this, 0, retval); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } if (njs_is_fast_array(this)) { array = njs_array(this); array->start++; array->length--; } else { ret = njs_value_property_i64_delete(vm, this, 0, retval); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } for (i = 1; i < length; i++) { ret = njs_value_property_i64_delete(vm, this, i, &entry); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (ret == NJS_OK) { ret = njs_value_property_i64_set(vm, this, i - 1, &entry); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } } } ret = njs_object_length_set(vm, this, length - 1); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } } return NJS_OK; } static njs_int_t njs_array_prototype_splice(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t i, n, start, length, items, delta, delete; njs_int_t ret; njs_value_t *this, value, del_object; njs_array_t *array, *deleted; this = njs_argument(args, 0); ret = njs_value_to_object(vm, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_object_length(vm, this, &length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } ret = njs_value_to_integer(vm, njs_arg(args, nargs, 1), &start); if (njs_slow_path(ret != NJS_OK)) { return ret; } start = (start < 0) ? njs_max(length + start, 0) : njs_min(start, length); items = 0; delete = 0; if (nargs == 2) { delete = length - start; } else if (nargs > 2) { items = nargs - 3; ret = njs_value_to_integer(vm, njs_arg(args, nargs, 2), &delete); if (njs_slow_path(ret != NJS_OK)) { return ret; } delete = njs_min(njs_max(delete, 0), length - start); } delta = items - delete; if (njs_slow_path((length + delta) > NJS_MAX_LENGTH)) { njs_type_error(vm, "Invalid length"); return NJS_ERROR; } /* TODO: ArraySpeciesCreate(). */ deleted = njs_array_alloc(vm, 0, delete, 0); if (njs_slow_path(deleted == NULL)) { return NJS_ERROR; } njs_set_array(&del_object, deleted); if (njs_fast_path(njs_is_fast_array(this) && deleted->object.fast_array && delete <= deleted->length && start + delete <= njs_array_len(this))) { array = njs_array(this); for (i = 0, n = start; i < delete; i++, n++) { njs_value_assign(&deleted->start[i], &array->start[n]); } } else { for (i = 0, n = start; i < delete; i++, n++) { ret = njs_value_property_i64(vm, this, n, &value); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } if (ret == NJS_OK) { ret = njs_value_create_data_prop_i64(vm, &del_object, i, &value, 0); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } } else { if (deleted->object.fast_array) { njs_set_invalid(&deleted->start[i]); } } } } ret = njs_object_length_set(vm, &del_object, delete); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } if (delta != 0) { ret = njs_array_copy_within(vm, this, start + items, start + delete, length - (start + delete), delta < 0); if (njs_slow_path(ret != NJS_OK)) { return ret; } for (i = length - 1; i >= length + delta; i--) { ret = njs_value_property_i64_delete(vm, this, i, NULL); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } } } /* Copy new items. */ for (i = 3, n = start; items-- > 0; i++, n++) { ret = njs_value_property_i64_set(vm, this, n, &args[i]); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } } ret = njs_object_length_set(vm, this, length + delta); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_set_array(retval, deleted); return NJS_OK; } static njs_int_t njs_array_prototype_to_spliced(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t i, n, r, start, length, to_insert, to_skip, new_length; njs_int_t ret; njs_value_t *this, a, value; njs_array_t *array; this = njs_argument(args, 0); ret = njs_value_to_object(vm, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_object_length(vm, this, &length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } ret = njs_value_to_integer(vm, njs_arg(args, nargs, 1), &start); if (njs_slow_path(ret != NJS_OK)) { return ret; } start = (start < 0) ? njs_max(length + start, 0) : njs_min(start, length); to_insert = 0; to_skip = 0; if (nargs == 2) { to_skip = length - start; } else if (nargs > 2) { to_insert = nargs - 3; ret = njs_value_to_integer(vm, njs_arg(args, nargs, 2), &to_skip); if (njs_slow_path(ret != NJS_OK)) { return ret; } to_skip = njs_min(njs_max(to_skip, 0), length - start); } new_length = length + to_insert - to_skip; if (njs_slow_path(new_length > NJS_MAX_LENGTH)) { njs_type_error(vm, "Invalid length"); return NJS_ERROR; } array = njs_array_alloc(vm, 0, new_length, 0); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } njs_set_array(&a, array); for (i = 0; i < start; i++) { ret = njs_value_property_i64(vm, this, i, &value); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } ret = njs_value_create_data_prop_i64(vm, &a, i, &value, 0); if (njs_slow_path(ret != NJS_OK)) { return ret; } } for (n = 3; to_insert-- > 0; i++, n++) { ret = njs_value_create_data_prop_i64(vm, &a, i, &args[n], 0); if (njs_slow_path(ret != NJS_OK)) { return ret; } } r = start + to_skip; while (i < new_length) { ret = njs_value_property_i64(vm, this, r, &value); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } ret = njs_value_create_data_prop_i64(vm, &a, i, &value, 0); if (njs_slow_path(ret != NJS_OK)) { return ret; } r++; i++; } njs_set_array(retval, array); return NJS_OK; } static njs_int_t njs_array_prototype_reverse(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t length, l, h; njs_int_t ret, lret, hret; njs_value_t value, lvalue, hvalue, *this; this = njs_argument(args, 0); ret = njs_value_to_object(vm, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_object_length(vm, this, &length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (njs_slow_path(length < 2)) { njs_value_assign(retval, this); return NJS_OK; } for (l = 0, h = length - 1; l < h; l++, h--) { lret = njs_value_property_i64(vm, this, l, &lvalue); if (njs_slow_path(lret == NJS_ERROR)) { return NJS_ERROR; } hret = njs_value_property_i64(vm, this, h, &hvalue); if (njs_slow_path(hret == NJS_ERROR)) { return NJS_ERROR; } if (lret == NJS_OK) { ret = njs_value_property_i64_set(vm, this, h, &lvalue); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } if (hret == NJS_OK) { ret = njs_value_property_i64_set(vm, this, l, &hvalue); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } } else { ret = njs_value_property_i64_delete(vm, this, l, &value); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } } } else if (hret == NJS_OK) { ret = njs_value_property_i64_set(vm, this, l, &hvalue); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } ret = njs_value_property_i64_delete(vm, this, h, &value); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } } } njs_value_assign(retval, this); return NJS_OK; } static njs_int_t njs_array_prototype_to_reversed(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t length, i; njs_int_t ret; njs_array_t *array; njs_value_t *this, a, value; this = njs_argument(args, 0); ret = njs_value_to_object(vm, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_value_length(vm, this, &length); if (njs_slow_path(ret != NJS_OK)) { return ret; } array = njs_array_alloc(vm, 0, length, 0); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } njs_set_array(&a, array); for (i = 0; i < length; i++) { ret = njs_value_property_i64(vm, this, length - i - 1, &value); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } ret = njs_value_create_data_prop_i64(vm, &a, i, &value, 0); if (njs_slow_path(ret != NJS_OK)) { return ret; } } njs_set_array(retval, array); return NJS_OK; } njs_int_t njs_array_prototype_to_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_value_t value; njs_lvlhsh_query_t lhq; static const njs_value_t join_string = njs_string("join"); if (njs_is_object(njs_argument(args, 0))) { njs_object_property_init(&lhq, &join_string, NJS_JOIN_HASH); ret = njs_object_property(vm, njs_object(njs_argument(args, 0)), &lhq, &value); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (njs_is_function(&value)) { return njs_function_apply(vm, njs_function(&value), args, nargs, retval); } } return njs_object_prototype_to_string(vm, args, nargs, unused, retval); } static njs_int_t njs_array_prototype_join(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { u_char *p; int64_t i, size, len, length; njs_int_t ret; njs_chb_t chain; njs_value_t *value, *this, entry; njs_string_prop_t separator, string; this = njs_argument(args, 0); ret = njs_value_to_object(vm, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } value = njs_arg(args, nargs, 1); if (njs_slow_path(!njs_is_string(value))) { if (njs_is_undefined(value)) { value = njs_value_arg(&njs_string_comma); } else { ret = njs_value_to_string(vm, value, value); if (njs_slow_path(ret != NJS_OK)) { return ret; } } } (void) njs_string_prop(&separator, value); if (njs_slow_path(!njs_is_object(this))) { njs_value_assign(retval, &njs_string_empty); return NJS_OK; } length = 0; ret = njs_object_length(vm, this, &len); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (njs_slow_path(len == 0)) { njs_value_assign(retval, &njs_string_empty); return NJS_OK; } value = &entry; NJS_CHB_MP_INIT(&chain, vm); for (i = 0; i < len; i++) { ret = njs_value_property_i64(vm, this, i, value); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (!njs_is_null_or_undefined(value)) { if (!njs_is_string(value)) { ret = njs_value_to_chain(vm, &chain, value); if (njs_slow_path(ret < NJS_OK)) { return ret; } length += ret; } else { (void) njs_string_prop(&string, value); length += string.length; njs_chb_append(&chain, string.start, string.size); } } length += separator.length; njs_chb_append(&chain, separator.start, separator.size); if (njs_slow_path(length > NJS_STRING_MAX_LENGTH)) { njs_range_error(vm, "invalid string length"); return NJS_ERROR; } } njs_chb_drop(&chain, separator.size); size = njs_chb_size(&chain); if (njs_slow_path(size < 0)) { njs_memory_error(vm); return NJS_ERROR; } length -= separator.length; p = njs_string_alloc(vm, retval, size, length); if (njs_slow_path(p == NULL)) { return NJS_ERROR; } njs_chb_join_to(&chain, p); njs_chb_destroy(&chain); return NJS_OK; } static int njs_array_indices_handler(const void *first, const void *second, void *ctx) { double num1, num2; int64_t diff, cmp_res; njs_str_t str1, str2; const njs_value_t *val1, *val2; val1 = first; val2 = second; num1 = njs_string_to_index(val1); num2 = njs_string_to_index(val2); if (!isnan(num1) || !isnan(num2)) { if (isnan(num1)) { return 1; } if (isnan(num2)) { return -1; } diff = (int64_t) (num1 - num2); if (diff < 0) { return -1; } return diff != 0; } njs_string_get(val1, &str1); njs_string_get(val2, &str2); cmp_res = strncmp((const char *) str1.start, (const char *) str2.start, njs_min(str1.length, str2.length)); if (cmp_res == 0) { if (str1.length < str2.length) { return -1; } else if (str1.length > str2.length) { return 1; } else { return 0; } } return cmp_res; } int njs_array_indices_handler_nums(const void *first, const void *second, void *ctx) { double num1, num2; int64_t diff; const njs_value_t *val1, *val2; val1 = first; val2 = second; num1 = njs_string_to_index(val1); num2 = njs_string_to_index(val2); if (!isnan(num1) || !isnan(num2)) { if (isnan(num1)) { if (!isnan(num2)) { return 1; } else { return 0; } } if (isnan(num2)) { return -1; } diff = (int64_t) (num1 - num2); if (diff < 0) { return -1; } return diff != 0; } return 0; } njs_array_t * njs_array_keys(njs_vm_t *vm, njs_value_t *object, njs_bool_t all) { njs_array_t *keys; keys = njs_value_own_enumerate(vm, object, NJS_ENUM_KEYS | NJS_ENUM_STRING | (!all ? NJS_ENUM_ENUMERABLE_ONLY : 0)); if (njs_slow_path(keys == NULL)) { return NULL; } njs_qsort(keys->start, keys->length, sizeof(njs_value_t), njs_array_indices_handler, NULL); return keys; } njs_array_t * njs_array_indices(njs_vm_t *vm, njs_value_t *object) { double idx; uint32_t i; njs_array_t *keys; keys = njs_array_keys(vm, object, 1); if (njs_slow_path(keys == NULL)) { return NULL; } for (i = 0; i < keys->length; i++) { idx = njs_string_to_index(&keys->start[i]); if (isnan(idx)) { keys->length = i; break; } } return keys; } njs_inline njs_int_t njs_is_concat_spreadable(njs_vm_t *vm, njs_value_t *value) { njs_int_t ret; njs_value_t retval; static const njs_value_t key = njs_wellknown_symbol(NJS_SYMBOL_IS_CONCAT_SPREADABLE); if (njs_slow_path(!njs_is_object(value))) { return NJS_DECLINED; } ret = njs_value_property(vm, value, njs_value_arg(&key), &retval); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } if (njs_is_defined(&retval)) { return njs_bool(&retval) ? NJS_OK : NJS_DECLINED; } return njs_is_array(value) ? NJS_OK : NJS_DECLINED; } static njs_int_t njs_array_prototype_concat(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double idx; int64_t k, len, length; njs_int_t ret; njs_uint_t i; njs_value_t this, value, *e; njs_array_t *array, *keys; ret = njs_value_to_object(vm, &args[0]); if (njs_slow_path(ret != NJS_OK)) { return ret; } /* TODO: ArraySpeciesCreate(). */ array = njs_array_alloc(vm, 0, 0, NJS_ARRAY_SPARE); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } njs_set_array(&this, array); len = 0; length = 0; for (i = 0; i < nargs; i++) { e = njs_argument(args, i); ret = njs_is_concat_spreadable(vm, e); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } if (ret == NJS_OK) { ret = njs_object_length(vm, e, &len); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (njs_slow_path((length + len) > NJS_MAX_LENGTH)) { njs_type_error(vm, "Invalid length"); return NJS_ERROR; } if (njs_is_fast_array(e) || njs_fast_object(len)) { for (k = 0; k < len; k++, length++) { ret = njs_value_property_i64(vm, e, k, &value); if (njs_slow_path(ret != NJS_OK)) { if (ret == NJS_ERROR) { return NJS_ERROR; } njs_set_invalid(&value); } ret = njs_value_property_i64_set(vm, &this, length, &value); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } } continue; } keys = njs_array_indices(vm, e); if (njs_slow_path(keys == NULL)) { return NJS_ERROR; } for (k = 0; k < keys->length; k++) { ret = njs_value_property(vm, e, &keys->start[k], &value); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (ret == NJS_OK) { idx = njs_string_to_index(&keys->start[k]) + length; ret = njs_value_property_i64_set(vm, &this, idx, &value); if (njs_slow_path(ret == NJS_ERROR)) { njs_array_destroy(vm, keys); return ret; } } } njs_array_destroy(vm, keys); length += len; continue; } if (njs_slow_path((length + len) >= NJS_MAX_LENGTH)) { njs_type_error(vm, "Invalid length"); return NJS_ERROR; } ret = njs_value_property_i64_set(vm, &this, length, e); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } length++; } ret = njs_object_length_set(vm, &this, length); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_value_assign(retval, &this); return NJS_OK; } static njs_int_t njs_array_prototype_fill(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t i, length, start, end; njs_int_t ret; njs_array_t *array; njs_value_t *this, *value; this = njs_argument(args, 0); ret = njs_value_to_object(vm, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_object_length(vm, this, &length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } ret = njs_value_to_integer(vm, njs_arg(args, nargs, 2), &start); if (njs_slow_path(ret != NJS_OK)) { return ret; } start = (start < 0) ? njs_max(length + start, 0) : njs_min(start, length); if (njs_is_undefined(njs_arg(args, nargs, 3))) { end = length; } else { ret = njs_value_to_integer(vm, njs_arg(args, nargs, 3), &end); if (njs_slow_path(ret != NJS_OK)) { return ret; } } end = (end < 0) ? njs_max(length + end, 0) : njs_min(end, length); value = njs_arg(args, nargs, 1); if (njs_is_fast_array(this)) { array = njs_array(this); end = njs_min(end, array->length); for (i = start; i < end; i++) { njs_value_assign(&array->start[i], value); } njs_value_assign(retval, this); return NJS_OK; } while (start < end) { ret = njs_value_property_i64_set(vm, this, start++, value); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } } njs_value_assign(retval, this); return NJS_OK; } njs_inline njs_int_t njs_array_iterator_call(njs_vm_t *vm, njs_iterator_args_t *args, const njs_value_t *entry, uint32_t n, njs_value_t *retval) { njs_value_t arguments[3]; arguments[0] = *entry; njs_set_number(&arguments[1], n); njs_value_assign(&arguments[2], &args->value); return njs_function_call(vm, args->function, njs_value_arg(&args->argument), arguments, 3, retval); } static njs_int_t njs_array_handler_every(njs_vm_t *vm, njs_iterator_args_t *args, njs_value_t *entry, int64_t n, njs_value_t *retval) { njs_int_t ret; if (njs_is_valid(entry)) { ret = njs_array_iterator_call(vm, args, entry, n, retval); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (!njs_is_true(retval)) { njs_value_assign(retval, &njs_value_false); return NJS_DONE; } } return NJS_OK; } static njs_int_t njs_array_handler_some(njs_vm_t *vm, njs_iterator_args_t *args, njs_value_t *entry, int64_t n, njs_value_t *retval) { njs_int_t ret; if (njs_is_valid(entry)) { ret = njs_array_iterator_call(vm, args, entry, n, retval); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_is_true(retval)) { njs_value_assign(retval, &njs_value_true); return NJS_DONE; } } return NJS_OK; } static njs_int_t njs_array_handler_includes(njs_vm_t *vm, njs_iterator_args_t *args, njs_value_t *entry, int64_t n, njs_value_t *retval) { if (!njs_is_valid(entry)) { entry = njs_value_arg(&njs_value_undefined); } if (njs_values_same_zero(njs_value_arg(&args->argument), entry)) { njs_set_true(retval); return NJS_DONE; } return NJS_OK; } static njs_int_t njs_array_handler_index_of(njs_vm_t *vm, njs_iterator_args_t *args, njs_value_t *entry, int64_t n, njs_value_t *retval) { if (njs_values_strict_equal(njs_value_arg(&args->argument), entry)) { njs_set_number(retval, n); return NJS_DONE; } return NJS_OK; } static njs_int_t njs_array_handler_for_each(njs_vm_t *vm, njs_iterator_args_t *args, njs_value_t *entry, int64_t n, njs_value_t *retval) { if (njs_is_valid(entry)) { return njs_array_iterator_call(vm, args, entry, n, retval); } return NJS_OK; } static njs_int_t njs_array_handler_find(njs_vm_t *vm, njs_iterator_args_t *args, njs_value_t *entry, int64_t n, njs_value_t *retval) { njs_int_t ret; njs_value_t copy; if (njs_is_valid(entry)) { copy = *entry; } else { njs_set_undefined(©); } ret = njs_array_iterator_call(vm, args, ©, n, retval); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_is_true(retval)) { njs_value_assign(retval, ©); return NJS_DONE; } return NJS_OK; } static njs_int_t njs_array_handler_find_index(njs_vm_t *vm, njs_iterator_args_t *args, njs_value_t *entry, int64_t n, njs_value_t *retval) { njs_int_t ret; njs_value_t copy; if (njs_is_valid(entry)) { copy = *entry; } else { njs_set_undefined(©); } ret = njs_array_iterator_call(vm, args, ©, n, retval); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_is_true(retval)) { njs_set_number(retval, n); return NJS_DONE; } return NJS_OK; } static njs_int_t njs_array_handler_reduce(njs_vm_t *vm, njs_iterator_args_t *args, njs_value_t *entry, int64_t n, njs_value_t *retval) { njs_int_t ret; njs_value_t arguments[5]; if (njs_is_valid(entry)) { if (!njs_value_is_valid(njs_value_arg(&args->argument))) { njs_value_assign(&args->argument, entry); return NJS_OK; } njs_set_undefined(&arguments[0]); njs_value_assign(&arguments[1], &args->argument); arguments[2] = *entry; njs_set_number(&arguments[3], n); njs_value_assign(&arguments[4], &args->value); ret = njs_function_apply(vm, args->function, arguments, 5, njs_value_arg(&args->argument)); if (njs_slow_path(ret != NJS_OK)) { return ret; } } return NJS_OK; } static njs_int_t njs_array_handler_filter(njs_vm_t *vm, njs_iterator_args_t *args, njs_value_t *entry, int64_t n, njs_value_t *retval) { njs_int_t ret; njs_value_t copy; if (njs_is_valid(entry)) { copy = *entry; ret = njs_array_iterator_call(vm, args, ©, n, retval); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_is_true(retval)) { ret = njs_array_add(vm, args->data, ©); if (njs_slow_path(ret != NJS_OK)) { return ret; } } } return NJS_OK; } static njs_int_t njs_array_handler_map(njs_vm_t *vm, njs_iterator_args_t *args, njs_value_t *entry, int64_t n, njs_value_t *retval) { njs_int_t ret; njs_array_t *array; njs_value_t this; array = args->data; if (array->object.fast_array) { njs_set_invalid(&array->start[n]); } if (njs_is_valid(entry)) { ret = njs_array_iterator_call(vm, args, entry, n, retval); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_is_valid(retval)) { if (array->object.fast_array) { njs_value_assign(&array->start[n], retval); } else { njs_set_array(&this, array); ret = njs_value_property_i64_set(vm, &this, n, retval); if (njs_slow_path(ret != NJS_OK)) { return ret; } } } } return NJS_OK; } static njs_int_t njs_array_prototype_iterator(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t magic, njs_value_t *retval) { int64_t i, length; njs_int_t ret; njs_array_t *array; njs_iterator_args_t iargs; njs_iterator_handler_t handler; njs_value_assign(&iargs.value, njs_argument(args, 0)); ret = njs_value_to_object(vm, njs_value_arg(&iargs.value)); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_value_length(vm, njs_value_arg(&iargs.value), &iargs.to); if (njs_slow_path(ret != NJS_OK)) { return ret; } iargs.from = 0; if (njs_array_arg1(magic) == NJS_ARRAY_FUNC) { if (njs_slow_path(!njs_is_function(njs_arg(args, nargs, 1)))) { njs_type_error(vm, "callback argument is not callable"); return NJS_ERROR; } iargs.function = njs_function(njs_argument(args, 1)); njs_value_assign(&iargs.argument, njs_arg(args, nargs, 2)); } else { njs_value_assign(&iargs.argument, njs_arg(args, nargs, 1)); } switch (njs_array_type(magic)) { case NJS_ARRAY_EVERY: handler = njs_array_handler_every; break; case NJS_ARRAY_SOME: handler = njs_array_handler_some; break; case NJS_ARRAY_INCLUDES: case NJS_ARRAY_INDEX_OF: switch (njs_array_type(magic)) { case NJS_ARRAY_INCLUDES: handler = njs_array_handler_includes; if (iargs.to == 0) { goto done; } break; default: handler = njs_array_handler_index_of; } ret = njs_value_to_integer(vm, njs_arg(args, nargs, 2), &iargs.from); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (iargs.from < 0) { iargs.from += iargs.to; if (iargs.from < 0) { iargs.from = 0; } } break; case NJS_ARRAY_FOR_EACH: handler = njs_array_handler_for_each; break; case NJS_ARRAY_FIND: handler = njs_array_handler_find; break; case NJS_ARRAY_FIND_INDEX: handler = njs_array_handler_find_index; break; case NJS_ARRAY_REDUCE: handler = njs_array_handler_reduce; if (nargs <= 2) { njs_value_invalid_set(njs_value_arg(&iargs.argument)); } break; case NJS_ARRAY_FILTER: case NJS_ARRAY_MAP: default: if (njs_array_type(magic) == NJS_ARRAY_FILTER) { length = 0; handler = njs_array_handler_filter; } else { length = iargs.to; handler = njs_array_handler_map; } array = njs_array_alloc(vm, 0, length, NJS_ARRAY_SPARE); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } if (array->object.fast_array) { for (i = 0; i < length; i++) { njs_set_invalid(&array->start[i]); } } iargs.data = array; break; } ret = njs_object_iterate(vm, &iargs, handler, retval); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (ret == NJS_DONE) { return NJS_OK; } done: /* Default values. */ switch (njs_array_type(magic)) { case NJS_ARRAY_EVERY: njs_set_boolean(retval, 1); break; case NJS_ARRAY_SOME: case NJS_ARRAY_INCLUDES: njs_set_boolean(retval, 0); break; case NJS_ARRAY_INDEX_OF: case NJS_ARRAY_FIND_INDEX: njs_set_number(retval, -1); break; case NJS_ARRAY_FOR_EACH: case NJS_ARRAY_FIND: njs_set_undefined(retval); break; case NJS_ARRAY_REDUCE: if (!njs_value_is_valid(njs_value_arg(&iargs.argument))) { njs_type_error(vm, "Reduce of empty object with no initial value"); return NJS_ERROR; } njs_value_assign(retval, njs_value_arg(&iargs.argument)); break; case NJS_ARRAY_FILTER: case NJS_ARRAY_MAP: default: njs_set_array(retval, iargs.data); } return NJS_OK; } static njs_int_t njs_array_prototype_reverse_iterator(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t type, njs_value_t *retval) { int64_t from, length; njs_int_t ret; njs_iterator_args_t iargs; njs_iterator_handler_t handler; njs_value_assign(&iargs.value, njs_argument(args, 0)); ret = njs_value_to_object(vm, njs_value_arg(&iargs.value)); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_value_assign(&iargs.argument, njs_arg(args, nargs, 1)); ret = njs_value_length(vm, njs_value_arg(&iargs.value), &length); if (njs_slow_path(ret != NJS_OK)) { return ret; } switch (type) { case NJS_ARRAY_LAST_INDEX_OF: handler = njs_array_handler_index_of; if (length == 0) { goto done; } if (nargs > 2) { ret = njs_value_to_integer(vm, njs_arg(args, nargs, 2), &from); if (njs_slow_path(ret != NJS_OK)) { return ret; } } else { from = length - 1; } if (from >= 0) { from = njs_min(from, length - 1); } else if (from < 0) { from += length; } break; case NJS_ARRAY_REDUCE_RIGHT: default: handler = njs_array_handler_reduce; if (njs_slow_path(!njs_is_function(njs_arg(args, nargs, 1)))) { njs_type_error(vm, "callback argument is not callable"); return NJS_ERROR; } iargs.function = njs_function(njs_argument(args, 1)); njs_value_invalid_set(njs_value_arg(&iargs.argument)); if (nargs > 2) { njs_value_assign(&iargs.argument, njs_argument(args, 2)); } else if (length == 0) { goto done; } from = length - 1; break; } iargs.from = from; iargs.to = 0; ret = njs_object_iterate_reverse(vm, &iargs, handler, retval); if (njs_fast_path(ret == NJS_ERROR)) { return NJS_ERROR; } if (ret == NJS_DONE) { return NJS_OK; } done: switch (type) { case NJS_ARRAY_LAST_INDEX_OF: njs_set_number(retval, -1); break; case NJS_ARRAY_REDUCE_RIGHT: default: if (!njs_value_is_valid(njs_value_arg(&iargs.argument))) { njs_type_error(vm, "Reduce of empty object with no initial value"); return NJS_ERROR; } njs_value_assign(retval, njs_value_arg(&iargs.argument)); break; } return NJS_OK; } typedef struct { njs_value_t value; njs_value_t *str; int64_t pos; } njs_array_sort_slot_t; typedef struct { njs_vm_t *vm; njs_function_t *function; njs_bool_t exception; njs_arr_t strings; } njs_array_sort_ctx_t; static int njs_array_compare(const void *a, const void *b, void *c) { double num; njs_int_t ret; njs_value_t arguments[3], retval; njs_array_sort_ctx_t *ctx; njs_array_sort_slot_t *aslot, *bslot; ctx = c; if (ctx->exception) { return 0; } aslot = (njs_array_sort_slot_t *) a; bslot = (njs_array_sort_slot_t *) b; if (ctx->function != NULL) { njs_set_undefined(&arguments[0]); arguments[1] = aslot->value; arguments[2] = bslot->value; ret = njs_function_apply(ctx->vm, ctx->function, arguments, 3, &retval); if (njs_slow_path(ret != NJS_OK)) { goto exception; } ret = njs_value_to_number(ctx->vm, &retval, &num); if (njs_slow_path(ret != NJS_OK)) { goto exception; } if (njs_slow_path(isnan(num))) { return 0; } if (num != 0) { return (num > 0) - (num < 0); } goto compare_same; } if (aslot->str == NULL) { aslot->str = njs_arr_add(&ctx->strings); ret = njs_value_to_string(ctx->vm, aslot->str, &aslot->value); if (njs_slow_path(ret != NJS_OK)) { goto exception; } } if (bslot->str == NULL) { bslot->str = njs_arr_add(&ctx->strings); ret = njs_value_to_string(ctx->vm, bslot->str, &bslot->value); if (njs_slow_path(ret != NJS_OK)) { goto exception; } } ret = njs_string_cmp(aslot->str, bslot->str); if (ret != 0) { return ret; } compare_same: /* Ensures stable sorting. */ return (aslot->pos > bslot->pos) - (aslot->pos < bslot->pos); exception: ctx->exception = 1; return 0; } static njs_array_sort_slot_t * njs_sort_indexed_properties(njs_vm_t *vm, njs_value_t *obj, int64_t length, njs_function_t *compare, njs_bool_t skip_holes, int64_t *nslots, int64_t *nunds) { int64_t i, ilength, nlen; njs_int_t ret; njs_array_t *array, *keys; njs_value_t *start, *strings, key; njs_array_sort_ctx_t ctx; njs_array_sort_slot_t *p, *end, *slots, *newslots; njs_assert(length != 0); slots = NULL; keys = NULL; ctx.vm = vm; ctx.function = compare; ctx.strings.separate = 0; ctx.strings.pointer = 0; ctx.exception = 0; if (njs_fast_path(njs_is_fast_array(obj))) { array = njs_array(obj); start = array->start; slots = njs_mp_alloc(vm->mem_pool, sizeof(njs_array_sort_slot_t) * length); if (njs_slow_path(slots == NULL)) { njs_memory_error(vm); return NULL; } *nunds = 0; p = slots; for (i = 0; i < length; i++) { if (njs_fast_path(njs_is_valid(&start[i]))) { /* not an empty value at index i. */ njs_value_assign(&p->value, &start[i]); } else { njs_uint32_to_string(&key, i); ret = njs_value_property(vm, obj, &key, &p->value); if (njs_slow_path(ret == NJS_ERROR)) { goto exception; } if (ret == NJS_DECLINED && skip_holes) { continue; } } if (njs_slow_path(njs_is_undefined(&p->value))) { (*nunds)++; continue; } p->pos = i; p->str = NULL; p++; } *nslots = p - slots; } else { if (skip_holes) { keys = njs_array_indices(vm, obj); if (njs_slow_path(keys == NULL)) { return NULL; } slots = njs_mp_alloc(vm->mem_pool, sizeof(njs_array_sort_slot_t) * keys->length); if (njs_slow_path(slots == NULL)) { njs_memory_error(vm); ret = NJS_ERROR; goto exception; } *nunds = 0; p = slots; ilength = njs_min(keys->length, length); for (i = 0; i < ilength; i++) { ret = njs_value_property(vm, obj, &keys->start[i], &p->value); if (njs_slow_path(ret == NJS_ERROR)) { goto exception; } if (ret == NJS_DECLINED) { continue; } if (njs_is_undefined(&p->value)) { (*nunds)++; continue; } p->pos = i; p->str = NULL; p++; } *nslots = p - slots; } else { /* !skip_holes */ nlen = njs_min(length, 8); slots = njs_mp_alloc(vm->mem_pool, sizeof(njs_array_sort_slot_t) * nlen); if (njs_slow_path(slots == NULL)) { njs_memory_error(vm); ret = NJS_ERROR; goto exception; } p = slots; end = slots + nlen; for (i = 0; i < length; i++) { if (p >= end) { nlen = njs_min(njs_max((p - slots) * 2, 8), length); newslots = njs_mp_alloc(vm->mem_pool, sizeof(njs_array_sort_slot_t) * nlen); if (njs_slow_path(newslots == NULL)) { njs_memory_error(vm); ret = NJS_ERROR; goto exception; } p = (void *) njs_cpymem(newslots, slots, sizeof(njs_array_sort_slot_t) * (p - slots)); njs_mp_free(vm->mem_pool, slots); slots = newslots; end = slots + nlen; } ret = njs_value_property_i64(vm, obj, i, &p->value); if (njs_slow_path(ret == NJS_ERROR)) { ret = NJS_ERROR; goto exception; } if (njs_is_undefined(&p->value)) { continue; } p->pos = i; p->str = NULL; p++; } *nslots = p - slots; *nunds = length - *nslots; } } strings = njs_arr_init(vm->mem_pool, &ctx.strings, NULL, *nslots + 1, sizeof(njs_value_t)); if (njs_slow_path(strings == NULL)) { njs_mp_free(vm->mem_pool, slots); return NULL; } njs_qsort(slots, *nslots, sizeof(njs_array_sort_slot_t), njs_array_compare, &ctx); ret = NJS_OK; njs_arr_destroy(&ctx.strings); exception: if (keys != NULL) { njs_array_destroy(vm, keys); } if ((ctx.exception || ret == NJS_ERROR) && slots != NULL) { njs_mp_free(vm->mem_pool, slots); return NULL; } return slots; } static njs_int_t njs_array_prototype_sort(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t i, nslots, nunds, length; njs_int_t ret; njs_value_t *this, *comparefn; njs_function_t *compare; njs_array_sort_slot_t *slots; comparefn = njs_arg(args, nargs, 1); if (njs_is_defined(comparefn)) { if (njs_slow_path(!njs_is_function(comparefn))) { njs_type_error(vm, "comparefn must be callable or undefined"); return NJS_ERROR; } compare = njs_function(comparefn); } else { compare = NULL; } this = njs_argument(args, 0); ret = njs_value_to_object(vm, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_value_length(vm, this, &length); if (njs_slow_path(ret != NJS_OK)) { return ret; } slots = NULL; if (length == 0) { goto done; } /* Satisfy gcc -O3 */ nslots = 0; slots = njs_sort_indexed_properties(vm, this, length, compare, 1, &nslots, &nunds); if (njs_slow_path(slots == NULL)) { ret = NJS_ERROR; goto exception; } njs_assert(length >= (nslots + nunds)); for (i = 0; i < nslots; i++) { ret = njs_value_property_i64_set(vm, this, i, &slots[i].value); if (njs_slow_path(ret == NJS_ERROR)) { goto exception; } } for (i = nslots; nunds-- > 0; i++) { ret = njs_value_property_i64_set(vm, this, i, njs_value_arg(&njs_value_undefined)); if (njs_slow_path(ret == NJS_ERROR)) { goto exception; } } for (; i < length; i++) { ret = njs_value_property_i64_delete(vm, this, i, NULL); if (njs_slow_path(ret == NJS_ERROR)) { goto exception; } } done: njs_value_assign(retval, this); ret = NJS_OK; exception: if (slots != NULL) { njs_mp_free(vm->mem_pool, slots); } return ret; } static njs_int_t njs_array_prototype_to_sorted(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t i, nslots, nunds, length; njs_int_t ret; njs_array_t *array; njs_value_t *this, *comparefn, a; njs_function_t *compare; njs_array_sort_slot_t *slots; comparefn = njs_arg(args, nargs, 1); if (njs_is_defined(comparefn)) { if (njs_slow_path(!njs_is_function(comparefn))) { njs_type_error(vm, "comparefn must be callable or undefined"); return NJS_ERROR; } compare = njs_function(comparefn); } else { compare = NULL; } this = njs_argument(args, 0); ret = njs_value_to_object(vm, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_value_length(vm, this, &length); if (njs_slow_path(ret != NJS_OK)) { return ret; } array = njs_array_alloc(vm, 0, length, 0); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } if (length != 0) { slots = njs_sort_indexed_properties(vm, this, length, compare, 0, &nslots, &nunds); if (njs_slow_path(slots == NULL)) { ret = NJS_ERROR; goto exception; } } else { slots = NULL; length = 0; nslots = 0; nunds = 0; } njs_assert(length == (nslots + nunds)); njs_set_array(&a, array); for (i = 0; i < nslots; i++) { ret = njs_value_create_data_prop_i64(vm, &a, i, &slots[i].value, 0); if (njs_slow_path(ret != NJS_OK)) { goto exception; } } for (; i < length; i++) { ret = njs_value_create_data_prop_i64(vm, &a, i, njs_value_arg(&njs_value_undefined), 0); if (njs_slow_path(ret != NJS_OK)) { goto exception; } } ret = NJS_OK; njs_set_array(retval, array); exception: if (slots != NULL) { njs_mp_free(vm->mem_pool, slots); } return ret; } static njs_int_t njs_array_prototype_copy_within(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t length, count, to, from, end; njs_int_t ret; njs_value_t *this, *value; this = njs_argument(args, 0); ret = njs_value_to_object(vm, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_value_length(vm, this, &length); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_value_to_integer(vm, njs_arg(args, nargs, 1), &to); if (njs_slow_path(ret != NJS_OK)) { return ret; } to = (to < 0) ? njs_max(length + to, 0) : njs_min(to, length); ret = njs_value_to_integer(vm, njs_arg(args, nargs, 2), &from); if (njs_slow_path(ret != NJS_OK)) { return ret; } from = (from < 0) ? njs_max(length + from, 0) : njs_min(from, length); value = njs_arg(args, nargs, 3); if (njs_is_undefined(value)) { end = length; } else { ret = njs_value_to_integer(vm, value, &end); if (njs_slow_path(ret != NJS_OK)) { return ret; } } end = (end < 0) ? njs_max(length + end, 0) : njs_min(end, length); count = njs_min(end - from, length - to); njs_value_assign(retval, this); return njs_array_copy_within(vm, this, to, from, count, !(from < to && to < from + count)); } static njs_int_t njs_array_prototype_iterator_obj(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t kind, njs_value_t *retval) { njs_int_t ret; njs_value_t *this; this = njs_argument(args, 0); ret = njs_value_to_object(vm, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } return njs_array_iterator_create(vm, this, retval, kind); } static const njs_object_prop_t njs_array_prototype_properties[] = { NJS_DECLARE_PROP_HANDLER("length", njs_array_length, 0, 0, NJS_OBJECT_PROP_VALUE_W), NJS_DECLARE_PROP_HANDLER("constructor", njs_object_prototype_create_constructor, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_NATIVE("concat", njs_array_prototype_concat, 1, 0), NJS_DECLARE_PROP_NATIVE("copyWithin", njs_array_prototype_copy_within, 2, 0), NJS_DECLARE_PROP_NATIVE("entries", njs_array_prototype_iterator_obj, 0, NJS_ENUM_BOTH), NJS_DECLARE_PROP_NATIVE("every", njs_array_prototype_iterator, 1, njs_array_func(NJS_ARRAY_EVERY)), NJS_DECLARE_PROP_NATIVE("fill", njs_array_prototype_fill, 1, 0), NJS_DECLARE_PROP_NATIVE("filter", njs_array_prototype_iterator, 1, njs_array_func(NJS_ARRAY_FILTER)), NJS_DECLARE_PROP_NATIVE("find", njs_array_prototype_iterator, 1, njs_array_func(NJS_ARRAY_FIND)), NJS_DECLARE_PROP_NATIVE("findIndex", njs_array_prototype_iterator, 1, njs_array_func(NJS_ARRAY_FIND_INDEX)), NJS_DECLARE_PROP_NATIVE("forEach", njs_array_prototype_iterator, 1, njs_array_func(NJS_ARRAY_FOR_EACH)), NJS_DECLARE_PROP_NATIVE("includes", njs_array_prototype_iterator, 1, njs_array_arg(NJS_ARRAY_INCLUDES)), NJS_DECLARE_PROP_NATIVE("indexOf", njs_array_prototype_iterator, 1, njs_array_arg(NJS_ARRAY_INDEX_OF)), NJS_DECLARE_PROP_NATIVE("join", njs_array_prototype_join, 1, 0), NJS_DECLARE_PROP_NATIVE("keys", njs_array_prototype_iterator_obj, 0, NJS_ENUM_KEYS), NJS_DECLARE_PROP_NATIVE("lastIndexOf", njs_array_prototype_reverse_iterator, 1, NJS_ARRAY_LAST_INDEX_OF), NJS_DECLARE_PROP_NATIVE("map", njs_array_prototype_iterator, 1, njs_array_func(NJS_ARRAY_MAP)), NJS_DECLARE_PROP_NATIVE("pop", njs_array_prototype_pop, 0, 0), NJS_DECLARE_PROP_NATIVE("push", njs_array_prototype_push, 1, 0), NJS_DECLARE_PROP_NATIVE("reduce", njs_array_prototype_iterator, 1, njs_array_func(NJS_ARRAY_REDUCE)), NJS_DECLARE_PROP_NATIVE("reduceRight", njs_array_prototype_reverse_iterator, 1, njs_array_func(NJS_ARRAY_REDUCE_RIGHT)), NJS_DECLARE_PROP_NATIVE("reverse", njs_array_prototype_reverse, 0, 0), NJS_DECLARE_PROP_NATIVE("shift", njs_array_prototype_shift, 0, 0), NJS_DECLARE_PROP_NATIVE("slice", njs_array_prototype_slice, 2, 0), NJS_DECLARE_PROP_NATIVE("some", njs_array_prototype_iterator, 1, njs_array_func(NJS_ARRAY_SOME)), NJS_DECLARE_PROP_NATIVE("sort", njs_array_prototype_sort, 1, 0), NJS_DECLARE_PROP_NATIVE("splice", njs_array_prototype_splice, 2, 0), NJS_DECLARE_PROP_NATIVE("toReversed", njs_array_prototype_to_reversed, 0, 0), NJS_DECLARE_PROP_NATIVE("toSorted", njs_array_prototype_to_sorted, 1, 0), NJS_DECLARE_PROP_NATIVE("toSpliced", njs_array_prototype_to_spliced, 2, 0), NJS_DECLARE_PROP_NATIVE("toString", njs_array_prototype_to_string, 0, 0), NJS_DECLARE_PROP_NATIVE("unshift", njs_array_prototype_unshift, 1, 0), NJS_DECLARE_PROP_NATIVE("values", njs_array_prototype_iterator_obj, 0, NJS_ENUM_VALUES), { .type = NJS_PROPERTY, .name = njs_wellknown_symbol(NJS_SYMBOL_ITERATOR), .u.value = njs_native_function2(njs_array_prototype_iterator_obj, 0, NJS_ENUM_VALUES), .writable = 1, .configurable = 1, }, }; const njs_object_init_t njs_array_prototype_init = { njs_array_prototype_properties, njs_nitems(njs_array_prototype_properties), }; const njs_object_prop_t njs_array_instance_properties[] = { NJS_DECLARE_PROP_HANDLER("length", njs_array_length, 0, 0, NJS_OBJECT_PROP_VALUE_W), }; const njs_object_init_t njs_array_instance_init = { njs_array_instance_properties, njs_nitems(njs_array_instance_properties), }; const njs_object_type_init_t njs_array_type_init = { .constructor = njs_native_ctor(njs_array_constructor, 1, 0), .constructor_props = &njs_array_constructor_init, .prototype_props = &njs_array_prototype_init, .prototype_value = { .object = { .type = NJS_ARRAY, .fast_array = 1 } }, }; njs-0.8.9/src/njs_array.h000066400000000000000000000037751474132077100152740ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_ARRAY_H_INCLUDED_ #define _NJS_ARRAY_H_INCLUDED_ #define NJS_ARRAY_MAX_INDEX 0xffffffff #define NJS_ARRAY_INVALID_INDEX NJS_ARRAY_MAX_INDEX #define NJS_ARRAY_SPARE 8 #define NJS_ARRAY_FAST_OBJECT_LENGTH (1024) #define NJS_ARRAY_LARGE_OBJECT_LENGTH (32768) #define NJS_ARRAY_FLAT_MAX_LENGTH (1048576) #define njs_fast_object(_sz) ((_sz) <= NJS_ARRAY_FAST_OBJECT_LENGTH) njs_array_t *njs_array_alloc(njs_vm_t *vm, njs_bool_t flat, uint64_t length, uint32_t spare); void njs_array_destroy(njs_vm_t *vm, njs_array_t *array); njs_int_t njs_array_add(njs_vm_t *vm, njs_array_t *array, njs_value_t *value); njs_int_t njs_array_convert_to_slow_array(njs_vm_t *vm, njs_array_t *array); njs_int_t njs_array_length_redefine(njs_vm_t *vm, njs_value_t *value, uint32_t length, int writable); njs_int_t njs_array_length_set(njs_vm_t *vm, njs_value_t *value, njs_object_prop_t *prev, njs_value_t *setval); njs_array_t *njs_array_keys(njs_vm_t *vm, njs_value_t *array, njs_bool_t all); njs_array_t *njs_array_indices(njs_vm_t *vm, njs_value_t *object); njs_int_t njs_array_string_add(njs_vm_t *vm, njs_array_t *array, const u_char *start, size_t size, size_t length); njs_int_t njs_array_expand(njs_vm_t *vm, njs_array_t *array, uint32_t prepend, uint32_t append); njs_int_t njs_array_prototype_to_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); int njs_array_indices_handler_nums(const void *first, const void *second, void *ctx); njs_inline njs_value_t * njs_array_push(njs_vm_t *vm, njs_array_t *array) { njs_int_t ret; ret = njs_array_expand(vm, array, 0, 1); if (njs_slow_path(ret != NJS_OK)) { return NULL; } return &array->start[array->length++]; } extern const njs_object_init_t njs_array_instance_init; extern const njs_object_type_init_t njs_array_type_init; #endif /* _NJS_ARRAY_H_INCLUDED_ */ njs-0.8.9/src/njs_array_buffer.c000066400000000000000000000160071474132077100166100ustar00rootroot00000000000000/* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include njs_array_buffer_t * njs_array_buffer_alloc(njs_vm_t *vm, uint64_t size, njs_bool_t zeroing) { njs_object_t *proto; njs_array_buffer_t *array; if (njs_slow_path(size > UINT32_MAX)) { goto overflow; } array = njs_mp_alloc(vm->mem_pool, sizeof(njs_array_buffer_t)); if (njs_slow_path(array == NULL)) { goto memory_error; } if (zeroing) { array->u.data = njs_mp_zalloc(vm->mem_pool, size); } else { array->u.data = njs_mp_alloc(vm->mem_pool, size); } if (njs_slow_path(array->u.data == NULL)) { goto memory_error; } proto = njs_vm_proto(vm, NJS_OBJ_TYPE_ARRAY_BUFFER); njs_lvlhsh_init(&array->object.hash); njs_lvlhsh_init(&array->object.shared_hash); array->object.__proto__ = proto; array->object.slots = NULL; array->object.type = NJS_ARRAY_BUFFER; array->object.shared = 0; array->object.extensible = 1; array->object.error_data = 0; array->object.fast_array = 0; array->size = size; return array; memory_error: njs_memory_error(vm); return NULL; overflow: njs_range_error(vm, "Invalid array length"); return NULL; } static njs_int_t njs_array_buffer_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { uint64_t size; njs_int_t ret; njs_value_t *value; njs_array_buffer_t *array; if (!vm->top_frame->ctor) { njs_type_error(vm, "Constructor ArrayBuffer requires 'new'"); return NJS_ERROR; } size = 0; value = njs_arg(args, nargs, 1); ret = njs_value_to_index(vm, value, &size); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } array = njs_array_buffer_alloc(vm, size, 1); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } njs_set_array_buffer(retval, array); return NJS_OK; } static njs_int_t njs_array_buffer_get_this(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_value_assign(retval, njs_argument(args, 0)); return NJS_OK; } static njs_int_t njs_array_buffer_is_view(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_set_boolean(retval, njs_is_typed_array(njs_arg(args, nargs, 1))); return NJS_OK; } njs_int_t njs_array_buffer_writable(njs_vm_t *vm, njs_array_buffer_t *buffer) { void *dst; if (!buffer->object.shared) { return NJS_OK; } dst = njs_mp_alloc(vm->mem_pool, buffer->size); if (njs_slow_path(dst == NULL)) { njs_memory_error(vm); return NJS_ERROR; } memcpy(dst, buffer->u.data, buffer->size); buffer->object.shared = 0; buffer->u.data = dst; return NJS_OK; } static const njs_object_prop_t njs_array_buffer_constructor_properties[] = { NJS_DECLARE_PROP_LENGTH(1), NJS_DECLARE_PROP_NAME("ArrayBuffer"), NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), { .type = NJS_ACCESSOR, .name = njs_wellknown_symbol(NJS_SYMBOL_SPECIES), .u.accessor = njs_getter(njs_array_buffer_get_this, 0), .writable = NJS_ATTRIBUTE_UNSET, .configurable = 1, }, NJS_DECLARE_PROP_NATIVE("isView", njs_array_buffer_is_view, 1, 0), }; const njs_object_init_t njs_array_buffer_constructor_init = { njs_array_buffer_constructor_properties, njs_nitems(njs_array_buffer_constructor_properties), }; static njs_int_t njs_array_buffer_prototype_byte_length(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_value_t *value; njs_array_buffer_t *array; value = njs_argument(args, 0); if (!njs_is_array_buffer(value)) { njs_type_error(vm, "Method ArrayBuffer.prototype.byteLength called " "on incompatible receiver"); return NJS_ERROR; } array = njs_array_buffer(value); if (njs_slow_path(njs_is_detached_buffer(array))) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } njs_set_number(retval, array->size); return NJS_OK; } static njs_int_t njs_array_buffer_prototype_slice(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t len, start, end; njs_int_t ret; njs_value_t *value; njs_array_buffer_t *this, *buffer; value = njs_argument(args, 0); if (!njs_is_array_buffer(value)) { njs_type_error(vm, "Method ArrayBuffer.prototype.slice called " "on incompatible receiver"); return NJS_ERROR; } this = njs_array_buffer(value); len = njs_array_buffer_size(this); end = len; value = njs_arg(args, nargs, 1); ret = njs_value_to_integer(vm, value, &start); if (njs_slow_path(ret != NJS_OK)) { return ret; } value = njs_arg(args, nargs, 2); if (!njs_is_undefined(value)) { ret = njs_value_to_integer(vm, value, &end); if (njs_slow_path(ret != NJS_OK)) { return ret; } } buffer = njs_array_buffer_slice(vm, this, start, end); if (njs_slow_path(buffer == NULL)) { return NJS_ERROR; } njs_set_array_buffer(retval, buffer); return NJS_OK; } njs_int_t njs_array_buffer_detach(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_value_t *value; njs_array_buffer_t *buffer; value = njs_arg(args, nargs, 1); if (njs_slow_path(!njs_is_array_buffer(value))) { njs_type_error(vm, "\"this\" is not an ArrayBuffer"); return NJS_ERROR; } buffer = njs_array_buffer(value); buffer->u.data = NULL; buffer->size = 0; njs_set_null(retval); return NJS_OK; } static const njs_object_prop_t njs_array_buffer_prototype_properties[] = { NJS_DECLARE_PROP_HANDLER("constructor", njs_object_prototype_create_constructor, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_GETTER("byteLength", njs_array_buffer_prototype_byte_length, 0), NJS_DECLARE_PROP_NATIVE("slice", njs_array_buffer_prototype_slice, 2, 0), { .type = NJS_PROPERTY, .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG), .u.value = njs_string("ArrayBuffer"), .configurable = 1, }, }; const njs_object_init_t njs_array_buffer_prototype_init = { njs_array_buffer_prototype_properties, njs_nitems(njs_array_buffer_prototype_properties), }; const njs_object_type_init_t njs_array_buffer_type_init = { .constructor = njs_native_ctor(njs_array_buffer_constructor, 1, 0), .prototype_props = &njs_array_buffer_prototype_init, .constructor_props = &njs_array_buffer_constructor_init, .prototype_value = { .object = { .type = NJS_OBJECT } }, }; njs-0.8.9/src/njs_array_buffer.h000066400000000000000000000024501474132077100166120ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_ARRAY_BUFFER_H_INCLUDED_ #define _NJS_ARRAY_BUFFER_H_INCLUDED_ #define njs_array_buffer_size(buffer) \ ((buffer)->size) njs_array_buffer_t *njs_array_buffer_alloc(njs_vm_t *vm, uint64_t size, njs_bool_t zeroing); njs_int_t njs_array_buffer_writable(njs_vm_t *vm, njs_array_buffer_t *buffer); NJS_EXPORT njs_int_t njs_array_buffer_detach(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); njs_inline njs_array_buffer_t * njs_array_buffer_slice(njs_vm_t *vm, njs_array_buffer_t *this, int64_t start, int64_t end) { int64_t len, new_len, first, final; njs_array_buffer_t *new_buffer; len = njs_array_buffer_size(this); first = (start < 0) ? njs_max(len + start, 0) : njs_min(start, len); final = (end < 0) ? njs_max(len + end, 0) : njs_min(end, len); new_len = njs_max(final - first, 0); new_buffer = njs_array_buffer_alloc(vm, new_len, 1); if (new_buffer == NULL) { return NULL; } memcpy(new_buffer->u.u8, &this->u.u8[first], new_len); return new_buffer; } extern const njs_object_type_init_t njs_array_buffer_type_init; #endif /* _NJS_ARRAY_BUFFER_H_INCLUDED_ */ njs-0.8.9/src/njs_assert.h000066400000000000000000000026331474132077100154470ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_ASSERT_H_INCLUDED_ #define _NJS_ASSERT_H_INCLUDED_ #if (NJS_DEBUG) #define njs_assert(condition) \ do { \ if (!(condition)) { \ njs_stderror("Assertion \"%s\" failed at %s:%d\n", #condition, \ __FILE__, __LINE__); \ abort(); \ } \ } while (0) #define njs_assert_msg(condition, fmt, ...) \ do { \ if (!(condition)) { \ njs_stderror(fmt, ##__VA_ARGS__); \ njs_stderror(" at %s:%d\n", __FILE__, __LINE__); \ abort(); \ } \ } while (0) #else #define njs_assert(condition) #define njs_assert_msg(condition, fmt, ...) #endif #endif /* _NJS_ASSERT_H_INCLUDED_ */ njs-0.8.9/src/njs_async.c000066400000000000000000000133761474132077100152640ustar00rootroot00000000000000 /* * Copyright (C) Alexander Borisov * Copyright (C) Nginx, Inc. */ #include static void njs_async_context_free(njs_vm_t *vm, njs_async_ctx_t *ctx); njs_int_t njs_async_function_frame_invoke(njs_vm_t *vm, njs_value_t *retval) { njs_int_t ret; njs_value_t ctor; njs_promise_capability_t *capability; njs_set_function(&ctor, &njs_vm_ctor(vm, NJS_OBJ_TYPE_PROMISE)); capability = njs_promise_new_capability(vm, &ctor); if (njs_slow_path(capability == NULL)) { return NJS_ERROR; } ret = njs_function_lambda_call(vm, retval, capability); if (ret == NJS_OK) { ret = njs_function_call(vm, njs_function(&capability->resolve), &njs_value_undefined, retval, 1, retval); } else if (ret == NJS_AGAIN) { ret = NJS_OK; } else if (ret == NJS_ERROR) { if (njs_is_memory_error(vm, &vm->exception)) { return NJS_ERROR; } *retval = njs_vm_exception(vm); ret = njs_function_call(vm, njs_function(&capability->reject), &njs_value_undefined, retval, 1, retval); } njs_value_assign(retval, &capability->promise); return ret; } njs_int_t njs_await_fulfilled(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t exception, njs_value_t *retval) { njs_int_t ret; njs_value_t **cur_local, **cur_closures, *value, result; njs_frame_t *frame, *async_frame; njs_async_ctx_t *ctx; njs_native_frame_t *top, *async; ctx = vm->top_frame->function->context; value = njs_arg(args, nargs, 1); async_frame = ctx->await; async = &async_frame->native; async->previous = vm->top_frame; cur_local = vm->levels[NJS_LEVEL_LOCAL]; cur_closures = vm->levels[NJS_LEVEL_CLOSURE]; top = vm->top_frame; frame = vm->active_frame; vm->levels[NJS_LEVEL_LOCAL] = async->local; vm->levels[NJS_LEVEL_CLOSURE] = njs_function_closures(async->function); vm->top_frame = async; vm->active_frame = async_frame; if (exception) { njs_vm_throw(vm, value); } else { *njs_scope_value(vm, ctx->index) = *value; } ret = njs_vmcode_interpreter(vm, ctx->pc, &result, ctx->capability, ctx); vm->levels[NJS_LEVEL_LOCAL] = cur_local; vm->levels[NJS_LEVEL_CLOSURE] = cur_closures; vm->top_frame = top; vm->active_frame = frame; if (ret == NJS_OK) { ret = njs_function_call(vm, njs_function(&ctx->capability->resolve), &njs_value_undefined, &result, 1, retval); njs_async_context_free(vm, ctx); } else if (ret == NJS_AGAIN) { ret = NJS_OK; } else if (ret == NJS_ERROR) { if (njs_is_memory_error(vm, &vm->exception)) { return NJS_ERROR; } result = njs_vm_exception(vm); (void) njs_function_call(vm, njs_function(&ctx->capability->reject), &njs_value_undefined, &result, 1, retval); njs_async_context_free(vm, ctx); return NJS_ERROR; } return ret; } njs_int_t njs_await_rejected(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_value_t *value; njs_async_ctx_t *ctx; ctx = vm->top_frame->function->context; value = njs_arg(args, nargs, 1); if (ctx->await->native.pc == ctx->pc) { /* No catch block was set before await. */ (void) njs_function_call(vm, njs_function(&ctx->capability->reject), &njs_value_undefined, value, 1, retval); njs_async_context_free(vm, ctx); return NJS_ERROR; } /* ctx->await->native.pc points to a catch block here. */ ctx->pc = ctx->await->native.pc; return njs_await_fulfilled(vm, args, nargs, 1, retval); } static void njs_async_context_free(njs_vm_t *vm, njs_async_ctx_t *ctx) { njs_mp_free(vm->mem_pool, ctx->capability); njs_mp_free(vm->mem_pool, ctx); } static const njs_object_prop_t njs_async_constructor_properties[] = { NJS_DECLARE_PROP_LENGTH(1), NJS_DECLARE_PROP_NAME("AsyncFunction"), NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), }; const njs_object_init_t njs_async_constructor_init = { njs_async_constructor_properties, njs_nitems(njs_async_constructor_properties), }; static const njs_object_prop_t njs_async_prototype_properties[] = { { .type = NJS_PROPERTY, .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG), .u.value = njs_string("AsyncFunction"), .configurable = 1, }, NJS_DECLARE_PROP_HANDLER("constructor", njs_object_prototype_create_constructor, 0, 0, NJS_OBJECT_PROP_VALUE_CW), }; const njs_object_init_t njs_async_prototype_init = { njs_async_prototype_properties, njs_nitems(njs_async_prototype_properties), }; const njs_object_type_init_t njs_async_function_type_init = { .constructor = njs_native_ctor(njs_function_constructor, 1, 1), .constructor_props = &njs_async_constructor_init, .prototype_props = &njs_async_prototype_init, .prototype_value = { .object = { .type = NJS_OBJECT } }, }; const njs_object_prop_t njs_async_function_instance_properties[] = { NJS_DECLARE_PROP_HANDLER("length", njs_function_instance_length, 0, 0, NJS_OBJECT_PROP_VALUE_C), NJS_DECLARE_PROP_HANDLER("name", njs_function_instance_name, 0, 0, NJS_OBJECT_PROP_VALUE_C), }; const njs_object_init_t njs_async_function_instance_init = { njs_async_function_instance_properties, njs_nitems(njs_async_function_instance_properties), }; njs-0.8.9/src/njs_async.h000066400000000000000000000015001474132077100152530ustar00rootroot00000000000000 /* * Copyright (C) Alexander Borisov * Copyright (C) Nginx, Inc. */ #ifndef _NJS_ASYNC_H_INCLUDED_ #define _NJS_ASYNC_H_INCLUDED_ typedef struct { njs_promise_capability_t *capability; njs_frame_t *await; uintptr_t index; u_char *pc; } njs_async_ctx_t; njs_int_t njs_async_function_frame_invoke(njs_vm_t *vm, njs_value_t *retval); njs_int_t njs_await_fulfilled(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); njs_int_t njs_await_rejected(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); extern const njs_object_type_init_t njs_async_function_type_init; extern const njs_object_init_t njs_async_function_instance_init; #endif /* _NJS_ASYNC_H_INCLUDED_ */ njs-0.8.9/src/njs_boolean.c000066400000000000000000000072671474132077100155700ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include static njs_int_t njs_boolean_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { const njs_value_t *value; njs_object_value_t *object; if (nargs == 1) { value = &njs_value_false; } else { value = njs_is_true(&args[1]) ? &njs_value_true : &njs_value_false; } if (vm->top_frame->ctor) { object = njs_object_value_alloc(vm, NJS_OBJ_TYPE_BOOLEAN, 0, value); if (njs_slow_path(object == NULL)) { return NJS_ERROR; } njs_set_object_value(retval, object); } else { njs_value_assign(retval, value); } return NJS_OK; } static const njs_object_prop_t njs_boolean_constructor_properties[] = { { .type = NJS_PROPERTY, .name = njs_string("name"), .u.value = njs_string("Boolean"), .configurable = 1, }, { .type = NJS_PROPERTY, .name = njs_string("length"), .u.value = njs_value(NJS_NUMBER, 1, 1.0), .configurable = 1, }, NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), }; const njs_object_init_t njs_boolean_constructor_init = { njs_boolean_constructor_properties, njs_nitems(njs_boolean_constructor_properties), }; static njs_int_t njs_boolean_prototype_value_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_value_t *value; value = &args[0]; if (value->type != NJS_BOOLEAN) { if (njs_is_object_boolean(value)) { value = njs_object_value(value); } else { njs_type_error(vm, "unexpected value type:%s", njs_type_string(value->type)); return NJS_ERROR; } } njs_value_assign(retval, value); return NJS_OK; } static njs_int_t njs_boolean_prototype_to_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_value_t *value; value = &args[0]; if (value->type != NJS_BOOLEAN) { if (njs_is_object_boolean(value)) { value = njs_object_value(value); } else { njs_type_error(vm, "unexpected value type:%s", njs_type_string(value->type)); return NJS_ERROR; } } njs_value_assign(retval, njs_is_true(value) ? &njs_string_true : &njs_string_false); return NJS_OK; } static const njs_object_prop_t njs_boolean_prototype_properties[] = { NJS_DECLARE_PROP_HANDLER("__proto__", njs_primitive_prototype_get_proto, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("constructor", njs_object_prototype_create_constructor, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_NATIVE("valueOf", njs_boolean_prototype_value_of, 0, 0), NJS_DECLARE_PROP_NATIVE("toString", njs_boolean_prototype_to_string, 0, 0), }; const njs_object_init_t njs_boolean_prototype_init = { njs_boolean_prototype_properties, njs_nitems(njs_boolean_prototype_properties), }; const njs_object_type_init_t njs_boolean_type_init = { .constructor = njs_native_ctor(njs_boolean_constructor, 1, 0), .constructor_props = &njs_boolean_constructor_init, .prototype_props = &njs_boolean_prototype_init, .prototype_value = { .object_value = { .value = njs_value(NJS_BOOLEAN, 0, 0.0), .object = { .type = NJS_OBJECT_VALUE } } }, }; njs-0.8.9/src/njs_boolean.h000066400000000000000000000003531474132077100155620ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_BOOLEAN_H_INCLUDED_ #define _NJS_BOOLEAN_H_INCLUDED_ extern const njs_object_type_init_t njs_boolean_type_init; #endif /* _NJS_BOOLEAN_H_INCLUDED_ */ njs-0.8.9/src/njs_buffer.c000066400000000000000000002154361474132077100154210ustar00rootroot00000000000000 /* * Copyright (C) Alexander Borisov * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #include #define INT24_MAX 0x7FFFFF #define INT24_MIN (-0x800000) #define INT40_MAX 0x7FFFFFFFFFLL #define INT40_MIN (-0x8000000000LL) #define INT48_MAX 0x7FFFFFFFFFFFLL #define INT48_MIN (-0x800000000000LL) #define UINT24_MAX 0xFFFFFFLL #define UINT40_MAX 0xFFFFFFFFFFLL #define UINT48_MAX 0xFFFFFFFFFFFFLL #define njs_buffer_magic(size, sign, little) \ ((size << 2) | (sign << 1) | little) static njs_buffer_encoding_t njs_buffer_encodings[] = { { njs_str("utf-8"), njs_string_decode_utf8, njs_string_decode_utf8, njs_decode_utf8_length }, { njs_str("utf8"), njs_string_decode_utf8, njs_string_decode_utf8, njs_decode_utf8_length }, { njs_str("hex"), njs_string_hex, njs_string_decode_hex, njs_decode_hex_length }, { njs_str("base64"), njs_string_base64, njs_string_decode_base64, njs_decode_base64_length }, { njs_str("base64url"), njs_string_base64url, njs_string_decode_base64url, njs_decode_base64url_length }, { njs_null_str, 0, 0, 0 } }; static njs_int_t njs_buffer_from_object(njs_vm_t *vm, njs_value_t *value, njs_value_t *retval); static njs_int_t njs_buffer_from_array_buffer(njs_vm_t *vm, njs_value_t *value, njs_value_t *length, njs_value_t *offset, njs_value_t *retval); static njs_int_t njs_buffer_from_typed_array(njs_vm_t *vm, njs_value_t *value, njs_value_t *retval); static njs_int_t njs_buffer_from_string(njs_vm_t *vm, njs_value_t *value, const njs_buffer_encoding_t *encoding, njs_value_t *retval); static njs_int_t njs_buffer_write_string(njs_vm_t *vm, njs_value_t *value, njs_typed_array_t *array, const njs_buffer_encoding_t *encoding, uint64_t offset, uint64_t length, njs_value_t *retval); static njs_int_t njs_buffer_fill(njs_vm_t *vm, njs_typed_array_t *array, const njs_value_t *fill, const njs_value_t *encoding, uint64_t offset, uint64_t end); static njs_int_t njs_buffer_fill_string(njs_vm_t *vm, const njs_value_t *value, njs_typed_array_t *array, const njs_buffer_encoding_t *encoding, uint8_t *start, uint8_t *end); static njs_int_t njs_buffer_fill_typed_array(njs_vm_t *vm, const njs_value_t *value, njs_typed_array_t *array, uint8_t *start, uint8_t *end); static njs_int_t njs_buffer(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t njs_buffer_constants(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t njs_buffer_constant(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t njs_buffer_init(njs_vm_t *vm); static njs_external_t njs_ext_buffer[] = { { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("Buffer"), .enumerable = 1, .u.property = { .handler = njs_buffer, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("constants"), .enumerable = 1, .u.property = { .handler = njs_buffer_constants, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("kMaxLength"), .enumerable = 1, .u.property = { .handler = njs_buffer_constant, .magic32 = INT32_MAX, } }, }; njs_module_t njs_buffer_module = { .name = njs_str("buffer"), .preinit = NULL, .init = njs_buffer_init, }; njs_int_t njs_buffer_set(njs_vm_t *vm, njs_value_t *value, const u_char *start, uint32_t size) { njs_object_t *proto; njs_typed_array_t *array; njs_array_buffer_t *buffer; array = njs_mp_alloc(vm->mem_pool, sizeof(njs_typed_array_t) + sizeof(njs_array_buffer_t)); if (njs_slow_path(array == NULL)) { njs_memory_error(vm); return NJS_ERROR; } buffer = (njs_array_buffer_t *) &array[1]; proto = njs_vm_proto(vm, NJS_OBJ_TYPE_ARRAY_BUFFER); njs_lvlhsh_init(&buffer->object.hash); njs_lvlhsh_init(&buffer->object.shared_hash); buffer->object.__proto__ = proto; buffer->object.slots = NULL; buffer->object.type = NJS_ARRAY_BUFFER; buffer->object.shared = 1; buffer->object.extensible = 1; buffer->object.error_data = 0; buffer->object.fast_array = 0; buffer->u.data = (void *) start; buffer->size = size; proto = njs_vm_proto(vm, NJS_OBJ_TYPE_BUFFER); array->type = NJS_OBJ_TYPE_UINT8_ARRAY; njs_lvlhsh_init(&array->object.hash); njs_lvlhsh_init(&array->object.shared_hash); array->object.__proto__ = proto; array->object.slots = NULL; array->object.type = NJS_TYPED_ARRAY; array->object.shared = 0; array->object.extensible = 1; array->object.error_data = 0; array->object.fast_array = 1; array->buffer = buffer; array->offset = 0; array->byte_length = size; njs_set_typed_array(value, array); return NJS_OK; } static njs_typed_array_t * njs_buffer_alloc(njs_vm_t *vm, size_t size, njs_bool_t zeroing) { njs_value_t value; njs_typed_array_t *array; njs_set_number(&value, size); array = njs_typed_array_alloc(vm, &value, 1, zeroing, NJS_OBJ_TYPE_UINT8_ARRAY); if (njs_slow_path(array == NULL)) { return NULL; } array->object.__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_BUFFER); return array; } njs_int_t njs_buffer_new(njs_vm_t *vm, njs_value_t *value, const u_char *start, uint32_t size) { njs_typed_array_t *buffer; buffer = njs_buffer_alloc(vm, size, 0); if (njs_slow_path(buffer == NULL)) { return NJS_ERROR; } memcpy(njs_typed_array_buffer(buffer)->u.u8, start, size); njs_set_typed_array(value, buffer); return NJS_OK; } static njs_int_t njs_buffer_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_type_error(vm, "Buffer is not a constructor"); return NJS_ERROR; } static njs_int_t njs_buffer_alloc_safe(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t safe, njs_value_t *retval) { double size; njs_int_t ret; njs_typed_array_t *array; const njs_value_t *fill; if (njs_slow_path(!njs_is_number(njs_arg(args, nargs, 1)))) { njs_type_error(vm, "\"size\" argument must be of type number"); return NJS_ERROR; } size = njs_number(njs_argument(args, 1)); if (njs_slow_path(size < 0 || size > INT32_MAX)) { njs_range_error(vm, "invalid size"); return NJS_ERROR; } array = njs_buffer_alloc(vm, size, safe || nargs <= 2); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } fill = njs_arg(args, nargs, 2); if (safe && njs_is_defined(fill)) { ret = njs_buffer_fill(vm, array, fill, njs_arg(args, nargs, 3), 0, array->byte_length); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } njs_set_typed_array(retval, array); return NJS_OK; } static njs_int_t njs_buffer_from(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_value_t *value; const njs_buffer_encoding_t *encoding; value = njs_arg(args, nargs, 1); next: switch (value->type) { case NJS_TYPED_ARRAY: return njs_buffer_from_typed_array(vm, value, retval); case NJS_ARRAY_BUFFER: return njs_buffer_from_array_buffer(vm, value, njs_arg(args, nargs, 2), njs_arg(args, nargs, 3), retval); case NJS_STRING: encoding = njs_buffer_encoding(vm, njs_arg(args, nargs, 2), 1); if (njs_slow_path(encoding == NULL)) { return NJS_ERROR; } return njs_buffer_from_string(vm, value, encoding, retval); default: if (njs_is_object(value)) { ret = njs_value_of(vm, value, retval); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (ret == NJS_OK && !njs_is_null(retval) && !(njs_is_object(retval) && njs_object(retval) == njs_object(value))) { njs_value_assign(value, retval); goto next; } ret = njs_buffer_from_object(vm, value, retval); if (njs_slow_path(ret != NJS_DECLINED)) { return ret; } } njs_type_error(vm, "first argument %s is not a string " "or Buffer-like object", njs_type_string(value->type)); } return NJS_ERROR; } static njs_int_t njs_buffer_from_object(njs_vm_t *vm, njs_value_t *value, njs_value_t *retval) { double num; int64_t len; uint8_t *p; uint32_t i; njs_str_t str; njs_int_t ret; njs_value_t val, data, length; njs_typed_array_t *buffer; static const njs_value_t string_length = njs_string("length"); static const njs_str_t str_buffer = njs_str("Buffer"); next: ret = njs_value_property(vm, value, njs_value_arg(&string_length), &length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (ret == NJS_DECLINED) { ret = njs_value_property(vm, value, njs_value_arg(&njs_string_type), &val); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_value_to_string(vm, &val, &val); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_string_get(&val, &str); if (!njs_strstr_eq(&str, &str_buffer)) { return NJS_DECLINED; } ret = njs_value_property(vm, value, njs_value_arg(&njs_string_data), &val); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_is_object(&val)) { njs_value_assign(&data, &val); value = &data; goto next; } return NJS_DECLINED; } ret = njs_value_to_length(vm, &length, &len); if (njs_slow_path(ret != NJS_OK)) { return ret; } buffer = njs_buffer_alloc(vm, len, 0); if (njs_slow_path(buffer == NULL)) { return NJS_ERROR; } p = njs_typed_array_buffer(buffer)->u.u8; for (i = 0; i < len; i++) { ret = njs_value_property_i64(vm, value, i, &val); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } ret = njs_value_to_number(vm, &val, &num); if (njs_slow_path(ret != NJS_OK)) { return ret; } *p++ = njs_number_to_int32(num); } njs_set_typed_array(retval, buffer); return NJS_OK; } static njs_int_t njs_buffer_from_array_buffer(njs_vm_t *vm, njs_value_t *value, njs_value_t *offset, njs_value_t *length, njs_value_t *retval) { int64_t off, len; njs_int_t ret; njs_value_t arg; njs_typed_array_t *buffer; njs_array_buffer_t *array; array = njs_array_buffer(value); ret = njs_value_to_index(vm, offset, (uint64_t *) &off); if (njs_slow_path(ret != NJS_OK)) { return ret; } if ((size_t) off > array->size) { njs_range_error(vm, "\"offset\" is outside of buffer bounds"); return NJS_ERROR; } if (njs_is_defined(length)) { ret = njs_value_to_length(vm, length, &len); if (njs_slow_path(ret != NJS_OK)) { return ret; } } else { len = array->size - off; } if ((size_t) (off + len) > array->size) { njs_range_error(vm, "\"length\" is outside of buffer bounds"); return NJS_ERROR; } njs_set_array_buffer(&arg, array); buffer = njs_typed_array_alloc(vm, &arg, 1, 0, NJS_OBJ_TYPE_UINT8_ARRAY); if (njs_slow_path(buffer == NULL)) { return NJS_ERROR; } buffer->object.__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_BUFFER); buffer->offset = off; buffer->byte_length = len; njs_set_typed_array(retval, buffer); return NJS_OK; } static njs_int_t njs_buffer_from_typed_array(njs_vm_t *vm, njs_value_t *value, njs_value_t *retval) { uint8_t *p; uint32_t i, length; njs_typed_array_t *buffer, *array; array = njs_typed_array(value); if (njs_slow_path(njs_is_detached_buffer(array->buffer))) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } length = njs_typed_array_length(array); buffer = njs_buffer_alloc(vm, length, 0); if (njs_slow_path(buffer == NULL)) { return NJS_ERROR; } p = njs_typed_array_buffer(buffer)->u.u8; for (i = 0; i < length; i++) { *p++ = njs_number_to_int32(njs_typed_array_prop(array, i)); } njs_set_typed_array(retval, buffer); return NJS_OK; } static njs_int_t njs_buffer_from_string(njs_vm_t *vm, njs_value_t *value, const njs_buffer_encoding_t *encoding, njs_value_t *retval) { njs_int_t ret; njs_str_t str; njs_value_t dst; njs_typed_array_t *buffer; ret = njs_buffer_decode_string(vm, value, &dst, encoding); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_string_get(&dst, &str); buffer = njs_buffer_alloc(vm, str.length, 0); if (njs_slow_path(buffer == NULL)) { return NJS_ERROR; } memcpy(njs_typed_array_buffer(buffer)->u.u8, str.start, str.length); njs_set_typed_array(retval, buffer); return NJS_OK; } static size_t njs_buffer_decode_string_length(njs_value_t *value, const njs_buffer_encoding_t *encoding) { size_t size; njs_str_t str; njs_string_prop_t string; (void) njs_string_prop(&string, value); str.start = string.start; str.length = string.size; size = string.size; if (encoding->decode == njs_string_decode_utf8 && string.length != 0) { return size; } encoding->decode_length(&str, &size); return size; } static njs_int_t njs_buffer_byte_length(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { size_t size; njs_value_t *value; const njs_buffer_encoding_t *encoding; value = njs_arg(args, nargs, 1); switch (value->type) { case NJS_TYPED_ARRAY: njs_set_number(retval, njs_typed_array(value)->byte_length); return NJS_OK; case NJS_ARRAY_BUFFER: njs_set_number(retval, njs_array_buffer(value)->size); return NJS_OK; case NJS_DATA_VIEW: njs_set_number(retval, njs_data_view(value)->byte_length); return NJS_OK; case NJS_STRING: encoding = njs_buffer_encoding(vm, njs_arg(args, nargs, 2), 1); if (njs_slow_path(encoding == NULL)) { return NJS_ERROR; } size = njs_buffer_decode_string_length(value, encoding); njs_set_number(retval, size); return NJS_OK; default: njs_type_error(vm, "first argument %s is not a string " "or Buffer-like object", njs_type_string(value->type)); } return NJS_ERROR; } static njs_typed_array_t * njs_buffer_slot_internal(njs_vm_t *vm, njs_value_t *value) { njs_typed_array_t *array; if (njs_is_object(value)) { array = njs_object_proto_lookup(njs_object(value), NJS_TYPED_ARRAY, njs_typed_array_t); if (array != NULL && array->type == NJS_OBJ_TYPE_UINT8_ARRAY) { return array; } } return NULL; } static njs_typed_array_t * njs_buffer_slot(njs_vm_t *vm, njs_value_t *value, const char *name) { njs_typed_array_t *array; array = njs_buffer_slot_internal(vm, value); if (njs_slow_path(array == NULL)) { njs_type_error(vm, "\"%s\" argument must be an instance " "of Buffer or Uint8Array", name); return NULL; } return array; } njs_int_t njs_value_buffer_get(njs_vm_t *vm, njs_value_t *value, njs_str_t *dst) { njs_typed_array_t *array; njs_array_buffer_t *array_buffer; if (njs_slow_path(!(njs_is_typed_array(value) || njs_is_data_view(value)))) { njs_type_error(vm, "first argument must be a Buffer or DataView"); return NJS_ERROR; } array = njs_typed_array(value); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } array_buffer = njs_typed_array_writable(vm, array); if (njs_slow_path(array_buffer == NULL)) { return NJS_ERROR; } dst->length = array->byte_length; dst->start = &array_buffer->u.u8[array->offset]; return NJS_OK; } static njs_int_t njs_buffer_array_range(njs_vm_t *vm, njs_typed_array_t *array, const njs_value_t *start, const njs_value_t *end, const char *name, uint8_t **out_start, uint8_t **out_end) { uint64_t num_start, num_end; njs_int_t ret; njs_array_buffer_t *buffer; num_start = 0; if (njs_is_defined(start)) { ret = njs_value_to_index(vm, njs_value_arg(start), &num_start); if (njs_slow_path(ret != NJS_OK)) { return ret; } } if (num_start > array->byte_length) { njs_range_error(vm, "\"%sStart\" is out of range: %L", name, num_start); return NJS_ERROR; } num_end = array->byte_length; if (njs_is_defined(end)) { ret = njs_value_to_index(vm, njs_value_arg(end), &num_end); if (njs_slow_path(ret != NJS_OK)) { return ret; } } if (num_end > array->byte_length) { njs_range_error(vm, "\"%sEnd\" is out of range: %L", name, num_end); return NJS_ERROR; } if (num_start > num_end) { num_end = num_start; } buffer = njs_typed_array_buffer(array); if (njs_slow_path(njs_is_detached_buffer(buffer))) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } *out_start = &buffer->u.u8[array->offset + num_start]; *out_end = &buffer->u.u8[array->offset + num_end]; return NJS_OK; } static njs_int_t njs_buffer_compare_array(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2, const njs_value_t *target_start, const njs_value_t *target_end, const njs_value_t *source_start, const njs_value_t *source_end, njs_value_t *retval) { size_t size, src_size, trg_size; uint8_t *src, *src_end, *trg, *trg_end; njs_int_t ret; njs_typed_array_t *source, *target; source = njs_buffer_slot(vm, val1, "source"); if (njs_slow_path(source == NULL)) { return NJS_ERROR; } target = njs_buffer_slot(vm, val2, "target"); if (njs_slow_path(target == NULL)) { return NJS_ERROR; } ret = njs_buffer_array_range(vm, target, target_start, target_end, "target", &trg, &trg_end); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_buffer_array_range(vm, source, source_start, source_end, "source", &src, &src_end); if (njs_slow_path(ret != NJS_OK)) { return ret; } trg_size = trg_end - trg; src_size = src_end - src; size = njs_min(trg_size, src_size); ret = memcmp(trg, src, size); if (ret != 0) { njs_set_number(retval, (ret < 0) ? 1 : -1); return NJS_OK; } if (trg_size > src_size) { ret = -1; } else if (trg_size < src_size) { ret = 1; } njs_set_number(retval, ret); return NJS_OK; } static njs_int_t njs_buffer_compare(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { return njs_buffer_compare_array(vm, njs_arg(args, nargs, 1), njs_arg(args, nargs, 2), &njs_value_undefined, &njs_value_undefined, &njs_value_undefined, &njs_value_undefined, retval); } static njs_int_t njs_buffer_concat(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { u_char *p, *src; size_t n; int64_t i, len, list_len; njs_int_t ret; njs_value_t *list, *value, *length, val; njs_array_t *array; njs_typed_array_t *buffer, *arr; list = njs_arg(args, nargs, 1); if (njs_slow_path(!njs_is_array(list))) { njs_type_error(vm, "\"list\" argument must be an instance of Array"); return NJS_ERROR; } len = 0; ret = njs_object_length(vm, list, &list_len); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (njs_is_fast_array(list)) { array = njs_array(list); for (i = 0; i < list_len; i++) { value = &array->start[i]; if (njs_slow_path(!njs_is_typed_array_uint8(value))) { njs_type_error(vm, "\"list[%L]\" argument must be an " "instance of Buffer or Uint8Array", i); return NJS_ERROR; } arr = njs_typed_array(value); if (njs_slow_path(njs_is_detached_buffer(arr->buffer))) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } if (njs_slow_path((SIZE_MAX - len) < arr->byte_length)) { njs_type_error(vm, "Invalid length"); return NJS_ERROR; } len += arr->byte_length; } } else { for (i = 0; i < list_len; i++) { ret = njs_value_property_i64(vm, list, i, &val); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (njs_slow_path(!njs_is_typed_array(&val))) { njs_type_error(vm, "\"list[%L]\" argument must be an " "instance of Buffer or Uint8Array", i); return NJS_ERROR; } arr = njs_typed_array(&val); if (njs_slow_path(njs_is_detached_buffer(arr->buffer))) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } if (njs_slow_path((SIZE_MAX - len) < arr->byte_length)) { njs_type_error(vm, "Invalid length"); return NJS_ERROR; } len += arr->byte_length; } } length = njs_arg(args, nargs, 2); if (njs_is_defined(length)) { if (njs_slow_path(!njs_is_number(length))) { njs_type_error(vm, "\"length\" argument must be of type number"); return NJS_ERROR; } len = njs_number(length); if (njs_slow_path(len < 0)) { njs_range_error(vm, "\"length\" is out of range"); return NJS_ERROR; } } buffer = njs_buffer_alloc(vm, len, 0); if (njs_slow_path(buffer == NULL)) { return NJS_ERROR; } p = njs_typed_array_buffer(buffer)->u.u8; if (njs_is_fast_array(list)) { array = njs_array(list); for (i = 0; len != 0 && i < list_len; i++) { arr = njs_typed_array(&array->start[i]); n = njs_min((size_t) len, arr->byte_length); src = &njs_typed_array_buffer(arr)->u.u8[arr->offset]; p = njs_cpymem(p, src, n); len -= n; } } else { for (i = 0; len != 0 && i < list_len; i++) { ret = njs_value_property_i64(vm, list, i, &val); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } arr = njs_typed_array(&val); n = njs_min((size_t) len, arr->byte_length); src = &njs_typed_array_buffer(arr)->u.u8[arr->offset]; p = njs_cpymem(p, src, n); len -= n; } } if (len != 0) { njs_memzero(p, len); } njs_set_typed_array(retval, buffer); return NJS_OK; } static njs_int_t njs_buffer_is_buffer(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_bool_t is; njs_typed_array_t *array; is = 0; array = njs_buffer_slot_internal(vm, njs_arg(args, nargs, 1)); if (njs_fast_path(array != NULL && array->object.__proto__ == njs_vm_proto(vm, NJS_OBJ_TYPE_BUFFER))) { is = 1; } njs_set_boolean(retval, is); return NJS_OK; } static njs_int_t njs_buffer_is_encoding(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { const njs_value_t *value; value = njs_arg(args, nargs, 1); njs_set_boolean(retval, njs_is_defined(value) && njs_buffer_encoding(vm, value, 0) != NULL); return NJS_OK; } static njs_int_t njs_buffer_prototype_length(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { njs_typed_array_t *array; array = njs_buffer_slot_internal(vm, value); if (njs_slow_path(array == NULL)) { njs_set_undefined(retval); return NJS_DECLINED; } njs_set_number(retval, array->byte_length); return NJS_OK; } static njs_int_t njs_buffer_prototype_read_int(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t magic, njs_value_t *retval) { double v; uint32_t u32; uint64_t u64, index, size; njs_int_t ret; njs_bool_t little, swap, sign; njs_value_t *this, *value; const uint8_t *u8; njs_typed_array_t *array; njs_array_buffer_t *buffer; this = njs_argument(args, 0); array = njs_buffer_slot(vm, this, "this"); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } ret = njs_value_to_index(vm, njs_arg(args, nargs, 1), &index); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } size = magic >> 2; if (!size) { value = njs_arg(args, nargs, 2); if (njs_slow_path(!njs_is_number(value))) { njs_type_error(vm, "\"byteLength\" is not a number"); return NJS_ERROR; } size = (size_t) njs_number(value); if (njs_slow_path(size > 6)) { njs_type_error(vm, "\"byteLength\" must be <= 6"); return NJS_ERROR; } } if (njs_slow_path(size + index > array->byte_length)) { njs_range_error(vm, "index %uL is outside the bound of the buffer", index); return NJS_ERROR; } sign = (magic >> 1) & 1; little = magic & 1; swap = little; #if NJS_HAVE_LITTLE_ENDIAN swap = !swap; #endif buffer = array->buffer; if (njs_slow_path(njs_is_detached_buffer(buffer))) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } u8 = &buffer->u.u8[index + array->offset]; switch (size) { case 1: if (sign) { v = (int8_t) *u8; } else { v = *u8; } break; case 2: u32 = njs_get_u16(u8); if (swap) { u32 = njs_bswap_u16(u32); } if (sign) { /* Sign extension. */ u32 |= (u32 & (INT16_MAX + 1ULL)) * UINT32_MAX; v = (int16_t) u32; } else { v = u32; } break; case 3: if (little) { u32 = (u8[2] << 16) | (u8[1] << 8) | u8[0]; } else { u32 = (u8[0] << 16) | (u8[1] << 8) | u8[2]; } if (sign) { /* Sign extension. */ u32 |= (u32 & (INT24_MAX + 1ULL)) * UINT32_MAX; v = (int32_t) u32; } else { v = u32; } break; case 4: u32 = njs_get_u32(u8); if (swap) { u32 = njs_bswap_u32(u32); } if (sign) { /* Sign extension. */ u32 |= (u32 & (INT32_MAX + 1ULL)) * UINT32_MAX; v = (int32_t) u32; } else { v = u32; } break; case 5: if (little) { u64 = ((uint64_t) u8[4] << 32) | ((uint64_t) u8[3] << 24) | (u8[2] << 16) | (u8[1] << 8) | u8[0]; } else { u64 = ((uint64_t) u8[0] << 32) | ((uint64_t) u8[1] << 24) | (u8[2] << 16) | (u8[3] << 8) | u8[4]; } if (sign) { /* Sign extension. */ u64 |= (u64 & (INT40_MAX + 1ULL)) * UINT64_MAX; v = (int64_t) u64; } else { v = u64; } break; case 6: default: if (little) { u64 = ((uint64_t) u8[5] << 40) | ((uint64_t) u8[4] << 32) |((uint64_t) u8[3] << 24) | (u8[2] << 16) | (u8[1] << 8) | u8[0]; } else { u64 = ((uint64_t) u8[0] << 40) | ((uint64_t) u8[1] << 32) | ((uint64_t) u8[2] << 24) | (u8[3] << 16) | (u8[4] << 8) | u8[5]; } if (sign) { /* Sign extension. */ u64 |= (u64 & (INT48_MAX + 1ULL)) * UINT64_MAX; v = (int64_t) u64; } else { v = u64; } break; } njs_set_number(retval, v); return NJS_OK; } static njs_int_t njs_buffer_prototype_read_float(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t magic, njs_value_t *retval) { double v; uint32_t u32; uint64_t index, size; njs_int_t ret; njs_bool_t little, swap; njs_value_t *this; const uint8_t *u8; njs_conv_f32_t conv_f32; njs_conv_f64_t conv_f64; njs_typed_array_t *array; njs_array_buffer_t *buffer; this = njs_argument(args, 0); array = njs_buffer_slot(vm, this, "this"); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } ret = njs_value_to_index(vm, njs_arg(args, nargs, 1), &index); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } size = magic >> 2; if (njs_slow_path(size + index > array->byte_length)) { njs_range_error(vm, "index %uL is outside the bound of the buffer", index); return NJS_ERROR; } little = magic & 1; swap = little; #if NJS_HAVE_LITTLE_ENDIAN swap = !swap; #endif buffer = array->buffer; if (njs_slow_path(njs_is_detached_buffer(buffer))) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } u8 = &buffer->u.u8[index + array->offset]; switch (size) { case 4: u32 = *((uint32_t *) u8); if (swap) { u32 = njs_bswap_u32(u32); } conv_f32.u = u32; v = conv_f32.f; break; case 8: default: conv_f64.u = *((uint64_t *) u8); if (swap) { conv_f64.u = njs_bswap_u64(conv_f64.u); } v = conv_f64.f; } njs_set_number(retval, v); return NJS_OK; } static njs_int_t njs_buffer_prototype_write_int(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t magic, njs_value_t *retval) { uint8_t *u8; int64_t i64; uint32_t u32; uint64_t index, size; njs_int_t ret; njs_bool_t little, swap, sign; njs_value_t *this, *value; njs_typed_array_t *array; njs_array_buffer_t *buffer; this = njs_argument(args, 0); array = njs_buffer_slot(vm, this, "this"); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } ret = njs_value_to_integer(vm, njs_arg(args, nargs, 1), &i64); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } index = 0; if (nargs > 2) { ret = njs_value_to_index(vm, njs_argument(args, 2), &index); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } size = magic >> 2; if (!size) { value = njs_arg(args, nargs, 3); if (njs_slow_path(!njs_is_number(value))) { njs_type_error(vm, "\"byteLength\" is not a number"); return NJS_ERROR; } size = (size_t) njs_number(value); if (njs_slow_path(size > 6)) { njs_type_error(vm, "\"byteLength\" must be <= 6"); return NJS_ERROR; } } if (njs_slow_path(size + index > array->byte_length)) { njs_range_error(vm, "index %uL is outside the bound of the buffer", index); return NJS_ERROR; } little = magic & 1; sign = (magic >> 1) & 1; swap = little; #if NJS_HAVE_LITTLE_ENDIAN swap = !swap; #endif buffer = njs_typed_array_writable(vm, array); if (njs_slow_path(buffer == NULL)) { return NJS_ERROR; } u8 = &buffer->u.u8[index + array->offset]; switch (size) { case 1: if (sign) { if (i64 < INT8_MIN || i64 > INT8_MAX) { njs_range_error(vm, "value is outside the range of " "a representable int8"); return NJS_ERROR; } } else { if (i64 < 0 || i64 > UINT8_MAX) { njs_range_error(vm, "value is outside the range of " "a representable uint8"); return NJS_ERROR; } } *u8 = i64; break; case 2: u32 = (uint16_t) i64; if (sign) { if (i64 < INT16_MIN || i64 > INT16_MAX) { njs_range_error(vm, "value is outside the range of " "a representable int16"); return NJS_ERROR; } } else { if (i64 < 0 || i64 > UINT16_MAX) { njs_range_error(vm, "value is outside the range of " "a representable uint16"); return NJS_ERROR; } } if (swap) { u32 = njs_bswap_u16(u32); } njs_set_u16(u8, u32); break; case 3: if (sign) { if (i64 < INT24_MIN || i64 > INT24_MAX) { njs_range_error(vm, "value is outside the range of " "a representable int24"); return NJS_ERROR; } } else { if (i64 < 0 || i64 > UINT24_MAX) { njs_range_error(vm, "value is outside the range of " "a representable uint24"); return NJS_ERROR; } } if (little) { *u8++ = i64; i64 >>= 8; *u8++ = i64; i64 >>= 8; *u8++ = i64; } else { u8 += 2; *u8-- = i64; i64 >>= 8; *u8-- = i64; i64 >>= 8; *u8 = i64; } break; case 4: if (sign) { if (i64 < INT32_MIN || i64 > INT32_MAX) { njs_range_error(vm, "value is outside the range of " "a representable int32"); return NJS_ERROR; } } else { if (i64 < 0 || i64 > UINT32_MAX) { njs_range_error(vm, "value is outside the range of " "a representable uint32"); return NJS_ERROR; } } u32 = i64; if (swap) { u32 = njs_bswap_u32(u32); } njs_set_u32(u8, u32); break; case 5: if (sign) { if (i64 < INT40_MIN || i64 > INT40_MAX) { njs_range_error(vm, "value is outside the range of " "a representable int40"); return NJS_ERROR; } } else { if (i64 < 0 || i64 > UINT40_MAX) { njs_range_error(vm, "value is outside the range of " "a representable uint40"); return NJS_ERROR; } } if (little) { *u8++ = i64; i64 >>= 8; *u8++ = i64; i64 >>= 8; *u8++ = i64; i64 >>= 8; *u8++ = i64; i64 >>= 8; *u8++ = i64; } else { u8 += 4; *u8-- = i64; i64 >>= 8; *u8-- = i64; i64 >>= 8; *u8-- = i64; i64 >>= 8; *u8-- = i64; i64 >>= 8; *u8 = i64; } break; case 6: default: if (sign) { if (i64 < INT48_MIN || i64 > INT48_MAX) { njs_range_error(vm, "value is outside the range of " "a representable int48"); return NJS_ERROR; } } else { if (i64 < 0 || i64 > UINT48_MAX) { njs_range_error(vm, "value is outside the range of " "a representable uint48"); return NJS_ERROR; } } if (little) { *u8++ = i64; i64 >>= 8; *u8++ = i64; i64 >>= 8; *u8++ = i64; i64 >>= 8; *u8++ = i64; i64 >>= 8; *u8++ = i64; i64 >>= 8; *u8++ = i64; } else { u8 += 5; *u8-- = i64; i64 >>= 8; *u8-- = i64; i64 >>= 8; *u8-- = i64; i64 >>= 8; *u8-- = i64; i64 >>= 8; *u8-- = i64; i64 >>= 8; *u8 = i64; } break; } njs_set_number(retval, index + size); return NJS_OK; } static njs_int_t njs_buffer_prototype_write_float(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t magic, njs_value_t *retval) { double v; uint8_t *u8; uint64_t index, size; njs_int_t ret; njs_bool_t little, swap; njs_value_t *this; njs_conv_f32_t conv_f32; njs_conv_f64_t conv_f64; njs_typed_array_t *array; njs_array_buffer_t *buffer; this = njs_argument(args, 0); array = njs_buffer_slot(vm, this, "this"); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } ret = njs_value_to_number(vm, njs_arg(args, nargs, 1), &v); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } index = 0; if (nargs > 2) { ret = njs_value_to_index(vm, njs_argument(args, 2), &index); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } size = magic >> 2; if (njs_slow_path(size + index > array->byte_length)) { njs_range_error(vm, "index %uL is outside the bound of the buffer", index); return NJS_ERROR; } little = magic & 1; swap = little; #if NJS_HAVE_LITTLE_ENDIAN swap = !swap; #endif buffer = njs_typed_array_writable(vm, array); if (njs_slow_path(buffer == NULL)) { return NJS_ERROR; } u8 = &buffer->u.u8[index + array->offset]; switch (size) { case 4: conv_f32.f = (float) v; if (swap) { conv_f32.u = njs_bswap_u32(conv_f32.u); } *((uint32_t *) u8) = conv_f32.u; break; case 8: default: conv_f64.f = v; if (swap) { conv_f64.u = njs_bswap_u64(conv_f64.u); } *((uint64_t *) u8) = conv_f64.u; } njs_set_number(retval, index + size); return NJS_OK; } static njs_int_t njs_buffer_prototype_write(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { uint64_t offset, length; njs_int_t ret; njs_value_t *this, *value, *value_offset, *value_length, *enc; njs_typed_array_t *array; njs_array_buffer_t *buffer; const njs_buffer_encoding_t *encoding; this = njs_argument(args, 0); array = njs_buffer_slot(vm, this, "this"); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } value = njs_arg(args, nargs, 1); value_offset = njs_arg(args, nargs, 2); value_length = njs_arg(args, nargs, 3); enc = njs_arg(args, nargs, 4); offset = 0; length = array->byte_length; if (njs_slow_path(!njs_is_string(value))) { njs_type_error(vm, "first argument must be a string"); return NJS_ERROR; } if (njs_is_defined(value_offset)) { if (njs_is_string(value_offset)) { enc = value_offset; goto encoding; } ret = njs_value_to_index(vm, value_offset, &offset); if (njs_slow_path(ret != NJS_OK)) { return ret; } } if (njs_is_defined(value_length)) { if (njs_is_string(value_length)) { enc = value_length; goto encoding; } ret = njs_value_to_index(vm, value_length, &length); if (njs_slow_path(ret != NJS_OK)) { return ret; } } encoding: encoding = njs_buffer_encoding(vm, enc, 1); if (njs_slow_path(encoding == NULL)) { return NJS_ERROR; } buffer = njs_typed_array_writable(vm, array); if (njs_slow_path(buffer == NULL)) { return NJS_ERROR; } if (offset > array->byte_length) { njs_range_error(vm, "\"offset\" is out of range"); return NJS_ERROR; } return njs_buffer_write_string(vm, value, array, encoding, offset, length, retval); } static njs_int_t njs_buffer_write_string(njs_vm_t *vm, njs_value_t *value, njs_typed_array_t *array, const njs_buffer_encoding_t *encoding, uint64_t offset, uint64_t length, njs_value_t *retval) { uint8_t *start; njs_int_t ret; njs_str_t str; njs_value_t dst; const u_char *p, *end, *prev; njs_array_buffer_t *buffer; buffer = njs_typed_array_buffer(array); ret = njs_buffer_decode_string(vm, value, &dst, encoding); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_string_get(&dst, &str); start = &buffer->u.u8[array->offset + offset]; if (length > array->byte_length - offset) { length = array->byte_length - offset; } if (str.length == 0) { length = 0; goto done; } length = njs_min(str.length, (size_t) length); if (encoding->decode == njs_string_decode_utf8) { /* Avoid writing incomplete UTF-8 characters. */ p = prev = str.start; end = p + length; while (p < end) { p = njs_utf8_next(p, str.start + str.length); if (p <= end) { prev = p; } } length = prev - str.start; } memcpy(start, str.start, length); done: njs_set_number(retval, length); return NJS_OK; } static njs_int_t njs_buffer_prototype_fill(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { uint64_t offset, end; njs_int_t ret; njs_value_t *this, *value, *value_offset, *value_end, *encode; njs_typed_array_t *array; this = njs_argument(args, 0); if (njs_slow_path(nargs < 2)) { goto done; } array = njs_buffer_slot(vm, this, "this"); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } value = njs_arg(args, nargs, 1); value_offset = njs_arg(args, nargs, 2); value_end = njs_arg(args, nargs, 3); encode = njs_arg(args, nargs, 4); offset = 0; end = array->byte_length; if (njs_is_defined(value_offset)) { if (njs_is_string(value) && njs_is_string(value_offset)) { encode = value_offset; goto fill; } ret = njs_value_to_index(vm, value_offset, &offset); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } if (njs_is_defined(value_end)) { if (njs_is_string(value) && njs_is_string(value_end)) { encode = value_end; goto fill; } ret = njs_value_to_index(vm, value_end, &end); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } fill: ret = njs_buffer_fill(vm, array, value, encode, offset, end); if (njs_slow_path(ret != NJS_OK)) { return ret; } done: njs_value_assign(retval, this); return NJS_OK; } static njs_int_t njs_buffer_fill(njs_vm_t *vm, njs_typed_array_t *array, const njs_value_t *fill, const njs_value_t *encode, uint64_t offset, uint64_t end) { double num; uint8_t *start, *stop; njs_int_t ret; njs_array_buffer_t *buffer; const njs_buffer_encoding_t *encoding; buffer = njs_typed_array_writable(vm, array); if (njs_slow_path(buffer == NULL)) { return NJS_ERROR; } if (njs_slow_path(offset > array->byte_length)) { njs_range_error(vm, "\"offset\" is out of range"); return NJS_ERROR; } if (njs_slow_path(end > array->byte_length)) { njs_range_error(vm, "\"end\" is out of range"); return NJS_ERROR; } if (njs_slow_path(offset >= end)) { return NJS_OK; } start = &buffer->u.u8[array->offset + offset]; stop = &buffer->u.u8[array->offset + end]; switch (fill->type) { case NJS_STRING: encoding = njs_buffer_encoding(vm, encode, 1); if (njs_slow_path(encoding == NULL)) { return NJS_ERROR; } return njs_buffer_fill_string(vm, fill, array, encoding, start, stop); case NJS_TYPED_ARRAY: return njs_buffer_fill_typed_array(vm, fill, array, start, stop); default: ret = njs_value_to_number(vm, njs_value_arg(fill), &num); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_slow_path(njs_is_detached_buffer(buffer))) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } memset(start, njs_number_to_uint32(num) & 0xff, end - offset); } return NJS_OK; } static njs_int_t njs_buffer_fill_string(njs_vm_t *vm, const njs_value_t *value, njs_typed_array_t *array, const njs_buffer_encoding_t *encoding, uint8_t *start, uint8_t *end) { uint64_t n; njs_int_t ret; njs_str_t str; njs_value_t dst; ret = njs_buffer_decode_string(vm, value, &dst, encoding); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_string_get(&dst, &str); if (str.length == 0) { memset(start, 0, end - start); return NJS_OK; } while (start < end) { n = njs_min(str.length, (size_t) (end - start)); start = njs_cpymem(start, str.start, n); } return NJS_OK; } static njs_int_t njs_buffer_fill_typed_array(njs_vm_t *vm, const njs_value_t *value, njs_typed_array_t *array, uint8_t *to, uint8_t *end) { size_t byte_length; uint8_t *from; uint64_t n; njs_typed_array_t *arr_from; njs_array_buffer_t *buffer; buffer = njs_typed_array_buffer(array); arr_from = njs_typed_array(value); byte_length = arr_from->byte_length; from = &njs_typed_array_buffer(arr_from)->u.u8[arr_from->offset]; if (njs_typed_array_buffer(arr_from)->u.u8 == buffer->u.u8) { while (to < end) { n = njs_min(byte_length, (size_t) (end - to)); memmove(to, from, n); to += n; } } else { while (to < end) { n = njs_min(byte_length, (size_t) (end - to)); to = njs_cpymem(to, from, n); } } return NJS_OK; } static njs_int_t njs_buffer_prototype_to_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { uint64_t start, end; njs_int_t ret; njs_str_t str; njs_value_t *this, *value_start, *value_end; njs_typed_array_t *array; const njs_buffer_encoding_t *encoding; this = njs_argument(args, 0); array = njs_buffer_slot(vm, this, "this"); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } value_start = njs_arg(args, nargs, 2); value_end = njs_arg(args, nargs, 3); start = 0; end = array->byte_length; encoding = njs_buffer_encoding(vm, njs_arg(args, nargs, 1), 1); if (njs_slow_path(encoding == NULL)) { return NJS_ERROR; } if (njs_is_defined(value_start)) { ret = njs_value_to_index(vm, value_start, &start); if (njs_slow_path(ret != NJS_OK)) { return ret; } start = njs_min(start, array->byte_length); } if (njs_is_defined(value_end)) { ret = njs_value_to_index(vm, value_end, &end); if (njs_slow_path(ret != NJS_OK)) { return ret; } end = njs_min(end, array->byte_length); } if (njs_slow_path(njs_is_detached_buffer(array->buffer))) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } str.start = &njs_typed_array_buffer(array)->u.u8[array->offset + start]; str.length = end - start; if (njs_slow_path(str.length == 0)) { njs_value_assign(retval, &njs_string_empty); return NJS_OK; } return encoding->encode(vm, retval, &str); } static njs_int_t njs_buffer_prototype_compare(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { return njs_buffer_compare_array(vm, njs_argument(args, 0), njs_arg(args, nargs, 1), njs_arg(args, nargs, 2), njs_arg(args, nargs, 3), njs_arg(args, nargs, 4), njs_arg(args, nargs, 5), retval); } static njs_int_t njs_buffer_prototype_copy(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { size_t size; uint8_t *src, *src_end, *trg, *trg_end; njs_int_t ret; njs_value_t *val1, *val2; njs_typed_array_t *source, *target; njs_array_buffer_t *buffer, *array; val1 = njs_argument(args, 0); val2 = njs_arg(args, nargs, 1); source = njs_buffer_slot(vm, val1, "source"); if (njs_slow_path(source == NULL)) { return NJS_ERROR; } target = njs_buffer_slot(vm, val2, "target"); if (njs_slow_path(target == NULL)) { return NJS_ERROR; } ret = njs_buffer_array_range(vm, target, njs_arg(args, nargs, 2), &njs_value_undefined, "target", &trg, &trg_end); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_buffer_array_range(vm, source, njs_arg(args, nargs, 3), njs_arg(args, nargs, 4), "source", &src, &src_end); if (njs_slow_path(ret != NJS_OK)) { return ret; } buffer = njs_typed_array_writable(vm, target); if (njs_slow_path(buffer == NULL)) { return NJS_ERROR; } array = njs_typed_array_buffer(source); size = njs_min(trg_end - trg, src_end - src); if (buffer->u.data != array->u.data) { memcpy(trg, src, size); } else { memmove(trg, src, size); } njs_set_number(retval, size); return NJS_OK; } static njs_int_t njs_buffer_prototype_equals(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; ret = njs_buffer_compare_array(vm, njs_argument(args, 0), njs_arg(args, nargs, 1), &njs_value_undefined, &njs_value_undefined, &njs_value_undefined, &njs_value_undefined, retval); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_set_boolean(retval, njs_number(retval) == 0); return NJS_OK; } static njs_int_t njs_buffer_prototype_index_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t last, njs_value_t *retval) { uint8_t byte; int64_t from, to, increment, length, index, i; njs_int_t ret; njs_str_t str; njs_value_t *this, *value, *value_from, *enc, dst; const uint8_t *u8; njs_typed_array_t *array, *src; njs_array_buffer_t *buffer; const njs_buffer_encoding_t *encoding; this = njs_argument(args, 0); value = njs_arg(args, nargs, 1); value_from = njs_arg(args, nargs, 2); enc = njs_arg(args, nargs, 3); array = njs_buffer_slot(vm, this, "this"); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } index = -1; length = array->byte_length; if (last) { from = length - 1; to = -1; increment = -1; } else { from = 0; to = length; increment = 1; } if (njs_is_defined(value_from)) { if (njs_is_string(value) && njs_is_string(value_from)) { enc = value_from; goto encoding; } ret = njs_value_to_integer(vm, value_from, &from); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (from >= 0) { from = njs_min(from, length); } else { from = njs_max(0, length + from); } } encoding: encoding = njs_buffer_encoding(vm, enc, 1); if (njs_slow_path(encoding == NULL)) { return NJS_ERROR; } buffer = njs_typed_array_buffer(array); if (njs_slow_path(njs_is_detached_buffer(buffer))) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } u8 = &buffer->u.u8[array->offset]; switch (value->type) { case NJS_STRING: case NJS_TYPED_ARRAY: if (njs_is_string(value)) { ret = njs_buffer_decode_string(vm, value, &dst, encoding); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_string_get(&dst, &str); } else { src = njs_typed_array(value); if (njs_slow_path(src->type != NJS_OBJ_TYPE_UINT8_ARRAY)) { goto fail; } if (njs_slow_path(njs_is_detached_buffer(src->buffer))) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } str.start = &src->buffer->u.u8[src->offset]; str.length = src->byte_length; } if (last) { from = njs_min(from, length - (int64_t) str.length); if (to > from) { goto done; } } else { to -= (int64_t) str.length - 1; if (from > to) { goto done; } } if (from == to && str.length == 0) { index = 0; goto done; } for (i = from; i != to; i += increment) { if (memcmp(&u8[i], str.start, str.length) == 0) { index = i; goto done; } } break; case NJS_NUMBER: byte = njs_number_to_uint32(njs_number(value)); if (last) { from = njs_min(from, length - 1); } for (i = from; i != to; i += increment) { if (u8[i] == byte) { index = i; goto done; } } break; default: fail: njs_type_error(vm, "\"value\" argument %s is not a string " "or Buffer-like object", njs_type_string(value->type)); return NJS_ERROR; } done: njs_set_number(retval, index); return NJS_OK; } static njs_int_t njs_buffer_prototype_includes(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; ret = njs_buffer_prototype_index_of(vm, args, nargs, unused, retval); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_set_boolean(retval, (njs_number(retval) != -1)); return NJS_OK; } static njs_int_t njs_buffer_prototype_slice(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_typed_array_t *array; ret = njs_typed_array_prototype_slice(vm, args, nargs, unused, retval); if (njs_slow_path(ret != NJS_OK)) { return ret; } array = njs_typed_array(retval); array->object.__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_BUFFER); return NJS_OK; } static njs_int_t njs_buffer_prototype_swap(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t size, njs_value_t *retval) { uint8_t *p, *end; njs_typed_array_t *array; njs_array_buffer_t *buffer; array = njs_buffer_slot(vm, njs_argument(args, 0), "this"); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } if ((array->byte_length % size) != 0) { njs_range_error(vm, "Buffer size must be a multiple of %d-bits", (int) (size << 3)); return NJS_ERROR; } buffer = njs_typed_array_writable(vm, array); if (njs_slow_path(buffer == NULL)) { return NJS_ERROR; } p = &buffer->u.u8[array->offset]; end = p + array->byte_length; switch (size) { case 2: for (; p < end; p += 2) { njs_set_u16(p, njs_bswap_u16(njs_get_u16(p))); } break; case 4: for (; p < end; p += 4) { njs_set_u32(p, njs_bswap_u32(njs_get_u32(p))); } break; case 8: default: for (; p < end; p += 8) { njs_set_u64(p, njs_bswap_u64(njs_get_u64(p))); } } njs_set_typed_array(retval, array); return NJS_OK; } static njs_int_t njs_buffer_prototype_to_json(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { u_char *p, *end; njs_int_t ret; njs_value_t *value; njs_value_t object, array; njs_array_t *arr; njs_object_t *obj; njs_typed_array_t *ta; njs_array_buffer_t *buffer; static const njs_value_t string_buffer = njs_string("Buffer"); ta = njs_buffer_slot(vm, njs_argument(args, 0), "this"); if (njs_slow_path(ta == NULL)) { return NJS_ERROR; } obj = njs_object_alloc(vm); if (njs_slow_path(obj == NULL)) { return NJS_ERROR; } njs_set_object(&object, obj); ret = njs_value_property_set(vm, &object, njs_value_arg(&njs_string_type), njs_value_arg(&string_buffer)); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } arr = njs_array_alloc(vm, 1, ta->byte_length, 0); if (njs_slow_path(arr == NULL)) { return NJS_ERROR; } buffer = njs_typed_array_buffer(ta); if (njs_slow_path(njs_is_detached_buffer(buffer))) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } p = &buffer->u.u8[ta->offset]; end = p + ta->byte_length; value = arr->start; while (p < end) { njs_set_number(value++, *p++); } njs_set_array(&array, arr); ret = njs_value_property_set(vm, &object, njs_value_arg(&njs_string_data), &array); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_set_object(retval, obj); return NJS_OK; } const njs_buffer_encoding_t * njs_buffer_encoding(njs_vm_t *vm, const njs_value_t *value, njs_bool_t throw) { njs_str_t name; njs_buffer_encoding_t *encoding; if (njs_slow_path(!njs_is_string(value))) { if (njs_is_defined(value)) { njs_type_error(vm, "encoding must be a string"); return NULL; } return &njs_buffer_encodings[0]; } njs_string_get(value, &name); for (encoding = &njs_buffer_encodings[0]; encoding->name.length != 0; encoding++) { if (njs_strstr_eq(&name, &encoding->name)) { return encoding; } } if (throw) { njs_type_error(vm, "\"%V\" encoding is not supported", &name); } return NULL; } njs_int_t njs_buffer_decode_string(njs_vm_t *vm, const njs_value_t *value, njs_value_t *dst, const njs_buffer_encoding_t *encoding) { njs_int_t ret; njs_str_t str; njs_string_prop_t string; (void) njs_string_prop(&string, value); str.start = string.start; str.length = string.size; *dst = *value; if (encoding->decode == njs_string_decode_utf8 && string.length != 0) { return NJS_OK; } ret = encoding->decode(vm, dst, &str); if (njs_slow_path(ret != NJS_OK)) { return ret; } return NJS_OK; } static const njs_object_prop_t njs_buffer_prototype_properties[] = { { .type = NJS_PROPERTY, .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG), .u.value = njs_string("Buffer"), .configurable = 1, }, NJS_DECLARE_PROP_HANDLER("constructor", njs_object_prototype_create_constructor, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("length", njs_buffer_prototype_length, 0, 0, 0), NJS_DECLARE_PROP_NATIVE("readInt8", njs_buffer_prototype_read_int, 1, njs_buffer_magic(1, 1, 1)), NJS_DECLARE_PROP_NATIVE("readUInt8", njs_buffer_prototype_read_int, 1, njs_buffer_magic(1, 0, 1)), NJS_DECLARE_PROP_NATIVE("readInt16LE", njs_buffer_prototype_read_int, 1, njs_buffer_magic(2, 1, 1)), NJS_DECLARE_PROP_NATIVE("readUInt16LE", njs_buffer_prototype_read_int, 1, njs_buffer_magic(2, 0, 1)), NJS_DECLARE_PROP_NATIVE("readInt16BE", njs_buffer_prototype_read_int, 1, njs_buffer_magic(2, 1, 0)), NJS_DECLARE_PROP_NATIVE("readUInt16BE", njs_buffer_prototype_read_int, 1, njs_buffer_magic(2, 0, 0)), NJS_DECLARE_PROP_NATIVE("readInt32LE", njs_buffer_prototype_read_int, 1, njs_buffer_magic(4, 1, 1)), NJS_DECLARE_PROP_NATIVE("readUInt32LE", njs_buffer_prototype_read_int, 1, njs_buffer_magic(4, 0, 1)), NJS_DECLARE_PROP_NATIVE("readInt32BE", njs_buffer_prototype_read_int, 1, njs_buffer_magic(4, 1, 0)), NJS_DECLARE_PROP_NATIVE("readUInt32BE", njs_buffer_prototype_read_int, 1, njs_buffer_magic(4, 0, 0)), NJS_DECLARE_PROP_NATIVE("readIntLE", njs_buffer_prototype_read_int, 2, njs_buffer_magic(0, 1, 1)), NJS_DECLARE_PROP_NATIVE("readUIntLE", njs_buffer_prototype_read_int, 2, njs_buffer_magic(0, 0, 1)), NJS_DECLARE_PROP_NATIVE("readIntBE", njs_buffer_prototype_read_int, 2, njs_buffer_magic(0, 1, 0)), NJS_DECLARE_PROP_NATIVE("readUIntBE", njs_buffer_prototype_read_int, 2, njs_buffer_magic(0, 0, 0)), NJS_DECLARE_PROP_NATIVE("readFloatLE", njs_buffer_prototype_read_float, 1, njs_buffer_magic(4, 0, 1)), NJS_DECLARE_PROP_NATIVE("readFloatBE", njs_buffer_prototype_read_float, 1, njs_buffer_magic(4, 0, 0)), NJS_DECLARE_PROP_NATIVE("readDoubleLE", njs_buffer_prototype_read_float, 1, njs_buffer_magic(8, 0, 1)), NJS_DECLARE_PROP_NATIVE("readDoubleBE", njs_buffer_prototype_read_float, 1, njs_buffer_magic(8, 0, 0)), NJS_DECLARE_PROP_NATIVE("writeInt8", njs_buffer_prototype_write_int, 1, njs_buffer_magic(1, 1, 0)), NJS_DECLARE_PROP_NATIVE("writeUInt8", njs_buffer_prototype_write_int, 1, njs_buffer_magic(1, 0, 0)), NJS_DECLARE_PROP_NATIVE("writeInt16LE", njs_buffer_prototype_write_int, 1, njs_buffer_magic(2, 1, 1)), NJS_DECLARE_PROP_NATIVE("writeUInt16LE", njs_buffer_prototype_write_int, 1, njs_buffer_magic(2, 0, 1)), NJS_DECLARE_PROP_NATIVE("writeInt16BE", njs_buffer_prototype_write_int, 1, njs_buffer_magic(2, 1, 0)), NJS_DECLARE_PROP_NATIVE("writeUInt16BE", njs_buffer_prototype_write_int, 1, njs_buffer_magic(2, 0, 0)), NJS_DECLARE_PROP_NATIVE("writeInt32LE", njs_buffer_prototype_write_int, 1, njs_buffer_magic(4, 1, 1)), NJS_DECLARE_PROP_NATIVE("writeUInt32LE", njs_buffer_prototype_write_int, 1, njs_buffer_magic(4, 0, 1)), NJS_DECLARE_PROP_NATIVE("writeInt32BE", njs_buffer_prototype_write_int, 1, njs_buffer_magic(4, 1, 0)), NJS_DECLARE_PROP_NATIVE("writeUInt32BE", njs_buffer_prototype_write_int, 1, njs_buffer_magic(4, 0, 0)), NJS_DECLARE_PROP_NATIVE("writeIntLE", njs_buffer_prototype_write_int, 1, njs_buffer_magic(0, 1, 1)), NJS_DECLARE_PROP_NATIVE("writeUIntLE", njs_buffer_prototype_write_int, 1, njs_buffer_magic(0, 0, 1)), NJS_DECLARE_PROP_NATIVE("writeIntBE", njs_buffer_prototype_write_int, 1, njs_buffer_magic(0, 1, 0)), NJS_DECLARE_PROP_NATIVE("writeUIntBE", njs_buffer_prototype_write_int, 1, njs_buffer_magic(0, 0, 0)), NJS_DECLARE_PROP_NATIVE("writeFloatLE", njs_buffer_prototype_write_float, 1, njs_buffer_magic(4, 0, 1)), NJS_DECLARE_PROP_NATIVE("writeFloatBE", njs_buffer_prototype_write_float, 1, njs_buffer_magic(4, 0, 0)), NJS_DECLARE_PROP_NATIVE("writeDoubleLE", njs_buffer_prototype_write_float, 1, njs_buffer_magic(8, 0, 1)), NJS_DECLARE_PROP_NATIVE("writeDoubleBE", njs_buffer_prototype_write_float, 1, njs_buffer_magic(8, 0, 0)), NJS_DECLARE_PROP_NATIVE("write", njs_buffer_prototype_write, 1, 0), NJS_DECLARE_PROP_NATIVE("fill", njs_buffer_prototype_fill, 1, 0), NJS_DECLARE_PROP_NATIVE("toString", njs_buffer_prototype_to_string, 0, 0), NJS_DECLARE_PROP_NATIVE("compare", njs_buffer_prototype_compare, 1, 0), NJS_DECLARE_PROP_NATIVE("copy", njs_buffer_prototype_copy, 1, 0), NJS_DECLARE_PROP_NATIVE("equals", njs_buffer_prototype_equals, 1, 0), NJS_DECLARE_PROP_NATIVE("indexOf", njs_buffer_prototype_index_of, 1, 0), NJS_DECLARE_PROP_NATIVE("lastIndexOf", njs_buffer_prototype_index_of, 1, 1), NJS_DECLARE_PROP_NATIVE("includes", njs_buffer_prototype_includes, 1, 0), NJS_DECLARE_PROP_NATIVE("subarray", njs_buffer_prototype_slice, 2, 0), NJS_DECLARE_PROP_NATIVE("slice", njs_buffer_prototype_slice, 2, 0), NJS_DECLARE_PROP_NATIVE("swap16", njs_buffer_prototype_swap, 0, 2), NJS_DECLARE_PROP_NATIVE("swap32", njs_buffer_prototype_swap, 0, 4), NJS_DECLARE_PROP_NATIVE("swap64", njs_buffer_prototype_swap, 0, 8), NJS_DECLARE_PROP_NATIVE("toJSON", njs_buffer_prototype_to_json, 0, 0), }; const njs_object_init_t njs_buffer_prototype_init = { njs_buffer_prototype_properties, njs_nitems(njs_buffer_prototype_properties), }; static const njs_object_prop_t njs_buffer_constructor_properties[] = { NJS_DECLARE_PROP_LENGTH(0), NJS_DECLARE_PROP_NAME("Buffer"), NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), NJS_DECLARE_PROP_NATIVE("alloc", njs_buffer_alloc_safe, 0, 1), NJS_DECLARE_PROP_NATIVE("allocUnsafe", njs_buffer_alloc_safe, 0, 0), NJS_DECLARE_PROP_LNATIVE("allocUnsafeSlow", njs_buffer_alloc_safe, 1, 0), NJS_DECLARE_PROP_NATIVE("byteLength", njs_buffer_byte_length, 1, 0), NJS_DECLARE_PROP_NATIVE("compare", njs_buffer_compare, 2, 0), NJS_DECLARE_PROP_NATIVE("concat", njs_buffer_concat, 1, 0), NJS_DECLARE_PROP_NATIVE("from", njs_buffer_from, 3, 0), NJS_DECLARE_PROP_NATIVE("isBuffer", njs_buffer_is_buffer, 1, 0), NJS_DECLARE_PROP_NATIVE("isEncoding", njs_buffer_is_encoding, 1, 0), }; const njs_object_init_t njs_buffer_constructor_init = { njs_buffer_constructor_properties, njs_nitems(njs_buffer_constructor_properties), }; const njs_object_type_init_t njs_buffer_type_init = { .constructor = njs_native_ctor(njs_buffer_constructor, 0, 0), .prototype_props = &njs_buffer_prototype_init, .constructor_props = &njs_buffer_constructor_init, .prototype_value = { .object = { .type = NJS_OBJECT } }, }; static const njs_object_prop_t njs_buffer_constants_properties[] = { NJS_DECLARE_PROP_VALUE("MAX_LENGTH", njs_value(NJS_NUMBER, 1, INT32_MAX), NJS_OBJECT_PROP_VALUE_E), NJS_DECLARE_PROP_LVALUE("MAX_STRING_LENGTH", njs_value(NJS_NUMBER, 1, NJS_STRING_MAX_LENGTH), NJS_OBJECT_PROP_VALUE_E), }; static const njs_object_init_t njs_buffer_constants_init = { njs_buffer_constants_properties, njs_nitems(njs_buffer_constants_properties), }; static njs_int_t njs_buffer(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *unused, njs_value_t *retval) { return njs_object_prop_init(vm, &njs_buffer_constructor_init, prop, value, retval); } static njs_int_t njs_buffer_constants(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *unused, njs_value_t *retval) { return njs_object_prop_init(vm, &njs_buffer_constants_init, prop, value, retval); } static njs_int_t njs_buffer_constant(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *unused, njs_value_t *retval) { njs_value_number_set(retval, njs_vm_prop_magic32(prop)); return NJS_OK; } static njs_int_t njs_buffer_init(njs_vm_t *vm) { njs_int_t ret, proto_id; njs_mod_t *module; njs_opaque_value_t value; proto_id = njs_vm_external_prototype(vm, njs_ext_buffer, njs_nitems(njs_ext_buffer)); if (njs_slow_path(proto_id < 0)) { return NJS_ERROR; } ret = njs_vm_external_create(vm, njs_value_arg(&value), proto_id, NULL, 1); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } module = njs_vm_add_module(vm, &njs_str_value("buffer"), njs_value_arg(&value)); if (njs_slow_path(module == NULL)) { return NJS_ERROR; } return NJS_OK; } njs-0.8.9/src/njs_buffer.h000066400000000000000000000021531474132077100154140ustar00rootroot00000000000000 /* * Copyright (C) Alexander Borisov * Copyright (C) NGINX, Inc. */ #ifndef _NJS_BUFFER_H_INCLUDED_ #define _NJS_BUFFER_H_INCLUDED_ typedef njs_int_t (*njs_buffer_encode_t)(njs_vm_t *vm, njs_value_t *value, const njs_str_t *src); typedef size_t (*njs_buffer_encode_length_t)(const njs_str_t *src, size_t *out_size); typedef struct { njs_str_t name; njs_buffer_encode_t encode; njs_buffer_encode_t decode; njs_buffer_encode_length_t decode_length; } njs_buffer_encoding_t; njs_int_t njs_buffer_set(njs_vm_t *vm, njs_value_t *value, const u_char *start, uint32_t size); njs_int_t njs_buffer_new(njs_vm_t *vm, njs_value_t *value, const u_char *start, uint32_t size); const njs_buffer_encoding_t *njs_buffer_encoding(njs_vm_t *vm, const njs_value_t *value, njs_bool_t thrw); njs_int_t njs_buffer_decode_string(njs_vm_t *vm, const njs_value_t *value, njs_value_t *dst, const njs_buffer_encoding_t *encoding); extern const njs_object_type_init_t njs_buffer_type_init; #endif /* _NJS_BUFFER_H_INCLUDED_ */ njs-0.8.9/src/njs_builtin.c000066400000000000000000001244361474132077100156150ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #include #include typedef struct { enum { NJS_BUILTIN_TRAVERSE_KEYS, NJS_BUILTIN_TRAVERSE_MATCH, } type; njs_function_t *func; njs_lvlhsh_t keys; njs_str_t match; } njs_builtin_traverse_t; typedef struct { njs_str_t name; int value; } njs_signal_entry_t; static njs_int_t njs_global_this_prop_handler(njs_vm_t *vm, njs_object_prop_t *self, njs_value_t *global, njs_value_t *setval, njs_value_t *retval); static njs_int_t njs_env_hash_init(njs_vm_t *vm, njs_lvlhsh_t *hash, char **environment); static const njs_object_init_t njs_global_this_init; static const njs_object_init_t njs_njs_object_init; static const njs_object_init_t njs_process_object_init; static const njs_object_init_t *njs_object_init[] = { &njs_global_this_init, &njs_njs_object_init, &njs_process_object_init, &njs_math_object_init, &njs_json_object_init, NULL }; static const njs_object_type_init_t *const njs_object_type_init[NJS_OBJ_TYPE_MAX] = { /* Global types. */ &njs_obj_type_init, &njs_array_type_init, &njs_boolean_type_init, &njs_number_type_init, &njs_symbol_type_init, &njs_string_type_init, &njs_function_type_init, &njs_async_function_type_init, &njs_regexp_type_init, &njs_date_type_init, &njs_promise_type_init, &njs_array_buffer_type_init, &njs_data_view_type_init, &njs_text_decoder_type_init, &njs_text_encoder_type_init, &njs_buffer_type_init, /* Hidden types. */ &njs_iterator_type_init, &njs_array_iterator_type_init, &njs_typed_array_type_init, /* TypedArray types. */ &njs_typed_array_u8_type_init, &njs_typed_array_u8clamped_type_init, &njs_typed_array_i8_type_init, &njs_typed_array_u16_type_init, &njs_typed_array_i16_type_init, &njs_typed_array_u32_type_init, &njs_typed_array_i32_type_init, &njs_typed_array_f32_type_init, &njs_typed_array_f64_type_init, /* Error types. */ &njs_error_type_init, &njs_eval_error_type_init, &njs_internal_error_type_init, &njs_range_error_type_init, &njs_reference_error_type_init, &njs_syntax_error_type_init, &njs_type_error_type_init, &njs_uri_error_type_init, &njs_memory_error_type_init, &njs_aggregate_error_type_init, }; /* P1990 signals from `man 7 signal` are supported */ static njs_signal_entry_t njs_signals_table[] = { { njs_str("ABRT"), SIGABRT }, { njs_str("ALRM"), SIGALRM }, { njs_str("CHLD"), SIGCHLD }, { njs_str("CONT"), SIGCONT }, { njs_str("FPE"), SIGFPE }, { njs_str("HUP"), SIGHUP }, { njs_str("ILL"), SIGILL }, { njs_str("INT"), SIGINT }, { njs_str("KILL"), SIGKILL }, { njs_str("PIPE"), SIGPIPE }, { njs_str("QUIT"), SIGQUIT }, { njs_str("SEGV"), SIGSEGV }, { njs_str("STOP"), SIGSTOP }, { njs_str("TSTP"), SIGTSTP }, { njs_str("TERM"), SIGTERM }, { njs_str("TTIN"), SIGTTIN }, { njs_str("TTOU"), SIGTTOU }, { njs_str("USR1"), SIGUSR1 }, { njs_str("USR2"), SIGUSR2 }, { njs_null_str, 0 } }; njs_inline njs_int_t njs_object_hash_init(njs_vm_t *vm, njs_lvlhsh_t *hash, const njs_object_init_t *init) { return njs_object_hash_create(vm, hash, init->properties, init->items); } njs_int_t njs_builtin_objects_create(njs_vm_t *vm) { njs_int_t ret, index; njs_uint_t i; njs_object_t *object, *string_object; njs_function_t *constructor; njs_vm_shared_t *shared; njs_regexp_pattern_t *pattern; njs_object_prototype_t *prototype; const njs_object_init_t *obj, **p; shared = njs_mp_zalloc(vm->mem_pool, sizeof(njs_vm_shared_t)); if (njs_slow_path(shared == NULL)) { return NJS_ERROR; } vm->shared = shared; njs_lvlhsh_init(&shared->keywords_hash); njs_lvlhsh_init(&shared->values_hash); pattern = njs_regexp_pattern_create(vm, (u_char *) "(?:)", njs_length("(?:)"), 0); if (njs_slow_path(pattern == NULL)) { return NJS_ERROR; } shared->empty_regexp_pattern = pattern; ret = njs_object_hash_init(vm, &shared->array_instance_hash, &njs_array_instance_init); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_object_hash_init(vm, &shared->string_instance_hash, &njs_string_instance_init); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_object_hash_init(vm, &shared->function_instance_hash, &njs_function_instance_init); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_object_hash_init(vm, &shared->async_function_instance_hash, &njs_async_function_instance_init); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_object_hash_init(vm, &shared->arrow_instance_hash, &njs_arrow_instance_init); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_object_hash_init(vm, &shared->arguments_object_instance_hash, &njs_arguments_object_instance_init); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_object_hash_init(vm, &shared->regexp_instance_hash, &njs_regexp_instance_init); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } object = shared->objects; for (p = njs_object_init; *p != NULL; p++) { obj = *p; ret = njs_object_hash_init(vm, &object->shared_hash, obj); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } object->type = NJS_OBJECT; object->shared = 1; object->extensible = 1; object++; } ret = njs_env_hash_init(vm, &shared->env_hash, environ); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } for (i = NJS_OBJ_TYPE_OBJECT; i < NJS_OBJ_TYPE_MAX; i++) { index = njs_vm_ctor_push(vm); if (njs_slow_path(index < 0)) { return NJS_ERROR; } njs_assert_msg((njs_uint_t) index == i, "ctor index should match object type"); prototype = njs_shared_prototype(shared, i); *prototype = njs_object_type_init[i]->prototype_value; ret = njs_object_hash_init(vm, &prototype->object.shared_hash, njs_object_type_init[i]->prototype_props); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } prototype->object.extensible = 1; } prototype = njs_shared_prototype(shared, NJS_OBJ_TYPE_REGEXP); prototype->regexp.pattern = shared->empty_regexp_pattern; for (i = NJS_OBJ_TYPE_OBJECT; i < NJS_OBJ_TYPE_MAX; i++) { constructor = njs_shared_ctor(shared, i); if (njs_object_type_init[i]->constructor_props == NULL) { njs_memzero(constructor, sizeof(njs_function_t)); continue; } *constructor = njs_object_type_init[i]->constructor; constructor->object.shared = 0; ret = njs_object_hash_init(vm, &constructor->object.shared_hash, njs_object_type_init[i]->constructor_props); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } shared->global_slots.prop_handler = njs_global_this_prop_handler; shared->global_slots.writable = 1; shared->global_slots.configurable = 1; shared->global_slots.enumerable = 1; shared->objects[0].slots = &shared->global_slots; vm->global_object = shared->objects[0]; vm->global_object.shared = 0; string_object = &shared->string_object; njs_lvlhsh_init(&string_object->hash); string_object->shared_hash = shared->string_instance_hash; string_object->type = NJS_OBJECT_VALUE; string_object->shared = 1; string_object->extensible = 0; njs_lvlhsh_init(&shared->modules_hash); return NJS_OK; } static njs_int_t njs_builtin_traverse(njs_vm_t *vm, njs_traverse_t *traverse, void *data) { size_t len; u_char *p, *start, *end; njs_int_t ret, n; njs_str_t name; njs_bool_t symbol; njs_value_t key, *value; njs_function_t *func, *target; njs_object_prop_t *prop; njs_lvlhsh_query_t lhq; njs_builtin_traverse_t *ctx; njs_traverse_t *path[NJS_TRAVERSE_MAX_DEPTH]; u_char buf[256]; ctx = data; if (ctx->type == NJS_BUILTIN_TRAVERSE_MATCH) { prop = traverse->prop; func = ctx->func; if (njs_is_accessor_descriptor(prop)) { target = njs_prop_getter(prop); } else { value = njs_prop_value(prop); target = (njs_is_function(value) && njs_function(value)->native) ? njs_function(value) : NULL; } if (target == NULL || !njs_native_function_same(target, func)) { return NJS_OK; } } if (traverse == NULL) { njs_type_error(vm, "njs_builtin_traverse() traverse arg is NULL"); return NJS_ERROR; } n = 0; while (traverse != NULL) { path[n++] = traverse; traverse = traverse->parent; } n--; p = buf; end = buf + sizeof(buf); do { symbol = 0; key = path[n]->prop->name; if (njs_slow_path(njs_is_symbol(&key))) { symbol = 1; key = *njs_symbol_description(&key); if (njs_is_undefined(&key)) { key = njs_string_empty; } } if (njs_slow_path(!njs_is_string(&key))) { /* Skipping special properties (e.g. array index properties). */ return NJS_OK; } njs_string_get(&key, &name); if (njs_slow_path((p + name.length + 3) > end)) { njs_type_error(vm, "njs_builtin_traverse() key is too long"); return NJS_ERROR; } if (symbol) { *p++ = '['; } else if (p != buf) { *p++ = '.'; } p = njs_cpymem(p, name.start, name.length); if (symbol) { *p++ = ']'; } } while (n-- > 0); if (ctx->type == NJS_BUILTIN_TRAVERSE_MATCH) { len = ctx->match.length; start = njs_mp_alloc(vm->mem_pool, len + (p - buf) + (len != 0)); if (njs_slow_path(start == NULL)) { njs_memory_error(vm); return NJS_ERROR; } if (len != 0) { memcpy(start, ctx->match.start, len); start[len++] = '.'; } memcpy(start + len, buf, p - buf); ctx->match.length = len + p - buf; ctx->match.start = start; return NJS_DONE; } /* NJS_BUILTIN_TRAVERSE_KEYS. */ prop = njs_object_prop_alloc(vm, &njs_value_undefined, &njs_value_null, 0); if (njs_slow_path(prop == NULL)) { return NJS_ERROR; } ret = njs_string_create(vm, &prop->name, buf, p - buf); if (njs_slow_path(ret != NJS_OK)) { return ret; } lhq.value = prop; njs_string_get(&prop->name, &lhq.key); lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length); lhq.replace = 1; lhq.pool = vm->mem_pool; lhq.proto = &njs_object_hash_proto; ret = njs_lvlhsh_insert(&ctx->keys, &lhq); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "lvlhsh insert/replace failed"); return NJS_ERROR; } return NJS_OK; } typedef struct { njs_str_t name; njs_function_native_t native; uint8_t magic8; } njs_function_name_t; njs_int_t njs_builtin_match_native_function(njs_vm_t *vm, njs_function_t *function, njs_str_t *name) { uint8_t magic8; njs_int_t ret; njs_arr_t **pprotos; njs_mod_t *module; njs_uint_t i, n; njs_value_t value, tag; njs_object_t object; njs_lvlhsh_each_t lhe; njs_exotic_slots_t *slots; njs_function_name_t *fn; njs_function_native_t native; njs_builtin_traverse_t ctx; if (vm->functions_name_cache != NULL) { n = vm->functions_name_cache->items; fn = vm->functions_name_cache->start; magic8 = function->magic8; native = function->u.native; while (n != 0) { if (fn->native == native && fn->magic8 == magic8) { *name = fn->name; return NJS_OK; } fn++; n--; } } ctx.type = NJS_BUILTIN_TRAVERSE_MATCH; ctx.func = function; /* Global object. */ ctx.match = njs_str_value(""); ret = njs_object_traverse(vm, njs_object(&vm->global_value), &ctx, njs_builtin_traverse); if (ret == NJS_DONE) { goto found; } /* Constructor from built-in modules (not-mapped to global object). */ for (i = NJS_OBJ_TYPE_HIDDEN_MIN; i < NJS_OBJ_TYPE_HIDDEN_MAX; i++) { njs_set_object(&value, &njs_vm_ctor(vm, i).object); ret = njs_value_property(vm, &value, njs_value_arg(&njs_string_name), &tag); if (ret == NJS_OK && njs_is_string(&tag)) { njs_string_get(&tag, &ctx.match); } ret = njs_object_traverse(vm, njs_object(&value), &ctx, njs_builtin_traverse); if (ret == NJS_DONE) { goto found; } } /* Modules. */ njs_lvlhsh_each_init(&lhe, &njs_modules_hash_proto); for ( ;; ) { module = njs_lvlhsh_each(&vm->modules_hash, &lhe); if (module == NULL) { break; } if (njs_is_object(&module->value) && !njs_object(&module->value)->shared) { ctx.match = module->name; ret = njs_object_traverse(vm, njs_object(&module->value), &ctx, njs_builtin_traverse); if (ret == NJS_DONE) { goto found; } } } /* External prototypes (not mapped to global object). */ ctx.match = njs_str_value(""); for (i = 0; i< vm->protos->items; i++) { njs_memzero(&object, sizeof(njs_object_t)); pprotos = njs_arr_item(vm->protos, i); slots = (*pprotos)->start; object.shared_hash = slots->external_shared_hash; object.slots = slots; njs_set_object(&value, &object); ret = njs_object_string_tag(vm, &value, &tag); if (ret == NJS_OK && njs_is_string(&tag)) { njs_string_get(&tag, &ctx.match); } ret = njs_object_traverse(vm, njs_object(&value), &ctx, njs_builtin_traverse); if (ret == NJS_DONE) { goto found; } } return NJS_DECLINED; found: if (vm->functions_name_cache == NULL) { vm->functions_name_cache = njs_arr_create(vm->mem_pool, 4, sizeof(njs_function_name_t)); if (njs_slow_path(vm->functions_name_cache == NULL)) { return NJS_ERROR; } } fn = njs_arr_add(vm->functions_name_cache); if (njs_slow_path(fn == NULL)) { njs_memory_error(vm); return NJS_ERROR; } fn->name = ctx.match; fn->native = function->u.native; fn->magic8 = function->magic8; *name = fn->name; return NJS_OK; } static njs_int_t njs_ext_dump(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { uint32_t n; njs_int_t ret; njs_str_t str; njs_value_t *value, *indent; value = njs_arg(args, nargs, 1); indent = njs_arg(args, nargs, 2); ret = njs_value_to_uint32(vm, indent, &n); if (njs_slow_path(ret != NJS_OK)) { return ret; } n = njs_min(n, 5); if (njs_vm_value_dump(vm, &str, value, 1, n) != NJS_OK) { return NJS_ERROR; } return njs_string_create(vm, retval, str.start, str.length); } static njs_int_t njs_ext_on(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_str_t type; njs_uint_t i, n; njs_value_t *value; static const struct { njs_str_t name; njs_uint_t id; } hooks[] = { { njs_str("exit"), NJS_HOOK_EXIT }, }; value = njs_arg(args, nargs, 1); if (njs_slow_path(!njs_is_string(value))) { njs_type_error(vm, "hook type is not a string"); return NJS_ERROR; } njs_string_get(value, &type); i = 0; n = sizeof(hooks) / sizeof(hooks[0]); while (i < n) { if (njs_strstr_eq(&type, &hooks[i].name)) { break; } i++; } if (i == n) { njs_type_error(vm, "unknown hook type \"%V\"", &type); return NJS_ERROR; } value = njs_arg(args, nargs, 2); if (njs_slow_path(!njs_is_function(value) && !njs_is_null(value))) { njs_type_error(vm, "callback is not a function or null"); return NJS_ERROR; } vm->hooks[i] = njs_is_function(value) ? njs_function(value) : NULL; return NJS_OK; } static njs_int_t njs_ext_memory_stats(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *unused, njs_value_t *unused2, njs_value_t *retval) { njs_int_t ret; njs_value_t object, value; njs_object_t *stat; njs_mp_stat_t mp_stat; static const njs_value_t size_string = njs_string("size"); static const njs_value_t nblocks_string = njs_string("nblocks"); static const njs_value_t page_string = njs_string("page_size"); static const njs_value_t cluster_string = njs_string("cluster_size"); stat = njs_object_alloc(vm); if (njs_slow_path(stat == NULL)) { return NJS_ERROR; } njs_set_object(&object, stat); njs_mp_stat(vm->mem_pool, &mp_stat); njs_set_number(&value, mp_stat.size); ret = njs_value_property_set(vm, &object, njs_value_arg(&size_string), &value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_set_number(&value, mp_stat.nblocks); ret = njs_value_property_set(vm, &object, njs_value_arg(&nblocks_string), &value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_set_number(&value, mp_stat.cluster_size); ret = njs_value_property_set(vm, &object, njs_value_arg(&cluster_string), &value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_set_number(&value, mp_stat.page_size); ret = njs_value_property_set(vm, &object, njs_value_arg(&page_string), &value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_set_object(retval, stat); return NJS_OK; } static njs_int_t njs_global_this_prop_handler(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *global, njs_value_t *setval, njs_value_t *retval) { njs_int_t ret; njs_value_t *value; njs_variable_t *var; njs_function_t *function; njs_rbtree_node_t *rb_node; njs_lvlhsh_query_t lhq; njs_variable_node_t *node, var_node; if (retval == NULL) { return NJS_DECLINED; } njs_string_get(&prop->name, &lhq.key); lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length); lhq.proto = &njs_lexer_hash_proto; ret = njs_lvlhsh_find(&vm->shared->keywords_hash, &lhq); if (njs_slow_path(ret != NJS_OK || lhq.value == NULL)) { return NJS_DECLINED; } var_node.key = (uintptr_t) lhq.value; rb_node = njs_rbtree_find(&vm->global_scope->variables, &var_node.node); if (rb_node == NULL) { return NJS_DECLINED; } node = (njs_variable_node_t *) rb_node; var = node->variable; if (var->type == NJS_VARIABLE_LET || var->type == NJS_VARIABLE_CONST) { return NJS_DECLINED; } value = njs_scope_valid_value(vm, var->index); if (var->type == NJS_VARIABLE_FUNCTION && njs_is_undefined(value)) { njs_value_assign(value, &var->value); function = njs_function_value_copy(vm, value); if (njs_slow_path(function == NULL)) { return NJS_ERROR; } } if (setval != NULL) { njs_value_assign(value, setval); } njs_value_assign(retval, value); return NJS_OK; } static njs_int_t njs_global_this_object(njs_vm_t *vm, njs_object_prop_t *self, njs_value_t *global, njs_value_t *setval, njs_value_t *retval) { njs_int_t ret; njs_object_prop_t *prop; njs_lvlhsh_query_t lhq; if (retval == NULL) { return NJS_DECLINED; } njs_value_assign(retval, global); if (njs_slow_path(setval != NULL)) { njs_value_assign(retval, setval); } prop = njs_object_prop_alloc(vm, &self->name, retval, 1); if (njs_slow_path(prop == NULL)) { return NJS_ERROR; } njs_value_assign(njs_prop_value(prop), retval); prop->enumerable = self->enumerable; lhq.value = prop; njs_string_get(&self->name, &lhq.key); lhq.key_hash = njs_prop_magic32(self); lhq.replace = 1; lhq.pool = vm->mem_pool; lhq.proto = &njs_object_hash_proto; ret = njs_lvlhsh_insert(njs_object_hash(global), &lhq); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "lvlhsh insert/replace failed"); return NJS_ERROR; } return NJS_OK; } static njs_int_t njs_top_level_object(njs_vm_t *vm, njs_object_prop_t *self, njs_value_t *global, njs_value_t *setval, njs_value_t *retval) { njs_int_t ret; njs_object_t *object; njs_object_prop_t *prop; njs_lvlhsh_query_t lhq; if (njs_slow_path(setval != NULL)) { njs_value_assign(retval, setval); } else { if (njs_slow_path(retval == NULL)) { return NJS_DECLINED; } njs_set_object(retval, &vm->shared->objects[njs_prop_magic16(self)]); object = njs_object_value_copy(vm, retval); if (njs_slow_path(object == NULL)) { return NJS_ERROR; } object->__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_OBJECT); } prop = njs_object_prop_alloc(vm, &self->name, retval, 1); if (njs_slow_path(prop == NULL)) { return NJS_ERROR; } njs_value_assign(njs_prop_value(prop), retval); prop->enumerable = self->enumerable; lhq.value = prop; njs_string_get(&self->name, &lhq.key); lhq.key_hash = njs_prop_magic32(self); lhq.replace = 1; lhq.pool = vm->mem_pool; lhq.proto = &njs_object_hash_proto; ret = njs_lvlhsh_insert(njs_object_hash(global), &lhq); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "lvlhsh insert/replace failed"); return NJS_ERROR; } return NJS_OK; } static njs_int_t njs_top_level_constructor(njs_vm_t *vm, njs_object_prop_t *self, njs_value_t *global, njs_value_t *setval, njs_value_t *retval) { njs_int_t ret; njs_function_t *ctor; njs_object_prop_t *prop; njs_lvlhsh_query_t lhq; if (njs_slow_path(setval != NULL)) { njs_value_assign(retval, setval); } else { if (njs_slow_path(retval == NULL)) { return NJS_DECLINED; } ctor = &njs_vm_ctor(vm, njs_prop_magic16(self)); njs_set_function(retval, ctor); return NJS_OK; } prop = njs_object_prop_alloc(vm, &self->name, retval, 1); if (njs_slow_path(prop == NULL)) { return NJS_ERROR; } njs_value_assign(njs_prop_value(prop), retval); prop->enumerable = 0; lhq.value = prop; njs_string_get(&self->name, &lhq.key); lhq.key_hash = njs_prop_magic32(self); lhq.replace = 1; lhq.pool = vm->mem_pool; lhq.proto = &njs_object_hash_proto; ret = njs_lvlhsh_insert(njs_object_hash(global), &lhq); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "lvlhsh insert/replace failed"); return NJS_ERROR; } return NJS_OK; } static const njs_object_prop_t njs_global_this_object_properties[] = { { .type = NJS_PROPERTY, .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG), .u.value = njs_string("global"), .configurable = 1, }, /* Global aliases. */ NJS_DECLARE_PROP_HANDLER("global", njs_global_this_object, 0, NJS_GLOBAL_HASH, NJS_OBJECT_PROP_VALUE_ECW), NJS_DECLARE_PROP_HANDLER("globalThis", njs_global_this_object, 0, NJS_GLOBAL_THIS_HASH, NJS_OBJECT_PROP_VALUE_CW), /* Global constants. */ NJS_DECLARE_PROP_VALUE("NaN", njs_value(NJS_NUMBER, 0, NAN), 0), NJS_DECLARE_PROP_VALUE("Infinity", njs_value(NJS_NUMBER, 1, INFINITY), 0), NJS_DECLARE_PROP_VALUE("undefined", njs_value(NJS_UNDEFINED, 0, NAN), 0), /* Global functions. */ NJS_DECLARE_PROP_NATIVE("isFinite", njs_number_global_is_finite, 1, 0), NJS_DECLARE_PROP_NATIVE("isNaN", njs_number_global_is_nan, 1, 0), NJS_DECLARE_PROP_NATIVE("parseFloat", njs_number_parse_float, 1, 0), NJS_DECLARE_PROP_NATIVE("parseInt", njs_number_parse_int, 2, 0), NJS_DECLARE_PROP_NATIVE("toString", njs_object_prototype_to_string, 0, 0), NJS_DECLARE_PROP_NATIVE("encodeURI", njs_string_encode_uri, 1, 0), NJS_DECLARE_PROP_LNATIVE("encodeURIComponent", njs_string_encode_uri, 1, 1), NJS_DECLARE_PROP_NATIVE("decodeURI", njs_string_decode_uri, 1, 0), NJS_DECLARE_PROP_LNATIVE("decodeURIComponent", njs_string_decode_uri, 1, 1), NJS_DECLARE_PROP_NATIVE("atob", njs_string_atob, 1, 0), NJS_DECLARE_PROP_NATIVE("btoa", njs_string_btoa, 1, 0), NJS_DECLARE_PROP_NATIVE("eval", njs_eval_function, 1, 0), NJS_DECLARE_PROP_NATIVE("require", njs_module_require, 1, 0), /* Global objects. */ NJS_DECLARE_PROP_HANDLER("njs", njs_top_level_object, NJS_OBJECT_NJS, NJS_NJS_HASH, NJS_OBJECT_PROP_VALUE_ECW), NJS_DECLARE_PROP_HANDLER("process", njs_top_level_object, NJS_OBJECT_PROCESS, NJS_PROCESS_HASH, NJS_OBJECT_PROP_VALUE_ECW), NJS_DECLARE_PROP_HANDLER("Math", njs_top_level_object, NJS_OBJECT_MATH, NJS_MATH_HASH, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("JSON", njs_top_level_object, NJS_OBJECT_JSON, NJS_JSON_HASH, NJS_OBJECT_PROP_VALUE_CW), #ifdef NJS_TEST262 NJS_DECLARE_PROP_HANDLER("$262", njs_top_level_object, NJS_OBJECT_262, NJS_262_HASH, NJS_OBJECT_PROP_VALUE_ECW), #endif /* Global constructors. */ NJS_DECLARE_PROP_HANDLER("Object", njs_top_level_constructor, NJS_OBJ_TYPE_OBJECT, NJS_OBJECT_HASH, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("Array", njs_top_level_constructor, NJS_OBJ_TYPE_ARRAY, NJS_ARRAY_HASH, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("ArrayBuffer", njs_top_level_constructor, NJS_OBJ_TYPE_ARRAY_BUFFER, NJS_ARRAY_BUFFER_HASH, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("DataView", njs_top_level_constructor, NJS_OBJ_TYPE_DATA_VIEW, NJS_DATA_VIEW_HASH, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("TextDecoder", njs_top_level_constructor, NJS_OBJ_TYPE_TEXT_DECODER, NJS_TEXT_DECODER_HASH, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("TextEncoder", njs_top_level_constructor, NJS_OBJ_TYPE_TEXT_ENCODER, NJS_TEXT_ENCODER_HASH, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("Buffer", njs_top_level_constructor, NJS_OBJ_TYPE_BUFFER, NJS_BUFFER_HASH, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("Uint8Array", njs_top_level_constructor, NJS_OBJ_TYPE_UINT8_ARRAY, NJS_UINT8ARRAY_HASH, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("Uint16Array", njs_top_level_constructor, NJS_OBJ_TYPE_UINT16_ARRAY, NJS_UINT16ARRAY_HASH, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("Uint32Array", njs_top_level_constructor, NJS_OBJ_TYPE_UINT32_ARRAY, NJS_UINT32ARRAY_HASH, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("Int8Array", njs_top_level_constructor, NJS_OBJ_TYPE_INT8_ARRAY, NJS_INT8ARRAY_HASH, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("Int16Array", njs_top_level_constructor, NJS_OBJ_TYPE_INT16_ARRAY, NJS_INT16ARRAY_HASH, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("Int32Array", njs_top_level_constructor, NJS_OBJ_TYPE_INT32_ARRAY, NJS_INT32ARRAY_HASH, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("Float32Array", njs_top_level_constructor, NJS_OBJ_TYPE_FLOAT32_ARRAY, NJS_FLOAT32ARRAY_HASH, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("Float64Array", njs_top_level_constructor, NJS_OBJ_TYPE_FLOAT64_ARRAY, NJS_FLOAT64ARRAY_HASH, NJS_OBJECT_PROP_VALUE_CW), { .type = NJS_PROPERTY_HANDLER, .name = njs_long_string("Uint8ClampedArray"), .u.value = njs_prop_handler2(njs_top_level_constructor, NJS_OBJ_TYPE_UINT8_CLAMPED_ARRAY, NJS_UINT8CLAMPEDARRAY_HASH), .writable = 1, .configurable = 1, }, NJS_DECLARE_PROP_HANDLER("Boolean", njs_top_level_constructor, NJS_OBJ_TYPE_BOOLEAN, NJS_BOOLEAN_HASH, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("Number", njs_top_level_constructor, NJS_OBJ_TYPE_NUMBER, NJS_NUMBER_HASH, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("Symbol", njs_top_level_constructor, NJS_OBJ_TYPE_SYMBOL, NJS_SYMBOL_HASH, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("String", njs_top_level_constructor, NJS_OBJ_TYPE_STRING, NJS_STRING_HASH, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("Function", njs_top_level_constructor, NJS_OBJ_TYPE_FUNCTION, NJS_FUNCTION_HASH, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("RegExp", njs_top_level_constructor, NJS_OBJ_TYPE_REGEXP, NJS_REGEXP_HASH, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("Date", njs_top_level_constructor, NJS_OBJ_TYPE_DATE, NJS_DATE_HASH, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("Promise", njs_top_level_constructor, NJS_OBJ_TYPE_PROMISE, NJS_PROMISE_HASH, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("Error", njs_top_level_constructor, NJS_OBJ_TYPE_ERROR, NJS_ERROR_HASH, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("EvalError", njs_top_level_constructor, NJS_OBJ_TYPE_EVAL_ERROR, NJS_EVAL_ERROR_HASH, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("InternalError", njs_top_level_constructor, NJS_OBJ_TYPE_INTERNAL_ERROR, NJS_INTERNAL_ERROR_HASH, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("RangeError", njs_top_level_constructor, NJS_OBJ_TYPE_RANGE_ERROR, NJS_RANGE_ERROR_HASH, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("ReferenceError", njs_top_level_constructor, NJS_OBJ_TYPE_REF_ERROR, NJS_REF_ERROR_HASH, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("SyntaxError", njs_top_level_constructor, NJS_OBJ_TYPE_SYNTAX_ERROR, NJS_SYNTAX_ERROR_HASH, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("TypeError", njs_top_level_constructor, NJS_OBJ_TYPE_TYPE_ERROR, NJS_TYPE_ERROR_HASH, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("URIError", njs_top_level_constructor, NJS_OBJ_TYPE_URI_ERROR, NJS_URI_ERROR_HASH, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("MemoryError", njs_top_level_constructor, NJS_OBJ_TYPE_MEMORY_ERROR, NJS_MEMORY_ERROR_HASH, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("AggregateError", njs_top_level_constructor, NJS_OBJ_TYPE_AGGREGATE_ERROR, NJS_AGGREGATE_ERROR_HASH, NJS_OBJECT_PROP_VALUE_CW), }; static const njs_object_init_t njs_global_this_init = { njs_global_this_object_properties, njs_nitems(njs_global_this_object_properties) }; static const njs_object_prop_t njs_njs_object_properties[] = { { .type = NJS_PROPERTY, .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG), .u.value = njs_string("njs"), .configurable = 1, }, NJS_DECLARE_PROP_VALUE("engine", njs_string("njs"), NJS_OBJECT_PROP_VALUE_EC), NJS_DECLARE_PROP_VALUE("version", njs_string(NJS_VERSION), NJS_OBJECT_PROP_VALUE_EC), NJS_DECLARE_PROP_VALUE("version_number", njs_value(NJS_NUMBER, 1, NJS_VERSION_NUMBER), NJS_OBJECT_PROP_VALUE_EC), NJS_DECLARE_PROP_NATIVE("dump", njs_ext_dump, 0, 0), NJS_DECLARE_PROP_NATIVE("on", njs_ext_on, 0, 0), NJS_DECLARE_PROP_HANDLER("memoryStats", njs_ext_memory_stats, 0, 0, NJS_OBJECT_PROP_VALUE_EC), }; static const njs_object_init_t njs_njs_object_init = { njs_njs_object_properties, njs_nitems(njs_njs_object_properties), }; static njs_int_t njs_process_object_argv(njs_vm_t *vm, njs_object_prop_t *pr, njs_value_t *process, njs_value_t *unused, njs_value_t *retval) { char **arg; njs_int_t ret; njs_uint_t i; njs_array_t *argv; njs_object_prop_t *prop; njs_lvlhsh_query_t lhq; static const njs_value_t argv_string = njs_string("argv"); argv = njs_array_alloc(vm, 1, vm->options.argc, 0); if (njs_slow_path(argv == NULL)) { return NJS_ERROR; } i = 0; for (arg = vm->options.argv; i < vm->options.argc; arg++) { njs_string_create(vm, &argv->start[i++], (u_char *) *arg, njs_strlen(*arg)); } prop = njs_object_prop_alloc(vm, &argv_string, &njs_value_undefined, 1); if (njs_slow_path(prop == NULL)) { return NJS_ERROR; } njs_set_array(njs_prop_value(prop), argv); lhq.value = prop; lhq.key_hash = NJS_ARGV_HASH; lhq.key = njs_str_value("argv"); lhq.replace = 1; lhq.pool = vm->mem_pool; lhq.proto = &njs_object_hash_proto; ret = njs_lvlhsh_insert(njs_object_hash(process), &lhq); if (njs_fast_path(ret == NJS_OK)) { njs_value_assign(retval, njs_prop_value(prop)); return NJS_OK; } njs_internal_error(vm, "lvlhsh insert failed"); return NJS_ERROR; } static njs_int_t njs_env_hash_init(njs_vm_t *vm, njs_lvlhsh_t *hash, char **environment) { char **ep; u_char *dst; ssize_t length; uint32_t cp; njs_int_t ret; const u_char *val, *entry, *s, *end; njs_object_prop_t *prop; njs_string_prop_t string; njs_lvlhsh_query_t lhq; lhq.replace = 0; lhq.pool = vm->mem_pool; lhq.proto = &njs_object_hash_proto; ep = environment; while (*ep != NULL) { prop = njs_object_prop_alloc(vm, &njs_value_undefined, &njs_value_undefined, 1); if (njs_slow_path(prop == NULL)) { return NJS_ERROR; } entry = (u_char *) *ep++; val = njs_strchr(entry, '='); if (njs_slow_path(val == NULL)) { continue; } ret = njs_string_create(vm, &prop->name, entry, val - entry); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } (void) njs_string_prop(&string, &prop->name); length = string.length; s = string.start; end = s + string.size; dst = (u_char *) s; while (length != 0) { cp = njs_utf8_upper_case(&s, end); dst = njs_utf8_encode(dst, cp); length--; } val++; ret = njs_string_create(vm, njs_prop_value(prop), val, njs_strlen(val)); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } lhq.value = prop; njs_string_get(&prop->name, &lhq.key); lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length); ret = njs_lvlhsh_insert(hash, &lhq); if (njs_slow_path(ret != NJS_OK)) { if (ret == NJS_ERROR) { njs_internal_error(vm, "lvlhsh insert failed"); return NJS_ERROR; } /* ret == NJS_DECLINED: entry already exists */ /* * Always using the first element among the duplicates * and ignoring the rest. */ } } return NJS_OK; } static njs_int_t njs_process_object_env(njs_vm_t *vm, njs_object_prop_t *pr, njs_value_t *process, njs_value_t *unused, njs_value_t *retval) { njs_int_t ret; njs_object_t *env; njs_object_prop_t *prop; njs_lvlhsh_query_t lhq; static const njs_value_t env_string = njs_string("env"); env = njs_object_alloc(vm); if (njs_slow_path(env == NULL)) { return NJS_ERROR; } env->shared_hash = vm->shared->env_hash; prop = njs_object_prop_alloc(vm, &env_string, &njs_value_undefined, 1); if (njs_slow_path(prop == NULL)) { return NJS_ERROR; } njs_set_object(njs_prop_value(prop), env); lhq.replace = 1; lhq.pool = vm->mem_pool; lhq.proto = &njs_object_hash_proto; lhq.value = prop; lhq.key = njs_str_value("env"); lhq.key_hash = NJS_ENV_HASH; ret = njs_lvlhsh_insert(njs_object_hash(process), &lhq); if (njs_fast_path(ret == NJS_OK)) { njs_value_assign(retval, njs_prop_value(prop)); return NJS_OK; } njs_internal_error(vm, "lvlhsh insert failed"); return NJS_ERROR; } static njs_int_t njs_process_object_pid(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *unused, njs_value_t *unused2, njs_value_t *retval) { njs_set_number(retval, getpid()); return NJS_OK; } static njs_int_t njs_process_object_ppid(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *unused, njs_value_t *unused2, njs_value_t *retval) { njs_set_number(retval, getppid()); return NJS_OK; } static njs_int_t njs_ext_process_kill(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t magic, njs_value_t *retval) { int signal; njs_str_t str; njs_uint_t pid; njs_value_t *arg; njs_signal_entry_t *s; arg = njs_arg(args, nargs, 1); if (!njs_value_is_number(arg)) { njs_vm_type_error(vm, "\"pid\" is not a number"); return NJS_ERROR; } pid = njs_value_number(arg); signal = SIGTERM; arg = njs_arg(args, nargs, 2); if (njs_value_is_number(arg)) { signal = njs_value_number(arg); } else if (njs_value_is_string(arg)) { njs_value_string_get(arg, &str); if (str.length < 3 || memcmp(str.start, "SIG", 3) != 0) { njs_vm_type_error(vm, "\"signal\" unknown value: \"%V\"", &str); return NJS_ERROR; } str.start += 3; str.length -= 3; for (s = &njs_signals_table[0]; s->name.length != 0; s++) { if (njs_strstr_eq(&str, &s->name)) { signal = s->value; break; } } if (s->name.length == 0) { njs_vm_type_error(vm, "\"signal\" unknown value"); return NJS_ERROR; } } else if (!njs_value_is_undefined(arg)) { njs_vm_type_error(vm, "\"signal\" invalid type"); return NJS_ERROR; } if (kill(pid, signal) < 0) { njs_vm_error(vm, "kill failed with (%d:%s)", errno, strerror(errno)); return NJS_ERROR; } njs_set_boolean(retval, 1); return NJS_OK; } static const njs_object_prop_t njs_process_object_properties[] = { { .type = NJS_PROPERTY, .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG), .u.value = njs_string("process"), .configurable = 1, }, NJS_DECLARE_PROP_HANDLER("argv", njs_process_object_argv, 0, 0, 0), NJS_DECLARE_PROP_HANDLER("env", njs_process_object_env, 0, 0, 0), NJS_DECLARE_PROP_HANDLER("pid", njs_process_object_pid, 0, 0, 0), NJS_DECLARE_PROP_HANDLER("ppid", njs_process_object_ppid, 0, 0, 0), NJS_DECLARE_PROP_NATIVE("kill", njs_ext_process_kill, 2, 0), }; static const njs_object_init_t njs_process_object_init = { njs_process_object_properties, njs_nitems(njs_process_object_properties), }; njs-0.8.9/src/njs_chb.c000066400000000000000000000106041474132077100146720ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #include #define NJS_CHB_MIN_SIZE 256 void njs_chb_init(njs_chb_t *chain, void *pool, njs_chb_alloc_t alloc, njs_chb_free_t free) { chain->error = 0; chain->pool = pool; chain->alloc = alloc; chain->free = free; chain->nodes = NULL; chain->last = NULL; } void njs_chb_append0(njs_chb_t *chain, const char *msg, size_t len) { u_char *p; if (len != 0 && !chain->error) { p = njs_chb_reserve(chain, len); if (njs_slow_path(p == NULL)) { return; } memcpy(p, msg, len); njs_chb_written(chain, len); } } u_char * njs_chb_reserve(njs_chb_t *chain, size_t size) { njs_chb_node_t *n; n = chain->last; if (njs_fast_path(n != NULL && njs_chb_node_room(n) >= size)) { return n->pos; } if (size < NJS_CHB_MIN_SIZE) { size = NJS_CHB_MIN_SIZE; } n = chain->alloc(chain->pool, sizeof(njs_chb_node_t) + size); if (njs_slow_path(n == NULL)) { chain->error = 1; return NULL; } n->next = NULL; n->start = (u_char *) n + sizeof(njs_chb_node_t); n->pos = n->start; n->end = n->pos + size; if (chain->last != NULL) { chain->last->next = n; } else { chain->nodes = n; } chain->last = n; return n->start; } void njs_chb_vsprintf(njs_chb_t *chain, size_t size, const char *fmt, va_list args) { u_char *start, *end; start = njs_chb_reserve(chain, size); if (njs_slow_path(start == NULL)) { return; } end = njs_vsprintf(start, start + size, fmt, args); njs_chb_written(chain, end - start); } void njs_chb_sprintf(njs_chb_t *chain, size_t size, const char* fmt, ...) { va_list args; va_start(args, fmt); njs_chb_vsprintf(chain, size, fmt, args); va_end(args); } /* * Drains size bytes from the beginning of the chain. */ void njs_chb_drain(njs_chb_t *chain, size_t drain) { njs_chb_node_t *n; n = chain->nodes; while (n != NULL) { if (njs_chb_node_size(n) > drain) { n->start += drain; return; } drain -= njs_chb_node_size(n); chain->nodes = n->next; njs_mp_free(chain->pool, n); n = chain->nodes; } chain->last = NULL; } /* * Drops size bytes from the end of the chain. */ void njs_chb_drop(njs_chb_t *chain, size_t drop) { uint64_t size; njs_chb_node_t *n, *next; if (njs_slow_path(chain->error)) { return; } n = chain->last; if (njs_fast_path(n != NULL && (njs_chb_node_size(n) > drop))) { n->pos -= drop; return; } n = chain->nodes; size = (uint64_t) njs_chb_size(chain); if (drop >= size) { njs_chb_destroy(chain); njs_chb_init(chain, chain->pool, chain->alloc, chain->free); return; } while (n != NULL) { size -= njs_chb_node_size(n); if (size <= drop) { chain->last = n; chain->last->pos -= drop - size; n = chain->last->next; chain->last->next = NULL; break; } n = n->next; } while (n != NULL) { next = n->next; njs_mp_free(chain->pool, n); n = next; } } njs_int_t njs_chb_join(njs_chb_t *chain, njs_str_t *str) { u_char *start; uint64_t size; njs_chb_node_t *n; if (njs_slow_path(chain->error)) { return NJS_DECLINED; } n = chain->nodes; if (n == NULL) { str->length = 0; str->start = NULL; return NJS_OK; } size = (uint64_t) njs_chb_size(chain); if (njs_slow_path(size >= UINT32_MAX)) { return NJS_ERROR; } start = chain->alloc(chain->pool, size); if (njs_slow_path(start == NULL)) { return NJS_ERROR; } str->length = size; str->start = start; njs_chb_join_to(chain, start); return NJS_OK; } void njs_chb_join_to(njs_chb_t *chain, u_char *dst) { njs_chb_node_t *n; n = chain->nodes; while (n != NULL) { dst = njs_cpymem(dst, n->start, njs_chb_node_size(n)); n = n->next; } } void njs_chb_destroy(njs_chb_t *chain) { njs_chb_node_t *n, *next; n = chain->nodes; while (n != NULL) { next = n->next; chain->free(chain->pool, n); n = next; } } njs-0.8.9/src/njs_chb.h000066400000000000000000000070671474132077100147100ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_CHB_H_INCLUDED_ #define _NJS_CHB_H_INCLUDED_ typedef struct njs_chb_node_s njs_chb_node_t; struct njs_chb_node_s { njs_chb_node_t *next; u_char *start; u_char *pos; u_char *end; }; typedef void *(*njs_chb_alloc_t)(void *pool, size_t size); typedef void (*njs_chb_free_t)(void *pool, void *p); typedef struct { njs_bool_t error; void *pool; njs_chb_alloc_t alloc; njs_chb_free_t free; njs_chb_node_t *nodes; njs_chb_node_t *last; } njs_chb_t; void njs_chb_init(njs_chb_t *chain, void *pool, njs_chb_alloc_t alloc, njs_chb_free_t free); #define NJS_CHB_MP_INIT(chain, vm) \ njs_chb_init(chain, njs_vm_memory_pool(vm), (njs_chb_alloc_t) njs_mp_alloc,\ (njs_chb_free_t) njs_mp_free) #define NJS_CHB_CTX_INIT(chain, ctx) \ njs_chb_init(chain, ctx, (njs_chb_alloc_t) js_malloc, \ (njs_chb_free_t) js_free) void njs_chb_append0(njs_chb_t *chain, const char *msg, size_t len); void njs_chb_vsprintf(njs_chb_t *chain, size_t size, const char *fmt, va_list args); void njs_chb_sprintf(njs_chb_t *chain, size_t size, const char* fmt, ...); u_char *njs_chb_reserve(njs_chb_t *chain, size_t size); void njs_chb_drain(njs_chb_t *chain, size_t drop); void njs_chb_drop(njs_chb_t *chain, size_t drop); njs_int_t njs_chb_join(njs_chb_t *chain, njs_str_t *str); void njs_chb_join_to(njs_chb_t *chain, u_char *dst); void njs_chb_destroy(njs_chb_t *chain); #define njs_chb_append(chain, msg, len) \ njs_chb_append0(chain, (const char *) (msg), len) #define njs_chb_append_literal(chain, literal) \ njs_chb_append0(chain, literal, njs_length(literal)) #define njs_chb_node_size(n) (size_t) ((n)->pos - (n)->start) #define njs_chb_node_room(n) (size_t) ((n)->end - (n)->pos) njs_inline void njs_chb_append_str(njs_chb_t *chain, njs_str_t *str) { njs_chb_append0(chain, (const char *) str->start, str->length); } njs_inline int64_t njs_chb_size(njs_chb_t *chain) { uint64_t size; njs_chb_node_t *n; if (njs_slow_path(chain->error)) { return -1; } n = chain->nodes; size = 0; while (n != NULL) { size += njs_chb_node_size(n); n = n->next; } return size; } njs_inline int64_t njs_chb_utf8_length(njs_chb_t *chain) { u_char *p, *p_end; size_t size; int64_t len, length; njs_chb_node_t *n; if (njs_slow_path(chain->error)) { return -1; } n = chain->nodes; length = 0; while (n != NULL) { p = n->start; size = njs_chb_node_size(n); p_end = p + size; while (p < p_end && *p < 0x80) { p++; } if (p != p_end) { break; } length += size; n = n->next; } while (n != NULL) { len = njs_utf8_length(n->start, njs_chb_node_size(n)); if (njs_slow_path(len < 0)) { return -1; } length += len; n = n->next; } return length; } njs_inline u_char * njs_chb_current(njs_chb_t *chain) { return (chain->last != NULL) ? chain->last->pos : NULL; } njs_inline void njs_chb_written(njs_chb_t *chain, size_t bytes) { chain->last->pos += bytes; } #endif /* _NJS_JSON_H_INCLUDED_ */ njs-0.8.9/src/njs_clang.h000066400000000000000000000124541474132077100152340ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_CLANG_H_INCLUDED_ #define _NJS_CLANG_H_INCLUDED_ #include #include /* offsetof(). */ #define njs_inline static inline __attribute__((always_inline)) #define njs_noinline __attribute__((noinline)) #define njs_cdecl #define njs_container_of(p, type, field) \ (type *) ((u_char *) (p) - offsetof(type, field)) #define njs_nitems(x) \ (sizeof(x) / sizeof((x)[0])) #define njs_max(val1, val2) \ ((val1 < val2) ? (val2) : (val1)) #define njs_min(val1, val2) \ ((val1 < val2) ? (val1) : (val2)) #if (NJS_HAVE_BUILTIN_EXPECT) #define njs_expect(c, x) __builtin_expect((long) (x), (c)) #define njs_fast_path(x) njs_expect(1, x) #define njs_slow_path(x) njs_expect(0, x) #else #define njs_expect(c, x) (x) #define njs_fast_path(x) (x) #define njs_slow_path(x) (x) #endif #if (NJS_HAVE_BUILTIN_UNREACHABLE) #define njs_unreachable() __builtin_unreachable() #else #define njs_unreachable() #endif #if (NJS_HAVE_BUILTIN_PREFETCH) #define njs_prefetch(a) __builtin_prefetch(a) #else #define njs_prefetch(a) #endif #if (NJS_HAVE_BUILTIN_CLZ) #define njs_leading_zeros(x) (((x) == 0) ? 32 : __builtin_clz(x)) #else njs_inline uint32_t njs_leading_zeros(uint32_t x) { uint32_t n; /* * There is no sense to optimize this function, since almost * all platforms nowadays support the built-in instruction. */ if (x == 0) { return 32; } n = 0; while ((x & 0x80000000) == 0) { n++; x <<= 1; } return n; } #endif #if (NJS_HAVE_BUILTIN_CLZLL) #define njs_leading_zeros64(x) (((x) == 0) ? 64 : __builtin_clzll(x)) #else njs_inline uint64_t njs_leading_zeros64(uint64_t x) { uint64_t n; /* * There is no sense to optimize this function, since almost * all platforms nowadays support the built-in instruction. */ if (x == 0) { return 64; } n = 0; while ((x & 0x8000000000000000) == 0) { n++; x <<= 1; } return n; } #endif #if (NJS_HAVE_GCC_ATTRIBUTE_VISIBILITY) #define NJS_EXPORT __attribute__((visibility("default"))) #else #define NJS_EXPORT #endif #if (NJS_HAVE_GCC_ATTRIBUTE_ALIGNED) #define njs_aligned(x) __attribute__((aligned(x))) #else #define njs_aligned(x) #endif #if (NJS_HAVE_GCC_ATTRIBUTE_PACKED) #define NJS_PACKED __attribute__((packed)) #else #define NJS_PACKED #endif #if (NJS_HAVE_GCC_ATTRIBUTE_FALLTHROUGH) #define NJS_FALLTHROUGH __attribute__((fallthrough)) #else #define NJS_FALLTHROUGH #endif #if (NJS_HAVE_GCC_ATTRIBUTE_MALLOC) #define NJS_MALLOC_LIKE __attribute__((__malloc__)) #else #define NJS_MALLOC_LIKE #endif #if (NJS_CLANG) /* Any __asm__ directive disables loop vectorization in GCC and Clang. */ #define njs_pragma_loop_disable_vectorization __asm__("") #else #define njs_pragma_loop_disable_vectorization #endif #define njs_stringify(v) #v #if (NJS_HAVE_MEMORY_SANITIZER) #include #define njs_msan_unpoison(ptr, size) __msan_unpoison(ptr, size) #else #define njs_msan_unpoison(ptr, size) #endif #if (NJS_HAVE_GCC_ATTRIBUTE_NO_SANITIZE) #define NJS_NOSANITIZE(options) __attribute__((no_sanitize(options))) #else #define NJS_NOSANITIZE(options) #endif njs_inline NJS_NOSANITIZE("float-cast-overflow") int64_t njs_unsafe_cast_double_to_int64(double num) { /* * Casting NaN to integer is undefined behavior, * but it is fine in some cases where we do additional checks later. * For example: * int64_t i64 = njs_unsafe_cast_double_to_int64(num); * if (i64 == num) { * // num is integer * } * * We do this as inline function to avoid UndefinedBehaviorSanitizer * warnings. */ return (int64_t) num; } #if (NJS_HAVE_DENORMALS_CONTROL) #include /* * 0x8000 Flush to zero * 0x0040 Denormals are zeros */ #define NJS_MM_DENORMALS_MASK 0x8040 #define njs_mm_denormals(on) \ _mm_setcsr((_mm_getcsr() & ~NJS_MM_DENORMALS_MASK) | (!(on) ? 0x8040: 0x0)) #else #define njs_mm_denormals(on) #endif #ifndef NJS_MAX_ALIGNMENT #if (NJS_SOLARIS) /* x86_64: 16, i386: 4, sparcv9: 16, sparcv8: 8. */ #define NJS_MAX_ALIGNMENT _MAX_ALIGNMENT #elif (NJS_WINDOWS) /* Win64: 16, Win32: 8. */ #define NJS_MAX_ALIGNMENT MEMORY_ALLOCATION_ALIGNMENT #elif (__amd64__) #define NJS_MAX_ALIGNMENT 16 #elif (__i386__ || __i386) #define NJS_MAX_ALIGNMENT 4 #elif (__arm__) #define NJS_MAX_ALIGNMENT 16 #else #define NJS_MAX_ALIGNMENT 16 #endif #endif #define njs_align_size(size, a) \ (((size) + ((size_t) (a) - 1)) & ~((size_t) (a) - 1)) #define njs_align_ptr(p, a) \ (u_char *) (((uintptr_t) (p) + ((uintptr_t) (a) - 1)) \ & ~((uintptr_t) (a) - 1)) #define njs_trunc_ptr(p, a) \ (u_char *) ((uintptr_t) (p) & ~((uintptr_t) (a) - 1)) #endif /* _NJS_CLANG_H_INCLUDED_ */ njs-0.8.9/src/njs_date.c000066400000000000000000001150231474132077100150540ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include #define NJS_DATE_TIME_LEN \ sizeof("Mon Sep 28 1970 12:00:00 GMT+0600 (XXXXX)") #define NJS_DATE_MAX_FIELDS 8 #define NJS_DATE_WDAY 0 #define NJS_DATE_YR 1 #define NJS_DATE_MON 2 #define NJS_DATE_DAY 3 #define NJS_DATE_HR 4 #define NJS_DATE_MIN 5 #define NJS_DATE_SEC 6 #define NJS_DATE_MSEC 7 #if (NJS_HAVE_TM_GMTOFF) #define njs_timezone(tm) \ ((tm)->tm_gmtoff) #elif (NJS_HAVE_ALTZONE) #define njs_timezone(tm) \ (-(((tm)->tm_isdst > 0) ? altzone : timezone)) #endif #define njs_date_magic(field, local) \ ((local << 6) + field) #define njs_date_magic2(since, len, local) \ ((local << 6) + ((len & 7) << 3) + since) typedef enum { NJS_DATE_FMT_TO_TIME_STRING, NJS_DATE_FMT_TO_DATE_STRING, NJS_DATE_FMT_TO_STRING, NJS_DATE_FMT_TO_UTC_STRING, NJS_DATE_FMT_TO_ISO_STRING, } njs_date_fmt_t; static double njs_date_string_parse(njs_value_t *date); static double njs_date_rfc2822_string_parse(int64_t tm[], const u_char *p, const u_char *end); static double njs_date_js_string_parse(int64_t tm[], const u_char *p, const u_char *end); static const u_char *njs_date_skip_week_day(const u_char *p, const u_char *end); static const u_char *njs_date_skip_spaces(const u_char *p, const u_char *end); static njs_int_t njs_date_month_parse(const u_char *p, const u_char *end); static const u_char *njs_date_time_parse(int64_t tm[], const u_char *p, const u_char *end); static int64_t njs_date_gmtoff_parse(const u_char *start, const u_char *end); static const u_char *njs_date_number_parse(int64_t *value, const u_char *p, const u_char *end, size_t size); static njs_int_t njs_date_string(njs_vm_t *vm, njs_value_t *retval, njs_date_fmt_t fmt, double time); static const njs_value_t njs_string_invalid_date = njs_string("Invalid Date"); njs_inline int64_t njs_mod(int64_t a, int64_t b) { int64_t m; m = a % b; return m + (m < 0) * b; } njs_inline int64_t njs_floor_div(int64_t a, int64_t b) { int64_t m; m = a % b; return (a - (m + (m < 0) * b)) / b; } njs_inline uint64_t njs_gettime(void) { struct timeval tv; gettimeofday(&tv, NULL); return (uint64_t) tv.tv_sec * 1000 + tv.tv_usec / 1000; } njs_inline double njs_timeclip(double time) { if (time < -8.64e15 || time > 8.64e15) { return NAN; } return trunc(time) + 0.0; } njs_inline int64_t njs_days_in_year(int64_t y) { return 365 + !(y % 4) - !(y % 100) + !(y % 400); } njs_inline int64_t njs_days_from_year(int64_t y) { return 365 * (y - 1970) + njs_floor_div(y - 1969, 4) - njs_floor_div(y - 1901, 100) + njs_floor_div(y - 1601, 400); } njs_inline double njs_make_day(int64_t yr, int64_t month, int64_t date) { double days; int64_t i, ym, mn, md; static const int min_year = -271821; static const int max_year = 275760; static const int month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; if (yr < min_year || yr > max_year || month < (min_year * 12) || month > (max_year * 12) || date < (min_year * 12 * 366) || date > (max_year * 12 * 366)) { return NAN; } mn = njs_mod(month, 12); ym = yr + (month - mn) / 12; days = njs_days_from_year(ym); for (i = 0; i < mn; i++) { md = month_days[i]; if (i == 1) { /* Leap day. */ md += njs_days_in_year(ym) - 365; } days += md; } return days + date - 1; } njs_inline int64_t njs_tz_offset(int64_t time) { time_t ti; struct tm tm; time /= 1000; #if (NJS_TIME_T_SIZE < 8) /* Smart truncation. */ if ((time_t) -1 < 0) { if (time < INT32_MIN) { time = INT32_MIN; } else if (time > INT32_MAX) { time = INT32_MAX; } } else { if (time < 0) { time = 0; } else if (time > UINT32_MAX) { time = UINT32_MAX; } } #endif ti = time; localtime_r(&ti, &tm); /* * As njs_timezone(&tm) may return value which is not a multiple of 60 * secs (see "zdump -v /etc/localtime" for MSK zone) rounding it to * minutes precision here to ensure: * var date = new Date() * date.valueOf() - date.getTimezoneOffset() * 60000 == Date.UTC() * which is expected by test262. */ return -njs_timezone(&tm) / 60; } njs_inline int64_t njs_year_from_days(int64_t *days) { int64_t y, d1, nd, d; d = *days; y = njs_floor_div(d * 10000, 3652425) + 1970; for ( ;; ) { d1 = d - njs_days_from_year(y); if (d1 < 0) { y--; } else { nd = njs_days_in_year(y); if (d1 < nd) { break; } y++; } } *days = d1; return y; } njs_inline double njs_make_date(int64_t tm[], njs_bool_t local) { double time, days; days = njs_make_day(tm[NJS_DATE_YR], tm[NJS_DATE_MON], tm[NJS_DATE_DAY]); if (njs_slow_path(isnan(days))) { return NAN; } time = ((tm[NJS_DATE_HR] * 60.0 + tm[NJS_DATE_MIN]) * 60.0 + tm[NJS_DATE_SEC]) * 1000.0 + tm[NJS_DATE_MSEC]; time += days * 86400000.0; if (time < -8.64e15 || time > 8.64e15) { return NAN; } if (local) { time += njs_tz_offset(time) * 60000; } return njs_timeclip(time); } njs_inline int64_t njs_destruct_date(double time, int64_t tm[], int index, njs_bool_t local) { int64_t days, wd, y, i, md, h, m, s, ms; static const int month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; if (njs_slow_path(isnan(time))) { time = 0; } else if (local) { time -= njs_tz_offset(time) * 60000; } h = njs_mod(time, 86400000); days = (time - h) / 86400000; ms = h % 1000; h = (h - ms) / 1000; s = h % 60; h = (h - s) / 60; m = h % 60; h = (h - m) / 60; wd = njs_mod(days + 4, 7); y = njs_year_from_days(&days); for (i = 0; i < 11; i++) { md = month_days[i]; if (i == 1) { /* Leap day. */ md += njs_days_in_year(y) - 365; } if (days < md) { break; } days -= md; } tm[NJS_DATE_YR] = y; tm[NJS_DATE_MON] = i; tm[NJS_DATE_DAY] = days + 1; tm[NJS_DATE_HR] = h; tm[NJS_DATE_MIN] = m; tm[NJS_DATE_SEC] = s; tm[NJS_DATE_MSEC] = ms; tm[NJS_DATE_WDAY] = wd; return tm[index]; } static njs_int_t njs_date_args(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, int64_t tm[]) { double num; njs_int_t ret; njs_uint_t i, n; njs_memzero(tm, NJS_DATE_MAX_FIELDS * sizeof(int64_t)); tm[NJS_DATE_DAY] = 1; n = njs_min(8, nargs); for (i = 1; i < n; i++) { ret = njs_value_to_number(vm, &args[i], &num); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (!isfinite(num)) { tm[NJS_DATE_YR] = INT64_MIN; continue; } tm[i] = njs_number_to_integer(num); } if (tm[NJS_DATE_YR] >= 0 && tm[NJS_DATE_YR] < 100) { tm[NJS_DATE_YR] += 1900; } return NJS_OK; } njs_date_t * njs_date_alloc(njs_vm_t *vm, double time) { njs_date_t *date; date = njs_mp_alloc(vm->mem_pool, sizeof(njs_date_t)); if (njs_slow_path(date == NULL)) { njs_memory_error(vm); return NULL; } njs_lvlhsh_init(&date->object.hash); njs_lvlhsh_init(&date->object.shared_hash); date->object.type = NJS_DATE; date->object.shared = 0; date->object.extensible = 1; date->object.error_data = 0; date->object.fast_array = 0; date->object.__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_DATE); date->object.slots = NULL; date->time = time; return date; } static njs_int_t njs_date_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double time; njs_int_t ret; njs_date_t *date; int64_t tm[NJS_DATE_MAX_FIELDS]; if (!vm->top_frame->ctor) { return njs_date_string(vm, retval, NJS_DATE_FMT_TO_STRING, njs_gettime()); } if (nargs == 1) { time = njs_gettime(); } else if (nargs == 2) { if (njs_is_object(&args[1])) { if (!njs_is_date(&args[1])) { ret = njs_value_to_primitive(vm, &args[1], &args[1], 0); if (njs_slow_path(ret != NJS_OK)) { return ret; } } } if (njs_is_date(&args[1])) { time = njs_date(&args[1])->time; } else if (njs_is_string(&args[1])) { time = njs_date_string_parse(&args[1]); } else { time = njs_timeclip(njs_number(&args[1])); } } else { ret = njs_date_args(vm, args, nargs, tm); if (njs_slow_path(ret != NJS_OK)) { return ret; } time = njs_make_date(tm, 1); } date = njs_date_alloc(vm, time); if (njs_slow_path(date == NULL)) { return NJS_ERROR; } njs_set_date(retval, date); return NJS_OK; } static njs_int_t njs_date_utc(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double time; njs_int_t ret; int64_t tm[NJS_DATE_MAX_FIELDS]; time = NAN; if (nargs > 1) { ret = njs_date_args(vm, args, nargs, tm); if (njs_slow_path(ret != NJS_OK)) { return ret; } time = njs_make_date(tm, 0); } njs_set_number(retval, time); return NJS_OK; } static njs_int_t njs_date_now(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_set_number(retval, njs_gettime()); return NJS_OK; } static njs_int_t njs_date_parse(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double time; njs_int_t ret; if (nargs > 1) { if (njs_slow_path(!njs_is_string(&args[1]))) { ret = njs_value_to_string(vm, &args[1], &args[1]); if (njs_slow_path(ret != NJS_OK)) { return ret; } } time = njs_date_string_parse(&args[1]); } else { time = NAN; } njs_set_number(retval, time); return NJS_OK; } static int64_t njs_date_utc_offset_parse(const u_char *start, const u_char *end) { int64_t utc_off, hour, min; const u_char *p; if (njs_fast_path(start + 2 < end && (*start == '+' || *start == '-'))) { p = njs_date_number_parse(&hour, start + 1, end, 2); if (njs_slow_path(p == NULL || hour > 23)) { return -1; } if (p < end && *p == ':') { p++; } p = njs_date_number_parse(&min, p, end, 2); if (njs_slow_path(p == NULL || min > 59)) { return -1; } utc_off = hour * 60 + min; if (*start == '-') { utc_off = -utc_off; } return utc_off; } return -1; } static double njs_date_string_parse(njs_value_t *date) { size_t ms_length; int64_t ext, utc_off; njs_str_t string; njs_bool_t sign, week, utc; const u_char *p, *next, *end; int64_t tm[NJS_DATE_MAX_FIELDS]; njs_string_get(date, &string); p = string.start; end = p + string.length; if (njs_slow_path(p >= end)) { return NAN; } if (*p == '+' || *p == '-') { p++; sign = 1; } else { sign = 0; } tm[NJS_DATE_MON] = 0; tm[NJS_DATE_DAY] = 1; tm[NJS_DATE_HR] = 0; tm[NJS_DATE_MIN] = 0; tm[NJS_DATE_SEC] = 0; tm[NJS_DATE_MSEC] = 0; next = njs_date_number_parse(&tm[NJS_DATE_YR], p, end, 4); if (next != NULL) { utc = 1; /* ISO-8601 format: "1970-09-28T06:00:00.000Z" */ if (next == end) { goto done; } if (*next != '-') { /* Extended ISO-8601 format: "+001970-09-28T06:00:00.000Z" */ next = njs_date_number_parse(&ext, next, end, 2); if (njs_slow_path(next == NULL)) { return NAN; } tm[NJS_DATE_YR] *= 100; tm[NJS_DATE_YR] += ext; if (string.start[0] == '-') { if (tm[NJS_DATE_YR] == 0) { return NAN; } tm[NJS_DATE_YR] = -tm[NJS_DATE_YR]; } if (next == end) { goto done; } if (*next != '-') { return NAN; } } p = njs_date_number_parse(&tm[NJS_DATE_MON], next + 1, end, 2); if (njs_slow_path(p == NULL)) { return NAN; } tm[NJS_DATE_MON]--; if (p == end) { goto done; } if (njs_slow_path(*p != '-')) { return NAN; } p = njs_date_number_parse(&tm[NJS_DATE_DAY], p + 1, end, 2); if (njs_slow_path(p == NULL)) { return NAN; } if (p == end) { goto done; } if (njs_slow_path(*p != 'T')) { return NAN; } end--; if (*end != 'Z') { utc = 0; end++; } p = njs_date_time_parse(tm, p + 1, end); if (njs_slow_path(p == NULL)) { return NAN; } if (p == end) { goto done; } if (njs_slow_path(p > end || *p != '.')) { return NAN; } p++; for (ms_length = 0; p + ms_length < end; ms_length++) { if (p[ms_length] < '0' || p[ms_length] > '9') { break; } } if (njs_slow_path(njs_date_number_parse(&tm[NJS_DATE_MSEC], p, end, njs_min(ms_length, 3)) == NULL)) { return NAN; } if (ms_length == 1) { tm[NJS_DATE_MSEC] *= 100; } else if (ms_length == 2) { tm[NJS_DATE_MSEC] *= 10; } p += ms_length; if (p < end) { utc_off = njs_date_utc_offset_parse(p, end); if (njs_slow_path(utc_off == -1)) { return NAN; } utc = 1; tm[NJS_DATE_MSEC] += -utc_off * 60000; } done: return njs_make_date(tm, !utc); } if (sign) { return NAN; } week = 1; for ( ;; ) { next = njs_date_number_parse(&tm[NJS_DATE_DAY], p, end, 2); if (next != NULL) { /* * RFC 2822 format: * "Mon, 28 Sep 1970 06:00:00 GMT", * "Mon, 28 Sep 1970 06:00:00 UTC", * "Mon, 28 Sep 1970 12:00:00 +0600". */ return njs_date_rfc2822_string_parse(tm, next, end); } tm[NJS_DATE_MON] = njs_date_month_parse(p, end); if (tm[NJS_DATE_MON] >= 0) { /* Date.toString() format: "Mon Sep 28 1970 12:00:00 GMT+0600". */ return njs_date_js_string_parse(tm, p + 3, end); } if (!week) { return NAN; } p = njs_date_skip_week_day(p, end); if (njs_slow_path(p == NULL)) { return NAN; } p = njs_date_skip_spaces(p, end); if (njs_slow_path(p == NULL)) { return NAN; } week = 0; } } static double njs_date_rfc2822_string_parse(int64_t tm[], const u_char *p, const u_char *end) { int64_t gmtoff; p = njs_date_skip_spaces(p, end); if (njs_slow_path(p == NULL)) { return NAN; } tm[NJS_DATE_MON] = njs_date_month_parse(p, end); if (njs_slow_path(tm[NJS_DATE_MON] < 0)) { return NAN; } p = njs_date_skip_spaces(p + 3, end); if (njs_slow_path(p == NULL)) { return NAN; } p = njs_date_number_parse(&tm[NJS_DATE_YR], p, end, 4); if (njs_slow_path(p == NULL)) { return NAN; } gmtoff = 0; if (p == end) { goto done; } p = njs_date_skip_spaces(p, end); if (njs_slow_path(p == NULL)) { return NAN; } if (p == end) { goto done; } p = njs_date_time_parse(tm, p, end); if (njs_slow_path(p == NULL)) { return NAN; } if (p == end) { goto done; } p = njs_date_skip_spaces(p, end); if (njs_slow_path(p == NULL)) { return NAN; } if (p == end) { goto done; } if (njs_slow_path(p + 2 >= end)) { return NAN; } if ((p[0] == 'G' && p[1] == 'M' && p[2] == 'T') || (p[0] == 'U' && p[1] == 'T' && p[2] == 'C')) { gmtoff = 0; } else { gmtoff = njs_date_gmtoff_parse(p, end); if (njs_slow_path(gmtoff == -1)) { return NAN; } } done: tm[NJS_DATE_MSEC] = -gmtoff * 60000; return njs_make_date(tm, 0); } static double njs_date_js_string_parse(int64_t tm[], const u_char *p, const u_char *end) { int64_t gmtoff; p = njs_date_skip_spaces(p, end); if (njs_slow_path(p == NULL)) { return NAN; } p = njs_date_number_parse(&tm[NJS_DATE_DAY], p, end, 2); if (njs_slow_path(p == NULL)) { return NAN; } p = njs_date_skip_spaces(p, end); if (njs_slow_path(p == NULL)) { return NAN; } p = njs_date_number_parse(&tm[NJS_DATE_YR], p, end, 4); if (njs_slow_path(p == NULL)) { return NAN; } if (p == end) { goto done; } p = njs_date_skip_spaces(p, end); if (njs_slow_path(p == NULL)) { return NAN; } if (p == end) { goto done; } p = njs_date_time_parse(tm, p, end); if (njs_slow_path(p == NULL)) { return NAN; } if (p == end) { goto done; } p = njs_date_skip_spaces(p, end); if (njs_slow_path(p == NULL)) { return NAN; } if (p == end) { goto done; } if (p + 2 < end && p[0] == 'G' && p[1] == 'M' && p[2] == 'T') { gmtoff = njs_date_gmtoff_parse(&p[3], end); if (njs_fast_path(gmtoff != -1)) { tm[NJS_DATE_MSEC] = -gmtoff * 60000; return njs_make_date(tm, 0); } } return NAN; done: return njs_make_date(tm, 0); } static const u_char * njs_date_skip_week_day(const u_char *p, const u_char *end) { while (p < end) { if (*p == ' ') { return p; } p++; } return NULL; } static const u_char * njs_date_skip_spaces(const u_char *p, const u_char *end) { if (p < end && *p++ == ' ') { while (p < end) { if (*p != ' ') { return p; } p++; } return p; } return NULL; } static njs_int_t njs_date_month_parse(const u_char *p, const u_char *end) { if (p + 2 < end) { switch (p[0]) { case 'J': if (p[1] == 'a' && p[2] == 'n') { return 0; } if (p[1] == 'u') { if (p[2] == 'n') { return 5; } if (p[2] == 'l') { return 6; } } break; case 'F': if (p[1] == 'e' && p[2] == 'b') { return 1; } break; case 'M': if (p[1] == 'a') { if (p[2] == 'r') { return 2; } if (p[2] == 'y') { return 4; } } break; case 'A': if (p[1] == 'p' && p[2] == 'r') { return 3; } if (p[1] == 'u' && p[2] == 'g') { return 7; } break; case 'S': if (p[1] == 'e' && p[2] == 'p') { return 8; } break; case 'O': if (p[1] == 'c' && p[2] == 't') { return 9; } break; case 'N': if (p[1] == 'o' && p[2] == 'v') { return 10; } break; case 'D': if (p[1] == 'e' && p[2] == 'c') { return 11; } break; } } return -1; } static const u_char * njs_date_time_parse(int64_t tm[], const u_char *p, const u_char *end) { p = njs_date_number_parse(&tm[NJS_DATE_HR], p, end, 2); if (njs_slow_path(p == NULL)) { return p; } if (njs_slow_path(p >= end || *p != ':')) { return NULL; } p = njs_date_number_parse(&tm[NJS_DATE_MIN], p + 1, end, 2); if (njs_slow_path(p == NULL)) { return p; } if (p == end) { return p; } if (njs_slow_path(*p != ':')) { return NULL; } return njs_date_number_parse(&tm[NJS_DATE_SEC], p + 1, end, 2); } static int64_t njs_date_gmtoff_parse(const u_char *start, const u_char *end) { int64_t gmtoff, hour, min; const u_char *p; if (njs_fast_path(start + 4 < end && (*start == '+' || *start == '-'))) { p = njs_date_number_parse(&hour, start + 1, end, 2); if (njs_slow_path(p == NULL)) { return -1; } p = njs_date_number_parse(&min, p, end, 2); if (njs_slow_path(p == NULL)) { return -1; } gmtoff = hour * 60 + min; if (*start == '-') { gmtoff = -gmtoff; } return gmtoff; } return -1; } static const u_char * njs_date_number_parse(int64_t *value, const u_char *p, const u_char *end, size_t size) { u_char c; njs_int_t n; n = 0; do { if (njs_slow_path(p >= end)) { return NULL; } c = *p++; /* Values below '0' become >= 208. */ c = c - '0'; if (njs_slow_path(c > 9)) { return NULL; } n = n * 10 + c; size--; } while (size != 0); *value = n; return p; } static const njs_object_prop_t njs_date_constructor_properties[] = { NJS_DECLARE_PROP_LENGTH(7), NJS_DECLARE_PROP_NAME("Date"), NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), NJS_DECLARE_PROP_NATIVE("UTC", njs_date_utc, 7, 0), NJS_DECLARE_PROP_NATIVE("now", njs_date_now, 0, 0), NJS_DECLARE_PROP_NATIVE("parse", njs_date_parse, 1, 0), }; const njs_object_init_t njs_date_constructor_init = { njs_date_constructor_properties, njs_nitems(njs_date_constructor_properties), }; static njs_int_t njs_date_prototype_value_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { if (njs_slow_path(!njs_is_date(&args[0]))) { njs_type_error(vm, "cannot convert %s to date", njs_type_string(args[0].type)); return NJS_ERROR; } njs_set_number(retval, njs_date(&args[0])->time); return NJS_OK; } static njs_int_t njs_date_prototype_to_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t fmt, njs_value_t *retval) { double time; if (njs_slow_path(!njs_is_date(&args[0]))) { njs_type_error(vm, "cannot convert %s to date", njs_type_string(args[0].type)); return NJS_ERROR; } time = njs_date(&args[0])->time; if (fmt == NJS_DATE_FMT_TO_ISO_STRING && isnan(time)) { njs_range_error(vm, "Invalid time value"); return NJS_ERROR; } return njs_date_string(vm, retval, fmt, time); } static njs_int_t njs_date_string(njs_vm_t *vm, njs_value_t *retval, njs_date_fmt_t fmt, double time) { int year, tz; u_char *p, sign; u_char buf[NJS_DATE_TIME_LEN]; int64_t tm[NJS_DATE_MAX_FIELDS]; static const char *week[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; static const char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; if (njs_slow_path(isnan(time))) { *retval = njs_string_invalid_date; return NJS_OK; } p = buf; switch (fmt) { case NJS_DATE_FMT_TO_ISO_STRING: case NJS_DATE_FMT_TO_UTC_STRING: njs_destruct_date(time, tm, 0, 0); year = tm[NJS_DATE_YR]; if (fmt == NJS_DATE_FMT_TO_UTC_STRING) { p = njs_sprintf(p, buf + NJS_DATE_TIME_LEN, "%s, %02L %s %04d %02L:%02L:%02L GMT", week[tm[NJS_DATE_WDAY]], tm[NJS_DATE_DAY], month[tm[NJS_DATE_MON]], year, tm[NJS_DATE_HR], tm[NJS_DATE_MIN], tm[NJS_DATE_SEC]); break; } if (year >= 0 && year <= 9999) { p = njs_sprintf(p, buf + NJS_DATE_TIME_LEN, "%04d", year); } else { if (year > 0) { *p++ = '+'; } p = njs_sprintf(p, buf + NJS_DATE_TIME_LEN, "%06d", year); } p = njs_sprintf(p, buf + NJS_DATE_TIME_LEN, "-%02L-%02LT%02L:%02L:%02L.%03LZ", tm[NJS_DATE_MON] + 1, tm[NJS_DATE_DAY], tm[NJS_DATE_HR], tm[NJS_DATE_MIN], tm[NJS_DATE_SEC], tm[NJS_DATE_MSEC]); break; case NJS_DATE_FMT_TO_TIME_STRING: case NJS_DATE_FMT_TO_DATE_STRING: case NJS_DATE_FMT_TO_STRING: default: njs_destruct_date(time, tm, 0, 1); if (fmt != NJS_DATE_FMT_TO_TIME_STRING) { p = njs_sprintf(p, buf + NJS_DATE_TIME_LEN, "%s %s %02L %04L", week[tm[NJS_DATE_WDAY]], month[tm[NJS_DATE_MON]], tm[NJS_DATE_DAY], tm[NJS_DATE_YR]); } if (fmt != NJS_DATE_FMT_TO_DATE_STRING) { tz = -njs_tz_offset(time); sign = (tz < 0) ? '-' : '+'; if (tz < 0) { tz = -tz; } if (p != buf) { *p++ = ' '; } p = njs_sprintf(p, buf + NJS_DATE_TIME_LEN, "%02L:%02L:%02L GMT%c%02d%02d", tm[NJS_DATE_HR], tm[NJS_DATE_MIN], tm[NJS_DATE_SEC], sign, tz / 60, tz % 60); } } return njs_string_new(vm, retval, buf, p - buf, p - buf); } njs_int_t njs_date_to_string(njs_vm_t *vm, njs_value_t *retval, const njs_value_t *date) { if (njs_slow_path(!njs_is_date(date))) { njs_type_error(vm, "cannot convert %s to date", njs_type_string(date->type)); return NJS_ERROR; } return njs_date_string(vm, retval, NJS_DATE_FMT_TO_ISO_STRING, njs_date(date)->time); } static njs_int_t njs_date_prototype_get_field(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t magic, njs_value_t *retval) { double value; int64_t tm[NJS_DATE_MAX_FIELDS]; if (njs_slow_path(!njs_is_date(&args[0]))) { njs_type_error(vm, "cannot convert %s to date", njs_type_string(args[0].type)); return NJS_ERROR; } value = njs_date(&args[0])->time; if (njs_fast_path(!isnan(value))) { value = njs_destruct_date(value, tm, magic & 0xf, magic & 0x40); } njs_set_number(retval, value); return NJS_OK; } static njs_int_t njs_date_prototype_get_timezone_offset(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double value; if (njs_slow_path(!njs_is_date(&args[0]))) { njs_type_error(vm, "cannot convert %s to date", njs_type_string(args[0].type)); return NJS_ERROR; } value = njs_date(&args[0])->time; if (njs_fast_path(!isnan(value))) { value = njs_tz_offset(value); } njs_set_number(retval, value); return NJS_OK; } static njs_int_t njs_date_prototype_set_time(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double time; njs_int_t ret; if (njs_slow_path(!njs_is_date(&args[0]))) { njs_type_error(vm, "cannot convert %s to date", njs_type_string(args[0].type)); return NJS_ERROR; } if (nargs > 1) { if (njs_slow_path(!njs_is_number(&args[1]))) { ret = njs_value_to_numeric(vm, &args[1], &args[1]); if (njs_slow_path(ret != NJS_OK)) { return ret; } } time = njs_timeclip(njs_number(&args[1])); } else { time = NAN; } njs_date(&args[0])->time = time; njs_set_number(retval, time); return NJS_OK; } static njs_int_t njs_date_prototype_set_fields(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t magic, njs_value_t *retval) { double time, num; njs_int_t ret; njs_uint_t since, left, i; int64_t tm[NJS_DATE_MAX_FIELDS]; if (njs_slow_path(!njs_is_date(&args[0]))) { njs_type_error(vm, "cannot convert %s to date", njs_type_string(args[0].type)); return NJS_ERROR; } time = njs_date(&args[0])->time; since = magic & 7; if (njs_slow_path(nargs < 2 || (since != NJS_DATE_YR && isnan(time)))) { time = NAN; goto done; } i = 1; left = njs_min(((magic >> 3) & 7), nargs - 1); njs_destruct_date(time, tm, 0, magic & 0x40); do { ret = njs_value_to_number(vm, &args[i++], &num); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (!isfinite(num)) { tm[NJS_DATE_YR] = INT64_MIN; continue; } tm[since++] = njs_number_to_integer(num); } while (--left); time = njs_make_date(tm, 1); done: njs_date(&args[0])->time = time; njs_set_number(retval, time); return NJS_OK; } static njs_int_t njs_date_prototype_to_json(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_value_t value; njs_lvlhsh_query_t lhq; static const njs_value_t to_iso_string = njs_string("toISOString"); if (njs_is_object(njs_argument(args, 0))) { njs_object_property_init(&lhq, &to_iso_string, NJS_TO_ISO_STRING_HASH); ret = njs_object_property(vm, njs_object(njs_argument(args, 0)), &lhq, &value); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (njs_is_function(&value)) { return njs_function_apply(vm, njs_function(&value), args, nargs, retval); } } njs_type_error(vm, "\"this\" argument is not an object"); return NJS_ERROR; } static const njs_object_prop_t njs_date_prototype_properties[] = { NJS_DECLARE_PROP_HANDLER("__proto__", njs_primitive_prototype_get_proto, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("constructor", njs_object_prototype_create_constructor, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_NATIVE("valueOf", njs_date_prototype_value_of, 0, 0), NJS_DECLARE_PROP_NATIVE("toString", njs_date_prototype_to_string, 0, NJS_DATE_FMT_TO_STRING), NJS_DECLARE_PROP_NATIVE("toDateString", njs_date_prototype_to_string, 0, NJS_DATE_FMT_TO_DATE_STRING), NJS_DECLARE_PROP_NATIVE("toTimeString", njs_date_prototype_to_string, 0, NJS_DATE_FMT_TO_TIME_STRING), NJS_DECLARE_PROP_NATIVE("toLocaleString", njs_date_prototype_to_string, 0, NJS_DATE_FMT_TO_STRING), NJS_DECLARE_PROP_LNATIVE("toLocaleDateString", njs_date_prototype_to_string, 0, NJS_DATE_FMT_TO_DATE_STRING), NJS_DECLARE_PROP_LNATIVE("toLocaleTimeString", njs_date_prototype_to_string, 0, NJS_DATE_FMT_TO_TIME_STRING), NJS_DECLARE_PROP_NATIVE("toUTCString", njs_date_prototype_to_string, 0, NJS_DATE_FMT_TO_UTC_STRING), NJS_DECLARE_PROP_NATIVE("toISOString", njs_date_prototype_to_string, 0, NJS_DATE_FMT_TO_ISO_STRING), NJS_DECLARE_PROP_NATIVE("toJSON", njs_date_prototype_to_json, 1, 0), NJS_DECLARE_PROP_NATIVE("getTime", njs_date_prototype_value_of, 0, 0), NJS_DECLARE_PROP_NATIVE("getFullYear", njs_date_prototype_get_field, 0, njs_date_magic(NJS_DATE_YR, 1)), NJS_DECLARE_PROP_NATIVE("getUTCFullYear", njs_date_prototype_get_field, 0, njs_date_magic(NJS_DATE_YR, 0)), NJS_DECLARE_PROP_NATIVE("getMonth", njs_date_prototype_get_field, 0, njs_date_magic(NJS_DATE_MON, 1)), NJS_DECLARE_PROP_NATIVE("getUTCMonth", njs_date_prototype_get_field, 0, njs_date_magic(NJS_DATE_MON, 0)), NJS_DECLARE_PROP_NATIVE("getDate", njs_date_prototype_get_field, 0, njs_date_magic(NJS_DATE_DAY, 1)), NJS_DECLARE_PROP_NATIVE("getUTCDate", njs_date_prototype_get_field, 0, njs_date_magic(NJS_DATE_DAY, 0)), NJS_DECLARE_PROP_NATIVE("getDay", njs_date_prototype_get_field, 0, njs_date_magic(NJS_DATE_WDAY, 1)), NJS_DECLARE_PROP_NATIVE("getUTCDay", njs_date_prototype_get_field, 0, njs_date_magic(NJS_DATE_WDAY, 0)), NJS_DECLARE_PROP_NATIVE("getHours", njs_date_prototype_get_field, 0, njs_date_magic(NJS_DATE_HR, 1)), NJS_DECLARE_PROP_NATIVE("getUTCHours", njs_date_prototype_get_field, 0, njs_date_magic(NJS_DATE_HR, 0)), NJS_DECLARE_PROP_NATIVE("getMinutes", njs_date_prototype_get_field, 0, njs_date_magic(NJS_DATE_MIN, 1)), NJS_DECLARE_PROP_NATIVE("getUTCMinutes", njs_date_prototype_get_field, 0, njs_date_magic(NJS_DATE_MIN, 0)), NJS_DECLARE_PROP_NATIVE("getSeconds", njs_date_prototype_get_field, 0, njs_date_magic(NJS_DATE_SEC, 1)), NJS_DECLARE_PROP_NATIVE("getUTCSeconds", njs_date_prototype_get_field, 0, njs_date_magic(NJS_DATE_SEC, 0)), NJS_DECLARE_PROP_LNATIVE("getMilliseconds", njs_date_prototype_get_field, 0, njs_date_magic(NJS_DATE_MSEC, 1)), NJS_DECLARE_PROP_LNATIVE("getUTCMilliseconds", njs_date_prototype_get_field, 0, njs_date_magic(NJS_DATE_MSEC, 0)), NJS_DECLARE_PROP_LNATIVE("getTimezoneOffset", njs_date_prototype_get_timezone_offset, 0, 0), NJS_DECLARE_PROP_NATIVE("setTime", njs_date_prototype_set_time, 1, 0), NJS_DECLARE_PROP_LNATIVE("setMilliseconds", njs_date_prototype_set_fields, 1, njs_date_magic2(NJS_DATE_MSEC, 1, 1)), NJS_DECLARE_PROP_LNATIVE("setUTCMilliseconds", njs_date_prototype_set_fields, 1, njs_date_magic2(NJS_DATE_MSEC, 1, 0)), NJS_DECLARE_PROP_NATIVE("setSeconds", njs_date_prototype_set_fields, 2, njs_date_magic2(NJS_DATE_SEC, 2, 1)), NJS_DECLARE_PROP_NATIVE("setUTCSeconds", njs_date_prototype_set_fields, 2, njs_date_magic2(NJS_DATE_SEC, 2, 0)), NJS_DECLARE_PROP_NATIVE("setMinutes", njs_date_prototype_set_fields, 3, njs_date_magic2(NJS_DATE_MIN, 3, 1)), NJS_DECLARE_PROP_NATIVE("setUTCMinutes", njs_date_prototype_set_fields, 3, njs_date_magic2(NJS_DATE_MIN, 3, 0)), NJS_DECLARE_PROP_NATIVE("setHours", njs_date_prototype_set_fields, 4, njs_date_magic2(NJS_DATE_HR, 4, 1)), NJS_DECLARE_PROP_NATIVE("setUTCHours", njs_date_prototype_set_fields, 4, njs_date_magic2(NJS_DATE_HR, 4, 0)), NJS_DECLARE_PROP_NATIVE("setDate", njs_date_prototype_set_fields, 1, njs_date_magic2(NJS_DATE_DAY, 1, 1)), NJS_DECLARE_PROP_NATIVE("setUTCDate", njs_date_prototype_set_fields, 1, njs_date_magic2(NJS_DATE_DAY, 1, 0)), NJS_DECLARE_PROP_NATIVE("setMonth", njs_date_prototype_set_fields, 2, njs_date_magic2(NJS_DATE_MON, 2, 1)), NJS_DECLARE_PROP_NATIVE("setUTCMonth", njs_date_prototype_set_fields, 2, njs_date_magic2(NJS_DATE_MON, 2, 0)), NJS_DECLARE_PROP_NATIVE("setFullYear", njs_date_prototype_set_fields, 3, njs_date_magic2(NJS_DATE_YR, 3, 1)), NJS_DECLARE_PROP_NATIVE("setUTCFullYear", njs_date_prototype_set_fields, 3, njs_date_magic2(NJS_DATE_YR, 3, 0)), }; const njs_object_init_t njs_date_prototype_init = { njs_date_prototype_properties, njs_nitems(njs_date_prototype_properties), }; const njs_object_type_init_t njs_date_type_init = { .constructor = njs_native_ctor(njs_date_constructor, 7, 0), .constructor_props = &njs_date_constructor_init, .prototype_props = &njs_date_prototype_init, .prototype_value = { .object = { .type = NJS_OBJECT } }, }; njs-0.8.9/src/njs_date.h000066400000000000000000000005661474132077100150660ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_DATE_H_INCLUDED_ #define _NJS_DATE_H_INCLUDED_ njs_date_t *njs_date_alloc(njs_vm_t *vm, double time); njs_int_t njs_date_to_string(njs_vm_t *vm, njs_value_t *retval, const njs_value_t *date); extern const njs_object_type_init_t njs_date_type_init; #endif /* _NJS_DATE_H_INCLUDED_ */ njs-0.8.9/src/njs_disassembler.c000066400000000000000000000454171474132077100166250ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include typedef struct { njs_vmcode_t operation; size_t size; njs_str_t name; } njs_code_name_t; static njs_code_name_t code_names[] = { { NJS_VMCODE_PUT_ARG, sizeof(njs_vmcode_1addr_t), njs_str("PUT ARG ") }, { NJS_VMCODE_OBJECT, sizeof(njs_vmcode_object_t), njs_str("OBJECT ") }, { NJS_VMCODE_FUNCTION, sizeof(njs_vmcode_function_t), njs_str("FUNCTION ") }, { NJS_VMCODE_ARGUMENTS, sizeof(njs_vmcode_arguments_t), njs_str("ARGUMENTS ") }, { NJS_VMCODE_REGEXP, sizeof(njs_vmcode_regexp_t), njs_str("REGEXP ") }, { NJS_VMCODE_TEMPLATE_LITERAL, sizeof(njs_vmcode_template_literal_t), njs_str("TEMPLATE LITERAL") }, { NJS_VMCODE_FUNCTION_COPY, sizeof(njs_vmcode_function_copy_t), njs_str("FUNCTION COPY ") }, { NJS_VMCODE_PROPERTY_GET, sizeof(njs_vmcode_prop_get_t), njs_str("PROP GET ") }, { NJS_VMCODE_GLOBAL_GET, sizeof(njs_vmcode_prop_get_t), njs_str("GLOBAL GET ") }, { NJS_VMCODE_PROPERTY_INIT, sizeof(njs_vmcode_prop_set_t), njs_str("PROP INIT ") }, { NJS_VMCODE_PROTO_INIT, sizeof(njs_vmcode_prop_set_t), njs_str("PROTO INIT ") }, { NJS_VMCODE_PROPERTY_SET, sizeof(njs_vmcode_prop_set_t), njs_str("PROP SET ") }, { NJS_VMCODE_PROPERTY_IN, sizeof(njs_vmcode_3addr_t), njs_str("PROP IN ") }, { NJS_VMCODE_PROPERTY_DELETE, sizeof(njs_vmcode_3addr_t), njs_str("PROP DELETE ") }, { NJS_VMCODE_INSTANCE_OF, sizeof(njs_vmcode_instance_of_t), njs_str("INSTANCE OF ") }, { NJS_VMCODE_FUNCTION_CALL, sizeof(njs_vmcode_function_call_t), njs_str("FUNCTION CALL ") }, { NJS_VMCODE_RETURN, sizeof(njs_vmcode_return_t), njs_str("RETURN ") }, { NJS_VMCODE_STOP, sizeof(njs_vmcode_stop_t), njs_str("STOP ") }, { NJS_VMCODE_INCREMENT, sizeof(njs_vmcode_3addr_t), njs_str("INC ") }, { NJS_VMCODE_DECREMENT, sizeof(njs_vmcode_3addr_t), njs_str("DEC ") }, { NJS_VMCODE_POST_INCREMENT, sizeof(njs_vmcode_3addr_t), njs_str("POST INC ") }, { NJS_VMCODE_POST_DECREMENT, sizeof(njs_vmcode_3addr_t), njs_str("POST DEC ") }, { NJS_VMCODE_DELETE, sizeof(njs_vmcode_2addr_t), njs_str("DELETE ") }, { NJS_VMCODE_VOID, sizeof(njs_vmcode_2addr_t), njs_str("VOID ") }, { NJS_VMCODE_TYPEOF, sizeof(njs_vmcode_2addr_t), njs_str("TYPEOF ") }, { NJS_VMCODE_TO_PROPERTY_KEY, sizeof(njs_vmcode_2addr_t), njs_str("TO PROP KEY ") }, { NJS_VMCODE_TO_PROPERTY_KEY_CHK, sizeof(njs_vmcode_3addr_t), njs_str("TO PROP KEY CHK ") }, { NJS_VMCODE_SET_FUNCTION_NAME, sizeof(njs_vmcode_2addr_t), njs_str("SET FUNC NAME ") }, { NJS_VMCODE_UNARY_PLUS, sizeof(njs_vmcode_2addr_t), njs_str("PLUS ") }, { NJS_VMCODE_UNARY_NEGATION, sizeof(njs_vmcode_2addr_t), njs_str("NEGATION ") }, { NJS_VMCODE_ADDITION, sizeof(njs_vmcode_3addr_t), njs_str("ADD ") }, { NJS_VMCODE_SUBTRACTION, sizeof(njs_vmcode_3addr_t), njs_str("SUBTRACT ") }, { NJS_VMCODE_MULTIPLICATION, sizeof(njs_vmcode_3addr_t), njs_str("MULTIPLY ") }, { NJS_VMCODE_EXPONENTIATION, sizeof(njs_vmcode_3addr_t), njs_str("POWER ") }, { NJS_VMCODE_DIVISION, sizeof(njs_vmcode_3addr_t), njs_str("DIVIDE ") }, { NJS_VMCODE_REMAINDER, sizeof(njs_vmcode_3addr_t), njs_str("REMAINDER ") }, { NJS_VMCODE_LEFT_SHIFT, sizeof(njs_vmcode_3addr_t), njs_str("LEFT SHIFT ") }, { NJS_VMCODE_RIGHT_SHIFT, sizeof(njs_vmcode_3addr_t), njs_str("RIGHT SHIFT ") }, { NJS_VMCODE_UNSIGNED_RIGHT_SHIFT, sizeof(njs_vmcode_3addr_t), njs_str("USGN RIGHT SHIFT") }, { NJS_VMCODE_LOGICAL_NOT, sizeof(njs_vmcode_2addr_t), njs_str("LOGICAL NOT ") }, { NJS_VMCODE_BITWISE_NOT, sizeof(njs_vmcode_2addr_t), njs_str("BINARY NOT ") }, { NJS_VMCODE_BITWISE_AND, sizeof(njs_vmcode_3addr_t), njs_str("BINARY AND ") }, { NJS_VMCODE_BITWISE_XOR, sizeof(njs_vmcode_3addr_t), njs_str("BINARY XOR ") }, { NJS_VMCODE_BITWISE_OR, sizeof(njs_vmcode_3addr_t), njs_str("BINARY OR ") }, { NJS_VMCODE_EQUAL, sizeof(njs_vmcode_3addr_t), njs_str("EQUAL ") }, { NJS_VMCODE_NOT_EQUAL, sizeof(njs_vmcode_3addr_t), njs_str("NOT EQUAL ") }, { NJS_VMCODE_LESS, sizeof(njs_vmcode_3addr_t), njs_str("LESS ") }, { NJS_VMCODE_LESS_OR_EQUAL, sizeof(njs_vmcode_3addr_t), njs_str("LESS OR EQUAL ") }, { NJS_VMCODE_GREATER, sizeof(njs_vmcode_3addr_t), njs_str("GREATER ") }, { NJS_VMCODE_GREATER_OR_EQUAL, sizeof(njs_vmcode_3addr_t), njs_str("GREATER OR EQUAL") }, { NJS_VMCODE_STRICT_EQUAL, sizeof(njs_vmcode_3addr_t), njs_str("STRICT EQUAL ") }, { NJS_VMCODE_STRICT_NOT_EQUAL, sizeof(njs_vmcode_3addr_t), njs_str("STRICT NOT EQUAL") }, { NJS_VMCODE_MOVE, sizeof(njs_vmcode_move_t), njs_str("MOVE ") }, { NJS_VMCODE_THROW, sizeof(njs_vmcode_throw_t), njs_str("THROW ") }, { NJS_VMCODE_LET, sizeof(njs_vmcode_variable_t), njs_str("LET ") }, { NJS_VMCODE_LET_UPDATE, sizeof(njs_vmcode_variable_t), njs_str("LET UPDATE ") }, { NJS_VMCODE_INITIALIZATION_TEST, sizeof(njs_vmcode_variable_t), njs_str("INIT TEST ") }, { NJS_VMCODE_NOT_INITIALIZED, sizeof(njs_vmcode_variable_t), njs_str("NOT INIT ") }, { NJS_VMCODE_ASSIGNMENT_ERROR, sizeof(njs_vmcode_variable_t), njs_str("ASSIGNMENT ERROR") }, { NJS_VMCODE_DEBUGGER, sizeof(njs_vmcode_debugger_t), njs_str("DEBUGGER ") }, { NJS_VMCODE_AWAIT, sizeof(njs_vmcode_await_t), njs_str("AWAIT ") }, }; void njs_disassembler(njs_vm_t *vm) { njs_uint_t n; njs_vm_code_t *code; code = vm->codes->start; n = vm->codes->items; while (n != 0) { njs_printf("%V:%V\n", &code->file, &code->name); njs_disassemble(code->start, code->end, -1, code->lines); code++; n--; } njs_printf("\n"); } void njs_disassemble(u_char *start, u_char *end, njs_int_t count, njs_arr_t *lines) { u_char *p; uint32_t line; njs_str_t *name; njs_uint_t n; const char *type; njs_vmcode_t operation; njs_code_name_t *code_name; njs_vmcode_jump_t *jump; njs_vmcode_error_t *error; njs_vmcode_1addr_t *code1; njs_vmcode_2addr_t *code2; njs_vmcode_3addr_t *code3; njs_vmcode_array_t *array; njs_vmcode_catch_t *catch; njs_vmcode_import_t *import; njs_vmcode_finally_t *finally; njs_vmcode_try_end_t *try_end; njs_vmcode_try_start_t *try_start; njs_vmcode_cond_jump_t *cond_jump; njs_vmcode_test_jump_t *test_jump; njs_vmcode_prop_next_t *prop_next; njs_vmcode_try_return_t *try_return; njs_vmcode_equal_jump_t *equal; njs_vmcode_prop_foreach_t *prop_foreach; njs_vmcode_method_frame_t *method; njs_vmcode_prop_accessor_t *prop_accessor; njs_vmcode_try_trampoline_t *try_tramp; njs_vmcode_function_frame_t *function; /* * On some 32-bit platform uintptr_t is int and compilers warn * about %l format modifier. size_t has the size as pointer so * there is no run-time overhead. */ p = start; while (((p < end) && (count == -1)) || (count-- > 0)) { operation = *(njs_vmcode_t *) p; line = njs_lookup_line(lines, p - start); if (operation == NJS_VMCODE_ARRAY) { array = (njs_vmcode_array_t *) p; njs_printf("%5uD | %05uz ARRAY %04Xz %uz%s\n", line, p - start, (size_t) array->retval, (size_t) array->length, array->ctor ? " INIT" : ""); p += sizeof(njs_vmcode_array_t); continue; } if (operation == NJS_VMCODE_IF_TRUE_JUMP) { cond_jump = (njs_vmcode_cond_jump_t *) p; njs_printf("%5uD | %05uz JUMP IF TRUE %04Xz %z\n", line, p - start, (size_t) cond_jump->cond, (size_t) cond_jump->offset); p += sizeof(njs_vmcode_cond_jump_t); continue; } if (operation == NJS_VMCODE_IF_FALSE_JUMP) { cond_jump = (njs_vmcode_cond_jump_t *) p; njs_printf("%5uD | %05uz JUMP IF FALSE %04Xz %z\n", line, p - start, (size_t) cond_jump->cond, (size_t) cond_jump->offset); p += sizeof(njs_vmcode_cond_jump_t); continue; } if (operation == NJS_VMCODE_JUMP) { jump = (njs_vmcode_jump_t *) p; njs_printf("%5uD | %05uz JUMP %z\n", line, p - start, (size_t) jump->offset); p += sizeof(njs_vmcode_jump_t); continue; } if (operation == NJS_VMCODE_IF_EQUAL_JUMP) { equal = (njs_vmcode_equal_jump_t *) p; njs_printf("%5uD | %05uz JUMP IF EQUAL %04Xz %04Xz %z\n", line, p - start, (size_t) equal->value1, (size_t) equal->value2, (size_t) equal->offset); p += sizeof(njs_vmcode_equal_jump_t); continue; } if (operation == NJS_VMCODE_TEST_IF_TRUE) { test_jump = (njs_vmcode_test_jump_t *) p; njs_printf("%5uD | %05uz TEST IF TRUE %04Xz %04Xz %z\n", line, p - start, (size_t) test_jump->retval, (size_t) test_jump->value, (size_t) test_jump->offset); p += sizeof(njs_vmcode_test_jump_t); continue; } if (operation == NJS_VMCODE_TEST_IF_FALSE) { test_jump = (njs_vmcode_test_jump_t *) p; njs_printf("%5uD | %05uz TEST IF FALSE %04Xz %04Xz %z\n", line, p - start, (size_t) test_jump->retval, (size_t) test_jump->value, (size_t) test_jump->offset); p += sizeof(njs_vmcode_test_jump_t); continue; } if (operation == NJS_VMCODE_COALESCE) { test_jump = (njs_vmcode_test_jump_t *) p; njs_printf("%5uD | %05uz COALESCE %04Xz %04Xz %z\n", line, p - start, (size_t) test_jump->retval, (size_t) test_jump->value, (size_t) test_jump->offset); p += sizeof(njs_vmcode_test_jump_t); continue; } if (operation == NJS_VMCODE_FUNCTION_FRAME) { function = (njs_vmcode_function_frame_t *) p; njs_printf("%5uD | %05uz FUNCTION FRAME %04Xz %uz%s\n", line, p - start, (size_t) function->name, function->nargs, function->ctor ? " CTOR" : ""); p += sizeof(njs_vmcode_function_frame_t); continue; } if (operation == NJS_VMCODE_METHOD_FRAME) { method = (njs_vmcode_method_frame_t *) p; njs_printf("%5uD | %05uz METHOD FRAME %04Xz %04Xz %uz%s\n", line, p - start, (size_t) method->object, (size_t) method->method, method->nargs, method->ctor ? " CTOR" : ""); p += sizeof(njs_vmcode_method_frame_t); continue; } if (operation == NJS_VMCODE_PROPERTY_FOREACH) { prop_foreach = (njs_vmcode_prop_foreach_t *) p; njs_printf("%5uD | %05uz PROP FOREACH %04Xz %04Xz %z\n", line, p - start, (size_t) prop_foreach->next, (size_t) prop_foreach->object, (size_t) prop_foreach->offset); p += sizeof(njs_vmcode_prop_foreach_t); continue; } if (operation == NJS_VMCODE_PROPERTY_NEXT) { prop_next = (njs_vmcode_prop_next_t *) p; njs_printf("%5uD | %05uz PROP NEXT %04Xz %04Xz %04Xz %z\n", line, p - start, (size_t) prop_next->retval, (size_t) prop_next->object, (size_t) prop_next->next, (size_t) prop_next->offset); p += sizeof(njs_vmcode_prop_next_t); continue; } if (operation == NJS_VMCODE_PROPERTY_ACCESSOR) { prop_accessor = (njs_vmcode_prop_accessor_t *) p; njs_printf("%5uD | %05uz PROP %s ACCESSOR %04Xz %04Xz %04Xz\n", line, p - start, (prop_accessor->type == NJS_OBJECT_PROP_GETTER) ? "GET" : "SET", (size_t) prop_accessor->value, (size_t) prop_accessor->object, (size_t) prop_accessor->property); p += sizeof(njs_vmcode_prop_accessor_t); continue; } if (operation == NJS_VMCODE_IMPORT) { import = (njs_vmcode_import_t *) p; njs_printf("%5uD | %05uz IMPORT %04Xz %V\n", line, p - start, (size_t) import->retval, &import->module->name); p += sizeof(njs_vmcode_import_t); continue; } if (operation == NJS_VMCODE_TRY_START) { try_start = (njs_vmcode_try_start_t *) p; njs_printf("%5uD | %05uz TRY START %04Xz %04Xz %z\n", line, p - start, (size_t) try_start->exception_value, (size_t) try_start->exit_value, (size_t) try_start->offset); p += sizeof(njs_vmcode_try_start_t); continue; } if (operation == NJS_VMCODE_TRY_BREAK) { try_tramp = (njs_vmcode_try_trampoline_t *) p; njs_printf("%5uD | %05uz TRY BREAK %04Xz %z\n", line, p - start, (size_t) try_tramp->exit_value, (size_t) try_tramp->offset); p += sizeof(njs_vmcode_try_trampoline_t); continue; } if (operation == NJS_VMCODE_TRY_CONTINUE) { try_tramp = (njs_vmcode_try_trampoline_t *) p; njs_printf("%5uD | %05uz TRY CONTINUE %04Xz %z\n", line, p - start, (size_t) try_tramp->exit_value, (size_t) try_tramp->offset); p += sizeof(njs_vmcode_try_trampoline_t); continue; } if (operation == NJS_VMCODE_TRY_RETURN) { try_return = (njs_vmcode_try_return_t *) p; njs_printf("%5uD | %05uz TRY RETURN %04Xz %04Xz %z\n", line, p - start, (size_t) try_return->save, (size_t) try_return->retval, (size_t) try_return->offset); p += sizeof(njs_vmcode_try_return_t); continue; } if (operation == NJS_VMCODE_CATCH) { catch = (njs_vmcode_catch_t *) p; njs_printf("%5uD | %05uz CATCH %04Xz %z\n", line, p - start, (size_t) catch->exception, (size_t) catch->offset); p += sizeof(njs_vmcode_catch_t); continue; } if (operation == NJS_VMCODE_TRY_END) { try_end = (njs_vmcode_try_end_t *) p; njs_printf("%5uD | %05uz TRY END %z\n", line, p - start, (size_t) try_end->offset); p += sizeof(njs_vmcode_try_end_t); continue; } if (operation == NJS_VMCODE_FINALLY) { finally = (njs_vmcode_finally_t *) p; njs_printf("%5uD | %05uz TRY FINALLY %04Xz %04Xz %z %z\n", line, p - start, (size_t) finally->retval, (size_t) finally->exit_value, (size_t) finally->continue_offset, (size_t) finally->break_offset); p += sizeof(njs_vmcode_finally_t); continue; } if (operation == NJS_VMCODE_ERROR) { error = (njs_vmcode_error_t *) p; switch (error->type) { case NJS_OBJ_TYPE_REF_ERROR: type = "REFERENCE"; break; case NJS_OBJ_TYPE_TYPE_ERROR: type = "TYPE"; break; case NJS_OBJ_TYPE_ERROR: default: type = ""; } njs_printf("%5uD | %05uz %s ERROR\n", line, p - start, type); p += sizeof(njs_vmcode_error_t); continue; } code_name = code_names; n = njs_nitems(code_names); do { if (operation == code_name->operation) { name = &code_name->name; if (code_name->size == sizeof(njs_vmcode_3addr_t)) { code3 = (njs_vmcode_3addr_t *) p; njs_printf("%5uD | %05uz %*s %04Xz %04Xz %04Xz\n", line, p - start, name->length, name->start, (size_t) code3->dst, (size_t) code3->src1, (size_t) code3->src2); } else if (code_name->size == sizeof(njs_vmcode_2addr_t)) { code2 = (njs_vmcode_2addr_t *) p; njs_printf("%5uD | %05uz %*s %04Xz %04Xz\n", line, p - start, name->length, name->start, (size_t) code2->dst, (size_t) code2->src); } else if (code_name->size == sizeof(njs_vmcode_1addr_t)) { code1 = (njs_vmcode_1addr_t *) p; njs_printf("%5uD | %05uz %*s %04Xz\n", line, p - start, name->length, name->start, (size_t) code1->index); } p += code_name->size; goto next; } code_name++; n--; } while (n != 0); njs_printf("%5uD | %05uz UNKNOWN %04Xz\n", line, p - start, (size_t) (uintptr_t) operation); p += sizeof(njs_vmcode_t); next: continue; } } njs-0.8.9/src/njs_diyfp.c000066400000000000000000000136601474132077100152560ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. * * An internal diy_fp implementation. * For details, see Loitsch, Florian. "Printing floating-point numbers quickly * and accurately with integers." ACM Sigplan Notices 45.6 (2010): 233-243. */ #include #include typedef struct njs_cpe_s { uint64_t significand; int16_t bin_exp; int16_t dec_exp; } njs_cpe_t; static const njs_cpe_t njs_cached_powers[] = { { njs_uint64(0xfa8fd5a0, 0x081c0288), -1220, -348 }, { njs_uint64(0xbaaee17f, 0xa23ebf76), -1193, -340 }, { njs_uint64(0x8b16fb20, 0x3055ac76), -1166, -332 }, { njs_uint64(0xcf42894a, 0x5dce35ea), -1140, -324 }, { njs_uint64(0x9a6bb0aa, 0x55653b2d), -1113, -316 }, { njs_uint64(0xe61acf03, 0x3d1a45df), -1087, -308 }, { njs_uint64(0xab70fe17, 0xc79ac6ca), -1060, -300 }, { njs_uint64(0xff77b1fc, 0xbebcdc4f), -1034, -292 }, { njs_uint64(0xbe5691ef, 0x416bd60c), -1007, -284 }, { njs_uint64(0x8dd01fad, 0x907ffc3c), -980, -276 }, { njs_uint64(0xd3515c28, 0x31559a83), -954, -268 }, { njs_uint64(0x9d71ac8f, 0xada6c9b5), -927, -260 }, { njs_uint64(0xea9c2277, 0x23ee8bcb), -901, -252 }, { njs_uint64(0xaecc4991, 0x4078536d), -874, -244 }, { njs_uint64(0x823c1279, 0x5db6ce57), -847, -236 }, { njs_uint64(0xc2109436, 0x4dfb5637), -821, -228 }, { njs_uint64(0x9096ea6f, 0x3848984f), -794, -220 }, { njs_uint64(0xd77485cb, 0x25823ac7), -768, -212 }, { njs_uint64(0xa086cfcd, 0x97bf97f4), -741, -204 }, { njs_uint64(0xef340a98, 0x172aace5), -715, -196 }, { njs_uint64(0xb23867fb, 0x2a35b28e), -688, -188 }, { njs_uint64(0x84c8d4df, 0xd2c63f3b), -661, -180 }, { njs_uint64(0xc5dd4427, 0x1ad3cdba), -635, -172 }, { njs_uint64(0x936b9fce, 0xbb25c996), -608, -164 }, { njs_uint64(0xdbac6c24, 0x7d62a584), -582, -156 }, { njs_uint64(0xa3ab6658, 0x0d5fdaf6), -555, -148 }, { njs_uint64(0xf3e2f893, 0xdec3f126), -529, -140 }, { njs_uint64(0xb5b5ada8, 0xaaff80b8), -502, -132 }, { njs_uint64(0x87625f05, 0x6c7c4a8b), -475, -124 }, { njs_uint64(0xc9bcff60, 0x34c13053), -449, -116 }, { njs_uint64(0x964e858c, 0x91ba2655), -422, -108 }, { njs_uint64(0xdff97724, 0x70297ebd), -396, -100 }, { njs_uint64(0xa6dfbd9f, 0xb8e5b88f), -369, -92 }, { njs_uint64(0xf8a95fcf, 0x88747d94), -343, -84 }, { njs_uint64(0xb9447093, 0x8fa89bcf), -316, -76 }, { njs_uint64(0x8a08f0f8, 0xbf0f156b), -289, -68 }, { njs_uint64(0xcdb02555, 0x653131b6), -263, -60 }, { njs_uint64(0x993fe2c6, 0xd07b7fac), -236, -52 }, { njs_uint64(0xe45c10c4, 0x2a2b3b06), -210, -44 }, { njs_uint64(0xaa242499, 0x697392d3), -183, -36 }, { njs_uint64(0xfd87b5f2, 0x8300ca0e), -157, -28 }, { njs_uint64(0xbce50864, 0x92111aeb), -130, -20 }, { njs_uint64(0x8cbccc09, 0x6f5088cc), -103, -12 }, { njs_uint64(0xd1b71758, 0xe219652c), -77, -4 }, { njs_uint64(0x9c400000, 0x00000000), -50, 4 }, { njs_uint64(0xe8d4a510, 0x00000000), -24, 12 }, { njs_uint64(0xad78ebc5, 0xac620000), 3, 20 }, { njs_uint64(0x813f3978, 0xf8940984), 30, 28 }, { njs_uint64(0xc097ce7b, 0xc90715b3), 56, 36 }, { njs_uint64(0x8f7e32ce, 0x7bea5c70), 83, 44 }, { njs_uint64(0xd5d238a4, 0xabe98068), 109, 52 }, { njs_uint64(0x9f4f2726, 0x179a2245), 136, 60 }, { njs_uint64(0xed63a231, 0xd4c4fb27), 162, 68 }, { njs_uint64(0xb0de6538, 0x8cc8ada8), 189, 76 }, { njs_uint64(0x83c7088e, 0x1aab65db), 216, 84 }, { njs_uint64(0xc45d1df9, 0x42711d9a), 242, 92 }, { njs_uint64(0x924d692c, 0xa61be758), 269, 100 }, { njs_uint64(0xda01ee64, 0x1a708dea), 295, 108 }, { njs_uint64(0xa26da399, 0x9aef774a), 322, 116 }, { njs_uint64(0xf209787b, 0xb47d6b85), 348, 124 }, { njs_uint64(0xb454e4a1, 0x79dd1877), 375, 132 }, { njs_uint64(0x865b8692, 0x5b9bc5c2), 402, 140 }, { njs_uint64(0xc83553c5, 0xc8965d3d), 428, 148 }, { njs_uint64(0x952ab45c, 0xfa97a0b3), 455, 156 }, { njs_uint64(0xde469fbd, 0x99a05fe3), 481, 164 }, { njs_uint64(0xa59bc234, 0xdb398c25), 508, 172 }, { njs_uint64(0xf6c69a72, 0xa3989f5c), 534, 180 }, { njs_uint64(0xb7dcbf53, 0x54e9bece), 561, 188 }, { njs_uint64(0x88fcf317, 0xf22241e2), 588, 196 }, { njs_uint64(0xcc20ce9b, 0xd35c78a5), 614, 204 }, { njs_uint64(0x98165af3, 0x7b2153df), 641, 212 }, { njs_uint64(0xe2a0b5dc, 0x971f303a), 667, 220 }, { njs_uint64(0xa8d9d153, 0x5ce3b396), 694, 228 }, { njs_uint64(0xfb9b7cd9, 0xa4a7443c), 720, 236 }, { njs_uint64(0xbb764c4c, 0xa7a44410), 747, 244 }, { njs_uint64(0x8bab8eef, 0xb6409c1a), 774, 252 }, { njs_uint64(0xd01fef10, 0xa657842c), 800, 260 }, { njs_uint64(0x9b10a4e5, 0xe9913129), 827, 268 }, { njs_uint64(0xe7109bfb, 0xa19c0c9d), 853, 276 }, { njs_uint64(0xac2820d9, 0x623bf429), 880, 284 }, { njs_uint64(0x80444b5e, 0x7aa7cf85), 907, 292 }, { njs_uint64(0xbf21e440, 0x03acdd2d), 933, 300 }, { njs_uint64(0x8e679c2f, 0x5e44ff8f), 960, 308 }, { njs_uint64(0xd433179d, 0x9c8cb841), 986, 316 }, { njs_uint64(0x9e19db92, 0xb4e31ba9), 1013, 324 }, { njs_uint64(0xeb96bf6e, 0xbadf77d9), 1039, 332 }, { njs_uint64(0xaf87023b, 0x9bf0ee6b), 1066, 340 }, }; #define NJS_D_1_LOG2_10 0.30102999566398114 /* 1 / log2(10). */ njs_diyfp_t njs_cached_power_dec(int exp, int *dec_exp) { u_int index; const njs_cpe_t *cp; index = (exp + NJS_DECIMAL_EXPONENT_OFF) / NJS_DECIMAL_EXPONENT_DIST; cp = &njs_cached_powers[index]; *dec_exp = cp->dec_exp; return njs_diyfp(cp->significand, cp->bin_exp); } njs_diyfp_t njs_cached_power_bin(int exp, int *dec_exp) { int k; u_int index; const njs_cpe_t *cp; k = (int) ceil((-61 - exp) * NJS_D_1_LOG2_10) + NJS_DECIMAL_EXPONENT_OFF - 1; index = (unsigned) (k >> 3) + 1; cp = &njs_cached_powers[index]; *dec_exp = -(NJS_DECIMAL_EXPONENT_MIN + (int) (index << 3)); return njs_diyfp(cp->significand, cp->bin_exp); } njs-0.8.9/src/njs_diyfp.h000066400000000000000000000115701474132077100152610ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. * * An internal diy_fp implementation. * For details, see Loitsch, Florian. "Printing floating-point numbers quickly * and accurately with integers." ACM Sigplan Notices 45.6 (2010): 233-243. */ #ifndef _NJS_DIYFP_H_INCLUDED_ #define _NJS_DIYFP_H_INCLUDED_ typedef struct { uint64_t significand; int exp; } njs_diyfp_t; typedef union { double d; uint64_t u64; } njs_diyfp_conv_t; #define njs_diyfp(_s, _e) (njs_diyfp_t) \ { .significand = (_s), .exp = (_e) } #define njs_uint64(h, l) (((uint64_t) (h) << 32) + (l)) #define NJS_DBL_SIGNIFICAND_SIZE 52 #define NJS_DBL_EXPONENT_OFFSET ((int64_t) 0x3ff) #define NJS_DBL_EXPONENT_BIAS (NJS_DBL_EXPONENT_OFFSET \ + NJS_DBL_SIGNIFICAND_SIZE) #define NJS_DBL_EXPONENT_MIN (-NJS_DBL_EXPONENT_BIAS) #define NJS_DBL_EXPONENT_MAX (0x7ff - NJS_DBL_EXPONENT_BIAS) #define NJS_DBL_EXPONENT_DENORMAL (-NJS_DBL_EXPONENT_BIAS + 1) #define NJS_DBL_SIGNIFICAND_MASK njs_uint64(0x000FFFFF, 0xFFFFFFFF) #define NJS_DBL_HIDDEN_BIT njs_uint64(0x00100000, 0x00000000) #define NJS_DBL_EXPONENT_MASK njs_uint64(0x7FF00000, 0x00000000) #define NJS_DBL_SIGN_MASK njs_uint64(0x80000000, 0x00000000) #define NJS_DIYFP_SIGNIFICAND_SIZE 64 #define NJS_SIGNIFICAND_SIZE 53 #define NJS_SIGNIFICAND_SHIFT (NJS_DIYFP_SIGNIFICAND_SIZE \ - NJS_DBL_SIGNIFICAND_SIZE) #define NJS_DECIMAL_EXPONENT_OFF 348 #define NJS_DECIMAL_EXPONENT_MIN (-348) #define NJS_DECIMAL_EXPONENT_MAX 340 #define NJS_DECIMAL_EXPONENT_DIST 8 njs_inline njs_diyfp_t njs_d2diyfp(double d) { int biased_exp; uint64_t significand; njs_diyfp_t r; union { double d; uint64_t u64; } u; u.d = d; biased_exp = (u.u64 & NJS_DBL_EXPONENT_MASK) >> NJS_DBL_SIGNIFICAND_SIZE; significand = u.u64 & NJS_DBL_SIGNIFICAND_MASK; if (biased_exp != 0) { r.significand = significand + NJS_DBL_HIDDEN_BIT; r.exp = biased_exp - NJS_DBL_EXPONENT_BIAS; } else { r.significand = significand; r.exp = NJS_DBL_EXPONENT_MIN + 1; } return r; } njs_inline double njs_diyfp2d(njs_diyfp_t v) { int exp; uint64_t significand, biased_exp; njs_diyfp_conv_t conv; exp = v.exp; significand = v.significand; while (significand > NJS_DBL_HIDDEN_BIT + NJS_DBL_SIGNIFICAND_MASK) { significand >>= 1; exp++; } if (exp >= NJS_DBL_EXPONENT_MAX) { return INFINITY; } if (exp < NJS_DBL_EXPONENT_DENORMAL) { return 0.0; } while (exp > NJS_DBL_EXPONENT_DENORMAL && (significand & NJS_DBL_HIDDEN_BIT) == 0) { significand <<= 1; exp--; } if (exp == NJS_DBL_EXPONENT_DENORMAL && (significand & NJS_DBL_HIDDEN_BIT) == 0) { biased_exp = 0; } else { biased_exp = (uint64_t) (exp + NJS_DBL_EXPONENT_BIAS); } conv.u64 = (significand & NJS_DBL_SIGNIFICAND_MASK) | (biased_exp << NJS_DBL_SIGNIFICAND_SIZE); return conv.d; } njs_inline njs_diyfp_t njs_diyfp_shift_left(njs_diyfp_t v, unsigned shift) { return njs_diyfp(v.significand << shift, v.exp - shift); } njs_inline njs_diyfp_t njs_diyfp_shift_right(njs_diyfp_t v, unsigned shift) { return njs_diyfp(v.significand >> shift, v.exp + shift); } njs_inline njs_diyfp_t njs_diyfp_sub(njs_diyfp_t lhs, njs_diyfp_t rhs) { return njs_diyfp(lhs.significand - rhs.significand, lhs.exp); } njs_inline njs_diyfp_t njs_diyfp_mul(njs_diyfp_t lhs, njs_diyfp_t rhs) { #if (NJS_HAVE_UNSIGNED_INT128) uint64_t l, h; njs_uint128_t u128; u128 = (njs_uint128_t) (lhs.significand) * (njs_uint128_t) (rhs.significand); h = u128 >> 64; l = (uint64_t) u128; /* rounding. */ if (l & ((uint64_t) 1 << 63)) { h++; } return njs_diyfp(h, lhs.exp + rhs.exp + 64); #else uint64_t a, b, c, d, ac, bc, ad, bd, tmp; a = lhs.significand >> 32; b = lhs.significand & 0xffffffff; c = rhs.significand >> 32; d = rhs.significand & 0xffffffff; ac = a * c; bc = b * c; ad = a * d; bd = b * d; tmp = (bd >> 32) + (ad & 0xffffffff) + (bc & 0xffffffff); /* mult_round. */ tmp += 1U << 31; return njs_diyfp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), lhs.exp + rhs.exp + 64); #endif } njs_inline njs_diyfp_t njs_diyfp_normalize(njs_diyfp_t v) { return njs_diyfp_shift_left(v, njs_leading_zeros64(v.significand)); } njs_diyfp_t njs_cached_power_dec(int exp, int *dec_exp); njs_diyfp_t njs_cached_power_bin(int exp, int *dec_exp); #endif /* _NJS_DIYFP_H_INCLUDED_ */ njs-0.8.9/src/njs_djb_hash.c000066400000000000000000000011641474132077100157010ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include uint32_t njs_djb_hash(const void *data, size_t len) { uint32_t hash; const u_char *p; p = data; hash = NJS_DJB_HASH_INIT; while (len != 0) { hash = njs_djb_hash_add(hash, *p++); len--; } return hash; } uint32_t njs_djb_hash_lowcase(const void *data, size_t len) { uint32_t hash; const u_char *p; p = data; hash = NJS_DJB_HASH_INIT; while (len != 0) { hash = njs_djb_hash_add(hash, njs_lower_case(*p++)); len--; } return hash; } njs-0.8.9/src/njs_djb_hash.h000066400000000000000000000010551474132077100157050ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_DJB_HASH_H_INCLUDED_ #define _NJS_DJB_HASH_H_INCLUDED_ /* A fast and simple hash function by Daniel J. Bernstein. */ NJS_EXPORT uint32_t njs_djb_hash(const void *data, size_t len); NJS_EXPORT uint32_t njs_djb_hash_lowcase(const void *data, size_t len); #define NJS_DJB_HASH_INIT 5381 #define njs_djb_hash_add(hash, val) \ ((uint32_t) ((((hash) << 5) + (hash)) ^ (uint32_t) (val))) #endif /* _NJS_DJB_HASH_H_INCLUDED_ */ njs-0.8.9/src/njs_dtoa.c000066400000000000000000000344021474132077100150670ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. * * Grisu2 algorithm implementation for printing floating-point numbers based * upon the work of Milo Yip and Doug Currie. * * For algorithm information, see Loitsch, Florian. "Printing * floating-point numbers quickly and accurately with integers." ACM Sigplan * Notices 45.6 (2010): 233-243. * * Copyright (C) 2015 Doug Currie * based on dtoa_milo.h * Copyright (C) 2014 Milo Yip * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include njs_inline void njs_round(char *start, size_t length, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t margin) { while (rest < margin && delta - rest >= ten_kappa && (rest + ten_kappa < margin || /* closer */ margin - rest > rest + ten_kappa - margin)) { start[length - 1]--; rest += ten_kappa; } } njs_inline void njs_round_prec(char *start, size_t length, uint64_t rest, uint64_t ten_kappa, uint64_t unit, int *kappa) { njs_int_t i; if (unit >= ten_kappa || ten_kappa - unit <= unit) { return; } if ((ten_kappa - rest > rest) && (ten_kappa - 2 * rest >= 2 * unit)) { return; } if ((rest > unit) && (ten_kappa - (rest - unit) <= (rest - unit))) { start[length - 1]++; for (i = length - 1; i > 0; --i) { if (start[i] != '0' + 10) { break; } start[i] = '0'; start[i - 1]++; } if (start[0] == '0' + 10) { start[0] = '1'; *kappa += 1; } } } njs_inline int njs_dec_count(uint32_t n) { if (n < 10000) { if (n < 100) { return (n < 10) ? 1 : 2; } else { return (n < 1000) ? 3 : 4; } } else { if (n < 1000000) { return (n < 100000) ? 5 : 6; } else { if (n < 100000000) { return (n < 10000000) ? 7 : 8; } else { return (n < 1000000000) ? 9 : 10; } } } } njs_inline size_t njs_digit_gen(njs_diyfp_t v, njs_diyfp_t high, uint64_t delta, char *start, int *dec_exp) { int kappa; char *p; uint32_t integer, d; uint64_t fraction, rest, margin; njs_diyfp_t one; static const uint64_t pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; one = njs_diyfp((uint64_t) 1 << -high.exp, high.exp); integer = (uint32_t) (high.significand >> -one.exp); fraction = high.significand & (one.significand - 1); margin = njs_diyfp_sub(high, v).significand; p = start; kappa = njs_dec_count(integer); while (kappa > 0) { switch (kappa) { case 10: d = integer / 1000000000; integer %= 1000000000; break; case 9: d = integer / 100000000; integer %= 100000000; break; case 8: d = integer / 10000000; integer %= 10000000; break; case 7: d = integer / 1000000; integer %= 1000000; break; case 6: d = integer / 100000; integer %= 100000; break; case 5: d = integer / 10000; integer %= 10000; break; case 4: d = integer / 1000; integer %= 1000; break; case 3: d = integer / 100; integer %= 100; break; case 2: d = integer / 10; integer %= 10; break; default: d = integer; integer = 0; break; } if (d != 0 || p != start) { *p++ = '0' + d; } kappa--; rest = ((uint64_t) integer << -one.exp) + fraction; if (rest < delta) { *dec_exp += kappa; njs_round(start, p - start, delta, rest, pow10[kappa] << -one.exp, margin); return p - start; } } /* kappa = 0. */ for ( ;; ) { fraction *= 10; delta *= 10; d = (uint32_t) (fraction >> -one.exp); if (d != 0 || p != start) { *p++ = '0' + d; } fraction &= one.significand - 1; kappa--; if (fraction < delta) { *dec_exp += kappa; margin *= (-kappa < 10) ? pow10[-kappa] : 0; njs_round(start, p - start, delta, fraction, one.significand, margin); return p - start; } } } njs_inline size_t njs_digit_gen_prec(njs_diyfp_t v, size_t prec, char *start, int *dec_exp) { int kappa; char *p; uint32_t integer, divisor; uint64_t fraction, rest, error; njs_diyfp_t one; static const uint64_t pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; one = njs_diyfp((uint64_t) 1 << -v.exp, v.exp); integer = (uint32_t) (v.significand >> -one.exp); fraction = v.significand & (one.significand - 1); error = 1; p = start; kappa = njs_dec_count(integer); while (kappa > 0) { divisor = pow10[kappa - 1]; *p++ = '0' + integer / divisor; integer %= divisor; kappa--; prec--; if (prec == 0) { rest = ((uint64_t) integer << -one.exp) + fraction; njs_round_prec(start, p - start, rest, pow10[kappa] << -one.exp, error, &kappa); *dec_exp += kappa; return p - start; } } /* kappa = 0. */ while (prec > 0 && fraction > error) { fraction *= 10; error *= 10; *p++ = '0' + (fraction >> -one.exp); fraction &= one.significand - 1; kappa--; prec--; } njs_round_prec(start, p - start, fraction, one.significand, error, &kappa); *dec_exp += kappa; return p - start; } njs_inline njs_diyfp_t njs_diyfp_normalize_boundary(njs_diyfp_t v) { while ((v.significand & (NJS_DBL_HIDDEN_BIT << 1)) == 0) { v.significand <<= 1; v.exp--; } return njs_diyfp_shift_left(v, NJS_SIGNIFICAND_SHIFT - 2); } njs_inline void njs_diyfp_normalize_boundaries(njs_diyfp_t v, njs_diyfp_t* minus, njs_diyfp_t* plus) { njs_diyfp_t pl, mi; pl = njs_diyfp_normalize_boundary(njs_diyfp((v.significand << 1) + 1, v.exp - 1)); if (v.significand == NJS_DBL_HIDDEN_BIT) { mi = njs_diyfp((v.significand << 2) - 1, v.exp - 2); } else { mi = njs_diyfp((v.significand << 1) - 1, v.exp - 1); } mi.significand <<= mi.exp - pl.exp; mi.exp = pl.exp; *plus = pl; *minus = mi; } /* * Grisu2 produces optimal (shortest) decimal representation for 99.8% * of IEEE doubles. For remaining 0.2% bignum algorithm like Dragon4 is requred. */ njs_inline size_t njs_grisu2(double value, char *start, int *point) { int dec_exp; size_t length; njs_diyfp_t v, low, high, ten_mk, scaled_v, scaled_low, scaled_high; v = njs_d2diyfp(value); njs_diyfp_normalize_boundaries(v, &low, &high); ten_mk = njs_cached_power_bin(high.exp, &dec_exp); scaled_v = njs_diyfp_mul(njs_diyfp_normalize(v), ten_mk); scaled_low = njs_diyfp_mul(low, ten_mk); scaled_high = njs_diyfp_mul(high, ten_mk); scaled_low.significand++; scaled_high.significand--; length = njs_digit_gen(scaled_v, scaled_high, scaled_high.significand - scaled_low.significand, start, &dec_exp); *point = length + dec_exp; return length; } njs_inline size_t njs_grisu2_prec(double value, char *start, size_t prec, int *point) { int dec_exp; size_t length; njs_diyfp_t v, ten_mk, scaled_v; v = njs_diyfp_normalize(njs_d2diyfp(value)); ten_mk = njs_cached_power_bin(v.exp, &dec_exp); scaled_v = njs_diyfp_mul(v, ten_mk); length = njs_digit_gen_prec(scaled_v, prec, start, &dec_exp); *point = length + dec_exp; return length; } njs_inline size_t njs_write_exponent(int exp, char *start) { char *p; size_t length; uint32_t u32; char buf[4]; /* -324 <= exp <= 308. */ if (exp < 0) { *start++ = '-'; exp = -exp; } else { *start++ = '+'; } u32 = exp; p = buf + njs_length(buf); do { *--p = u32 % 10 + '0'; u32 /= 10; } while (u32 != 0); length = buf + njs_length(buf) - p; memcpy(start, p, length); return length + 1; } njs_inline size_t njs_dtoa_format(char *start, size_t len, int point) { int offset, length; size_t size; length = (int) len; if (length <= point && point <= 21) { /* 1234e7 -> 12340000000 */ if (point - length > 0) { njs_memset(&start[length], '0', point - length); } return point; } if (0 < point && point <= 21) { /* 1234e-2 -> 12.34 */ memmove(&start[point + 1], &start[point], length - point); start[point] = '.'; return length + 1; } if (-6 < point && point <= 0) { /* 1234e-6 -> 0.001234 */ offset = 2 - point; memmove(&start[offset], start, length); start[0] = '0'; start[1] = '.'; if (offset - 2 > 0) { njs_memset(&start[2], '0', offset - 2); } return length + offset; } /* 1234e30 -> 1.234e33 */ if (length == 1) { /* 1e30 */ start[1] = 'e'; size = njs_write_exponent(point - 1, &start[2]); return size + 2; } memmove(&start[2], &start[1], length - 1); start[1] = '.'; start[length + 1] = 'e'; size = njs_write_exponent(point - 1, &start[length + 2]); return size + length + 2; } njs_inline size_t njs_dtoa_exp_format(char *start, int exponent, size_t prec, size_t len) { char *p; p = &start[len]; if (prec != 1) { memmove(&start[2], &start[1], len - 1); start[1] = '.'; p++; } njs_memset(p, '0', prec - len); p += prec - len; *p++ = 'e'; return prec + 1 + (prec != 1) + njs_write_exponent(exponent, p); } njs_inline size_t njs_dtoa_prec_format(char *start, size_t prec, size_t len, int point) { int exponent; size_t m, rest; exponent = point - 1; if (exponent < -6 || exponent >= (int) prec) { return njs_dtoa_exp_format(start, exponent, prec, len); } /* Fixed notation. */ if (point <= 0) { /* 1234e-2 => 0.001234000 */ memmove(&start[2 + (-point)], start, len); start[0] = '0'; start[1] = '.'; njs_memset(&start[2], '0', -point); if (prec > len) { njs_memset(&start[2 + (-point) + len], '0', prec - len); } return prec + 2 + (-point); } if (point >= (int) len) { /* TODO: (2**96).toPrecision(45) not enough precision, BigInt needed. */ njs_memset(&start[len], '0', point - len); if (point < (int) prec) { start[point] = '.'; njs_memset(&start[point + 1], '0', prec - point); } } else if (point < (int) prec) { /* 123456 -> 123.45600 */ m = njs_min((int) len, point); rest = njs_min(len, prec) - m; memmove(&start[m + 1], &start[m], rest); start[m] = '.'; njs_memset(&start[m + rest + 1], '0', prec - m - rest); } return prec + (point < (int) prec); } size_t njs_dtoa(double value, char *start) { int point, minus; char *p; size_t length; /* Not handling NaN and inf. */ minus = 0; p = start; if (value == 0) { *p++ = '0'; return p - start; } if (signbit(value)) { *p++ = '-'; value = -value; minus = 1; } length = njs_grisu2(value, p, &point); return njs_dtoa_format(p, length, point) + minus; } /* * TODO: For prec > 16 result maybe rounded. To support prec > 16 Bignum * support is requred. */ size_t njs_dtoa_precision(double value, char *start, size_t prec) { int point, minus; char *p; size_t length; /* Not handling NaN and inf. */ p = start; minus = 0; if (value != 0) { if (value < 0) { *p++ = '-'; value = -value; minus = 1; } length = njs_grisu2_prec(value, p, prec, &point); } else { start[0] = '0'; length = 1; point = 1; } return njs_dtoa_prec_format(p, prec, length, point) + minus; } size_t njs_dtoa_exponential(double value, char *start, njs_int_t frac) { int point, minus; char *p; size_t length; /* Not handling NaN and inf. */ p = start; minus = 0; if (value != 0) { if (value < 0) { *p++ = '-'; value = -value; minus = 1; } if (frac == -1) { length = njs_grisu2(value, p, &point); } else { length = njs_grisu2_prec(value, p, frac + 1, &point); } } else { start[0] = '0'; length = 1; point = 1; } if (frac == -1) { frac = length - 1; } return njs_dtoa_exp_format(p, point - 1, frac + 1, length) + minus; } njs-0.8.9/src/njs_dtoa.h000066400000000000000000000006051474132077100150720ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) Nginx, Inc. */ #ifndef _NJS_DTOA_H_INCLUDED_ #define _NJS_DTOA_H_INCLUDED_ NJS_EXPORT size_t njs_dtoa(double value, char *start); NJS_EXPORT size_t njs_dtoa_precision(double value, char *start, size_t prec); NJS_EXPORT size_t njs_dtoa_exponential(double value, char *start, njs_int_t frac); #endif /* _NJS_DTOA_H_INCLUDED_ */ njs-0.8.9/src/njs_dtoa_fixed.c000066400000000000000000000231201474132077100162410ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. * * An internal fixed_dtoa() implementation based upon V8 * src/numbers/fixed-dtoa.cc without bignum support. * * Copyright 2011 the V8 project authors. All rights reserved. * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file. */ #include #include typedef struct { uint64_t high; uint64_t low; } njs_diyu128_t; #define njs_diyu128(_h, _l) (njs_diyu128_t) { .high = (_h), .low = (_l) } njs_inline njs_diyu128_t njs_diyu128_mul(njs_diyu128_t v, uint32_t multiplicand) { uint32_t part; uint64_t accumulator; accumulator = (v.low & UINT32_MAX) * multiplicand; part = (uint32_t) (accumulator & UINT32_MAX); accumulator >>= 32; accumulator = accumulator + (v.low >> 32) * multiplicand; v.low = (accumulator << 32) + part; accumulator >>= 32; accumulator = accumulator + (v.high & UINT32_MAX) * multiplicand; part = (uint32_t) (accumulator & UINT32_MAX); accumulator >>= 32; accumulator = accumulator + (v.high >> 32) * multiplicand; v.high = (accumulator << 32) + part; return v; } njs_inline njs_diyu128_t njs_diyu128_shift(njs_diyu128_t v, njs_int_t shift) { /* -64 <= shift <= 64.*/ if (shift < 0) { if (shift == -64) { v.high = v.low; v.low = 0; } else { v.high <<= -shift; v.high += v.low >> (64 + shift); v.low <<= -shift; } return v; } if (shift > 0) { if (shift == 64) { v.low = v.high; v.high = 0; } else { v.low >>= shift; v.low += v.high << (64 - shift); v.high >>= shift; } } return v; } njs_inline njs_int_t njs_diyu128_div_mod_pow2(njs_diyu128_t *v, njs_int_t power) { uint64_t part_low, part_high; njs_int_t result; if (power >= 64) { result = (int) (v->high >> (power - 64)); v->high -= (uint64_t) result << (power - 64); return result; } part_low = v->low >> power; part_high = v->high << (64 - power); result = (int) (part_low + part_high); v->low -= part_low << power; v->high = 0; return result; } njs_inline njs_bool_t njs_diyu128_is_zero(njs_diyu128_t v) { if (v.low == 0 && v.high == 0) { return 1; } return 0; } njs_inline njs_uint_t njs_diyu128_bit_at(njs_diyu128_t v, njs_uint_t pos) { if (pos >= 64) { return (njs_uint_t) (v.high >> (pos - 64)) & 1; } return (njs_uint_t) (v.low >> pos) & 1; } static size_t njs_fill_digits32(uint32_t number, char *start, size_t length) { char c; size_t i, j, n; njs_int_t digit; n = 0; while (number != 0) { digit = number % 10; number /= 10; start[length + n] = '0' + digit; n++; } i = length; j = length + n - 1; while (i < j) { c = start[i]; start[i] = start[j]; start[j] = c; i++; j--; } return length + n; } njs_inline size_t njs_fill_digits32_fixed_length(uint32_t number, size_t requested_length, char *start, size_t length) { size_t i; i = requested_length; while (i-- > 0) { start[length + i] = '0' + number % 10; number /= 10; } return length + requested_length; } njs_inline size_t njs_fill_digits64(uint64_t number, char *start, size_t length) { uint32_t part0, part1, part2, ten7; ten7 = 10000000; part2 = (uint32_t) (number % ten7); number /= ten7; part1 = (uint32_t) (number % ten7); part0 = (uint32_t) (number / ten7); if (part0 != 0) { length = njs_fill_digits32(part0, start, length); length = njs_fill_digits32_fixed_length(part1, 7, start, length); return njs_fill_digits32_fixed_length(part2, 7, start, length); } if (part1 != 0) { length = njs_fill_digits32(part1, start, length); return njs_fill_digits32_fixed_length(part2, 7, start, length); } return njs_fill_digits32(part2, start, length); } njs_inline size_t njs_fill_digits64_fixed_length(uint64_t number, char *start, size_t length) { uint32_t part0, part1, part2, ten7; ten7 = 10000000; part2 = (uint32_t) (number % ten7); number /= ten7; part1 = (uint32_t) (number % ten7); part0 = (uint32_t) (number / ten7); length = njs_fill_digits32_fixed_length(part0, 3, start, length); length = njs_fill_digits32_fixed_length(part1, 7, start, length); return njs_fill_digits32_fixed_length(part2, 7, start, length); } njs_inline size_t njs_dtoa_round_up(char *start, size_t length, njs_int_t *point) { size_t i; if (length == 0) { start[0] = '1'; *point = 1; return 1; } start[length - 1]++; for (i = length - 1; i > 0; --i) { if (start[i] != '0' + 10) { return length; } start[i] = '0'; start[i - 1]++; } if (start[0] == '0' + 10) { start[0] = '1'; (*point)++; } return length; } static size_t njs_fill_fractionals(uint64_t fractionals, int exponent, njs_uint_t frac, char *start, size_t length, njs_int_t *point) { njs_int_t n, digit; njs_uint_t i; njs_diyu128_t fractionals128; /* * -128 <= exponent <= 0. * 0 <= fractionals * 2^exponent < 1. */ if (-exponent <= 64) { /* fractionals <= 2^56. */ n = -exponent; for (i = 0; i < frac && fractionals != 0; ++i) { /* * Multiplication by 10 is replaced with multiplication by 5 and * point location adjustment. To avoid integer-overflow. */ fractionals *= 5; n--; digit = (njs_int_t) (fractionals >> n); fractionals -= (uint64_t) digit << n; start[length++] = '0' + digit; } if (n > 0 && ((fractionals >> (n - 1)) & 1)) { length = njs_dtoa_round_up(start, length, point); } } else { fractionals128 = njs_diyu128(fractionals, 0); fractionals128 = njs_diyu128_shift(fractionals128, -exponent - 64); n = 128; for (i = 0; i < frac && !njs_diyu128_is_zero(fractionals128); ++i) { /* * Multiplication by 10 is replaced with multiplication by 5 and * point location adjustment. To avoid integer-overflow. */ fractionals128 = njs_diyu128_mul(fractionals128, 5); n--; digit = njs_diyu128_div_mod_pow2(&fractionals128, n); start[length++] = '0' + digit; } if (njs_diyu128_bit_at(fractionals128, n - 1)) { length = njs_dtoa_round_up(start, length, point); } } return length; } njs_inline size_t njs_trim_zeroes(char *start, size_t length, njs_int_t *point) { size_t i, n; while (length > 0 && start[length - 1] == '0') { length--; } n = 0; while (n < length && start[n] == '0') { n++; } if (n != 0) { for (i = n; i < length; ++i) { start[i - n] = start[i]; } length -= n; *point -= n; } return length; } size_t njs_fixed_dtoa(double value, njs_uint_t frac, char *start, njs_int_t *point) { size_t length; uint32_t quotient; uint64_t significand, divisor, dividend, remainder, integral, fract; njs_int_t exponent; njs_diyfp_t v; length = 0; v = njs_d2diyfp(value); significand = v.significand; exponent = v.exp; /* exponent <= 19. */ if (exponent + NJS_SIGNIFICAND_SIZE > 64) { /* exponent > 11. */ divisor = njs_uint64(0xB1, 0xA2BC2EC5); /* 5 ^ 17 */ dividend = significand; if (exponent > 17) { /* (e - 17) <= 3. */ dividend <<= exponent - 17; quotient = (uint32_t) (dividend / divisor); remainder = (dividend % divisor) << 17; } else { divisor <<= 17 - exponent; quotient = (uint32_t) (dividend / divisor); remainder = (dividend % divisor) << exponent; } length = njs_fill_digits32(quotient, start, length); length = njs_fill_digits64_fixed_length(remainder, start, length); *point = (njs_int_t) length; } else if (exponent >= 0) { /* 0 <= exponent <= 11. */ significand <<= exponent; length = njs_fill_digits64(significand, start, length); *point = (njs_int_t) length; } else if (exponent > -NJS_SIGNIFICAND_SIZE) { /* -53 < exponent < 0. */ integral = significand >> -exponent; fract = significand - (integral << -exponent); if (integral > UINT32_MAX) { length = njs_fill_digits64(integral, start, length); } else { length = njs_fill_digits32((uint32_t) integral, start, length); } *point = (njs_int_t) length; length = njs_fill_fractionals(fract, exponent, frac, start, length, point); } else if (exponent < -128) { /* Valid for frac =< 20 only. TODO: bignum support. */ start[0] = '\0'; *point = -frac; } else { /* -128 <= exponent <= -53. */ *point = 0; length = njs_fill_fractionals(significand, exponent, frac, start, length, point); } length = njs_trim_zeroes(start, length, point); start[length] = '\0'; if (length == 0) { *point = -frac; } return length; } njs-0.8.9/src/njs_dtoa_fixed.h000066400000000000000000000004371474132077100162540ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) Nginx, Inc. */ #ifndef _NJS_DTOA_FIXED_H_INCLUDED_ #define _NJS_DTOA_FIXED_H_INCLUDED_ NJS_EXPORT size_t njs_fixed_dtoa(double value, njs_uint_t frac, char *start, njs_int_t *point); #endif /* _NJS_DTOA_FIXED_H_INCLUDED_ */ njs-0.8.9/src/njs_encoding.c000066400000000000000000000373051474132077100157330ustar00rootroot00000000000000 /* * Copyright (C) Alexander Borisov * Copyright (C) NGINX, Inc. */ #include typedef enum { NJS_ENCODING_UTF8, } njs_encoding_t; typedef struct { njs_encoding_t encoding; njs_bool_t fatal; njs_bool_t ignore_bom; njs_unicode_decode_t ctx; } njs_encoding_decode_t; typedef struct { njs_str_t name; njs_encoding_t encoding; } njs_encoding_label_t; static njs_encoding_label_t njs_encoding_labels[] = { { njs_str("utf-8"), NJS_ENCODING_UTF8 }, { njs_str("utf8") , NJS_ENCODING_UTF8 }, { njs_null_str, 0 } }; static njs_int_t njs_text_encoder_encode_utf8(njs_vm_t *vm, njs_string_prop_t *prop, njs_value_t *retval); static njs_int_t njs_text_decoder_arg_encoding(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_encoding_decode_t *data); static njs_int_t njs_text_decoder_arg_options(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_encoding_decode_t *data); static njs_int_t njs_text_encoder_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_object_value_t *encoder; if (!vm->top_frame->ctor) { njs_type_error(vm, "Constructor of TextEncoder requires 'new'"); return NJS_ERROR; } encoder = njs_object_value_alloc(vm, NJS_OBJ_TYPE_TEXT_ENCODER, 0, NULL); if (njs_slow_path(encoder == NULL)) { return NJS_ERROR; } njs_set_data(&encoder->value, NULL, NJS_DATA_TAG_TEXT_ENCODER); njs_set_object_value(retval, encoder); return NJS_OK; } static njs_int_t njs_text_encoder_encode(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { u_char *dst; size_t size; njs_int_t ret; njs_value_t *this, *input, value; const u_char *start, *end; njs_string_prop_t prop; njs_typed_array_t *array; njs_unicode_decode_t ctx; this = njs_argument(args, 0); if (njs_slow_path(!njs_is_object_data(this, NJS_DATA_TAG_TEXT_ENCODER))) { njs_type_error(vm, "\"this\" is not a TextEncoder"); return NJS_ERROR; } start = NULL; end = NULL; if (nargs > 1) { input = njs_argument(args, 1); if (njs_slow_path(!njs_is_string(input))) { ret = njs_value_to_string(vm, input, input); if (njs_slow_path(ret != NJS_OK)) { return ret; } } (void) njs_string_prop(&prop, input); if (prop.length != 0) { return njs_text_encoder_encode_utf8(vm, &prop, retval); } start = prop.start; end = start + prop.size; } njs_utf8_decode_init(&ctx); (void) njs_utf8_stream_length(&ctx, start, end - start, 1, 0, &size); njs_set_number(&value, size); array = njs_typed_array_alloc(vm, &value, 1, 0, NJS_OBJ_TYPE_UINT8_ARRAY); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } dst = njs_typed_array_buffer(array)->u.u8; njs_utf8_decode_init(&ctx); (void) njs_utf8_stream_encode(&ctx, start, end, dst, 1, 0); njs_set_typed_array(retval, array); return NJS_OK; } static njs_int_t njs_text_encoder_encode_utf8(njs_vm_t *vm, njs_string_prop_t *prop, njs_value_t *retval) { njs_value_t value; njs_typed_array_t *array; njs_set_number(&value, prop->size); array = njs_typed_array_alloc(vm, &value, 1, 0, NJS_OBJ_TYPE_UINT8_ARRAY); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } memcpy(njs_typed_array_buffer(array)->u.u8, prop->start, prop->size); njs_set_typed_array(retval, array); return NJS_OK; } static njs_int_t njs_text_encoder_encode_into(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { u_char *to, *to_end; size_t size; uint32_t cp; njs_int_t ret; njs_str_t str; njs_value_t *this, *input, *dest, value, read, written; const u_char *start, *end; njs_typed_array_t *array; njs_unicode_decode_t ctx; static const njs_value_t read_str = njs_string("read"); static const njs_value_t written_str = njs_string("written"); this = njs_argument(args, 0); input = njs_arg(args, nargs, 1); dest = njs_arg(args, nargs, 2); if (njs_slow_path(!njs_is_object_data(this, NJS_DATA_TAG_TEXT_ENCODER))) { njs_type_error(vm, "\"this\" is not a TextEncoder"); return NJS_ERROR; } if (njs_slow_path(!njs_is_string(input))) { ret = njs_value_to_string(vm, &value, input); if (njs_slow_path(ret != NJS_OK)) { return ret; } input = &value; } if (njs_slow_path(!njs_is_typed_array_uint8(dest))) { njs_type_error(vm, "The \"destination\" argument must be an instance " "of Uint8Array"); return NJS_ERROR; } njs_string_get(input, &str); start = str.start; end = start + str.length; array = njs_typed_array(dest); to = njs_typed_array_start(array); to_end = to + array->byte_length; njs_set_number(&read, 0); njs_set_number(&written, 0); njs_utf8_decode_init(&ctx); while (start < end) { cp = njs_utf8_decode(&ctx, &start, end); if (cp > NJS_UNICODE_MAX_CODEPOINT) { cp = NJS_UNICODE_REPLACEMENT; } size = njs_utf8_size(cp); if (to + size > to_end) { break; } njs_number(&read) += (cp > 0xFFFF) ? 2 : 1; njs_number(&written) += size; to = njs_utf8_encode(to, cp); } return njs_vm_object_alloc(vm, retval, &read_str, &read, &written_str, &written, NULL); } static const njs_object_prop_t njs_text_encoder_properties[] = { NJS_DECLARE_PROP_HANDLER("constructor", njs_object_prototype_create_constructor, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_VALUE("encoding", njs_string("utf-8"), 0), NJS_DECLARE_PROP_NATIVE("encode", njs_text_encoder_encode, 0, 0), NJS_DECLARE_PROP_NATIVE("encodeInto", njs_text_encoder_encode_into, 2, 0), }; const njs_object_init_t njs_text_encoder_init = { njs_text_encoder_properties, njs_nitems(njs_text_encoder_properties), }; static const njs_object_prop_t njs_text_encoder_constructor_properties[] = { NJS_DECLARE_PROP_LENGTH(0), NJS_DECLARE_PROP_NAME("TextEncoder"), NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), }; const njs_object_init_t njs_text_encoder_constructor_init = { njs_text_encoder_constructor_properties, njs_nitems(njs_text_encoder_constructor_properties), }; const njs_object_type_init_t njs_text_encoder_type_init = { .constructor = njs_native_ctor(njs_text_encoder_constructor, 0, 0), .prototype_props = &njs_text_encoder_init, .constructor_props = &njs_text_encoder_constructor_init, .prototype_value = { .object = { .type = NJS_OBJECT } }, }; static njs_int_t njs_text_decoder_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_object_value_t *decoder; njs_encoding_decode_t *data; if (!vm->top_frame->ctor) { njs_type_error(vm, "Constructor of TextDecoder requires 'new'"); return NJS_ERROR; } decoder = njs_object_value_alloc(vm, NJS_OBJ_TYPE_TEXT_DECODER, sizeof(njs_encoding_decode_t), NULL); if (njs_slow_path(decoder == NULL)) { return NJS_ERROR; } data = (njs_encoding_decode_t *) ((uint8_t *) decoder + sizeof(njs_object_value_t)); ret = njs_text_decoder_arg_encoding(vm, args, nargs, data); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_text_decoder_arg_options(vm, args, nargs, data); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_utf8_decode_init(&data->ctx); njs_set_data(&decoder->value, data, NJS_DATA_TAG_TEXT_DECODER); njs_set_object_value(retval, decoder); return NJS_OK; } static njs_int_t njs_text_decoder_arg_encoding(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_encoding_decode_t *data) { njs_str_t str; njs_int_t ret; njs_value_t *value; njs_encoding_label_t *label; if (nargs < 2) { data->encoding = NJS_ENCODING_UTF8; return NJS_OK; } value = njs_argument(args, 1); if (njs_slow_path(!njs_is_string(value))) { ret = njs_value_to_string(vm, value, value); if (njs_slow_path(ret != NJS_OK)) { return ret; } } njs_string_get(value, &str); for (label = &njs_encoding_labels[0]; label->name.length != 0; label++) { if (njs_strstr_eq(&str, &label->name)) { data->encoding = label->encoding; return NJS_OK; } } njs_range_error(vm, "The \"%V\" encoding is not supported", &str); return NJS_ERROR; } static njs_int_t njs_text_decoder_arg_options(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_encoding_decode_t *data) { njs_int_t ret; njs_value_t retval, *value; static const njs_value_t fatal_str = njs_string("fatal"); static const njs_value_t ignore_bom_str = njs_string("ignoreBOM"); if (nargs < 3) { data->fatal = 0; data->ignore_bom = 0; return NJS_OK; } value = njs_argument(args, 2); if (njs_slow_path(!njs_is_object(value))) { njs_type_error(vm, "The \"options\" argument must be of type object"); return NJS_ERROR; } ret = njs_value_property(vm, value, njs_value_arg(&fatal_str), &retval); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } data->fatal = njs_bool(&retval); ret = njs_value_property(vm, value, njs_value_arg(&ignore_bom_str), &retval); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } data->ignore_bom = njs_bool(&retval); return NJS_OK; } static njs_int_t njs_text_decoder_encoding(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { njs_encoding_decode_t *data; static const njs_value_t utf8_str = njs_string("utf-8"); if (njs_slow_path(!njs_is_object_data(value, NJS_DATA_TAG_TEXT_DECODER))) { njs_set_undefined(retval); return NJS_DECLINED; } data = njs_object_data(value); switch (data->encoding) { case NJS_ENCODING_UTF8: *retval = utf8_str; break; default: njs_type_error(vm, "unknown encoding"); return NJS_ERROR; } return NJS_OK; } static njs_int_t njs_text_decoder_fatal(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { njs_encoding_decode_t *data; if (njs_slow_path(!njs_is_object_data(value, NJS_DATA_TAG_TEXT_DECODER))) { njs_set_undefined(retval); return NJS_DECLINED; } data = njs_object_data(value); njs_set_boolean(retval, data->fatal); return NJS_OK; } static njs_int_t njs_text_decoder_ignore_bom(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { njs_encoding_decode_t *data; if (njs_slow_path(!njs_is_object_data(value, NJS_DATA_TAG_TEXT_DECODER))) { njs_set_undefined(retval); return NJS_DECLINED; } data = njs_object_data(value); njs_set_boolean(retval, data->ignore_bom); return NJS_OK; } static njs_int_t njs_text_decoder_decode(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { u_char *dst; size_t size; ssize_t length; njs_int_t ret; njs_bool_t stream; njs_value_t *this, *value, *options; const u_char *start, *end; njs_unicode_decode_t ctx; njs_encoding_decode_t *data; const njs_typed_array_t *array; const njs_array_buffer_t *buffer; static const njs_value_t stream_str = njs_string("stream"); start = NULL; end = NULL; stream = 0; this = njs_argument(args, 0); if (njs_slow_path(!njs_is_object_data(this, NJS_DATA_TAG_TEXT_DECODER))) { njs_type_error(vm, "\"this\" is not a TextDecoder"); return NJS_ERROR; } if (njs_fast_path(nargs > 1)) { value = njs_argument(args, 1); if (njs_is_typed_array(value)) { array = njs_typed_array(value); start = njs_typed_array_start(array); end = start + array->byte_length; } else if (njs_is_array_buffer(value)) { buffer = njs_array_buffer(value); start = buffer->u.u8; end = start + buffer->size; } else { njs_type_error(vm, "The \"input\" argument must be an instance " "of TypedArray"); return NJS_ERROR; } } if (nargs > 2) { options = njs_argument(args, 2); if (njs_slow_path(!njs_is_object(options))) { njs_type_error(vm, "The \"options\" argument must be " "of type object"); return NJS_ERROR; } ret = njs_value_property(vm, options, njs_value_arg(&stream_str), retval); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } stream = njs_bool(retval); } data = njs_object_data(this); ctx = data->ctx; /* Looking for BOM. */ if (start != NULL && !data->ignore_bom) { start += njs_utf8_bom(start, end); } length = njs_utf8_stream_length(&ctx, start, end - start, !stream, data->fatal, &size); if (length == -1) { njs_type_error(vm, "The encoded data was not valid"); return NJS_ERROR; } dst = njs_string_alloc(vm, retval, size, length); if (njs_slow_path(dst == NULL)) { return NJS_ERROR; } (void) njs_utf8_stream_encode(&data->ctx, start, end, dst, !stream, 0); if (!stream) { njs_utf8_decode_init(&data->ctx); } return NJS_OK; } static const njs_object_prop_t njs_text_decoder_properties[] = { NJS_DECLARE_PROP_HANDLER("constructor", njs_object_prototype_create_constructor, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("encoding", njs_text_decoder_encoding, 0, 0, 0), NJS_DECLARE_PROP_HANDLER("fatal", njs_text_decoder_fatal, 0, 0, 0), NJS_DECLARE_PROP_HANDLER("ignoreBOM", njs_text_decoder_ignore_bom, 0, 0, 0), NJS_DECLARE_PROP_NATIVE("decode", njs_text_decoder_decode, 0, 0), }; const njs_object_init_t njs_text_decoder_init = { njs_text_decoder_properties, njs_nitems(njs_text_decoder_properties), }; static const njs_object_prop_t njs_text_decoder_constructor_properties[] = { NJS_DECLARE_PROP_LENGTH(0), NJS_DECLARE_PROP_NAME("TextDecoder"), NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), }; const njs_object_init_t njs_text_decoder_constructor_init = { njs_text_decoder_constructor_properties, njs_nitems(njs_text_decoder_constructor_properties), }; const njs_object_type_init_t njs_text_decoder_type_init = { .constructor = njs_native_ctor(njs_text_decoder_constructor, 0, 0), .prototype_props = &njs_text_decoder_init, .constructor_props = &njs_text_decoder_constructor_init, .prototype_value = { .object = { .type = NJS_OBJECT } }, }; njs-0.8.9/src/njs_encoding.h000066400000000000000000000004711474132077100157320ustar00rootroot00000000000000 /* * Copyright (C) Alexander Borisov * Copyright (C) NGINX, Inc. */ #ifndef _NJS_ENCODING_H_INCLUDED_ #define _NJS_ENCODING_H_INCLUDED_ extern const njs_object_type_init_t njs_text_encoder_type_init; extern const njs_object_type_init_t njs_text_decoder_type_init; #endif /* _NJS_ENCODING_H_INCLUDED_ */ njs-0.8.9/src/njs_error.c000066400000000000000000000763451474132077100153050ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #include typedef struct { union { njs_function_t *function; u_char *pc; } u; uint8_t native; } njs_stack_entry_t; typedef struct { njs_str_t name; njs_str_t file; uint32_t line; } njs_backtrace_entry_t; static njs_int_t njs_add_backtrace_entry(njs_vm_t *vm, njs_arr_t *stack, njs_stack_entry_t *se); static njs_int_t njs_backtrace_to_string(njs_vm_t *vm, njs_arr_t *backtrace, njs_str_t *dst); static const njs_value_t njs_error_message_string = njs_string("message"); static const njs_value_t njs_error_name_string = njs_string("name"); static const njs_value_t njs_error_stack_string = njs_string("stack"); static const njs_value_t njs_error_errors_string = njs_string("errors"); void njs_error_new(njs_vm_t *vm, njs_value_t *dst, njs_object_t *proto, u_char *start, size_t size) { njs_int_t ret; njs_value_t string; njs_object_t *error; ret = njs_string_create(vm, &string, start, size); if (njs_slow_path(ret != NJS_OK)) { return; } error = njs_error_alloc(vm, proto, NULL, &string, NULL); if (njs_slow_path(error == NULL)) { return; } njs_set_object(dst, error); } void njs_throw_error_va(njs_vm_t *vm, njs_object_t *proto, const char *fmt, va_list args) { u_char buf[NJS_MAX_ERROR_STR], *p; p = njs_vsprintf(buf, buf + sizeof(buf), fmt, args); njs_error_new(vm, &vm->exception, proto, buf, p - buf); } void njs_throw_error(njs_vm_t *vm, njs_object_type_t type, const char *fmt, ...) { va_list args; va_start(args, fmt); njs_throw_error_va(vm, njs_vm_proto(vm, type), fmt, args); va_end(args); } void njs_error_fmt_new(njs_vm_t *vm, njs_value_t *dst, njs_object_type_t type, const char *fmt, ...) { va_list args; u_char buf[NJS_MAX_ERROR_STR], *p; va_start(args, fmt); p = njs_vsprintf(buf, buf + sizeof(buf), fmt, args); va_end(args); njs_error_new(vm, dst, njs_vm_proto(vm, type), buf, p - buf); } static njs_int_t njs_error_stack_new(njs_vm_t *vm, njs_object_value_t *error) { njs_arr_t *stack; njs_stack_entry_t *se; njs_native_frame_t *frame; stack = njs_arr_create(vm->mem_pool, 4, sizeof(njs_stack_entry_t)); if (njs_slow_path(stack == NULL)) { return NJS_ERROR; } frame = vm->top_frame; for ( ;; ) { if (frame->native || frame->pc != NULL) { se = njs_arr_add(stack); if (njs_slow_path(se == NULL)) { return NJS_ERROR; } se->native = frame->native; if (se->native) { se->u.function = frame->function; } else { se->u.pc = frame->pc; } } frame = frame->previous; if (frame == NULL) { break; } } njs_data(&error->value) = stack; return NJS_OK; } njs_int_t njs_error_stack_attach(njs_vm_t *vm, njs_value_t value) { njs_int_t ret; if (njs_slow_path(!njs_is_error(&value)) || njs_object(&value)->stack_attached) { return NJS_DECLINED; } if (njs_slow_path(!vm->options.backtrace || vm->start == NULL)) { return NJS_OK; } ret = njs_error_stack_new(vm, value.data.u.object_value); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "njs_error_stack_new() failed"); return NJS_ERROR; } njs_object(&value)->stack_attached = 1; return NJS_OK; } njs_int_t njs_error_stack(njs_vm_t *vm, njs_value_t *value, njs_value_t *stack) { njs_int_t ret; ret = njs_value_property(vm, value, njs_value_arg(&njs_error_stack_string), stack); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (!njs_is_string(stack)) { return NJS_DECLINED; } return NJS_OK; } njs_object_t * njs_error_alloc(njs_vm_t *vm, njs_object_t *proto, const njs_value_t *name, const njs_value_t *message, const njs_value_t *errors) { njs_int_t ret; njs_object_t *error; njs_object_prop_t *prop; njs_object_value_t *ov; njs_lvlhsh_query_t lhq; ov = njs_mp_alloc(vm->mem_pool, sizeof(njs_object_value_t)); if (njs_slow_path(ov == NULL)) { goto memory_error; } njs_set_data(&ov->value, NULL, NJS_DATA_TAG_ANY); error = &ov->object; njs_lvlhsh_init(&error->hash); njs_lvlhsh_init(&error->shared_hash); error->type = NJS_OBJECT_VALUE; error->shared = 0; error->extensible = 1; error->fast_array = 0; error->error_data = 1; error->stack_attached = 0; error->__proto__ = proto; error->slots = NULL; lhq.replace = 0; lhq.pool = vm->mem_pool; lhq.proto = &njs_object_hash_proto; if (name != NULL) { lhq.key = njs_str_value("name"); lhq.key_hash = NJS_NAME_HASH; prop = njs_object_prop_alloc(vm, &njs_error_name_string, name, 1); if (njs_slow_path(prop == NULL)) { goto memory_error; } lhq.value = prop; ret = njs_lvlhsh_insert(&error->hash, &lhq); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "lvlhsh insert failed"); return NULL; } } if (message!= NULL) { lhq.key = njs_str_value("message"); lhq.key_hash = NJS_MESSAGE_HASH; prop = njs_object_prop_alloc(vm, &njs_error_message_string, message, 1); if (njs_slow_path(prop == NULL)) { goto memory_error; } prop->enumerable = 0; lhq.value = prop; ret = njs_lvlhsh_insert(&error->hash, &lhq); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "lvlhsh insert failed"); return NULL; } } if (errors != NULL) { lhq.key = njs_str_value("errors"); lhq.key_hash = NJS_ERRORS_HASH; prop = njs_object_prop_alloc(vm, &njs_error_errors_string, errors, 1); if (njs_slow_path(prop == NULL)) { goto memory_error; } prop->enumerable = 0; lhq.value = prop; ret = njs_lvlhsh_insert(&error->hash, &lhq); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "lvlhsh insert failed"); return NULL; } } return error; memory_error: njs_memory_error(vm); return NULL; } njs_int_t njs_error_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t type, njs_value_t *retval) { njs_int_t ret; njs_value_t *iterator, *value, list; njs_array_t *array; njs_object_t *error; if (type != NJS_OBJ_TYPE_AGGREGATE_ERROR) { iterator = NULL; value = njs_arg(args, nargs, 1); njs_set_undefined(&list); } else { iterator = njs_arg(args, nargs, 1); value = njs_arg(args, nargs, 2); if (njs_slow_path(iterator->type < NJS_STRING)) { njs_type_error(vm, "first argument is not iterable"); return NJS_ERROR; } array = njs_iterator_to_array(vm, iterator, retval); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } njs_set_array(&list, array); } if (njs_slow_path(!njs_is_string(value))) { if (!njs_is_undefined(value)) { ret = njs_value_to_string(vm, value, value); if (njs_slow_path(ret != NJS_OK)) { return ret; } } } error = njs_error_alloc(vm, njs_vm_proto(vm, type), NULL, njs_is_defined(value) ? value : NULL, njs_is_defined(&list) ? &list : NULL); if (njs_slow_path(error == NULL)) { return NJS_ERROR; } njs_set_object(retval, error); return NJS_OK; } static const njs_object_prop_t njs_error_constructor_properties[] = { NJS_DECLARE_PROP_LENGTH(1), NJS_DECLARE_PROP_NAME("Error"), NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), }; const njs_object_init_t njs_error_constructor_init = { njs_error_constructor_properties, njs_nitems(njs_error_constructor_properties), }; static const njs_object_prop_t njs_eval_error_constructor_properties[] = { NJS_DECLARE_PROP_LENGTH(1), NJS_DECLARE_PROP_NAME("EvalError"), NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), }; const njs_object_init_t njs_eval_error_constructor_init = { njs_eval_error_constructor_properties, njs_nitems(njs_eval_error_constructor_properties), }; static const njs_object_prop_t njs_internal_error_constructor_properties[] = { NJS_DECLARE_PROP_LENGTH(1), NJS_DECLARE_PROP_NAME("InternalError"), NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), }; const njs_object_init_t njs_internal_error_constructor_init = { njs_internal_error_constructor_properties, njs_nitems(njs_internal_error_constructor_properties), }; static const njs_object_prop_t njs_range_error_constructor_properties[] = { NJS_DECLARE_PROP_LENGTH(1), NJS_DECLARE_PROP_NAME("RangeError"), NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), }; const njs_object_init_t njs_range_error_constructor_init = { njs_range_error_constructor_properties, njs_nitems(njs_range_error_constructor_properties), }; static const njs_object_prop_t njs_reference_error_constructor_properties[] = { NJS_DECLARE_PROP_LENGTH(1), NJS_DECLARE_PROP_NAME("ReferenceError"), NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), }; const njs_object_init_t njs_reference_error_constructor_init = { njs_reference_error_constructor_properties, njs_nitems(njs_reference_error_constructor_properties), }; static const njs_object_prop_t njs_syntax_error_constructor_properties[] = { NJS_DECLARE_PROP_LENGTH(1), NJS_DECLARE_PROP_NAME("SyntaxError"), NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), }; const njs_object_init_t njs_syntax_error_constructor_init = { njs_syntax_error_constructor_properties, njs_nitems(njs_syntax_error_constructor_properties), }; static const njs_object_prop_t njs_type_error_constructor_properties[] = { NJS_DECLARE_PROP_LENGTH(1), NJS_DECLARE_PROP_NAME("TypeError"), NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), }; const njs_object_init_t njs_type_error_constructor_init = { njs_type_error_constructor_properties, njs_nitems(njs_type_error_constructor_properties), }; static const njs_object_prop_t njs_uri_error_constructor_properties[] = { NJS_DECLARE_PROP_LENGTH(1), NJS_DECLARE_PROP_NAME("URIError"), NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), }; const njs_object_init_t njs_uri_error_constructor_init = { njs_uri_error_constructor_properties, njs_nitems(njs_uri_error_constructor_properties), }; static const njs_object_prop_t njs_aggregate_error_constructor_properties[] = { NJS_DECLARE_PROP_LENGTH(1), NJS_DECLARE_PROP_NAME("AggregateError"), NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), }; const njs_object_init_t njs_aggregate_error_constructor_init = { njs_aggregate_error_constructor_properties, njs_nitems(njs_aggregate_error_constructor_properties), }; void njs_memory_error_set(njs_vm_t *vm, njs_value_t *value) { njs_object_t *object; njs_object_value_t *ov; ov = &vm->memory_error_object; njs_set_data(&ov->value, NULL, NJS_DATA_TAG_ANY); object = &ov->object; njs_lvlhsh_init(&object->hash); njs_lvlhsh_init(&object->shared_hash); object->__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_INTERNAL_ERROR); object->slots = NULL; object->type = NJS_OBJECT_VALUE; object->shared = 1; /* * Marking it nonextensible to differentiate * it from ordinary internal errors. */ object->extensible = 0; object->fast_array = 0; object->error_data = 1; njs_set_object(value, object); } void njs_memory_error(njs_vm_t *vm) { njs_memory_error_set(vm, &vm->exception); } static njs_int_t njs_memory_error_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_memory_error_set(vm, retval); return NJS_OK; } static njs_int_t njs_memory_error_prototype_create(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { int32_t index; njs_function_t *function; const njs_value_t *proto; /* MemoryError has no its own prototype. */ index = NJS_OBJ_TYPE_INTERNAL_ERROR; function = njs_function(value); proto = njs_property_prototype_create(vm, &function->object.hash, njs_vm_proto(vm, index)); if (proto == NULL) { proto = &njs_value_undefined; } *retval = *proto; return NJS_OK; } static const njs_object_prop_t njs_memory_error_constructor_properties[] = { NJS_DECLARE_PROP_LENGTH(1), NJS_DECLARE_PROP_NAME("MemoryError"), NJS_DECLARE_PROP_HANDLER("prototype", njs_memory_error_prototype_create, 0, 0, 0), }; const njs_object_init_t njs_memory_error_constructor_init = { njs_memory_error_constructor_properties, njs_nitems(njs_memory_error_constructor_properties), }; static njs_int_t njs_error_prototype_value_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_value_assign(retval, njs_argument(args, 0)); return NJS_OK; } static njs_int_t njs_error_to_string2(njs_vm_t *vm, njs_value_t *retval, const njs_value_t *error, njs_bool_t want_stack) { size_t length; u_char *p; njs_int_t ret; njs_value_t value1, value2; njs_value_t *name_value, *message_value; njs_string_prop_t name, message; static const njs_value_t string_message = njs_string("message"); static const njs_value_t default_name = njs_string("Error"); njs_assert(njs_is_object(error)); if (want_stack) { ret = njs_error_stack(vm, njs_value_arg(error), retval); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (ret == NJS_OK) { return NJS_OK; } } ret = njs_value_property(vm, (njs_value_t *) error, njs_value_arg(&njs_string_name), &value1); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } name_value = (ret == NJS_OK) ? &value1 : njs_value_arg(&default_name); if (njs_slow_path(!njs_is_string(name_value))) { ret = njs_value_to_string(vm, &value1, name_value); if (njs_slow_path(ret != NJS_OK)) { return ret; } name_value = &value1; } (void) njs_string_prop(&name, name_value); ret = njs_value_property(vm, (njs_value_t *) error, njs_value_arg(&string_message), &value2); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } message_value = (ret == NJS_OK) ? &value2 : njs_value_arg(&njs_string_empty); if (njs_slow_path(!njs_is_string(message_value))) { ret = njs_value_to_string(vm, &value2, message_value); if (njs_slow_path(ret != NJS_OK)) { return ret; } message_value = &value2; } (void) njs_string_prop(&message, message_value); if (name.size == 0) { *retval = *message_value; return NJS_OK; } if (message.size == 0) { *retval = *name_value; return NJS_OK; } if (name.length != 0 && message.length != 0) { length = name.length + message.length + 2; } else { length = 0; } p = njs_string_alloc(vm, retval, name.size + message.size + 2, length); if (njs_fast_path(p != NULL)) { p = njs_cpymem(p, name.start, name.size); *p++ = ':'; *p++ = ' '; memcpy(p, message.start, message.size); return NJS_OK; } njs_memory_error(vm); return NJS_ERROR; } static njs_int_t njs_error_prototype_to_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { if (nargs < 1 || !njs_is_object(&args[0])) { njs_type_error(vm, "\"this\" argument is not an object"); return NJS_ERROR; } return njs_error_to_string2(vm, retval, &args[0], 0); } static njs_int_t njs_error_prototype_stack(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { njs_int_t ret; njs_str_t string; njs_arr_t *stack, *backtrace; njs_uint_t i; njs_value_t rv, *stackval; njs_stack_entry_t *se; if (retval != NULL) { if (!njs_is_error(value)) { njs_set_undefined(retval); return NJS_DECLINED; } stackval = njs_object_value(value); if (setval != NULL) { njs_value_assign(stackval, setval); return NJS_OK; } if (!njs_is_data(stackval, NJS_DATA_TAG_ANY)) { njs_value_assign(retval, stackval); return NJS_OK; } stack = njs_data(stackval); if (stack == NULL) { njs_set_undefined(retval); return NJS_OK; } se = stack->start; backtrace = njs_arr_create(vm->mem_pool, stack->items, sizeof(njs_backtrace_entry_t)); if (njs_slow_path(backtrace == NULL)) { return NJS_ERROR; } for (i = 0; i < stack->items; i++) { if (njs_add_backtrace_entry(vm, backtrace, &se[i]) != NJS_OK) { return NJS_ERROR; } } ret = njs_error_to_string2(vm, &rv, value, 0); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_string_get(&rv, &string); ret = njs_backtrace_to_string(vm, backtrace, &string); njs_arr_destroy(backtrace); njs_arr_destroy(stack); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_string_create(vm, stackval, string.start, string.length); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_value_assign(retval, stackval); return NJS_OK; } /* Delete. */ if (njs_is_error(value)) { stackval = njs_object_value(value); njs_set_data(stackval, NULL, NJS_DATA_TAG_ANY); } return NJS_OK; } njs_int_t njs_error_to_string(njs_vm_t *vm, njs_value_t *retval, const njs_value_t *error) { if (njs_slow_path(!njs_is_object(error))) { njs_type_error(vm, "\"error\" is not an object"); return NJS_ERROR; } return njs_error_to_string2(vm, retval, error, 1); } static const njs_object_prop_t njs_error_prototype_properties[] = { NJS_DECLARE_PROP_HANDLER("constructor", njs_object_prototype_create_constructor, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_VALUE("name", njs_string("Error"), NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_VALUE("message", njs_string(""), NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_NATIVE("valueOf", njs_error_prototype_value_of, 0, 0), NJS_DECLARE_PROP_NATIVE("toString", njs_error_prototype_to_string, 0, 0), NJS_DECLARE_PROP_HANDLER("stack", njs_error_prototype_stack, 0, 0, NJS_OBJECT_PROP_VALUE_CW), }; const njs_object_init_t njs_error_prototype_init = { njs_error_prototype_properties, njs_nitems(njs_error_prototype_properties), }; const njs_object_type_init_t njs_error_type_init = { .constructor = njs_native_ctor(njs_error_constructor, 1, NJS_OBJ_TYPE_ERROR), .constructor_props = &njs_error_constructor_init, .prototype_props = &njs_error_prototype_init, .prototype_value = { .object = { .type = NJS_OBJECT } }, }; static const njs_object_prop_t njs_eval_error_prototype_properties[] = { NJS_DECLARE_PROP_HANDLER("constructor", njs_object_prototype_create_constructor, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_VALUE("name", njs_string("EvalError"), NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_VALUE("message", njs_string(""), NJS_OBJECT_PROP_VALUE_CW), }; const njs_object_init_t njs_eval_error_prototype_init = { njs_eval_error_prototype_properties, njs_nitems(njs_eval_error_prototype_properties), }; const njs_object_type_init_t njs_eval_error_type_init = { .constructor = njs_native_ctor(njs_error_constructor, 1, NJS_OBJ_TYPE_EVAL_ERROR), .constructor_props = &njs_eval_error_constructor_init, .prototype_props = &njs_eval_error_prototype_init, .prototype_value = { .object = { .type = NJS_OBJECT } }, }; static njs_int_t njs_internal_error_prototype_to_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { static const njs_value_t name = njs_string("MemoryError"); if (nargs >= 1 && njs_is_object(&args[0])) { /* MemoryError is a nonextensible internal error. */ if (!njs_object(&args[0])->extensible) { njs_value_assign(retval, &name); return NJS_OK; } } return njs_error_prototype_to_string(vm, args, nargs, unused, retval); } static const njs_object_prop_t njs_internal_error_prototype_properties[] = { NJS_DECLARE_PROP_VALUE("name", njs_string("InternalError"), NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_VALUE("message", njs_string(""), NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_NATIVE("toString", njs_internal_error_prototype_to_string, 0, 0), }; const njs_object_init_t njs_internal_error_prototype_init = { njs_internal_error_prototype_properties, njs_nitems(njs_internal_error_prototype_properties), }; const njs_object_type_init_t njs_internal_error_type_init = { .constructor = njs_native_ctor(njs_error_constructor, 1, NJS_OBJ_TYPE_INTERNAL_ERROR), .constructor_props = &njs_internal_error_constructor_init, .prototype_props = &njs_internal_error_prototype_init, .prototype_value = { .object = { .type = NJS_OBJECT } }, }; const njs_object_type_init_t njs_memory_error_type_init = { .constructor = njs_native_ctor(njs_memory_error_constructor, 1, 0), .constructor_props = &njs_memory_error_constructor_init, .prototype_props = &njs_internal_error_prototype_init, .prototype_value = { .object = { .type = NJS_OBJECT } }, }; static const njs_object_prop_t njs_range_error_prototype_properties[] = { NJS_DECLARE_PROP_HANDLER("constructor", njs_object_prototype_create_constructor, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_VALUE("name", njs_string("RangeError"), NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_VALUE("message", njs_string(""), NJS_OBJECT_PROP_VALUE_CW), }; const njs_object_init_t njs_range_error_prototype_init = { njs_range_error_prototype_properties, njs_nitems(njs_range_error_prototype_properties), }; const njs_object_type_init_t njs_range_error_type_init = { .constructor = njs_native_ctor(njs_error_constructor, 1, NJS_OBJ_TYPE_RANGE_ERROR), .constructor_props = &njs_range_error_constructor_init, .prototype_props = &njs_range_error_prototype_init, .prototype_value = { .object = { .type = NJS_OBJECT } }, }; static const njs_object_prop_t njs_reference_error_prototype_properties[] = { NJS_DECLARE_PROP_HANDLER("constructor", njs_object_prototype_create_constructor, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_VALUE("name", njs_string("ReferenceError"), NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_VALUE("message", njs_string(""), NJS_OBJECT_PROP_VALUE_CW), }; const njs_object_init_t njs_reference_error_prototype_init = { njs_reference_error_prototype_properties, njs_nitems(njs_reference_error_prototype_properties), }; const njs_object_type_init_t njs_reference_error_type_init = { .constructor = njs_native_ctor(njs_error_constructor, 1, NJS_OBJ_TYPE_REF_ERROR), .constructor_props = &njs_reference_error_constructor_init, .prototype_props = &njs_reference_error_prototype_init, .prototype_value = { .object = { .type = NJS_OBJECT } }, }; static const njs_object_prop_t njs_syntax_error_prototype_properties[] = { NJS_DECLARE_PROP_HANDLER("constructor", njs_object_prototype_create_constructor, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_VALUE("name", njs_string("SyntaxError"), NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_VALUE("message", njs_string(""), NJS_OBJECT_PROP_VALUE_CW), }; const njs_object_init_t njs_syntax_error_prototype_init = { njs_syntax_error_prototype_properties, njs_nitems(njs_syntax_error_prototype_properties), }; const njs_object_type_init_t njs_syntax_error_type_init = { .constructor = njs_native_ctor(njs_error_constructor, 1, NJS_OBJ_TYPE_SYNTAX_ERROR), .constructor_props = &njs_syntax_error_constructor_init, .prototype_props = &njs_syntax_error_prototype_init, .prototype_value = { .object = { .type = NJS_OBJECT } }, }; static const njs_object_prop_t njs_type_error_prototype_properties[] = { NJS_DECLARE_PROP_HANDLER("constructor", njs_object_prototype_create_constructor, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_VALUE("name", njs_string("TypeError"), NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_VALUE("message", njs_string(""), NJS_OBJECT_PROP_VALUE_CW), }; const njs_object_init_t njs_type_error_prototype_init = { njs_type_error_prototype_properties, njs_nitems(njs_type_error_prototype_properties), }; const njs_object_type_init_t njs_type_error_type_init = { .constructor = njs_native_ctor(njs_error_constructor, 1, NJS_OBJ_TYPE_TYPE_ERROR), .constructor_props = &njs_type_error_constructor_init, .prototype_props = &njs_type_error_prototype_init, .prototype_value = { .object = { .type = NJS_OBJECT } }, }; static const njs_object_prop_t njs_uri_error_prototype_properties[] = { NJS_DECLARE_PROP_HANDLER("constructor", njs_object_prototype_create_constructor, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_VALUE("name", njs_string("URIError"), NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_VALUE("message", njs_string(""), NJS_OBJECT_PROP_VALUE_CW), }; const njs_object_init_t njs_uri_error_prototype_init = { njs_uri_error_prototype_properties, njs_nitems(njs_uri_error_prototype_properties), }; const njs_object_type_init_t njs_uri_error_type_init = { .constructor = njs_native_ctor(njs_error_constructor, 1, NJS_OBJ_TYPE_URI_ERROR), .constructor_props = &njs_uri_error_constructor_init, .prototype_props = &njs_uri_error_prototype_init, .prototype_value = { .object = { .type = NJS_OBJECT } }, }; static const njs_object_prop_t njs_aggregate_error_prototype_properties[] = { NJS_DECLARE_PROP_HANDLER("constructor", njs_object_prototype_create_constructor, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_VALUE("name", njs_string("AggregateError"), NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_VALUE("message", njs_string(""), NJS_OBJECT_PROP_VALUE_CW), }; const njs_object_init_t njs_aggregate_error_prototype_init = { njs_aggregate_error_prototype_properties, njs_nitems(njs_aggregate_error_prototype_properties), }; const njs_object_type_init_t njs_aggregate_error_type_init = { .constructor = njs_native_ctor(njs_error_constructor, 1, NJS_OBJ_TYPE_AGGREGATE_ERROR), .constructor_props = &njs_aggregate_error_constructor_init, .prototype_props = &njs_aggregate_error_prototype_init, .prototype_value = { .object = { .type = NJS_OBJECT } }, }; static njs_int_t njs_add_backtrace_entry(njs_vm_t *vm, njs_arr_t *stack, njs_stack_entry_t *se) { njs_int_t ret; njs_vm_code_t *code; njs_function_t *function; njs_backtrace_entry_t *be; function = se->native ? se->u.function : NULL; if (function != NULL && function->bound != NULL) { /* Skip. */ return NJS_OK; } be = njs_arr_add(stack); if (njs_slow_path(be == NULL)) { return NJS_ERROR; } be->line = 0; be->file = njs_str_value(""); if (function != NULL && function->native) { ret = njs_builtin_match_native_function(vm, function, &be->name); if (ret == NJS_OK) { return NJS_OK; } be->name = njs_entry_native; return NJS_OK; } code = njs_lookup_code(vm, se->u.pc); if (code != NULL) { be->name = code->name; if (be->name.length == 0) { be->name = njs_entry_anonymous; } be->line = njs_lookup_line(code->lines, se->u.pc - code->start); if (!vm->options.quiet) { be->file = code->file; } return NJS_OK; } be->name = njs_entry_unknown; return NJS_OK; } static njs_int_t njs_backtrace_to_string(njs_vm_t *vm, njs_arr_t *backtrace, njs_str_t *dst) { size_t count; njs_chb_t chain; njs_int_t ret; njs_uint_t i; njs_backtrace_entry_t *be, *prev; if (backtrace->items == 0) { return NJS_OK; } NJS_CHB_MP_INIT(&chain, vm); njs_chb_append_str(&chain, dst); njs_chb_append(&chain, "\n", 1); count = 0; prev = NULL; be = backtrace->start; for (i = 0; i < backtrace->items; i++) { if (i != 0 && prev->name.start == be->name.start && prev->line == be->line) { count++; } else { if (count != 0) { njs_chb_sprintf(&chain, 64, " repeats %uz times\n", count); count = 0; } njs_chb_sprintf(&chain, 10 + be->name.length, " at %V ", &be->name); if (be->line != 0) { njs_chb_sprintf(&chain, 12 + be->file.length, "(%V:%uD)\n", &be->file, be->line); } else { njs_chb_append(&chain, "(native)\n", 9); } } prev = be; be++; } ret = njs_chb_join(&chain, dst); njs_chb_destroy(&chain); return ret; } njs-0.8.9/src/njs_error.h000066400000000000000000000061571474132077100153040ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_ERROR_H_INCLUDED_ #define _NJS_ERROR_H_INCLUDED_ #define njs_error(vm, fmt, ...) \ njs_throw_error(vm, NJS_OBJ_TYPE_ERROR, fmt, ##__VA_ARGS__) #define njs_eval_error(vm, fmt, ...) \ njs_throw_error(vm, NJS_OBJ_TYPE_EVAL_ERROR, fmt, ##__VA_ARGS__) #define njs_internal_error(vm, fmt, ...) \ njs_throw_error(vm, NJS_OBJ_TYPE_INTERNAL_ERROR, fmt, ##__VA_ARGS__) #define njs_range_error(vm, fmt, ...) \ njs_throw_error(vm, NJS_OBJ_TYPE_RANGE_ERROR, fmt, ##__VA_ARGS__) #define njs_reference_error(vm, fmt, ...) \ njs_throw_error(vm, NJS_OBJ_TYPE_REF_ERROR, fmt, ##__VA_ARGS__) #define njs_syntax_error(vm, fmt, ...) \ njs_throw_error(vm, NJS_OBJ_TYPE_SYNTAX_ERROR, fmt, ##__VA_ARGS__) #define njs_type_error(vm, fmt, ...) \ njs_throw_error(vm, NJS_OBJ_TYPE_TYPE_ERROR, fmt, ##__VA_ARGS__) #define njs_uri_error(vm, fmt, ...) \ njs_throw_error(vm, NJS_OBJ_TYPE_URI_ERROR, fmt, ##__VA_ARGS__) void njs_error_new(njs_vm_t *vm, njs_value_t *dst, njs_object_t *proto, u_char *start, size_t size); void njs_noinline njs_error_fmt_new(njs_vm_t *vm, njs_value_t *dst, njs_object_type_t type, const char *fmt, ...); void njs_throw_error(njs_vm_t *vm, njs_object_type_t type, const char *fmt, ...); void njs_throw_error_va(njs_vm_t *vm, njs_object_t *proto, const char *fmt, va_list args); void njs_memory_error(njs_vm_t *vm); void njs_memory_error_set(njs_vm_t *vm, njs_value_t *value); njs_object_t *njs_error_alloc(njs_vm_t *vm, njs_object_t *proto, const njs_value_t *name, const njs_value_t *message, const njs_value_t *errors); njs_int_t njs_error_to_string(njs_vm_t *vm, njs_value_t *retval, const njs_value_t *error); njs_int_t njs_error_stack(njs_vm_t *vm, njs_value_t *value, njs_value_t *stack); njs_int_t njs_error_stack_attach(njs_vm_t *vm, njs_value_t value); extern const njs_object_type_init_t njs_error_type_init; extern const njs_object_type_init_t njs_eval_error_type_init; extern const njs_object_type_init_t njs_internal_error_type_init; extern const njs_object_type_init_t njs_range_error_type_init; extern const njs_object_type_init_t njs_reference_error_type_init; extern const njs_object_type_init_t njs_syntax_error_type_init; extern const njs_object_type_init_t njs_type_error_type_init; extern const njs_object_type_init_t njs_uri_error_type_init; extern const njs_object_type_init_t njs_memory_error_type_init; extern const njs_object_type_init_t njs_aggregate_error_type_init; njs_inline njs_int_t njs_is_memory_error(njs_vm_t *vm, njs_value_t *value) { if (njs_is_error(value) && njs_has_prototype(vm, value, NJS_OBJ_TYPE_INTERNAL_ERROR) && !njs_object(value)->extensible) { return 1; } return 0; } #endif /* _NJS_BOOLEAN_H_INCLUDED_ */ njs-0.8.9/src/njs_event.h000066400000000000000000000005361474132077100152670ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_EVENT_H_INCLUDED_ #define _NJS_EVENT_H_INCLUDED_ typedef struct { njs_function_t *function; njs_value_t *args; njs_uint_t nargs; njs_queue_link_t link; } njs_event_t; #endif /* _NJS_EVENT_H_INCLUDED_ */ njs-0.8.9/src/njs_extern.c000066400000000000000000000322501474132077100154440ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include static njs_int_t njs_external_prop_handler(njs_vm_t *vm, njs_object_prop_t *self, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t njs_external_add(njs_vm_t *vm, njs_arr_t *protos, const njs_external_t *external, njs_uint_t n) { size_t size; njs_int_t ret; njs_lvlhsh_t *hash; const u_char *start; njs_function_t *function; njs_object_prop_t *prop; njs_lvlhsh_query_t lhq; njs_exotic_slots_t *slot, *next; const njs_external_t *end; slot = njs_arr_add(protos); njs_memzero(slot, sizeof(njs_exotic_slots_t)); hash = &slot->external_shared_hash; njs_lvlhsh_init(hash); if (n == 0) { return NJS_OK; } lhq.replace = 0; lhq.proto = &njs_object_hash_proto; lhq.pool = vm->mem_pool; end = external + n; while (external < end) { if ((external->flags & NJS_EXTERN_TYPE_MASK) == NJS_EXTERN_SELF) { slot->writable = external->u.object.writable; slot->configurable = external->u.object.configurable; slot->enumerable = external->u.object.enumerable; slot->prop_handler = external->u.object.prop_handler; slot->magic32 = external->u.object.magic32; slot->keys = external->u.object.keys; external++; continue; } prop = njs_object_prop_alloc(vm, &njs_string_empty, &njs_value_invalid, 1); if (njs_slow_path(prop == NULL)) { goto memory_error; } prop->writable = external->writable; prop->configurable = external->configurable; prop->enumerable = external->enumerable; if (external->flags & NJS_EXTERN_SYMBOL) { njs_set_symbol(&prop->name, external->name.symbol, NULL); lhq.key_hash = external->name.symbol; } else { ret = njs_string_create(vm, &prop->name, external->name.string.start, external->name.string.length); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } lhq.key = external->name.string; lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length); } lhq.value = prop; switch (external->flags & NJS_EXTERN_TYPE_MASK) { case NJS_EXTERN_METHOD: function = njs_mp_zalloc(vm->mem_pool, sizeof(njs_function_t)); if (njs_slow_path(function == NULL)) { goto memory_error; } function->object.shared_hash = vm->shared->arrow_instance_hash; function->object.type = NJS_FUNCTION; function->object.shared = 1; function->object.extensible = 1; function->native = 1; function->u.native = external->u.method.native; function->magic8 = external->u.method.magic8; function->ctor = external->u.method.ctor; njs_set_function(njs_prop_value(prop), function); break; case NJS_EXTERN_PROPERTY: if (external->u.property.handler != NULL) { prop->type = NJS_PROPERTY_HANDLER; prop->u.value.type = NJS_INVALID; prop->u.value.data.truth = 1; njs_prop_magic16(prop) = external->u.property.magic16; njs_prop_magic32(prop) = external->u.property.magic32; njs_prop_handler(prop) = external->u.property.handler; } else { start = (u_char *) external->u.property.value; size = njs_strlen(start); ret = njs_string_create(vm, &prop->u.value, start, size); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } break; case NJS_EXTERN_OBJECT: default: next = njs_arr_item(protos, protos->items); ret = njs_external_add(vm, protos, external->u.object.properties, external->u.object.nproperties); if (njs_slow_path(ret != NJS_OK)) { return ret; } prop->type = NJS_PROPERTY_HANDLER; prop->u.value.type = NJS_INVALID; prop->u.value.data.truth = 1; njs_prop_magic16(prop) = next - slot; njs_prop_magic32(prop) = lhq.key_hash; njs_prop_handler(prop) = njs_external_prop_handler; if (external->u.object.prop_handler) { if (next->prop_handler) { njs_internal_error(vm, "overwritten self prop_handler"); return NJS_ERROR; } next->writable = external->u.object.writable; next->configurable = external->u.object.configurable; next->enumerable = external->u.object.enumerable; next->prop_handler = external->u.object.prop_handler; next->magic32 = external->u.object.magic32; } if (external->u.object.keys) { if (next->keys) { njs_internal_error(vm, "overwritten self keys"); return NJS_ERROR; } next->keys = external->u.object.keys; } break; } ret = njs_lvlhsh_insert(hash, &lhq); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "lvlhsh insert failed"); return NJS_ERROR; } external++; } return NJS_OK; memory_error: njs_memory_error(vm); return NJS_ERROR; } static njs_int_t njs_external_prop_handler(njs_vm_t *vm, njs_object_prop_t *self, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { njs_int_t ret; njs_object_prop_t *prop; njs_external_ptr_t external; njs_object_value_t *ov; njs_lvlhsh_query_t lhq; njs_exotic_slots_t *slots; if (njs_slow_path(retval == NULL)) { return NJS_DECLINED; } slots = NULL; if (njs_slow_path(setval != NULL)) { *retval = *setval; } else { ov = njs_object_value_alloc(vm, NJS_OBJ_TYPE_OBJECT, 0, NULL); if (njs_slow_path(ov == NULL)) { return NJS_ERROR; } slots = njs_object(value)->slots + njs_prop_magic16(self); ov->object.shared_hash = slots->external_shared_hash; ov->object.slots = slots; external = njs_vm_external(vm, NJS_PROTO_ID_ANY, value); njs_set_data(&ov->value, external, njs_value_external_tag(value)); njs_set_object_value(retval, ov); } prop = njs_object_prop_alloc(vm, &self->name, retval, 1); if (njs_slow_path(prop == NULL)) { return NJS_ERROR; } prop->writable = self->writable; prop->configurable = self->configurable; prop->enumerable = self->enumerable; lhq.value = prop; njs_string_get(&self->name, &lhq.key); lhq.key_hash = njs_prop_magic32(self); lhq.replace = 1; lhq.pool = vm->mem_pool; lhq.proto = &njs_object_hash_proto; ret = njs_lvlhsh_insert(njs_object_hash(value), &lhq); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "lvlhsh insert/replace failed"); return NJS_ERROR; } return NJS_OK; } static njs_uint_t njs_external_protos(const njs_external_t *external, njs_uint_t size) { njs_uint_t n; n = 1; while (size != 0) { if ((external->flags & 3) == NJS_EXTERN_OBJECT) { n += njs_external_protos(external->u.object.properties, external->u.object.nproperties); } size--; external++; } return n; } njs_int_t njs_vm_external_prototype(njs_vm_t *vm, const njs_external_t *definition, njs_uint_t n) { njs_arr_t *protos, **pr; njs_int_t ret; njs_uint_t size; size = njs_external_protos(definition, n) + 1; protos = njs_arr_create(vm->mem_pool, size, sizeof(njs_exotic_slots_t)); if (njs_slow_path(protos == NULL)) { njs_memory_error(vm); return -1; } ret = njs_external_add(vm, protos, definition, n); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "njs_vm_external_add() failed"); return -1; } if (vm->protos == NULL) { vm->protos = njs_arr_create(vm->mem_pool, 4, sizeof(njs_arr_t *)); if (njs_slow_path(vm->protos == NULL)) { return -1; } } pr = njs_arr_add(vm->protos); if (njs_slow_path(pr == NULL)) { return -1; } *pr = protos; return vm->protos->items - 1; } static njs_int_t njs_vm_external_constructor_handler(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { njs_set_function(retval, &njs_vm_ctor(vm, njs_prop_magic32(prop))); return NJS_OK; } njs_int_t njs_vm_external_constructor(njs_vm_t *vm, const njs_str_t *name, njs_function_native_t native, const njs_external_t *ctor_props, njs_uint_t ctor_nprops, const njs_external_t *proto_props, njs_uint_t proto_nprops) { njs_int_t ret, index, proto_id; njs_arr_t **pprotos; njs_function_t *constructor; njs_exotic_slots_t *slots; njs_object_prototype_t *prototype; index = njs_vm_ctor_push(vm); if (njs_slow_path(index < 0)) { njs_internal_error(vm, "njs_vm_ctor_push() failed"); return -1; } proto_id = njs_vm_external_prototype(vm, proto_props, proto_nprops); if (njs_slow_path(proto_id < 0)) { njs_internal_error(vm, "njs_vm_external_prototype(proto_props) failed"); return -1; } prototype = njs_shared_prototype(vm->shared, index); njs_memzero(prototype, sizeof(njs_object_prototype_t)); prototype->object.type = NJS_OBJECT; prototype->object.extensible = 1; pprotos = njs_arr_item(vm->protos, proto_id); slots = (*pprotos)->start; prototype->object.shared_hash = slots->external_shared_hash; proto_id = njs_vm_external_prototype(vm, ctor_props, ctor_nprops); if (njs_slow_path(proto_id < 0)) { njs_internal_error(vm, "njs_vm_external_prototype(ctor_props) failed"); return -1; } constructor = njs_shared_ctor(vm->shared, index); njs_memzero(constructor, sizeof(njs_function_t)); constructor->object.type = NJS_FUNCTION; constructor->u.native = native; constructor->magic8 = index; constructor->native = 1; constructor->ctor = 1; pprotos = njs_arr_item(vm->protos, proto_id); slots = (*pprotos)->start; constructor->object.shared_hash = slots->external_shared_hash; ret = njs_vm_bind_handler(vm, name, njs_vm_external_constructor_handler, 0, index, 1); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } return index; } njs_int_t njs_vm_external_create(njs_vm_t *vm, njs_value_t *value, njs_int_t proto_id, njs_external_ptr_t external, njs_bool_t shared) { njs_arr_t **pprotos; njs_object_value_t *ov; njs_exotic_slots_t *slots; if (vm->protos == NULL || (njs_int_t) vm->protos->items <= proto_id) { return NJS_ERROR; } ov = njs_object_value_alloc(vm, NJS_OBJ_TYPE_OBJECT, 0, NULL); if (njs_slow_path(ov == NULL)) { return NJS_ERROR; } pprotos = njs_arr_item(vm->protos, proto_id); slots = (*pprotos)->start; ov->object.shared_hash = slots->external_shared_hash; ov->object.shared = shared; ov->object.slots = slots; njs_set_object_value(value, ov); njs_set_data(&ov->value, external, njs_make_tag(proto_id)); return NJS_OK; } njs_external_ptr_t njs_vm_external(njs_vm_t *vm, njs_int_t proto_id, const njs_value_t *value) { njs_external_ptr_t external; if (njs_fast_path(njs_is_object_data(value, njs_make_tag(proto_id)))) { external = njs_object_data(value); if (external == NULL) { external = vm->external; } return external; } return NULL; } njs_int_t njs_value_external_tag(const njs_value_t *value) { if (njs_is_object_data(value, njs_make_tag(NJS_PROTO_ID_ANY))) { return njs_object_value(value)->data.magic32; } return -1; } njs_int_t njs_external_property(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { char *p; njs_int_t i; njs_uint_t ui; p = njs_vm_external(vm, NJS_PROTO_ID_ANY, value); if (p == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } switch (njs_vm_prop_magic16(prop)) { case NJS_EXTERN_TYPE_INT: i = *(njs_int_t *) (p + njs_vm_prop_magic32(prop)); njs_value_number_set(retval, i); break; case NJS_EXTERN_TYPE_UINT: ui = *(njs_uint_t *) (p + njs_vm_prop_magic32(prop)); njs_value_number_set(retval, ui); break; case NJS_EXTERN_TYPE_VALUE: default: njs_value_assign(retval, (njs_value_t *) (p + njs_vm_prop_magic32(prop))); } return NJS_OK; } njs-0.8.9/src/njs_flathsh.c000066400000000000000000000366241474132077100156010ustar00rootroot00000000000000 /* * Copyright (C) NGINX, Inc. */ #include /* * Flat hash consists of dynamic DATA STRUCTURE and set of OPERATIONS. * * DATA STRUCTURE * Consists of 3 parts allocated one by one in chunk of memory: * * HASH_CELLS array of indices of 1st list element in ELEMENTS array, * or 0 if list is empty. HASH_CELLS_length is power of 2. * DESCRIPTOR contains hash_mask (= HASH_CELLS_Length - 1), ELEMENTS_size, * number of last used element in ELEMENTS, * number of deleted elements in ELEMENTS; * ELEMENTS array of: [index of next element, hash_function(KEY), * link to stored value or NULL if element is not used)]. * * PROPERTIES of flat hash * It is relocatable in memory, preserve insertion order, expand and shrink * depending on number elements in it, maximum occupancy is 2^32-2 elements. * * OPERATIONS * ADD element by key S with value V * Prerequisite: be sure if flat hash not contains S. If ELEMENTS has free * space after last element, then this element is populated by: V, * hash_function(S), S. Then element is added to correspondent HASH_CELL. * In case when no free element in ELEMENTS, DATA STRUCTURE is expanded by * expnad_elts(). It does the following: ELEMENTS_size is increased by * EXPAND_FACTOR, which value is expected to be > 1. For fast access to * stored values, HASH_CELLS_size need to be big enough to provide its low * population: in average less than 1 element per HASH_CELL. So, * if HASH_CELLS_size < ELEMENTS_size then it will try doubling * HASH_CELLS_size, until new HASH_CELLS_size >= ELEMENTS_size. Now * new HASH_CELLS_size and new ELEMENTS_size are both defined. New * expanded hash is obtained as: * if HASH_CELLS_size is not increased, then * reallocate full DATA STRUCTURE, * else * create new DATA STRUCTURE and populate it * by ELEMENTS from old DATA STRUCTURE. * Replace old DATA STRUCTURE by new one and release old one. * * FIND element by key S * HASH_CELLS is array which contains cells of hash * table. As entry to the table the following index is used: * cell_num = hash_function(S) & hash_nask * hash_function is external and it is not specified here, it is needed to * be good hash function for Ss, and produce results in range from 0 to * at least 2^32 - 1; hash_mask is located in DESCRIPTOR, and it is equal * to HASH_CELLS_size - 1, where HASH_CELLS_size is always power of 2. * hash cell contains (may be empty) list of hash elements with same * cell_num. Now run over the list of elements and test if some element * contains link to S. Test function is external and is not specified here. * * DELETE element by key S * Locate S in ELEMENTS and remove it from elements list. Update number * of removed elements in hash_decriptor. Mark deleted * element as not used/deleted. If number of deleted elements is big * enough, then use shrink_elts(): it removes gaps in ELEMENTS, shrinks if * required HASH_CELLS, and creates new DATA STRUCTURE. * * ENUMERATE all elements in order of insertion * Returns one by one used elements from ELEMENTS. */ #define NJS_FLATHSH_ELTS_INITIAL_SIZE 2 #define NJS_FLATHSH_HASH_INITIAL_SIZE 4 #define NJS_FLATHSH_ELTS_EXPAND_FACTOR_NUM 3 #define NJS_FLATHSH_ELTS_EXPAND_FACTOR_DENOM 2 #define NJS_FLATHSH_ELTS_FRACTION_TO_SHRINK 2 #define NJS_FLATHSH_ELTS_MINIMUM_TO_SHRINK 8 struct njs_flathsh_descr_s { uint32_t hash_mask; uint32_t elts_size; /* allocated properties */ uint32_t elts_count; /* include deleted properties */ uint32_t elts_deleted_count; }; static njs_flathsh_descr_t *njs_flathsh_alloc(njs_flathsh_query_t *fhq, size_t hash_size, size_t elts_size); static njs_flathsh_descr_t *njs_expand_elts(njs_flathsh_query_t *fhq, njs_flathsh_descr_t *h); njs_inline size_t njs_flathsh_chunk_size(size_t hash_size, size_t elts_size) { return hash_size * sizeof(uint32_t) + sizeof(njs_flathsh_descr_t) + elts_size * sizeof(njs_flathsh_elt_t); } njs_inline void * njs_flathsh_malloc(njs_flathsh_query_t *fhq, size_t size) { return #ifdef NJS_FLATHSH_USE_SYSTEM_ALLOCATOR malloc(size) #else fhq->proto->alloc(fhq->pool, size) #endif ; } njs_inline void njs_flathsh_free(njs_flathsh_query_t *fhq, void *ptr) { #ifdef NJS_FLATHSH_USE_SYSTEM_ALLOCATOR free(ptr) #else fhq->proto->free(fhq->pool, ptr, 0) #endif ; } njs_inline njs_flathsh_descr_t * njs_flathsh_descr(void *chunk, size_t hash_size) { return (njs_flathsh_descr_t *) ((uint32_t *) chunk + hash_size); } njs_inline uint32_t * njs_hash_cells_end(njs_flathsh_descr_t *h) { return (uint32_t *) h; } njs_inline void * njs_flathsh_chunk(njs_flathsh_descr_t *h) { return njs_hash_cells_end(h) - ((njs_int_t) h->hash_mask + 1); } njs_inline njs_flathsh_elt_t * njs_hash_elts(njs_flathsh_descr_t *h) { return (njs_flathsh_elt_t *) ((char *) h + sizeof(njs_flathsh_descr_t)); } /* * Create a new empty flat hash. */ njs_flathsh_descr_t * njs_flathsh_new(njs_flathsh_query_t *fhq) { return njs_flathsh_alloc(fhq, NJS_FLATHSH_HASH_INITIAL_SIZE, NJS_FLATHSH_ELTS_INITIAL_SIZE); } void njs_flathsh_destroy(njs_flathsh_t *fh, njs_flathsh_query_t *fhq) { njs_flathsh_free(fhq, njs_flathsh_chunk(fh->slot)); fh->slot = NULL; } static njs_flathsh_descr_t * njs_flathsh_alloc(njs_flathsh_query_t *fhq, size_t hash_size, size_t elts_size) { void *chunk; size_t size; njs_flathsh_descr_t *h; njs_assert_msg(hash_size != 0 && (hash_size & (hash_size - 1)) == 0, "hash_size must be a power of two"); size = njs_flathsh_chunk_size(hash_size, elts_size); chunk = njs_flathsh_malloc(fhq, size); if (njs_slow_path(chunk == NULL)) { return NULL; } h = njs_flathsh_descr(chunk, hash_size); njs_memzero(chunk, sizeof(uint32_t) * hash_size); h->hash_mask = hash_size - 1; h->elts_size = elts_size; h->elts_count = 0; h->elts_deleted_count = 0; return h; } njs_flathsh_elt_t * njs_flathsh_add_elt(njs_flathsh_t *fh, njs_flathsh_query_t *fhq) { njs_int_t cell_num; njs_flathsh_elt_t *elt, *elts; njs_flathsh_descr_t *h; h = fh->slot; if (njs_slow_path(h == NULL)) { return NULL; } if (njs_slow_path(h->elts_count == h->elts_size)) { h = njs_expand_elts(fhq, h); if (njs_slow_path(h == NULL)) { return NULL; } fh->slot = h; } elts = njs_hash_elts(h); elt = &elts[h->elts_count++]; elt->value = fhq->value; elt->key_hash = fhq->key_hash; cell_num = fhq->key_hash & h->hash_mask; elt->next_elt = njs_hash_cells_end(h)[-cell_num - 1]; njs_hash_cells_end(h)[-cell_num - 1] = h->elts_count; return elt; } static njs_flathsh_descr_t * njs_expand_elts(njs_flathsh_query_t *fhq, njs_flathsh_descr_t *h) { void *chunk; size_t size, new_elts_size, new_hash_size; uint32_t new_hash_mask, i; njs_int_t cell_num; njs_flathsh_elt_t *elt; njs_flathsh_descr_t *h_src; new_elts_size = h->elts_size * (size_t) NJS_FLATHSH_ELTS_EXPAND_FACTOR_NUM / NJS_FLATHSH_ELTS_EXPAND_FACTOR_DENOM; new_elts_size = njs_max(h->elts_count + 1ul, new_elts_size); new_hash_size = h->hash_mask + 1ul; while (new_hash_size < new_elts_size) { new_hash_size = 2 * new_hash_size; } /* Overflow check. */ if (njs_slow_path(new_hash_size > UINT32_MAX)) { return NULL; } if (new_hash_size != (h->hash_mask + 1)) { /* Expand both hash table cells and its elts. */ h_src = h; size = njs_flathsh_chunk_size(new_hash_size, new_elts_size); chunk = njs_flathsh_malloc(fhq, size); if (njs_slow_path(chunk == NULL)) { return NULL; } h = njs_flathsh_descr(chunk, new_hash_size); memcpy(h, h_src, sizeof(njs_flathsh_descr_t) + sizeof(njs_flathsh_elt_t) * h_src->elts_size); new_hash_mask = new_hash_size - 1; h->hash_mask = new_hash_mask; njs_memzero(chunk, sizeof(uint32_t) * new_hash_size); for (i = 0, elt = njs_hash_elts(h); i < h->elts_count; i++, elt++) { if (elt->value != NULL) { cell_num = elt->key_hash & new_hash_mask; elt->next_elt = njs_hash_cells_end(h)[-cell_num - 1]; njs_hash_cells_end(h)[-cell_num - 1] = i + 1; } } njs_flathsh_free(fhq, njs_flathsh_chunk(h_src)); } else { size = njs_flathsh_chunk_size(new_hash_size, new_elts_size); /* Expand elts only. */ #ifdef NJS_FLATHSH_USE_SYSTEM_ALLOCATOR chunk = realloc(njs_flathsh_chunk(h), size); if (njs_slow_path(chunk == NULL)) { return NULL; } #else chunk = fhq->proto->alloc(fhq->pool, size); if (njs_slow_path(chunk == NULL)) { return NULL; } memcpy(chunk, njs_flathsh_chunk(h), njs_flathsh_chunk_size(h->hash_mask + 1, h->elts_size)); fhq->proto->free(fhq->pool, njs_flathsh_chunk(h), 0); #endif h = njs_flathsh_descr(chunk, new_hash_size); } h->elts_size = new_elts_size; return h; } njs_int_t njs_flathsh_find(const njs_flathsh_t *fh, njs_flathsh_query_t *fhq) { njs_int_t cell_num, elt_num; njs_flathsh_elt_t *e, *elts; njs_flathsh_descr_t *h; h = fh->slot; if (njs_slow_path(h == NULL)) { return NJS_DECLINED; } cell_num = fhq->key_hash & h->hash_mask; elt_num = njs_hash_cells_end(h)[-cell_num - 1]; elts = njs_hash_elts(h); while (elt_num != 0) { e = &elts[elt_num - 1]; /* TODO: need to be replaced by atomic test. */ if (e->key_hash == fhq->key_hash && fhq->proto->test(fhq, e->value) == NJS_OK) { fhq->value = e->value; return NJS_OK; } elt_num = e->next_elt; } return NJS_DECLINED; } njs_int_t njs_flathsh_insert(njs_flathsh_t *fh, njs_flathsh_query_t *fhq) { void *tmp; njs_int_t cell_num, elt_num; njs_flathsh_elt_t *elt, *elts; njs_flathsh_descr_t *h; h = fh->slot; if (h == NULL) { h = njs_flathsh_new(fhq); if (h == NULL) { return NJS_ERROR; } fh->slot = h; } cell_num = fhq->key_hash & h->hash_mask; elt_num = njs_hash_cells_end(h)[-cell_num - 1]; elts = njs_hash_elts(h); while (elt_num != 0) { elt = &elts[elt_num - 1]; /* TODO: need to be replaced by atomic test. */ if (elt->key_hash == fhq->key_hash && fhq->proto->test(fhq, elt->value) == NJS_OK) { if (fhq->replace) { tmp = fhq->value; fhq->value = elt->value; elt->value = tmp; return NJS_OK; } else { fhq->value = elt->value; return NJS_DECLINED; } } elt_num = elt->next_elt; } elt = njs_flathsh_add_elt(fh, fhq); if (elt == NULL) { return NJS_ERROR; } elt->value = fhq->value; return NJS_OK; } static njs_flathsh_descr_t * njs_shrink_elts(njs_flathsh_query_t *fhq, njs_flathsh_descr_t *h) { void *chunk; njs_int_t cell_num; uint32_t i, j, new_hash_size, new_hash_mask, new_elts_size; njs_flathsh_elt_t *elt, *elt_src; njs_flathsh_descr_t *h_src; new_elts_size = njs_max(NJS_FLATHSH_ELTS_INITIAL_SIZE, h->elts_count - h->elts_deleted_count); njs_assert(new_elts_size <= h->elts_size); new_hash_size = h->hash_mask + 1; while ((new_hash_size / 2) >= new_elts_size) { new_hash_size = new_hash_size / 2; } new_hash_mask = new_hash_size - 1; h_src = h; chunk = njs_flathsh_malloc(fhq, njs_flathsh_chunk_size(new_hash_size, new_elts_size)); if (njs_slow_path(chunk == NULL)) { return NULL; } h = njs_flathsh_descr(chunk, new_hash_size); memcpy(h, h_src, sizeof(njs_flathsh_descr_t)); njs_memzero(njs_hash_cells_end(h) - new_hash_size, sizeof(uint32_t) * new_hash_size); elt_src = njs_hash_elts(h_src); for (i = 0, j = 0, elt = njs_hash_elts(h); i < h->elts_count; i++) { if (elt_src->value != NULL) { elt->value = elt_src->value; elt->key_hash = elt_src->key_hash; cell_num = elt_src->key_hash & new_hash_mask; elt->next_elt = njs_hash_cells_end(h)[-cell_num - 1]; njs_hash_cells_end(h)[-cell_num - 1] = j + 1; j++; elt++; } elt_src++; } njs_assert(j == (h->elts_count - h->elts_deleted_count)); h->hash_mask = new_hash_mask; h->elts_size = new_elts_size; h->elts_deleted_count = 0; h->elts_count = j; njs_flathsh_free(fhq, njs_flathsh_chunk(h_src)); return h; } njs_int_t njs_flathsh_delete(njs_flathsh_t *fh, njs_flathsh_query_t *fhq) { njs_int_t cell_num, elt_num; njs_flathsh_elt_t *elt, *elt_prev, *elts; njs_flathsh_descr_t *h; h = fh->slot; if (njs_slow_path(h == NULL)) { return NJS_DECLINED; } cell_num = fhq->key_hash & h->hash_mask; elt_num = njs_hash_cells_end(h)[-cell_num - 1]; elts = njs_hash_elts(h); elt_prev = NULL; while (elt_num != 0) { elt = &elts[elt_num - 1]; /* TODO: use atomic comparision. */ if (elt->key_hash == fhq->key_hash && fhq->proto->test(fhq, elt->value) == NJS_OK) { fhq->value = elt->value; if (elt_prev != NULL) { elt_prev->next_elt = elt->next_elt; } else { njs_hash_cells_end(h)[-cell_num - 1] = elt->next_elt; } h->elts_deleted_count++; elt->value = NULL; /* Shrink elts if elts_deleted_count is eligible. */ if (h->elts_deleted_count >= NJS_FLATHSH_ELTS_MINIMUM_TO_SHRINK && h->elts_deleted_count >= (h->elts_count / NJS_FLATHSH_ELTS_FRACTION_TO_SHRINK)) { h = njs_shrink_elts(fhq, h); if (njs_slow_path(h == NULL)) { return NJS_ERROR; } fh->slot = h; } if (h->elts_deleted_count == h->elts_count) { njs_flathsh_free(fhq, njs_flathsh_chunk(h)); fh->slot = NULL; } return NJS_OK; } elt_prev = elt; elt_num = elt->next_elt; } return NJS_DECLINED; } void * njs_flathsh_each(const njs_flathsh_t *fh, njs_flathsh_each_t *fhe) { void *v; njs_flathsh_elt_t *elt; njs_flathsh_descr_t *h; h = fh->slot; if (njs_slow_path(h == NULL)) { return NULL; } elt = njs_hash_elts(h); while (fhe->cp < h->elts_count) { v = elt[fhe->cp++].value; if (v != NULL) { return v; } } return NULL; } njs-0.8.9/src/njs_flathsh.h000066400000000000000000000115371474132077100156020ustar00rootroot00000000000000 /* * Copyright (C) NGINX, Inc. */ #ifndef _NJS_FLATHSH_H_INCLUDED_ #define _NJS_FLATHSH_H_INCLUDED_ typedef struct { void *slot; } njs_flathsh_t; typedef struct { uint32_t next_elt; uint32_t key_hash; void *value; } njs_flathsh_elt_t; typedef struct njs_flathsh_descr_s njs_flathsh_descr_t; typedef struct njs_flathsh_query_s njs_flathsh_query_t; typedef njs_int_t (*njs_flathsh_test_t)(njs_flathsh_query_t *fhq, void *data); typedef void *(*njs_flathsh_alloc_t)(void *ctx, size_t size); typedef void (*njs_flathsh_free_t)(void *ctx, void *p, size_t size); typedef struct njs_flathsh_proto_s njs_flathsh_proto_t; struct njs_flathsh_proto_s { uint32_t not_used; njs_flathsh_test_t test; njs_flathsh_alloc_t alloc; njs_flathsh_free_t free; }; struct njs_flathsh_query_s { uint32_t key_hash; njs_str_t key; uint8_t replace; /* 1 bit */ void *value; const njs_flathsh_proto_t *proto; void *pool; /* Opaque data passed for the test function. */ void *data; }; #define njs_flathsh_is_empty(fh) \ ((fh)->slot == NULL) #define njs_flathsh_init(fh) \ (fh)->slot = NULL #define njs_flathsh_eq(fhl, fhr) \ ((fhl)->slot == (fhr)->slot) /* * njs_flathsh_find() finds a hash element. If the element has been * found then it is stored in the fhq->value and njs_flathsh_find() * returns NJS_OK. Otherwise NJS_DECLINED is returned. * * The required njs_flathsh_query_t fields: key_hash, key, proto. */ NJS_EXPORT njs_int_t njs_flathsh_find(const njs_flathsh_t *fh, njs_flathsh_query_t *fhq); /* * njs_flathsh_insert() adds a hash element. If the element is already * present in flathsh and the fhq->replace flag is zero, then fhq->value * is updated with the old element and NJS_DECLINED is returned. * If the element is already present in flathsh and the fhq->replace flag * is non-zero, then the old element is replaced with the new element. * fhq->value is updated with the old element, and NJS_OK is returned. * If the element is not present in flathsh, then it is inserted and * NJS_OK is returned. The fhq->value is not changed. * On memory allocation failure NJS_ERROR is returned. * * The required njs_flathsh_query_t fields: key_hash, key, proto, replace, * value. * The optional njs_flathsh_query_t fields: pool. */ NJS_EXPORT njs_int_t njs_flathsh_insert(njs_flathsh_t *fh, njs_flathsh_query_t *fhq); /* * njs_flathsh_delete() deletes a hash element. If the element has been * found then it is removed from flathsh and is stored in the fhq->value, * and NJS_OK is returned. Otherwise NJS_DECLINED is returned. * * The required njs_flathsh_query_t fields: key_hash, key, proto. * The optional njs_flathsh_query_t fields: pool. */ NJS_EXPORT njs_int_t njs_flathsh_delete(njs_flathsh_t *fh, njs_flathsh_query_t *fhq); typedef struct { const njs_flathsh_proto_t *proto; uint32_t key_hash; uint32_t cp; } njs_flathsh_each_t; #define njs_flathsh_each_init(lhe, _proto) \ do { \ njs_memzero(lhe, sizeof(njs_flathsh_each_t)); \ (lhe)->proto = _proto; \ } while (0) NJS_EXPORT void *njs_flathsh_each(const njs_flathsh_t *fh, njs_flathsh_each_t *fhe); /* * Add element into hash. * The element value is not initialized. * Returns NULL if memory error in hash expand. */ NJS_EXPORT njs_flathsh_elt_t *njs_flathsh_add_elt(njs_flathsh_t *fh, njs_flathsh_query_t *fhq); NJS_EXPORT njs_flathsh_descr_t *njs_flathsh_new(njs_flathsh_query_t *fhq); NJS_EXPORT void njs_flathsh_destroy(njs_flathsh_t *fh, njs_flathsh_query_t *fhq); /* Temporary backward compatibility .*/ typedef struct njs_flathsh_query_s njs_lvlhsh_query_t; #define NJS_LVLHSH_DEFAULT 0 #define NJS_LVLHSH_LARGE_SLAB 0 typedef struct njs_flathsh_proto_s njs_lvlhsh_proto_t; #define njs_lvlhsh_is_empty njs_flathsh_is_empty #define njs_lvlhsh_init njs_flathsh_init #define njs_lvlhsh_eq njs_flathsh_eq #define njs_lvlhsh_t njs_flathsh_t #define njs_lvlhsh_each_t njs_flathsh_each_t #define njs_lvlhsh_find(lh, lhq) njs_flathsh_find(lh, lhq) #define njs_lvlhsh_insert(lh, lhq) njs_flathsh_insert(lh, lhq) #define njs_lvlhsh_delete(lh, lhq) njs_flathsh_delete(lh, lhq) #define njs_lvlhsh_each_init(lhe, _proto) njs_flathsh_each_init(lhe, _proto) #define njs_lvlhsh_each(lh, lhe) njs_flathsh_each(lh, lhe) #endif /* _NJS_FLATHSH_H_INCLUDED_ */ njs-0.8.9/src/njs_function.c000066400000000000000000001134431474132077100157700ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include static njs_int_t njs_function_native_call(njs_vm_t *vm, njs_value_t *retval); njs_function_t * njs_function_alloc(njs_vm_t *vm, njs_function_lambda_t *lambda, njs_bool_t async) { size_t size; njs_object_t *proto; njs_function_t *function; size = sizeof(njs_function_t) + lambda->nclosures * sizeof(njs_value_t *); function = njs_mp_zalloc(vm->mem_pool, size); if (njs_slow_path(function == NULL)) { goto fail; } /* * njs_mp_zalloc() does also: * njs_lvlhsh_init(&function->object.hash); * function->object.__proto__ = NULL; */ function->ctor = lambda->ctor; function->u.lambda = lambda; if (function->ctor) { function->object.shared_hash = vm->shared->function_instance_hash; } else if (async) { function->object.shared_hash = vm->shared->async_function_instance_hash; } else { function->object.shared_hash = vm->shared->arrow_instance_hash; } if (async) { proto = njs_vm_proto(vm, NJS_OBJ_TYPE_ASYNC_FUNCTION); } else { proto = njs_vm_proto(vm, NJS_OBJ_TYPE_FUNCTION); } function->object.__proto__ = proto; function->object.type = NJS_FUNCTION; function->object.extensible = 1; return function; fail: njs_memory_error(vm); return NULL; } njs_function_t * njs_vm_function_alloc(njs_vm_t *vm, njs_function_native_t native, njs_bool_t shared, njs_bool_t ctor) { njs_function_t *function; function = njs_mp_zalloc(vm->mem_pool, sizeof(njs_function_t)); if (njs_slow_path(function == NULL)) { njs_memory_error(vm); return NULL; } function->native = 1; function->ctor = ctor; function->object.shared = shared; function->u.native = native; function->object.shared_hash = vm->shared->function_instance_hash; function->object.__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_FUNCTION); function->object.type = NJS_FUNCTION; return function; } njs_function_t * njs_function_value_copy(njs_vm_t *vm, njs_value_t *value) { njs_function_t *function, *copy; function = njs_function(value); if (!function->object.shared) { return function; } copy = njs_function_copy(vm, function); if (njs_slow_path(copy == NULL)) { njs_memory_error(vm); return NULL; } value->data.u.function = copy; return copy; } njs_int_t njs_function_name_set(njs_vm_t *vm, njs_function_t *function, njs_value_t *name, const char *prefix) { u_char *p; size_t len, symbol; njs_int_t ret; njs_value_t value; njs_string_prop_t string; njs_object_prop_t *prop; njs_lvlhsh_query_t lhq; prop = njs_object_prop_alloc(vm, &njs_string_name, name, 0); if (njs_slow_path(prop == NULL)) { return NJS_ERROR; } symbol = 0; if (njs_is_symbol(njs_prop_value(prop))) { symbol = 2; njs_value_assign(njs_prop_value(prop), njs_symbol_description(njs_prop_value(prop))); } if (prefix != NULL || symbol != 0) { if (njs_is_defined(njs_prop_value(prop))) { njs_value_assign(&value, njs_prop_value(prop)); (void) njs_string_prop(&string, &value); len = (prefix != NULL) ? njs_strlen(prefix) + 1: 0; p = njs_string_alloc(vm, njs_prop_value(prop), string.size + len + symbol, string.length + len + symbol); if (njs_slow_path(p == NULL)) { return NJS_ERROR; } if (len != 0) { p = njs_cpymem(p, prefix, len - 1); *p++ = ' '; } if (symbol != 0) { *p++ = '['; } p = njs_cpymem(p, string.start, string.size); if (symbol != 0) { *p++ = ']'; } } else { njs_value_assign(njs_prop_value(prop), &njs_string_empty); } } prop->configurable = 1; lhq.key_hash = NJS_NAME_HASH; lhq.key = njs_str_value("name"); lhq.replace = 0; lhq.value = prop; lhq.proto = &njs_object_hash_proto; lhq.pool = vm->mem_pool; ret = njs_lvlhsh_insert(&function->object.hash, &lhq); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "lvlhsh insert failed"); return NJS_ERROR; } return NJS_OK; } njs_function_t * njs_function_copy(njs_vm_t *vm, njs_function_t *function) { size_t size, n; njs_value_t **from, **to; njs_function_t *copy; njs_object_type_t type; n = (function->native) ? 0 : function->u.lambda->nclosures; size = sizeof(njs_function_t) + n * sizeof(njs_value_t *); copy = njs_mp_alloc(vm->mem_pool, size); if (njs_slow_path(copy == NULL)) { return NULL; } *copy = *function; type = njs_function_object_type(vm, function); copy->object.__proto__ = njs_vm_proto(vm, type); copy->object.shared = 0; if (copy->ctor) { copy->object.shared_hash = vm->shared->function_instance_hash; } else if (type == NJS_OBJ_TYPE_ASYNC_FUNCTION) { copy->object.shared_hash = vm->shared->async_function_instance_hash; } else { copy->object.shared_hash = vm->shared->arrow_instance_hash; } if (n == 0) { return copy; } from = njs_function_closures(function); to = njs_function_closures(copy); do { n--; to[n] = from[n]; } while (n != 0); return copy; } njs_int_t njs_function_arguments_object_init(njs_vm_t *vm, njs_native_frame_t *frame) { njs_int_t ret; njs_uint_t n; njs_value_t value, length; njs_object_t *arguments; static const njs_value_t string_length = njs_string("length"); arguments = njs_object_alloc(vm); if (njs_slow_path(arguments == NULL)) { return NJS_ERROR; } arguments->shared_hash = vm->shared->arguments_object_instance_hash; njs_set_object(&value, arguments); njs_set_number(&length, frame->nargs); ret = njs_object_prop_define(vm, &value, njs_value_arg(&string_length), &length, NJS_OBJECT_PROP_VALUE_CW, NJS_LENGTH_HASH); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } for (n = 0; n < frame->nargs; n++) { ret = njs_value_create_data_prop_i64(vm, &value, n, &frame->arguments[n], 0); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } frame->arguments_object = arguments; return NJS_OK; } njs_int_t njs_function_rest_parameters_init(njs_vm_t *vm, njs_native_frame_t *frame) { uint32_t length; njs_uint_t nargs, n, i; njs_array_t *array; njs_value_t *rest_arguments; nargs = frame->nargs; n = frame->function->u.lambda->nargs; length = (nargs >= n) ? (nargs - n + 1) : 0; array = njs_array_alloc(vm, 1, length, 0); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } for (i = 0; i < length; i++) { array->start[i] = frame->arguments[i + n - 1]; } rest_arguments = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t)); if (njs_slow_path(rest_arguments == NULL)) { return NJS_ERROR; } njs_set_array(rest_arguments, array); vm->top_frame->local[n] = rest_arguments; return NJS_OK; } static njs_int_t njs_function_prototype_thrower(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_type_error(vm, "\"caller\", \"callee\", \"arguments\" " "properties may not be accessed"); return NJS_ERROR; } const njs_object_prop_t njs_arguments_object_instance_properties[] = { { .type = NJS_ACCESSOR, .name = njs_string("callee"), .u.accessor = njs_accessor(njs_function_prototype_thrower, 0, njs_function_prototype_thrower, 0), .writable = NJS_ATTRIBUTE_UNSET, }, }; const njs_object_init_t njs_arguments_object_instance_init = { njs_arguments_object_instance_properties, njs_nitems(njs_arguments_object_instance_properties), }; njs_int_t njs_function_native_frame(njs_vm_t *vm, njs_function_t *function, const njs_value_t *this, const njs_value_t *args, njs_uint_t nargs, njs_bool_t ctor) { size_t size; njs_value_t *value; njs_native_frame_t *frame; size = NJS_NATIVE_FRAME_SIZE + (1 /* this */ + nargs) * sizeof(njs_value_t); frame = njs_function_frame_alloc(vm, size); if (njs_slow_path(frame == NULL)) { return NJS_ERROR; } frame->function = function; frame->nargs = nargs; frame->ctor = ctor; frame->native = 1; frame->pc = NULL; value = (njs_value_t *) ((u_char *) frame + NJS_NATIVE_FRAME_SIZE); njs_value_assign(value++, this++); frame->arguments = value; if (args != NULL) { memcpy(value, args, nargs * sizeof(njs_value_t)); } return NJS_OK; } njs_int_t njs_function_lambda_frame(njs_vm_t *vm, njs_function_t *function, const njs_value_t *this, const njs_value_t *args, njs_uint_t nargs, njs_bool_t ctor) { size_t n, frame_size; uint32_t args_count, value_count, value_size; njs_value_t *value, **new; njs_frame_t *frame; njs_native_frame_t *native_frame; njs_function_lambda_t *lambda; lambda = function->u.lambda; args_count = njs_max(nargs, lambda->nargs); value_count = args_count + lambda->nlocal; value_size = value_count * sizeof(njs_value_t *); frame_size = value_size + (value_count * sizeof(njs_value_t)); native_frame = njs_function_frame_alloc(vm, NJS_FRAME_SIZE + frame_size); if (njs_slow_path(native_frame == NULL)) { return NJS_ERROR; } /* Local */ new = (njs_value_t **) ((u_char *) native_frame + NJS_FRAME_SIZE); value = (njs_value_t *) ((u_char *) new + value_size); n = value_count; while (n != 0) { n--; new[n] = &value[n]; njs_set_invalid(new[n]); } native_frame->arguments = value; native_frame->local = new + args_count; native_frame->function = function; native_frame->nargs = nargs; native_frame->ctor = ctor; native_frame->native = 0; native_frame->pc = NULL; /* Set this and bound arguments. */ *native_frame->local[0] = *this; if (njs_slow_path(function->global_this && njs_is_null_or_undefined(this))) { njs_value_assign(native_frame->local[0], &vm->global_value); } /* Copy arguments. */ if (args != NULL) { while (nargs != 0) { njs_value_assign(value++, args++); nargs--; } } frame = (njs_frame_t *) native_frame; frame->exception.catch = NULL; frame->exception.next = NULL; frame->previous_active_frame = vm->active_frame; return NJS_OK; } njs_native_frame_t * njs_function_frame_alloc(njs_vm_t *vm, size_t size) { size_t spare_size, chunk_size; njs_native_frame_t *frame; spare_size = vm->top_frame ? vm->top_frame->free_size : 0; if (njs_fast_path(size <= spare_size)) { frame = (njs_native_frame_t *) vm->top_frame->free; chunk_size = 0; } else { spare_size = size + NJS_FRAME_SPARE_SIZE; spare_size = njs_align_size(spare_size, NJS_FRAME_SPARE_SIZE); if (spare_size > vm->spare_stack_size) { njs_range_error(vm, "Maximum call stack size exceeded"); return NULL; } frame = njs_mp_align(vm->mem_pool, sizeof(njs_value_t), spare_size); if (njs_slow_path(frame == NULL)) { njs_memory_error(vm); return NULL; } chunk_size = spare_size; vm->spare_stack_size -= spare_size; } njs_memzero(frame, sizeof(njs_native_frame_t)); frame->size = chunk_size; frame->free_size = spare_size - size; frame->free = (u_char *) frame + size; frame->previous = vm->top_frame; vm->top_frame = frame; return frame; } njs_int_t njs_function_call2(njs_vm_t *vm, njs_function_t *function, const njs_value_t *this, const njs_value_t *args, njs_uint_t nargs, njs_value_t *retval, njs_bool_t ctor) { njs_int_t ret; ret = njs_function_frame(vm, function, this, args, nargs, ctor); if (njs_slow_path(ret != NJS_OK)) { return ret; } return njs_function_frame_invoke(vm, retval); } njs_int_t njs_function_lambda_call(njs_vm_t *vm, njs_value_t *retval, void *promise_cap) { uint32_t n; njs_int_t ret; njs_frame_t *frame; njs_value_t *args, **local, *value; njs_value_t **cur_local, **cur_closures; njs_function_t *function; njs_declaration_t *declr; njs_function_lambda_t *lambda; frame = (njs_frame_t *) vm->top_frame; function = frame->native.function; njs_assert(function->context == NULL); if (function->global && !function->closure_copied) { ret = njs_function_capture_global_closures(vm, function); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } lambda = function->u.lambda; args = vm->top_frame->arguments; local = vm->top_frame->local + 1 /* this */; /* Move all arguments. */ for (n = 0; n < function->args_count; n++) { if (!njs_is_valid(args)) { njs_set_undefined(args); } *local++ = args++; } /* Store current level. */ cur_local = vm->levels[NJS_LEVEL_LOCAL]; cur_closures = vm->levels[NJS_LEVEL_CLOSURE]; /* Replace current level. */ vm->levels[NJS_LEVEL_LOCAL] = vm->top_frame->local; vm->levels[NJS_LEVEL_CLOSURE] = njs_function_closures(function); if (lambda->rest_parameters) { ret = njs_function_rest_parameters_init(vm, &frame->native); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } /* Self */ if (lambda->self != NJS_INDEX_NONE) { value = njs_scope_value(vm, lambda->self); if (!njs_is_valid(value)) { njs_set_function(value, function); } } vm->active_frame = frame; /* Closures */ n = lambda->ndeclarations; while (n != 0) { n--; declr = &lambda->declarations[n]; value = njs_scope_value(vm, declr->index); *value = *declr->value; function = njs_function_value_copy(vm, value); if (njs_slow_path(function == NULL)) { return NJS_ERROR; } ret = njs_function_capture_closure(vm, function, function->u.lambda); if (njs_slow_path(ret != NJS_OK)) { return ret; } } ret = njs_vmcode_interpreter(vm, lambda->start, retval, promise_cap, NULL); /* Restore current level. */ vm->levels[NJS_LEVEL_LOCAL] = cur_local; vm->levels[NJS_LEVEL_CLOSURE] = cur_closures; return ret; } njs_int_t njs_function_native_call(njs_vm_t *vm, njs_value_t *retval) { njs_int_t ret; njs_function_t *function; njs_native_frame_t *native; njs_function_native_t call; native = vm->top_frame; function = native->function; #ifdef NJS_DEBUG_OPCODE njs_str_t name; if (vm->options.opcode_debug) { ret = njs_builtin_match_native_function(vm, function, &name); if (ret != NJS_OK) { name = njs_str_value("unmapped"); } njs_printf("CALL NATIVE %V %P\n", &name, function->u.native); } #endif call = function->u.native; ret = call(vm, &native->arguments[-1], 1 /* this */ + native->nargs, function->magic8, retval); #ifdef NJS_DEBUG_OPCODE if (vm->options.opcode_debug) { njs_printf("CALL NATIVE RETCODE: %i %V %P\n", ret, &name, function->u.native); } #endif if (njs_slow_path(ret == NJS_ERROR)) { return ret; } njs_vm_scopes_restore(vm, native); njs_function_frame_free(vm, native); return NJS_OK; } njs_int_t njs_function_frame_invoke(njs_vm_t *vm, njs_value_t *retval) { njs_native_frame_t *frame; frame = vm->top_frame; if (njs_function_object_type(vm, frame->function) == NJS_OBJ_TYPE_ASYNC_FUNCTION) { return njs_async_function_frame_invoke(vm, retval); } if (frame->native) { return njs_function_native_call(vm, retval); } else { return njs_function_lambda_call(vm, retval, NULL); } } void njs_function_frame_free(njs_vm_t *vm, njs_native_frame_t *native) { if (native->size != 0) { vm->spare_stack_size += native->size; njs_mp_free(vm->mem_pool, native); } } njs_int_t njs_function_frame_save(njs_vm_t *vm, njs_frame_t *frame, u_char *pc) { size_t args_count, value_count, n; njs_value_t *start, *end, *p, **new, *value, **local; njs_function_t *function; njs_function_lambda_t *lambda; njs_native_frame_t *active, *native; *frame = *vm->active_frame; frame->previous_active_frame = NULL; native = &frame->native; native->size = 0; native->free = NULL; native->free_size = 0; active = &vm->active_frame->native; function = active->function; lambda = function->u.lambda; args_count = njs_max(native->nargs, lambda->nargs); value_count = args_count + lambda->nlocal; new = (njs_value_t **) ((u_char *) native + NJS_FRAME_SIZE); value = (njs_value_t *) (new + value_count); native->arguments = value; native->local = new + njs_function_frame_args_count(active); native->pc = pc; start = njs_function_frame_values(active, &end); p = native->arguments; while (start < end) { njs_value_assign(p, start++); *new++ = p++; } /* Move all arguments. */ p = native->arguments; local = native->local + 1 /* this */; for (n = 0; n < function->args_count; n++) { if (!njs_is_valid(p)) { njs_set_undefined(p); } *local++ = p++; } return NJS_OK; } njs_object_type_t njs_function_object_type(njs_vm_t *vm, njs_function_t *function) { if (function->object.shared_hash.slot == vm->shared->async_function_instance_hash.slot) { return NJS_OBJ_TYPE_ASYNC_FUNCTION; } return NJS_OBJ_TYPE_FUNCTION; } njs_int_t njs_function_capture_closure(njs_vm_t *vm, njs_function_t *function, njs_function_lambda_t *lambda) { void *start, *end; uint32_t n; njs_value_t *value, **closure; njs_native_frame_t *frame; if (lambda->nclosures == 0) { return NJS_OK; } frame = &vm->active_frame->native; while (frame->native) { frame = frame->previous; } start = frame; end = frame->free; closure = njs_function_closures(function); n = lambda->nclosures; do { n--; value = njs_scope_value(vm, lambda->closures[n]); if (start <= (void *) value && (void *) value < end) { value = njs_scope_value_clone(vm, lambda->closures[n], value); if (njs_slow_path(value == NULL)) { return NJS_ERROR; } } closure[n] = value; } while (n != 0); return NJS_OK; } njs_inline njs_value_t * njs_function_closure_value(njs_vm_t *vm, njs_value_t **scope, njs_index_t index, void *start, void *end) { njs_value_t *value, *newval; value = scope[njs_scope_index_value(index)]; if (start <= (void *) value && end > (void *) value) { newval = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t)); if (njs_slow_path(newval == NULL)) { njs_memory_error(vm); return NULL; } *newval = *value; value = newval; } scope[njs_scope_index_value(index)] = value; return value; } njs_int_t njs_function_capture_global_closures(njs_vm_t *vm, njs_function_t *function) { void *start, *end; uint32_t n; njs_value_t *value, **refs, **global; njs_index_t *indexes, index; njs_native_frame_t *native; njs_function_lambda_t *lambda; lambda = function->u.lambda; if (lambda->nclosures == 0) { return NJS_OK; } native = vm->top_frame; while (native->previous->function != NULL) { native = native->previous; } start = native; end = native->free; indexes = lambda->closures; refs = njs_function_closures(function); global = vm->levels[NJS_LEVEL_GLOBAL]; n = lambda->nclosures; while (n > 0) { n--; index = indexes[n]; switch (njs_scope_index_type(index)) { case NJS_LEVEL_LOCAL: value = njs_function_closure_value(vm, native->local, index, start, end); break; case NJS_LEVEL_GLOBAL: value = njs_function_closure_value(vm, global, index, start, end); break; default: njs_type_error(vm, "unexpected value type for closure \"%uD\"", njs_scope_index_type(index)); return NJS_ERROR; } if (njs_slow_path(value == NULL)) { return NJS_ERROR; } refs[n] = value; } function->closure_copied = 1; return NJS_OK; } static njs_value_t * njs_function_property_prototype_set(njs_vm_t *vm, njs_lvlhsh_t *hash, njs_value_t *prototype) { njs_int_t ret; njs_object_prop_t *prop; njs_lvlhsh_query_t lhq; const njs_value_t proto_string = njs_string("prototype"); prop = njs_object_prop_alloc(vm, &proto_string, prototype, 0); if (njs_slow_path(prop == NULL)) { return NULL; } prop->writable = 1; lhq.value = prop; lhq.key_hash = NJS_PROTOTYPE_HASH; lhq.key = njs_str_value("prototype"); lhq.replace = 1; lhq.pool = vm->mem_pool; lhq.proto = &njs_object_hash_proto; ret = njs_lvlhsh_insert(hash, &lhq); if (njs_fast_path(ret == NJS_OK)) { return njs_prop_value(prop); } njs_internal_error(vm, "lvlhsh insert failed"); return NULL; } /* * The "prototype" property of user defined functions is created on * demand in private hash of the functions by the "prototype" getter. * The getter creates a copy of function which is private to nJSVM, * adds a "prototype" object property to the copy, and then adds a * "constructor" property in the prototype object. The "constructor" * property points to the copy of function: * "F.prototype.constructor === F" */ njs_int_t njs_function_prototype_create(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { njs_value_t *proto, proto_value, *cons; njs_object_t *prototype; njs_function_t *function; if (setval == NULL) { prototype = njs_object_alloc(vm); if (njs_slow_path(prototype == NULL)) { return NJS_ERROR; } njs_set_object(&proto_value, prototype); setval = &proto_value; } function = njs_function_value_copy(vm, value); if (njs_slow_path(function == NULL)) { return NJS_ERROR; } proto = njs_function_property_prototype_set(vm, njs_object_hash(value), setval); if (njs_slow_path(proto == NULL)) { return NJS_ERROR; } if (setval == &proto_value && njs_is_object(proto)) { /* Only in getter context. */ cons = njs_property_constructor_set(vm, njs_object_hash(proto), value); if (njs_slow_path(cons == NULL)) { return NJS_ERROR; } } *retval = *proto; return NJS_OK; } njs_int_t njs_function_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t async, njs_value_t *retval) { njs_chb_t chain; njs_int_t ret; njs_str_t str, file; njs_uint_t i; njs_parser_t parser; njs_vm_code_t *code; njs_function_t *function; njs_generator_t generator; njs_parser_node_t *node; njs_function_lambda_t *lambda; const njs_token_type_t *type; static const njs_token_type_t safe_ast[] = { NJS_TOKEN_END, NJS_TOKEN_FUNCTION_EXPRESSION, NJS_TOKEN_STATEMENT, NJS_TOKEN_RETURN, NJS_TOKEN_THIS, NJS_TOKEN_ILLEGAL }; static const njs_token_type_t safe_ast_async[] = { NJS_TOKEN_END, NJS_TOKEN_ASYNC_FUNCTION_EXPRESSION, NJS_TOKEN_STATEMENT, NJS_TOKEN_RETURN, NJS_TOKEN_THIS, NJS_TOKEN_ILLEGAL }; if (!vm->options.unsafe && nargs != 2) { goto fail; } NJS_CHB_MP_INIT(&chain, vm); if (async) { njs_chb_append_literal(&chain, "(async function("); } else { njs_chb_append_literal(&chain, "(function("); } for (i = 1; i < nargs - 1; i++) { ret = njs_value_to_chain(vm, &chain, njs_argument(args, i)); if (njs_slow_path(ret < NJS_OK)) { return ret; } if (i != (nargs - 2)) { njs_chb_append_literal(&chain, ","); } } njs_chb_append_literal(&chain, "){"); if (nargs > 1) { ret = njs_value_to_chain(vm, &chain, njs_argument(args, nargs - 1)); if (njs_slow_path(ret < NJS_OK)) { return ret; } } njs_chb_append_literal(&chain, "})"); ret = njs_chb_join(&chain, &str); if (njs_slow_path(ret != NJS_OK)) { njs_memory_error(vm); return NJS_ERROR; } file = njs_str_value("runtime"); ret = njs_parser_init(vm, &parser, NULL, &file, str.start, str.start + str.length, 1); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_parser(vm, &parser); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (!vm->options.unsafe) { /* * Safe mode exception: * "(new Function('return this'))" is often used to get * the global object in a portable way. */ node = parser.node; type = (async) ? &safe_ast_async[0] : &safe_ast[0]; for (; *type != NJS_TOKEN_ILLEGAL; type++, node = node->right) { if (node == NULL) { goto fail; } if (node->left != NULL && node->token_type != NJS_TOKEN_FUNCTION_EXPRESSION && node->left->token_type != NJS_TOKEN_NAME) { goto fail; } if (node->token_type != *type) { goto fail; } } } ret = njs_generator_init(&generator, &file, 0, 1); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "njs_generator_init() failed"); return NJS_ERROR; } code = njs_generate_scope(vm, &generator, parser.scope, &njs_entry_anonymous); if (njs_slow_path(code == NULL)) { if (!njs_is_error(retval)) { njs_internal_error(vm, "njs_generate_scope() failed"); } return NJS_ERROR; } njs_chb_destroy(&chain); lambda = ((njs_vmcode_function_t *) generator.code_start)->lambda; function = njs_function_alloc(vm, lambda, (njs_bool_t) async); if (njs_slow_path(function == NULL)) { return NJS_ERROR; } function->global = 1; function->global_this = 1; function->args_count = lambda->nargs - lambda->rest_parameters; ret = njs_function_name_set(vm, function, njs_value_arg(&njs_string_anonymous), NULL); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } njs_set_function(retval, function); return NJS_OK; fail: njs_type_error(vm, "function constructor is disabled in \"safe\" mode"); return NJS_ERROR; } static const njs_object_prop_t njs_function_constructor_properties[] = { NJS_DECLARE_PROP_LENGTH(1), NJS_DECLARE_PROP_NAME("Function"), NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), }; const njs_object_init_t njs_function_constructor_init = { njs_function_constructor_properties, njs_nitems(njs_function_constructor_properties), }; njs_int_t njs_function_instance_length(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { njs_function_t *function; function = njs_object_proto_lookup(njs_object(value), NJS_FUNCTION, njs_function_t); if (njs_slow_path(function == NULL)) { njs_set_undefined(retval); return NJS_DECLINED; } njs_set_number(retval, function->args_count); return NJS_OK; } njs_int_t njs_function_instance_name(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { njs_function_t *function; function = njs_object_proto_lookup(njs_object(value), NJS_FUNCTION, njs_function_t); if (njs_slow_path(function == NULL)) { njs_set_undefined(retval); return NJS_DECLINED; } if (!function->native) { njs_value_assign(retval, &function->u.lambda->name); return NJS_OK; } njs_value_assign(retval, &njs_string_empty); return NJS_OK; } static njs_int_t njs_function_prototype_call(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { const njs_value_t *this; if (!njs_is_function(&args[0])) { njs_type_error(vm, "\"this\" argument is not a function"); return NJS_ERROR; } if (nargs > 1) { this = &args[1]; nargs -= 2; } else { this = (njs_value_t *) &njs_value_undefined; nargs = 0; } return njs_function_call(vm, njs_function(&args[0]), this, &args[2], nargs, retval); } static njs_int_t njs_function_prototype_apply(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t i, length; njs_int_t ret; njs_value_t *this, *arr_like; njs_array_t *arr; njs_function_t *func; if (!njs_is_function(njs_argument(args, 0))) { njs_type_error(vm, "\"this\" argument is not a function"); return NJS_ERROR; } func = njs_function(njs_argument(args, 0)); this = njs_arg(args, nargs, 1); arr_like = njs_arg(args, nargs, 2); if (njs_is_null_or_undefined(arr_like)) { length = 0; goto activate; } if (njs_slow_path(!njs_is_object(arr_like))) { njs_type_error(vm, "second argument is not an array-like object"); return NJS_ERROR; } ret = njs_object_length(vm, arr_like, &length); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_slow_path(length > 1024)) { njs_internal_error(vm, "argument list is too long"); return NJS_ERROR; } arr = njs_array_alloc(vm, 1, length, NJS_ARRAY_SPARE); if (njs_slow_path(arr == NULL)) { return NJS_ERROR; } args = arr->start; for (i = 0; i < length; i++) { ret = njs_value_property_i64(vm, arr_like, i, &args[i]); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } } activate: return njs_function_call(vm, func, this, args, length, retval); } static njs_int_t njs_function_bound_call(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { u_char *p; njs_int_t ret; size_t args_count; njs_value_t *arguments; njs_function_t *function, *bound; function = vm->top_frame->function; bound = function->context; njs_assert(bound != NULL); args_count = 1 /* this */ + function->bound_args; if (nargs == 1) { return njs_function_apply(vm, bound, function->bound, args_count, retval); } arguments = njs_mp_alloc(vm->mem_pool, (args_count + nargs - 1) * sizeof(njs_value_t)); if (njs_slow_path(arguments == NULL)) { njs_memory_error(vm); return NJS_ERROR; } p = njs_cpymem(arguments, function->bound, args_count * sizeof(njs_value_t)); memcpy(p, &args[1], (nargs - 1) * sizeof(njs_value_t)); ret = njs_function_apply(vm, bound, arguments, args_count + nargs - 1, retval); njs_mp_free(vm->mem_pool, arguments); return ret; } static njs_int_t njs_function_prototype_bind(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { size_t size; njs_int_t ret; njs_uint_t bound_args; njs_value_t *values, name; njs_function_t *function; if (!njs_is_function(&args[0])) { njs_type_error(vm, "\"this\" argument is not a function"); return NJS_ERROR; } function = njs_mp_alloc(vm->mem_pool, sizeof(njs_function_t)); if (njs_slow_path(function == NULL)) { njs_memory_error(vm); return NJS_ERROR; } *function = *njs_function(&args[0]); function->native = 1; function->u.native = njs_function_bound_call; njs_lvlhsh_init(&function->object.hash); /* Bound functions have no "prototype" property. */ function->object.shared_hash = vm->shared->arrow_instance_hash; function->object.__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_FUNCTION); function->object.shared = 0; function->context = njs_function(&args[0]); ret = njs_value_property(vm, &args[0], njs_value_arg(&njs_string_name), &name); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (!njs_is_string(&name)) { name = njs_string_empty; } ret = njs_function_name_set(vm, function, &name, "bound"); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (nargs == 1) { args = njs_value_arg(&njs_value_undefined); bound_args = 0; } else { args++; bound_args = nargs - 2; } if (bound_args > function->args_count) { function->args_count = 0; } else { function->args_count -= bound_args; } function->bound_args = bound_args; size = (1 /* this */ + bound_args) * sizeof(njs_value_t); values = njs_mp_alloc(vm->mem_pool, size); if (njs_slow_path(values == NULL)) { njs_memory_error(vm); njs_mp_free(vm->mem_pool, function); return NJS_ERROR; } function->bound = values; memcpy(values, args, size); njs_set_function(retval, function); return NJS_OK; } static const njs_object_prop_t njs_function_prototype_properties[] = { NJS_DECLARE_PROP_LENGTH(0), NJS_DECLARE_PROP_NAME(""), NJS_DECLARE_PROP_HANDLER("constructor", njs_object_prototype_create_constructor, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_NATIVE("call", njs_function_prototype_call, 1, 0), NJS_DECLARE_PROP_NATIVE("apply", njs_function_prototype_apply, 2, 0), NJS_DECLARE_PROP_NATIVE("bind", njs_function_prototype_bind, 1, 0), { .type = NJS_ACCESSOR, .name = njs_string("caller"), .u.accessor = njs_accessor(njs_function_prototype_thrower, 0, njs_function_prototype_thrower, 0), .writable = NJS_ATTRIBUTE_UNSET, .configurable = 1, }, { .type = NJS_ACCESSOR, .name = njs_string("arguments"), .u.accessor = njs_accessor(njs_function_prototype_thrower, 0, njs_function_prototype_thrower, 0), .writable = NJS_ATTRIBUTE_UNSET, .configurable = 1, }, }; const njs_object_init_t njs_function_prototype_init = { njs_function_prototype_properties, njs_nitems(njs_function_prototype_properties), }; const njs_object_prop_t njs_function_instance_properties[] = { NJS_DECLARE_PROP_HANDLER("length", njs_function_instance_length, 0, 0, NJS_OBJECT_PROP_VALUE_C), NJS_DECLARE_PROP_HANDLER("name", njs_function_instance_name, 0, 0, NJS_OBJECT_PROP_VALUE_C), NJS_DECLARE_PROP_HANDLER("prototype", njs_function_prototype_create, 0, 0, NJS_OBJECT_PROP_VALUE_W), }; const njs_object_init_t njs_function_instance_init = { njs_function_instance_properties, njs_nitems(njs_function_instance_properties), }; const njs_object_prop_t njs_arrow_instance_properties[] = { NJS_DECLARE_PROP_HANDLER("length", njs_function_instance_length, 0, 0, NJS_OBJECT_PROP_VALUE_C), NJS_DECLARE_PROP_HANDLER("name", njs_function_instance_name, 0, 0, NJS_OBJECT_PROP_VALUE_C), }; const njs_object_init_t njs_arrow_instance_init = { njs_arrow_instance_properties, njs_nitems(njs_arrow_instance_properties), }; njs_int_t njs_eval_function(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_internal_error(vm, "Not implemented"); return NJS_ERROR; } static njs_int_t njs_prototype_function(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_set_undefined(retval); return NJS_OK; } const njs_object_type_init_t njs_function_type_init = { .constructor = njs_native_ctor(njs_function_constructor, 1, 0), .constructor_props = &njs_function_constructor_init, .prototype_props = &njs_function_prototype_init, .prototype_value = { .function = { .native = 1, .u.native = njs_prototype_function, .object = { .type = NJS_FUNCTION } } }, }; njs-0.8.9/src/njs_function.h000066400000000000000000000167051474132077100160000ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_FUNCTION_H_INCLUDED_ #define _NJS_FUNCTION_H_INCLUDED_ struct njs_function_lambda_s { njs_index_t *closures; uint32_t nclosures; uint32_t nlocal; njs_declaration_t *declarations; uint32_t ndeclarations; njs_index_t self; uint32_t nargs; uint8_t ctor; /* 1 bit */ uint8_t rest_parameters; /* 1 bit */ njs_value_t name; u_char *start; }; /* The frame size must be aligned to njs_value_t. */ #define NJS_NATIVE_FRAME_SIZE \ njs_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 \ njs_align_size(sizeof(njs_frame_t), sizeof(njs_value_t)) #define NJS_FRAME_SPARE_SIZE (4 * 1024) struct njs_native_frame_s { u_char *free; u_char *pc; njs_function_t *function; njs_native_frame_t *previous; /* Points to the first arg after 'this'. */ njs_value_t *arguments; njs_object_t *arguments_object; njs_value_t **local; uint32_t size; uint32_t free_size; /* Number of allocated args on the frame. */ uint32_t nargs; /* Number of already put args. */ uint32_t put_args; uint8_t native; /* 1 bit */ /* Function is called as constructor with "new" keyword. */ uint8_t ctor; /* 1 bit */ }; typedef struct njs_exception_s njs_exception_t; struct njs_exception_s { njs_exception_t *next; u_char *catch; }; struct njs_frame_s { njs_native_frame_t native; njs_exception_t exception; njs_frame_t *previous_active_frame; }; njs_function_t *njs_function_alloc(njs_vm_t *vm, njs_function_lambda_t *lambda, njs_bool_t async); njs_function_t *njs_function_value_copy(njs_vm_t *vm, njs_value_t *value); njs_int_t njs_function_name_set(njs_vm_t *vm, njs_function_t *function, njs_value_t *name, const char *prefix); njs_function_t *njs_function_copy(njs_vm_t *vm, njs_function_t *function); njs_int_t njs_function_arguments_object_init(njs_vm_t *vm, njs_native_frame_t *frame); njs_int_t njs_function_rest_parameters_init(njs_vm_t *vm, njs_native_frame_t *frame); njs_int_t njs_function_prototype_create(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); njs_int_t njs_function_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); njs_int_t njs_function_instance_length(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); njs_int_t njs_function_instance_name(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); njs_int_t njs_eval_function(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); njs_int_t njs_function_native_frame(njs_vm_t *vm, njs_function_t *function, const njs_value_t *this, const njs_value_t *args, njs_uint_t nargs, njs_bool_t ctor); njs_int_t njs_function_lambda_frame(njs_vm_t *vm, njs_function_t *function, const njs_value_t *this, const njs_value_t *args, njs_uint_t nargs, njs_bool_t ctor); njs_int_t njs_function_call2(njs_vm_t *vm, njs_function_t *function, const njs_value_t *this, const njs_value_t *args, njs_uint_t nargs, njs_value_t *retval, njs_bool_t ctor); njs_int_t njs_function_lambda_call(njs_vm_t *vm, njs_value_t *retval, void *promise_cap); njs_native_frame_t *njs_function_frame_alloc(njs_vm_t *vm, size_t size); void njs_function_frame_free(njs_vm_t *vm, njs_native_frame_t *frame); njs_int_t njs_function_frame_save(njs_vm_t *vm, njs_frame_t *native, u_char *pc); njs_object_type_t njs_function_object_type(njs_vm_t *vm, njs_function_t *function); njs_int_t njs_function_capture_closure(njs_vm_t *vm, njs_function_t *function, njs_function_lambda_t *lambda); njs_int_t njs_function_capture_global_closures(njs_vm_t *vm, njs_function_t *function); njs_int_t njs_function_frame_invoke(njs_vm_t *vm, njs_value_t *retval); njs_inline njs_function_lambda_t * njs_function_lambda_alloc(njs_vm_t *vm, uint8_t ctor) { njs_function_lambda_t *lambda; lambda = njs_mp_zalloc(vm->mem_pool, sizeof(njs_function_lambda_t)); if (njs_fast_path(lambda != NULL)) { lambda->ctor = ctor; } return lambda; } njs_inline njs_int_t njs_function_frame(njs_vm_t *vm, njs_function_t *function, const njs_value_t *this, const njs_value_t *args, njs_uint_t nargs, njs_bool_t ctor) { if (function->native) { return njs_function_native_frame(vm, function, this, args, nargs, ctor); } else { return njs_function_lambda_frame(vm, function, this, args, nargs, ctor); } } njs_inline njs_int_t njs_function_call(njs_vm_t *vm, njs_function_t *function, const njs_value_t *this, const njs_value_t *args, njs_uint_t nargs, njs_value_t *retval) { return njs_function_call2(vm, function, this, args, nargs, retval, 0); } njs_inline njs_int_t njs_function_apply(njs_vm_t *vm, njs_function_t *function, const njs_value_t *args, njs_uint_t nargs, njs_value_t *retval) { return njs_function_call2(vm, function, &args[0], &args[1], nargs - 1, retval, 0); } njs_inline njs_bool_t njs_native_function_same(const njs_function_t *f1, const njs_function_t *f2) { return f1->u.native == f2->u.native && f1->magic8 == f2->magic8; } njs_inline njs_value_t ** njs_function_closures(const njs_function_t *func) { return (njs_value_t **) ((u_char *) func + sizeof(njs_function_t)); } njs_inline size_t njs_function_frame_size(njs_native_frame_t *frame) { size_t size; uintptr_t start; start = (uintptr_t) ((u_char *) frame + NJS_FRAME_SIZE); size = ((uintptr_t) frame->arguments - start) / sizeof(njs_value_t *); return NJS_FRAME_SIZE + (size * sizeof(njs_value_t *)) + (size * sizeof(njs_value_t)); } njs_inline size_t njs_function_frame_args_count(njs_native_frame_t *frame) { uintptr_t start; start = (uintptr_t) ((u_char *) frame + NJS_FRAME_SIZE); return ((uintptr_t) frame->local - start) / sizeof(njs_value_t *); } njs_inline njs_value_t * njs_function_frame_values(njs_native_frame_t *frame, njs_value_t **end) { size_t count; uintptr_t start; start = (uintptr_t) ((u_char *) frame + NJS_FRAME_SIZE); count = ((uintptr_t) frame->arguments - start) / sizeof(njs_value_t *); *end = frame->arguments + count; return frame->arguments; } extern const njs_object_type_init_t njs_function_type_init; extern const njs_object_init_t njs_function_instance_init; extern const njs_object_init_t njs_arrow_instance_init; extern const njs_object_init_t njs_arguments_object_instance_init; #endif /* _NJS_FUNCTION_H_INCLUDED_ */ njs-0.8.9/src/njs_generator.c000066400000000000000000005266261474132077100161440ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) Dmitry Volyntsev * Copyright (C) Alexander Borisov * Copyright (C) NGINX, Inc. */ #include #define NJS_FUNCTION_MAX_DEPTH 128 typedef struct njs_generator_patch_s njs_generator_patch_t; typedef enum { NJS_GENERATOR_LOOP = 1, NJS_GENERATOR_SWITCH = 2, NJS_GENERATOR_BLOCK = 4, NJS_GENERATOR_TRY = 8, #define NJS_GENERATOR_ALL (NJS_GENERATOR_LOOP | NJS_GENERATOR_SWITCH) } njs_generator_block_type_t; typedef enum { NJS_GENERATOR_CONTINUATION = 1, NJS_GENERATOR_EXIT = 2, } njs_generator_patch_type_t; struct njs_generator_patch_s { /* * The jump_offset field points to jump offset field which contains a small * adjustment and the adjustment should be added as (njs_jump_off_t *) * because pointer to u_char accesses only one byte so this does not * work on big endian platforms. */ njs_jump_off_t jump_offset; njs_generator_patch_t *next; njs_str_t label; }; struct njs_generator_block_s { njs_generator_block_type_t type; /* 4 bits */ njs_str_t label; /* list of "continue" instruction offsets to be patched. */ njs_generator_patch_t *continuation; /* * list of "return" from try-catch block and "break" * instruction offsets to be patched. */ njs_generator_patch_t *exit; njs_generator_block_t *next; /* exit value index, used only for NJS_GENERATOR_TRY blocks. */ njs_index_t index; }; typedef struct { njs_generator_state_func_t state; njs_queue_link_t link; njs_parser_node_t *node; void *context; } njs_generator_stack_entry_t; typedef struct { njs_generator_patch_t *patch; njs_generator_patch_t **last; njs_vmcode_jump_t *jump; njs_jump_off_t jump_offset; njs_index_t index; } njs_generator_switch_ctx_t; typedef struct { njs_jump_off_t jump_offset; njs_jump_off_t loop_offset; njs_vmcode_jump_t *jump; njs_variable_t *var; njs_index_t index; njs_index_t index_next_value; } njs_generator_loop_ctx_t; typedef struct { njs_index_t exception_index; njs_jump_off_t try_offset; njs_jump_off_t catch_offset; njs_generator_block_t *try_block; njs_generator_block_t *catch_block; njs_str_t try_cont_label; njs_str_t try_exit_label; njs_str_t catch_cont_label; njs_str_t catch_exit_label; } njs_generator_try_ctx_t; static u_char *njs_generate_reserve(njs_vm_t *vm, njs_generator_t *generator, size_t size); static njs_int_t njs_generate_code_map(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node, u_char *code); static njs_int_t njs_generate_name(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_variable(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node, njs_reference_type_t type, njs_variable_t **retvar); static njs_int_t njs_generate_var_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_var_statement_after(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_let(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node, njs_variable_t *var); static njs_int_t njs_generate_if_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_if_statement_cond(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_if_statement_then(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_if_statement_else(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_cond_expression(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_cond_expression_handler(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_cond_expression_true(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_cond_expression_false(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_switch_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_switch_expression(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *swtch); static njs_int_t njs_generate_switch_case(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *swtch); static njs_int_t njs_generate_switch_case_after(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *branch); static njs_int_t njs_generate_switch_case_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *branch); static njs_int_t njs_generate_switch_default(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *branch); static njs_int_t njs_generate_switch_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *swtch); static njs_int_t njs_generate_while_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_while_condition(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_while_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_do_while_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_do_while_condition(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_do_while_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_for_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_for_init(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_for_body(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_for_update(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_for_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_for_let_update(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_for_resolve_closure(njs_vm_t *vm, njs_parser_node_t *node); static njs_int_t njs_generate_for_in_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_for_in_set_prop_block(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_for_in_name_assign(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_for_in_object(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_for_in_object_wo_decl(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_for_in_object_left_hand_expr(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_for_in_body(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_for_in_body_wo_decl(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_for_in_body_left_hand_expr(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_start_block(njs_vm_t *vm, njs_generator_t *generator, njs_generator_block_type_t type, const njs_str_t *label); static njs_generator_block_t *njs_generate_lookup_block( njs_generator_block_t *block, uint32_t mask, const njs_str_t *label); static njs_generator_block_t *njs_generate_find_block(njs_vm_t *vm, njs_generator_block_t *block, uint32_t mask, const njs_str_t *label); static void njs_generate_patch_block(njs_vm_t *vm, njs_generator_t *generator, njs_generator_block_t *block, unsigned type); static njs_generator_patch_t *njs_generate_make_continuation_patch(njs_vm_t *vm, njs_generator_block_t *block, const njs_str_t *label, njs_jump_off_t offset); static njs_generator_patch_t *njs_generate_make_exit_patch(njs_vm_t *vm, njs_generator_block_t *block, const njs_str_t *label, njs_jump_off_t offset); static void njs_generate_patch_block_exit(njs_vm_t *vm, njs_generator_t *generator); static const njs_str_t *njs_generate_jump_destination(njs_vm_t *vm, njs_generator_block_t *block, const char *inst_type, uint32_t mask, const njs_str_t *label1, const njs_str_t *label2); static njs_int_t njs_generate_continue_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_break_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_debugger_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_block_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_block_statement_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_children(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_stop_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_stop_statement_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_comma_expression(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_comma_expression_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_global_property_set(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node_dst, njs_parser_node_t *node_src); static njs_int_t njs_generate_assignment(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_assignment_name(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_assignment_prop(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_assignment_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_operation_assignment(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_operation_assignment_name(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_operation_assignment_prop(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_operation_assignment_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_object(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_property_accessor(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_property_accessor_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_array(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_function_expression(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_function(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_regexp(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_template_literal(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_template_literal_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_test_jump_expression(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_test_jump_expression_after(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_test_jump_expression_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_3addr_operation(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node, njs_bool_t swap); static njs_int_t njs_generate_3addr_operation_name(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_3addr_operation_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_2addr_operation(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_2addr_operation_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_typeof_operation(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_typeof_operation_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_inc_dec_operation(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node, njs_bool_t post); static njs_int_t njs_generate_inc_dec_operation_prop(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_function_declaration(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_function_scope(njs_vm_t *vm, njs_generator_t *generator, njs_function_lambda_t *lambda, njs_parser_node_t *node, const njs_str_t *name); static njs_int_t njs_generate_scope_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static int64_t njs_generate_lambda_variables(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_return_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_return_statement_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_function_call(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_function_call_arguments(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_function_call_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_method_call(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_method_call_arguments(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_method_call_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_call(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_move_arguments(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_try_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_try_left(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_try_catch(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_try_finally(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_try_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_throw_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_throw_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_import_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_export_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_export_statement_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_await(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_await_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_wo_dest(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_wo_dest_after(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_global_reference(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node, njs_bool_t exception); static njs_int_t njs_generate_reference_error(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_index_t njs_generate_dest_index(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_index_t njs_generate_object_dest_index(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_index_t njs_generate_node_temp_index_get(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_index_t njs_generate_temp_index_get(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_children_indexes_release(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_node_index_release(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_node_index_release_pop(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); static njs_int_t njs_generate_index_release(njs_vm_t *vm, njs_generator_t *generator, njs_index_t index); #define njs_generate_code(generator, type, _code, _op, nd) \ do { \ _code = (type *) njs_generate_reserve(vm, generator, sizeof(type)); \ if (njs_slow_path(_code == NULL)) { \ return NJS_ERROR; \ } \ \ if (njs_generate_code_map(vm, generator, nd, (u_char *) _code) \ != NJS_OK) \ { \ return NJS_ERROR; \ } \ \ generator->code_end += sizeof(type); \ \ _code->code = _op; \ } while (0) #define njs_generate_code_jump(generator, _code, _offset) \ do { \ njs_generate_code(generator, njs_vmcode_jump_t, _code, \ NJS_VMCODE_JUMP, NULL); \ _code->offset = _offset; \ } while (0) #define njs_generate_code_move(generator, _code, _dst, _src, node) \ do { \ njs_generate_code(generator, njs_vmcode_move_t, _code, \ NJS_VMCODE_MOVE, node); \ _code->dst = _dst; \ _code->src = _src; \ } while (0) #define njs_code_offset(generator, code) \ ((u_char *) code - generator->code_start) #define njs_code_ptr(generator, type, offset) \ (type *) (generator->code_start + offset) #define njs_code_jump_ptr(generator, offset) \ (njs_jump_off_t *) (generator->code_start + offset) #define njs_code_offset_diff(generator, offset) \ ((generator->code_end - generator->code_start) - offset) #define njs_code_set_offset(generator, offset, target) \ *(njs_code_jump_ptr(generator, offset)) \ = njs_code_offset_diff(generator, target) #define njs_code_set_jump_offset(generator, type, code_offset) \ *(njs_code_jump_ptr(generator, code_offset + offsetof(type, offset))) \ = njs_code_offset_diff(generator, code_offset) #define njs_code_update_offset(generator, patch) \ *(njs_code_jump_ptr(generator, patch->jump_offset)) += \ njs_code_offset_diff(generator, patch->jump_offset) #define njs_generate_syntax_error(vm, node, file, fmt, ...) \ njs_parser_node_error(vm, NJS_OBJ_TYPE_SYNTAX_ERROR, node, file, fmt, \ ##__VA_ARGS__) #ifdef NJS_DEBUG_GENERATOR #define njs_debug_generator(vm, msg, ...) \ if (vm->options.generator_debug) \ njs_printf("GENERATOR " msg "\n", ##__VA_ARGS__) #define njs_debug_generator_code(code) \ if (vm->options.generator_debug) \ njs_disassemble((u_char *) code, NULL, 1, NULL) #else #define njs_debug_generator(vm, msg, ...) #define njs_debug_generator_code(code) #endif static const njs_str_t no_label = njs_str(""); static const njs_str_t return_label = njs_str("@return"); /* GCC and Clang complain about NULL argument passed to memcmp(). */ static const njs_str_t undef_label = { 0xffffffff, (u_char *) "" }; njs_int_t njs_generator_init(njs_generator_t *generator, njs_str_t *file, njs_int_t depth, njs_bool_t runtime) { njs_memzero(generator, sizeof(njs_generator_t)); njs_queue_init(&generator->stack); generator->file = *file; generator->depth = depth; generator->runtime = runtime; return NJS_OK; } njs_inline void njs_generator_next(njs_generator_t *generator, njs_generator_state_func_t state, njs_parser_node_t *node) { generator->state = state; generator->node = node; } njs_inline njs_int_t njs_generator_after(njs_vm_t *vm, njs_generator_t *generator, njs_queue_link_t *link, njs_parser_node_t *node, njs_generator_state_func_t state, void *ctx, size_t size) { njs_generator_stack_entry_t *entry; entry = njs_mp_alloc(vm->mem_pool, sizeof(njs_generator_stack_entry_t)); if (njs_slow_path(entry == NULL)) { return NJS_ERROR; } entry->state = state; entry->node = node; entry->context = ctx; njs_queue_insert_before(link, &entry->link); if (size > 0) { entry->context = njs_mp_alloc(vm->mem_pool, size); if (njs_slow_path(entry->context == NULL)) { return NJS_ERROR; } memcpy(entry->context, ctx, size); } return NJS_OK; } njs_inline njs_int_t njs_generator_stack_pop(njs_vm_t *vm, njs_generator_t *generator, void *ctx) { njs_queue_link_t *link; njs_generator_stack_entry_t *entry; entry = njs_queue_link_data(njs_queue_first(&generator->stack), njs_generator_stack_entry_t, link); link = njs_queue_first(&generator->stack); njs_queue_remove(link); if (ctx != NULL) { njs_mp_free(vm->mem_pool, ctx); } generator->context = entry->context; njs_generator_next(generator, entry->state, entry->node); njs_mp_free(vm->mem_pool, entry); return NJS_OK; } static njs_int_t njs_generate(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { if (node == NULL) { return njs_generator_stack_pop(vm, generator, NULL); } switch (node->token_type) { case NJS_TOKEN_VAR: case NJS_TOKEN_LET: case NJS_TOKEN_CONST: return njs_generate_var_statement(vm, generator, node); case NJS_TOKEN_IF: return njs_generate_if_statement(vm, generator, node); case NJS_TOKEN_CONDITIONAL: return njs_generate_cond_expression(vm, generator, node); case NJS_TOKEN_SWITCH: return njs_generate_switch_statement(vm, generator, node); case NJS_TOKEN_WHILE: return njs_generate_while_statement(vm, generator, node); case NJS_TOKEN_DO: return njs_generate_do_while_statement(vm, generator, node); case NJS_TOKEN_FOR: return njs_generate_for_statement(vm, generator, node); case NJS_TOKEN_FOR_IN: return njs_generate_for_in_statement(vm, generator, node); case NJS_TOKEN_CONTINUE: return njs_generate_continue_statement(vm, generator, node); case NJS_TOKEN_BREAK: return njs_generate_break_statement(vm, generator, node); case NJS_TOKEN_DEBUGGER: return njs_generate_debugger_statement(vm, generator, node); case NJS_TOKEN_STATEMENT: return njs_generate_statement(vm, generator, node); case NJS_TOKEN_BLOCK: return njs_generate_block_statement(vm, generator, node); case NJS_TOKEN_END: return njs_generate_stop_statement(vm, generator, node); case NJS_TOKEN_COMMA: return njs_generate_comma_expression(vm, generator, node); case NJS_TOKEN_ASSIGNMENT: return njs_generate_assignment(vm, generator, 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_SUBTRACTION_ASSIGNMENT: case NJS_TOKEN_MULTIPLICATION_ASSIGNMENT: case NJS_TOKEN_EXPONENTIATION_ASSIGNMENT: case NJS_TOKEN_DIVISION_ASSIGNMENT: case NJS_TOKEN_REMAINDER_ASSIGNMENT: return njs_generate_operation_assignment(vm, generator, node); 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_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_SUBTRACTION: case NJS_TOKEN_MULTIPLICATION: case NJS_TOKEN_EXPONENTIATION: case NJS_TOKEN_DIVISION: case NJS_TOKEN_REMAINDER: case NJS_TOKEN_PROPERTY_DELETE: case NJS_TOKEN_PROPERTY: return njs_generate_3addr_operation(vm, generator, node, 0); case NJS_TOKEN_IN: /* * An "in" operation is parsed as standard binary expression * by njs_parser_binary_expression(). However, its operands * should be swapped to be uniform with other property operations * (get/set and delete) to use the property trap. */ return njs_generate_3addr_operation(vm, generator, node, 1); case NJS_TOKEN_LOGICAL_AND: case NJS_TOKEN_LOGICAL_OR: case NJS_TOKEN_COALESCE: return njs_generate_test_jump_expression(vm, generator, node); case NJS_TOKEN_DELETE: case NJS_TOKEN_VOID: 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, generator, node); case NJS_TOKEN_TYPEOF: return njs_generate_typeof_operation(vm, generator, node); case NJS_TOKEN_INCREMENT: case NJS_TOKEN_DECREMENT: return njs_generate_inc_dec_operation(vm, generator, node, 0); case NJS_TOKEN_POST_INCREMENT: case NJS_TOKEN_POST_DECREMENT: return njs_generate_inc_dec_operation(vm, generator, node, 1); case NJS_TOKEN_NULL: case NJS_TOKEN_TRUE: case NJS_TOKEN_FALSE: case NJS_TOKEN_NUMBER: case NJS_TOKEN_STRING: node->index = njs_scope_global_index(vm, &node->u.value, generator->runtime); if (njs_slow_path(node->index == NJS_INDEX_ERROR)) { return NJS_ERROR; } return njs_generator_stack_pop(vm, generator, NULL); case NJS_TOKEN_OBJECT_VALUE: node->index = node->u.object->index; return njs_generator_stack_pop(vm, generator, NULL); case NJS_TOKEN_OBJECT: return njs_generate_object(vm, generator, node); case NJS_TOKEN_PROPERTY_GETTER: case NJS_TOKEN_PROPERTY_SETTER: return njs_generate_property_accessor(vm, generator, node); case NJS_TOKEN_ARRAY: return njs_generate_array(vm, generator, node); case NJS_TOKEN_FUNCTION_EXPRESSION: case NJS_TOKEN_ASYNC_FUNCTION_EXPRESSION: return njs_generate_function_expression(vm, generator, node); case NJS_TOKEN_FUNCTION: case NJS_TOKEN_ASYNC_FUNCTION: return njs_generate_function(vm, generator, node); case NJS_TOKEN_REGEXP: return njs_generate_regexp(vm, generator, node); case NJS_TOKEN_TEMPLATE_LITERAL: return njs_generate_template_literal(vm, generator, node); case NJS_TOKEN_EXTERNAL: return njs_generator_stack_pop(vm, generator, NULL); case NJS_TOKEN_NAME: case NJS_TOKEN_ARGUMENTS: case NJS_TOKEN_EVAL: case NJS_TOKEN_THIS: return njs_generate_name(vm, generator, node); case NJS_TOKEN_FUNCTION_DECLARATION: case NJS_TOKEN_ASYNC_FUNCTION_DECLARATION: return njs_generate_function_declaration(vm, generator, node); case NJS_TOKEN_FUNCTION_CALL: return njs_generate_function_call(vm, generator, node); case NJS_TOKEN_RETURN: return njs_generate_return_statement(vm, generator, node); case NJS_TOKEN_METHOD_CALL: return njs_generate_method_call(vm, generator, node); case NJS_TOKEN_TRY: return njs_generate_try_statement(vm, generator, node); case NJS_TOKEN_THROW: return njs_generate_throw_statement(vm, generator, node); case NJS_TOKEN_IMPORT: return njs_generate_import_statement(vm, generator, node); case NJS_TOKEN_EXPORT: return njs_generate_export_statement(vm, generator, node); case NJS_TOKEN_AWAIT: return njs_generate_await(vm, generator, node); default: njs_internal_error(vm, "Generator failed: unknown token"); return NJS_ERROR; } } static njs_int_t njs_generator_pop(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { return njs_generator_stack_pop(vm, generator, NULL); } static u_char * njs_generate_reserve(njs_vm_t *vm, njs_generator_t *generator, size_t size) { u_char *p; if (generator->code_end + size <= generator->code_start + generator->code_size) { return generator->code_end; } size = njs_max(generator->code_end - generator->code_start + size, generator->code_size); if (size < 1024) { size *= 2; } else { size += size / 2; } p = njs_mp_alloc(vm->mem_pool, size); if (njs_slow_path(p == NULL)) { njs_memory_error(vm); return NULL; } generator->code_size = size; size = generator->code_end - generator->code_start; memcpy(p, generator->code_start, size); njs_mp_free(vm->mem_pool, generator->code_start); generator->code_start = p; generator->code_end = p + size; return generator->code_end; } static njs_int_t njs_generate_code_map(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node, u_char *code) { njs_arr_t *map; njs_vm_line_num_t *last; map = generator->lines; if (map != NULL && node != NULL) { last = (map->items != 0) ? njs_arr_last(map) : NULL; if (last == NULL || (node->token_line != last->line)) { last = njs_arr_add(map); if (njs_slow_path(last == NULL)) { return NJS_ERROR; } last->line = node->token_line; last->offset = njs_code_offset(generator, code); } } return NJS_OK; } njs_vm_code_t * njs_lookup_code(njs_vm_t *vm, u_char *pc) { njs_uint_t i; njs_vm_code_t *code; code = vm->codes->start; for (i = 0; i < vm->codes->items; i++, code++) { if (code->start <= pc && pc < code->end) { return code; } } return NULL; } uint32_t njs_lookup_line(njs_arr_t *lines, uint32_t offset) { njs_uint_t n; njs_vm_line_num_t *map; n = 0; map = NULL; if (lines != NULL) { n = lines->items; map = (njs_vm_line_num_t *) lines->start; } while (n != 0) { if (offset >= map->offset && (n == 1 || offset < map[1].offset)) { return map->line; } map++; n--; } return 0; } static njs_int_t njs_generate_name(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_variable_t *var; njs_parser_scope_t *scope; njs_vmcode_variable_t *variable; njs_vmcode_function_copy_t *copy; var = njs_variable_reference(vm, node); if (njs_slow_path(var == NULL)) { ret = njs_generate_global_reference(vm, generator, node, 1); if (njs_slow_path(ret != NJS_OK)) { return ret; } return njs_generator_stack_pop(vm, generator, NULL); } if (var->function && var->type == NJS_VARIABLE_FUNCTION) { njs_generate_code(generator, njs_vmcode_function_copy_t, copy, NJS_VMCODE_FUNCTION_COPY, node); copy->function = &var->value; copy->retval = node->index; } if (var->init) { return njs_generator_stack_pop(vm, generator, NULL); } if (var->type == NJS_VARIABLE_LET || var->type == NJS_VARIABLE_CONST) { scope = njs_function_scope(node->scope); if (scope->dest_disable) { njs_generate_code(generator, njs_vmcode_variable_t, variable, NJS_VMCODE_NOT_INITIALIZED, node); variable->dst = node->index; } } return njs_generator_stack_pop(vm, generator, NULL); } static njs_int_t njs_generate_variable(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node, njs_reference_type_t type, njs_variable_t **retvar) { njs_variable_t *var; njs_parser_scope_t *scope; njs_vmcode_variable_t *variable; njs_vmcode_function_copy_t *copy; var = njs_variable_reference(vm, node); if (retvar != NULL) { *retvar = var; } if (njs_slow_path(var == NULL)) { switch (type) { case NJS_DECLARATION: return njs_generate_reference_error(vm, generator, node); case NJS_REFERENCE: case NJS_TYPEOF: return njs_generate_global_reference(vm, generator, node, type == NJS_REFERENCE); } } if (var->function && var->type == NJS_VARIABLE_FUNCTION) { njs_generate_code(generator, njs_vmcode_function_copy_t, copy, NJS_VMCODE_FUNCTION_COPY, node); copy->function = &var->value; copy->retval = node->index; } if (var->init) { return NJS_OK; } if (var->type == NJS_VARIABLE_LET || var->type == NJS_VARIABLE_CONST) { scope = njs_function_scope(node->scope); if ((!scope->dest_disable && njs_function_scope(var->scope) == scope)) { njs_generate_code(generator, njs_vmcode_variable_t, variable, NJS_VMCODE_NOT_INITIALIZED, node); variable->dst = node->index; } } return NJS_OK; } static njs_int_t njs_generate_variable_wo_dest(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node, njs_reference_type_t type, njs_variable_t **retvar) { njs_int_t ret; njs_parser_scope_t *scope; scope = njs_function_scope(node->scope); scope->dest_disable = 1; ret = njs_generate_variable(vm, generator, node, type, retvar); scope->dest_disable = 0; return ret; } static njs_int_t njs_generate_var_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_variable_t *var; njs_parser_node_t *lvalue, *expr; lvalue = node->left; ret = njs_generate_variable_wo_dest(vm, generator, lvalue, NJS_DECLARATION, &var); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } expr = node->right; if (expr == NULL) { /* Variable is only declared. */ if (var->type == NJS_VARIABLE_CONST) { njs_syntax_error(vm, "missing initializer in const declaration"); return NJS_ERROR; } if (var->type == NJS_VARIABLE_LET) { ret = njs_generate_let(vm, generator, node, var); if (njs_slow_path(ret != NJS_OK)) { return ret; } } var->init = 1; return njs_generator_stack_pop(vm, generator, NULL); } if (var->type == NJS_VARIABLE_LET || var->type == NJS_VARIABLE_CONST) { ret = njs_generate_wo_dest(vm, generator, expr); if (njs_slow_path(ret != NJS_OK)) { return ret; } } else { expr->dest = lvalue; njs_generator_next(generator, njs_generate, expr); } return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_var_statement_after, var, 0); } static njs_int_t njs_generate_var_statement_after(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_variable_t *var; njs_parser_node_t *lvalue, *expr; njs_vmcode_move_t *move; const njs_lexer_entry_t *lex_entry; lvalue = node->left; expr = node->right; var = generator->context; if (var->type == NJS_VARIABLE_LET || var->type == NJS_VARIABLE_CONST) { ret = njs_generate_let(vm, generator, node, var); if (njs_slow_path(ret != NJS_OK)) { return ret; } } var->init = 1; /* * 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_move(generator, move, lvalue->index, expr->index, lvalue); } node->index = expr->index; node->temporary = expr->temporary; if ((expr->token_type == NJS_TOKEN_FUNCTION_EXPRESSION || expr->token_type == NJS_TOKEN_ASYNC_FUNCTION_EXPRESSION) && njs_values_same(&expr->u.value.data.u.lambda->name, &njs_string_empty)) { lex_entry = njs_lexer_entry(node->left->u.reference.unique_id); if (njs_slow_path(lex_entry == NULL)) { return NJS_ERROR; } ret = njs_string_create(vm, &expr->u.value.data.u.lambda->name, lex_entry->name.start, lex_entry->name.length); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } return njs_generator_stack_pop(vm, generator, NULL); } static njs_int_t njs_generate_let(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node, njs_variable_t *var) { njs_vmcode_variable_t *code; njs_generate_code(generator, njs_vmcode_variable_t, code, NJS_VMCODE_LET, node); code->dst = var->index; return NJS_OK; } static njs_int_t njs_generate_if_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; ret = njs_generate_start_block(vm, generator, NJS_GENERATOR_BLOCK, &node->name); if (njs_slow_path(ret != NJS_OK)) { return ret; } /* The condition expression. */ njs_generator_next(generator, njs_generate, node->left); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_if_statement_cond, NULL, 0); } static njs_int_t njs_generate_if_statement_cond(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_jump_off_t jump_offset; njs_vmcode_cond_jump_t *cond_jump; njs_generate_code(generator, njs_vmcode_cond_jump_t, cond_jump, NJS_VMCODE_IF_FALSE_JUMP, node); cond_jump->cond = node->left->index; ret = njs_generate_node_index_release(vm, generator, node->left); if (njs_slow_path(ret != NJS_OK)) { return ret; } jump_offset = njs_code_offset(generator, cond_jump); if (node->right != NULL && node->right->token_type == NJS_TOKEN_BRANCHING) { /* The "then" branch in a case of "if/then/else" statement. */ node = node->right; njs_generator_next(generator, njs_generate, node->left); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_if_statement_then, &jump_offset, sizeof(njs_jump_off_t)); } /* * The "then" branch in a case of "if/then" statement * or the "else" branch in a case of "if/then/else" statement. */ njs_generator_next(generator, njs_generate, node->right); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_if_statement_else, &jump_offset, sizeof(njs_jump_off_t)); } static njs_int_t njs_generate_if_statement_then(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_jump_off_t *jump_offset, label_offset; njs_vmcode_jump_t *jump; ret = njs_generate_node_index_release(vm, generator, node->left); if (njs_slow_path(ret != NJS_OK)) { return ret; } jump_offset = (njs_jump_off_t *) generator->context; label_offset = *jump_offset + offsetof(njs_vmcode_cond_jump_t, offset); njs_generate_code_jump(generator, jump, 0); njs_code_set_offset(generator, label_offset, *jump_offset); *jump_offset = njs_code_offset(generator, jump); njs_generator_next(generator, njs_generate, node->right); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_if_statement_else, jump_offset, 0); } static njs_int_t njs_generate_if_statement_else(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_jump_off_t *jump_offset, label_offset; jump_offset = (njs_jump_off_t *) generator->context; label_offset = *jump_offset + offsetof(njs_vmcode_cond_jump_t, offset); ret = njs_generate_node_index_release(vm, generator, node->right); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_code_set_offset(generator, label_offset, *jump_offset); njs_generate_patch_block_exit(vm, generator); return njs_generator_stack_pop(vm, generator, generator->context); } static njs_int_t njs_generate_cond_expression(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_generator_next(generator, njs_generate, node->left); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_cond_expression_handler, NULL, 0); } static njs_int_t njs_generate_cond_expression_handler(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_jump_off_t jump_offset; njs_vmcode_cond_jump_t *cond_jump; njs_generate_code(generator, njs_vmcode_cond_jump_t, cond_jump, NJS_VMCODE_IF_FALSE_JUMP, node); jump_offset = njs_code_offset(generator, cond_jump); cond_jump->cond = node->left->index; node->index = njs_generate_dest_index(vm, generator, node); if (njs_slow_path(node->index == NJS_INDEX_ERROR)) { return NJS_ERROR; } njs_generator_next(generator, njs_generate, node->right->left); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_cond_expression_true, &jump_offset, sizeof(njs_jump_off_t)); } static njs_int_t njs_generate_cond_expression_true(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_jump_off_t jump_offset; njs_parser_node_t *branch; njs_vmcode_move_t *move; njs_vmcode_jump_t *jump; branch = node->right; /* * Branches usually uses node->index as destination, however, * if branch expression is a literal, variable or assignment, * then a MOVE operation is required. */ if (node->index != branch->left->index) { njs_generate_code_move(generator, move, node->index, branch->left->index, node); } ret = njs_generate_node_index_release(vm, generator, branch->left); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_generate_code_jump(generator, jump, 0); jump_offset = njs_code_offset(generator, jump); njs_code_set_jump_offset(generator, njs_vmcode_cond_jump_t, *((njs_jump_off_t *) generator->context)); njs_generator_next(generator, njs_generate, branch->right); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_cond_expression_false, &jump_offset, sizeof(njs_jump_off_t)); } static njs_int_t njs_generate_cond_expression_false(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_parser_node_t *branch; njs_vmcode_move_t *move; branch = node->right; if (node->index != branch->right->index) { njs_generate_code_move(generator, move, node->index, branch->right->index, node); } njs_code_set_jump_offset(generator, njs_vmcode_cond_jump_t, *((njs_jump_off_t *) generator->context)); ret = njs_generate_node_index_release(vm, generator, branch->right); if (njs_slow_path(ret != NJS_OK)) { return ret; } return njs_generator_stack_pop(vm, generator, generator->context); } static njs_int_t njs_generate_switch_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *swtch) { njs_generator_switch_ctx_t ctx; /* The "switch" expression. */ njs_generator_next(generator, njs_generate, swtch->left); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), swtch, njs_generate_switch_expression, &ctx, sizeof(njs_generator_switch_ctx_t)); } static njs_int_t njs_generate_switch_expression(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *swtch) { njs_int_t ret; njs_parser_node_t *expr; njs_vmcode_move_t *move; njs_generator_switch_ctx_t *ctx; ctx = generator->context; expr = swtch->left; ctx->index = expr->index; if (!expr->temporary) { ctx->index = njs_generate_temp_index_get(vm, generator, swtch); if (njs_slow_path(ctx->index == NJS_INDEX_ERROR)) { return NJS_ERROR; } njs_generate_code_move(generator, move, ctx->index, expr->index, swtch); } ret = njs_generate_start_block(vm, generator, NJS_GENERATOR_SWITCH, &swtch->name); if (njs_slow_path(ret != NJS_OK)) { return ret; } ctx->patch = NULL; ctx->last = &ctx->patch; if (swtch->right != NULL) { /* The "case" expression. */ njs_generator_next(generator, njs_generate_switch_case, swtch->right); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), swtch, njs_generate_switch_case_end, ctx, 0); } return njs_generate_switch_case_end(vm, generator, swtch); } static njs_int_t njs_generate_switch_case(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *branch) { if (branch->token_type == NJS_TOKEN_DEFAULT) { if (branch->left == NULL) { return njs_generator_stack_pop(vm, generator, NULL); } branch = branch->left; } njs_generator_next(generator, njs_generate, branch->right->left); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), branch, njs_generate_switch_case_after, generator->context, 0); } static njs_int_t njs_generate_switch_case_after(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *branch) { njs_int_t ret; njs_parser_node_t *node; njs_generator_patch_t *patch; njs_vmcode_equal_jump_t *equal; njs_generator_switch_ctx_t *ctx; ctx = generator->context; node = branch->right; njs_generate_code(generator, njs_vmcode_equal_jump_t, equal, NJS_VMCODE_IF_EQUAL_JUMP, branch); equal->offset = offsetof(njs_vmcode_equal_jump_t, offset); equal->value1 = ctx->index; equal->value2 = node->left->index; ret = njs_generate_node_index_release(vm, generator, node->left); if (njs_slow_path(ret != NJS_OK)) { return ret; } patch = njs_mp_alloc(vm->mem_pool, sizeof(njs_generator_patch_t)); if (njs_slow_path(patch == NULL)) { return NJS_ERROR; } patch->jump_offset = njs_code_offset(generator, equal) + offsetof(njs_vmcode_equal_jump_t, offset); patch->label = no_label; *ctx->last = patch; ctx->last = &patch->next; if (branch->left == NULL) { return njs_generator_stack_pop(vm, generator, NULL); } branch = branch->left; if (branch->token_type == NJS_TOKEN_DEFAULT) { branch = branch->left; if (branch == NULL) { return njs_generator_stack_pop(vm, generator, NULL); } } njs_generator_next(generator, njs_generate, branch->right->left); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), branch, njs_generate_switch_case_after, ctx, 0); } static njs_int_t njs_generate_switch_case_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *swtch) { njs_int_t ret; njs_parser_node_t *branch; njs_generator_switch_ctx_t *ctx; ctx = generator->context; /* Release either temporary index or temporary expr->index. */ ret = njs_generate_index_release(vm, generator, ctx->index); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_generate_code_jump(generator, ctx->jump, offsetof(njs_vmcode_jump_t, offset)); ctx->jump_offset = njs_code_offset(generator, ctx->jump); branch = swtch->right; if (branch != NULL) { njs_generator_next(generator, njs_generate_switch_default, branch); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), swtch, njs_generate_switch_end, ctx, 0); } return njs_generate_switch_end(vm, generator, swtch); } static njs_int_t njs_generate_switch_default(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *branch) { njs_parser_node_t *node; njs_generator_patch_t *next; njs_generator_switch_ctx_t *ctx; ctx = generator->context; if (branch->token_type == NJS_TOKEN_DEFAULT) { njs_code_set_jump_offset(generator, njs_vmcode_jump_t, ctx->jump_offset); ctx->jump = NULL; node = branch; } else { njs_code_update_offset(generator, ctx->patch); next = ctx->patch->next; njs_mp_free(vm->mem_pool, ctx->patch); ctx->patch = next; node = branch->right; } njs_generator_next(generator, njs_generate, node->right); branch = branch->left; if (branch == NULL) { return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), branch, njs_generator_pop, NULL, 0); } return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), branch, njs_generate_switch_default, ctx, 0); } static njs_int_t njs_generate_switch_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *swtch) { njs_generator_switch_ctx_t *ctx; ctx = generator->context; if (ctx->jump != NULL) { /* A "switch" without default case. */ njs_code_set_jump_offset(generator, njs_vmcode_jump_t, ctx->jump_offset); } /* Patch "break" statements offsets. */ njs_generate_patch_block_exit(vm, generator); return njs_generator_stack_pop(vm, generator, ctx); } static njs_int_t njs_generate_while_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_vmcode_jump_t *jump; njs_generator_loop_ctx_t ctx; /* * 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_jump(generator, jump, 0); ctx.jump_offset = njs_code_offset(generator, jump); ret = njs_generate_start_block(vm, generator, NJS_GENERATOR_LOOP, &node->name); if (njs_slow_path(ret != NJS_OK)) { return ret; } ctx.loop_offset = njs_code_offset(generator, generator->code_end); njs_generator_next(generator, njs_generate, node->left); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_while_condition, &ctx, sizeof(njs_generator_loop_ctx_t)); } static njs_int_t njs_generate_while_condition(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_generator_loop_ctx_t *ctx; ctx = generator->context; njs_generate_patch_block(vm, generator, generator->block, NJS_GENERATOR_CONTINUATION); njs_code_set_jump_offset(generator, njs_vmcode_jump_t, ctx->jump_offset); njs_generator_next(generator, njs_generate, node->right); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_while_end, ctx, 0); } static njs_int_t njs_generate_while_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_vmcode_cond_jump_t *cond_jump; njs_generator_loop_ctx_t *ctx; ctx = generator->context; njs_generate_code(generator, njs_vmcode_cond_jump_t, cond_jump, NJS_VMCODE_IF_TRUE_JUMP, node->right); cond_jump->offset = ctx->loop_offset - njs_code_offset(generator, cond_jump); cond_jump->cond = node->right->index; njs_generate_patch_block_exit(vm, generator); ret = njs_generate_node_index_release(vm, generator, node->right); if (njs_slow_path(ret != NJS_OK)) { return ret; } return njs_generator_stack_pop(vm, generator, ctx); } static njs_int_t njs_generate_do_while_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_generator_loop_ctx_t ctx; ret = njs_generate_start_block(vm, generator, NJS_GENERATOR_LOOP, &node->name); if (njs_slow_path(ret != NJS_OK)) { return ret; } ctx.loop_offset = njs_code_offset(generator, generator->code_end); njs_generator_next(generator, njs_generate, node->left); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_do_while_condition, &ctx, sizeof(njs_generator_loop_ctx_t)); } static njs_int_t njs_generate_do_while_condition(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_generate_patch_block(vm, generator, generator->block, NJS_GENERATOR_CONTINUATION); njs_generator_next(generator, njs_generate, node->right); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_do_while_end, generator->context, 0); } static njs_int_t njs_generate_do_while_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_vmcode_cond_jump_t *cond_jump; njs_generator_loop_ctx_t *ctx; ctx = generator->context; njs_generate_code(generator, njs_vmcode_cond_jump_t, cond_jump, NJS_VMCODE_IF_TRUE_JUMP, node->right); cond_jump->offset = ctx->loop_offset - njs_code_offset(generator, cond_jump); cond_jump->cond = node->right->index; njs_generate_patch_block_exit(vm, generator); ret = njs_generate_node_index_release(vm, generator, node->right); if (njs_slow_path(ret != NJS_OK)) { return ret; } return njs_generator_stack_pop(vm, generator, ctx); } static njs_int_t njs_generate_for_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_generator_loop_ctx_t ctx; ret = njs_generate_start_block(vm, generator, NJS_GENERATOR_LOOP, &node->name); if (njs_slow_path(ret != NJS_OK)) { return ret; } ctx.jump = NULL; njs_generator_next(generator, njs_generate, node->left); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_for_init, &ctx, sizeof(njs_generator_loop_ctx_t)); } static njs_int_t njs_generate_for_init(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_parser_node_t *condition; njs_generator_loop_ctx_t *ctx; ctx = generator->context; ret = njs_generate_node_index_release(vm, generator, node->left); if (njs_slow_path(ret != NJS_OK)) { return ret; } condition = node->right->left; /* * Closures can occur in conditional and loop updates. This must be * foreseen in order to generate optimized code for let updates. */ ret = njs_generate_for_resolve_closure(vm, condition); if (njs_slow_path(ret != NJS_OK)) { return ret; } ctx->jump_offset = 0; 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_jump(generator, ctx->jump, 0); ctx->jump_offset = njs_code_offset(generator, ctx->jump); } /* The loop body. */ ctx->loop_offset = njs_code_offset(generator, generator->code_end); njs_generator_next(generator, njs_generate, node->right->right->left); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_for_body, ctx, 0); } static njs_int_t njs_generate_for_body(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_parser_node_t *update, *init; njs_generator_loop_ctx_t *ctx; ctx = generator->context; /* The loop update. */ init = node->left; update = node->right->right->right; ret = njs_generate_for_resolve_closure(vm, update); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_generate_for_let_update(vm, generator, init); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_generate_patch_block(vm, generator, generator->block, NJS_GENERATOR_CONTINUATION); njs_generator_next(generator, njs_generate, update); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_for_update, ctx, 0); } static njs_int_t njs_generate_for_update(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_parser_node_t *condition, *update; njs_generator_loop_ctx_t *ctx; ctx = generator->context; update = node->right->right->right; condition = node->right->left; ret = njs_generate_node_index_release(vm, generator, update); if (njs_slow_path(ret != NJS_OK)) { return ret; } /* The loop condition. */ if (condition != NULL) { njs_code_set_jump_offset(generator, njs_vmcode_jump_t, ctx->jump_offset); njs_generator_next(generator, njs_generate, condition); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_for_end, ctx, 0); } return njs_generate_for_end(vm, generator, node); } static njs_int_t njs_generate_for_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_parser_node_t *condition; njs_vmcode_cond_jump_t *cond_jump; njs_generator_loop_ctx_t *ctx; ctx = generator->context; condition = node->right->left; if (condition != NULL) { njs_generate_code(generator, njs_vmcode_cond_jump_t, cond_jump, NJS_VMCODE_IF_TRUE_JUMP, condition); cond_jump->offset = ctx->loop_offset - njs_code_offset(generator, cond_jump); cond_jump->cond = condition->index; njs_generate_patch_block_exit(vm, generator); ret = njs_generate_node_index_release(vm, generator, condition); if (njs_slow_path(ret != NJS_OK)) { return ret; } return njs_generator_stack_pop(vm, generator, ctx); } njs_generate_code_jump(generator, ctx->jump, ctx->loop_offset - njs_code_offset(generator, ctx->jump)); njs_generate_patch_block_exit(vm, generator); return njs_generator_stack_pop(vm, generator, ctx); } static njs_int_t njs_generate_for_let_update(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_parser_node_t *let; njs_vmcode_variable_t *code_var; njs_variable_reference_t *ref; while (node != NULL && node->token_type == NJS_TOKEN_STATEMENT) { let = node->right; if (let->token_type != NJS_TOKEN_LET && let->token_type != NJS_TOKEN_CONST) { return NJS_OK; } ref = &let->left->u.reference; if (ref->variable->closure) { njs_generate_code(generator, njs_vmcode_variable_t, code_var, NJS_VMCODE_LET_UPDATE, let); code_var->dst = let->left->index; } node = node->left; } return NJS_OK; } static njs_int_t njs_generate_for_resolve_closure_cb(njs_vm_t *vm, njs_parser_node_t *node, void *unused) { njs_bool_t closure; njs_variable_t *var; if (node->token_type == NJS_TOKEN_NAME) { var = njs_variable_resolve(vm, node); if (njs_fast_path(var != NULL)) { closure = njs_variable_closure_test(node->scope, var->scope); if (closure) { var->closure = 1; } } } return NJS_OK; } static njs_int_t njs_generate_for_resolve_closure(njs_vm_t *vm, njs_parser_node_t *node) { return njs_parser_traverse(vm, node, NULL, njs_generate_for_resolve_closure_cb); } static njs_int_t njs_generate_for_in_name_assign(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_variable_t *var; njs_parser_node_t *foreach, *lvalue, *expr; njs_vmcode_move_t *move; njs_generator_loop_ctx_t *ctx; ctx = generator->context; foreach = node->left; lvalue = foreach->left; expr = node->right; var = njs_variable_reference(vm, lvalue); if (var != NULL) { ctx->index_next_value = lvalue->index; } else { ctx->index_next_value = njs_generate_temp_index_get(vm, generator, foreach->left); if (njs_slow_path(ctx->index_next_value == NJS_INDEX_ERROR)) { return NJS_ERROR; } if (expr != NULL) { expr->index = ctx->index_next_value; /* * 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_move(generator, move, lvalue->index, expr->index, expr); } ret = njs_generate_global_property_set(vm, generator, foreach->left, expr); if (njs_slow_path(ret != NJS_OK)) { return ret; } } } return njs_generator_stack_pop(vm, generator, NULL); } static njs_int_t njs_generate_for_in_body_wo_decl(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_jump_off_t prop_offset; njs_parser_node_t *foreach, *name; njs_vmcode_prop_next_t *prop_next; njs_generator_loop_ctx_t *ctx; ctx = generator->context; foreach = node->left; name = foreach->left->right; /* The loop iterator. */ if (name != NULL) { ret = njs_generate_for_let_update(vm, generator, foreach->left); if (njs_slow_path(ret != NJS_OK)) { return ret; } } njs_generate_patch_block(vm, generator, generator->block, NJS_GENERATOR_CONTINUATION); njs_code_set_jump_offset(generator, njs_vmcode_prop_foreach_t, ctx->jump_offset); njs_generate_code(generator, njs_vmcode_prop_next_t, prop_next, NJS_VMCODE_PROPERTY_NEXT, node->left->left); prop_offset = njs_code_offset(generator, prop_next); prop_next->retval = ctx->index_next_value; prop_next->object = foreach->right->index; prop_next->next = ctx->index; prop_next->offset = ctx->loop_offset - prop_offset; njs_generate_patch_block_exit(vm, generator); /* * Release object and iterator indexes: an object can be a function result * or a property of another object and an iterator can be given with "let". */ ret = njs_generate_children_indexes_release(vm, generator, foreach); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_generate_index_release(vm, generator, ctx->index); if (njs_slow_path(ret != NJS_OK)) { return ret; } return njs_generator_stack_pop(vm, generator, ctx); } static njs_int_t njs_generate_for_in_object_wo_decl(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_parser_node_t *foreach, *name; njs_generator_loop_ctx_t *ctx; njs_vmcode_prop_foreach_t *prop_foreach; ctx = generator->context; foreach = node->left; name = foreach->left->right; if (name != NULL) { ctx->var->init = 1; } njs_generate_code(generator, njs_vmcode_prop_foreach_t, prop_foreach, NJS_VMCODE_PROPERTY_FOREACH, foreach); ctx->jump_offset = njs_code_offset(generator, prop_foreach); prop_foreach->object = foreach->right->index; ctx->index = njs_generate_temp_index_get(vm, generator, foreach->right); if (njs_slow_path(ctx->index == NJS_INDEX_ERROR)) { return NJS_ERROR; } prop_foreach->next = ctx->index; /* The loop body. */ ctx->loop_offset = njs_code_offset(generator, generator->code_end); /* 1) left. */ njs_generator_next(generator, njs_generate, foreach->left); /* 4) loop-body-end. */ ret = njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_for_in_body_wo_decl, ctx, 0); if (ret != NJS_OK) { return ret; } /* 3) loop-body. */ ret = njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node->right, njs_generate, ctx, 0); if (ret != NJS_OK) { return ret; } /* 2) assign value to name. */ return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_for_in_name_assign, ctx, 0); } static njs_int_t njs_generate_for_in_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_parser_node_t *foreach, *name; njs_generator_loop_ctx_t ctx; ret = njs_generate_start_block(vm, generator, NJS_GENERATOR_LOOP, &node->name); if (njs_slow_path(ret != NJS_OK)) { return ret; } /* The object. */ foreach = node->left; if (foreach->left->token_type != NJS_TOKEN_PROPERTY) { name = foreach->left->right; if (name != NULL) { name = name->left; ret = njs_generate_variable_wo_dest(vm, generator, name, NJS_DECLARATION, &ctx.var); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } foreach->left->index = name->index; njs_generator_next(generator, njs_generate, foreach->right); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_for_in_object, &ctx, sizeof(njs_generator_loop_ctx_t)); } } else { /* foreach->right is object in 'in object'. */ njs_generator_next(generator, njs_generate, foreach->right); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_for_in_object_left_hand_expr, &ctx, sizeof(njs_generator_loop_ctx_t)); } njs_generator_next(generator, njs_generate, foreach->right); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_for_in_object_wo_decl, &ctx, sizeof(njs_generator_loop_ctx_t)); } static njs_int_t njs_generate_for_in_object_left_hand_expr(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_parser_node_t *foreach; njs_generator_loop_ctx_t *ctx; njs_vmcode_prop_foreach_t *prop_foreach; ctx = generator->context; foreach = node->left; njs_generate_code(generator, njs_vmcode_prop_foreach_t, prop_foreach, NJS_VMCODE_PROPERTY_FOREACH, foreach); ctx->jump_offset = njs_code_offset(generator, prop_foreach); prop_foreach->object = foreach->right->index; ctx->index = njs_generate_temp_index_get(vm, generator, foreach->right); if (njs_slow_path(ctx->index == NJS_INDEX_ERROR)) { return NJS_ERROR; } ctx->index_next_value = njs_generate_temp_index_get(vm, generator, foreach->left); if (njs_slow_path(ctx->index_next_value == NJS_INDEX_ERROR)) { return NJS_ERROR; } prop_foreach->next = ctx->index; ctx->loop_offset = njs_code_offset(generator, generator->code_end); /* Object part calculation. */ njs_generator_next(generator, njs_generate, foreach->left->left); /* The loop body. */ ret = njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_for_in_body_left_hand_expr, ctx, sizeof(njs_generator_loop_ctx_t)); if (njs_slow_path(ret != NJS_OK)) { return ret; } /* set-property and block. */ ret = njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_for_in_set_prop_block, ctx, sizeof(njs_generator_loop_ctx_t)); if (njs_slow_path(ret != NJS_OK)) { return ret; } /* Key part calculation. */ return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), foreach->left->right, njs_generate, NULL, 0); } static njs_int_t njs_generate_for_in_set_prop_block(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_parser_node_t *foreach; njs_vmcode_prop_set_t *prop_set; njs_generator_loop_ctx_t *ctx; ctx = generator->context; foreach = node->left; njs_generate_code(generator, njs_vmcode_prop_set_t, prop_set, NJS_VMCODE_PROPERTY_SET, foreach); prop_set->object = foreach->left->left->index; prop_set->property = foreach->left->right->index; prop_set->value = ctx->index_next_value; njs_generator_next(generator, njs_generate, node->right); return NJS_OK; } static njs_int_t njs_generate_for_in_object(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_parser_node_t *foreach, *name; njs_generator_loop_ctx_t *ctx; njs_vmcode_prop_foreach_t *prop_foreach; ctx = generator->context; foreach = node->left; name = foreach->left->right; if (name != NULL) { ctx->var->init = 1; } njs_generate_code(generator, njs_vmcode_prop_foreach_t, prop_foreach, NJS_VMCODE_PROPERTY_FOREACH, foreach); ctx->jump_offset = njs_code_offset(generator, prop_foreach); prop_foreach->object = foreach->right->index; ctx->index = njs_generate_temp_index_get(vm, generator, foreach->right); if (njs_slow_path(ctx->index == NJS_INDEX_ERROR)) { return NJS_ERROR; } prop_foreach->next = ctx->index; /* The loop body. */ ctx->loop_offset = njs_code_offset(generator, generator->code_end); njs_generator_next(generator, njs_generate, node->right); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_for_in_body, ctx, 0); } static njs_int_t njs_generate_for_in_body_left_hand_expr(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_jump_off_t prop_offset; njs_parser_node_t *foreach; njs_vmcode_prop_next_t *prop_next; njs_generator_loop_ctx_t *ctx; ctx = generator->context; foreach = node->left; njs_generate_patch_block(vm, generator, generator->block, NJS_GENERATOR_CONTINUATION); njs_code_set_jump_offset(generator, njs_vmcode_prop_foreach_t, ctx->jump_offset); njs_generate_code(generator, njs_vmcode_prop_next_t, prop_next, NJS_VMCODE_PROPERTY_NEXT, node->left->left); prop_offset = njs_code_offset(generator, prop_next); prop_next->retval = ctx->index_next_value; prop_next->object = foreach->right->index; prop_next->next = ctx->index; prop_next->offset = ctx->loop_offset - prop_offset; njs_generate_patch_block_exit(vm, generator); /* * Release object and iterator indexes: an object can be a function result * or a property of another object and an iterator can be given with "let". */ ret = njs_generate_children_indexes_release(vm, generator, foreach); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_generate_index_release(vm, generator, ctx->index); if (njs_slow_path(ret != NJS_OK)) { return ret; } return njs_generator_stack_pop(vm, generator, ctx); } static njs_int_t njs_generate_for_in_body(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_jump_off_t prop_offset; njs_parser_node_t *foreach, *name; njs_vmcode_prop_next_t *prop_next; njs_generator_loop_ctx_t *ctx; ctx = generator->context; foreach = node->left; name = foreach->left->right; /* The loop iterator. */ if (name != NULL) { ret = njs_generate_for_let_update(vm, generator, foreach->left); if (njs_slow_path(ret != NJS_OK)) { return ret; } } njs_generate_patch_block(vm, generator, generator->block, NJS_GENERATOR_CONTINUATION); njs_code_set_jump_offset(generator, njs_vmcode_prop_foreach_t, ctx->jump_offset); njs_generate_code(generator, njs_vmcode_prop_next_t, prop_next, NJS_VMCODE_PROPERTY_NEXT, node->left->left); prop_offset = njs_code_offset(generator, prop_next); prop_next->retval = foreach->left->index; prop_next->object = foreach->right->index; prop_next->next = ctx->index; prop_next->offset = ctx->loop_offset - prop_offset; njs_generate_patch_block_exit(vm, generator); /* * Release object and iterator indexes: an object can be a function result * or a property of another object and an iterator can be given with "let". */ ret = njs_generate_children_indexes_release(vm, generator, foreach); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_generate_index_release(vm, generator, ctx->index); if (njs_slow_path(ret != NJS_OK)) { return ret; } return njs_generator_stack_pop(vm, generator, ctx); } #ifdef NJS_DEBUG_GENERATOR njs_inline const char* njs_block_type(njs_generator_block_type_t type) { switch (type) { case NJS_GENERATOR_LOOP: return "LOOP "; case NJS_GENERATOR_SWITCH: return "SWITCH"; case NJS_GENERATOR_BLOCK: return "BLOCK "; default: return "TRY "; } } #endif static njs_int_t njs_generate_start_block(njs_vm_t *vm, njs_generator_t *generator, njs_generator_block_type_t type, const njs_str_t *label) { njs_generator_block_t *block; block = njs_mp_alloc(vm->mem_pool, sizeof(njs_generator_block_t)); if (njs_fast_path(block != NULL)) { block->next = generator->block; generator->block = block; block->type = type; block->label = *label; block->continuation = NULL; block->exit = NULL; block->index = 0; njs_debug_generator(vm, "START %s %p", njs_block_type(type), block); return NJS_OK; } return NJS_ERROR; } static njs_generator_block_t * njs_generate_lookup_block(njs_generator_block_t *block, uint32_t mask, const njs_str_t *label) { if (njs_strstr_eq(label, &return_label)) { mask = NJS_GENERATOR_TRY; label = &no_label; } while (block != NULL) { if ((block->type & mask) != 0 && (label->length == 0 || njs_strstr_eq(&block->label, label))) { return block; } block = block->next; } return NULL; } static njs_generator_block_t * njs_generate_find_block(njs_vm_t *vm, njs_generator_block_t *block, uint32_t mask, const njs_str_t *label) { njs_generator_block_t *dest_block; /* * ES5.1: 12.8 The break Statement * "break" without a label is valid only from within * loop or switch statement. */ if ((mask & NJS_GENERATOR_ALL) == NJS_GENERATOR_ALL && label->length != 0) { mask |= NJS_GENERATOR_BLOCK; } dest_block = njs_generate_lookup_block(block, mask, label); if (dest_block != NULL) { /* * Looking for intermediate try-catch blocks. Before jumping to * the destination finally blocks have to be executed. */ while (block != NULL) { if (block->type & NJS_GENERATOR_TRY) { njs_debug_generator(vm, "FIND %s %p", njs_block_type(block->type), block); return block; } if (block == dest_block) { break; } block = block->next; } } njs_debug_generator(vm, "FIND %s %p", dest_block != NULL ? njs_block_type(dest_block->type) : "NONE ", dest_block); return dest_block; } static njs_generator_patch_t * njs_generate_make_continuation_patch(njs_vm_t *vm, njs_generator_block_t *block, const njs_str_t *label, njs_jump_off_t offset) { njs_generator_patch_t *patch; patch = njs_mp_alloc(vm->mem_pool, sizeof(njs_generator_patch_t)); if (njs_slow_path(patch == NULL)) { njs_memory_error(vm); return NULL; } patch->next = block->continuation; block->continuation = patch; patch->jump_offset = offset; patch->label = *label; njs_debug_generator(vm, "MAKE CONT %p %L %V", patch, patch->jump_offset, &patch->label); return patch; } static void njs_generate_patch(njs_vm_t *vm, njs_generator_t *generator, njs_generator_patch_t *list) { njs_generator_patch_t *patch, *next; for (patch = list; patch != NULL; patch = next) { njs_code_update_offset(generator, patch); njs_debug_generator(vm, "PATCH %p at %L to %L %V", patch, patch->jump_offset, *(njs_code_jump_ptr(generator, patch->jump_offset)), &patch->label); next = patch->next; njs_mp_free(vm->mem_pool, patch); } } static void njs_generate_patch_block(njs_vm_t *vm, njs_generator_t *generator, njs_generator_block_t *block, unsigned type) { if (type & NJS_GENERATOR_CONTINUATION) { njs_debug_generator(vm, "PATCH CONT %p", block); njs_generate_patch(vm, generator, block->continuation); } if (type & NJS_GENERATOR_EXIT) { njs_debug_generator(vm, "PATCH EXIT %p", block); njs_generate_patch(vm, generator, block->exit); } } static njs_generator_patch_t * njs_generate_make_exit_patch(njs_vm_t *vm, njs_generator_block_t *block, const njs_str_t *label, njs_jump_off_t offset) { njs_generator_patch_t *patch; patch = njs_mp_alloc(vm->mem_pool, sizeof(njs_generator_patch_t)); if (njs_slow_path(patch == NULL)) { njs_memory_error(vm); return NULL; } patch->next = block->exit; block->exit = patch; patch->jump_offset = offset; patch->label = *label; njs_debug_generator(vm, "MAKE EXIT %p %L %V", patch, patch->jump_offset, &patch->label); return patch; } static void njs_generate_patch_block_exit(njs_vm_t *vm, njs_generator_t *generator) { njs_generator_block_t *block; block = generator->block; generator->block = block->next; njs_generate_patch_block(vm, generator, block, NJS_GENERATOR_EXIT); njs_debug_generator(vm, "EXIT %s %p", njs_block_type(block->type), block); njs_mp_free(vm->mem_pool, block); } /* * TODO: support multiple destination points from within try-catch block. */ static const njs_str_t * njs_generate_jump_destination(njs_vm_t *vm, njs_generator_block_t *block, const char *inst_type, uint32_t mask, const njs_str_t *label1, const njs_str_t *label2) { njs_generator_block_t *block1, *block2; if (label1->length == undef_label.length) { return label2; } if (label2->length == undef_label.length) { return label1; } block1 = njs_generate_lookup_block(block, mask, label1); block2 = njs_generate_lookup_block(block, mask, label2); if (block1 != block2) { njs_internal_error(vm, "%s instructions with different labels " "(\"%V\" vs \"%V\") " "from try-catch block are not supported", inst_type, label1, label2); return NULL; } return label1; } static njs_int_t njs_generate_continue_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { const njs_str_t *label, *dest; njs_vmcode_jump_t *jump; njs_generator_patch_t *patch; njs_generator_block_t *block; label = &node->name; block = njs_generate_find_block(vm, generator->block, NJS_GENERATOR_LOOP, label); if (njs_slow_path(block == NULL)) { goto syntax_error; } if (block->type == NJS_GENERATOR_TRY && block->continuation != NULL) { dest = njs_generate_jump_destination(vm, block->next, "continue", NJS_GENERATOR_LOOP, &block->continuation->label, label); if (njs_slow_path(dest == NULL)) { return NJS_ERROR; } } njs_generate_code_jump(generator, jump, offsetof(njs_vmcode_jump_t, offset)); patch = njs_generate_make_continuation_patch(vm, block, label, njs_code_offset(generator, jump) + offsetof(njs_vmcode_jump_t, offset)); if (njs_slow_path(patch == NULL)) { return NJS_ERROR; } return njs_generator_stack_pop(vm, generator, NULL); syntax_error: njs_generate_syntax_error(vm, node, &generator->file, "Illegal continue statement"); return NJS_ERROR; } static njs_int_t njs_generate_break_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { const njs_str_t *label, *dest; njs_vmcode_jump_t *jump; njs_generator_patch_t *patch; njs_generator_block_t *block; label = &node->name; block = njs_generate_find_block(vm, generator->block, NJS_GENERATOR_ALL, label); if (njs_slow_path(block == NULL)) { goto syntax_error; } if (block->type == NJS_GENERATOR_TRY && block->exit != NULL) { dest = njs_generate_jump_destination(vm, block->next, "break/return", NJS_GENERATOR_ALL, &block->exit->label, label); if (njs_slow_path(dest == NULL)) { return NJS_ERROR; } } njs_generate_code_jump(generator, jump, offsetof(njs_vmcode_jump_t, offset)); patch = njs_generate_make_exit_patch(vm, block, label, njs_code_offset(generator, jump) + offsetof(njs_vmcode_jump_t, offset)); if (njs_slow_path(patch == NULL)) { return NJS_ERROR; } return njs_generator_stack_pop(vm, generator, NULL); syntax_error: njs_generate_syntax_error(vm, node, &generator->file, "Illegal break statement"); return NJS_ERROR; } static njs_int_t njs_generate_debugger_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_vmcode_debugger_t *debugger; njs_generate_code(generator, njs_vmcode_debugger_t, debugger, NJS_VMCODE_DEBUGGER, node); debugger->retval = njs_generate_dest_index(vm, generator, node); if (njs_slow_path(debugger->retval == NJS_INDEX_ERROR)) { return debugger->retval; } return njs_generator_stack_pop(vm, generator, NULL); } static njs_int_t njs_generate_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_variable_t *var; njs_queue_link_t *link; njs_parser_node_t *right; njs_vmcode_variable_t *code; right = node->right; if (right != NULL && right->token_type == NJS_TOKEN_NAME) { var = njs_variable_reference(vm, right); if (njs_slow_path(var == NULL)) { goto statement; } if (!var->init && (var->type == NJS_VARIABLE_LET || var->type == NJS_VARIABLE_CONST)) { njs_generate_code(generator, njs_vmcode_variable_t, code, NJS_VMCODE_INITIALIZATION_TEST, right); code->dst = right->index; } if (node->left == NULL) { return njs_generator_stack_pop(vm, generator, NULL); } node = node->left; } statement: link = njs_queue_first(&generator->stack); ret = njs_generate_children(vm, generator, node); if (njs_slow_path(ret != NJS_OK)) { return ret; } return njs_generator_after(vm, generator, link, right, njs_generate_node_index_release_pop, NULL, 0); } static njs_int_t njs_generate_block_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_queue_link_t *link; ret = njs_generate_start_block(vm, generator, NJS_GENERATOR_BLOCK, &node->name); if (njs_slow_path(ret != NJS_OK)) { return ret; } link = njs_queue_first(&generator->stack); ret = njs_generate_statement(vm, generator, node); if (njs_slow_path(ret != NJS_OK)) { return ret; } return njs_generator_after(vm, generator, link, node, njs_generate_block_statement_end, NULL, 0); } static njs_int_t njs_generate_block_statement_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_generate_patch_block_exit(vm, generator); return njs_generator_stack_pop(vm, generator, NULL); } static njs_int_t njs_generate_children(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_generator_next(generator, njs_generate, node->left); ret = njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node->right, njs_generate, NULL, 0); if (njs_slow_path(ret != NJS_OK)) { return ret; } return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node->left, njs_generate_node_index_release_pop, NULL, 0); } static njs_int_t njs_generate_stop_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_queue_link_t *link; link = njs_queue_first(&generator->stack); ret = njs_generate_children(vm, generator, node); if (njs_fast_path(ret != NJS_OK)) { return ret; } return njs_generator_after(vm, generator, link, node, njs_generate_stop_statement_end, NULL, 0); } static njs_int_t njs_generate_stop_statement_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_index_t index; njs_vmcode_stop_t *stop; njs_generate_code(generator, njs_vmcode_stop_t, stop, NJS_VMCODE_STOP, node); index = njs_scope_undefined_index(vm, 0); node = node->right; if (node != NULL) { if ((node->index != NJS_INDEX_NONE && node->token_type != NJS_TOKEN_FUNCTION_DECLARATION && node->token_type != NJS_TOKEN_ASYNC_FUNCTION_DECLARATION) || node->token_type == NJS_TOKEN_THIS) { index = node->index; } } stop->retval = index; return njs_generator_stack_pop(vm, generator, NULL); } static njs_int_t njs_generate_comma_expression(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_queue_link_t *link; link = njs_queue_first(&generator->stack); ret = njs_generate_children(vm, generator, node); if (njs_fast_path(ret != NJS_OK)) { return ret; } return njs_generator_after(vm, generator, link, node, njs_generate_comma_expression_end, NULL, 0); } static njs_int_t njs_generate_comma_expression_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { node->index = node->right->index; return njs_generator_stack_pop(vm, generator, NULL); } static njs_int_t njs_generate_global_property_set(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node_dst, njs_parser_node_t *node_src) { njs_int_t ret; njs_value_t property; njs_variable_t *var; njs_vmcode_prop_set_t *prop_set; const njs_lexer_entry_t *lex_entry; var = njs_variable_reference(vm, node_dst); if (var == NULL) { njs_generate_code(generator, njs_vmcode_prop_set_t, prop_set, NJS_VMCODE_PROPERTY_SET, node_src); prop_set->value = node_dst->index; prop_set->object = njs_scope_global_this_index(); lex_entry = njs_lexer_entry(node_dst->u.reference.unique_id); if (njs_slow_path(lex_entry == NULL)) { return NJS_ERROR; } ret = njs_string_create(vm, &property, lex_entry->name.start, lex_entry->name.length); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } prop_set->property = njs_scope_global_index(vm, &property, generator->runtime); if (njs_slow_path(prop_set->property == NJS_INDEX_ERROR)) { return NJS_ERROR; } } return NJS_OK; } static njs_int_t njs_generate_assignment(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_variable_t *var; njs_parser_node_t *lvalue, *expr; njs_vmcode_variable_t *var_code; lvalue = node->left; expr = node->right; expr->dest = NULL; if (lvalue->token_type == NJS_TOKEN_NAME) { ret = njs_generate_variable(vm, generator, lvalue, NJS_REFERENCE, &var); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (var != NULL && var->type == NJS_VARIABLE_CONST) { njs_generate_code(generator, njs_vmcode_variable_t, var_code, NJS_VMCODE_ASSIGNMENT_ERROR, node); var_code->dst = var->index; return njs_generator_stack_pop(vm, generator, NULL); } expr->dest = lvalue; njs_generator_next(generator, njs_generate, expr); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_assignment_name, NULL, 0); } /* lvalue->token == NJS_TOKEN_PROPERTY(_INIT) */ /* Object. */ njs_generator_next(generator, njs_generate, lvalue->left); ret = njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_assignment_prop, NULL, 0); if (njs_slow_path(ret != NJS_OK)) { return ret; } /* Property. */ return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), lvalue->right, njs_generate, NULL, 0); } static njs_int_t njs_generate_assignment_name(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_parser_node_t *lvalue, *expr; njs_vmcode_move_t *move; lvalue = node->left; expr = node->right; /* * 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_move(generator, move, lvalue->index, expr->index, expr); } node->index = expr->index; node->temporary = expr->temporary; ret = njs_generate_global_property_set(vm, generator, node->left, expr); if (njs_slow_path(ret != NJS_OK)) { return ret; } return njs_generator_stack_pop(vm, generator, NULL); } static njs_int_t njs_generate_assignment_prop(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_index_t index, src; njs_parser_node_t *lvalue, *expr, *object, *property; njs_vmcode_move_t *move; lvalue = node->left; expr = node->right; object = lvalue->left; property = lvalue->right; if (njs_slow_path(njs_parser_has_side_effect(expr))) { /* * Preserve object and property values stored in variables in a case * if the variables can be changed by side effects in expression. */ if (object->token_type == NJS_TOKEN_NAME) { src = object->index; index = njs_generate_node_temp_index_get(vm, generator, object); if (njs_slow_path(index == NJS_INDEX_ERROR)) { return NJS_ERROR; } njs_generate_code_move(generator, move, index, src, object); } if (property->token_type == NJS_TOKEN_NAME) { src = property->index; index = njs_generate_node_temp_index_get(vm, generator, property); if (njs_slow_path(index == NJS_INDEX_ERROR)) { return NJS_ERROR; } njs_generate_code_move(generator, move, index, src, property); } } njs_generator_next(generator, njs_generate, expr); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_assignment_end, NULL, 0); } static njs_int_t njs_generate_assignment_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_index_t prop_index; njs_parser_node_t *lvalue, *expr, *object, *property; njs_vmcode_2addr_t *set_function, *to_prop_key; njs_vmcode_prop_set_t *prop_set; lvalue = node->left; expr = node->right; object = lvalue->left; property = lvalue->right; prop_index = property->index; switch (lvalue->token_type) { case NJS_TOKEN_PROPERTY_INIT: if ((object->token_type == NJS_TOKEN_OBJECT || (object->token_type == NJS_TOKEN_OBJECT_VALUE && object->u.object->token_type == NJS_TOKEN_OBJECT)) && (expr->token_type == NJS_TOKEN_FUNCTION || expr->token_type == NJS_TOKEN_FUNCTION_EXPRESSION || expr->token_type == NJS_TOKEN_ASYNC_FUNCTION_EXPRESSION)) { if (property->token_type == NJS_TOKEN_STRING) { njs_value_assign(&expr->u.value.data.u.lambda->name, &property->u.value); } else { njs_generate_code(generator, njs_vmcode_2addr_t, to_prop_key, NJS_VMCODE_TO_PROPERTY_KEY, property); prop_index = njs_generate_temp_index_get(vm, generator, property); if (njs_slow_path(prop_index == NJS_INDEX_ERROR)) { return NJS_ERROR; } to_prop_key->src = property->index; to_prop_key->dst = prop_index; njs_generate_code(generator, njs_vmcode_2addr_t, set_function, NJS_VMCODE_SET_FUNCTION_NAME, expr); set_function->dst = expr->index; set_function->src = prop_index; } } njs_generate_code(generator, njs_vmcode_prop_set_t, prop_set, NJS_VMCODE_PROPERTY_INIT, expr); break; case NJS_TOKEN_PROTO_INIT: njs_generate_code(generator, njs_vmcode_prop_set_t, prop_set, NJS_VMCODE_PROTO_INIT, expr); break; default: /* NJS_VMCODE_PROPERTY_SET */ njs_generate_code(generator, njs_vmcode_prop_set_t, prop_set, NJS_VMCODE_PROPERTY_SET, expr); } prop_set->value = expr->index; prop_set->object = object->index; prop_set->property = prop_index; if (prop_index != property->index) { ret = njs_generate_index_release(vm, generator, prop_index); if (njs_slow_path(ret != NJS_OK)) { return ret; } } node->index = expr->index; node->temporary = expr->temporary; ret = njs_generate_children_indexes_release(vm, generator, lvalue); if (njs_slow_path(ret != NJS_OK)) { return ret; } return njs_generator_stack_pop(vm, generator, NULL); } static njs_int_t njs_generate_operation_assignment(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_index_t index; njs_variable_t *var; njs_parser_node_t *lvalue, *expr; njs_vmcode_move_t *move; njs_vmcode_variable_t *var_code; lvalue = node->left; if (lvalue->token_type == NJS_TOKEN_NAME) { ret = njs_generate_variable(vm, generator, lvalue, NJS_REFERENCE, &var); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (var != NULL && var->type == NJS_VARIABLE_CONST) { njs_generate_code(generator, njs_vmcode_variable_t, var_code, NJS_VMCODE_ASSIGNMENT_ERROR, node); var_code->dst = var->index; return njs_generator_stack_pop(vm, generator, NULL); } index = lvalue->index; expr = node->right; if (njs_slow_path(njs_parser_has_side_effect(expr))) { /* Preserve variable value if it may be changed by expression. */ njs_generate_code(generator, njs_vmcode_move_t, move, NJS_VMCODE_MOVE, expr); move->src = lvalue->index; index = njs_generate_temp_index_get(vm, generator, expr); if (njs_slow_path(index == NJS_INDEX_ERROR)) { return NJS_ERROR; } move->dst = index; } njs_generator_next(generator, njs_generate, expr); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_operation_assignment_name, &index, sizeof(njs_index_t)); } /* lvalue->token == NJS_TOKEN_PROPERTY */ /* Object. */ njs_generator_next(generator, njs_generate, lvalue->left); ret = njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_operation_assignment_prop, NULL, 0); if (njs_slow_path(ret != NJS_OK)) { return ret; } /* Property. */ return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), lvalue->right, njs_generate, NULL, 0); } static njs_int_t njs_generate_operation_assignment_name(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_index_t index; njs_parser_node_t *lvalue, *expr; njs_vmcode_3addr_t *code; lvalue = node->left; expr = node->right; index = *((njs_index_t *) generator->context); njs_generate_code(generator, njs_vmcode_3addr_t, code, node->u.operation, expr); code->dst = lvalue->index; code->src1 = index; code->src2 = expr->index; node->index = lvalue->index; ret = njs_generate_global_property_set(vm, generator, node->left, expr); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (lvalue->index != index) { ret = njs_generate_index_release(vm, generator, index); if (njs_slow_path(ret != NJS_OK)) { return ret; } } njs_mp_free(vm->mem_pool, generator->context); return njs_generate_node_index_release_pop(vm, generator, expr); } static njs_int_t njs_generate_operation_assignment_prop(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_index_t index, src, prop_index; njs_parser_node_t *lvalue, *object, *property; njs_vmcode_move_t *move; njs_vmcode_3addr_t *to_property_key; njs_vmcode_prop_get_t *prop_get; lvalue = node->left; object = lvalue->left; property = lvalue->right; if (njs_slow_path(njs_parser_has_side_effect(node->right))) { /* * Preserve object and property values stored in variables in a case * if the variables can be changed by side effects in expression. */ if (object->token_type == NJS_TOKEN_NAME) { src = object->index; index = njs_generate_node_temp_index_get(vm, generator, object); if (njs_slow_path(index == NJS_INDEX_ERROR)) { return NJS_ERROR; } njs_generate_code_move(generator, move, index, src, object); } if (property->token_type == NJS_TOKEN_NAME) { src = property->index; index = njs_generate_node_temp_index_get(vm, generator, property); if (njs_slow_path(index == NJS_INDEX_ERROR)) { return NJS_ERROR; } njs_generate_code_move(generator, move, index, src, property); } } prop_index = property->index; if (!njs_parser_is_primitive(property)) { prop_index = njs_generate_node_temp_index_get(vm, generator, node); if (njs_slow_path(prop_index == NJS_INDEX_ERROR)) { return NJS_ERROR; } njs_generate_code(generator, njs_vmcode_3addr_t, to_property_key, NJS_VMCODE_TO_PROPERTY_KEY_CHK, property); to_property_key->src2 = object->index; to_property_key->src1 = property->index; to_property_key->dst = prop_index; } index = njs_generate_node_temp_index_get(vm, generator, node); if (njs_slow_path(index == NJS_INDEX_ERROR)) { return NJS_ERROR; } njs_generate_code(generator, njs_vmcode_prop_get_t, prop_get, NJS_VMCODE_PROPERTY_GET, property); prop_get->value = index; prop_get->object = object->index; prop_get->property = prop_index; njs_generator_next(generator, njs_generate, node->right); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_operation_assignment_end, &prop_index, sizeof(njs_index_t)); } static njs_int_t njs_generate_operation_assignment_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_index_t prop_index; njs_parser_node_t *lvalue, *expr; njs_vmcode_3addr_t *code; njs_vmcode_prop_set_t *prop_set; lvalue = node->left; expr = node->right; prop_index = *((njs_index_t *) generator->context); njs_generate_code(generator, njs_vmcode_3addr_t, code, node->u.operation, expr); code->dst = node->index; code->src1 = node->index; code->src2 = expr->index; njs_generate_code(generator, njs_vmcode_prop_set_t, prop_set, NJS_VMCODE_PROPERTY_SET, expr); prop_set->value = node->index; prop_set->object = lvalue->left->index; prop_set->property = prop_index; ret = njs_generate_children_indexes_release(vm, generator, lvalue); if (njs_slow_path(ret != NJS_OK)) { return ret; } return njs_generate_node_index_release_pop(vm, generator, expr); } static njs_int_t njs_generate_object(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_vmcode_object_t *object; node->index = njs_generate_object_dest_index(vm, generator, node); if (njs_slow_path(node->index == NJS_INDEX_ERROR)) { return NJS_ERROR; } njs_generate_code(generator, njs_vmcode_object_t, object, NJS_VMCODE_OBJECT, node); object->retval = node->index; /* Initialize object. */ njs_generator_next(generator, njs_generate, node->left); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), NULL, njs_generator_pop, NULL, 0); } static njs_int_t njs_generate_property_accessor(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_generator_next(generator, njs_generate, node->left->left); ret = njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_property_accessor_end, NULL, 0); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node->right, njs_generate, NULL, 0); if (njs_slow_path(ret != NJS_OK)) { return ret; } return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node->left->right, njs_generate, NULL, 0); } static njs_int_t njs_generate_property_accessor_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_parser_node_t *lvalue, *function; njs_vmcode_prop_accessor_t *accessor; lvalue = node->left; function = node->right; njs_generate_code(generator, njs_vmcode_prop_accessor_t, accessor, NJS_VMCODE_PROPERTY_ACCESSOR, function); accessor->value = function->index; accessor->object = lvalue->left->index; accessor->property = lvalue->right->index; accessor->type = (node->token_type == NJS_TOKEN_PROPERTY_GETTER) ? NJS_OBJECT_PROP_GETTER : NJS_OBJECT_PROP_SETTER; return njs_generator_stack_pop(vm, generator, NULL); } static njs_int_t njs_generate_array(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_vmcode_array_t *array; node->index = njs_generate_object_dest_index(vm, generator, node); if (njs_slow_path(node->index == NJS_INDEX_ERROR)) { return NJS_ERROR; } njs_generate_code(generator, njs_vmcode_array_t, array, NJS_VMCODE_ARRAY, node); array->ctor = node->ctor; array->retval = node->index; array->length = node->u.length; /* Initialize array. */ njs_generator_next(generator, njs_generate, node->left); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), NULL, njs_generator_pop, NULL, 0); } static njs_int_t njs_generate_function_expression(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_variable_t *var; njs_function_lambda_t *lambda; njs_vmcode_function_t *function; const njs_lexer_entry_t *lex_entry; var = njs_variable_reference(vm, node->left); if (njs_slow_path(var == NULL)) { ret = njs_generate_reference_error(vm, generator, node->left); if (njs_slow_path(ret != NJS_OK)) { return ret; } return njs_generator_stack_pop(vm, generator, NULL); } lambda = node->u.value.data.u.lambda; lex_entry = njs_lexer_entry(var->unique_id); if (njs_slow_path(lex_entry == NULL)) { return NJS_ERROR; } ret = njs_generate_function_scope(vm, generator, lambda, node, &lex_entry->name); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_string_create(vm, &lambda->name, lex_entry->name.start, lex_entry->name.length); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_generate_code(generator, njs_vmcode_function_t, function, NJS_VMCODE_FUNCTION, node); function->lambda = lambda; function->async = (node->token_type == NJS_TOKEN_ASYNC_FUNCTION_EXPRESSION); node->index = njs_generate_object_dest_index(vm, generator, node); if (njs_slow_path(node->index == NJS_INDEX_ERROR)) { return NJS_ERROR; } function->retval = node->index; return njs_generator_stack_pop(vm, generator, NULL); } static njs_int_t njs_generate_function(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_function_lambda_t *lambda; njs_vmcode_function_t *function; lambda = node->u.value.data.u.lambda; ret = njs_generate_function_scope(vm, generator, lambda, node, &njs_entry_empty); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_generate_code(generator, njs_vmcode_function_t, function, NJS_VMCODE_FUNCTION, node); function->lambda = lambda; function->async = (node->token_type == NJS_TOKEN_ASYNC_FUNCTION); node->index = njs_generate_object_dest_index(vm, generator, node); if (njs_slow_path(node->index == NJS_INDEX_ERROR)) { return NJS_ERROR; } function->retval = node->index; return njs_generator_stack_pop(vm, generator, NULL); } static njs_int_t njs_generate_regexp(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_vmcode_regexp_t *regexp; node->index = njs_generate_object_dest_index(vm, generator, node); if (njs_slow_path(node->index == NJS_INDEX_ERROR)) { return NJS_ERROR; } njs_generate_code(generator, njs_vmcode_regexp_t, regexp, NJS_VMCODE_REGEXP, node); regexp->retval = node->index; regexp->pattern = node->u.value.data.u.data; return njs_generator_stack_pop(vm, generator, NULL); } static njs_int_t njs_generate_template_literal(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_generator_next(generator, njs_generate, node->left); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_template_literal_end, NULL, 0); } static njs_int_t njs_generate_template_literal_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_vmcode_template_literal_t *code; njs_generate_code(generator, njs_vmcode_template_literal_t, code, NJS_VMCODE_TEMPLATE_LITERAL, node); code->retval = node->left->index; node->index = node->left->index; return njs_generator_stack_pop(vm, generator, NULL); } static njs_int_t njs_generate_test_jump_expression(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_generator_next(generator, njs_generate, node->left); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_test_jump_expression_after, NULL, 0); } static njs_int_t njs_generate_test_jump_expression_after(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_jump_off_t jump_offset; njs_vmcode_test_jump_t *test_jump; njs_generate_code(generator, njs_vmcode_test_jump_t, test_jump, node->u.operation, node); jump_offset = njs_code_offset(generator, test_jump); test_jump->value = node->left->index; node->index = njs_generate_node_temp_index_get(vm, generator, node); if (njs_slow_path(node->index == NJS_INDEX_ERROR)) { return node->index; } test_jump->retval = node->index; njs_generator_next(generator, njs_generate, node->right); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_test_jump_expression_end, &jump_offset, sizeof(njs_jump_off_t)); } static njs_int_t njs_generate_test_jump_expression_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_vmcode_move_t *move; /* * The right expression usually uses node->index as destination, * however, if the expression is a literal, variable or assignment, * then a MOVE operation is required. */ if (node->index != node->right->index) { njs_generate_code_move(generator, move, node->index, node->right->index, node); } njs_code_set_jump_offset(generator, njs_vmcode_test_jump_t, *((njs_jump_off_t *) generator->context)); ret = njs_generate_children_indexes_release(vm, generator, node); if (njs_slow_path(ret != NJS_OK)) { return ret; } return njs_generator_stack_pop(vm, generator, generator->context); } static njs_int_t njs_generate_3addr_operation(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node, njs_bool_t swap) { njs_int_t ret; njs_parser_node_t *left, *right; left = node->left; right = node->right; njs_generator_next(generator, njs_generate, left); if (left->token_type == NJS_TOKEN_NAME) { return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_3addr_operation_name, &swap, sizeof(njs_bool_t)); } ret = njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_3addr_operation_end, &swap, sizeof(njs_bool_t)); if (njs_slow_path(ret != NJS_OK)) { return ret; } return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), right, njs_generate, NULL, 0); } static njs_int_t njs_generate_3addr_operation_name(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_index_t index; njs_parser_node_t *left; njs_vmcode_move_t *move; left = node->left; if (njs_slow_path(njs_parser_has_side_effect(node->right))) { njs_generate_code(generator, njs_vmcode_move_t, move, NJS_VMCODE_MOVE, node); move->src = left->index; index = njs_generate_node_temp_index_get(vm, generator, left); if (njs_slow_path(index == NJS_INDEX_ERROR)) { return NJS_ERROR; } move->dst = index; } njs_generator_next(generator, njs_generate, node->right); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_3addr_operation_end, generator->context, 0); } static njs_int_t njs_generate_3addr_operation_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_bool_t swap; njs_parser_node_t *left, *right; njs_vmcode_3addr_t *code; left = node->left; right = node->right; njs_generate_code(generator, njs_vmcode_3addr_t, code, node->u.operation, node); swap = *((njs_bool_t *) generator->context); if (!swap) { code->src1 = left->index; code->src2 = right->index; } else { code->src1 = right->index; code->src2 = left->index; } /* * The temporary index of MOVE destination * will be released here as index of node->left. */ node->index = njs_generate_dest_index(vm, generator, node); if (njs_slow_path(node->index == NJS_INDEX_ERROR)) { return node->index; } code->dst = node->index; njs_debug_generator_code(code); return njs_generator_stack_pop(vm, generator, generator->context); } static njs_int_t njs_generate_2addr_operation(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_generator_next(generator, njs_generate, node->left); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_2addr_operation_end, NULL, 0); } static njs_int_t njs_generate_2addr_operation_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_vmcode_2addr_t *code; njs_generate_code(generator, njs_vmcode_2addr_t, code, node->u.operation, node); code->src = node->left->index; node->index = njs_generate_dest_index(vm, generator, node); if (njs_slow_path(node->index == NJS_INDEX_ERROR)) { return node->index; } code->dst = node->index; njs_debug_generator_code(code); return njs_generator_stack_pop(vm, generator, NULL); } static njs_int_t njs_generate_typeof_operation(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_parser_node_t *expr; expr = node->left; if (expr->token_type != NJS_TOKEN_NAME) { njs_generator_next(generator, njs_generate, node->left); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_typeof_operation_end, NULL, 0); } ret = njs_generate_variable(vm, generator, expr, NJS_TYPEOF, NULL); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } return njs_generate_typeof_operation_end(vm, generator, node); } static njs_int_t njs_generate_typeof_operation_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_vmcode_2addr_t *code; njs_generate_code(generator, njs_vmcode_2addr_t, code, node->u.operation, node->left); code->src = node->left->index; node->index = njs_generate_dest_index(vm, generator, node); if (njs_slow_path(node->index == NJS_INDEX_ERROR)) { return node->index; } code->dst = node->index; njs_debug_generator_code(code); return njs_generator_stack_pop(vm, generator, NULL); } static njs_int_t njs_generate_inc_dec_operation(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node, njs_bool_t post) { njs_int_t ret; njs_index_t index; njs_variable_t *var; njs_parser_node_t *lvalue; njs_vmcode_3addr_t *code; njs_vmcode_variable_t *var_code; lvalue = node->left; if (lvalue->token_type == NJS_TOKEN_NAME) { ret = njs_generate_variable(vm, generator, lvalue, NJS_REFERENCE, &var); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (var != NULL && var->type == NJS_VARIABLE_CONST) { njs_generate_code(generator, njs_vmcode_variable_t, var_code, NJS_VMCODE_ASSIGNMENT_ERROR, node); var_code->dst = var->index; return njs_generator_stack_pop(vm, generator, NULL); } index = njs_generate_dest_index(vm, generator, node); if (njs_slow_path(index == NJS_INDEX_ERROR)) { return index; } node->index = index; njs_generate_code(generator, njs_vmcode_3addr_t, code, node->u.operation, node); code->dst = index; code->src1 = lvalue->index; code->src2 = lvalue->index; ret = njs_generate_global_property_set(vm, generator, lvalue, lvalue); if (njs_slow_path(ret) != NJS_OK) { return ret; } return njs_generator_stack_pop(vm, generator, NULL); } /* lvalue->token == NJS_TOKEN_PROPERTY */ /* Object. */ njs_generator_next(generator, njs_generate, lvalue->left); ret = njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_inc_dec_operation_prop, &post, sizeof(njs_bool_t)); if (njs_slow_path(ret != NJS_OK)) { return ret; } /* Property. */ return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), lvalue->right, njs_generate, NULL, 0); } static njs_int_t njs_generate_inc_dec_operation_prop(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_bool_t post; njs_index_t index, dest_index, prop_index; njs_parser_node_t *lvalue; njs_vmcode_3addr_t *code, *to_property_key; njs_vmcode_prop_get_t *prop_get; njs_vmcode_prop_set_t *prop_set; lvalue = node->left; if (node->dest != NULL) { dest_index = node->dest->index; if (dest_index != NJS_INDEX_NONE && dest_index != lvalue->left->index && dest_index != lvalue->right->index) { node->index = dest_index; goto found; } } dest_index = njs_generate_node_temp_index_get(vm, generator, node); found: prop_index = lvalue->right->index; if (!njs_parser_is_primitive(lvalue->right)) { prop_index = njs_generate_temp_index_get(vm, generator, node); if (njs_slow_path(prop_index == NJS_INDEX_ERROR)) { return NJS_ERROR; } njs_generate_code(generator, njs_vmcode_3addr_t, to_property_key, NJS_VMCODE_TO_PROPERTY_KEY_CHK, node); to_property_key->src2 = lvalue->left->index; to_property_key->src1 = lvalue->right->index; to_property_key->dst = prop_index; } post = *((njs_bool_t *) generator->context); index = post ? njs_generate_temp_index_get(vm, generator, node) : dest_index; if (njs_slow_path(index == NJS_INDEX_ERROR)) { return NJS_ERROR; } njs_generate_code(generator, njs_vmcode_prop_get_t, prop_get, NJS_VMCODE_PROPERTY_GET, node); prop_get->value = index; prop_get->object = lvalue->left->index; prop_get->property = prop_index; njs_generate_code(generator, njs_vmcode_3addr_t, code, node->u.operation, node); code->dst = dest_index; code->src1 = index; code->src2 = index; njs_generate_code(generator, njs_vmcode_prop_set_t, prop_set, NJS_VMCODE_PROPERTY_SET, node); prop_set->value = index; prop_set->object = lvalue->left->index; prop_set->property = prop_index; if (post) { ret = njs_generate_index_release(vm, generator, index); if (njs_slow_path(ret != NJS_OK)) { return ret; } } njs_mp_free(vm->mem_pool, generator->context); ret = njs_generate_children_indexes_release(vm, generator, lvalue); if (njs_slow_path(ret != NJS_OK)) { return ret; } return njs_generator_stack_pop(vm, generator, NULL); } static njs_int_t njs_generate_function_declaration(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_bool_t async; njs_variable_t *var; njs_function_t *function; njs_function_lambda_t *lambda; const njs_lexer_entry_t *lex_entry; var = njs_variable_reference(vm, node); if (njs_slow_path(var == NULL)) { ret = njs_generate_reference_error(vm, generator, node); if (njs_slow_path(ret != NJS_OK)) { return ret; } return njs_generator_stack_pop(vm, generator, NULL); } lambda = njs_variable_lambda(var); lex_entry = njs_lexer_entry(node->u.reference.unique_id); if (njs_slow_path(lex_entry == NULL)) { return NJS_ERROR; } ret = njs_string_create(vm, &lambda->name, lex_entry->name.start, lex_entry->name.length); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_generate_function_scope(vm, generator, lambda, node, &lex_entry->name); if (njs_slow_path(ret != NJS_OK)) { return ret; } async = (node->token_type == NJS_TOKEN_ASYNC_FUNCTION_DECLARATION); function = njs_function_alloc(vm, lambda, async); if (njs_slow_path(function == NULL)) { return NJS_ERROR; } function->global = njs_function_scope(var->scope)->type == NJS_SCOPE_GLOBAL; function->object.shared = 1; function->args_count = lambda->nargs - lambda->rest_parameters; njs_set_function(&var->value, function); return njs_generator_stack_pop(vm, generator, NULL); } static njs_int_t njs_generate_function_scope(njs_vm_t *vm, njs_generator_t *prev, njs_function_lambda_t *lambda, njs_parser_node_t *node, const njs_str_t *name) { njs_int_t ret; njs_arr_t *arr; njs_uint_t depth; njs_vm_code_t *code; njs_generator_t generator; depth = prev->depth; if (++depth >= NJS_FUNCTION_MAX_DEPTH) { njs_range_error(vm, "Maximum function nesting depth exceeded"); return NJS_ERROR; } ret = njs_generator_init(&generator, &prev->file, depth, prev->runtime); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "njs_generator_init() failed"); return NJS_ERROR; } node = node->right; code = njs_generate_scope(vm, &generator, node->scope, name); if (njs_slow_path(code == NULL)) { if (!njs_is_error(&vm->exception)) { njs_internal_error(vm, "njs_generate_scope() failed"); } return NJS_ERROR; } lambda->start = generator.code_start; lambda->closures = generator.closures->start; lambda->nclosures = generator.closures->items; lambda->nlocal = node->scope->items; arr = node->scope->declarations; lambda->declarations = (arr != NULL) ? arr->start : NULL; lambda->ndeclarations = (arr != NULL) ? arr->items : 0; return NJS_OK; } njs_vm_code_t * njs_generate_scope(njs_vm_t *vm, njs_generator_t *generator, njs_parser_scope_t *scope, const njs_str_t *name) { u_char *p; int64_t nargs; njs_int_t ret; njs_uint_t index; njs_vm_code_t *code; generator->code_size = 128; p = njs_mp_alloc(vm->mem_pool, generator->code_size); if (njs_slow_path(p == NULL)) { njs_memory_error(vm); return NULL; } generator->code_start = p; generator->code_end = p; nargs = njs_generate_lambda_variables(vm, generator, scope->top); if (njs_slow_path(nargs < NJS_OK)) { return NULL; } if (vm->codes == NULL) { vm->codes = njs_arr_create(vm->mem_pool, 4, sizeof(njs_vm_code_t)); if (njs_slow_path(vm->codes == NULL)) { return NULL; } } index = vm->codes->items; code = njs_arr_add(vm->codes); if (njs_slow_path(code == NULL)) { njs_memory_error(vm); return NULL; } code->lines = NULL; if (vm->options.backtrace) { code->lines = njs_arr_create(vm->mem_pool, 4, sizeof(njs_vm_line_num_t)); if (njs_slow_path(code->lines == NULL)) { njs_memory_error(vm); return NULL; } generator->lines = code->lines; } generator->closures = njs_arr_create(vm->mem_pool, 4, sizeof(njs_index_t)); if (njs_slow_path(generator->closures == NULL)) { return NULL; } scope->closures = generator->closures; njs_queue_init(&generator->stack); njs_generator_next(generator, njs_generate, scope->top); ret = njs_generator_after(vm, generator, njs_queue_first(&generator->stack), NULL, njs_generate_scope_end, NULL, 0); if (njs_slow_path(ret != NJS_OK)) { return NULL; } do { ret = generator->state(vm, generator, generator->node); if (njs_slow_path(ret != NJS_OK)) { return NULL; } } while (generator->state != NULL); code = njs_arr_item(vm->codes, index); code->start = generator->code_start; code->end = generator->code_end; code->file = generator->file; code->name = *name; generator->code_size = generator->code_end - generator->code_start; return code; } static njs_int_t njs_generate_scope_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { generator->state = NULL; return NJS_OK; } static int64_t njs_generate_lambda_variables(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { int64_t nargs; njs_variable_t *var; njs_rbtree_node_t *rb_node; njs_variable_node_t *var_node; njs_vmcode_arguments_t *arguments; nargs = 0; rb_node = njs_rbtree_min(&node->scope->variables); while (njs_rbtree_is_there_successor(&node->scope->variables, rb_node)) { var_node = (njs_variable_node_t *) rb_node; var = var_node->variable; if (var == NULL) { break; } if (var->argument) { nargs++; } if (var->arguments_object) { njs_generate_code(generator, njs_vmcode_arguments_t, arguments, NJS_VMCODE_ARGUMENTS, NULL); arguments->dst = var->index; } rb_node = njs_rbtree_node_successor(&node->scope->variables, rb_node); } return nargs; } static njs_int_t njs_generate_return_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_generator_next(generator, njs_generate, node->right); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_return_statement_end, NULL, 0); } static njs_int_t njs_generate_return_statement_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_index_t index; const njs_str_t *dest; njs_vmcode_return_t *code; njs_generator_patch_t *patch; njs_generator_block_t *block, *immediate, *top; njs_vmcode_try_return_t *try_return; if (node->right != NULL) { index = node->right->index; } else { index = njs_scope_global_index(vm, &njs_value_undefined, generator->runtime); } if (njs_slow_path(index == NJS_INDEX_ERROR)) { return NJS_ERROR; } immediate = njs_generate_lookup_block(generator->block, NJS_GENERATOR_TRY, &no_label); njs_debug_generator(vm, "LOOKUP TRY %p", immediate); if (njs_fast_path(immediate == NULL)) { njs_generate_code(generator, njs_vmcode_return_t, code, NJS_VMCODE_RETURN, node); code->retval = index; node->index = index; return njs_generator_stack_pop(vm, generator, NULL); } if (immediate->type == NJS_GENERATOR_TRY && immediate->exit != NULL) { dest = njs_generate_jump_destination(vm, immediate->next, "break/return", NJS_GENERATOR_ALL, &immediate->exit->label, &return_label); if (njs_slow_path(dest == NULL)) { return NJS_ERROR; } } top = immediate; block = immediate->next; while (block != NULL) { if (block->type & NJS_GENERATOR_TRY) { top = block; } block = block->next; } njs_generate_code(generator, njs_vmcode_try_return_t, try_return, NJS_VMCODE_TRY_RETURN, node); try_return->retval = index; try_return->save = top->index; try_return->offset = offsetof(njs_vmcode_try_return_t, offset); patch = njs_generate_make_exit_patch(vm, immediate, &return_label, njs_code_offset(generator, try_return) + offsetof(njs_vmcode_try_return_t, offset)); if (njs_slow_path(patch == NULL)) { return NJS_ERROR; } return njs_generator_stack_pop(vm, generator, NULL); } static njs_int_t njs_generate_function_call(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_variable_t *var; var = NULL; if (node->left != NULL) { /* Generate function code in function expression. */ njs_generator_next(generator, njs_generate, node->left); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_function_call_arguments, NULL, 0); } ret = njs_generate_variable(vm, generator, node, NJS_REFERENCE, &var); if (njs_slow_path(ret != NJS_OK)) { return ret; } return njs_generate_function_call_arguments(vm, generator, node); } static njs_int_t njs_generate_function_call_arguments(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_jump_off_t func_offset; njs_parser_node_t *name; njs_vmcode_function_frame_t *func; name = node; if (node->left != NULL) { name = node->left; } njs_generate_code(generator, njs_vmcode_function_frame_t, func, NJS_VMCODE_FUNCTION_FRAME, node); func_offset = njs_code_offset(generator, func); func->ctor = node->ctor; func->name = name->index; func->nargs = 0; njs_generator_next(generator, njs_generate, (node->right != NULL ? node->right->left : NULL)); ret = njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_function_call_end, NULL, 0); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (node->right == NULL) { return NJS_OK; } return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node->right, njs_generate_move_arguments, &func_offset, sizeof(njs_jump_off_t)); } static njs_int_t njs_generate_function_call_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; ret = njs_generate_call(vm, generator, node); if (njs_fast_path(ret != NJS_OK)) { return ret; } return njs_generator_stack_pop(vm, generator, generator->context); } static njs_int_t njs_generate_method_call(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_parser_node_t *prop; prop = node->left; /* Object. */ njs_generator_next(generator, njs_generate, prop->left); ret = njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_method_call_arguments, NULL, 0); if (njs_slow_path(ret != NJS_OK)) { return ret; } /* Method name. */ return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), prop->right, njs_generate, NULL, 0); } static njs_int_t njs_generate_method_call_arguments(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_jump_off_t method_offset; njs_parser_node_t *prop; njs_vmcode_method_frame_t *method; prop = node->left; njs_generate_code(generator, njs_vmcode_method_frame_t, method, NJS_VMCODE_METHOD_FRAME, prop); method_offset = njs_code_offset(generator, method); method->ctor = node->ctor; method->object = prop->left->index; method->method = prop->right->index; method->nargs = 0; njs_generator_next(generator, njs_generate, (node->right != NULL ? node->right->left : node->right)); ret = njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_method_call_end, NULL, 0); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (node->right == NULL) { return NJS_OK; } return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node->right, njs_generate_move_arguments, &method_offset, sizeof(njs_jump_off_t)); } static njs_int_t njs_generate_method_call_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; ret = njs_generate_call(vm, generator, node); if (njs_fast_path(ret != NJS_OK)) { return ret; } return njs_generator_stack_pop(vm, generator, generator->context); } static njs_int_t njs_generate_call(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_index_t retval; njs_vmcode_function_call_t *call; retval = njs_generate_dest_index(vm, generator, node); if (njs_slow_path(retval == NJS_INDEX_ERROR)) { return retval; } node->index = retval; njs_generate_code(generator, njs_vmcode_function_call_t, call, NJS_VMCODE_FUNCTION_CALL, node); call->retval = retval; return NJS_OK; } static njs_int_t njs_generate_move_arguments(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_jump_off_t func_offset; njs_vmcode_1addr_t *put_arg; njs_vmcode_function_frame_t *func; if (node == NULL) { return njs_generator_stack_pop(vm, generator, generator->context); } njs_generate_code(generator, njs_vmcode_1addr_t, put_arg, NJS_VMCODE_PUT_ARG, node); put_arg->index = node->left->index; func_offset = *((njs_jump_off_t *) generator->context); func = njs_code_ptr(generator, njs_vmcode_function_frame_t, func_offset); func->nargs++; if (node->right == NULL) { return njs_generator_stack_pop(vm, generator, generator->context); } njs_generator_next(generator, njs_generate, node->right->left); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node->right, njs_generate_move_arguments, generator->context, 0); } #define njs_generate_code_catch(generator, _code, _exception, node) \ do { \ njs_generate_code(generator, njs_vmcode_catch_t, _code, \ NJS_VMCODE_CATCH, node); \ _code->offset = sizeof(njs_vmcode_catch_t); \ _code->exception = _exception; \ } while (0) #define njs_generate_code_finally(generator, _code, _retval, _exit, node) \ do { \ njs_generate_code(generator, njs_vmcode_finally_t, _code, \ NJS_VMCODE_FINALLY, node); \ _code->retval = _retval; \ _code->exit_value = _exit; \ _code->continue_offset = offsetof(njs_vmcode_finally_t, \ continue_offset); \ _code->break_offset = offsetof(njs_vmcode_finally_t, \ break_offset); \ } while (0) static njs_int_t njs_generate_try_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_index_t exception_index, exit_index; njs_vmcode_try_start_t *try_start; njs_generator_try_ctx_t ctx; njs_memzero(&ctx, sizeof(njs_generator_try_ctx_t)); njs_generate_code(generator, njs_vmcode_try_start_t, try_start, NJS_VMCODE_TRY_START, node); ctx.try_offset = njs_code_offset(generator, try_start); exception_index = njs_generate_temp_index_get(vm, generator, node); if (njs_slow_path(exception_index == NJS_INDEX_ERROR)) { return NJS_ERROR; } try_start->exception_value = exception_index; /* * exit_value is used in njs_vmcode_finally to make a decision * which way to go after "break", "continue" and "return" instruction * inside "try" or "catch" blocks. */ exit_index = njs_generate_temp_index_get(vm, generator, node); if (njs_slow_path(exit_index == NJS_INDEX_ERROR)) { return NJS_ERROR; } try_start->exit_value = exit_index; ret = njs_generate_start_block(vm, generator, NJS_GENERATOR_TRY, &no_label); if (njs_slow_path(ret != NJS_OK)) { return ret; } ctx.try_block = generator->block; ctx.try_block->index = exit_index; ctx.exception_index = exception_index; ctx.catch_cont_label = undef_label; ctx.catch_exit_label = undef_label; ctx.try_cont_label = undef_label; ctx.try_exit_label = undef_label; njs_generator_next(generator, njs_generate, node->left); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_try_left, &ctx, sizeof(njs_generator_try_ctx_t)); } static njs_int_t njs_generate_try_left(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_index_t exit_index, catch_index; njs_jump_off_t try_end_offset; njs_variable_t *var; njs_vmcode_catch_t *catch; njs_vmcode_try_end_t *try_end; njs_generator_block_t *try_block; njs_generator_try_ctx_t *ctx; njs_vmcode_try_trampoline_t *try_break, *try_continue; ctx = generator->context; try_block = ctx->try_block; exit_index = try_block->index; njs_generate_code(generator, njs_vmcode_try_end_t, try_end, NJS_VMCODE_TRY_END, NULL); try_end_offset = njs_code_offset(generator, try_end); if (try_block->exit != NULL) { ctx->try_exit_label = try_block->exit->label; njs_debug_generator(vm, "TRY CTX %p EXIT LABEL %V", ctx, &ctx->try_exit_label); njs_generate_patch_block(vm, generator, try_block, NJS_GENERATOR_EXIT); njs_generate_code(generator, njs_vmcode_try_trampoline_t, try_break, NJS_VMCODE_TRY_BREAK, NULL); try_break->exit_value = exit_index; try_break->offset = -sizeof(njs_vmcode_try_end_t); } else { try_break = NULL; } if (try_block->continuation != NULL) { ctx->try_cont_label = try_block->continuation->label; njs_generate_patch_block(vm, generator, try_block, NJS_GENERATOR_CONTINUATION); njs_generate_code(generator, njs_vmcode_try_trampoline_t, try_continue, NJS_VMCODE_TRY_CONTINUE, NULL); try_continue->exit_value = exit_index; try_continue->offset = -sizeof(njs_vmcode_try_end_t); if (try_break != NULL) { try_continue->offset -= sizeof(njs_vmcode_try_trampoline_t); } } njs_debug_generator(vm, "EXIT %s %p", njs_block_type(generator->block->type), generator->block); generator->block = try_block->next; njs_code_set_jump_offset(generator, njs_vmcode_try_start_t, ctx->try_offset); ctx->try_offset = try_end_offset; node = node->right; if (node->token_type == NJS_TOKEN_CATCH) { /* A "try/catch" case. */ var = njs_variable_reference(vm, node->left); if (njs_slow_path(var == NULL)) { return NJS_ERROR; } catch_index = node->left->index; njs_generate_code_catch(generator, catch, catch_index, node); njs_generator_next(generator, njs_generate, node->right); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_try_catch, ctx, 0); } if (node->left != NULL) { /* A try/catch/finally case. */ var = njs_variable_reference(vm, node->left->left); if (njs_slow_path(var == NULL)) { return NJS_ERROR; } catch_index = node->left->left->index; njs_generate_code_catch(generator, catch, catch_index, node); ctx->catch_offset = njs_code_offset(generator, catch); ret = njs_generate_start_block(vm, generator, NJS_GENERATOR_TRY, &no_label); if (njs_slow_path(ret != NJS_OK)) { return ret; } ctx->catch_block = generator->block; ctx->catch_block->index = exit_index; njs_generator_next(generator, njs_generate, node->left->right); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_try_finally, ctx, 0); } /* A try/finally case. */ njs_generate_code_catch(generator, catch, ctx->exception_index, NULL); ctx->catch_block = NULL; njs_code_set_jump_offset(generator, njs_vmcode_try_end_t, ctx->try_offset); njs_generator_next(generator, njs_generate, node->right); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_try_end, ctx, 0); } static njs_int_t njs_generate_try_catch(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_index_t exit_index; njs_vmcode_finally_t *finally; njs_generator_patch_t *patch; njs_generator_block_t *block, *try_block; njs_generator_try_ctx_t *ctx; ctx = generator->context; try_block = ctx->try_block; exit_index = try_block->index; njs_code_set_jump_offset(generator, njs_vmcode_try_end_t, ctx->try_offset); if (try_block->continuation != NULL || try_block->exit != NULL) { njs_generate_code_finally(generator, finally, ctx->exception_index, exit_index, NULL); if (try_block->continuation != NULL) { /* * block != NULL is checked * by njs_generate_continue_statement() */ block = njs_generate_find_block(vm, generator->block, NJS_GENERATOR_LOOP, &ctx->try_cont_label); patch = njs_generate_make_continuation_patch(vm, block, &ctx->try_cont_label, njs_code_offset(generator, finally) + offsetof(njs_vmcode_finally_t, continue_offset)); if (njs_slow_path(patch == NULL)) { return NJS_ERROR; } } if (try_block->exit != NULL) { block = njs_generate_find_block(vm, generator->block, NJS_GENERATOR_ALL, &ctx->try_exit_label); /* * block can be NULL when &ctx->try_exit_label is "@return" * for outermost try-catch block. */ if (block != NULL) { patch = njs_generate_make_exit_patch(vm, block, &ctx->try_exit_label, njs_code_offset(generator, finally) + offsetof(njs_vmcode_finally_t, break_offset)); if (njs_slow_path(patch == NULL)) { return NJS_ERROR; } } else { /* * when block == NULL, we still want to patch the "finally" * instruction break_offset. */ block = njs_generate_find_block(vm, generator->block, NJS_GENERATOR_ALL, &no_label); if (block != NULL) { patch = njs_generate_make_exit_patch(vm, block, &no_label, njs_code_offset(generator, finally) + offsetof(njs_vmcode_finally_t, break_offset)); if (njs_slow_path(patch == NULL)) { return NJS_ERROR; } } } } } /* TODO: release exception variable index. */ ret = njs_generate_index_release(vm, generator, ctx->exception_index); if (njs_slow_path(ret != NJS_OK)) { return ret; } return njs_generator_stack_pop(vm, generator, ctx); } static njs_int_t njs_generate_try_finally(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_index_t exit_index; njs_jump_off_t catch_end_offset; njs_vmcode_catch_t *catch; njs_vmcode_try_end_t *catch_end; njs_generator_block_t *try_block, *catch_block; njs_generator_try_ctx_t *ctx; njs_vmcode_try_trampoline_t *try_break, *try_continue; ctx = generator->context; try_block = ctx->try_block; exit_index = try_block->index; catch_block = ctx->catch_block; njs_generate_code(generator, njs_vmcode_try_end_t, catch_end, NJS_VMCODE_TRY_END, node->left->right); catch_end_offset = njs_code_offset(generator, catch_end); if (catch_block->exit != NULL) { ctx->catch_exit_label = catch_block->exit->label; njs_generate_patch_block(vm, generator, catch_block, NJS_GENERATOR_EXIT); njs_generate_code(generator, njs_vmcode_try_trampoline_t, try_break, NJS_VMCODE_TRY_BREAK, NULL); try_break->exit_value = exit_index; try_break->offset = -sizeof(njs_vmcode_try_end_t); } else { try_break = NULL; } if (catch_block->continuation != NULL) { ctx->catch_cont_label = catch_block->continuation->label; njs_generate_patch_block(vm, generator, catch_block, NJS_GENERATOR_CONTINUATION); njs_generate_code(generator, njs_vmcode_try_trampoline_t, try_continue, NJS_VMCODE_TRY_CONTINUE, NULL); try_continue->exit_value = exit_index; try_continue->offset = -sizeof(njs_vmcode_try_end_t); if (try_break != NULL) { try_continue->offset -= sizeof(njs_vmcode_try_trampoline_t); } } njs_debug_generator(vm, "EXIT %s %p", njs_block_type(generator->block->type), generator->block); generator->block = catch_block->next; njs_code_set_jump_offset(generator, njs_vmcode_catch_t, ctx->catch_offset); /* TODO: release exception variable index. */ njs_generate_code_catch(generator, catch, ctx->exception_index, NULL); njs_code_set_jump_offset(generator, njs_vmcode_try_end_t, catch_end_offset); njs_code_set_jump_offset(generator, njs_vmcode_try_end_t, ctx->try_offset); njs_generator_next(generator, njs_generate, node->right); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_try_end, ctx, 0); } static njs_int_t njs_generate_try_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; njs_index_t exit_index; const njs_str_t *dest_label; njs_vmcode_finally_t *finally; njs_generator_patch_t *patch; njs_generator_block_t *block, *try_block, *catch_block; njs_generator_try_ctx_t *ctx; ctx = generator->context; try_block = ctx->try_block; exit_index = try_block->index; catch_block = ctx->catch_block; njs_generate_code_finally(generator, finally, ctx->exception_index, exit_index, node); if (try_block->continuation != NULL || (catch_block && catch_block->continuation != NULL)) { dest_label = njs_generate_jump_destination(vm, generator->block, "try continue", NJS_GENERATOR_LOOP, &ctx->try_cont_label, &ctx->catch_cont_label); if (njs_slow_path(dest_label == NULL)) { return NJS_ERROR; } /* * block != NULL is checked * by njs_generate_continue_statement() */ block = njs_generate_find_block(vm, generator->block, NJS_GENERATOR_LOOP, dest_label); patch = njs_generate_make_continuation_patch(vm, block, dest_label, njs_code_offset(generator, finally) + offsetof(njs_vmcode_finally_t, continue_offset)); if (njs_slow_path(patch == NULL)) { return NJS_ERROR; } } if (try_block->exit != NULL || (catch_block != NULL && catch_block->exit != NULL)) { dest_label = njs_generate_jump_destination(vm, generator->block, "try break/return", NJS_GENERATOR_ALL | NJS_GENERATOR_TRY, &ctx->try_exit_label, &ctx->catch_exit_label); if (njs_slow_path(dest_label == NULL)) { return NJS_ERROR; } /* * block can be NULL for "return" instruction in * outermost try-catch block. */ block = njs_generate_find_block(vm, generator->block, NJS_GENERATOR_ALL, dest_label); if (block != NULL) { patch = njs_generate_make_exit_patch(vm, block, dest_label, njs_code_offset(generator, finally) + offsetof(njs_vmcode_finally_t, break_offset)); if (njs_slow_path(patch == NULL)) { return NJS_ERROR; } } else { block = njs_generate_find_block(vm, generator->block, NJS_GENERATOR_ALL, &no_label); if (block != NULL) { patch = njs_generate_make_exit_patch(vm, block, &no_label, njs_code_offset(generator, finally) + offsetof(njs_vmcode_finally_t, break_offset)); if (njs_slow_path(patch == NULL)) { return NJS_ERROR; } } } } ret = njs_generate_index_release(vm, generator, ctx->exception_index); if (njs_slow_path(ret != NJS_OK)) { return ret; } return njs_generator_stack_pop(vm, generator, ctx); } static njs_int_t njs_generate_throw_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_generator_next(generator, njs_generate, node->right); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_throw_end, NULL, 0); } static njs_int_t njs_generate_throw_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_vmcode_throw_t *throw; njs_generate_code(generator, njs_vmcode_throw_t, throw, NJS_VMCODE_THROW, node); node->index = node->right->index; throw->retval = node->index; return njs_generator_stack_pop(vm, generator, NULL); } static njs_int_t njs_generate_import_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_variable_t *var; njs_parser_node_t *lvalue; njs_vmcode_import_t *import; lvalue = node->left; var = njs_variable_reference(vm, lvalue); if (njs_slow_path(var == NULL)) { return NJS_ERROR; } njs_generate_code(generator, njs_vmcode_import_t, import, NJS_VMCODE_IMPORT, node); import->module = node->u.module; import->retval = lvalue->index; return njs_generator_stack_pop(vm, generator, NULL); } static njs_int_t njs_generate_export_statement(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_generator_next(generator, njs_generate, node->right); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_export_statement_end, NULL, 0); } static njs_int_t njs_generate_export_statement_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_parser_node_t *obj; njs_vmcode_return_t *code; obj = node->right; njs_generate_code(generator, njs_vmcode_return_t, code, NJS_VMCODE_RETURN, NULL); code->retval = obj->index; node->index = obj->index; return njs_generator_stack_pop(vm, generator, NULL); } static njs_int_t njs_generate_await(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_generator_next(generator, njs_generate, node->right); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_await_end, NULL, 0); } static njs_int_t njs_generate_await_end(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_index_t index; njs_vmcode_await_t *code; index = node->right->index; if (njs_slow_path(index == NJS_INDEX_ERROR)) { return NJS_ERROR; } njs_generate_code(generator, njs_vmcode_await_t, code, NJS_VMCODE_AWAIT, node); code->retval = index; node->index = index; return njs_generator_stack_pop(vm, generator, NULL); } static njs_int_t njs_generate_wo_dest(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_parser_scope_t *scope; scope = njs_function_scope(node->scope); scope->dest_disable = 1; njs_generator_next(generator, njs_generate, node); return njs_generator_after(vm, generator, njs_queue_first(&generator->stack), node, njs_generate_wo_dest_after, NULL, 0); } static njs_int_t njs_generate_wo_dest_after(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_parser_scope_t *scope; scope = njs_function_scope(node->scope); scope->dest_disable = 0; return njs_generator_stack_pop(vm, generator, NULL); } static njs_int_t njs_generate_global_reference(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node, njs_bool_t exception) { njs_int_t ret; njs_index_t index; njs_value_t property; njs_vmcode_prop_get_t *prop_get; const njs_lexer_entry_t *lex_entry; index = njs_generate_temp_index_get(vm, generator, node); if (njs_slow_path(index == NJS_INDEX_ERROR)) { return NJS_ERROR; } njs_generate_code(generator, njs_vmcode_prop_get_t, prop_get, exception ? NJS_VMCODE_GLOBAL_GET: NJS_VMCODE_PROPERTY_GET, node); prop_get->value = index; prop_get->object = njs_scope_global_this_index(); if (njs_slow_path(prop_get->object == NJS_INDEX_ERROR)) { return NJS_ERROR; } lex_entry = njs_lexer_entry(node->u.reference.unique_id); if (njs_slow_path(lex_entry == NULL)) { return NJS_ERROR; } ret = njs_string_create(vm, &property, lex_entry->name.start, lex_entry->name.length); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } prop_get->property = njs_scope_global_index(vm, &property, generator->runtime); if (njs_slow_path(prop_get->property == NJS_INDEX_ERROR)) { return NJS_ERROR; } node->index = index; if (!exception) { return NJS_OK; } return njs_generate_reference_error(vm, generator, node); } static njs_int_t njs_generate_reference_error(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_vmcode_error_t *ref_err; const njs_lexer_entry_t *lex_entry; if (njs_slow_path(!node->u.reference.not_defined)) { njs_internal_error(vm, "variable is not defined but not_defined " "is not set"); return NJS_ERROR; } njs_generate_code(generator, njs_vmcode_error_t, ref_err, NJS_VMCODE_ERROR, NULL); ref_err->type = NJS_OBJ_TYPE_REF_ERROR; lex_entry = njs_lexer_entry(node->u.reference.unique_id); if (njs_slow_path(lex_entry == NULL)) { return NJS_ERROR; } return njs_name_copy(vm, &ref_err->u.name, &lex_entry->name); } static njs_index_t njs_generate_dest_index(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_index_t ret; njs_parser_node_t *dest; njs_parser_scope_t *scope; ret = njs_generate_children_indexes_release(vm, generator, node); if (njs_slow_path(ret != NJS_OK)) { return ret; } dest = node->dest; if (dest != NULL && dest->index != NJS_INDEX_NONE) { scope = njs_function_scope(node->scope); if (!scope->dest_disable) { return dest->index; } } return njs_generate_node_temp_index_get(vm, generator, node); } static njs_index_t njs_generate_object_dest_index(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_index_t index; njs_parser_node_t *dest; dest = node->dest; if (dest != NULL && dest->index != NJS_INDEX_NONE) { index = dest->index; if (node->left == NULL) { /* Assign empty object directly to variable */ return index; } } return njs_generate_node_temp_index_get(vm, generator, node); } static njs_index_t njs_generate_node_temp_index_get(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { node->temporary = 1; node->index = njs_generate_temp_index_get(vm, generator, node); return node->index; } static njs_index_t njs_generate_temp_index_get(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_arr_t *cache; njs_index_t *last; njs_parser_scope_t *scope; cache = generator->index_cache; if (cache != NULL && cache->items != 0) { last = njs_arr_remove_last(cache); njs_debug_generator(vm, "INDEX REUSE %04Xz", (size_t) *last); return *last; } scope = njs_function_scope(node->scope); if (njs_slow_path(scope == NULL)) { return NJS_ERROR; } return njs_scope_index(scope->type, scope->items++, NJS_LEVEL_LOCAL, NJS_VARIABLE_VAR); } static njs_int_t njs_generate_children_indexes_release(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; ret = njs_generate_node_index_release(vm, generator, node->left); if (njs_fast_path(ret == NJS_OK)) { return njs_generate_node_index_release(vm, generator, node->right); } return ret; } static njs_int_t njs_generate_node_index_release(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { if (node != NULL && node->temporary) { return njs_generate_index_release(vm, generator, node->index); } return NJS_OK; } static njs_int_t njs_generate_node_index_release_pop(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node) { njs_int_t ret; if (node != NULL && node->temporary) { ret = njs_generate_index_release(vm, generator, node->index); if (njs_slow_path(ret != NJS_OK)) { return ret; } } return njs_generator_stack_pop(vm, generator, NULL); } static njs_int_t njs_generate_index_release(njs_vm_t *vm, njs_generator_t *generator, njs_index_t index) { njs_arr_t *cache; njs_index_t *last; njs_debug_generator(vm, "INDEX RELEASE %04Xz", (size_t) index); cache = generator->index_cache; if (cache == NULL) { cache = njs_arr_create(vm->mem_pool, 4, sizeof(njs_value_t *)); if (njs_slow_path(cache == NULL)) { return NJS_ERROR; } generator->index_cache = cache; } last = njs_arr_add(cache); if (njs_fast_path(last != NULL)) { *last = index; return NJS_OK; } return NJS_ERROR; } njs-0.8.9/src/njs_generator.h000066400000000000000000000027571474132077100161430ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_GENERATOR_H_INCLUDED_ #define _NJS_GENERATOR_H_INCLUDED_ typedef struct njs_generator_block_s njs_generator_block_t; typedef njs_int_t (*njs_generator_state_func_t)(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node); struct njs_generator_s { njs_generator_state_func_t state; njs_queue_t stack; njs_parser_node_t *node; void *context; njs_value_t *local_scope; njs_generator_block_t *block; njs_arr_t *index_cache; njs_arr_t *closures; njs_str_t file; njs_arr_t *lines; size_t code_size; u_char *code_start; u_char *code_end; /* Parsing Function() or eval(). */ uint8_t runtime; /* 1 bit */ njs_uint_t depth; }; njs_int_t njs_generator_init(njs_generator_t *generator, njs_str_t *file, njs_int_t depth, njs_bool_t runtime); njs_vm_code_t *njs_generate_scope(njs_vm_t *vm, njs_generator_t *generator, njs_parser_scope_t *scope, const njs_str_t *name); njs_vm_code_t *njs_lookup_code(njs_vm_t *vm, u_char *pc); uint32_t njs_lookup_line(njs_arr_t *lines, uint32_t offset); #endif /* _NJS_GENERATOR_H_INCLUDED_ */ njs-0.8.9/src/njs_iterator.c000066400000000000000000000426411474132077100157750ustar00rootroot00000000000000 /* * Copyright (C) Artem S. Povalyukhin * Copyright (C) NGINX, Inc. */ #include struct njs_value_iterator_s { njs_value_t target; int64_t next; njs_object_enum_t kind; }; typedef struct njs_value_iterator_s njs_array_iterator_t; static const njs_value_t string_done = njs_string("done"); static const njs_value_t string_value = njs_string("value"); static njs_int_t njs_iterator_object_handler(njs_vm_t *vm, njs_iterator_handler_t handler, njs_iterator_args_t *args, njs_value_t *key, int64_t i, njs_value_t *retval); static njs_int_t njs_iterator_to_array_handler(njs_vm_t *vm, njs_iterator_args_t *args, njs_value_t *value, int64_t index, njs_value_t *retval); njs_int_t njs_array_iterator_create(njs_vm_t *vm, const njs_value_t *target, njs_value_t *retval, njs_object_enum_t kind) { njs_object_value_t *iterator; njs_array_iterator_t *it; iterator = njs_object_value_alloc(vm, NJS_OBJ_TYPE_ARRAY_ITERATOR, 0, NULL); if (njs_slow_path(iterator == NULL)) { njs_memory_error(vm); return NJS_ERROR; } it = njs_mp_alloc(vm->mem_pool, sizeof(njs_array_iterator_t)); if (njs_slow_path(it == NULL)) { njs_memory_error(vm); return NJS_ERROR; } it->target = *target; it->next = 0; it->kind = kind; njs_set_data(&iterator->value, it, NJS_DATA_TAG_ARRAY_ITERATOR); njs_set_object_value(retval, iterator); return NJS_OK; } njs_int_t njs_array_iterator_next(njs_vm_t *vm, njs_value_t *iterator, njs_value_t *retval) { int64_t length; njs_int_t ret; njs_array_t *array, *entry; njs_typed_array_t *tarray; const njs_value_t *value; njs_array_iterator_t *it; if (njs_slow_path(!njs_is_valid(njs_object_value(iterator)))) { return NJS_DECLINED; } it = njs_object_data(iterator); value = &njs_value_undefined; if (njs_is_fast_array(&it->target)) { array = njs_array(&it->target); length = array->length; if (it->next >= length) { goto release; } if (it->kind > NJS_ENUM_KEYS && njs_is_valid(&array->start[it->next])) { value = &array->start[it->next]; } } else if (njs_is_typed_array(&it->target)) { tarray = njs_typed_array(&it->target); if (njs_slow_path(njs_is_detached_buffer(tarray->buffer))) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } length = njs_typed_array_length(tarray); if (it->next >= length) { goto release; } if (it->kind > NJS_ENUM_KEYS) { njs_set_number(retval, njs_typed_array_prop(tarray, it->next)); value = retval; } } else { ret = njs_object_length(vm, &it->target, &length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (it->next >= length) { goto release; } if (it->kind > NJS_ENUM_KEYS) { ret = njs_value_property_i64(vm, &it->target, it->next, retval); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } value = njs_is_valid(retval) ? retval : &njs_value_undefined; } } switch (it->kind) { case NJS_ENUM_KEYS: njs_set_number(retval, it->next++); break; case NJS_ENUM_VALUES: it->next++; *retval = *value; break; case NJS_ENUM_BOTH: entry = njs_array_alloc(vm, 0, 2, 0); if (njs_slow_path(entry == NULL)) { return NJS_ERROR; } njs_set_number(&entry->start[0], it->next++); entry->start[1] = *value; njs_set_array(retval, entry); break; default: njs_internal_error(vm, "invalid enum kind"); return NJS_ERROR; } return NJS_OK; release: njs_mp_free(vm->mem_pool, it); njs_set_invalid(njs_object_value(iterator)); return NJS_DECLINED; } static njs_int_t njs_iterator_prototype_get_this(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_value_assign(retval, njs_argument(args, 0)); return NJS_OK; } static const njs_object_prop_t njs_iterator_prototype_properties[] = { { .type = NJS_PROPERTY, .name = njs_wellknown_symbol(NJS_SYMBOL_ITERATOR), .u.value = njs_native_function(njs_iterator_prototype_get_this, 0), .configurable = 1, .writable = 1, }, }; static const njs_object_init_t njs_iterator_prototype_init = { njs_iterator_prototype_properties, njs_nitems(njs_iterator_prototype_properties), }; const njs_object_type_init_t njs_iterator_type_init = { .prototype_props = &njs_iterator_prototype_init, .prototype_value = { .object = { .type = NJS_OBJECT } }, }; static njs_int_t njs_array_iterator_prototype_next(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t tag, njs_value_t *retval) { njs_int_t ret; njs_bool_t check; njs_value_t *this; njs_object_t *object; njs_object_prop_t *prop_value, *prop_done; this = njs_argument(args, 0); check = njs_is_object_value(this) && (njs_is_object_data(this, NJS_DATA_TAG_ARRAY_ITERATOR) || !njs_is_valid(njs_object_value(this))); if (njs_slow_path(!check)) { njs_type_error(vm, "Method [Array Iterator].prototype.next" " called on incompatible receiver"); return NJS_ERROR; } object = njs_object_alloc(vm); if (njs_slow_path(object == NULL)) { return NJS_ERROR; } njs_set_object(retval, object); prop_value = njs_object_property_add(vm, retval, njs_value_arg(&string_value), 0); if (njs_slow_path(prop_value == NULL)) { return NJS_ERROR; } prop_done = njs_object_property_add(vm, retval, njs_value_arg(&string_done), 0); if (njs_slow_path(prop_done == NULL)) { return NJS_ERROR; } ret = njs_array_iterator_next(vm, this, njs_prop_value(prop_value)); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (njs_slow_path(ret == NJS_DECLINED)) { njs_set_undefined(njs_prop_value(prop_value)); njs_set_boolean(njs_prop_value(prop_done), 1); return NJS_OK; } njs_set_boolean(njs_prop_value(prop_done), 0); return NJS_OK; } static const njs_object_prop_t njs_array_iterator_prototype_properties[] = { NJS_DECLARE_PROP_NATIVE("next", njs_array_iterator_prototype_next, 0, NJS_DATA_TAG_ARRAY_ITERATOR), { .type = NJS_PROPERTY, .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG), .u.value = njs_string("Array Iterator"), .configurable = 1, }, }; static const njs_object_init_t njs_array_iterator_prototype_init = { njs_array_iterator_prototype_properties, njs_nitems(njs_array_iterator_prototype_properties), }; const njs_object_type_init_t njs_array_iterator_type_init = { .prototype_props = &njs_array_iterator_prototype_init, .prototype_value = { .object = { .type = NJS_OBJECT } }, }; njs_int_t njs_object_iterate(njs_vm_t *vm, njs_iterator_args_t *args, njs_iterator_handler_t handler, njs_value_t *retval) { double idx; int64_t length, i, from, to; njs_int_t ret; njs_array_t *array, *keys; njs_value_t *value, *entry, prop, character; const u_char *p, *end, *pos; njs_string_prop_t string_prop; value = njs_value_arg(&args->value); from = args->from; to = args->to; if (njs_is_array(value)) { array = njs_array(value); for (; from < to; from++) { if (njs_slow_path(!array->object.fast_array)) { goto process_object; } if (njs_fast_path(from < array->length && njs_is_valid(&array->start[from]))) { ret = handler(vm, args, &array->start[from], from, retval); } else { entry = njs_value_arg(&njs_value_invalid); ret = njs_value_property_i64(vm, value, from, &prop); if (njs_slow_path(ret != NJS_DECLINED)) { if (ret == NJS_ERROR) { return NJS_ERROR; } entry = ∝ } ret = handler(vm, args, entry, from, retval); } if (njs_slow_path(ret != NJS_OK)) { if (ret == NJS_DONE) { return NJS_DONE; } return NJS_ERROR; } } return NJS_OK; } if (njs_is_string(value) || njs_is_object_string(value)) { if (!njs_is_string(value)) { value = njs_object_value(value); } length = njs_string_prop(&string_prop, value); p = string_prop.start; end = p + string_prop.size; if ((size_t) length == string_prop.size) { /* ASCII string. */ for (i = from; i < to; i++) { /* This cannot fail. */ (void) njs_string_new(vm, &character, p + i, 1, 1); ret = handler(vm, args, &character, i, retval); if (njs_slow_path(ret != NJS_OK)) { if (ret == NJS_DONE) { return NJS_DONE; } return NJS_ERROR; } } } else { /* UTF-8 string. */ for (i = from; i < to; i++) { pos = njs_utf8_next(p, end); /* This cannot fail. */ (void) njs_string_new(vm, &character, p, pos - p, 1); ret = handler(vm, args, &character, i, retval); if (njs_slow_path(ret != NJS_OK)) { if (ret == NJS_DONE) { return NJS_DONE; } return NJS_ERROR; } p = pos; } } return NJS_OK; } if (!njs_is_object(value)) { return NJS_OK; } process_object: if (!njs_fast_object(to - from)) { keys = njs_array_indices(vm, value); if (njs_slow_path(keys == NULL)) { return NJS_ERROR; } for (i = 0; i < keys->length; i++) { idx = njs_string_to_index(&keys->start[i]); if (idx < from || idx >= to) { continue; } ret = njs_iterator_object_handler(vm, handler, args, &keys->start[i], idx, retval); if (njs_slow_path(ret != NJS_OK)) { njs_array_destroy(vm, keys); return ret; } } njs_array_destroy(vm, keys); return NJS_OK; } for (i = from; i < to; i++) { ret = njs_iterator_object_handler(vm, handler, args, NULL, i, retval); if (njs_slow_path(ret != NJS_OK)) { return ret; } } return NJS_OK; } njs_int_t njs_object_iterate_reverse(njs_vm_t *vm, njs_iterator_args_t *args, njs_iterator_handler_t handler, njs_value_t *retval) { double idx; int64_t i, from, to, length; njs_int_t ret; njs_array_t *array, *keys; njs_value_t *entry, *value, prop, character; const u_char *p, *end, *pos; njs_string_prop_t string_prop; value = njs_value_arg(&args->value); from = args->from; to = args->to; if (njs_is_array(value)) { array = njs_array(value); from += 1; while (from-- > to) { if (njs_slow_path(!array->object.fast_array)) { goto process_object; } if (njs_fast_path(from < array->length && njs_is_valid(&array->start[from]))) { ret = handler(vm, args, &array->start[from], from, retval); } else { entry = njs_value_arg(&njs_value_invalid); ret = njs_value_property_i64(vm, value, from, &prop); if (njs_slow_path(ret != NJS_DECLINED)) { if (ret == NJS_ERROR) { return NJS_ERROR; } entry = ∝ } ret = handler(vm, args, entry, from, retval); } if (njs_slow_path(ret != NJS_OK)) { if (ret == NJS_DONE) { return NJS_DONE; } return NJS_ERROR; } } return NJS_OK; } if (njs_is_string(value) || njs_is_object_string(value)) { if (!njs_is_string(value)) { value = njs_object_value(value); } length = njs_string_prop(&string_prop, value); end = string_prop.start + string_prop.size; if ((size_t) length == string_prop.size) { /* Byte or ASCII string. */ p = string_prop.start + from; i = from + 1; while (i-- > to) { /* This cannot fail. */ (void) njs_string_new(vm, &character, p, 1, 1); ret = handler(vm, args, &character, i, retval); if (njs_slow_path(ret != NJS_OK)) { if (ret == NJS_DONE) { return NJS_DONE; } return NJS_ERROR; } p--; } } else { /* UTF-8 string. */ p = NULL; i = from + 1; if (i > to) { p = njs_string_utf8_offset(string_prop.start, end, from); p = njs_utf8_next(p, end); } while (i-- > to) { pos = njs_utf8_prev(p, string_prop.start); /* This cannot fail. */ (void) njs_string_new(vm, &character, pos, p - pos , 1); ret = handler(vm, args, &character, i, retval); if (njs_slow_path(ret != NJS_OK)) { if (ret == NJS_DONE) { return NJS_DONE; } return NJS_ERROR; } p = pos; } } return NJS_OK; } if (!njs_is_object(value)) { return NJS_OK; } process_object: if (!njs_fast_object(from - to)) { keys = njs_array_indices(vm, value); if (njs_slow_path(keys == NULL)) { return NJS_ERROR; } i = keys->length; while (i > 0) { idx = njs_string_to_index(&keys->start[--i]); if (idx < to || idx > from) { continue; } ret = njs_iterator_object_handler(vm, handler, args, &keys->start[i], idx, retval); if (njs_slow_path(ret != NJS_OK)) { njs_array_destroy(vm, keys); return ret; } } njs_array_destroy(vm, keys); return NJS_OK; } i = from + 1; while (i-- > to) { ret = njs_iterator_object_handler(vm, handler, args, NULL, i, retval); if (njs_slow_path(ret != NJS_OK)) { return ret; } } return NJS_OK; } static njs_int_t njs_iterator_object_handler(njs_vm_t *vm, njs_iterator_handler_t handler, njs_iterator_args_t *args, njs_value_t *key, int64_t i, njs_value_t *retval) { njs_int_t ret; njs_value_t prop, *entry; if (key != NULL) { ret = njs_value_property(vm, njs_value_arg(&args->value), key, &prop); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } } else { ret = njs_value_property_i64(vm, njs_value_arg(&args->value), i, &prop); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } } entry = (ret == NJS_OK) ? &prop : njs_value_arg(&njs_value_invalid); ret = handler(vm, args, entry, i, retval); if (njs_slow_path(ret != NJS_OK)) { if (ret == NJS_DONE) { return NJS_DONE; } return NJS_ERROR; } return ret; } njs_array_t * njs_iterator_to_array(njs_vm_t *vm, njs_value_t *iterator, njs_value_t *retval) { int64_t length; njs_int_t ret; njs_iterator_args_t args; njs_memzero(&args, sizeof(njs_iterator_args_t)); ret = njs_object_length(vm, iterator, &length); if (njs_slow_path(ret != NJS_OK)) { return NULL; } args.data = njs_array_alloc(vm, 0, 0, njs_min(length, NJS_ARRAY_LARGE_OBJECT_LENGTH)); if (njs_slow_path(args.data == NULL)) { return NULL; } njs_value_assign(&args.value, iterator); args.to = length; ret = njs_object_iterate(vm, &args, njs_iterator_to_array_handler, retval); if (njs_slow_path(ret == NJS_ERROR)) { njs_mp_free(vm->mem_pool, args.data); return NULL; } return args.data; } static njs_int_t njs_iterator_to_array_handler(njs_vm_t *vm, njs_iterator_args_t *args, njs_value_t *value, int64_t index, njs_value_t *retval) { njs_value_t array; njs_set_array(&array, args->data); return njs_value_property_i64_set(vm, &array, index, value); } njs-0.8.9/src/njs_iterator.h000066400000000000000000000016041474132077100157740ustar00rootroot00000000000000 /* * Copyright (C) Artem S. Povalyukhin * Copyright (C) NGINX, Inc. */ #ifndef _NJS_ITERATOR_H_INCLUDED_ #define _NJS_ITERATOR_H_INCLUDED_ njs_int_t njs_array_iterator_create(njs_vm_t *vm, const njs_value_t *src, njs_value_t *dst, njs_object_enum_t kind); njs_int_t njs_array_iterator_next(njs_vm_t *vm, njs_value_t *iterator, njs_value_t *retval); njs_int_t njs_object_iterate(njs_vm_t *vm, njs_iterator_args_t *args, njs_iterator_handler_t handler, njs_value_t *retval); njs_int_t njs_object_iterate_reverse(njs_vm_t *vm, njs_iterator_args_t *args, njs_iterator_handler_t handler, njs_value_t *retval); njs_array_t *njs_iterator_to_array(njs_vm_t *vm, njs_value_t *iterator, njs_value_t *retval); extern const njs_object_type_init_t njs_iterator_type_init; extern const njs_object_type_init_t njs_array_iterator_type_init; #endif /* _NJS_ITERATOR_H_INCLUDED_ */ njs-0.8.9/src/njs_json.c000066400000000000000000001473231474132077100151200ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #include typedef struct { njs_vm_t *vm; njs_mp_t *pool; njs_uint_t depth; const u_char *start; const u_char *end; } njs_json_parse_ctx_t; typedef struct { njs_value_t value; uint8_t written; /* 1 bit */ uint8_t array; /* 1 bit */ uint8_t fast_array; /* 1 bit */ int64_t index; int64_t length; njs_array_t *keys; njs_value_t *key; njs_value_t prop; } njs_json_state_t; typedef struct { njs_value_t retval; njs_vm_t *vm; njs_uint_t depth; #define NJS_JSON_MAX_DEPTH 32 njs_json_state_t states[NJS_JSON_MAX_DEPTH]; njs_value_t replacer; njs_str_t space; u_char space_buf[16]; uint32_t keys_type; } njs_json_stringify_t; static const u_char *njs_json_parse_value(njs_json_parse_ctx_t *ctx, njs_value_t *value, const u_char *p); static const u_char *njs_json_parse_object(njs_json_parse_ctx_t *ctx, njs_value_t *value, const u_char *p); static const u_char *njs_json_parse_array(njs_json_parse_ctx_t *ctx, njs_value_t *value, const u_char *p); static const u_char *njs_json_parse_string(njs_json_parse_ctx_t *ctx, njs_value_t *value, const u_char *p); static const u_char *njs_json_parse_number(njs_json_parse_ctx_t *ctx, njs_value_t *value, const u_char *p); njs_inline uint32_t njs_json_unicode(const u_char *p); static const u_char *njs_json_skip_space(const u_char *start, const u_char *end); static njs_int_t njs_json_internalize_property(njs_vm_t *vm, njs_function_t *reviver, njs_value_t *holder, njs_value_t *name, njs_int_t depth, njs_value_t *retval); static void njs_json_parse_exception(njs_json_parse_ctx_t *ctx, const char *msg, const u_char *pos); static njs_int_t njs_json_stringify_iterator(njs_json_stringify_t *stringify, njs_value_t *value, njs_value_t *retval); static njs_function_t *njs_object_to_json_function(njs_vm_t *vm, njs_value_t *value); static njs_int_t njs_json_stringify_to_json(njs_json_stringify_t* stringify, njs_json_state_t *state, njs_value_t *key, njs_value_t *value); static njs_int_t njs_json_stringify_replacer(njs_json_stringify_t* stringify, njs_json_state_t *state, njs_value_t *key, njs_value_t *value); static njs_int_t njs_json_stringify_array(njs_json_stringify_t *stringify); static njs_int_t njs_json_append_value(njs_vm_t *vm, njs_chb_t *chain, njs_value_t *value); static void njs_json_append_string(njs_chb_t *chain, const njs_value_t *value, char quote); static void njs_json_append_number(njs_chb_t *chain, const njs_value_t *value); static njs_object_t *njs_json_wrap_value(njs_vm_t *vm, njs_value_t *wrapper, const njs_value_t *value); static const njs_object_prop_t njs_json_object_properties[]; static njs_int_t njs_json_parse(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_value_t *text, value, lvalue, wrapper; njs_object_t *obj; const u_char *p, *end; const njs_value_t *reviver; njs_string_prop_t string; njs_json_parse_ctx_t ctx; text = njs_lvalue_arg(&lvalue, args, nargs, 1); if (njs_slow_path(!njs_is_string(text))) { ret = njs_value_to_string(vm, text, text); if (njs_slow_path(ret != NJS_OK)) { return ret; } } (void) njs_string_prop(&string, text); p = string.start; end = p + string.size; ctx.vm = vm; ctx.pool = vm->mem_pool; ctx.depth = NJS_JSON_MAX_DEPTH; ctx.start = string.start; ctx.end = end; p = njs_json_skip_space(p, end); if (njs_slow_path(p == end)) { njs_json_parse_exception(&ctx, "Unexpected end of input", p); return NJS_ERROR; } p = njs_json_parse_value(&ctx, &value, p); if (njs_slow_path(p == NULL)) { return NJS_ERROR; } p = njs_json_skip_space(p, end); if (njs_slow_path(p != end)) { njs_json_parse_exception(&ctx, "Unexpected token", p); return NJS_ERROR; } reviver = njs_arg(args, nargs, 2); if (njs_slow_path(njs_is_function(reviver))) { obj = njs_json_wrap_value(vm, &wrapper, &value); if (njs_slow_path(obj == NULL)) { return NJS_ERROR; } return njs_json_internalize_property(vm, njs_function(reviver), &wrapper, njs_value_arg(&njs_string_empty), 0, retval); } njs_value_assign(retval, &value); return NJS_OK; } njs_int_t njs_vm_json_parse(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_value_t *retval) { njs_function_t *parse; parse = njs_function(&njs_json_object_properties[1].u.value); return njs_vm_invoke(vm, parse, args, nargs, retval); } static njs_int_t njs_json_stringify(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { size_t length; int64_t i64; njs_int_t i; njs_int_t ret; njs_value_t *replacer, *space; const u_char *p; njs_string_prop_t prop; njs_json_stringify_t *stringify, json_stringify; stringify = &json_stringify; stringify->vm = vm; stringify->depth = 0; stringify->keys_type = NJS_ENUM_STRING; replacer = njs_arg(args, nargs, 2); if (njs_is_function(replacer) || njs_is_array(replacer)) { stringify->replacer = *replacer; if (njs_is_array(replacer)) { ret = njs_json_stringify_array(stringify); if (njs_slow_path(ret != NJS_OK)) { goto memory_error; } } } else { njs_set_undefined(&stringify->replacer); } space = njs_arg(args, nargs, 3); if (njs_is_object(space)) { if (njs_is_object_number(space)) { ret = njs_value_to_numeric(vm, space, space); if (njs_slow_path(ret != NJS_OK)) { return ret; } } else if (njs_is_object_string(space)) { ret = njs_value_to_string(vm, space, space); if (njs_slow_path(ret != NJS_OK)) { return ret; } } } switch (space->type) { case NJS_STRING: length = njs_string_prop(&prop, space); p = njs_string_offset(&prop, njs_min(length, 10)); stringify->space.start = prop.start; stringify->space.length = p - prop.start; break; case NJS_NUMBER: i64 = njs_min(njs_number_to_integer(njs_number(space)), 10); if (i64 > 0) { stringify->space.length = i64; stringify->space.start = stringify->space_buf; for (i = 0; i < i64; i++) { stringify->space.start[i] = ' '; } break; } /* Fall through. */ default: stringify->space.length = 0; break; } return njs_json_stringify_iterator(stringify, njs_arg(args, nargs, 1), retval); memory_error: njs_memory_error(vm); return NJS_ERROR; } njs_int_t njs_vm_json_stringify(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_value_t *retval) { njs_function_t *stringify; stringify = njs_function(&njs_json_object_properties[2].u.value); return njs_vm_invoke(vm, stringify, args, nargs, retval); } static const u_char * njs_json_parse_value(njs_json_parse_ctx_t *ctx, njs_value_t *value, const u_char *p) { switch (*p) { case '{': return njs_json_parse_object(ctx, value, p); case '[': return njs_json_parse_array(ctx, value, p); case '"': return njs_json_parse_string(ctx, value, p); case 't': if (njs_fast_path(ctx->end - p >= 4 && memcmp(p, "true", 4) == 0)) { *value = njs_value_true; return p + 4; } goto error; case 'f': if (njs_fast_path(ctx->end - p >= 5 && memcmp(p, "false", 5) == 0)) { *value = njs_value_false; return p + 5; } goto error; case 'n': if (njs_fast_path(ctx->end - p >= 4 && memcmp(p, "null", 4) == 0)) { *value = njs_value_null; return p + 4; } goto error; } if (njs_fast_path(*p == '-' || (*p - '0') <= 9)) { return njs_json_parse_number(ctx, value, p); } error: njs_json_parse_exception(ctx, "Unexpected token", p); return NULL; } static const u_char * njs_json_parse_object(njs_json_parse_ctx_t *ctx, njs_value_t *value, const u_char *p) { njs_int_t ret; njs_object_t *object; njs_value_t prop_name, prop_value; njs_object_prop_t *prop; njs_lvlhsh_query_t lhq; if (njs_slow_path(--ctx->depth == 0)) { njs_json_parse_exception(ctx, "Nested too deep", p); return NULL; } object = njs_object_alloc(ctx->vm); if (njs_slow_path(object == NULL)) { goto memory_error; } prop = NULL; for ( ;; ) { p = njs_json_skip_space(p + 1, ctx->end); if (njs_slow_path(p == ctx->end)) { goto error_end; } if (*p != '"') { if (njs_fast_path(*p == '}')) { if (njs_slow_path(prop != NULL)) { njs_json_parse_exception(ctx, "Trailing comma", p - 1); return NULL; } break; } goto error_token; } p = njs_json_parse_string(ctx, &prop_name, p); if (njs_slow_path(p == NULL)) { /* The exception is set by the called function. */ return NULL; } p = njs_json_skip_space(p, ctx->end); if (njs_slow_path(p == ctx->end || *p != ':')) { goto error_token; } p = njs_json_skip_space(p + 1, ctx->end); if (njs_slow_path(p == ctx->end)) { goto error_end; } p = njs_json_parse_value(ctx, &prop_value, p); if (njs_slow_path(p == NULL)) { /* The exception is set by the called function. */ return NULL; } prop = njs_object_prop_alloc(ctx->vm, &prop_name, &prop_value, 1); if (njs_slow_path(prop == NULL)) { goto memory_error; } njs_string_get(&prop_name, &lhq.key); lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length); lhq.value = prop; lhq.replace = 1; lhq.pool = ctx->pool; lhq.proto = &njs_object_hash_proto; ret = njs_lvlhsh_insert(&object->hash, &lhq); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(ctx->vm, "lvlhsh insert/replace failed"); return NULL; } p = njs_json_skip_space(p, ctx->end); if (njs_slow_path(p == ctx->end)) { goto error_end; } if (*p != ',') { if (njs_fast_path(*p == '}')) { break; } goto error_token; } } njs_set_object(value, object); ctx->depth++; return p + 1; error_token: njs_json_parse_exception(ctx, "Unexpected token", p); return NULL; error_end: njs_json_parse_exception(ctx, "Unexpected end of input", p); return NULL; memory_error: njs_memory_error(ctx->vm); return NULL; } static const u_char * njs_json_parse_array(njs_json_parse_ctx_t *ctx, njs_value_t *value, const u_char *p) { njs_int_t ret; njs_bool_t empty; njs_array_t *array; njs_value_t element; if (njs_slow_path(--ctx->depth == 0)) { njs_json_parse_exception(ctx, "Nested too deep", p); return NULL; } array = njs_array_alloc(ctx->vm, 0, 0, NJS_ARRAY_SPARE); if (njs_slow_path(array == NULL)) { return NULL; } empty = 1; for ( ;; ) { p = njs_json_skip_space(p + 1, ctx->end); if (njs_slow_path(p == ctx->end)) { goto error_end; } if (*p == ']') { if (njs_slow_path(!empty)) { njs_json_parse_exception(ctx, "Trailing comma", p - 1); return NULL; } break; } p = njs_json_parse_value(ctx, &element, p); if (njs_slow_path(p == NULL)) { return NULL; } ret = njs_array_add(ctx->vm, array, &element); if (njs_slow_path(ret != NJS_OK)) { return NULL; } empty = 0; p = njs_json_skip_space(p, ctx->end); if (njs_slow_path(p == ctx->end)) { goto error_end; } if (*p != ',') { if (njs_fast_path(*p == ']')) { break; } goto error_token; } } njs_set_array(value, array); ctx->depth++; return p + 1; error_token: njs_json_parse_exception(ctx, "Unexpected token", p); return NULL; error_end: njs_json_parse_exception(ctx, "Unexpected end of input", p); return NULL; } static const u_char * njs_json_parse_string(njs_json_parse_ctx_t *ctx, njs_value_t *value, const u_char *p) { u_char ch, *s, *dst; size_t size, surplus; uint32_t utf, utf_low; njs_int_t ret; const u_char *start, *last; enum { sw_usual = 0, sw_escape, sw_encoded1, sw_encoded2, sw_encoded3, sw_encoded4, } state; start = p + 1; dst = NULL; state = 0; surplus = 0; for (p = start; p < ctx->end; p++) { ch = *p; switch (state) { case sw_usual: if (ch == '"') { break; } if (ch == '\\') { state = sw_escape; continue; } if (njs_fast_path(ch >= ' ')) { continue; } njs_json_parse_exception(ctx, "Forbidden source char", p); return NULL; case sw_escape: switch (ch) { case '"': case '\\': case '/': case 'n': case 'r': case 't': case 'b': case 'f': surplus++; state = sw_usual; continue; case 'u': /* * Basic unicode 6 bytes "\uXXXX" in JSON * and up to 3 bytes in UTF-8. * * Surrogate pair: 12 bytes "\uXXXX\uXXXX" in JSON * and 3 or 4 bytes in UTF-8. */ surplus += 3; state = sw_encoded1; continue; } njs_json_parse_exception(ctx, "Unknown escape char", p); return NULL; case sw_encoded1: case sw_encoded2: case sw_encoded3: case sw_encoded4: if (njs_fast_path((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f'))) { state = (state == sw_encoded4) ? sw_usual : state + 1; continue; } njs_json_parse_exception(ctx, "Invalid Unicode escape sequence", p); return NULL; } break; } if (njs_slow_path(p == ctx->end)) { njs_json_parse_exception(ctx, "Unexpected end of input", p); return NULL; } /* Points to the ending quote mark. */ last = p; size = last - start - surplus; if (surplus != 0) { p = start; dst = njs_mp_alloc(ctx->pool, size); if (njs_slow_path(dst == NULL)) { njs_memory_error(ctx->vm);; return NULL; } s = dst; do { ch = *p++; if (ch != '\\') { *s++ = ch; continue; } ch = *p++; switch (ch) { case '"': case '\\': case '/': *s++ = ch; continue; case 'n': *s++ = '\n'; continue; case 'r': *s++ = '\r'; continue; case 't': *s++ = '\t'; continue; case 'b': *s++ = '\b'; continue; case 'f': *s++ = '\f'; continue; } /* "\uXXXX": Unicode escape sequence. */ utf = njs_json_unicode(p); p += 4; if (njs_surrogate_any(utf)) { if (utf > 0xdbff || p[0] != '\\' || p[1] != 'u') { s = njs_utf8_encode(s, NJS_UNICODE_REPLACEMENT); continue; } p += 2; utf_low = njs_json_unicode(p); p += 4; if (njs_fast_path(njs_surrogate_trailing(utf_low))) { utf = njs_surrogate_pair(utf, utf_low); } else if (njs_surrogate_leading(utf_low)) { utf = NJS_UNICODE_REPLACEMENT; s = njs_utf8_encode(s, NJS_UNICODE_REPLACEMENT); } else { utf = utf_low; s = njs_utf8_encode(s, NJS_UNICODE_REPLACEMENT); } } s = njs_utf8_encode(s, utf); } while (p != last); size = s - dst; start = dst; } ret = njs_string_create(ctx->vm, value, (u_char *) start, size); if (njs_slow_path(ret != NJS_OK)) { return NULL; } if (dst != NULL) { njs_mp_free(ctx->pool, dst); } return last + 1; } static const u_char * njs_json_parse_number(njs_json_parse_ctx_t *ctx, njs_value_t *value, const u_char *p) { double num; njs_int_t sign; const u_char *start; sign = 1; if (*p == '-') { if (p + 1 == ctx->end) { goto error; } p++; sign = -1; } start = p; num = njs_number_dec_parse(&p, ctx->end, 0); if (p != start) { njs_set_number(value, sign * num); return p; } error: njs_json_parse_exception(ctx, "Unexpected number", p); return NULL; } njs_inline uint32_t njs_json_unicode(const u_char *p) { u_char c; uint32_t utf; njs_uint_t i; utf = 0; for (i = 0; i < 4; i++) { utf <<= 4; c = p[i] | 0x20; c -= '0'; if (c > 9) { c += '0' - 'a' + 10; } utf |= c; } return utf; } static const u_char * njs_json_skip_space(const u_char *start, const u_char *end) { const u_char *p; for (p = start; njs_fast_path(p != end); p++) { switch (*p) { case ' ': case '\t': case '\r': case '\n': continue; } break; } return p; } static njs_int_t njs_json_internalize_property(njs_vm_t *vm, njs_function_t *reviver, njs_value_t *holder, njs_value_t *name, njs_int_t depth, njs_value_t *retval) { int64_t k, length; njs_int_t ret; njs_value_t val, new_elem, index; njs_value_t arguments[3]; njs_array_t *keys; if (njs_slow_path(depth++ >= NJS_JSON_MAX_DEPTH)) { njs_type_error(vm, "Nested too deep or a cyclic structure"); return NJS_ERROR; } ret = njs_value_property(vm, holder, name, &val); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } keys = NULL; if (njs_is_object(&val)) { if (!njs_is_array(&val)) { keys = njs_array_keys(vm, &val, 0); if (njs_slow_path(keys == NULL)) { return NJS_ERROR; } for (k = 0; k < keys->length; k++) { ret = njs_json_internalize_property(vm, reviver, &val, &keys->start[k], depth, &new_elem); if (njs_slow_path(ret != NJS_OK)) { goto done; } if (njs_is_undefined(&new_elem)) { ret = njs_value_property_delete(vm, &val, &keys->start[k], NULL, 0); } else { ret = njs_value_property_set(vm, &val, &keys->start[k], &new_elem); } if (njs_slow_path(ret == NJS_ERROR)) { goto done; } } } else { ret = njs_object_length(vm, &val, &length); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } for (k = 0; k < length; k++) { ret = njs_int64_to_string(vm, &index, k); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_json_internalize_property(vm, reviver, &val, &index, depth, &new_elem); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } if (njs_is_undefined(&new_elem)) { ret = njs_value_property_delete(vm, &val, &index, NULL, 0); } else { ret = njs_value_property_set(vm, &val, &index, &new_elem); } if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } } } } njs_value_assign(&arguments[0], holder); njs_value_assign(&arguments[1], name); njs_value_assign(&arguments[2], &val); ret = njs_function_apply(vm, reviver, arguments, 3, retval); done: if (keys != NULL) { njs_array_destroy(vm, keys); } return ret; } static void njs_json_parse_exception(njs_json_parse_ctx_t *ctx, const char *msg, const u_char *pos) { ssize_t length; length = njs_utf8_length(ctx->start, pos - ctx->start); if (njs_slow_path(length < 0)) { length = 0; } njs_syntax_error(ctx->vm, "%s at position %z", msg, length); } static njs_json_state_t * njs_json_push_stringify_state(njs_json_stringify_t *stringify, njs_value_t *value) { njs_int_t ret; njs_json_state_t *state; if (njs_slow_path(stringify->depth >= NJS_JSON_MAX_DEPTH)) { njs_type_error(stringify->vm, "Nested too deep or a cyclic structure"); return NULL; } state = &stringify->states[stringify->depth++]; state->value = *value; state->array = njs_is_array(value); state->fast_array = njs_is_fast_array(value); state->index = 0; state->written = 0; state->keys = NULL; state->key = NULL; if (state->fast_array) { state->length = njs_array_len(value); } if (njs_is_array(&stringify->replacer)) { state->keys = njs_array(&stringify->replacer); } else if (state->array) { state->keys = njs_array_keys(stringify->vm, value, 1); if (njs_slow_path(state->keys == NULL)) { return NULL; } ret = njs_object_length(stringify->vm, &state->value, &state->length); if (njs_slow_path(ret == NJS_ERROR)) { return NULL; } } else { state->keys = njs_value_own_enumerate(stringify->vm, value, NJS_ENUM_KEYS | stringify->keys_type | NJS_ENUM_ENUMERABLE_ONLY); if (njs_slow_path(state->keys == NULL)) { return NULL; } } return state; } njs_inline njs_json_state_t * njs_json_pop_stringify_state(njs_json_stringify_t *stringify) { njs_json_state_t *state; state = &stringify->states[stringify->depth - 1]; if (!njs_is_array(&stringify->replacer) && state->keys != NULL) { njs_array_destroy(stringify->vm, state->keys); state->keys = NULL; } if (stringify->depth > 1) { stringify->depth--; state = &stringify->states[stringify->depth - 1]; state->written = 1; return state; } return NULL; } njs_inline njs_bool_t njs_json_is_object(const njs_value_t *value) { if (!njs_is_object(value)) { return 0; } if (njs_is_function(value)) { return 0; } if (njs_is_object_value(value)) { switch (njs_object_value(value)->type) { case NJS_BOOLEAN: case NJS_NUMBER: case NJS_STRING: return 0; default: break; } } return 1; } njs_inline void njs_json_stringify_indent(njs_json_stringify_t *stringify, njs_chb_t *chain, njs_int_t times) { njs_int_t i; if (stringify->space.length != 0) { times += stringify->depth; njs_chb_append(chain,"\n", 1); for (i = 0; i < (times - 1); i++) { njs_chb_append_str(chain, &stringify->space); } } } njs_inline njs_bool_t njs_json_stringify_done(njs_json_state_t *state, njs_bool_t array) { return array ? state->index >= state->length : state->index >= state->keys->length; } static njs_int_t njs_json_stringify_iterator(njs_json_stringify_t *stringify, njs_value_t *object, njs_value_t *retval) { int64_t size; njs_int_t ret; njs_chb_t chain; njs_value_t *key, *value, index, wrapper; njs_object_t *obj; njs_json_state_t *state; obj = njs_json_wrap_value(stringify->vm, &wrapper, object); if (njs_slow_path(obj == NULL)) { goto memory_error; } state = njs_json_push_stringify_state(stringify, &wrapper); if (njs_slow_path(state == NULL)) { goto memory_error; } NJS_CHB_MP_INIT(&chain, stringify->vm); for ( ;; ) { if (state->index == 0) { njs_chb_append(&chain, state->array ? "[" : "{", 1); njs_json_stringify_indent(stringify, &chain, 0); } if (njs_json_stringify_done(state, state->array)) { njs_json_stringify_indent(stringify, &chain, -1); njs_chb_append(&chain, state->array ? "]" : "}", 1); state = njs_json_pop_stringify_state(stringify); if (state == NULL) { goto done; } continue; } value = &stringify->retval; if (state->array) { njs_set_number(&index, state->index); key = &index; } else { key = &state->keys->start[state->index]; } ret = njs_value_property(stringify->vm, &state->value, key, value); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (state->array && ret == NJS_DECLINED) { njs_set_null(value); } ret = njs_json_stringify_to_json(stringify, state, key, value); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_json_stringify_replacer(stringify, state, key, value); if (njs_slow_path(ret != NJS_OK)) { return ret; } state->index++; if (!state->array && (njs_is_undefined(value) || njs_is_symbol(value) || njs_is_function(value) || !njs_is_valid(value))) { continue; } if (state->written) { njs_chb_append_literal(&chain,","); njs_json_stringify_indent(stringify, &chain, 0); } state->written = 1; if (!state->array) { njs_json_append_string(&chain, key, '\"'); njs_chb_append_literal(&chain,":"); if (stringify->space.length != 0) { njs_chb_append_literal(&chain," "); } } if (njs_json_is_object(value)) { state = njs_json_push_stringify_state(stringify, value); if (njs_slow_path(state == NULL)) { return NJS_ERROR; } continue; } ret = njs_json_append_value(stringify->vm, &chain, value); if (njs_slow_path(ret != NJS_OK)) { return ret; } } done: /* * The value to stringify is wrapped as '{"": value}'. * Stripping the wrapper's data. */ njs_chb_drain(&chain, njs_length("{\"\":")); njs_chb_drop(&chain, njs_length("}")); if (stringify->space.length != 0) { njs_chb_drain(&chain, njs_length("\n ")); njs_chb_drop(&chain, njs_length("\n")); } size = njs_chb_size(&chain); if (njs_slow_path(size < 0)) { njs_chb_destroy(&chain); goto memory_error; } if (size == 0) { njs_set_undefined(retval); goto release; } ret = njs_string_create_chb(stringify->vm, retval, &chain); if (njs_slow_path(ret != NJS_OK)) { njs_chb_destroy(&chain); goto memory_error; } release: njs_chb_destroy(&chain); return NJS_OK; memory_error: njs_memory_error(stringify->vm); return NJS_ERROR; } static njs_function_t * njs_object_to_json_function(njs_vm_t *vm, njs_value_t *value) { njs_int_t ret; njs_value_t retval; njs_lvlhsh_query_t lhq; static const njs_value_t to_json_string = njs_string("toJSON"); if (njs_is_object(value)) { njs_object_property_init(&lhq, &to_json_string, NJS_TO_JSON_HASH); ret = njs_object_property(vm, njs_object(value), &lhq, &retval); if (njs_slow_path(ret == NJS_ERROR)) { return NULL; } return njs_is_function(&retval) ? njs_function(&retval) : NULL; } return NULL; } static njs_int_t njs_json_stringify_to_json(njs_json_stringify_t* stringify, njs_json_state_t *state, njs_value_t *key, njs_value_t *value) { njs_value_t arguments[2]; njs_function_t *to_json; if (!njs_is_object(value)) { return NJS_OK; } to_json = njs_object_to_json_function(stringify->vm, value); if (to_json == NULL) { return NJS_OK; } arguments[0] = *value; if (!state->array) { arguments[1] = *key; } else { njs_uint32_to_string(&arguments[1], state->index); } return njs_function_apply(stringify->vm, to_json, arguments, 2, &stringify->retval); } static njs_int_t njs_json_stringify_replacer(njs_json_stringify_t* stringify, njs_json_state_t *state, njs_value_t *key, njs_value_t *value) { njs_value_t arguments[3]; if (!njs_is_function(&stringify->replacer)) { return NJS_OK; } arguments[0] = state->value; arguments[2] = *value; if (!state->array) { arguments[1] = *key; } else { njs_uint32_to_string(&arguments[1], state->index); } return njs_function_apply(stringify->vm, njs_function(&stringify->replacer), arguments, 3, &stringify->retval); } static njs_int_t njs_json_stringify_array(njs_json_stringify_t *stringify) { njs_int_t ret; int64_t i, k, length; njs_value_t *value, *item; njs_array_t *properties; ret = njs_object_length(stringify->vm, &stringify->replacer, &length); if (njs_slow_path(ret != NJS_OK)) { return ret; } properties = njs_array_alloc(stringify->vm, 1, 0, NJS_ARRAY_SPARE); if (njs_slow_path(properties == NULL)) { return NJS_ERROR; } item = njs_array_push(stringify->vm, properties); njs_value_assign(item, &njs_string_empty); for (i = 0; i < length; i++) { ret = njs_value_property_i64(stringify->vm, &stringify->replacer, i, &stringify->retval); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } value = &stringify->retval; switch (value->type) { case NJS_STRING: break; case NJS_NUMBER: ret = njs_number_to_string(stringify->vm, value, value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } break; case NJS_OBJECT_VALUE: switch (njs_object_value(value)->type) { case NJS_NUMBER: case NJS_STRING: ret = njs_value_to_string(stringify->vm, value, value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } break; default: continue; } break; default: continue; } for (k = 0; k < properties->length; k++) { if (njs_values_strict_equal(value, &properties->start[k]) == 1) { break; } } if (k == properties->length) { item = njs_array_push(stringify->vm, properties); if (njs_slow_path(item == NULL)) { return NJS_ERROR; } njs_value_assign(item, value); } } njs_set_array(&stringify->replacer, properties); return NJS_OK; } static njs_int_t njs_json_append_value(njs_vm_t *vm, njs_chb_t *chain, njs_value_t *value) { njs_int_t ret; if (njs_is_object_value(value)) { switch (njs_object_value(value)->type) { case NJS_NUMBER: ret = njs_value_to_numeric(vm, value, value); if (njs_slow_path(ret != NJS_OK)) { return ret; } break; case NJS_BOOLEAN: njs_value_assign(value, njs_object_value(value)); break; case NJS_STRING: ret = njs_value_to_string(vm, value, value); if (njs_slow_path(ret != NJS_OK)) { return ret; } break; default: break; } } switch (value->type) { case NJS_STRING: njs_json_append_string(chain, value, '\"'); break; case NJS_NUMBER: njs_json_append_number(chain, value); break; case NJS_BOOLEAN: if (njs_is_true(value)) { njs_chb_append_literal(chain, "true"); } else { njs_chb_append_literal(chain, "false"); } break; case NJS_UNDEFINED: case NJS_NULL: case NJS_SYMBOL: case NJS_INVALID: case NJS_FUNCTION: default: njs_chb_append_literal(chain, "null"); } return NJS_OK; } static void njs_json_append_string(njs_chb_t *chain, const njs_value_t *value, char quote) { size_t size; u_char c, *dst, *dst_end; njs_bool_t utf8; const u_char *p, *end; njs_string_prop_t string; static char hex2char[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; (void) njs_string_prop(&string, value); p = string.start; end = p + string.size; utf8 = (string.length != 0 && string.length != string.size); size = njs_max(string.size + 2, 7); dst = njs_chb_reserve(chain, size); if (njs_slow_path(dst == NULL)) { return; } dst_end = dst + size; *dst++ = quote; njs_chb_written(chain, 1); while (p < end) { if (njs_slow_path(dst_end <= dst + njs_length("\\uXXXX"))) { size = njs_max(end - p + 1, 6); dst = njs_chb_reserve(chain, size); if (njs_slow_path(dst == NULL)) { return; } dst_end = dst + size; } if (njs_slow_path(*p < ' ' || *p == '\\' || (*p == '\"' && quote == '\"'))) { c = (u_char) *p++; *dst++ = '\\'; njs_chb_written(chain, 2); switch (c) { case '\\': *dst++ = '\\'; break; case '"': *dst++ = '\"'; break; case '\r': *dst++ = 'r'; break; case '\n': *dst++ = 'n'; break; case '\t': *dst++ = 't'; break; case '\b': *dst++ = 'b'; break; case '\f': *dst++ = 'f'; break; default: *dst++ = 'u'; *dst++ = '0'; *dst++ = '0'; *dst++ = hex2char[(c & 0xf0) >> 4]; *dst++ = hex2char[c & 0x0f]; njs_chb_written(chain, 4); } continue; } if (utf8) { /* UTF-8 string. */ dst = njs_utf8_copy(dst, &p, end); } else { /* ASCII string. */ *dst++ = *p++; } njs_chb_written(chain, dst - chain->last->pos); } njs_chb_append(chain, "e, 1); } static void njs_json_append_number(njs_chb_t *chain, const njs_value_t *value) { u_char *p; size_t size; double num; num = njs_number(value); if (isnan(num) || isinf(num)) { njs_chb_append_literal(chain, "null"); } else { p = njs_chb_reserve(chain, 64); if (njs_slow_path(p == NULL)) { return; } size = njs_dtoa(num, (char *) p); njs_chb_written(chain, size); } } /* * Wraps a value as '{"": }'. */ static njs_object_t * njs_json_wrap_value(njs_vm_t *vm, njs_value_t *wrapper, const njs_value_t *value) { njs_int_t ret; njs_object_prop_t *prop; njs_lvlhsh_query_t lhq; wrapper->data.u.object = njs_object_alloc(vm); if (njs_slow_path(njs_object(wrapper) == NULL)) { return NULL; } wrapper->type = NJS_OBJECT; wrapper->data.truth = 1; lhq.replace = 0; lhq.proto = &njs_object_hash_proto; lhq.pool = vm->mem_pool; lhq.key = njs_str_value(""); lhq.key_hash = NJS_DJB_HASH_INIT; prop = njs_object_prop_alloc(vm, &njs_string_empty, value, 1); if (njs_slow_path(prop == NULL)) { return NULL; } lhq.value = prop; ret = njs_lvlhsh_insert(njs_object_hash(wrapper), &lhq); if (njs_slow_path(ret != NJS_OK)) { return NULL; } return wrapper->data.u.object; } static const njs_object_prop_t njs_json_object_properties[] = { { .type = NJS_PROPERTY, .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG), .u.value = njs_string("JSON"), .configurable = 1, }, NJS_DECLARE_PROP_NATIVE("parse", njs_json_parse, 2, 0), NJS_DECLARE_PROP_NATIVE("stringify", njs_json_stringify, 3, 0), }; const njs_object_init_t njs_json_object_init = { njs_json_object_properties, njs_nitems(njs_json_object_properties), }; static njs_int_t njs_dump_terminal(njs_json_stringify_t *stringify, njs_chb_t *chain, njs_value_t *value, njs_uint_t console) { njs_str_t str; njs_int_t ret; njs_value_t str_val, tag; njs_typed_array_t *array; njs_string_prop_t string; static const njs_value_t name_string = njs_string("name"); njs_int_t (*to_string)(njs_vm_t *, njs_value_t *, const njs_value_t *); switch (value->type) { case NJS_NULL: njs_chb_append_literal(chain, "null"); break; case NJS_UNDEFINED: njs_chb_append_literal(chain, "undefined"); break; case NJS_BOOLEAN: if (njs_is_true(value)) { njs_chb_append_literal(chain, "true"); } else { njs_chb_append_literal(chain, "false"); } break; case NJS_STRING: njs_string_get(value, &str); if (!console || stringify->depth != 0) { njs_json_append_string(chain, value, '\''); return NJS_OK; } njs_chb_append_str(chain, &str); break; case NJS_SYMBOL: ret = njs_symbol_descriptive_string(stringify->vm, &str_val, value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_string_get(&str_val, &str); njs_chb_append_str(chain, &str); break; case NJS_INVALID: /* Fall through. */ break; case NJS_OBJECT_VALUE: value = njs_object_value(value); switch (value->type) { case NJS_BOOLEAN: if (njs_is_true(value)) { njs_chb_append_literal(chain, "[Boolean: true]"); } else { njs_chb_append_literal(chain, "[Boolean: false]"); } break; case NJS_NUMBER: if (njs_slow_path(njs_number(value) == 0.0 && signbit(njs_number(value)))) { njs_chb_append_literal(chain, "[Number: -0]"); break; } ret = njs_number_to_string(stringify->vm, &str_val, value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_string_get(&str_val, &str); njs_chb_sprintf(chain, 16 + str.length, "[Number: %V]", &str); break; case NJS_SYMBOL: ret = njs_symbol_descriptive_string(stringify->vm, &str_val, value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_string_get(&str_val, &str); njs_chb_sprintf(chain, 16 + str.length, "[Symbol: %V]", &str); break; case NJS_STRING: default: njs_chb_append_literal(chain, "[String: "); njs_json_append_string(chain, value, '\''); njs_chb_append_literal(chain, "]"); break; } break; case NJS_FUNCTION: if (njs_function(value)->native) { str = njs_str_value("native"); } else { str = njs_str_value(""); } ret = njs_value_property(stringify->vm, value, njs_value_arg(&name_string), &tag); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (njs_is_string(&tag)) { njs_string_get(&tag, &str); } if (str.length != 0) { njs_chb_sprintf(chain, 32 + str.length, "[Function: %V]", &str); } else { njs_chb_append_literal(chain, "[Function]"); } break; case NJS_TYPED_ARRAY: array = njs_typed_array(value); ret = njs_object_string_tag(stringify->vm, value, &tag); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (ret == NJS_OK) { (void) njs_string_prop(&string, &tag); njs_chb_append(chain, string.start, string.size); njs_chb_append_literal(chain, " "); } njs_chb_append_literal(chain, "["); njs_typed_array_to_chain(stringify->vm, chain, array, NULL); njs_chb_append_literal(chain, "]"); break; case NJS_NUMBER: if (njs_slow_path(njs_number(value) == 0.0 && signbit(njs_number(value)))) { njs_chb_append_literal(chain, "-0"); break; } /* Fall through. */ case NJS_OBJECT: case NJS_REGEXP: case NJS_DATE: switch (value->type) { case NJS_NUMBER: to_string = njs_number_to_string; break; case NJS_REGEXP: to_string = njs_regexp_to_string; break; case NJS_DATE: to_string = njs_date_to_string; break; default: to_string = njs_error_to_string; } ret = to_string(stringify->vm, &str_val, value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_string_get(&str_val, &str); njs_chb_append_str(chain, &str); break; default: njs_chb_sprintf(chain, 64, "[Unknown value type:%uD]", value->type); } return NJS_OK; } njs_inline njs_bool_t njs_dump_is_recursive(const njs_value_t *value) { return (value->type == NJS_OBJECT && !njs_object(value)->error_data) || (value->type == NJS_ARRAY) || (value->type >= NJS_OBJECT_SPECIAL_MAX && !njs_is_object_primitive(value)); } njs_inline njs_int_t njs_dump_visited(njs_vm_t *vm, njs_json_stringify_t *stringify, const njs_value_t *value) { njs_int_t depth; depth = stringify->depth - 1; for (; depth >= 0; depth--) { if (njs_values_same(&stringify->states[depth].value, value)) { return 1; } } return 0; } njs_inline void njs_dump_empty(njs_json_stringify_t *stringify, njs_json_state_t *state, njs_chb_t *chain, njs_bool_t sep_position) { double key, prev; int64_t diff; if (!state->array) { return; } if (sep_position) { key = njs_key_to_index(state->key); prev = (state->index > 1) ? njs_key_to_index(&state->key[-1]) : -1; } else { key = state->length; if (state->key == NULL) { prev = -1; } else { prev = (state->index > 0) ? njs_key_to_index(state->key) : -1; } } if (isnan(prev)) { return; } if (isnan(key)) { key = state->length; } diff = key - prev; if (diff > 1) { if (sep_position == 0 && state->keys->length) { if (prev != -1) { njs_chb_append_literal(chain, ","); njs_json_stringify_indent(stringify, chain, 1); } } if (diff - 1 == 1) { njs_chb_sprintf(chain, 64, ""); } else { njs_chb_sprintf(chain, 64, "<%L empty items>", diff - 1); } state->written = 1; if (sep_position == 1 && state->keys->length) { njs_chb_append_literal(chain, ","); njs_json_stringify_indent(stringify, chain, 1); } } } static const njs_value_t string_get = njs_string("[Getter]"); static const njs_value_t string_set = njs_string("[Setter]"); static const njs_value_t string_get_set = njs_long_string("[Getter/Setter]"); njs_int_t njs_vm_value_dump(njs_vm_t *vm, njs_str_t *retval, njs_value_t *value, njs_uint_t console, njs_uint_t indent) { njs_int_t ret; njs_chb_t chain; njs_str_t str; njs_value_t *key, *val, tag, exception; njs_json_state_t *state; njs_string_prop_t string; njs_object_prop_t *prop; njs_property_query_t pq; njs_json_stringify_t *stringify, dump_stringify; stringify = &dump_stringify; stringify->vm = vm; stringify->depth = 0; if (njs_slow_path(vm->top_frame == NULL)) { /* An exception was thrown during compilation. */ njs_vm_runtime_init(vm); } if (njs_is_valid(&vm->exception)) { exception = njs_vm_exception(vm); value = &exception; } NJS_CHB_MP_INIT(&chain, vm); if (!njs_dump_is_recursive(value)) { ret = njs_dump_terminal(stringify, &chain, value, console); if (njs_slow_path(ret != NJS_OK)) { goto memory_error; } goto done; } njs_set_undefined(&stringify->replacer); stringify->keys_type = NJS_ENUM_STRING | NJS_ENUM_SYMBOL; indent = njs_min(indent, 10); stringify->space.length = indent; stringify->space.start = stringify->space_buf; njs_memset(stringify->space.start, ' ', indent); state = njs_json_push_stringify_state(stringify, value); if (njs_slow_path(state == NULL)) { goto memory_error; } for ( ;; ) { if (state->index == 0) { ret = njs_object_string_tag(vm, &state->value, &tag); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (ret == NJS_OK) { (void) njs_string_prop(&string, &tag); njs_chb_append(&chain, string.start, string.size); njs_chb_append_literal(&chain, " "); } njs_chb_append(&chain, state->array ? "[" : "{", 1); njs_json_stringify_indent(stringify, &chain, 1); } if (njs_json_stringify_done(state, 0)) { njs_dump_empty(stringify, state, &chain, 0); njs_json_stringify_indent(stringify, &chain, 0); njs_chb_append(&chain, state->array ? "]" : "}", 1); state = njs_json_pop_stringify_state(stringify); if (state == NULL) { goto done; } continue; } njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0, 0); key = &state->keys->start[state->index++]; if(state->array) { if (key->type == NJS_STRING) { njs_string_get(key, &str); if (str.length == 6 && memcmp(str.start, "length", 6) == 0) { continue; } } } state->key = key; ret = njs_property_query(vm, &pq, &state->value, key); if (njs_slow_path(ret != NJS_OK)) { if (ret == NJS_DECLINED) { continue; } goto exception; } prop = pq.lhq.value; if (prop->type == NJS_WHITEOUT || !prop->enumerable) { if (!state->array) { continue; } } if (state->written) { njs_chb_append_literal(&chain, ","); njs_json_stringify_indent(stringify, &chain, 1); } state->written = 1; njs_dump_empty(stringify, state, &chain, 1); if (!state->array || isnan(njs_key_to_index(key))) { njs_key_string_get(vm, key, &pq.lhq.key); njs_chb_append(&chain, pq.lhq.key.start, pq.lhq.key.length); njs_chb_append_literal(&chain, ":"); if (stringify->space.length != 0) { njs_chb_append_literal(&chain, " "); } } val = njs_prop_value(prop); if (prop->type == NJS_PROPERTY_HANDLER) { pq.scratch = *prop; prop = &pq.scratch; ret = njs_prop_handler(prop)(vm, prop, &state->value, NULL, njs_prop_value(prop)); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } val = njs_prop_value(prop); } if (njs_is_accessor_descriptor(prop)) { if (njs_prop_getter(prop) != NULL) { if (njs_prop_setter(prop) != NULL) { val = njs_value_arg(&string_get_set); } else { val = njs_value_arg(&string_get); } } else { val = njs_value_arg(&string_set); } } if (njs_dump_is_recursive(val)) { if (njs_slow_path(njs_dump_visited(vm, stringify, val))) { njs_chb_append_literal(&chain, "[Circular]"); continue; } state = njs_json_push_stringify_state(stringify, val); if (njs_slow_path(state == NULL)) { goto exception; } continue; } ret = njs_dump_terminal(stringify, &chain, val, console); if (njs_slow_path(ret != NJS_OK)) { if (ret == NJS_DECLINED) { goto exception; } goto memory_error; } } done: ret = njs_chb_join(&chain, &str); if (njs_slow_path(ret != NJS_OK)) { goto memory_error; } njs_chb_destroy(&chain); *retval = str; return NJS_OK; memory_error: njs_memory_error(vm); exception: njs_vm_value_string(vm, retval, &vm->exception); return NJS_OK; } njs-0.8.9/src/njs_json.h000066400000000000000000000003411474132077100151110ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_JSON_H_INCLUDED_ #define _NJS_JSON_H_INCLUDED_ extern const njs_object_init_t njs_json_object_init; #endif /* _NJS_JSON_H_INCLUDED_ */ njs-0.8.9/src/njs_lexer.c000066400000000000000000000731601474132077100152630ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include 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_int_t njs_lexer_hash_test(njs_lvlhsh_query_t *lhq, void *data); static njs_int_t njs_lexer_word(njs_lexer_t *lexer, njs_lexer_token_t *token); static void njs_lexer_string(njs_lexer_t *lexer, njs_lexer_token_t *token, u_char quote); static void njs_lexer_number(njs_lexer_t *lexer, njs_lexer_token_t *token); static void njs_lexer_multi(njs_lexer_t *lexer, njs_lexer_token_t *token, const njs_lexer_multi_t *multi, size_t length); static void njs_lexer_division(njs_lexer_t *lexer, njs_lexer_token_t *token); const njs_lvlhsh_proto_t njs_lexer_hash_proto njs_aligned(64) = { NJS_LVLHSH_DEFAULT, njs_lexer_hash_test, njs_lvlhsh_alloc, njs_lvlhsh_free, }; static const uint8_t njs_tokens[256] njs_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_SPACE, /* \r */ NJS_TOKEN_SPACE, NJS_TOKEN_SPACE, 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_SUBTRACTION, /* . / */ 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_GRAVE, 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_SUBTRACTION_ASSIGNMENT, 0, NULL }, }; static const njs_lexer_multi_t njs_exponentiation_token[] = { { '=', NJS_TOKEN_EXPONENTIATION_ASSIGNMENT, 0, NULL }, }; static const njs_lexer_multi_t njs_multiplication_token[] = { { '=', NJS_TOKEN_MULTIPLICATION_ASSIGNMENT, 0, NULL }, { '*', NJS_TOKEN_EXPONENTIATION, 1, njs_exponentiation_token }, }; 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_strict_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_conditional_token[] = { { '?', NJS_TOKEN_COALESCE, 0, NULL }, }; static const njs_lexer_multi_t njs_assignment_token[] = { { '=', NJS_TOKEN_EQUAL, 1, njs_strict_equal_token }, { '>', NJS_TOKEN_ARROW, 0, NULL }, }; njs_int_t njs_lexer_init(njs_vm_t *vm, njs_lexer_t *lexer, njs_str_t *file, u_char *start, u_char *end, njs_uint_t runtime, njs_int_t init_lexer_memory) { if (init_lexer_memory) { njs_memzero(lexer, sizeof(njs_lexer_t)); } lexer->file = *file; lexer->start = start; lexer->end = end; lexer->line = 1; lexer->keywords_hash = (runtime) ? &vm->keywords_hash : &vm->shared->keywords_hash; lexer->mem_pool = vm->mem_pool; njs_queue_init(&lexer->preread); return njs_lexer_in_stack_init(lexer); } njs_int_t njs_lexer_in_stack_init(njs_lexer_t *lexer) { lexer->in_stack_size = 128; lexer->in_stack = njs_mp_zalloc(lexer->mem_pool, lexer->in_stack_size); if (lexer->in_stack == NULL) { return NJS_ERROR; } lexer->in_stack_ptr = 0; return NJS_OK; } njs_int_t njs_lexer_in_stack_push(njs_lexer_t *lexer) { u_char *tmp; size_t size; lexer->in_stack_ptr++; if (lexer->in_stack_ptr < lexer->in_stack_size) { lexer->in_stack[lexer->in_stack_ptr] = 0; return NJS_OK; } /* Realloc in_stack, it is up to higher layer generate error if any. */ size = lexer->in_stack_size; lexer->in_stack_size = size * 2; tmp = njs_mp_alloc(lexer->mem_pool, size * 2); if (tmp == NULL) { return NJS_ERROR; } memcpy(tmp, lexer->in_stack, size); memset(&tmp[size], 0, size); njs_mp_free(lexer->mem_pool, lexer->in_stack); lexer->in_stack = tmp; return NJS_OK; } void njs_lexer_in_stack_pop(njs_lexer_t *lexer) { /** * if in_stack_ptr <= 0 do nothing, it is up to higher layer * generate error. */ if (lexer->in_stack_ptr > 0) { lexer->in_stack_ptr--; } } njs_int_t njs_lexer_in_fail_get(njs_lexer_t *lexer) { return lexer->in_stack[lexer->in_stack_ptr]; } void njs_lexer_in_fail_set(njs_lexer_t *lexer, njs_int_t flag) { lexer->in_stack[lexer->in_stack_ptr] = flag; } njs_inline njs_int_t njs_lexer_in_stack(njs_lexer_t *lexer, njs_lexer_token_t *token) { switch (token->type) { case NJS_TOKEN_OPEN_PARENTHESIS: case NJS_TOKEN_OPEN_BRACKET: case NJS_TOKEN_OPEN_BRACE: return njs_lexer_in_stack_push(lexer); case NJS_TOKEN_CLOSE_PARENTHESIS: case NJS_TOKEN_CLOSE_BRACKET: case NJS_TOKEN_CLOSE_BRACE: njs_lexer_in_stack_pop(lexer); break; default: break; } return NJS_OK; } njs_inline njs_lexer_token_t * njs_lexer_next_token(njs_lexer_t *lexer) { njs_int_t ret; njs_lexer_token_t *token; token = njs_mp_zalloc(lexer->mem_pool, sizeof(njs_lexer_token_t)); if (njs_slow_path(token == NULL)) { return NULL; } do { ret = njs_lexer_make_token(lexer, token); if (njs_slow_path(ret != NJS_OK)) { return NULL; } } while (token->type == NJS_TOKEN_COMMENT); njs_queue_insert_tail(&lexer->preread, &token->link); ret = njs_lexer_in_stack(lexer, token); if (njs_slow_path(ret != NJS_OK)) { return NULL; } return token; } njs_lexer_token_t * njs_lexer_token(njs_lexer_t *lexer, njs_bool_t with_end_line) { njs_queue_link_t *lnk; njs_lexer_token_t *token; lnk = njs_queue_first(&lexer->preread); while (lnk != njs_queue_head(&lexer->preread)) { token = njs_queue_link_data(lnk, njs_lexer_token_t, link); if (!with_end_line && token->type == NJS_TOKEN_LINE_END) { lexer->prev_type = token->type; lnk = njs_queue_next(&token->link); continue; } return token; } do { token = njs_lexer_next_token(lexer); if (token == NULL) { return NULL; } if (!with_end_line && token->type == NJS_TOKEN_LINE_END) { lexer->prev_type = token->type; continue; } break; } while (1); return token; } njs_lexer_token_t * njs_lexer_peek_token(njs_lexer_t *lexer, njs_lexer_token_t *current, njs_bool_t with_end_line) { njs_queue_link_t *lnk; njs_lexer_token_t *token; lnk = njs_queue_next(¤t->link); while (lnk != njs_queue_head(&lexer->preread)) { token = njs_queue_link_data(lnk, njs_lexer_token_t, link); if (!with_end_line && token->type == NJS_TOKEN_LINE_END) { lnk = njs_queue_next(&token->link); continue; } return token; } do { token = njs_lexer_next_token(lexer); if (token == NULL) { return NULL; } if (!with_end_line && token->type == NJS_TOKEN_LINE_END) { continue; } break; } while (1); return token; } void njs_lexer_consume_token(njs_lexer_t *lexer, unsigned length) { njs_queue_link_t *lnk; njs_lexer_token_t *token; while (length > 0) { lnk = njs_queue_first(&lexer->preread); token = njs_queue_link_data(lnk, njs_lexer_token_t, link); lexer->prev_type = token->type; if (token->type != NJS_TOKEN_LINE_END) { length--; } njs_queue_remove(lnk); njs_mp_free(lexer->mem_pool, token); } } njs_int_t njs_lexer_make_token(njs_lexer_t *lexer, njs_lexer_token_t *token) { u_char c, *p; uint32_t cp; njs_unicode_decode_t ctx; c = ' '; njs_utf8_decode_init(&ctx); while (lexer->start < lexer->end) { c = *lexer->start; if (njs_fast_path(!(c & 0x80))) { lexer->start++; if (njs_tokens[c] != NJS_TOKEN_SPACE) { break; } } else { /* Unicode. */ cp = njs_utf8_decode(&ctx, (const u_char **) &lexer->start, lexer->end); if (njs_slow_path(cp > NJS_UNICODE_MAX_CODEPOINT)) { c = '\0'; break; } if (!njs_utf8_is_whitespace(cp)) { break; } } } token->type = njs_tokens[c]; token->line = lexer->line; switch (token->type) { case NJS_TOKEN_LETTER: return njs_lexer_word(lexer, token); case NJS_TOKEN_DOUBLE_QUOTE: case NJS_TOKEN_SINGLE_QUOTE: njs_lexer_string(lexer, token, c); break; case NJS_TOKEN_DOT: p = lexer->start; if (p + 1 < lexer->end && njs_tokens[p[0]] == NJS_TOKEN_DOT && njs_tokens[p[1]] == NJS_TOKEN_DOT) { token->text.start = lexer->start - 1; token->text.length = (p - token->text.start) + 2; token->type = NJS_TOKEN_ELLIPSIS; lexer->start += 2; return NJS_OK; } if (p == lexer->end || njs_tokens[*p] != NJS_TOKEN_DIGIT) { token->text.start = lexer->start - 1; token->text.length = p - token->text.start; token->type = NJS_TOKEN_DOT; return NJS_OK; } /* Fall through. */ case NJS_TOKEN_DIGIT: njs_lexer_number(lexer, token); break; case NJS_TOKEN_DIVISION: njs_lexer_division(lexer, token); break; case NJS_TOKEN_ASSIGNMENT: njs_lexer_multi(lexer, token, njs_assignment_token, njs_nitems(njs_assignment_token)); break; case NJS_TOKEN_ADDITION: njs_lexer_multi(lexer, token, njs_addition_token, njs_nitems(njs_addition_token)); break; case NJS_TOKEN_SUBTRACTION: njs_lexer_multi(lexer, token, njs_substraction_token, njs_nitems(njs_substraction_token)); break; case NJS_TOKEN_MULTIPLICATION: njs_lexer_multi(lexer, token, njs_multiplication_token, njs_nitems(njs_multiplication_token)); break; case NJS_TOKEN_REMAINDER: njs_lexer_multi(lexer, token, njs_remainder_token, njs_nitems(njs_remainder_token)); break; case NJS_TOKEN_BITWISE_AND: njs_lexer_multi(lexer, token, njs_bitwise_and_token, njs_nitems(njs_bitwise_and_token)); break; case NJS_TOKEN_BITWISE_XOR: njs_lexer_multi(lexer, token, njs_bitwise_xor_token, njs_nitems(njs_bitwise_xor_token)); break; case NJS_TOKEN_BITWISE_OR: njs_lexer_multi(lexer, token, njs_bitwise_or_token, njs_nitems(njs_bitwise_or_token)); break; case NJS_TOKEN_LOGICAL_NOT: njs_lexer_multi(lexer, token, njs_logical_not_token, njs_nitems(njs_logical_not_token)); break; case NJS_TOKEN_LESS: njs_lexer_multi(lexer, token, njs_less_token, njs_nitems(njs_less_token)); break; case NJS_TOKEN_GREATER: njs_lexer_multi(lexer, token, njs_greater_token, njs_nitems(njs_greater_token)); break; case NJS_TOKEN_CONDITIONAL: njs_lexer_multi(lexer, token, njs_conditional_token, njs_nitems(njs_conditional_token)); break; case NJS_TOKEN_SPACE: token->type = NJS_TOKEN_END; return NJS_OK; case NJS_TOKEN_LINE_END: lexer->line++; /* Fall through. */ default: token->text.start = lexer->start - 1; token->text.length = lexer->start - token->text.start; break; } return NJS_OK; } static njs_int_t njs_lexer_hash_test(njs_lvlhsh_query_t *lhq, void *data) { njs_lexer_entry_t *entry; entry = data; if (entry->name.length == lhq->key.length && memcmp(entry->name.start, lhq->key.start, lhq->key.length) == 0) { return NJS_OK; } return NJS_DECLINED; } static njs_lexer_entry_t * njs_lexer_keyword_find(njs_lexer_t *lexer, u_char *key, size_t length, uint32_t hash) { njs_int_t ret; njs_lexer_entry_t *entry; njs_lvlhsh_query_t lhq; lhq.key.start = key; lhq.key.length = length; lhq.key_hash = hash; lhq.proto = &njs_lexer_hash_proto; ret = njs_lvlhsh_find(lexer->keywords_hash, &lhq); if (ret == NJS_OK) { return lhq.value; } entry = njs_mp_alloc(lexer->mem_pool, sizeof(njs_lexer_entry_t)); if (njs_slow_path(entry == NULL)) { return NULL; } entry->name.start = njs_mp_alloc(lexer->mem_pool, length + 1); if (njs_slow_path(entry->name.start == NULL)) { return NULL; } memcpy(entry->name.start, key, length); entry->name.start[length] = '\0'; entry->name.length = length; lhq.value = entry; lhq.pool = lexer->mem_pool; ret = njs_lvlhsh_insert(lexer->keywords_hash, &lhq); if (njs_slow_path(ret != NJS_OK)) { return NULL; } return entry; } static njs_int_t njs_lexer_word(njs_lexer_t *lexer, njs_lexer_token_t *token) { u_char *p, c; uint32_t hash_id; const njs_lexer_entry_t *entry; const njs_lexer_keyword_entry_t *key_entry; /* TODO: UTF-8 */ static const uint8_t letter_digit[32] njs_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 */ }; token->text.start = lexer->start - 1; hash_id = njs_djb_hash_add(NJS_DJB_HASH_INIT, *token->text.start); for (p = lexer->start; p < lexer->end; p++) { c = *p; if ((letter_digit[c / 8] & (1 << (c & 7))) == 0) { break; } hash_id = njs_djb_hash_add(hash_id, c); } token->text.length = p - token->text.start; lexer->start = p; key_entry = njs_lexer_keyword(token->text.start, token->text.length); if (key_entry == NULL) { entry = njs_lexer_keyword_find(lexer, token->text.start, token->text.length, hash_id); if (njs_slow_path(entry == NULL)) { return NJS_ERROR; } token->type = NJS_TOKEN_NAME; token->keyword_type = NJS_KEYWORD_TYPE_UNDEF; } else { entry = &key_entry->value->entry; token->type = key_entry->value->type; token->keyword_type = NJS_KEYWORD_TYPE_KEYWORD; token->keyword_type |= key_entry->value->reserved; } token->unique_id = (uintptr_t) entry; return NJS_OK; } static void njs_lexer_string(njs_lexer_t *lexer, njs_lexer_token_t *token, u_char quote) { u_char *p, c; njs_bool_t escape; escape = 0; p = lexer->start; token->text.start = p; while (p < lexer->end) { c = *p++; if (c == '\\') { if (p == lexer->end) { break; } p++; /* Line continuation. */ if (p < lexer->end && p[-1] == '\r' && p[0] == '\n') { p++; } escape = 1; continue; } /* Line terminator. */ if (c == '\r' || c == '\n') { break; } if (c == quote) { lexer->start = p; token->text.length = (p - 1) - token->text.start; token->type = (escape == 0) ? NJS_TOKEN_STRING : NJS_TOKEN_ESCAPE_STRING; return; } } token->text.start--; token->text.length = p - token->text.start; token->type = NJS_TOKEN_UNTERMINATED_STRING; } static void njs_lexer_number(njs_lexer_t *lexer, njs_lexer_token_t *token) { u_char c; const u_char *p; c = lexer->start[-1]; p = lexer->start; token->text.start = lexer->start - 1; if (c == '0' && p != lexer->end) { /* Hexadecimal literal values. */ if (*p == 'x' || *p == 'X') { p++; if (p == lexer->end || njs_char_to_hex(*p) < 0) { goto illegal_token; } token->number = njs_number_hex_parse(&p, lexer->end, 1); goto done; } /* Octal literal values. */ if (*p == 'o' || *p == 'O') { p++; if (p == lexer->end || (u_char)(*p - '0') > 7) { goto illegal_token; } token->number = njs_number_oct_parse(&p, lexer->end, 1); if (p < lexer->end && (*p == '8' || *p == '9')) { goto illegal_trailer; } goto done; } /* Binary literal values. */ if (*p == 'b' || *p == 'B') { p++; if (p == lexer->end || (u_char)(*p - '0') > 1) { goto illegal_token; } token->number = njs_number_bin_parse(&p, lexer->end, 1); if (p < lexer->end && (*p >= '2' && *p <= '9')) { goto illegal_trailer; } goto done; } /* Legacy Octal literals are deprecated. */ if ((*p >= '0' && *p <= '9') || *p == '_') { goto illegal_trailer; } } p--; token->number = njs_number_dec_parse(&p, lexer->end, 1); done: if (p[-1] == '_') { p--; } lexer->start = (u_char *) p; token->text.length = p - token->text.start; token->type = NJS_TOKEN_NUMBER; return; illegal_trailer: p++; illegal_token: token->text.length = p - token->text.start; token->type = NJS_TOKEN_ILLEGAL; } static void njs_lexer_multi(njs_lexer_t *lexer, njs_lexer_token_t *token, const njs_lexer_multi_t *multi, size_t length) { u_char c; token->line = lexer->line; token->text.start = lexer->start - 1; while (length != 0 && multi != NULL && lexer->start < lexer->end) { c = lexer->start[0]; if (c == multi->symbol) { lexer->start++; token->type = multi->token; if (multi->count == 0) { break; } length = multi->count; multi = multi->next; } else { length--; multi++; } } token->text.length = lexer->start - token->text.start; } static void njs_lexer_division(njs_lexer_t *lexer, njs_lexer_token_t *token) { u_char c, *p; token->text.start = lexer->start - 1; if (lexer->start >= lexer->end) { goto done; } c = lexer->start[0]; if (c == '/') { lexer->start++; for (p = lexer->start; p < lexer->end; p++) { if (*p == '\n' || (p + 1) == lexer->end) { lexer->start = p + 1; lexer->line++; token->type = NJS_TOKEN_LINE_END; goto done; } } } else if (c == '*') { lexer->start++; for (p = lexer->start; p < lexer->end; p++) { if (*p == '\n') { lexer->line++; continue; } if (*p == '*') { if (p + 1 < lexer->end && p[1] == '/') { lexer->start = p + 2; token->type = NJS_TOKEN_COMMENT; goto done; } } } token->type = NJS_TOKEN_ILLEGAL; } else if (c == '=') { lexer->start++; token->type = NJS_TOKEN_DIVISION_ASSIGNMENT; } done: token->text.length = lexer->start - token->text.start; } njs-0.8.9/src/njs_lexer.h000066400000000000000000000204231474132077100152620ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_LEXER_H_INCLUDED_ #define _NJS_LEXER_H_INCLUDED_ typedef enum { 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_ELLIPSIS, NJS_TOKEN_SEMICOLON, NJS_TOKEN_COLON, NJS_TOKEN_CONDITIONAL, NJS_TOKEN_COMMENT, NJS_TOKEN_ASSIGNMENT, NJS_TOKEN_ARROW, NJS_TOKEN_ADDITION_ASSIGNMENT, NJS_TOKEN_SUBTRACTION_ASSIGNMENT, NJS_TOKEN_MULTIPLICATION_ASSIGNMENT, NJS_TOKEN_EXPONENTIATION_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, NJS_TOKEN_INCREMENT, NJS_TOKEN_DECREMENT, NJS_TOKEN_POST_INCREMENT, NJS_TOKEN_POST_DECREMENT, #define NJS_TOKEN_LAST_ASSIGNMENT NJS_TOKEN_POST_DECREMENT 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_SUBTRACTION, NJS_TOKEN_UNARY_NEGATION, NJS_TOKEN_MULTIPLICATION, NJS_TOKEN_EXPONENTIATION, 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_COALESCE, NJS_TOKEN_IN, NJS_TOKEN_OF, NJS_TOKEN_INSTANCEOF, NJS_TOKEN_TYPEOF, NJS_TOKEN_VOID, NJS_TOKEN_NEW, NJS_TOKEN_DELETE, NJS_TOKEN_YIELD, NJS_TOKEN_DIGIT, NJS_TOKEN_LETTER, #define NJS_TOKEN_FIRST_CONST NJS_TOKEN_NULL NJS_TOKEN_NULL, NJS_TOKEN_NUMBER, NJS_TOKEN_TRUE, NJS_TOKEN_UNDEFINED, NJS_TOKEN_FALSE, NJS_TOKEN_STRING, #define NJS_TOKEN_LAST_CONST NJS_TOKEN_STRING NJS_TOKEN_ESCAPE_STRING, NJS_TOKEN_UNTERMINATED_STRING, NJS_TOKEN_NAME, NJS_TOKEN_OBJECT, NJS_TOKEN_OBJECT_VALUE, NJS_TOKEN_PROPERTY, NJS_TOKEN_PROPERTY_INIT, NJS_TOKEN_PROPERTY_DELETE, NJS_TOKEN_PROPERTY_GETTER, NJS_TOKEN_PROPERTY_SETTER, NJS_TOKEN_PROTO_INIT, NJS_TOKEN_ARRAY, NJS_TOKEN_GRAVE, NJS_TOKEN_TEMPLATE_LITERAL, NJS_TOKEN_FUNCTION, NJS_TOKEN_FUNCTION_DECLARATION, NJS_TOKEN_FUNCTION_EXPRESSION, NJS_TOKEN_FUNCTION_CALL, NJS_TOKEN_METHOD_CALL, NJS_TOKEN_ARGUMENT, NJS_TOKEN_RETURN, NJS_TOKEN_ASYNC_FUNCTION, NJS_TOKEN_ASYNC_FUNCTION_DECLARATION, NJS_TOKEN_ASYNC_FUNCTION_EXPRESSION, NJS_TOKEN_REGEXP, NJS_TOKEN_EXTERNAL, NJS_TOKEN_STATEMENT, NJS_TOKEN_BLOCK, NJS_TOKEN_VAR, NJS_TOKEN_IF, NJS_TOKEN_ELSE, NJS_TOKEN_BRANCHING, NJS_TOKEN_WHILE, NJS_TOKEN_DO, NJS_TOKEN_FOR, NJS_TOKEN_FOR_IN, 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_ARGUMENTS, NJS_TOKEN_EVAL, NJS_TOKEN_IMPORT, NJS_TOKEN_EXPORT, NJS_TOKEN_TARGET, NJS_TOKEN_FROM, NJS_TOKEN_META, NJS_TOKEN_AWAIT, NJS_TOKEN_ASYNC, NJS_TOKEN_CLASS, NJS_TOKEN_CONST, NJS_TOKEN_DEBUGGER, NJS_TOKEN_ENUM, NJS_TOKEN_EXTENDS, NJS_TOKEN_IMPLEMENTS, NJS_TOKEN_INTERFACE, NJS_TOKEN_LET, NJS_TOKEN_PACKAGE, NJS_TOKEN_PRIVATE, NJS_TOKEN_PROTECTED, NJS_TOKEN_PUBLIC, NJS_TOKEN_STATIC, NJS_TOKEN_SUPER, NJS_TOKEN_RESERVED, } njs_token_type_t; typedef enum { NJS_KEYWORD_TYPE_UNDEF = 0, NJS_KEYWORD_TYPE_RESERVED = 1, NJS_KEYWORD_TYPE_KEYWORD = 2 } njs_keyword_type_t; typedef struct { njs_str_t name; } njs_lexer_entry_t; typedef struct { njs_lexer_entry_t entry; njs_token_type_t type; njs_bool_t reserved; } njs_keyword_t; typedef struct { const char *key; const njs_keyword_t *value; size_t length; size_t next; } njs_lexer_keyword_entry_t; typedef struct { njs_token_type_t type:16; njs_keyword_type_t keyword_type; uint32_t line; uintptr_t unique_id; njs_str_t text; double number; njs_queue_link_t link; } njs_lexer_token_t; typedef struct { njs_lexer_token_t *token; njs_queue_t preread; /* of njs_lexer_token_t */ u_char *prev_start; njs_token_type_t prev_type:16; njs_token_type_t last_type:16; uint32_t line; njs_str_t file; njs_lvlhsh_t *keywords_hash; njs_mp_t *mem_pool; u_char *start; u_char *end; #define NJS_INITIAL_IN_STACK_SIZE 128 uint8_t *in_stack; njs_int_t in_stack_ptr; njs_int_t in_stack_size; } njs_lexer_t; njs_int_t njs_lexer_init(njs_vm_t *vm, njs_lexer_t *lexer, njs_str_t *file, u_char *start, u_char *end, njs_uint_t runtime, njs_int_t init_lexer_memory); njs_lexer_token_t *njs_lexer_token(njs_lexer_t *lexer, njs_bool_t with_end_line); njs_lexer_token_t *njs_lexer_peek_token(njs_lexer_t *lexer, njs_lexer_token_t *current, njs_bool_t with_end_line); void njs_lexer_consume_token(njs_lexer_t *lexer, unsigned length); njs_int_t njs_lexer_make_token(njs_lexer_t *lexer, njs_lexer_token_t *token); njs_int_t njs_lexer_in_stack_init(njs_lexer_t *lexer); njs_int_t njs_lexer_in_stack_push(njs_lexer_t *lexer); void njs_lexer_in_stack_pop(njs_lexer_t *lexer); void njs_lexer_in_fail_set(njs_lexer_t *lexer, njs_int_t flag); njs_int_t njs_lexer_in_fail_get(njs_lexer_t *lexer); const njs_lexer_keyword_entry_t *njs_lexer_keyword(const u_char *key, size_t length); njs_int_t njs_lexer_keywords(njs_arr_t *array); njs_inline const njs_lexer_entry_t * njs_lexer_entry(uintptr_t unique_id) { return (const njs_lexer_entry_t *) unique_id; } njs_inline njs_bool_t njs_lexer_token_is_keyword(njs_lexer_token_t *token) { return token->keyword_type & NJS_KEYWORD_TYPE_KEYWORD; } njs_inline njs_bool_t njs_lexer_token_is_reserved(njs_lexer_token_t *token) { return token->keyword_type & NJS_KEYWORD_TYPE_RESERVED; } njs_inline njs_bool_t njs_lexer_token_is_name(njs_lexer_token_t *token) { return token->type == NJS_TOKEN_NAME || (!njs_lexer_token_is_reserved(token) && njs_lexer_token_is_keyword(token)); } njs_inline njs_bool_t njs_lexer_token_is_identifier_name(njs_lexer_token_t *token) { return token->type == NJS_TOKEN_NAME || njs_lexer_token_is_keyword(token); } njs_inline njs_bool_t njs_lexer_token_is_binding_identifier(njs_lexer_token_t *token) { switch (token->type) { case NJS_TOKEN_NAME: case NJS_TOKEN_YIELD: case NJS_TOKEN_AWAIT: return 1; default: return (!njs_lexer_token_is_reserved(token) && njs_lexer_token_is_keyword(token)); }; } njs_inline njs_bool_t njs_lexer_token_is_label_identifier(njs_lexer_token_t *token) { return njs_lexer_token_is_binding_identifier(token); } njs_inline njs_bool_t njs_lexer_token_is_identifier_reference(njs_lexer_token_t *token) { return njs_lexer_token_is_binding_identifier(token); } extern const njs_lvlhsh_proto_t njs_lexer_hash_proto; #endif /* _NJS_LEXER_H_INCLUDED_ */ njs-0.8.9/src/njs_lexer_keyword.c000066400000000000000000000030321474132077100170160ustar00rootroot00000000000000 /* * Copyright (C) NGINX, Inc. */ #include #include njs_inline int njs_lexer_keyword_hash(const u_char *key, size_t size, size_t table_size) { return ((((key[0] * key[size - 1]) + size) % table_size) + 0x01); } njs_inline const njs_lexer_keyword_entry_t * njs_lexer_keyword_entry(const njs_lexer_keyword_entry_t *root, const u_char *key, size_t length) { const njs_lexer_keyword_entry_t *entry; entry = root + njs_lexer_keyword_hash(key, length, root->length); while (entry->key != NULL) { if (entry->length == length) { if (strncmp(entry->key, (char *) key, length) == 0) { return entry; } entry = &root[entry->next]; } else if (entry->length > length) { return NULL; } else { entry = &root[entry->next]; } } return NULL; } const njs_lexer_keyword_entry_t * njs_lexer_keyword(const u_char *key, size_t length) { const njs_lexer_keyword_entry_t *entry; entry = njs_lexer_keyword_entry(njs_lexer_keyword_entries, key, length); if (njs_slow_path(entry == NULL)) { return NULL; } return entry; } njs_int_t njs_lexer_keywords(njs_arr_t *list) { njs_str_t *kw; njs_uint_t i; for (i = 0; i < sizeof(njs_lexer_kws) / sizeof(njs_keyword_t); i++) { kw = njs_arr_add(list); if (njs_slow_path(kw == NULL)) { return NJS_ERROR; } *kw = njs_lexer_kws[i].entry.name; } return NJS_OK; } njs-0.8.9/src/njs_lexer_tables.h000066400000000000000000000230041474132077100166120ustar00rootroot00000000000000 /* * Copyright (C) Nginx, Inc. * * Do not edit, generated by: utils/lexer_keyword.py. */ #ifndef _NJS_LEXER_TABLES_H_INCLUDED_ #define _NJS_LEXER_TABLES_H_INCLUDED_ static const njs_keyword_t njs_lexer_kws[54] = { { .entry = { njs_str("arguments") }, .type = NJS_TOKEN_ARGUMENTS, .reserved = 0 }, { .entry = { njs_str("async") }, .type = NJS_TOKEN_ASYNC, .reserved = 0 }, { .entry = { njs_str("await") }, .type = NJS_TOKEN_AWAIT, .reserved = 1 }, { .entry = { njs_str("break") }, .type = NJS_TOKEN_BREAK, .reserved = 1 }, { .entry = { njs_str("case") }, .type = NJS_TOKEN_CASE, .reserved = 1 }, { .entry = { njs_str("catch") }, .type = NJS_TOKEN_CATCH, .reserved = 1 }, { .entry = { njs_str("class") }, .type = NJS_TOKEN_CLASS, .reserved = 1 }, { .entry = { njs_str("const") }, .type = NJS_TOKEN_CONST, .reserved = 1 }, { .entry = { njs_str("continue") }, .type = NJS_TOKEN_CONTINUE, .reserved = 1 }, { .entry = { njs_str("debugger") }, .type = NJS_TOKEN_DEBUGGER, .reserved = 1 }, { .entry = { njs_str("default") }, .type = NJS_TOKEN_DEFAULT, .reserved = 1 }, { .entry = { njs_str("delete") }, .type = NJS_TOKEN_DELETE, .reserved = 1 }, { .entry = { njs_str("do") }, .type = NJS_TOKEN_DO, .reserved = 1 }, { .entry = { njs_str("else") }, .type = NJS_TOKEN_ELSE, .reserved = 1 }, { .entry = { njs_str("enum") }, .type = NJS_TOKEN_ENUM, .reserved = 1 }, { .entry = { njs_str("eval") }, .type = NJS_TOKEN_EVAL, .reserved = 0 }, { .entry = { njs_str("export") }, .type = NJS_TOKEN_EXPORT, .reserved = 1 }, { .entry = { njs_str("extends") }, .type = NJS_TOKEN_EXTENDS, .reserved = 1 }, { .entry = { njs_str("false") }, .type = NJS_TOKEN_FALSE, .reserved = 1 }, { .entry = { njs_str("finally") }, .type = NJS_TOKEN_FINALLY, .reserved = 1 }, { .entry = { njs_str("for") }, .type = NJS_TOKEN_FOR, .reserved = 1 }, { .entry = { njs_str("from") }, .type = NJS_TOKEN_FROM, .reserved = 0 }, { .entry = { njs_str("function") }, .type = NJS_TOKEN_FUNCTION, .reserved = 1 }, { .entry = { njs_str("if") }, .type = NJS_TOKEN_IF, .reserved = 1 }, { .entry = { njs_str("implements") }, .type = NJS_TOKEN_IMPLEMENTS, .reserved = 1 }, { .entry = { njs_str("import") }, .type = NJS_TOKEN_IMPORT, .reserved = 1 }, { .entry = { njs_str("in") }, .type = NJS_TOKEN_IN, .reserved = 1 }, { .entry = { njs_str("instanceof") }, .type = NJS_TOKEN_INSTANCEOF, .reserved = 1 }, { .entry = { njs_str("interface") }, .type = NJS_TOKEN_INTERFACE, .reserved = 1 }, { .entry = { njs_str("let") }, .type = NJS_TOKEN_LET, .reserved = 1 }, { .entry = { njs_str("meta") }, .type = NJS_TOKEN_META, .reserved = 0 }, { .entry = { njs_str("new") }, .type = NJS_TOKEN_NEW, .reserved = 1 }, { .entry = { njs_str("null") }, .type = NJS_TOKEN_NULL, .reserved = 1 }, { .entry = { njs_str("of") }, .type = NJS_TOKEN_OF, .reserved = 0 }, { .entry = { njs_str("package") }, .type = NJS_TOKEN_PACKAGE, .reserved = 1 }, { .entry = { njs_str("private") }, .type = NJS_TOKEN_PRIVATE, .reserved = 1 }, { .entry = { njs_str("protected") }, .type = NJS_TOKEN_PROTECTED, .reserved = 1 }, { .entry = { njs_str("public") }, .type = NJS_TOKEN_PUBLIC, .reserved = 1 }, { .entry = { njs_str("return") }, .type = NJS_TOKEN_RETURN, .reserved = 1 }, { .entry = { njs_str("static") }, .type = NJS_TOKEN_STATIC, .reserved = 1 }, { .entry = { njs_str("super") }, .type = NJS_TOKEN_SUPER, .reserved = 1 }, { .entry = { njs_str("switch") }, .type = NJS_TOKEN_SWITCH, .reserved = 1 }, { .entry = { njs_str("target") }, .type = NJS_TOKEN_TARGET, .reserved = 0 }, { .entry = { njs_str("this") }, .type = NJS_TOKEN_THIS, .reserved = 1 }, { .entry = { njs_str("throw") }, .type = NJS_TOKEN_THROW, .reserved = 1 }, { .entry = { njs_str("true") }, .type = NJS_TOKEN_TRUE, .reserved = 1 }, { .entry = { njs_str("try") }, .type = NJS_TOKEN_TRY, .reserved = 1 }, { .entry = { njs_str("typeof") }, .type = NJS_TOKEN_TYPEOF, .reserved = 1 }, { .entry = { njs_str("undefined") }, .type = NJS_TOKEN_UNDEFINED, .reserved = 0 }, { .entry = { njs_str("var") }, .type = NJS_TOKEN_VAR, .reserved = 1 }, { .entry = { njs_str("void") }, .type = NJS_TOKEN_VOID, .reserved = 1 }, { .entry = { njs_str("while") }, .type = NJS_TOKEN_WHILE, .reserved = 1 }, { .entry = { njs_str("with") }, .type = NJS_TOKEN_WITH, .reserved = 1 }, { .entry = { njs_str("yield") }, .type = NJS_TOKEN_YIELD, .reserved = 1 }, }; static const njs_lexer_keyword_entry_t njs_lexer_keyword_entries[99] = { { NULL, NULL, 98, 0 }, { "continue", &njs_lexer_kws[8], 8, 0 }, { "finally", &njs_lexer_kws[19], 7, 0 }, { "return", &njs_lexer_kws[38], 6, 0 }, { "static", &njs_lexer_kws[39], 6, 0 }, { "async", &njs_lexer_kws[1], 5, 0 }, { "break", &njs_lexer_kws[3], 5, 0 }, { "interface", &njs_lexer_kws[28], 9, 0 }, { "case", &njs_lexer_kws[4], 4, 0 }, { "import", &njs_lexer_kws[25], 6, 0 }, { "protected", &njs_lexer_kws[36], 9, 0 }, { "switch", &njs_lexer_kws[41], 6, 0 }, { "catch", &njs_lexer_kws[5], 5, 1 }, { "delete", &njs_lexer_kws[11], 6, 0 }, { "else", &njs_lexer_kws[13], 4, 0 }, { "private", &njs_lexer_kws[35], 7, 0 }, { "extends", &njs_lexer_kws[17], 7, 0 }, { "this", &njs_lexer_kws[43], 4, 0 }, { "false", &njs_lexer_kws[18], 5, 0 }, { "await", &njs_lexer_kws[2], 5, 0 }, { NULL, NULL, 0, 0 }, { "public", &njs_lexer_kws[37], 6, 0 }, { NULL, NULL, 0, 0 }, { "class", &njs_lexer_kws[6], 5, 0 }, { "const", &njs_lexer_kws[7], 5, 4 }, { NULL, NULL, 0, 0 }, { "try", &njs_lexer_kws[46], 3, 0 }, { "null", &njs_lexer_kws[32], 4, 0 }, { NULL, NULL, 0, 0 }, { "do", &njs_lexer_kws[12], 2, 0 }, { "var", &njs_lexer_kws[49], 3, 0 }, { "if", &njs_lexer_kws[23], 2, 7 }, { "implements", &njs_lexer_kws[24], 10, 0 }, { "with", &njs_lexer_kws[52], 4, 0 }, { NULL, NULL, 0, 0 }, { "eval", &njs_lexer_kws[15], 4, 9 }, { NULL, NULL, 0, 0 }, { "target", &njs_lexer_kws[42], 6, 0 }, { "enum", &njs_lexer_kws[14], 4, 10 }, { "instanceof", &njs_lexer_kws[27], 10, 0 }, { NULL, NULL, 0, 0 }, { "debugger", &njs_lexer_kws[9], 8, 0 }, { NULL, NULL, 0, 0 }, { NULL, NULL, 0, 0 }, { "default", &njs_lexer_kws[10], 7, 0 }, { "void", &njs_lexer_kws[50], 4, 0 }, { NULL, NULL, 0, 0 }, { NULL, NULL, 0, 0 }, { "undefined", &njs_lexer_kws[48], 9, 0 }, { "from", &njs_lexer_kws[21], 4, 0 }, { "package", &njs_lexer_kws[34], 7, 15 }, { NULL, NULL, 0, 0 }, { "yield", &njs_lexer_kws[53], 5, 0 }, { NULL, NULL, 0, 0 }, { NULL, NULL, 0, 0 }, { "of", &njs_lexer_kws[33], 2, 0 }, { NULL, NULL, 0, 0 }, { "function", &njs_lexer_kws[22], 8, 0 }, { NULL, NULL, 0, 0 }, { "true", &njs_lexer_kws[45], 4, 16 }, { "new", &njs_lexer_kws[31], 3, 0 }, { "export", &njs_lexer_kws[16], 6, 0 }, { NULL, NULL, 0, 0 }, { NULL, NULL, 0, 0 }, { NULL, NULL, 0, 0 }, { NULL, NULL, 0, 0 }, { NULL, NULL, 0, 0 }, { NULL, NULL, 0, 0 }, { "for", &njs_lexer_kws[20], 3, 0 }, { "while", &njs_lexer_kws[51], 5, 0 }, { NULL, NULL, 0, 0 }, { NULL, NULL, 0, 0 }, { NULL, NULL, 0, 0 }, { NULL, NULL, 0, 0 }, { NULL, NULL, 0, 0 }, { NULL, NULL, 0, 0 }, { NULL, NULL, 0, 0 }, { NULL, NULL, 0, 0 }, { NULL, NULL, 0, 0 }, { "typeof", &njs_lexer_kws[47], 6, 0 }, { NULL, NULL, 0, 0 }, { NULL, NULL, 0, 0 }, { "super", &njs_lexer_kws[40], 5, 0 }, { NULL, NULL, 0, 0 }, { NULL, NULL, 0, 0 }, { NULL, NULL, 0, 0 }, { "let", &njs_lexer_kws[29], 3, 19 }, { "in", &njs_lexer_kws[26], 2, 0 }, { NULL, NULL, 0, 0 }, { NULL, NULL, 0, 0 }, { "throw", &njs_lexer_kws[44], 5, 0 }, { "arguments", &njs_lexer_kws[0], 9, 0 }, { "meta", &njs_lexer_kws[30], 4, 0 }, { NULL, NULL, 0, 0 }, { NULL, NULL, 0, 0 }, { NULL, NULL, 0, 0 }, { NULL, NULL, 0, 0 }, { NULL, NULL, 0, 0 }, { NULL, NULL, 0, 0 }, }; #endif /* _NJS_LEXER_TABLES_H_INCLUDED_ */ njs-0.8.9/src/njs_lvlhsh.c000066400000000000000000000543231474132077100154440ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include /* * 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 njs_lvlhsh_is_bucket(p) \ ((uintptr_t) (p) & 1) #define njs_lvlhsh_count_inc(n) \ n = (void *) ((uintptr_t) (n) + 2) #define njs_lvlhsh_count_dec(n) \ n = (void *) ((uintptr_t) (n) - 2) #define njs_lvlhsh_level_size(proto, nlvl) \ ((uintptr_t) 1 << proto->shift[nlvl]) #define njs_lvlhsh_level(lvl, mask) \ (void **) ((uintptr_t) lvl & (~mask << 2)) #define njs_lvlhsh_level_entries(lvl, mask) \ ((uintptr_t) lvl & (mask << 1)) #define njs_lvlhsh_store_bucket(slot, bkt) \ slot = (void **) ((uintptr_t) bkt | 2 | 1) #define njs_lvlhsh_bucket_size(proto) \ proto->bucket_size #define njs_lvlhsh_bucket(proto, bkt) \ (uint32_t *) ((uintptr_t) bkt & ~(uintptr_t) proto->bucket_mask) #define njs_lvlhsh_bucket_entries(proto, bkt) \ (((uintptr_t) bkt & (uintptr_t) proto->bucket_mask) >> 1) #define njs_lvlhsh_bucket_end(proto, bkt) \ &bkt[proto->bucket_end] #define njs_lvlhsh_free_entry(e) \ (!(njs_lvlhsh_valid_entry(e))) #define njs_lvlhsh_next_bucket(proto, bkt) \ ((void **) &bkt[proto->bucket_end]) #if (NJS_64BIT) #define njs_lvlhsh_valid_entry(e) \ (((e)[0] | (e)[1]) != 0) #define njs_lvlhsh_entry_value(e) \ (void *) (((uintptr_t) (e)[1] << 32) + (e)[0]) #define njs_lvlhsh_set_entry_value(e, n) \ (e)[0] = (uint32_t) (uintptr_t) n; \ (e)[1] = (uint32_t) ((uintptr_t) n >> 32) #define njs_lvlhsh_entry_key(e) \ (e)[2] #define njs_lvlhsh_set_entry_key(e, n) \ (e)[2] = n #else #define njs_lvlhsh_valid_entry(e) \ ((e)[0] != 0) #define njs_lvlhsh_entry_value(e) \ (void *) (e)[0] #define njs_lvlhsh_set_entry_value(e, n) \ (e)[0] = (uint32_t) n #define njs_lvlhsh_entry_key(e) \ (e)[1] #define njs_lvlhsh_set_entry_key(e, n) \ (e)[1] = n #endif #define NJS_LVLHSH_BUCKET_DONE ((void *) -1) static njs_int_t njs_lvlhsh_level_find(njs_lvlhsh_query_t *lhq, void **lvl, uint32_t key, njs_uint_t nlvl); static njs_int_t njs_lvlhsh_bucket_find(njs_lvlhsh_query_t *lhq, void **bkt); static njs_int_t njs_lvlhsh_new_bucket(njs_lvlhsh_query_t *lhq, void **slot); static njs_int_t njs_lvlhsh_level_insert(njs_lvlhsh_query_t *lhq, void **slot, uint32_t key, njs_uint_t nlvl); static njs_int_t njs_lvlhsh_bucket_insert(njs_lvlhsh_query_t *lhq, void **slot, uint32_t key, njs_int_t nlvl); static njs_int_t njs_lvlhsh_convert_bucket_to_level(njs_lvlhsh_query_t *lhq, void **slot, njs_uint_t nlvl, uint32_t *bucket); static njs_int_t njs_lvlhsh_level_convertion_insert(njs_lvlhsh_query_t *lhq, void **parent, uint32_t key, njs_uint_t nlvl); static njs_int_t njs_lvlhsh_bucket_convertion_insert(njs_lvlhsh_query_t *lhq, void **slot, uint32_t key, njs_int_t nlvl); static njs_int_t njs_lvlhsh_free_level(njs_lvlhsh_query_t *lhq, void **level, njs_uint_t size); static njs_int_t njs_lvlhsh_level_delete(njs_lvlhsh_query_t *lhq, void **slot, uint32_t key, njs_uint_t nlvl); static njs_int_t njs_lvlhsh_bucket_delete(njs_lvlhsh_query_t *lhq, void **bkt); static void *njs_lvlhsh_level_each(njs_lvlhsh_each_t *lhe, void **level, njs_uint_t nlvl, njs_uint_t shift); static void *njs_lvlhsh_bucket_each(njs_lvlhsh_each_t *lhe); njs_int_t njs_lvlhsh_find(const njs_lvlhsh_t *lh, njs_lvlhsh_query_t *lhq) { void *slot; slot = lh->slot; if (njs_fast_path(slot != NULL)) { if (njs_lvlhsh_is_bucket(slot)) { return njs_lvlhsh_bucket_find(lhq, slot); } return njs_lvlhsh_level_find(lhq, slot, lhq->key_hash, 0); } return NJS_DECLINED; } static njs_int_t njs_lvlhsh_level_find(njs_lvlhsh_query_t *lhq, void **lvl, uint32_t key, njs_uint_t nlvl) { void **slot; uintptr_t mask; njs_uint_t shift; shift = lhq->proto->shift[nlvl]; mask = ((uintptr_t) 1 << shift) - 1; lvl = njs_lvlhsh_level(lvl, mask); slot = lvl[key & mask]; if (slot != NULL) { if (njs_lvlhsh_is_bucket(slot)) { return njs_lvlhsh_bucket_find(lhq, slot); } return njs_lvlhsh_level_find(lhq, slot, key >> shift, nlvl + 1); } return NJS_DECLINED; } static njs_int_t njs_lvlhsh_bucket_find(njs_lvlhsh_query_t *lhq, void **bkt) { void *value; uint32_t *bucket, *e; njs_uint_t n; do { bucket = njs_lvlhsh_bucket(lhq->proto, bkt); n = njs_lvlhsh_bucket_entries(lhq->proto, bkt); e = bucket; do { if (njs_lvlhsh_valid_entry(e)) { n--; if (njs_lvlhsh_entry_key(e) == lhq->key_hash) { value = njs_lvlhsh_entry_value(e); if (lhq->proto->test(lhq, value) == NJS_OK) { lhq->value = value; return NJS_OK; } } } e += NJS_LVLHSH_ENTRY_SIZE; } while (n != 0); bkt = *njs_lvlhsh_next_bucket(lhq->proto, bucket); } while (bkt != NULL); return NJS_DECLINED; } njs_int_t njs_lvlhsh_insert(njs_lvlhsh_t *lh, njs_lvlhsh_query_t *lhq) { uint32_t key; if (njs_fast_path(lh->slot != NULL)) { key = lhq->key_hash; if (njs_lvlhsh_is_bucket(lh->slot)) { return njs_lvlhsh_bucket_insert(lhq, &lh->slot, key, -1); } return njs_lvlhsh_level_insert(lhq, &lh->slot, key, 0); } return njs_lvlhsh_new_bucket(lhq, &lh->slot); } static njs_int_t njs_lvlhsh_new_bucket(njs_lvlhsh_query_t *lhq, void **slot) { uint32_t *bucket; bucket = lhq->proto->alloc(lhq->pool, njs_lvlhsh_bucket_size(lhq->proto)); if (njs_fast_path(bucket != NULL)) { njs_lvlhsh_set_entry_value(bucket, lhq->value); njs_lvlhsh_set_entry_key(bucket, lhq->key_hash); *njs_lvlhsh_next_bucket(lhq->proto, bucket) = NULL; njs_lvlhsh_store_bucket(*slot, bucket); return NJS_OK; } return NJS_ERROR; } static njs_int_t njs_lvlhsh_level_insert(njs_lvlhsh_query_t *lhq, void **parent, uint32_t key, njs_uint_t nlvl) { void **slot, **lvl; njs_int_t ret; uintptr_t mask; njs_uint_t shift; shift = lhq->proto->shift[nlvl]; mask = ((uintptr_t) 1 << shift) - 1; lvl = njs_lvlhsh_level(*parent, mask); slot = &lvl[key & mask]; if (*slot != NULL) { key >>= shift; if (njs_lvlhsh_is_bucket(*slot)) { return njs_lvlhsh_bucket_insert(lhq, slot, key, nlvl); } return njs_lvlhsh_level_insert(lhq, slot, key, nlvl + 1); } ret = njs_lvlhsh_new_bucket(lhq, slot); if (njs_fast_path(ret == NJS_OK)) { njs_lvlhsh_count_inc(*parent); } return ret; } static njs_int_t njs_lvlhsh_bucket_insert(njs_lvlhsh_query_t *lhq, void **slot, uint32_t key, njs_int_t nlvl) { void **bkt, **vacant_bucket, *value; uint32_t *bucket, *e, *vacant_entry; njs_int_t ret; uintptr_t n; const void *new_value; const njs_lvlhsh_proto_t *proto; bkt = slot; vacant_entry = NULL; vacant_bucket = NULL; proto = lhq->proto; /* Search for duplicate entry in bucket chain. */ do { bucket = njs_lvlhsh_bucket(proto, *bkt); n = njs_lvlhsh_bucket_entries(proto, *bkt); e = bucket; do { if (njs_lvlhsh_valid_entry(e)) { if (njs_lvlhsh_entry_key(e) == lhq->key_hash) { value = njs_lvlhsh_entry_value(e); if (proto->test(lhq, value) == NJS_OK) { new_value = lhq->value; lhq->value = value; if (lhq->replace) { njs_lvlhsh_set_entry_value(e, new_value); return NJS_OK; } return NJS_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 += NJS_LVLHSH_ENTRY_SIZE; } while (n != 0); if (e < njs_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 = njs_lvlhsh_next_bucket(proto, bucket); } while (*bkt != NULL); if (vacant_entry != NULL) { njs_lvlhsh_set_entry_value(vacant_entry, lhq->value); njs_lvlhsh_set_entry_key(vacant_entry, lhq->key_hash); njs_lvlhsh_count_inc(*vacant_bucket); return NJS_OK; } /* All buckets are full. */ nlvl++; if (njs_fast_path(proto->shift[nlvl] != 0)) { ret = njs_lvlhsh_convert_bucket_to_level(lhq, slot, nlvl, bucket); if (njs_fast_path(ret == NJS_OK)) { return njs_lvlhsh_level_insert(lhq, slot, key, nlvl); } return ret; } /* The last allowed level, only buckets may be allocated here. */ return njs_lvlhsh_new_bucket(lhq, bkt); } static njs_int_t njs_lvlhsh_convert_bucket_to_level(njs_lvlhsh_query_t *lhq, void **slot, njs_uint_t nlvl, uint32_t *bucket) { void *lvl, **level; uint32_t *e, *end, key; njs_int_t ret; njs_uint_t i, shift, size; njs_lvlhsh_query_t q; const njs_lvlhsh_proto_t *proto; proto = lhq->proto; size = njs_lvlhsh_level_size(proto, nlvl); lvl = proto->alloc(lhq->pool, size * (sizeof(void *))); if (njs_slow_path(lvl == NULL)) { return NJS_ERROR; } njs_memzero(lvl, 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. */ njs_pragma_loop_disable_vectorization; shift += proto->shift[i]; } end = njs_lvlhsh_bucket_end(proto, bucket); for (e = bucket; e < end; e += NJS_LVLHSH_ENTRY_SIZE) { q.proto = proto; q.pool = lhq->pool; q.value = njs_lvlhsh_entry_value(e); key = njs_lvlhsh_entry_key(e); q.key_hash = key; ret = njs_lvlhsh_level_convertion_insert(&q, &lvl, key >> shift, nlvl); if (njs_slow_path(ret != NJS_OK)) { return njs_lvlhsh_free_level(lhq, level, size); } } *slot = lvl; proto->free(lhq->pool, bucket, njs_lvlhsh_bucket_size(proto)); return NJS_OK; } static njs_int_t njs_lvlhsh_level_convertion_insert(njs_lvlhsh_query_t *lhq, void **parent, uint32_t key, njs_uint_t nlvl) { void **slot, **lvl; njs_int_t ret; uintptr_t mask; njs_uint_t shift; shift = lhq->proto->shift[nlvl]; mask = ((uintptr_t) 1 << shift) - 1; lvl = njs_lvlhsh_level(*parent, mask); slot = &lvl[key & mask]; if (*slot == NULL) { ret = njs_lvlhsh_new_bucket(lhq, slot); if (njs_fast_path(ret == NJS_OK)) { njs_lvlhsh_count_inc(*parent); } return ret; } /* Only backets can be here. */ return njs_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 njs_int_t njs_lvlhsh_bucket_convertion_insert(njs_lvlhsh_query_t *lhq, void **slot, uint32_t key, njs_int_t nlvl) { void **bkt; uint32_t *bucket, *e; njs_int_t ret; uintptr_t n; const njs_lvlhsh_proto_t *proto; bkt = slot; proto = lhq->proto; do { bucket = njs_lvlhsh_bucket(proto, *bkt); n = njs_lvlhsh_bucket_entries(proto, *bkt); e = bucket + n * NJS_LVLHSH_ENTRY_SIZE; if (njs_fast_path(e < njs_lvlhsh_bucket_end(proto, bucket))) { njs_lvlhsh_set_entry_value(e, lhq->value); njs_lvlhsh_set_entry_key(e, lhq->key_hash); njs_lvlhsh_count_inc(*bkt); return NJS_OK; } bkt = njs_lvlhsh_next_bucket(proto, bucket); } while (*bkt != NULL); /* All buckets are full. */ nlvl++; if (njs_fast_path(proto->shift[nlvl] != 0)) { ret = njs_lvlhsh_convert_bucket_to_level(lhq, slot, nlvl, bucket); if (njs_fast_path(ret == NJS_OK)) { return njs_lvlhsh_level_insert(lhq, slot, key, nlvl); } return ret; } /* The last allowed level, only buckets may be allocated here. */ return njs_lvlhsh_new_bucket(lhq, bkt); } static njs_int_t njs_lvlhsh_free_level(njs_lvlhsh_query_t *lhq, void **level, njs_uint_t size) { size_t bsize; njs_uint_t i; const njs_lvlhsh_proto_t *proto; proto = lhq->proto; bsize = njs_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, njs_lvlhsh_bucket(proto, level[i]), bsize); } } proto->free(lhq->pool, level, size * (sizeof(void *))); return NJS_ERROR; } njs_int_t njs_lvlhsh_delete(njs_lvlhsh_t *lh, njs_lvlhsh_query_t *lhq) { if (njs_fast_path(lh->slot != NULL)) { if (njs_lvlhsh_is_bucket(lh->slot)) { return njs_lvlhsh_bucket_delete(lhq, &lh->slot); } return njs_lvlhsh_level_delete(lhq, &lh->slot, lhq->key_hash, 0); } return NJS_DECLINED; } static njs_int_t njs_lvlhsh_level_delete(njs_lvlhsh_query_t *lhq, void **parent, uint32_t key, njs_uint_t nlvl) { size_t size; void **slot, **lvl; uintptr_t mask; njs_int_t ret; njs_uint_t shift; shift = lhq->proto->shift[nlvl]; mask = ((uintptr_t) 1 << shift) - 1; lvl = njs_lvlhsh_level(*parent, mask); slot = &lvl[key & mask]; if (*slot != NULL) { if (njs_lvlhsh_is_bucket(*slot)) { ret = njs_lvlhsh_bucket_delete(lhq, slot); } else { key >>= shift; ret = njs_lvlhsh_level_delete(lhq, slot, key, nlvl + 1); } if (*slot == NULL) { njs_lvlhsh_count_dec(*parent); if (njs_lvlhsh_level_entries(*parent, mask) == 0) { *parent = NULL; size = njs_lvlhsh_level_size(lhq->proto, nlvl); lhq->proto->free(lhq->pool, lvl, size * sizeof(void *)); } } return ret; } return NJS_DECLINED; } static njs_int_t njs_lvlhsh_bucket_delete(njs_lvlhsh_query_t *lhq, void **bkt) { void *value; size_t size; uint32_t *bucket, *e; uintptr_t n; const njs_lvlhsh_proto_t *proto; proto = lhq->proto; do { bucket = njs_lvlhsh_bucket(proto, *bkt); n = njs_lvlhsh_bucket_entries(proto, *bkt); e = bucket; do { if (njs_lvlhsh_valid_entry(e)) { if (njs_lvlhsh_entry_key(e) == lhq->key_hash) { value = njs_lvlhsh_entry_value(e); if (proto->test(lhq, value) == NJS_OK) { if (njs_lvlhsh_bucket_entries(proto, *bkt) == 1) { *bkt = *njs_lvlhsh_next_bucket(proto, bucket); size = njs_lvlhsh_bucket_size(proto); proto->free(lhq->pool, bucket, size); } else { njs_lvlhsh_count_dec(*bkt); njs_lvlhsh_set_entry_value(e, NULL); } lhq->value = value; return NJS_OK; } } n--; } e += NJS_LVLHSH_ENTRY_SIZE; } while (n != 0); bkt = njs_lvlhsh_next_bucket(proto, bucket); } while (*bkt != NULL); return NJS_DECLINED; } void * njs_lvlhsh_each(const njs_lvlhsh_t *lh, njs_lvlhsh_each_t *lhe) { void **slot; if (lhe->bucket == NJS_LVLHSH_BUCKET_DONE) { slot = lh->slot; if (njs_lvlhsh_is_bucket(slot)) { return NULL; } } else { if (njs_slow_path(lhe->bucket == NULL)) { /* The first iteration only. */ slot = lh->slot; if (slot == NULL) { return NULL; } if (!njs_lvlhsh_is_bucket(slot)) { goto level; } lhe->bucket = njs_lvlhsh_bucket(lhe->proto, slot); lhe->entries = njs_lvlhsh_bucket_entries(lhe->proto, slot); } return njs_lvlhsh_bucket_each(lhe); } level: return njs_lvlhsh_level_each(lhe, slot, 0, 0); } static void * njs_lvlhsh_level_each(njs_lvlhsh_each_t *lhe, void **level, njs_uint_t nlvl, njs_uint_t shift) { void **slot, *value; uintptr_t mask; njs_uint_t n, level_shift; level_shift = lhe->proto->shift[nlvl]; mask = ((uintptr_t) 1 << level_shift) - 1; level = njs_lvlhsh_level(level, mask); do { n = (lhe->current >> shift) & mask; slot = level[n]; if (slot != NULL) { if (njs_lvlhsh_is_bucket(slot)) { if (lhe->bucket != NJS_LVLHSH_BUCKET_DONE) { lhe->bucket = njs_lvlhsh_bucket(lhe->proto, slot); lhe->entries = njs_lvlhsh_bucket_entries(lhe->proto, slot); lhe->entry = 0; return njs_lvlhsh_bucket_each(lhe); } lhe->bucket = NULL; } else { value = njs_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 void * njs_lvlhsh_bucket_each(njs_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 += NJS_LVLHSH_ENTRY_SIZE; } while (njs_lvlhsh_free_entry(bucket)); value = njs_lvlhsh_entry_value(bucket); lhe->key_hash = njs_lvlhsh_entry_key(bucket); lhe->entries--; if (lhe->entries == 0) { next = *njs_lvlhsh_next_bucket(lhe->proto, lhe->bucket); lhe->bucket = (next == NULL) ? NJS_LVLHSH_BUCKET_DONE : njs_lvlhsh_bucket(lhe->proto, next); lhe->entries = njs_lvlhsh_bucket_entries(lhe->proto, next); lhe->entry = 0; } return value; } njs-0.8.9/src/njs_lvlhsh.h000066400000000000000000000131161474132077100154440ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_LVLHSH_H_INCLUDED_ #define _NJS_LVLHSH_H_INCLUDED_ typedef struct njs_lvlhsh_query_s njs_lvlhsh_query_t; typedef njs_int_t (*njs_lvlhsh_test_t)(njs_lvlhsh_query_t *lhq, void *data); typedef void *(*njs_lvlhsh_alloc_t)(void *ctx, size_t size); typedef void (*njs_lvlhsh_free_t)(void *ctx, void *p, size_t size); #if (NJS_64BIT) #define NJS_LVLHSH_DEFAULT_BUCKET_SIZE 128 #define NJS_LVLHSH_ENTRY_SIZE 3 /* 3 is shift of 64-bit pointer. */ #define NJS_LVLHSH_MEMALIGN_SHIFT (NJS_MAX_MEMALIGN_SHIFT - 3) #else #define NJS_LVLHSH_DEFAULT_BUCKET_SIZE 64 #define NJS_LVLHSH_ENTRY_SIZE 2 /* 2 is shift of 32-bit pointer. */ #define NJS_LVLHSH_MEMALIGN_SHIFT (NJS_MAX_MEMALIGN_SHIFT - 2) #endif #if (NJS_LVLHSH_MEMALIGN_SHIFT < 10) #define NJS_LVLHSH_MAX_MEMALIGN_SHIFT NJS_LVLHSH_MEMALIGN_SHIFT #else #define NJS_LVLHSH_MAX_MEMALIGN_SHIFT 10 #endif #define NJS_LVLHSH_BUCKET_END(bucket_size) \ (((bucket_size) - sizeof(void *)) \ / (NJS_LVLHSH_ENTRY_SIZE * sizeof(uint32_t)) \ * NJS_LVLHSH_ENTRY_SIZE) #define NJS_LVLHSH_BUCKET_SIZE(bucket_size) \ NJS_LVLHSH_BUCKET_END(bucket_size), bucket_size, (bucket_size - 1) #define NJS_LVLHSH_DEFAULT \ NJS_LVLHSH_BUCKET_SIZE(NJS_LVLHSH_DEFAULT_BUCKET_SIZE), \ { 4, 4, 4, 4, 4, 4, 4, 0 } #define NJS_LVLHSH_LARGE_SLAB \ NJS_LVLHSH_BUCKET_SIZE(NJS_LVLHSH_DEFAULT_BUCKET_SIZE), \ { 10, 4, 4, 4, 4, 4, 4, 0 } #define NJS_LVLHSH_LARGE_MEMALIGN \ NJS_LVLHSH_BUCKET_SIZE(NJS_LVLHSH_DEFAULT_BUCKET_SIZE), \ { NJS_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]; njs_lvlhsh_test_t test; njs_lvlhsh_alloc_t alloc; njs_lvlhsh_free_t free; } njs_lvlhsh_proto_t; typedef struct { void *slot; } njs_lvlhsh_t; struct njs_lvlhsh_query_s { uint32_t key_hash; njs_str_t key; uint8_t replace; /* 1 bit */ void *value; const njs_lvlhsh_proto_t *proto; void *pool; /* Opaque data passed for the test function. */ void *data; }; #define njs_lvlhsh_is_empty(lh) \ ((lh)->slot == NULL) #define njs_lvlhsh_init(lh) \ (lh)->slot = NULL #define njs_lvlhsh_eq(lhl, lhr) \ ((lhl)->slot == (lhr)->slot) /* * njs_lvlhsh_find() finds a hash element. If the element has been * found then it is stored in the lhq->value and njs_lvlhsh_find() * returns NJS_OK. Otherwise NJS_DECLINED is returned. * * The required njs_lvlhsh_query_t fields: key_hash, key, proto. */ NJS_EXPORT njs_int_t njs_lvlhsh_find(const njs_lvlhsh_t *lh, njs_lvlhsh_query_t *lhq); /* * njs_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 NJS_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 NJS_OK is returned. * If the element is not present in lvlhsh, then it is inserted and * NJS_OK is returned. The lhq->value is not changed. * On memory allocation failure NJS_ERROR is returned. * * The required njs_lvlhsh_query_t fields: key_hash, key, proto, replace, value. * The optional njs_lvlhsh_query_t fields: pool. */ NJS_EXPORT njs_int_t njs_lvlhsh_insert(njs_lvlhsh_t *lh, njs_lvlhsh_query_t *lhq); /* * njs_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 NJS_OK is returned. Otherwise NJS_DECLINED is returned. * * The required njs_lvlhsh_query_t fields: key_hash, key, proto. * The optional njs_lvlhsh_query_t fields: pool. */ NJS_EXPORT njs_int_t njs_lvlhsh_delete(njs_lvlhsh_t *lh, njs_lvlhsh_query_t *lhq); typedef struct { const njs_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; uint32_t key_hash; } njs_lvlhsh_each_t; #define njs_lvlhsh_each_init(lhe, _proto) \ do { \ njs_memzero(lhe, sizeof(njs_lvlhsh_each_t)); \ (lhe)->proto = _proto; \ } while (0) NJS_EXPORT void *njs_lvlhsh_each(const njs_lvlhsh_t *lh, njs_lvlhsh_each_t *lhe); #endif /* _NJS_LVLHSH_H_INCLUDED_ */ njs-0.8.9/src/njs_main.h000066400000000000000000000032721474132077100150720ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_CORE_H_INCLUDED_ #define _NJS_CORE_H_INCLUDED_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif /* _NJS_CORE_H_INCLUDED_ */ njs-0.8.9/src/njs_malloc.c000066400000000000000000000015341474132077100154070ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include void * njs_zalloc(size_t size) { void *p; p = njs_malloc(size); if (njs_fast_path(p != NULL)) { njs_memzero(p, size); } return p; } #if (NJS_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 * njs_memalign(size_t alignment, size_t size) { int err; void *p; err = posix_memalign(&p, alignment, size); if (njs_fast_path(err == 0)) { return p; } return NULL; } #elif (NJS_HAVE_MEMALIGN) /* memalign() presents in Solaris, HP-UX. */ void * njs_memalign(size_t alignment, size_t size) { return memalign(alignment, size); } #else #error no memalign() implementation. #endif njs-0.8.9/src/njs_malloc.h000066400000000000000000000006011474132077100154060ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_MALLOC_H_INCLUDED_ #define _NJS_MALLOC_H_INCLUDED_ #define njs_malloc(size) malloc(size) #define njs_free(p) free(p) NJS_EXPORT void *njs_zalloc(size_t size) NJS_MALLOC_LIKE; NJS_EXPORT void *njs_memalign(size_t alignment, size_t size) NJS_MALLOC_LIKE; #endif /* _NJS_MALLOC_H_INCLUDED_ */ njs-0.8.9/src/njs_math.c000066400000000000000000000256331474132077100150770ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include typedef enum { NJS_MATH_ABS, NJS_MATH_ACOS, NJS_MATH_ACOSH, NJS_MATH_ASIN, NJS_MATH_ASINH, NJS_MATH_ATAN, NJS_MATH_ATAN2, NJS_MATH_ATANH, NJS_MATH_CBRT, NJS_MATH_CEIL, NJS_MATH_CLZ32, NJS_MATH_COS, NJS_MATH_COSH, NJS_MATH_EXP, NJS_MATH_EXPM1, NJS_MATH_FLOOR, NJS_MATH_FROUND, NJS_MATH_IMUL, NJS_MATH_LOG, NJS_MATH_LOG10, NJS_MATH_LOG1P, NJS_MATH_LOG2, NJS_MATH_POW, NJS_MATH_ROUND, NJS_MATH_SIGN, NJS_MATH_SIN, NJS_MATH_SINH, NJS_MATH_SQRT, NJS_MATH_TAN, NJS_MATH_TANH, NJS_MATH_TRUNC, } njs_math_func_t; static njs_int_t njs_object_math_func(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t magic, njs_value_t *retval) { double num, num2; uint8_t sign; uint32_t u32; uint64_t one, fraction_mask; njs_int_t ret, ep; njs_math_func_t func; njs_diyfp_conv_t conv; func = magic; ret = njs_value_to_number(vm, njs_arg(args, nargs, 1), &num); if (njs_slow_path(ret != NJS_OK)) { return ret; } switch (func) { case NJS_MATH_ABS: num = fabs(num); break; case NJS_MATH_ACOS: #if (NJS_SOLARIS) /* On Solaris acos(x) returns 0 for x > 1. */ if (fabs(num) > 1.0) { num = NAN; } #endif num = acos(num); break; case NJS_MATH_ACOSH: num = acosh(num); break; case NJS_MATH_ASIN: #if (NJS_SOLARIS) /* On Solaris asin(x) returns 0 for x > 1. */ if (fabs(num) > 1.0) { num = NAN; } #endif num = asin(num); break; case NJS_MATH_ASINH: num = asinh(num); break; case NJS_MATH_ATAN: num = atan(num); break; case NJS_MATH_ATANH: num = atanh(num); break; case NJS_MATH_CBRT: num = cbrt(num); break; case NJS_MATH_CEIL: num = ceil(num); break; case NJS_MATH_CLZ32: u32 = njs_number_to_uint32(num); num = njs_leading_zeros(u32); break; case NJS_MATH_COS: num = cos(num); break; case NJS_MATH_COSH: num = cosh(num); break; case NJS_MATH_EXP: num = exp(num); break; case NJS_MATH_EXPM1: num = expm1(num); break; case NJS_MATH_FLOOR: num = floor(num); break; case NJS_MATH_FROUND: num = (float) num; break; case NJS_MATH_LOG: num = log(num); break; case NJS_MATH_LOG10: num = log10(num); break; case NJS_MATH_LOG1P: num = log1p(num); break; case NJS_MATH_LOG2: #if (NJS_SOLARIS) /* On Solaris 10 log(-1) returns -Infinity. */ if (num < 0) { num = NAN; } #endif num = log2(num); break; case NJS_MATH_SIGN: if (!isnan(num) && num != 0) { num = signbit(num) ? -1 : 1; } break; case NJS_MATH_SIN: num = sin(num); break; case NJS_MATH_SINH: num = sinh(num); break; case NJS_MATH_SQRT: num = sqrt(num); break; case NJS_MATH_ROUND: conv.d = num; ep = (conv.u64 & NJS_DBL_EXPONENT_MASK) >> NJS_DBL_SIGNIFICAND_SIZE; if (ep < NJS_DBL_EXPONENT_OFFSET) { /* |v| < 1. */ if (ep == (NJS_DBL_EXPONENT_OFFSET - 1) && conv.u64 != njs_uint64(0xbfe00000, 0x00000000)) { /* (|v| > 0.5 || v == 0.5) => +-1.0 */ conv.u64 = conv.u64 & NJS_DBL_SIGN_MASK; conv.u64 |= NJS_DBL_EXPONENT_OFFSET << NJS_DBL_SIGNIFICAND_SIZE; } else { /* (|v| < 0.5 || v == -0.5) => +-0. */ conv.u64 &= ((uint64_t) 1) << 63; } } else if (ep < NJS_DBL_EXPONENT_BIAS) { /* |v| <= 2^52 - 1 (largest safe integer). */ one = ((uint64_t) 1) << (NJS_DBL_EXPONENT_BIAS - ep); fraction_mask = one - 1; /* truncation. */ sign = conv.u64 >> 63; conv.u64 += (one >> 1) - sign; conv.u64 &= ~fraction_mask; } num = conv.d; break; case NJS_MATH_TAN: num = tan(num); break; case NJS_MATH_TANH: num = tanh(num); break; case NJS_MATH_TRUNC: num = trunc(num); break; default: ret = njs_value_to_number(vm, njs_arg(args, nargs, 2), &num2); if (njs_slow_path(ret != NJS_OK)) { return ret; } switch (func) { case NJS_MATH_ATAN2: num = atan2(num, num2); break; case NJS_MATH_IMUL: u32 = njs_number_to_uint32(num); num = (int32_t) (u32 * njs_number_to_uint32(num2)); break; default: /* * According to ECMA-262: * 1. If exponent is NaN, the result should be NaN; * 2. The result of Math.pow(+/-1, +/-Infinity) should be NaN. */ if (fabs(num) != 1 || (!isnan(num2) && !isinf(num2))) { num = pow(num, num2); } else { num = NAN; } } } njs_set_number(retval, num); return NJS_OK; } static njs_int_t njs_object_math_hypot(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double num; njs_int_t ret; njs_uint_t i; ret = njs_value_to_number(vm, njs_arg(args, nargs, 1), &num); if (njs_slow_path(ret != NJS_OK)) { return ret; } num = (nargs > 1) ? fabs(num) : 0; for (i = 2; i < nargs; i++) { ret = njs_value_to_numeric(vm, &args[i], &args[i]); if (njs_slow_path(ret != NJS_OK)) { return ret; } num = hypot(num, njs_number(&args[i])); if (njs_slow_path(isinf(num))) { break; } } njs_set_number(retval, num); return NJS_OK; } njs_inline double njs_fmax(double x, double y) { if (x == 0 && y == 0) { return signbit(x) ? y : x; } return fmax(x, y); } njs_inline double njs_fmin(double x, double y) { if (x == 0 && y == 0) { return signbit(x) ? x : y; } return fmin(x, y); } static njs_int_t njs_object_math_min_max(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t max, njs_value_t *retval) { double num, value; njs_int_t ret; njs_uint_t i; value = max ? -INFINITY : INFINITY; for (i = 1; i < nargs; i++) { ret = njs_value_to_number(vm, &args[i], &num); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_slow_path(isnan(num))) { value = num; break; } value = max ? njs_fmax(value, num) : njs_fmin(value, num); } njs_set_number(retval, value); return NJS_OK; } static njs_int_t njs_object_math_random(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double num; num = njs_random(&vm->random) / 4294967296.0; njs_set_number(retval, num); return NJS_OK; } static const njs_object_prop_t njs_math_object_properties[] = { { .type = NJS_PROPERTY, .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG), .u.value = njs_string("Math"), .configurable = 1, }, NJS_DECLARE_PROP_VALUE("E", njs_value(NJS_NUMBER, 1, M_E), 0), NJS_DECLARE_PROP_VALUE("LN10", njs_value(NJS_NUMBER, 1, M_LN10), 0), NJS_DECLARE_PROP_VALUE("LN2", njs_value(NJS_NUMBER, 1, M_LN2), 0), NJS_DECLARE_PROP_VALUE("LOG10E", njs_value(NJS_NUMBER, 1, M_LOG10E), 0), NJS_DECLARE_PROP_VALUE("LOG2E", njs_value(NJS_NUMBER, 1, M_LOG2E), 0), NJS_DECLARE_PROP_VALUE("PI", njs_value(NJS_NUMBER, 1, M_PI), 0), NJS_DECLARE_PROP_VALUE("SQRT1_2", njs_value(NJS_NUMBER, 1, M_SQRT1_2), 0), NJS_DECLARE_PROP_VALUE("SQRT2", njs_value(NJS_NUMBER, 1, M_SQRT2), 0), NJS_DECLARE_PROP_NATIVE("abs", njs_object_math_func, 1, NJS_MATH_ABS), NJS_DECLARE_PROP_NATIVE("acos", njs_object_math_func, 1, NJS_MATH_ACOS), NJS_DECLARE_PROP_NATIVE("acosh", njs_object_math_func, 1, NJS_MATH_ACOSH), NJS_DECLARE_PROP_NATIVE("asin", njs_object_math_func, 1, NJS_MATH_ASIN), NJS_DECLARE_PROP_NATIVE("asinh", njs_object_math_func, 1, NJS_MATH_ASINH), NJS_DECLARE_PROP_NATIVE("atan", njs_object_math_func, 1, NJS_MATH_ATAN), NJS_DECLARE_PROP_NATIVE("atan2", njs_object_math_func, 2, NJS_MATH_ATAN2), NJS_DECLARE_PROP_NATIVE("atanh", njs_object_math_func, 1, NJS_MATH_ATANH), NJS_DECLARE_PROP_NATIVE("cbrt", njs_object_math_func, 1, NJS_MATH_CBRT), NJS_DECLARE_PROP_NATIVE("ceil", njs_object_math_func, 1, NJS_MATH_CEIL), NJS_DECLARE_PROP_NATIVE("clz32", njs_object_math_func, 1, NJS_MATH_CLZ32), NJS_DECLARE_PROP_NATIVE("cos", njs_object_math_func, 1, NJS_MATH_COS), NJS_DECLARE_PROP_NATIVE("cosh", njs_object_math_func, 1, NJS_MATH_COSH), NJS_DECLARE_PROP_NATIVE("exp", njs_object_math_func, 1, NJS_MATH_EXP), NJS_DECLARE_PROP_NATIVE("expm1", njs_object_math_func, 1, NJS_MATH_EXPM1), NJS_DECLARE_PROP_NATIVE("floor", njs_object_math_func, 1, NJS_MATH_FLOOR), NJS_DECLARE_PROP_NATIVE("fround", njs_object_math_func, 1, NJS_MATH_FROUND), NJS_DECLARE_PROP_NATIVE("hypot", njs_object_math_hypot, 2, 0), NJS_DECLARE_PROP_NATIVE("imul", njs_object_math_func, 2, NJS_MATH_IMUL), NJS_DECLARE_PROP_NATIVE("log", njs_object_math_func, 1, NJS_MATH_LOG), NJS_DECLARE_PROP_NATIVE("log10", njs_object_math_func, 1, NJS_MATH_LOG10), NJS_DECLARE_PROP_NATIVE("log1p", njs_object_math_func, 1, NJS_MATH_LOG1P), NJS_DECLARE_PROP_NATIVE("log2", njs_object_math_func, 1, NJS_MATH_LOG2), NJS_DECLARE_PROP_NATIVE("max", njs_object_math_min_max, 2, 1), NJS_DECLARE_PROP_NATIVE("min", njs_object_math_min_max, 2, 0), NJS_DECLARE_PROP_NATIVE("pow", njs_object_math_func, 2, NJS_MATH_POW), NJS_DECLARE_PROP_NATIVE("random", njs_object_math_random, 0, 0), NJS_DECLARE_PROP_NATIVE("round", njs_object_math_func, 1, NJS_MATH_ROUND), NJS_DECLARE_PROP_NATIVE("sign", njs_object_math_func, 1, NJS_MATH_SIGN), NJS_DECLARE_PROP_NATIVE("sin", njs_object_math_func, 1, NJS_MATH_SIN), NJS_DECLARE_PROP_NATIVE("sinh", njs_object_math_func, 1, NJS_MATH_SINH), NJS_DECLARE_PROP_NATIVE("sqrt", njs_object_math_func, 1, NJS_MATH_SQRT), NJS_DECLARE_PROP_NATIVE("tan", njs_object_math_func, 1, NJS_MATH_TAN), NJS_DECLARE_PROP_NATIVE("tanh", njs_object_math_func, 1, NJS_MATH_TANH), NJS_DECLARE_PROP_NATIVE("trunc", njs_object_math_func, 1, NJS_MATH_TRUNC), }; const njs_object_init_t njs_math_object_init = { njs_math_object_properties, njs_nitems(njs_math_object_properties), }; njs-0.8.9/src/njs_math.h000066400000000000000000000003341474132077100150730ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_MATH_H_INCLUDED_ #define _NJS_MATH_H_INCLUDED_ extern const njs_object_init_t njs_math_object_init; #endif /* _NJS_MATH_H_INCLUDED_ */ njs-0.8.9/src/njs_module.c000066400000000000000000000066061474132077100154320ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #include static njs_int_t njs_module_hash_test(njs_lvlhsh_query_t *lhq, void *data) { njs_mod_t *module; module = data; if (njs_strstr_eq(&lhq->key, &module->name)) { return NJS_OK; } return NJS_DECLINED; } const njs_lvlhsh_proto_t njs_modules_hash_proto njs_aligned(64) = { NJS_LVLHSH_DEFAULT, njs_module_hash_test, njs_lvlhsh_alloc, njs_lvlhsh_free, }; njs_mod_t * njs_module_find(njs_vm_t *vm, njs_str_t *name, njs_bool_t shared) { njs_int_t ret; njs_mod_t *shrd, *module; njs_object_t *object; njs_lvlhsh_query_t lhq; lhq.key = *name; lhq.key_hash = njs_djb_hash(name->start, name->length); lhq.proto = &njs_modules_hash_proto; if (njs_lvlhsh_find(&vm->modules_hash, &lhq) == NJS_OK) { return lhq.value; } if (njs_lvlhsh_find(&vm->shared->modules_hash, &lhq) == NJS_OK) { shrd = lhq.value; if (shared) { return shrd; } module = njs_mp_alloc(vm->mem_pool, sizeof(njs_mod_t)); if (njs_slow_path(module == NULL)) { njs_memory_error(vm); return NULL; } memcpy(module, shrd, sizeof(njs_mod_t)); object = njs_object_value_copy(vm, &module->value); if (njs_slow_path(object == NULL)) { return NULL; } lhq.replace = 0; lhq.value = module; lhq.pool = vm->mem_pool; ret = njs_lvlhsh_insert(&vm->modules_hash, &lhq); if (njs_fast_path(ret == NJS_OK)) { return module; } } return NULL; } njs_mod_t * njs_module_add(njs_vm_t *vm, njs_str_t *name, njs_value_t *value) { njs_int_t ret; njs_mod_t *module; njs_lvlhsh_query_t lhq; module = njs_mp_zalloc(vm->mem_pool, sizeof(njs_mod_t)); if (njs_slow_path(module == NULL)) { njs_memory_error(vm); return NULL; } ret = njs_name_copy(vm, &module->name, name); if (njs_slow_path(ret != NJS_OK)) { njs_memory_error(vm); return NULL; } lhq.replace = 0; lhq.key = *name; lhq.key_hash = njs_djb_hash(name->start, name->length); lhq.value = module; lhq.pool = vm->mem_pool; lhq.proto = &njs_modules_hash_proto; ret = njs_lvlhsh_insert(&vm->shared->modules_hash, &lhq); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "lvlhsh insert failed"); return NULL; } if (value != NULL) { njs_value_assign(&module->value, value); module->function.native = 1; } return module; } njs_int_t njs_module_require(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_str_t name; njs_mod_t *module; njs_value_t *path; if (nargs < 2) { njs_type_error(vm, "missing path"); return NJS_ERROR; } path = njs_argument(args, 1); ret = njs_value_to_string(vm, path, path); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_string_get(path, &name); module = njs_module_find(vm, &name, 0); if (njs_slow_path(module == NULL)) { njs_error(vm, "Cannot load module \"%V\"", &name); return NJS_ERROR; } njs_value_assign(retval, &module->value); return NJS_OK; } njs-0.8.9/src/njs_module.h000066400000000000000000000013601474132077100154270ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volynsev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_MODULE_H_INCLUDED_ #define _NJS_MODULE_H_INCLUDED_ struct njs_mod_s { njs_str_t name; njs_value_t value; njs_index_t index; njs_function_t function; }; njs_mod_t *njs_module_add(njs_vm_t *vm, njs_str_t *name, njs_value_t *value); njs_mod_t *njs_module_find(njs_vm_t *vm, njs_str_t *name, njs_bool_t shared); njs_int_t njs_module_require(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); extern njs_module_t *njs_modules[]; extern const njs_lvlhsh_proto_t njs_modules_hash_proto; #endif /* _NJS_MODULE_H_INCLUDED_ */ njs-0.8.9/src/njs_mp.c000066400000000000000000000465011474132077100145570ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include /* * 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 a 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 the clusters and large allocations are stored in rbtree blocks * to find them on free operations. The rbtree nodes are sorted by start * addresses. */ typedef struct { /* * Used to link pages with free chunks in pool chunk slot list * or to link free pages in clusters. */ njs_queue_link_t link; /* * Size of chunks or page shifted by mp->chunk_size_shift. * Zero means that page is free. */ uint8_t size; /* * Page number in page cluster. * There can be no more than 256 pages in a cluster. */ uint8_t number; /* Number of free chunks of a chunked page. */ uint8_t chunks; uint8_t _unused; /* Chunk bitmap. There can be no more than 32 chunks in a page. */ uint8_t map[4]; } njs_mp_page_t; typedef enum { /* Block of cluster. The block is allocated apart of the cluster. */ NJS_MP_CLUSTER_BLOCK = 0, /* * Block of large allocation. * The block is allocated apart of the allocation. */ NJS_MP_DISCRETE_BLOCK, /* * Block of large allocation. * The block is allocated just after of the allocation. */ NJS_MP_EMBEDDED_BLOCK, } njs_mp_block_type_t; typedef struct { NJS_RBTREE_NODE (node); njs_mp_block_type_t type:8; /* Block size must be less than 4G. */ uint32_t size; u_char *start; njs_mp_page_t pages[]; } njs_mp_block_t; typedef struct { njs_queue_t pages; /* Size of page chunks. */ #if (NJS_64BIT) uint32_t size; #else uint16_t size; #endif /* Maximum number of free chunks in chunked page. */ uint8_t chunks; } njs_mp_slot_t; struct njs_mp_s { /* rbtree of njs_mp_block_t. */ njs_rbtree_t blocks; njs_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; njs_mp_cleanup_t *cleanup; njs_mp_slot_t slots[]; }; #define njs_mp_chunk_is_free(map, chunk) \ ((map[chunk / 8] & (0x80 >> (chunk & 7))) == 0) #define njs_mp_chunk_set_free(map, chunk) \ map[chunk / 8] &= ~(0x80 >> (chunk & 7)) #define njs_mp_free_junk(p, size) \ njs_memset((p), 0x5A, size) #define njs_is_power_of_two(value) \ ((((value) - 1) & (value)) == 0) static njs_uint_t njs_mp_shift(njs_uint_t n); #if !(NJS_DEBUG_MEMORY) static void *njs_mp_alloc_small(njs_mp_t *mp, size_t size); static njs_uint_t njs_mp_alloc_chunk(u_char *map, njs_uint_t size); static njs_mp_page_t *njs_mp_alloc_page(njs_mp_t *mp); static njs_mp_block_t *njs_mp_alloc_cluster(njs_mp_t *mp); #endif static void *njs_mp_alloc_large(njs_mp_t *mp, size_t alignment, size_t size); static intptr_t njs_mp_rbtree_compare(njs_rbtree_node_t *node1, njs_rbtree_node_t *node2); static njs_mp_block_t *njs_mp_find_block(njs_rbtree_t *tree, u_char *p); static const char *njs_mp_chunk_free(njs_mp_t *mp, njs_mp_block_t *cluster, u_char *p); njs_mp_t * njs_mp_create(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 (njs_slow_path(!njs_is_power_of_two(page_alignment) || !njs_is_power_of_two(page_size) || !njs_is_power_of_two(min_chunk_size))) { return NULL; } page_alignment = njs_max(page_alignment, NJS_MAX_ALIGNMENT); if (njs_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 > 256 || cluster_size % page_size != 0)) { return NULL; } return njs_mp_fast_create(cluster_size, page_alignment, page_size, min_chunk_size); } njs_mp_t * njs_mp_fast_create(size_t cluster_size, size_t page_alignment, size_t page_size, size_t min_chunk_size) { njs_mp_t *mp; njs_uint_t slots, chunk_size; njs_mp_slot_t *slot; slots = 0; chunk_size = page_size; do { slots++; chunk_size /= 2; } while (chunk_size > min_chunk_size); mp = njs_zalloc(sizeof(njs_mp_t) + slots * sizeof(njs_mp_slot_t)); if (njs_fast_path(mp != NULL)) { mp->page_size = page_size; mp->page_alignment = njs_max(page_alignment, NJS_MAX_ALIGNMENT); mp->cluster_size = cluster_size; slot = mp->slots; do { njs_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); mp->chunk_size_shift = njs_mp_shift(min_chunk_size); mp->page_size_shift = njs_mp_shift(page_size); njs_rbtree_init(&mp->blocks, njs_mp_rbtree_compare); njs_queue_init(&mp->free_pages); } return mp; } static njs_uint_t njs_mp_shift(njs_uint_t n) { njs_uint_t shift; shift = 0; n /= 2; do { shift++; n /= 2; } while (n != 0); return shift; } njs_bool_t njs_mp_is_empty(njs_mp_t *mp) { return (njs_rbtree_is_empty(&mp->blocks) && njs_queue_is_empty(&mp->free_pages)); } void njs_mp_destroy(njs_mp_t *mp) { void *p; njs_mp_block_t *block; njs_mp_cleanup_t *c; njs_rbtree_node_t *node, *next; njs_debug_alloc("mp destroy\n"); for (c = mp->cleanup; c != NULL; c = c->next) { if (c->handler != NULL) { njs_debug_alloc("mp run cleanup: @%p\n", c); c->handler(c->data); } } next = njs_rbtree_root(&mp->blocks); while (next != njs_rbtree_sentinel(&mp->blocks)) { node = njs_rbtree_destroy_next(&mp->blocks, &next); block = (njs_mp_block_t *) node; p = block->start; if (block->type != NJS_MP_EMBEDDED_BLOCK) { njs_free(block); } njs_free(p); } njs_free(mp); } void njs_mp_stat(njs_mp_t *mp, njs_mp_stat_t *stat) { njs_mp_block_t *block; njs_rbtree_node_t *node; stat->size = 0; stat->nblocks = 0; stat->cluster_size = mp->cluster_size; stat->page_size = mp->page_size; node = njs_rbtree_min(&mp->blocks); while (njs_rbtree_is_there_successor(&mp->blocks, node)) { block = (njs_mp_block_t *) node; stat->nblocks++; stat->size += block->size; node = njs_rbtree_node_successor(&mp->blocks, node); } } void * njs_mp_alloc(njs_mp_t *mp, size_t size) { njs_debug_alloc("mp alloc: %uz\n", size); #if !(NJS_DEBUG_MEMORY) if (size <= mp->page_size) { return njs_mp_alloc_small(mp, size); } #endif return njs_mp_alloc_large(mp, NJS_MAX_ALIGNMENT, size); } void * njs_mp_zalloc(njs_mp_t *mp, size_t size) { void *p; p = njs_mp_alloc(mp, size); if (njs_fast_path(p != NULL)) { njs_memzero(p, size); } return p; } void * njs_mp_align(njs_mp_t *mp, size_t alignment, size_t size) { njs_debug_alloc("mp align: @%uz:%uz\n", alignment, size); /* Alignment must be a power of 2. */ if (njs_fast_path(njs_is_power_of_two(alignment))) { #if !(NJS_DEBUG_MEMORY) if (size <= mp->page_size && alignment <= mp->page_alignment) { size = njs_max(size, alignment); if (size <= mp->page_size) { return njs_mp_alloc_small(mp, size); } } #endif return njs_mp_alloc_large(mp, alignment, size); } return NULL; } void * njs_mp_zalign(njs_mp_t *mp, size_t alignment, size_t size) { void *p; p = njs_mp_align(mp, alignment, size); if (njs_fast_path(p != NULL)) { njs_memzero(p, size); } return p; } #if !(NJS_DEBUG_MEMORY) njs_inline u_char * njs_mp_page_addr(njs_mp_t *mp, njs_mp_page_t *page) { njs_mp_block_t *block; block = (njs_mp_block_t *) ((u_char *) page - page->number * sizeof(njs_mp_page_t) - offsetof(njs_mp_block_t, pages)); return block->start + (page->number << mp->page_size_shift); } static void * njs_mp_alloc_small(njs_mp_t *mp, size_t size) { u_char *p; njs_mp_page_t *page; njs_mp_slot_t *slot; njs_queue_link_t *link; p = NULL; if (size <= mp->page_size / 2) { /* Find a slot with appropriate chunk size. */ for (slot = mp->slots; slot->size < size; slot++) { /* void */ } size = slot->size; if (njs_fast_path(!njs_queue_is_empty(&slot->pages))) { link = njs_queue_first(&slot->pages); page = njs_queue_link_data(link, njs_mp_page_t, link); p = njs_mp_page_addr(mp, page); p += njs_mp_alloc_chunk(page->map, size); page->chunks--; if (page->chunks == 0) { /* * Remove full page from the mp chunk slot list * of pages with free chunks. */ njs_queue_remove(&page->link); } } else { page = njs_mp_alloc_page(mp); if (njs_fast_path(page != NULL)) { njs_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 >> mp->chunk_size_shift; p = njs_mp_page_addr(mp, page); } } } else { page = njs_mp_alloc_page(mp); if (njs_fast_path(page != NULL)) { page->size = mp->page_size >> mp->chunk_size_shift; p = njs_mp_page_addr(mp, page); } #if (NJS_DEBUG) size = mp->page_size; #endif } njs_debug_alloc("mp chunk:%uz alloc: %p\n", size, p); return p; } static njs_uint_t njs_mp_alloc_chunk(uint8_t *map, njs_uint_t size) { uint8_t mask; njs_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 njs_mp_page_t * njs_mp_alloc_page(njs_mp_t *mp) { njs_mp_page_t *page; njs_mp_block_t *cluster; njs_queue_link_t *link; if (njs_queue_is_empty(&mp->free_pages)) { cluster = njs_mp_alloc_cluster(mp); if (njs_slow_path(cluster == NULL)) { return NULL; } } link = njs_queue_first(&mp->free_pages); njs_queue_remove(link); page = njs_queue_link_data(link, njs_mp_page_t, link); return page; } static njs_mp_block_t * njs_mp_alloc_cluster(njs_mp_t *mp) { njs_uint_t n; njs_mp_block_t *cluster; n = mp->cluster_size >> mp->page_size_shift; cluster = njs_zalloc(sizeof(njs_mp_block_t) + n * sizeof(njs_mp_page_t)); if (njs_slow_path(cluster == NULL)) { return NULL; } /* NJS_MP_CLUSTER_BLOCK type is zero. */ cluster->size = mp->cluster_size; cluster->start = njs_memalign(mp->page_alignment, mp->cluster_size); if (njs_slow_path(cluster->start == NULL)) { njs_free(cluster); return NULL; } n--; cluster->pages[n].number = n; njs_queue_insert_head(&mp->free_pages, &cluster->pages[n].link); while (n != 0) { n--; cluster->pages[n].number = n; njs_queue_insert_before(&cluster->pages[n + 1].link, &cluster->pages[n].link); } njs_rbtree_insert(&mp->blocks, &cluster->node); return cluster; } #endif static void * njs_mp_alloc_large(njs_mp_t *mp, size_t alignment, size_t size) { u_char *p; size_t aligned_size; uint8_t type; njs_mp_block_t *block; /* Allocation must be less than 4G. */ if (njs_slow_path(size >= UINT32_MAX)) { return NULL; } #if (NJS_DEBUG) /* * Ensure that the size is not zero, otherwise njs_mp_find_block() * will not be able to find the block. */ size += size == 0; #endif if (njs_is_power_of_two(size)) { block = njs_malloc(sizeof(njs_mp_block_t)); if (njs_slow_path(block == NULL)) { return NULL; } p = njs_memalign(alignment, size); if (njs_slow_path(p == NULL)) { njs_free(block); return NULL; } type = NJS_MP_DISCRETE_BLOCK; } else { aligned_size = njs_align_size(size, sizeof(uintptr_t)); p = njs_memalign(alignment, aligned_size + sizeof(njs_mp_block_t)); if (njs_slow_path(p == NULL)) { return NULL; } block = (njs_mp_block_t *) (p + aligned_size); type = NJS_MP_EMBEDDED_BLOCK; } block->type = type; block->size = size; block->start = p; njs_rbtree_insert(&mp->blocks, &block->node); return p; } static intptr_t njs_mp_rbtree_compare(njs_rbtree_node_t *node1, njs_rbtree_node_t *node2) { njs_mp_block_t *block1, *block2; block1 = (njs_mp_block_t *) node1; block2 = (njs_mp_block_t *) node2; return (uintptr_t) block1->start - (uintptr_t) block2->start; } njs_mp_cleanup_t * njs_mp_cleanup_add(njs_mp_t *mp, size_t size) { njs_mp_cleanup_t *c; c = njs_mp_alloc(mp, sizeof(njs_mp_cleanup_t)); if (njs_slow_path(c == NULL)) { return NULL; } if (size) { c->data = njs_mp_alloc(mp, size); if (njs_slow_path(c->data == NULL)) { return NULL; } } else { c->data = NULL; } c->handler = NULL; c->next = mp->cleanup; mp->cleanup = c; njs_debug_alloc("mp add cleanup: @%p\n", c); return c; } void njs_mp_free(njs_mp_t *mp, void *p) { const char *err; njs_mp_block_t *block; njs_debug_alloc("mp free: @%p\n", p); block = njs_mp_find_block(&mp->blocks, p); if (njs_fast_path(block != NULL)) { if (block->type == NJS_MP_CLUSTER_BLOCK) { err = njs_mp_chunk_free(mp, block, p); if (njs_fast_path(err == NULL)) { return; } } else if (njs_fast_path(p == block->start)) { njs_rbtree_delete(&mp->blocks, &block->node); if (block->type == NJS_MP_DISCRETE_BLOCK) { njs_free(block); } njs_free(p); return; } else { njs_assert_msg(0, "freed pointer points to middle of blk: %p\n", p); } } else { njs_assert_msg(p == NULL, "freed pointer is out of mp: %p\n", p); } } static njs_mp_block_t * njs_mp_find_block(njs_rbtree_t *tree, u_char *p) { njs_mp_block_t *block; njs_rbtree_node_t *node, *sentinel; node = njs_rbtree_root(tree); sentinel = njs_rbtree_sentinel(tree); while (node != sentinel) { block = (njs_mp_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 * njs_mp_chunk_free(njs_mp_t *mp, njs_mp_block_t *cluster, u_char *p) { u_char *start; uintptr_t offset; njs_uint_t n, size, chunk; njs_mp_page_t *page; njs_mp_slot_t *slot; n = (p - cluster->start) >> mp->page_size_shift; start = cluster->start + (n << mp->page_size_shift); page = &cluster->pages[n]; if (page->size == 0) { return "freed pointer points to already free page: %p"; } size = page->size << mp->chunk_size_shift; if (size != mp->page_size) { offset = (uintptr_t) (p - start) & (mp->page_size - 1); chunk = offset / size; if (njs_slow_path(offset != chunk * size)) { return "freed pointer points to wrong chunk: %p"; } if (njs_slow_path(njs_mp_chunk_is_free(page->map, chunk))) { return "freed pointer points to already free chunk: %p"; } njs_mp_chunk_set_free(page->map, chunk); /* Find a slot with appropriate chunk size. */ for (slot = mp->slots; slot->size < size; slot++) { /* void */ } if (page->chunks != slot->chunks) { page->chunks++; if (page->chunks == 1) { /* * Add the page to the head of mp chunk slot list * of pages with free chunks. */ njs_queue_insert_head(&slot->pages, &page->link); } njs_mp_free_junk(p, size); return NULL; } else { /* * All chunks are free, remove the page from mp chunk slot * list of pages with free chunks. */ njs_queue_remove(&page->link); } } else if (njs_slow_path(p != start)) { return "invalid pointer to chunk: %p"; } /* Add the free page to the mp's free pages tree. */ page->size = 0; njs_queue_insert_head(&mp->free_pages, &page->link); njs_mp_free_junk(p, size); /* Test if all pages in the cluster are free. */ page = cluster->pages; n = mp->cluster_size >> mp->page_size_shift; do { if (page->size != 0) { return NULL; } page++; n--; } while (n != 0); /* Free cluster. */ page = cluster->pages; n = mp->cluster_size >> mp->page_size_shift; do { njs_queue_remove(&page->link); page++; n--; } while (n != 0); njs_rbtree_delete(&mp->blocks, &cluster->node); p = cluster->start; njs_free(cluster); njs_free(p); return NULL; } njs-0.8.9/src/njs_mp.h000066400000000000000000000033121474132077100145550ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_MP_H_INCLUDED_ #define _NJS_MP_H_INCLUDED_ typedef struct njs_mp_s njs_mp_t; typedef struct njs_mp_cleanup_s njs_mp_cleanup_t; typedef void (*njs_mp_cleanup_pt)(void *data); struct njs_mp_cleanup_s { njs_mp_cleanup_pt handler; void *data; njs_mp_cleanup_t *next; }; typedef struct { size_t size; size_t nblocks; size_t page_size; size_t cluster_size; } njs_mp_stat_t; NJS_EXPORT njs_mp_t *njs_mp_create(size_t cluster_size, size_t page_alignment, size_t page_size, size_t min_chunk_size) NJS_MALLOC_LIKE; NJS_EXPORT njs_mp_t * njs_mp_fast_create(size_t cluster_size, size_t page_alignment, size_t page_size, size_t min_chunk_size) NJS_MALLOC_LIKE; NJS_EXPORT njs_bool_t njs_mp_is_empty(njs_mp_t *mp); NJS_EXPORT void njs_mp_destroy(njs_mp_t *mp); NJS_EXPORT void njs_mp_stat(njs_mp_t *mp, njs_mp_stat_t *stat); NJS_EXPORT void *njs_mp_alloc(njs_mp_t *mp, size_t size) NJS_MALLOC_LIKE; NJS_EXPORT void *njs_mp_zalloc(njs_mp_t *mp, size_t size) NJS_MALLOC_LIKE; NJS_EXPORT void *njs_mp_align(njs_mp_t *mp, size_t alignment, size_t size) NJS_MALLOC_LIKE; NJS_EXPORT void *njs_mp_zalign(njs_mp_t *mp, size_t alignment, size_t size) NJS_MALLOC_LIKE; NJS_EXPORT njs_mp_cleanup_t *njs_mp_cleanup_add(njs_mp_t *mp, size_t size); NJS_EXPORT void njs_mp_free(njs_mp_t *mp, void *p); #if (NJS_ALLOC_DEBUG) #define njs_debug_alloc(...) \ njs_stderror(__VA_ARGS__) #else #define njs_debug_alloc(...) #endif #endif /* _NJS_MP_H_INCLUDED_ */ njs-0.8.9/src/njs_murmur_hash.c000066400000000000000000000023441474132077100164720ustar00rootroot00000000000000 /* * The code is based on the code by Austin Appleby, * released to the public domain. */ #include uint32_t njs_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; /* Fall through. */ case 2: h ^= p[1] << 8; /* Fall through. */ 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 njs_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; } njs-0.8.9/src/njs_murmur_hash.h000066400000000000000000000004761474132077100165030ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_MURMUR_HASH_H_INCLUDED_ #define _NJS_MURMUR_HASH_H_INCLUDED_ NJS_EXPORT uint32_t njs_murmur_hash2(const void *data, size_t len); NJS_EXPORT uint32_t njs_murmur_hash2_uint32(const void *data); #endif /* _NJS_MURMUR_HASH_H_INCLUDED_ */ njs-0.8.9/src/njs_number.c000066400000000000000000000606621474132077100154370ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include /* * 2^53 - 1 is the largest integer n such that n and n + 1 * as well as -n and -n - 1 are all exactly representable * in the IEEE-754 format. */ #define NJS_MAX_SAFE_INTEGER ((1LL << 53) - 1) static njs_int_t njs_number_to_string_radix(njs_vm_t *vm, njs_value_t *string, double number, uint32_t radix); double njs_key_to_index(const njs_value_t *value) { njs_array_t *array; if (njs_fast_path(njs_is_numeric(value))) { return njs_number(value); } else if (njs_is_string(value)) { return njs_string_to_index(value); } else if (njs_is_array(value)) { array = njs_array(value); if (njs_lvlhsh_is_empty(&array->object.hash)) { if (array->length == 0) { /* An empty array value is zero. */ return 0; } if (array->length == 1 && njs_is_valid(&array->start[0])) { /* A single value array is the zeroth array value. */ return njs_key_to_index(&array->start[0]); } } } return NAN; } double njs_number_dec_parse(const u_char **start, const u_char *end, njs_bool_t literal) { return njs_strtod(start, end, literal); } double njs_number_oct_parse(const u_char **start, const u_char *end, njs_bool_t literal) { u_char c; double num; const u_char *p, *_; p = *start; num = 0; _ = p - 1; for (; p < end; p++) { /* Values less than '0' become >= 208. */ c = *p - '0'; if (njs_slow_path(c > 7)) { if (literal && *p == '_' && (p - _) > 1) { _ = p; continue; } break; } num = num * 8 + c; } *start = p; return num; } double njs_number_bin_parse(const u_char **start, const u_char *end, njs_bool_t literal) { u_char c; double num; const u_char *p, *_; p = *start; num = 0; _ = p - 1; for (; p < end; p++) { /* Values less than '0' become >= 208. */ c = *p - '0'; if (njs_slow_path(c > 1)) { if (literal && *p == '_' && (p - _) > 1) { _ = p; continue; } break; } num = num * 2 + c; } *start = p; return num; } double njs_number_hex_parse(const u_char **start, const u_char *end, njs_bool_t literal) { double num; njs_int_t n; const u_char *p, *_; p = *start; num = 0; _ = p - 1; for (; p < end; p++) { n = njs_char_to_hex(*p); if (njs_slow_path(n < 0)) { if (literal && *p == '_' && (p - _) > 1) { _ = p; continue; } break; } num = num * 16 + n; } *start = p; return num; } static double njs_number_radix_parse(const u_char **start, const u_char *end, uint8_t radix) { uint8_t d; double num, n; const u_char *p; static const int8_t digits[256] njs_aligned(32) = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, }; num = NAN; n = 0; for (p = *start; p < end; p++) { d = digits[*p]; if (njs_slow_path(d >= radix)) { break; } n = (n * radix) + d; num = n; } *start = p; return num; } njs_int_t njs_number_to_string(njs_vm_t *vm, njs_value_t *string, const njs_value_t *number) { double num; size_t size; const njs_value_t *value; u_char buf[128]; num = njs_number(number); if (isnan(num)) { value = &njs_string_nan; } else if (isinf(num)) { if (num < 0) { value = &njs_string_minus_infinity; } else { value = &njs_string_plus_infinity; } } else { size = njs_dtoa(num, (char *) buf); return njs_string_new(vm, string, buf, size, size); } *string = *value; return NJS_OK; } njs_int_t njs_int64_to_string(njs_vm_t *vm, njs_value_t *value, int64_t i64) { size_t size; u_char *dst, *p; u_char buf[128]; if (njs_fast_path(i64 >= 0 && i64 < 0x3fffffffffffLL)) { /* Fits to short_string. */ dst = njs_string_short_start(value); p = njs_sprintf(dst, dst + NJS_STRING_SHORT, "%L", i64); njs_string_short_set(value, p - dst, p - dst); return NJS_OK; } size = njs_dtoa(i64, (char *) buf); return njs_string_new(vm, value, buf, size, size); } njs_int_t njs_number_to_chain(njs_vm_t *vm, njs_chb_t *chain, double num) { size_t size; u_char *p; if (isnan(num)) { njs_chb_append_literal(chain, "NaN"); return njs_length("NaN"); } if (isinf(num)) { if (num < 0) { njs_chb_append_literal(chain, "-Infinity"); return njs_length("-Infinity"); } else { njs_chb_append_literal(chain, "Infinity"); return njs_length("Infinity"); } } p = njs_chb_reserve(chain, 64); if (njs_slow_path(p == NULL)) { return NJS_ERROR; } size = njs_dtoa(num, (char *) p); njs_chb_written(chain, size); return size; } static njs_int_t njs_number_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_value_t *value; njs_object_value_t *object; if (nargs == 1) { value = njs_value_arg(&njs_value_zero); } else { value = &args[1]; if (njs_slow_path(!njs_is_number(value))) { ret = njs_value_to_numeric(vm, value, value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } } if (vm->top_frame->ctor) { object = njs_object_value_alloc(vm, NJS_OBJ_TYPE_NUMBER, 0, value); if (njs_slow_path(object == NULL)) { return NJS_ERROR; } njs_set_object_value(retval, object); } else { njs_set_number(retval, njs_number(value)); } return NJS_OK; } static njs_int_t njs_number_is_integer(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double num; njs_bool_t integer; integer = 0; if (nargs > 1 && njs_is_number(&args[1])) { num = njs_number(&args[1]); if (num == trunc(num) && !isinf(num)) { integer = 1; } } njs_set_boolean(retval, integer); return NJS_OK; } static njs_int_t njs_number_is_safe_integer(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double num; njs_bool_t integer; integer = 0; if (nargs > 1 && njs_is_number(&args[1])) { num = njs_number(&args[1]); if (num == njs_unsafe_cast_double_to_int64(num) && fabs(num) <= NJS_MAX_SAFE_INTEGER) { integer = 1; } } njs_set_boolean(retval, integer); return NJS_OK; } static njs_int_t njs_number_is_nan(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_bool_t nan; nan = 0; if (nargs > 1 && njs_is_number(&args[1]) && isnan(njs_number(&args[1]))) { nan = 1; } njs_set_boolean(retval, nan); return NJS_OK; } static njs_int_t njs_number_is_finite(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double num; njs_bool_t finite; finite = 0; if (nargs > 1 && njs_is_number(&args[1])) { num = njs_number(&args[1]); if (!isnan(num) && !isinf(num)) { finite = 1; } } njs_set_boolean(retval, finite); return NJS_OK; } static const njs_object_prop_t njs_number_constructor_properties[] = { NJS_DECLARE_PROP_LENGTH(1), NJS_DECLARE_PROP_NAME("Number"), NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), NJS_DECLARE_PROP_VALUE("EPSILON", njs_value(NJS_NUMBER, 1, DBL_EPSILON), 0), NJS_DECLARE_PROP_LVALUE("MAX_SAFE_INTEGER", njs_value(NJS_NUMBER, 1, NJS_MAX_SAFE_INTEGER), 0), NJS_DECLARE_PROP_LVALUE("MIN_SAFE_INTEGER", njs_value(NJS_NUMBER, 1, -NJS_MAX_SAFE_INTEGER), 0), NJS_DECLARE_PROP_VALUE("MAX_VALUE", njs_value(NJS_NUMBER, 1, DBL_MAX), 0), NJS_DECLARE_PROP_VALUE("MIN_VALUE", njs_value(NJS_NUMBER, 1, DBL_MIN), 0), NJS_DECLARE_PROP_VALUE("NaN", njs_value(NJS_NUMBER, 0, NAN), 0), NJS_DECLARE_PROP_LVALUE("POSITIVE_INFINITY", njs_value(NJS_NUMBER, 1, INFINITY), 0), NJS_DECLARE_PROP_LVALUE("NEGATIVE_INFINITY", njs_value(NJS_NUMBER, 1, -INFINITY), 0), NJS_DECLARE_PROP_NATIVE("isFinite", njs_number_is_finite, 1, 0), NJS_DECLARE_PROP_NATIVE("isInteger", njs_number_is_integer, 1, 0), NJS_DECLARE_PROP_NATIVE("isSafeInteger", njs_number_is_safe_integer, 1, 0), NJS_DECLARE_PROP_NATIVE("isNaN", njs_number_is_nan, 1, 0), NJS_DECLARE_PROP_NATIVE("parseFloat", njs_number_parse_float, 1, 0), NJS_DECLARE_PROP_NATIVE("parseInt", njs_number_parse_int, 2, 0), }; const njs_object_init_t njs_number_constructor_init = { njs_number_constructor_properties, njs_nitems(njs_number_constructor_properties), }; static njs_int_t njs_number_prototype_value_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_value_t *value; value = njs_argument(args, 0); if (value->type != NJS_NUMBER) { if (njs_is_object_number(value)) { value = njs_object_value(value); } else { njs_type_error(vm, "unexpected value type:%s", njs_type_string(value->type)); return NJS_ERROR; } } njs_value_assign(retval, value); return NJS_OK; } static njs_int_t njs_number_prototype_to_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double number; int32_t radix; njs_int_t ret; njs_value_t *value; value = &args[0]; if (value->type != NJS_NUMBER) { if (njs_is_object_number(value)) { value = njs_object_value(value); } else { njs_type_error(vm, "unexpected value type:%s", njs_type_string(value->type)); return NJS_ERROR; } } if (nargs > 1) { ret = njs_value_to_int32(vm, &args[1], &radix); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (radix < 2 || radix > 36 || radix != (int) radix) { njs_range_error(vm, "radix argument must be between 2 and 36"); return NJS_ERROR; } number = njs_number(value); if (radix != 10 && !isnan(number) && !isinf(number) && number != 0) { return njs_number_to_string_radix(vm, retval, number, radix); } } return njs_number_to_string(vm, retval, value); } static njs_int_t njs_number_prototype_to_fixed(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { u_char *p; int64_t frac; double number; size_t length, size; njs_int_t ret, point, prefix, postfix; njs_value_t *value; u_char buf[128], buf2[128]; /* 128 > 100 + 21 + njs_length(".-\0"). */ value = &args[0]; if (value->type != NJS_NUMBER) { if (njs_is_object_number(value)) { value = njs_object_value(value); } else { njs_type_error(vm, "unexpected value type:%s", njs_type_string(value->type)); return NJS_ERROR; } } ret = njs_value_to_integer(vm, njs_arg(args, nargs, 1), &frac); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_slow_path(frac < 0 || frac > 100)) { njs_range_error(vm, "digits argument must be between 0 and 100"); return NJS_ERROR; } number = njs_number(value); if (njs_slow_path(isnan(number) || fabs(number) >= 1e21)) { return njs_number_to_string(vm, retval, value); } point = 0; length = njs_fixed_dtoa(number, (njs_int_t) frac, (char *) buf, &point); prefix = 0; postfix = 0; if (point <= 0) { prefix = -point + 1; point = 1; } if (prefix + (njs_int_t) length < point + frac) { postfix = point + frac - length - prefix; } size = prefix + length + postfix + !!(number < 0); if (frac > 0) { size += njs_length("."); } p = buf2; while (--prefix >= 0) { *p++ = '0'; } if (length != 0) { p = njs_cpymem(p, buf, length); } while (--postfix >= 0) { *p++ = '0'; } p = njs_string_alloc(vm, retval, size, size); if (njs_slow_path(p == NULL)) { return NJS_ERROR; } if (number < 0) { *p++ = '-'; } p = njs_cpymem(p, buf2, point); if (frac > 0) { *p++ = '.'; memcpy(p, &buf2[point], frac); } return NJS_OK; } static njs_int_t njs_number_prototype_to_precision(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double number; size_t size; int64_t precision; njs_int_t ret; njs_value_t *value; u_char buf[128]; /* 128 > 100 + 21 + njs_length(".-\0"). */ value = &args[0]; if (value->type != NJS_NUMBER) { if (njs_is_object_number(value)) { value = njs_object_value(value); } else { njs_type_error(vm, "unexpected value type:%s", njs_type_string(value->type)); return NJS_ERROR; } } if (njs_is_undefined(njs_arg(args, nargs, 1))) { return njs_number_to_string(vm, retval, value); } ret = njs_value_to_integer(vm, njs_argument(args, 1), &precision); if (njs_slow_path(ret != NJS_OK)) { return ret; } number = njs_number(value); if (njs_slow_path(isnan(number) || isinf(number))) { return njs_number_to_string(vm, retval, value); } if (njs_slow_path(precision < 1 || precision > 100)) { njs_range_error(vm, "precision argument must be between 1 and 100"); return NJS_ERROR; } size = njs_dtoa_precision(number, (char *) buf, (size_t) precision); return njs_string_new(vm, retval, buf, size, size); } static njs_int_t njs_number_prototype_to_exponential(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double number; size_t size; int64_t frac; njs_int_t ret; njs_value_t *value, *value_frac; u_char buf[128]; value = &args[0]; if (value->type != NJS_NUMBER) { if (njs_is_object_number(value)) { value = njs_object_value(value); } else { njs_type_error(vm, "unexpected value type:%s", njs_type_string(value->type)); return NJS_ERROR; } } value_frac = njs_arg(args, nargs, 1); ret = njs_value_to_integer(vm, value_frac, &frac); if (njs_slow_path(ret != NJS_OK)) { return ret; } number = njs_number(value); if (njs_slow_path(isnan(number) || isinf(number))) { return njs_number_to_string(vm, retval, value); } if (njs_is_defined(value_frac)) { if (njs_slow_path(frac < 0 || frac > 100)) { njs_range_error(vm, "digits argument must be between 0 and 100"); return NJS_ERROR; } } else { frac = -1; } size = njs_dtoa_exponential(number, (char *) buf, (njs_int_t) frac); return njs_string_new(vm, retval, buf, size, size); } /* * The radix equal to 2 produces the longest value for a number. */ #define NJS_STRING_RADIX_INTERGRAL_LEN (1 + 1024) #define NJS_STRING_RADIX_FRACTION_LEN (1 + 1075) #define NJS_STRING_RADIX_LEN \ (NJS_STRING_RADIX_INTERGRAL_LEN + NJS_STRING_RADIX_FRACTION_LEN) njs_inline double njs_number_next_double(double n) { njs_diyfp_t v; v = njs_d2diyfp(n); if (signbit(n)) { if (v.significand == 0) { return 0.0; } v.significand--; } else { v.significand++; } return njs_diyfp2d(v); } static njs_int_t njs_number_to_string_radix(njs_vm_t *vm, njs_value_t *string, double number, uint32_t radix) { int digit; char ch; double n, remainder, integer, fraction, delta; u_char *p, *end; uint32_t size; u_char buf[NJS_STRING_RADIX_LEN]; static const char *digits = "0123456789abcdefghijklmnopqrstuvwxyz"; p = buf + NJS_STRING_RADIX_INTERGRAL_LEN; end = p; n = number; if (n < 0) { n = -n; } integer = floor(n); fraction = n - integer; delta = 0.5 * (njs_number_next_double(n) - n); delta = njs_max(njs_number_next_double(0.0), delta); if (fraction >= delta && delta != 0) { *p++ = '.'; do { fraction *= radix; delta *= radix; digit = (int) fraction; *p++ = digits[digit]; fraction -= digit; if ((fraction > 0.5 || (fraction == 0.5 && (digit & 1))) && (fraction + delta > 1)) { while (p-- != buf) { if (p == buf + NJS_STRING_RADIX_INTERGRAL_LEN) { integer += 1; break; } ch = *p; digit = (ch > '9') ? ch - 'a' + 10 : ch - '0'; if ((uint32_t) (digit + 1) < radix) { *p++ = digits[digit + 1]; break; } } break; } } while (fraction >= delta); end = p; } p = buf + NJS_STRING_RADIX_INTERGRAL_LEN; while (njs_d2diyfp(integer / radix).exp > 0) { integer /= radix; *(--p) = '0'; } do { remainder = fmod(integer, radix); *(--p) = digits[(int) remainder]; integer = (integer - remainder) / radix; } while (integer > 0); if (number < 0) { *(--p) = '-'; } size = (uint32_t) (end - p); return njs_string_new(vm, string, p, size, size); } static const njs_object_prop_t njs_number_prototype_properties[] = { NJS_DECLARE_PROP_HANDLER("__proto__", njs_primitive_prototype_get_proto, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("constructor", njs_object_prototype_create_constructor, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_NATIVE("valueOf", njs_number_prototype_value_of, 0, 0), NJS_DECLARE_PROP_NATIVE("toString", njs_number_prototype_to_string, 1, 0), NJS_DECLARE_PROP_NATIVE("toFixed", njs_number_prototype_to_fixed, 1, 0), NJS_DECLARE_PROP_NATIVE("toPrecision", njs_number_prototype_to_precision, 1, 0), NJS_DECLARE_PROP_NATIVE("toExponential", njs_number_prototype_to_exponential, 1, 0), }; const njs_object_init_t njs_number_prototype_init = { njs_number_prototype_properties, njs_nitems(njs_number_prototype_properties), }; njs_int_t njs_number_global_is_nan(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double num; njs_int_t ret; ret = njs_value_to_number(vm, njs_arg(args, nargs, 1), &num); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_set_boolean(retval, isnan(num)); return NJS_OK; } njs_int_t njs_number_global_is_finite(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double num; njs_int_t ret; ret = njs_value_to_number(vm, njs_arg(args, nargs, 1), &num); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_set_boolean(retval, !(isnan(num) || isinf(num))); return NJS_OK; } njs_int_t njs_number_parse_int(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double num; int32_t radix; njs_int_t ret; njs_bool_t minus, test_prefix; njs_value_t *value, lvalue; const u_char *p, *end; njs_string_prop_t string; num = NAN; value = njs_lvalue_arg(&lvalue, args, nargs, 1); ret = njs_value_to_string(vm, value, value); if (njs_slow_path(ret != NJS_OK)) { return ret; } (void) njs_string_trim(value, &string, NJS_TRIM_START); if (string.size == 0) { goto done; } p = string.start; end = p + string.size; minus = 0; if (p[0] == '-') { p++; minus = 1; } else if (p[0] == '+') { p++; } test_prefix = (end - p > 1); radix = 0; if (nargs > 2) { ret = njs_value_to_int32(vm, njs_argument(args, 2), &radix); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (radix != 0) { if (radix < 2 || radix > 36) { goto done; } if (radix != 16) { test_prefix = 0; } } } if (radix == 0) { radix = 10; } if (test_prefix && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) { p += 2; radix = 16; } num = njs_number_radix_parse(&p, end, radix); num = minus ? -num : num; done: njs_set_number(retval, num); return NJS_OK; } njs_int_t njs_number_parse_float(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double num; njs_int_t ret; njs_value_t *value, lvalue; njs_bool_t minus; const u_char *p, *start, *end; njs_string_prop_t string; value = njs_lvalue_arg(&lvalue, args, nargs, 1); ret = njs_value_to_string(vm, value, value); if (njs_slow_path(ret != NJS_OK)) { return ret; } (void) njs_string_trim(value, &string, NJS_TRIM_START); p = string.start; end = p + string.size; minus = 0; if (p == end) { num = NAN; goto done; } if (*p == '+') { p++; } else if (*p == '-') { p++; minus = 1; } start = p; num = njs_number_dec_parse(&p, end, 0); if (p == start) { if (p + njs_length("Infinity") > end || memcmp(p, "Infinity", njs_length("Infinity")) != 0) { num = NAN; goto done; } num = INFINITY; p += njs_length("Infinity"); } done: njs_set_number(retval, minus ? -num : num); return NJS_OK; } const njs_object_type_init_t njs_number_type_init = { .constructor = njs_native_ctor(njs_number_constructor, 1, 0), .constructor_props = &njs_number_constructor_init, .prototype_props = &njs_number_prototype_init, .prototype_value = { .object_value = { .value = njs_value(NJS_NUMBER, 0, 0.0), .object = { .type = NJS_OBJECT_VALUE } } }, }; njs-0.8.9/src/njs_number.h000066400000000000000000000102021474132077100154250ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_NUMBER_H_INCLUDED_ #define _NJS_NUMBER_H_INCLUDED_ #define NJS_MAX_LENGTH (0x1fffffffffffffLL) #define NJS_INT64_DBL_MIN (-9.223372036854776e+18) /* closest to INT64_MIN */ #define NJS_INT64_DBL_MAX (9.223372036854776e+18) /* closest to INT64_MAX */ double njs_key_to_index(const njs_value_t *value); double njs_number_dec_parse(const u_char **start, const u_char *end, njs_bool_t literal); double njs_number_oct_parse(const u_char **start, const u_char *end, njs_bool_t literal); double njs_number_bin_parse(const u_char **start, const u_char *end, njs_bool_t literal); double njs_number_hex_parse(const u_char **start, const u_char *end, njs_bool_t literal); njs_int_t njs_number_to_string(njs_vm_t *vm, njs_value_t *string, const njs_value_t *number); njs_int_t njs_number_to_chain(njs_vm_t *vm, njs_chb_t *chain, double number); njs_int_t njs_number_global_is_nan(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); njs_int_t njs_number_global_is_finite(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); njs_int_t njs_number_parse_int(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); njs_int_t njs_number_parse_float(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); njs_inline NJS_NOSANITIZE("float-cast-overflow") njs_bool_t njs_number_is_integer_index(double num) { uint32_t u32; u32 = num; return (u32 == num && u32 != 0xffffffff); } njs_inline njs_bool_t njs_key_is_integer_index(double num, const njs_value_t *value) { return (njs_number_is_integer_index(num)) && !(njs_is_string(value) && num == 0 && signbit(num)); } njs_inline int64_t njs_number_to_integer(double num) { if (njs_fast_path(!isnan(num))) { if (num < NJS_INT64_DBL_MIN) { return INT64_MIN; } else if (num > NJS_INT64_DBL_MAX) { return INT64_MAX; } return num; } return 0; } njs_inline int32_t njs_number_to_int32(double num) { uint32_t r; uint64_t v; njs_int_t exp; njs_diyfp_conv_t conv; conv.d = num; exp = (conv.u64 & NJS_DBL_EXPONENT_MASK) >> NJS_DBL_SIGNIFICAND_SIZE; if (njs_fast_path(exp < (NJS_DBL_EXPONENT_OFFSET + 31))) { /* |num| < 2**31. */ return num; } if (exp < (NJS_DBL_EXPONENT_OFFSET + 31 + 53)) { v = (conv.u64 & NJS_DBL_SIGNIFICAND_MASK) | NJS_DBL_HIDDEN_BIT; v <<= (exp - NJS_DBL_EXPONENT_BIAS + 32); r = v >> 32; if (conv.u64 & NJS_DBL_SIGN_MASK) { r = -r; } return r; } /* * ES5.1: integer must be modulo 2^32. * The distance between larger doubles * (exp >= NJS_DBL_EXPONENT_OFFSET + 31 + 53) is a multiple of 2**32 => 0. * This also handles NaN and Inf. */ return 0; } njs_inline uint32_t njs_number_to_uint32(double num) { return (uint32_t) njs_number_to_int32(num); } njs_inline uint16_t njs_number_to_uint16(double num) { return (uint16_t) njs_number_to_int32(num); } njs_inline uint64_t njs_number_to_length(double num) { if (isnan(num)) { return 0; } if (num > NJS_MAX_LENGTH) { return NJS_MAX_LENGTH; } else if (num < 0.0) { return 0; } return (uint64_t) num; } njs_inline njs_int_t njs_char_to_hex(u_char c) { c |= 0x20; /* Values less than '0' become >= 208. */ c = c - '0'; if (c > 9) { /* Values less than 'a' become >= 159. */ c = c - ('a' - '0'); if (njs_slow_path(c > 5)) { return -1; } c += 10; } return c; } njs_inline void njs_uint32_to_string(njs_value_t *value, uint32_t u32) { u_char *dst, *p; dst = njs_string_short_start(value); p = njs_sprintf(dst, dst + NJS_STRING_SHORT, "%uD", u32); njs_string_short_set(value, p - dst, p - dst); } extern const njs_object_type_init_t njs_number_type_init; #endif /* _NJS_NUMBER_H_INCLUDED_ */ njs-0.8.9/src/njs_object.c000066400000000000000000002155671474132077100154230ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include typedef enum { NJS_OBJECT_INTEGRITY_SEALED, NJS_OBJECT_INTEGRITY_FROZEN, } njs_object_integrity_level_t; static njs_int_t njs_object_hash_test(njs_lvlhsh_query_t *lhq, void *data); static njs_object_prop_t *njs_object_exist_in_proto(const njs_object_t *begin, const njs_object_t *end, njs_lvlhsh_query_t *lhq); static njs_int_t njs_object_enumerate_array(njs_vm_t *vm, const njs_array_t *array, njs_array_t *items, uint32_t flags); static njs_int_t njs_object_enumerate_typed_array(njs_vm_t *vm, const njs_typed_array_t *array, njs_array_t *items, uint32_t flags); static njs_int_t njs_object_enumerate_string(njs_vm_t *vm, const njs_value_t *value, njs_array_t *items, uint32_t flags); static njs_int_t njs_object_enumerate_object(njs_vm_t *vm, const njs_object_t *object, njs_array_t *items, uint32_t flags); static njs_int_t njs_object_own_enumerate_object(njs_vm_t *vm, const njs_object_t *object, const njs_object_t *parent, njs_array_t *items, uint32_t flags); static njs_int_t njs_object_define_properties(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_object_set_prototype(njs_vm_t *vm, njs_object_t *object, const njs_value_t *value); njs_object_t * njs_object_alloc(njs_vm_t *vm) { njs_object_t *object; object = njs_mp_alloc(vm->mem_pool, sizeof(njs_object_t)); if (njs_fast_path(object != NULL)) { njs_lvlhsh_init(&object->hash); njs_lvlhsh_init(&object->shared_hash); object->__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_OBJECT); object->slots = NULL; object->type = NJS_OBJECT; object->shared = 0; object->extensible = 1; object->error_data = 0; object->fast_array = 0; return object; } njs_memory_error(vm); return NULL; } njs_object_t * njs_object_value_copy(njs_vm_t *vm, njs_value_t *value) { size_t size; njs_object_t *object, *proto; object = njs_object(value); if (!object->shared) { return object; } switch (object->type) { case NJS_OBJECT: size = sizeof(njs_object_t); proto = (object->__proto__ != NULL) ? njs_vm_proto(vm, NJS_OBJ_TYPE_OBJECT) : NULL; break; case NJS_ARRAY: size = sizeof(njs_array_t); njs_assert_msg(!object->fast_array, "shared fast_array is not supported"); proto = (object->__proto__ != NULL) ? njs_vm_proto(vm, NJS_OBJ_TYPE_ARRAY) : NULL; break; case NJS_OBJECT_VALUE: size = sizeof(njs_object_value_t); proto = (object->__proto__ != NULL) ? njs_vm_proto(vm, NJS_OBJ_TYPE_OBJECT) : NULL; break; default: njs_internal_error(vm, "unexpected object type to copy"); return NULL; } object = njs_mp_alloc(vm->mem_pool, size); if (njs_fast_path(object != NULL)) { memcpy(object, njs_object(value), size); object->__proto__ = proto; object->shared = 0; value->data.u.object = object; return object; } njs_memory_error(vm); return NULL; } njs_object_value_t * njs_object_value_alloc(njs_vm_t *vm, njs_uint_t prototype_index, size_t extra, const njs_value_t *value) { njs_object_value_t *ov; ov = njs_mp_alloc(vm->mem_pool, sizeof(njs_object_value_t) + extra); if (njs_slow_path(ov == NULL)) { njs_memory_error(vm); return NULL; } njs_lvlhsh_init(&ov->object.hash); if (prototype_index == NJS_OBJ_TYPE_STRING) { ov->object.shared_hash = vm->shared->string_instance_hash; } else { njs_lvlhsh_init(&ov->object.shared_hash); } ov->object.type = NJS_OBJECT_VALUE; ov->object.shared = 0; ov->object.extensible = 1; ov->object.error_data = 0; ov->object.fast_array = 0; ov->object.__proto__ = njs_vm_proto(vm, prototype_index); ov->object.slots = NULL; if (value != NULL) { ov->value = *value; } return ov; } njs_int_t njs_object_hash_create(njs_vm_t *vm, njs_lvlhsh_t *hash, const njs_object_prop_t *prop, njs_uint_t n) { njs_int_t ret; njs_lvlhsh_query_t lhq; lhq.replace = 0; lhq.proto = &njs_object_hash_proto; lhq.pool = vm->mem_pool; while (n != 0) { njs_object_property_key_set(&lhq, &prop->name, 0); lhq.value = (void *) prop; ret = njs_lvlhsh_insert(hash, &lhq); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "lvlhsh insert failed"); return NJS_ERROR; } prop++; n--; } return NJS_OK; } const njs_lvlhsh_proto_t njs_object_hash_proto njs_aligned(64) = { NJS_LVLHSH_DEFAULT, njs_object_hash_test, njs_lvlhsh_alloc, njs_lvlhsh_free, }; static njs_int_t njs_object_hash_test(njs_lvlhsh_query_t *lhq, void *data) { size_t size; u_char *start; njs_value_t *name; njs_object_prop_t *prop; prop = data; name = &prop->name; if (njs_slow_path(njs_is_symbol(name))) { return ((njs_symbol_key(name) == lhq->key_hash) && lhq->key.start == NULL) ? NJS_OK : NJS_DECLINED; } /* string. */ size = name->short_string.size; if (size != NJS_STRING_LONG) { if (lhq->key.length != size) { return NJS_DECLINED; } start = name->short_string.start; } else { if (lhq->key.length != name->long_string.size) { return NJS_DECLINED; } start = name->long_string.data->start; } if (memcmp(start, lhq->key.start, lhq->key.length) == 0) { return NJS_OK; } return NJS_DECLINED; } static njs_int_t njs_object_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_uint_t type, index; njs_value_t *value; njs_object_t *object; njs_object_value_t *obj_val; value = njs_arg(args, nargs, 1); type = value->type; if (njs_is_null_or_undefined(value)) { object = njs_object_alloc(vm); if (njs_slow_path(object == NULL)) { return NJS_ERROR; } njs_set_object(retval, object); return NJS_OK; } if (njs_is_primitive(value)) { index = njs_primitive_prototype_index(type); obj_val = njs_object_value_alloc(vm, index, 0, value); if (njs_slow_path(obj_val == NULL)) { return NJS_ERROR; } njs_set_object_value(retval, obj_val); return NJS_OK; } if (njs_slow_path(!njs_is_object(value))) { njs_type_error(vm, "unexpected constructor argument:%s", njs_type_string(type)); return NJS_ERROR; } njs_value_assign(retval, value); return NJS_OK; } static njs_int_t njs_object_create(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_value_t *value, *descs, arguments[3]; njs_object_t *object; value = njs_arg(args, nargs, 1); if (njs_is_object(value) || njs_is_null(value)) { object = njs_object_alloc(vm); if (njs_slow_path(object == NULL)) { return NJS_ERROR; } if (!njs_is_null(value)) { object->__proto__ = njs_object(value); } else { object->__proto__ = NULL; } descs = njs_arg(args, nargs, 2); if (njs_slow_path(!njs_is_undefined(descs))) { arguments[0] = args[0]; njs_set_object(&arguments[1], object); arguments[2] = *descs; return njs_object_define_properties(vm, arguments, 3, unused, retval); } njs_set_object(retval, object); return NJS_OK; } njs_type_error(vm, "prototype may only be an object or null: %s", njs_type_string(value->type)); return NJS_ERROR; } static njs_int_t njs_object_keys(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_value_t *value; njs_array_t *keys; value = njs_arg(args, nargs, 1); if (njs_is_null_or_undefined(value)) { njs_type_error(vm, "cannot convert %s argument to object", njs_type_string(value->type)); return NJS_ERROR; } keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS | NJS_ENUM_STRING | NJS_ENUM_ENUMERABLE_ONLY); if (keys == NULL) { return NJS_ERROR; } njs_set_array(retval, keys); return NJS_OK; } static njs_int_t njs_object_values(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_array_t *array; njs_value_t *value; value = njs_arg(args, nargs, 1); if (njs_is_null_or_undefined(value)) { njs_type_error(vm, "cannot convert %s argument to object", njs_type_string(value->type)); return NJS_ERROR; } array = njs_value_own_enumerate(vm, value, NJS_ENUM_VALUES | NJS_ENUM_STRING | NJS_ENUM_ENUMERABLE_ONLY); if (array == NULL) { return NJS_ERROR; } njs_set_array(retval, array); return NJS_OK; } static njs_int_t njs_object_entries(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_array_t *array; njs_value_t *value; value = njs_arg(args, nargs, 1); if (njs_is_null_or_undefined(value)) { njs_type_error(vm, "cannot convert %s argument to object", njs_type_string(value->type)); return NJS_ERROR; } array = njs_value_own_enumerate(vm, value, NJS_ENUM_BOTH | NJS_ENUM_STRING | NJS_ENUM_ENUMERABLE_ONLY); if (array == NULL) { return NJS_ERROR; } njs_set_array(retval, array); return NJS_OK; } static njs_object_prop_t * njs_object_exist_in_proto(const njs_object_t *object, const njs_object_t *end, njs_lvlhsh_query_t *lhq) { njs_int_t ret; njs_object_prop_t *prop; while (object != end) { ret = njs_lvlhsh_find(&object->hash, lhq); if (njs_fast_path(ret == NJS_OK)) { prop = lhq->value; if (prop->type == NJS_WHITEOUT) { goto next; } return lhq->value; } ret = njs_lvlhsh_find(&object->shared_hash, lhq); if (njs_fast_path(ret == NJS_OK)) { return lhq->value; } next: object = object->__proto__; } return NULL; } njs_inline njs_int_t njs_object_enumerate_value(njs_vm_t *vm, const njs_object_t *object, njs_array_t *items, uint32_t flags) { njs_int_t ret; njs_object_value_t *obj_val; if (flags & NJS_ENUM_STRING) { switch (object->type) { case NJS_ARRAY: ret = njs_object_enumerate_array(vm, (njs_array_t *) object, items, flags); break; case NJS_TYPED_ARRAY: ret = njs_object_enumerate_typed_array(vm, (njs_typed_array_t *) object, items, flags); break; case NJS_OBJECT_VALUE: obj_val = (njs_object_value_t *) object; if (njs_is_string(&obj_val->value)) { ret = njs_object_enumerate_string(vm, &obj_val->value, items, flags); break; } /* Fall through. */ default: goto object; } if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } object: ret = njs_object_enumerate_object(vm, object, items, flags); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } return NJS_OK; } njs_inline njs_int_t njs_object_own_enumerate_value(njs_vm_t *vm, const njs_object_t *object, const njs_object_t *parent, njs_array_t *items, uint32_t flags) { njs_int_t ret; njs_object_value_t *obj_val; if (flags & NJS_ENUM_STRING) { switch (object->type) { case NJS_ARRAY: ret = njs_object_enumerate_array(vm, (njs_array_t *) object, items, flags); break; case NJS_TYPED_ARRAY: ret = njs_object_enumerate_typed_array(vm, (njs_typed_array_t *) object, items, flags); break; case NJS_OBJECT_VALUE: obj_val = (njs_object_value_t *) object; if (njs_is_string(&obj_val->value)) { ret = njs_object_enumerate_string(vm, &obj_val->value, items, flags); break; } /* Fall through. */ default: goto object; } if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } object: ret = njs_object_own_enumerate_object(vm, object, parent, items, flags); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } return NJS_OK; } njs_array_t * njs_object_enumerate(njs_vm_t *vm, const njs_object_t *object, uint32_t flags) { njs_int_t ret; njs_array_t *items; items = njs_array_alloc(vm, 1, 0, NJS_ARRAY_SPARE); if (njs_slow_path(items == NULL)) { return NULL; } ret = njs_object_enumerate_value(vm, object, items, flags); if (njs_slow_path(ret != NJS_OK)) { return NULL; } return items; } njs_array_t * njs_object_own_enumerate(njs_vm_t *vm, const njs_object_t *object, uint32_t flags) { njs_int_t ret; njs_array_t *items; items = njs_array_alloc(vm, 1, 0, NJS_ARRAY_SPARE); if (njs_slow_path(items == NULL)) { return NULL; } ret = njs_object_own_enumerate_value(vm, object, object, items, flags); if (njs_slow_path(ret != NJS_OK)) { return NULL; } return items; } njs_inline njs_bool_t njs_is_enumerable(const njs_value_t *value, uint32_t flags) { return (njs_is_string(value) && (flags & NJS_ENUM_STRING)) || (njs_is_symbol(value) && (flags & NJS_ENUM_SYMBOL)); } static njs_int_t njs_object_enumerate_array(njs_vm_t *vm, const njs_array_t *array, njs_array_t *items, uint32_t flags) { njs_int_t ret; njs_value_t *p, *start, *end; njs_array_t *entry; if (!array->object.fast_array || array->length == 0) { return NJS_OK; } start = array->start; p = start; end = p + array->length; switch (njs_object_enum_kind(flags)) { case NJS_ENUM_KEYS: while (p < end) { if (njs_is_valid(p)) { ret = njs_array_expand(vm, items, 0, 1); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_uint32_to_string(&items->start[items->length++], p - start); } p++; } break; case NJS_ENUM_VALUES: while (p < end) { if (njs_is_valid(p)) { ret = njs_array_add(vm, items, p); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } p++; } break; case NJS_ENUM_BOTH: while (p < end) { if (njs_is_valid(p)) { entry = njs_array_alloc(vm, 0, 2, 0); if (njs_slow_path(entry == NULL)) { return NJS_ERROR; } njs_uint32_to_string(&entry->start[0], p - start); entry->start[1] = *p; ret = njs_array_expand(vm, items, 0, 1); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_set_array(&items->start[items->length++], entry); } p++; } break; } return NJS_OK; } static njs_int_t njs_object_enumerate_typed_array(njs_vm_t *vm, const njs_typed_array_t *array, njs_array_t *items, uint32_t flags) { uint32_t i, length; njs_int_t ret; njs_value_t *item; njs_array_t *entry; length = njs_typed_array_length(array); ret = njs_array_expand(vm, items, 0, length); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } item = &items->start[items->length]; switch (njs_object_enum_kind(flags)) { case NJS_ENUM_KEYS: for (i = 0; i < length; i++) { njs_uint32_to_string(item++, i); } break; case NJS_ENUM_VALUES: for (i = 0; i < length; i++) { njs_set_number(item++, njs_typed_array_prop(array, i)); } break; case NJS_ENUM_BOTH: for (i = 0; i < length; i++) { entry = njs_array_alloc(vm, 0, 2, 0); if (njs_slow_path(entry == NULL)) { return NJS_ERROR; } njs_uint32_to_string(&entry->start[0], i); njs_set_number(&entry->start[1], njs_typed_array_prop(array, i)); njs_set_array(item++, entry); } break; } items->length += length; return NJS_OK; } static njs_int_t njs_object_enumerate_string(njs_vm_t *vm, const njs_value_t *value, njs_array_t *items, uint32_t flags) { u_char *begin; uint32_t i, len, size; njs_int_t ret; njs_value_t *item, *string; njs_array_t *entry; const u_char *src, *end; njs_string_prop_t str_prop; len = (uint32_t) njs_string_prop(&str_prop, value); ret = njs_array_expand(vm, items, 0, len); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } item = &items->start[items->length]; switch (njs_object_enum_kind(flags)) { case NJS_ENUM_KEYS: for (i = 0; i < len; i++) { njs_uint32_to_string(item++, i); } break; case NJS_ENUM_VALUES: if (str_prop.size == (size_t) len) { /* ASCII string. */ for (i = 0; i < len; i++) { begin = njs_string_short_start(item); *begin = str_prop.start[i]; njs_string_short_set(item, 1, 1); item++; } } else { /* UTF-8 string. */ src = str_prop.start; end = src + str_prop.size; do { begin = (u_char *) src; njs_utf8_copy(njs_string_short_start(item), &src, end); size = (uint32_t) (src - begin); njs_string_short_set(item, size, 1); item++; } while (src != end); } break; case NJS_ENUM_BOTH: if (str_prop.size == (size_t) len) { /* ASCII string. */ for (i = 0; i < len; i++) { entry = njs_array_alloc(vm, 0, 2, 0); if (njs_slow_path(entry == NULL)) { return NJS_ERROR; } njs_uint32_to_string(&entry->start[0], i); string = &entry->start[1]; begin = njs_string_short_start(string); *begin = str_prop.start[i]; njs_string_short_set(string, 1, 1); njs_set_array(item, entry); item++; } } else { /* UTF-8 string. */ src = str_prop.start; end = src + str_prop.size; i = 0; do { entry = njs_array_alloc(vm, 0, 2, 0); if (njs_slow_path(entry == NULL)) { return NJS_ERROR; } njs_uint32_to_string(&entry->start[0], i++); string = &entry->start[1]; begin = (u_char *) src; njs_utf8_copy(njs_string_short_start(string), &src, end); size = (uint32_t) (src - begin); njs_string_short_set(string, size, 1); njs_set_array(item, entry); item++; } while (src != end); } break; } items->length += len; return NJS_OK; } static njs_int_t njs_object_enumerate_object(njs_vm_t *vm, const njs_object_t *object, njs_array_t *items, uint32_t flags) { njs_int_t ret; const njs_object_t *proto; ret = njs_object_own_enumerate_object(vm, object, object, items, flags); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } proto = object->__proto__; while (proto != NULL) { ret = njs_object_own_enumerate_value(vm, proto, object, items, flags); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } proto = proto->__proto__; } return NJS_OK; } #define njs_process_prop(vm, prop, flags, items, items_symbol) \ if (!(flags & NJS_ENUM_SYMBOL && njs_is_symbol(&prop->name))) { \ /* \ * prop from shared_hash is not symbol: \ * add to items before props from hash \ */ \ \ ret = njs_array_add(vm, items, &prop->name); \ if (njs_slow_path(ret != NJS_OK)) { \ return NJS_ERROR; \ } \ \ } else { \ /* \ * prop from shared_hash is symbol: \ * add to items_symbol \ */ \ ret = njs_array_add(vm, items_symbol, &prop->name); \ if (njs_slow_path(ret != NJS_OK)) { \ return NJS_ERROR; \ } \ } static njs_int_t njs_get_own_ordered_keys(njs_vm_t *vm, const njs_object_t *object, const njs_object_t *parent, njs_array_t *items, uint32_t flags) { double num; uint32_t items_length; njs_int_t ret; njs_array_t *items_string, *items_symbol; njs_lvlhsh_each_t lhe; njs_object_prop_t *prop, *ext_prop; njs_lvlhsh_query_t lhq; const njs_lvlhsh_t *hash; items_length = items->length; items_string = njs_array_alloc(vm, 1, 0, NJS_ARRAY_SPARE); if (njs_slow_path(items_string == NULL)) { return NJS_ERROR; } items_symbol = njs_array_alloc(vm, 1, 0, NJS_ARRAY_SPARE); if (njs_slow_path(items_symbol == NULL)) { return NJS_ERROR; } lhq.proto = &njs_object_hash_proto; njs_lvlhsh_each_init(&lhe, &njs_object_hash_proto); hash = &object->shared_hash; if (flags & NJS_ENUM_NON_SHARED_ONLY) { goto local_hash; } for ( ;; ) { prop = njs_lvlhsh_each(hash, &lhe); if (prop == NULL) { break; } if (!njs_is_enumerable(&prop->name, flags)) { continue; } njs_object_property_key_set(&lhq, &prop->name, lhe.key_hash); ext_prop = njs_object_exist_in_proto(parent, object, &lhq); if (ext_prop != NULL) { continue; } ret = njs_lvlhsh_find(&object->hash, &lhq); if (ret != NJS_OK) { if (!(prop->enumerable || !(flags & NJS_ENUM_ENUMERABLE_ONLY))) { continue; } /* prop is: !in_hash && in_shared_hash */ num = njs_string_to_index(&prop->name); if (!njs_number_is_integer_index(num)) { njs_process_prop(vm, prop, flags, items_string, items_symbol); } else { ret = njs_array_add(vm, items, &prop->name); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } } else { if (!(((njs_object_prop_t *)(lhq.value))->enumerable || !(flags & NJS_ENUM_ENUMERABLE_ONLY))) { continue; } /* prop is: in_hash && in_shared_hash */ num = njs_string_to_index(&prop->name); if (!njs_number_is_integer_index(num)) { njs_object_prop_t *hash_prop = lhq.value; /* select names of prop which are not deleted and * not deleted and created again i.e., * they are replaced shared hash props */ if (hash_prop->type != NJS_WHITEOUT && !(hash_prop->enum_in_object_hash)) { njs_process_prop(vm, prop, flags, items_string, items_symbol); } } } } local_hash: njs_lvlhsh_each_init(&lhe, &njs_object_hash_proto); hash = &object->hash; for ( ;; ) { prop = njs_lvlhsh_each(hash, &lhe); if (prop == NULL) { break; } if (!njs_is_enumerable(&prop->name, flags) || !(prop->enumerable || !(flags & NJS_ENUM_ENUMERABLE_ONLY)) || prop->type == NJS_WHITEOUT) { continue; } njs_object_property_key_set(&lhq, &prop->name, lhe.key_hash); ext_prop = njs_object_exist_in_proto(parent, object, &lhq); if (ext_prop != NULL) { continue; } num = njs_string_to_index(&prop->name); if (njs_number_is_integer_index(num)) { ret = njs_array_add(vm, items, &prop->name); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } else { ret = njs_lvlhsh_find(&object->shared_hash, &lhq); if (ret != NJS_OK) { /* prop is: in_hash && !in_shared_hash */ /* select names of not deleted props */ njs_process_prop(vm, prop, flags, items_string, items_symbol); } else { /* prop is: in_hash && in_shared_hash */ /* select names of not deleted and created again */ if (prop->enum_in_object_hash) { njs_process_prop(vm, prop, flags, items_string, items_symbol); } } } } if (items->length >= 2) { njs_qsort(&items->start[items_length], items->length-items_length, sizeof(njs_value_t), njs_array_indices_handler_nums, NULL); } if (items_string->length != 0) { ret = njs_array_expand(vm, items, 0, items_string->length); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } memcpy(&items->start[items->length], &items_string->start[0], items_string->length * sizeof(njs_value_t)); items->length += items_string->length; } if (items_symbol->length != 0) { ret = njs_array_expand(vm, items, 0, items_symbol->length); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } memcpy(&items->start[items->length], &items_symbol->start[0], items_symbol->length * sizeof(njs_value_t)); items->length += items_symbol->length; } njs_array_destroy(vm, items_string); njs_array_destroy(vm, items_symbol); return NJS_OK; } static njs_int_t njs_object_own_enumerate_object(njs_vm_t *vm, const njs_object_t *object, const njs_object_t *parent, njs_array_t *items, uint32_t flags) { uint32_t i; njs_int_t ret; njs_array_t *items_sorted, *entry; njs_value_t value, retval; switch (njs_object_enum_kind(flags)) { case NJS_ENUM_KEYS: ret = njs_get_own_ordered_keys(vm, object, parent, items, flags); if (ret != NJS_OK) { return NJS_ERROR; } break; case NJS_ENUM_VALUES: case NJS_ENUM_BOTH: items_sorted = njs_array_alloc(vm, 1, 0, NJS_ARRAY_SPARE); if (njs_slow_path(items_sorted == NULL)) { return NJS_ERROR; } ret = njs_get_own_ordered_keys(vm, object, parent, items_sorted, flags); if (ret != NJS_OK) { return NJS_ERROR; } njs_set_object(&value, (njs_object_t *) object); for (i = 0; i< items_sorted->length; i++) { ret = njs_value_property(vm, &value, &items_sorted->start[i], &retval); if (njs_slow_path(ret != NJS_OK)) { njs_array_destroy(vm, items_sorted); return NJS_ERROR; } if (njs_object_enum_kind(flags) != NJS_ENUM_VALUES) { entry = njs_array_alloc(vm, 0, 2, 0); if (njs_slow_path(entry == NULL)) { return NJS_ERROR; } njs_string_copy(&entry->start[0], &items_sorted->start[i]); njs_value_assign(&entry->start[1], &retval); njs_set_array(&retval, entry); } ret = njs_array_add(vm, items, &retval); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } njs_array_destroy(vm, items_sorted); break; } return NJS_OK; } njs_inline njs_int_t njs_traverse_visit(njs_arr_t *list, const njs_value_t *value) { njs_object_t **p; if (njs_is_object(value)) { p = njs_arr_add(list); if (njs_slow_path(p == NULL)) { return NJS_ERROR; } *p = njs_object(value); } return NJS_OK; } njs_inline njs_int_t njs_traverse_visited(njs_arr_t *list, const njs_value_t *value) { njs_uint_t items, n; njs_object_t **start, *obj; if (!njs_is_object(value)) { /* External. */ return 0; } start = list->start; items = list->items; obj = njs_object(value); for (n = 0; n < items; n++) { if (start[n] == obj) { return 1; } } return 0; } static njs_int_t njs_object_copy_shared_hash(njs_vm_t *vm, njs_object_t *object) { njs_int_t ret; njs_flathsh_t new_hash, *shared_hash; njs_object_prop_t *prop; njs_flathsh_each_t fhe; njs_flathsh_query_t fhq; fhq.replace = 0; fhq.proto = &njs_object_hash_proto; fhq.pool = vm->mem_pool; njs_flathsh_init(&new_hash); shared_hash = &object->shared_hash; njs_flathsh_each_init(&fhe, &njs_object_hash_proto); for ( ;; ) { prop = njs_flathsh_each(shared_hash, &fhe); if (prop == NULL) { break; } if (njs_is_symbol(&prop->name)) { fhq.key_hash = njs_symbol_key(&prop->name); fhq.key.start = NULL; } else { njs_string_get(&prop->name, &fhq.key); fhq.key_hash = njs_djb_hash(fhq.key.start, fhq.key.length); } fhq.value = prop; ret = njs_flathsh_insert(&new_hash, &fhq); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "flathsh insert failed"); return NJS_ERROR; } } object->shared_hash = new_hash; return NJS_OK; } njs_int_t njs_object_make_shared(njs_vm_t *vm, njs_object_t *object) { njs_int_t ret; njs_arr_t visited; njs_object_t **start; njs_value_t value, *key; njs_traverse_t *s; njs_object_prop_t *prop; njs_property_query_t pq; njs_traverse_t state[NJS_TRAVERSE_MAX_DEPTH]; s = &state[0]; s->parent = NULL; s->index = 0; njs_set_object(&s->value, object); s->keys = njs_value_own_enumerate(vm, &s->value, NJS_ENUM_KEYS | NJS_ENUM_STRING | NJS_ENUM_NON_SHARED_ONLY); if (njs_slow_path(s->keys == NULL)) { return NJS_ERROR; } if (s->keys->length != 0 && !njs_flathsh_is_empty(&object->shared_hash)) { /* * object->shared_hash can be shared with other objects * and we do not want to modify other objects. */ ret = njs_object_copy_shared_hash(vm, object); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } start = njs_arr_init(vm->mem_pool, &visited, NULL, 8, sizeof(void *)); if (njs_slow_path(start == NULL)) { return NJS_ERROR; } (void) njs_traverse_visit(&visited, &s->value); pq.lhq.replace = 0; pq.lhq.pool = vm->mem_pool; for ( ;; ) { if (s->index >= s->keys->length) { njs_flathsh_init(&njs_object(&s->value)->hash); njs_object(&s->value)->shared = 1; njs_array_destroy(vm, s->keys); s->keys = NULL; if (s == &state[0]) { goto done; } s--; continue; } njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0, 0); key = &s->keys->start[s->index++]; ret = njs_property_query(vm, &pq, &s->value, key); if (njs_slow_path(ret != NJS_OK)) { if (ret == NJS_DECLINED) { continue; } return NJS_ERROR; } prop = pq.lhq.value; ret = njs_flathsh_insert(&njs_object(&s->value)->shared_hash, &pq.lhq); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "flathsh insert failed"); return NJS_ERROR; } njs_value_assign(&value, njs_prop_value(prop)); if (njs_is_object(&value) && !njs_object(&value)->shared && !njs_traverse_visited(&visited, &value)) { ret = njs_traverse_visit(&visited, &value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } if (s == &state[NJS_TRAVERSE_MAX_DEPTH - 1]) { njs_type_error(vm, "njs_object_traverse() recursion limit:%d", NJS_TRAVERSE_MAX_DEPTH); return NJS_ERROR; } s++; s->prop = NULL; s->parent = &s[-1]; s->index = 0; njs_value_assign(&s->value, &value); s->keys = njs_value_own_enumerate(vm, &s->value, NJS_ENUM_KEYS | NJS_ENUM_STRING | NJS_ENUM_NON_SHARED_ONLY); if (njs_slow_path(s->keys == NULL)) { return NJS_ERROR; } if (s->keys->length != 0 && !njs_flathsh_is_empty(&njs_object(&s->value)->shared_hash)) { ret = njs_object_copy_shared_hash(vm, njs_object(&s->value)); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } } } done: njs_arr_destroy(&visited); return NJS_OK; } njs_int_t njs_object_traverse(njs_vm_t *vm, njs_object_t *object, void *ctx, njs_object_traverse_cb_t cb) { njs_int_t ret; njs_arr_t visited; njs_object_t **start; njs_value_t value, *key; njs_traverse_t *s; njs_object_prop_t *prop; njs_property_query_t pq; njs_traverse_t state[NJS_TRAVERSE_MAX_DEPTH]; s = &state[0]; s->prop = NULL; s->parent = NULL; s->index = 0; njs_set_object(&s->value, object); s->keys = njs_value_own_enumerate(vm, &s->value, NJS_ENUM_KEYS | NJS_ENUM_STRING | NJS_ENUM_SYMBOL); if (njs_slow_path(s->keys == NULL)) { return NJS_ERROR; } start = njs_arr_init(vm->mem_pool, &visited, NULL, 8, sizeof(void *)); if (njs_slow_path(start == NULL)) { return NJS_ERROR; } (void) njs_traverse_visit(&visited, &s->value); for ( ;; ) { if (s->index >= s->keys->length) { njs_array_destroy(vm, s->keys); s->keys = NULL; if (s == &state[0]) { goto done; } s--; continue; } njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0, 0); key = &s->keys->start[s->index++]; ret = njs_property_query(vm, &pq, &s->value, key); if (njs_slow_path(ret != NJS_OK)) { if (ret == NJS_DECLINED) { continue; } return NJS_ERROR; } prop = pq.lhq.value; s->prop = prop; ret = cb(vm, s, ctx); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_is_accessor_descriptor(prop)) { continue; } njs_value_assign(&value, njs_prop_value(prop)); if (prop->type == NJS_PROPERTY_HANDLER) { ret = njs_prop_handler(prop)(vm, prop, &s->value, NULL, &value); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } } if (njs_is_object(&value) && !njs_traverse_visited(&visited, &value)) { ret = njs_traverse_visit(&visited, &value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } if (s == &state[NJS_TRAVERSE_MAX_DEPTH - 1]) { njs_type_error(vm, "njs_object_traverse() recursion limit:%d", NJS_TRAVERSE_MAX_DEPTH); return NJS_ERROR; } s++; s->prop = NULL; s->parent = &s[-1]; s->index = 0; njs_value_assign(&s->value, &value); s->keys = njs_value_own_enumerate(vm, &s->value, NJS_ENUM_KEYS | NJS_ENUM_STRING | NJS_ENUM_SYMBOL); if (njs_slow_path(s->keys == NULL)) { return NJS_ERROR; } } } done: njs_arr_destroy(&visited); return NJS_OK; } static njs_int_t njs_object_define_property(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_value_t *value, *name, *desc, lvalue; if (!njs_is_object(njs_arg(args, nargs, 1))) { njs_type_error(vm, "Object.defineProperty is called on non-object"); return NJS_ERROR; } desc = njs_arg(args, nargs, 3); if (!njs_is_object(desc)) { njs_type_error(vm, "descriptor is not an object"); return NJS_ERROR; } value = njs_argument(args, 1); name = njs_lvalue_arg(&lvalue, args, nargs, 2); ret = njs_object_prop_define(vm, value, name, desc, NJS_OBJECT_PROP_DESCRIPTOR, 0); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_value_assign(retval, value); return NJS_OK; } static njs_int_t njs_object_define_properties(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { uint32_t i, length; njs_int_t ret; njs_array_t *keys; njs_value_t desc, *value, *descs; njs_object_prop_t *prop; njs_property_query_t pq; if (!njs_is_object(njs_arg(args, nargs, 1))) { njs_type_error(vm, "Object.defineProperties is called on non-object"); return NJS_ERROR; } descs = njs_arg(args, nargs, 2); ret = njs_value_to_object(vm, descs); if (njs_slow_path(ret != NJS_OK)) { return ret; } keys = njs_value_own_enumerate(vm, descs, NJS_ENUM_KEYS | NJS_ENUM_STRING | NJS_ENUM_SYMBOL); if (njs_slow_path(keys == NULL)) { return NJS_ERROR; } length = keys->length; value = njs_argument(args, 1); njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0, 0); for (i = 0; i < length; i++) { pq.lhq.key_hash = 0; ret = njs_property_query(vm, &pq, descs, &keys->start[i]); if (njs_slow_path(ret == NJS_ERROR)) { goto done; } prop = pq.lhq.value; if (ret == NJS_DECLINED || !prop->enumerable) { continue; } ret = njs_value_property(vm, descs, &keys->start[i], &desc); if (njs_slow_path(ret == NJS_ERROR)) { goto done; } ret = njs_object_prop_define(vm, value, &keys->start[i], &desc, NJS_OBJECT_PROP_DESCRIPTOR, 0); if (njs_slow_path(ret != NJS_OK)) { goto done; } } ret = NJS_OK; njs_value_assign(retval, value); done: njs_array_destroy(vm, keys); return ret; } static njs_int_t njs_object_get_own_property_descriptor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_value_t lvalue, *value, *property; value = njs_arg(args, nargs, 1); if (njs_is_null_or_undefined(value)) { njs_type_error(vm, "cannot convert %s argument to object", njs_type_string(value->type)); return NJS_ERROR; } property = njs_lvalue_arg(&lvalue, args, nargs, 2); return njs_object_prop_descriptor(vm, retval, value, property); } static njs_int_t njs_object_get_own_property_descriptors(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; uint32_t i, length; njs_array_t *names; njs_value_t descriptor, *value, *key; njs_object_t *descriptors; njs_object_prop_t *pr; njs_lvlhsh_query_t lhq; value = njs_arg(args, nargs, 1); if (njs_is_null_or_undefined(value)) { njs_type_error(vm, "cannot convert %s argument to object", njs_type_string(value->type)); return NJS_ERROR; } names = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS | NJS_ENUM_STRING | NJS_ENUM_SYMBOL); if (njs_slow_path(names == NULL)) { return NJS_ERROR; } length = names->length; descriptors = njs_object_alloc(vm); if (njs_slow_path(descriptors == NULL)) { ret = NJS_ERROR; goto done; } lhq.replace = 0; lhq.pool = vm->mem_pool; lhq.proto = &njs_object_hash_proto; for (i = 0; i < length; i++) { key = &names->start[i]; ret = njs_object_prop_descriptor(vm, &descriptor, value, key); if (njs_slow_path(ret != NJS_OK)) { ret = NJS_ERROR; goto done; } pr = njs_object_prop_alloc(vm, key, &descriptor, 1); if (njs_slow_path(pr == NULL)) { ret = NJS_ERROR; goto done; } njs_object_property_key_set(&lhq, key, 0); lhq.value = pr; ret = njs_lvlhsh_insert(&descriptors->hash, &lhq); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "lvlhsh insert failed"); goto done; } } ret = NJS_OK; njs_set_object(retval, descriptors); done: njs_array_destroy(vm, names); return ret; } static njs_int_t njs_object_get_own_property(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t type, njs_value_t *retval) { njs_array_t *names; njs_value_t *value; value = njs_arg(args, nargs, 1); if (njs_is_null_or_undefined(value)) { njs_type_error(vm, "cannot convert %s argument to object", njs_type_string(value->type)); return NJS_ERROR; } names = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS | type); if (names == NULL) { return NJS_ERROR; } njs_set_array(retval, names); return NJS_OK; } njs_int_t njs_object_get_prototype_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { uint32_t index; njs_value_t *value; value = njs_arg(args, nargs, 1); if (njs_is_object(value)) { njs_object_prototype_proto(vm, NULL, value, NULL, retval); return NJS_OK; } if (!njs_is_null_or_undefined(value)) { index = njs_primitive_prototype_index(value->type); if (njs_is_symbol(value)) { njs_set_object(retval, njs_vm_proto(vm, index)); } else { njs_set_object_value(retval, &vm->prototypes[index].object_value); } return NJS_OK; } njs_type_error(vm, "cannot convert %s argument to object", njs_type_string(value->type)); return NJS_ERROR; } static njs_int_t njs_object_set_prototype_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_value_t *value, *proto; value = njs_arg(args, nargs, 1); if (njs_slow_path(njs_is_null_or_undefined(value))) { njs_type_error(vm, "cannot convert %s argument to object", njs_type_string(value->type)); return NJS_ERROR; } proto = njs_arg(args, nargs, 2); if (njs_slow_path(!njs_is_object(proto) && !njs_is_null(proto))) { njs_type_error(vm, "prototype may only be an object or null: %s", njs_type_string(proto->type)); return NJS_ERROR; } if (njs_slow_path(!njs_is_object(value))) { njs_value_assign(retval, value); return NJS_OK; } ret = njs_object_set_prototype(vm, njs_object(value), proto); if (njs_fast_path(ret == NJS_OK)) { njs_value_assign(retval, value); return NJS_OK; } if (ret == NJS_DECLINED) { njs_type_error(vm, "Cannot set property \"prototype\", " "object is not extensible"); } else { njs_type_error(vm, "Cyclic __proto__ value"); } return NJS_ERROR; } /* 7.3.15 SetIntegrityLevel */ static njs_int_t njs_object_set_integrity_level(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t level, njs_value_t *retval) { uint32_t length; njs_int_t ret; njs_array_t *array; njs_value_t *value; njs_lvlhsh_t *hash; njs_object_t *object; njs_object_prop_t *prop; njs_lvlhsh_each_t lhe; value = njs_arg(args, nargs, 1); if (njs_slow_path(!njs_is_object(value))) { njs_value_assign(retval, value); return NJS_OK; } if (njs_slow_path(level == NJS_OBJECT_INTEGRITY_FROZEN && njs_is_typed_array(value) && njs_typed_array_length(njs_typed_array(value)) != 0)) { njs_type_error(vm, "Cannot freeze array buffer views with elements"); return NJS_ERROR; } if (njs_is_fast_array(value)) { array = njs_array(value); length = array->length; ret = njs_array_convert_to_slow_array(vm, array); if (ret != NJS_OK) { return ret; } ret = njs_array_length_redefine(vm, value, length, 1); if (njs_slow_path(ret != NJS_OK)) { return ret; } } object = njs_object(value); object->extensible = 0; njs_lvlhsh_each_init(&lhe, &njs_object_hash_proto); hash = &object->hash; for ( ;; ) { prop = njs_lvlhsh_each(hash, &lhe); if (prop == NULL) { break; } if (level == NJS_OBJECT_INTEGRITY_FROZEN && !njs_is_accessor_descriptor(prop)) { prop->writable = 0; } prop->configurable = 0; } njs_value_assign(retval, value); return NJS_OK; } static njs_int_t njs_object_test_integrity_level(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t level, njs_value_t *retval) { njs_value_t *value; njs_lvlhsh_t *hash; njs_object_t *object; njs_object_prop_t *prop; njs_lvlhsh_each_t lhe; value = njs_arg(args, nargs, 1); if (njs_slow_path(!njs_is_object(value))) { njs_set_boolean(retval, 1); return NJS_OK; } njs_set_boolean(retval, 0); object = njs_object(value); if (object->extensible) { goto done; } if (njs_slow_path(level == NJS_OBJECT_INTEGRITY_FROZEN) && njs_is_typed_array(value) && njs_typed_array_length(njs_typed_array(value)) != 0) { goto done; } njs_lvlhsh_each_init(&lhe, &njs_object_hash_proto); hash = &object->hash; for ( ;; ) { prop = njs_lvlhsh_each(hash, &lhe); if (prop == NULL) { break; } if (prop->configurable) { goto done; } if (level == NJS_OBJECT_INTEGRITY_FROZEN && njs_is_data_descriptor(prop) && prop->writable) { goto done; } } njs_set_boolean(retval, 1); done: return NJS_OK; } static njs_int_t njs_object_prevent_extensions(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_value_t *value; value = njs_arg(args, nargs, 1); if (!njs_is_object(value)) { njs_value_assign(retval, value); return NJS_OK; } njs_object(&args[1])->extensible = 0; njs_value_assign(retval, value); return NJS_OK; } static njs_int_t njs_object_is_extensible(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_value_t *value; value = njs_arg(args, nargs, 1); if (!njs_is_object(value)) { njs_set_boolean(retval, 0); return NJS_OK; } njs_set_boolean(retval, njs_object(value)->extensible); return NJS_OK; } static njs_int_t njs_object_assign(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { uint32_t i, j, length; njs_int_t ret; njs_array_t *names; njs_value_t *key, *source, *value, setval; njs_object_prop_t *prop; njs_property_query_t pq; value = njs_arg(args, nargs, 1); ret = njs_value_to_object(vm, value); if (njs_slow_path(ret != NJS_OK)) { return ret; } names = NULL; for (i = 2; i < nargs; i++) { source = &args[i]; names = njs_value_own_enumerate(vm, source, NJS_ENUM_KEYS | NJS_ENUM_STRING | NJS_ENUM_SYMBOL); if (njs_slow_path(names == NULL)) { return NJS_ERROR; } length = names->length; for (j = 0; j < length; j++) { key = &names->start[j]; njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0, 1); ret = njs_property_query(vm, &pq, source, key); if (njs_slow_path(ret != NJS_OK)) { goto exception; } prop = pq.lhq.value; if (!prop->enumerable) { continue; } ret = njs_value_property(vm, source, key, &setval); if (njs_slow_path(ret != NJS_OK)) { goto exception; } ret = njs_value_property_set(vm, value, key, &setval); if (njs_slow_path(ret != NJS_OK)) { goto exception; } } njs_array_destroy(vm, names); } njs_value_assign(retval, value); return NJS_OK; exception: njs_array_destroy(vm, names); return NJS_ERROR; } static njs_int_t njs_object_is(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_set_boolean(retval, njs_values_same(njs_arg(args, nargs, 1), njs_arg(args, nargs, 2))); return NJS_OK; } /* * The __proto__ property of booleans, numbers and strings primitives, * of objects created by Boolean(), Number(), and String() constructors, * and of Boolean.prototype, Number.prototype, and String.prototype objects. */ njs_int_t njs_primitive_prototype_get_proto(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { njs_uint_t index; njs_object_t *proto; /* * The __proto__ getters reside in object prototypes of primitive types * and have to return different results for primitive type and for objects. */ if (njs_is_object(value)) { proto = njs_object(value)->__proto__; } else { index = njs_primitive_prototype_index(value->type); proto = njs_vm_proto(vm, index); } if (proto != NULL) { njs_set_type_object(retval, proto, proto->type); } else { njs_set_undefined(retval); } return NJS_OK; } /* * 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_int_t njs_object_prototype_create(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { int64_t index; njs_function_t *function; const njs_value_t *proto; proto = NULL; function = njs_function(value); index = function - vm->constructors; if (index >= 0 && (size_t) index < vm->constructors_size) { proto = njs_property_prototype_create(vm, &function->object.hash, &vm->prototypes[index].object); } if (proto == NULL) { proto = &njs_value_undefined; } *retval = *proto; return NJS_OK; } njs_value_t * njs_property_prototype_create(njs_vm_t *vm, njs_lvlhsh_t *hash, njs_object_t *prototype) { njs_int_t ret; njs_object_prop_t *prop; njs_lvlhsh_query_t lhq; static const njs_value_t proto_string = njs_string("prototype"); prop = njs_object_prop_alloc(vm, &proto_string, &njs_value_undefined, 0); if (njs_slow_path(prop == NULL)) { return NULL; } njs_set_type_object(njs_prop_value(prop), prototype, prototype->type); lhq.value = prop; lhq.key_hash = NJS_PROTOTYPE_HASH; lhq.key = njs_str_value("prototype"); lhq.replace = 1; lhq.pool = vm->mem_pool; lhq.proto = &njs_object_hash_proto; ret = njs_lvlhsh_insert(hash, &lhq); if (njs_fast_path(ret == NJS_OK)) { return njs_prop_value(prop); } njs_internal_error(vm, "lvlhsh insert failed"); return NULL; } static const njs_object_prop_t njs_object_constructor_properties[] = { NJS_DECLARE_PROP_LENGTH(1), NJS_DECLARE_PROP_NAME("Object"), NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), NJS_DECLARE_PROP_NATIVE("create", njs_object_create, 2, 0), NJS_DECLARE_PROP_NATIVE("keys", njs_object_keys, 1, 0), NJS_DECLARE_PROP_NATIVE("values", njs_object_values, 1, 0), NJS_DECLARE_PROP_NATIVE("entries", njs_object_entries, 1, 0), NJS_DECLARE_PROP_NATIVE("defineProperty", njs_object_define_property, 3, 0), NJS_DECLARE_PROP_LNATIVE("defineProperties", njs_object_define_properties, 2, 0), NJS_DECLARE_PROP_LNATIVE("getOwnPropertyDescriptor", njs_object_get_own_property_descriptor, 2, 0), NJS_DECLARE_PROP_LNATIVE("getOwnPropertyDescriptors", njs_object_get_own_property_descriptors, 1, 0), NJS_DECLARE_PROP_LNATIVE("getOwnPropertyNames", njs_object_get_own_property, 1, NJS_ENUM_STRING), NJS_DECLARE_PROP_LNATIVE("getOwnPropertySymbols", njs_object_get_own_property, 1, NJS_ENUM_SYMBOL), NJS_DECLARE_PROP_NATIVE("getPrototypeOf", njs_object_get_prototype_of, 1, 0), NJS_DECLARE_PROP_NATIVE("setPrototypeOf", njs_object_set_prototype_of, 2, 0), NJS_DECLARE_PROP_NATIVE("freeze", njs_object_set_integrity_level, 1, NJS_OBJECT_INTEGRITY_FROZEN), NJS_DECLARE_PROP_NATIVE("isFrozen", njs_object_test_integrity_level, 1, NJS_OBJECT_INTEGRITY_FROZEN), NJS_DECLARE_PROP_NATIVE("seal", njs_object_set_integrity_level, 1, NJS_OBJECT_INTEGRITY_SEALED), NJS_DECLARE_PROP_NATIVE("isSealed", njs_object_test_integrity_level, 1, NJS_OBJECT_INTEGRITY_SEALED), NJS_DECLARE_PROP_LNATIVE("preventExtensions", njs_object_prevent_extensions, 1, 0), NJS_DECLARE_PROP_NATIVE("isExtensible", njs_object_is_extensible, 1, 0), NJS_DECLARE_PROP_NATIVE("assign", njs_object_assign, 2, 0), NJS_DECLARE_PROP_NATIVE("is", njs_object_is, 2, 0), }; const njs_object_init_t njs_object_constructor_init = { njs_object_constructor_properties, njs_nitems(njs_object_constructor_properties), }; static njs_int_t njs_object_set_prototype(njs_vm_t *vm, njs_object_t *object, const njs_value_t *value) { const njs_object_t *proto; proto = njs_object(value); if (njs_slow_path(object->__proto__ == proto)) { return NJS_OK; } if (!object->extensible) { return NJS_DECLINED; } if (njs_slow_path(proto == NULL)) { object->__proto__ = NULL; return NJS_OK; } do { if (proto == object) { return NJS_ERROR; } proto = proto->__proto__; } while (proto != NULL); object->__proto__ = njs_object(value); return NJS_OK; } njs_int_t njs_object_prototype_proto(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { njs_int_t ret; njs_object_t *proto, *object; if (!njs_is_object(value)) { *retval = *value; return NJS_OK; } object = njs_object(value); if (setval != NULL) { if (njs_is_object(setval) || njs_is_null(setval)) { ret = njs_object_set_prototype(vm, object, setval); if (njs_slow_path(ret == NJS_ERROR)) { njs_type_error(vm, "Cyclic __proto__ value"); return NJS_ERROR; } } njs_set_undefined(retval); return NJS_OK; } proto = object->__proto__; if (njs_fast_path(proto != NULL)) { njs_set_type_object(retval, proto, proto->type); } else { *retval = njs_value_null; } return NJS_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. */ njs_int_t njs_object_prototype_create_constructor(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { int64_t index; njs_value_t *cons, constructor; njs_object_t *object; njs_object_prototype_t *prototype; if (setval != NULL) { if (!njs_is_object(value)) { njs_type_error(vm, "Cannot create property \"constructor\" on %s", njs_type_string(value->type)); return NJS_ERROR; } cons = njs_property_constructor_set(vm, njs_object_hash(value), setval); if (njs_slow_path(cons == NULL)) { return NJS_ERROR; } *retval = *cons; return NJS_OK; } if (njs_is_object(value)) { object = njs_object(value); do { prototype = (njs_object_prototype_t *) object; index = prototype - vm->prototypes; if (index >= 0 && (size_t) index < vm->constructors_size) { goto found; } object = object->__proto__; } while (object != NULL); njs_internal_error(vm, "prototype not found"); return NJS_ERROR; } else { index = njs_primitive_prototype_index(value->type); prototype = &vm->prototypes[index]; } found: if (njs_flathsh_is_empty(&vm->constructors[index].object.shared_hash)) { index = NJS_OBJ_TYPE_OBJECT; } njs_set_function(&constructor, &njs_vm_ctor(vm, index)); setval = &constructor; cons = njs_property_constructor_set(vm, &prototype->object.hash, setval); if (njs_slow_path(cons == NULL)) { return NJS_ERROR; } *retval = *cons; return NJS_OK; } njs_value_t * njs_property_constructor_set(njs_vm_t *vm, njs_lvlhsh_t *hash, njs_value_t *constructor) { njs_int_t ret; njs_object_prop_t *prop; njs_lvlhsh_query_t lhq; static const njs_value_t constructor_string = njs_string("constructor"); prop = njs_object_prop_alloc(vm, &constructor_string, constructor, 1); if (njs_slow_path(prop == NULL)) { return NULL; } njs_value_assign(njs_prop_value(prop), constructor); prop->enumerable = 0; lhq.value = prop; lhq.key_hash = NJS_CONSTRUCTOR_HASH; lhq.key = njs_str_value("constructor"); lhq.replace = 1; lhq.pool = vm->mem_pool; lhq.proto = &njs_object_hash_proto; ret = njs_lvlhsh_insert(hash, &lhq); if (njs_fast_path(ret == NJS_OK)) { return njs_prop_value(prop); } njs_internal_error(vm, "lvlhsh insert/replace failed"); return NULL; } static njs_int_t njs_object_prototype_value_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_value_t *value; value = njs_argument(args, 0); if (!njs_is_object(value)) { if (njs_value_to_object(vm, value) != NJS_OK) { return NJS_ERROR; } } njs_value_assign(retval, value); return NJS_OK; } static const njs_value_t njs_object_null_string = njs_string("[object Null]"); static const njs_value_t njs_object_undefined_string = njs_long_string("[object Undefined]"); static const njs_value_t njs_object_boolean_string = njs_long_string("[object Boolean]"); static const njs_value_t njs_object_number_string = njs_long_string("[object Number]"); static const njs_value_t njs_object_string_string = njs_long_string("[object String]"); static const njs_value_t njs_object_object_string = njs_long_string("[object Object]"); static const njs_value_t njs_object_array_string = njs_string("[object Array]"); static const njs_value_t njs_object_function_string = njs_long_string("[object Function]"); static const njs_value_t njs_object_regexp_string = njs_long_string("[object RegExp]"); static const njs_value_t njs_object_date_string = njs_string("[object Date]"); static const njs_value_t njs_object_error_string = njs_string("[object Error]"); static const njs_value_t njs_object_arguments_string = njs_long_string("[object Arguments]"); njs_int_t njs_object_prototype_to_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { return njs_object_to_string(vm, &args[0], retval); } njs_int_t njs_object_to_string(njs_vm_t *vm, njs_value_t *this, njs_value_t *retval) { u_char *p; njs_int_t ret; njs_value_t tag; njs_string_prop_t string; const njs_value_t *name; if (njs_is_null_or_undefined(this)) { njs_value_assign(retval, njs_is_null(this) ? &njs_object_null_string : &njs_object_undefined_string); return NJS_OK; } ret = njs_value_to_object(vm, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } name = &njs_object_object_string; if (njs_is_array(this)) { name = &njs_object_array_string; } else if (njs_is_object(this) && njs_lvlhsh_eq(&njs_object(this)->shared_hash, &vm->shared->arguments_object_instance_hash)) { name = &njs_object_arguments_string; } else if (njs_is_function(this)) { name = &njs_object_function_string; } else if (njs_is_error(this)) { name = &njs_object_error_string; } else if (njs_is_object_value(this)) { switch (njs_object_value(this)->type) { case NJS_BOOLEAN: name = &njs_object_boolean_string; break; case NJS_NUMBER: name = &njs_object_number_string; break; case NJS_STRING: name = &njs_object_string_string; break; default: break; } } else if (njs_is_date(this)) { name = &njs_object_date_string; } else if (njs_is_regexp(this)) { name = &njs_object_regexp_string; } ret = njs_object_string_tag(vm, this, &tag); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (ret == NJS_DECLINED) { if (njs_slow_path(name == NULL)) { njs_internal_error(vm, "Unknown value type"); return NJS_ERROR; } njs_value_assign(retval, name); return NJS_OK; } (void) njs_string_prop(&string, &tag); p = njs_string_alloc(vm, retval, string.size + njs_length("[object ]"), string.length + njs_length("[object ]")); if (njs_slow_path(p == NULL)) { return NJS_ERROR; } p = njs_cpymem(p, "[object ", 8); p = njs_cpymem(p, string.start, string.size); *p = ']'; return NJS_OK; } static njs_int_t njs_object_prototype_has_own_property(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_value_t *value, *property, lvalue; njs_property_query_t pq; value = njs_argument(args, 0); if (njs_is_null_or_undefined(value)) { njs_type_error(vm, "cannot convert %s argument to object", njs_type_string(value->type)); return NJS_ERROR; } property = njs_lvalue_arg(&lvalue, args, nargs, 1); if (njs_slow_path(!njs_is_key(property))) { ret = njs_value_to_key(vm, property, property); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0, 1); ret = njs_property_query(vm, &pq, value, property); switch (ret) { case NJS_OK: njs_set_boolean(retval, 1); return NJS_OK; case NJS_DECLINED: njs_set_boolean(retval, 0); return NJS_OK; case NJS_ERROR: default: return NJS_ERROR; } } static njs_int_t njs_object_prototype_prop_is_enumerable(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_value_t *value, *property, lvalue; njs_object_prop_t *prop; njs_property_query_t pq; value = njs_argument(args, 0); if (njs_is_null_or_undefined(value)) { njs_type_error(vm, "cannot convert %s argument to object", njs_type_string(value->type)); return NJS_ERROR; } property = njs_lvalue_arg(&lvalue, args, nargs, 1); if (njs_slow_path(!njs_is_key(property))) { ret = njs_value_to_key(vm, property, property); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0, 1); ret = njs_property_query(vm, &pq, value, property); switch (ret) { case NJS_OK: prop = pq.lhq.value; njs_set_boolean(retval, prop->enumerable); break; case NJS_DECLINED: njs_set_boolean(retval, 0); break; case NJS_ERROR: default: return NJS_ERROR; } return NJS_OK; } static njs_int_t njs_object_prototype_is_prototype_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_value_t *prototype, *value; njs_object_t *object, *proto; if (njs_slow_path(njs_is_null_or_undefined(njs_argument(args, 0)))) { njs_type_error(vm, "cannot convert undefined to object"); return NJS_ERROR; } prototype = &args[0]; value = njs_arg(args, nargs, 1); if (njs_is_object(prototype) && njs_is_object(value)) { proto = njs_object(prototype); object = njs_object(value); do { object = object->__proto__; if (object == proto) { njs_set_boolean(retval, 1); return NJS_OK; } } while (object != NULL); } njs_set_boolean(retval, 0); return NJS_OK; } static const njs_object_prop_t njs_object_prototype_properties[] = { NJS_DECLARE_PROP_HANDLER("__proto__", njs_object_prototype_proto, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("constructor", njs_object_prototype_create_constructor, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_NATIVE("valueOf", njs_object_prototype_value_of, 0, 0), NJS_DECLARE_PROP_NATIVE("toString", njs_object_prototype_to_string, 0, 0), NJS_DECLARE_PROP_NATIVE("hasOwnProperty", njs_object_prototype_has_own_property, 1, 0), NJS_DECLARE_PROP_LNATIVE("propertyIsEnumerable", njs_object_prototype_prop_is_enumerable, 1, 0), NJS_DECLARE_PROP_NATIVE("isPrototypeOf", njs_object_prototype_is_prototype_of, 1, 0), }; const njs_object_init_t njs_object_prototype_init = { njs_object_prototype_properties, njs_nitems(njs_object_prototype_properties), }; njs_int_t njs_object_length(njs_vm_t *vm, njs_value_t *value, int64_t *length) { njs_int_t ret; njs_value_t value_length; const njs_value_t string_length = njs_string("length"); if (njs_is_fast_array(value)) { *length = njs_array(value)->length; return NJS_OK; } ret = njs_value_property(vm, value, njs_value_arg(&string_length), &value_length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } return njs_value_to_length(vm, &value_length, length); } const njs_object_type_init_t njs_obj_type_init = { .constructor = njs_native_ctor(njs_object_constructor, 1, 0), .constructor_props = &njs_object_constructor_init, .prototype_props = &njs_object_prototype_init, .prototype_value = { .object = { .type = NJS_OBJECT } }, }; njs-0.8.9/src/njs_object.h000066400000000000000000000240521474132077100154130ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_OBJECT_H_INCLUDED_ #define _NJS_OBJECT_H_INCLUDED_ typedef enum { NJS_OBJECT_PROP_DESCRIPTOR = 0, NJS_OBJECT_PROP_VALUE = 1, NJS_OBJECT_PROP_GETTER = 2, NJS_OBJECT_PROP_SETTER = 3, #define njs_prop_type(flags) (flags & 3) NJS_OBJECT_PROP_CREATE = 4, NJS_OBJECT_PROP_ENUMERABLE = 8, NJS_OBJECT_PROP_CONFIGURABLE = 16, NJS_OBJECT_PROP_WRITABLE = 32, NJS_OBJECT_PROP_UNSET = 64, #define NJS_OBJECT_PROP_VALUE_ECW (NJS_OBJECT_PROP_VALUE \ | NJS_OBJECT_PROP_ENUMERABLE \ | NJS_OBJECT_PROP_CONFIGURABLE \ | NJS_OBJECT_PROP_WRITABLE) #define NJS_OBJECT_PROP_VALUE_EC (NJS_OBJECT_PROP_VALUE \ | NJS_OBJECT_PROP_ENUMERABLE \ | NJS_OBJECT_PROP_CONFIGURABLE) #define NJS_OBJECT_PROP_VALUE_CW (NJS_OBJECT_PROP_VALUE \ | NJS_OBJECT_PROP_CONFIGURABLE \ | NJS_OBJECT_PROP_WRITABLE) #define NJS_OBJECT_PROP_VALUE_E (NJS_OBJECT_PROP_VALUE \ | NJS_OBJECT_PROP_ENUMERABLE) #define NJS_OBJECT_PROP_VALUE_C (NJS_OBJECT_PROP_VALUE \ | NJS_OBJECT_PROP_CONFIGURABLE) #define NJS_OBJECT_PROP_VALUE_W (NJS_OBJECT_PROP_VALUE \ | NJS_OBJECT_PROP_WRITABLE) } njs_object_prop_flags_t; struct njs_object_init_s { const njs_object_prop_t *properties; njs_uint_t items; }; typedef struct njs_traverse_s njs_traverse_t; struct njs_traverse_s { struct njs_traverse_s *parent; njs_object_prop_t *prop; njs_value_t value; njs_array_t *keys; int64_t index; #define NJS_TRAVERSE_MAX_DEPTH 32 }; typedef njs_int_t (*njs_object_traverse_cb_t)(njs_vm_t *vm, njs_traverse_t *traverse, void *ctx); njs_object_t *njs_object_alloc(njs_vm_t *vm); njs_object_t *njs_object_value_copy(njs_vm_t *vm, njs_value_t *value); njs_object_value_t *njs_object_value_alloc(njs_vm_t *vm, njs_uint_t index, size_t extra,const njs_value_t *value); njs_array_t *njs_object_enumerate(njs_vm_t *vm, const njs_object_t *object, uint32_t flags); njs_array_t *njs_object_own_enumerate(njs_vm_t *vm, const njs_object_t *object, uint32_t flags); njs_int_t njs_object_traverse(njs_vm_t *vm, njs_object_t *object, void *ctx, njs_object_traverse_cb_t cb); njs_int_t njs_object_make_shared(njs_vm_t *vm, njs_object_t *object); njs_int_t njs_object_hash_create(njs_vm_t *vm, njs_lvlhsh_t *hash, const njs_object_prop_t *prop, njs_uint_t n); njs_int_t njs_primitive_prototype_get_proto(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); njs_int_t njs_object_prototype_create(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); njs_value_t *njs_property_prototype_create(njs_vm_t *vm, njs_lvlhsh_t *hash, njs_object_t *prototype); njs_int_t njs_object_prototype_proto(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); njs_int_t njs_object_prototype_create_constructor(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); njs_value_t *njs_property_constructor_set(njs_vm_t *vm, njs_lvlhsh_t *hash, njs_value_t *constructor); njs_int_t njs_object_to_string(njs_vm_t *vm, njs_value_t *value, njs_value_t *retval); njs_int_t njs_object_prototype_to_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); njs_int_t njs_object_length(njs_vm_t *vm, njs_value_t *value, int64_t *dst); njs_int_t njs_prop_private_copy(njs_vm_t *vm, njs_property_query_t *pq, njs_object_t *proto); njs_object_prop_t *njs_object_prop_alloc(njs_vm_t *vm, const njs_value_t *name, const njs_value_t *value, uint8_t attributes); njs_int_t njs_object_property(njs_vm_t *vm, njs_object_t *object, njs_lvlhsh_query_t *lhq, njs_value_t *retval); njs_object_prop_t *njs_object_property_add(njs_vm_t *vm, njs_value_t *object, njs_value_t *key, njs_bool_t replace); njs_int_t njs_object_prop_define(njs_vm_t *vm, njs_value_t *object, njs_value_t *name, njs_value_t *value, unsigned flags, uint32_t hash); njs_int_t njs_object_prop_descriptor(njs_vm_t *vm, njs_value_t *dest, njs_value_t *value, njs_value_t *setval); njs_int_t njs_object_get_prototype_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); const char *njs_prop_type_string(njs_object_prop_type_t type); njs_int_t njs_object_prop_init(njs_vm_t *vm, const njs_object_init_t* init, const njs_object_prop_t *base, njs_value_t *value, njs_value_t *retval); njs_inline njs_bool_t njs_is_data_descriptor(njs_object_prop_t *prop) { return prop->writable != NJS_ATTRIBUTE_UNSET || (prop->type != NJS_ACCESSOR && njs_is_valid(njs_prop_value(prop))) || prop->type == NJS_PROPERTY_HANDLER; } njs_inline njs_bool_t njs_is_accessor_descriptor(njs_object_prop_t *prop) { return prop->type == NJS_ACCESSOR; } njs_inline njs_bool_t njs_is_generic_descriptor(njs_object_prop_t *prop) { return !njs_is_data_descriptor(prop) && !njs_is_accessor_descriptor(prop); } njs_inline void njs_object_property_key_set(njs_lvlhsh_query_t *lhq, const njs_value_t *key, uint32_t hash) { if (njs_is_symbol(key)) { lhq->key.length = 0; lhq->key.start = NULL; lhq->key_hash = njs_symbol_key(key); } else { /* string. */ njs_string_get(key, &lhq->key); if (hash == 0) { lhq->key_hash = njs_djb_hash(lhq->key.start, lhq->key.length); } else { lhq->key_hash = hash; } } } njs_inline void njs_object_property_init(njs_lvlhsh_query_t *lhq, const njs_value_t *key, uint32_t hash) { lhq->proto = &njs_object_hash_proto; njs_object_property_key_set(lhq, key, hash); } njs_inline njs_int_t njs_primitive_value_to_key(njs_vm_t *vm, njs_value_t *dst, const njs_value_t *src) { const njs_value_t *value; switch (src->type) { case NJS_NULL: value = &njs_string_null; break; case NJS_UNDEFINED: value = &njs_string_undefined; 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_SYMBOL: case NJS_STRING: value = src; break; default: return NJS_ERROR; } *dst = *value; return NJS_OK; } njs_inline njs_int_t njs_value_to_key2(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value, njs_bool_t convert) { njs_int_t ret; njs_value_t primitive; if (njs_slow_path(!njs_is_primitive(value))) { if (njs_slow_path(njs_is_object_symbol(value))) { /* should fail */ value = njs_object_value(value); } else { if (convert) { ret = njs_value_to_primitive(vm, &primitive, value, 1); } else { ret = njs_object_to_string(vm, value, &primitive); } if (njs_slow_path(ret != NJS_OK)) { return ret; } value = &primitive; } } return njs_primitive_value_to_key(vm, dst, value); } njs_inline njs_int_t njs_value_to_key(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value) { return njs_value_to_key2(vm, dst, value, 1); } njs_inline njs_int_t njs_key_string_get(njs_vm_t *vm, njs_value_t *key, njs_str_t *str) { njs_int_t ret; if (njs_slow_path(njs_is_symbol(key))) { ret = njs_symbol_descriptive_string(vm, key, key); if (njs_slow_path(ret != NJS_OK)) { return ret; } } njs_string_get(key, str); return NJS_OK; } njs_inline njs_int_t njs_value_create_data_prop(njs_vm_t *vm, njs_value_t *value, njs_value_t *name, njs_value_t *setval, uint32_t hash) { return njs_object_prop_define(vm, value, name, setval, NJS_OBJECT_PROP_CREATE | NJS_OBJECT_PROP_VALUE_ECW, hash); } njs_inline njs_int_t njs_value_create_data_prop_i64(njs_vm_t *vm, njs_value_t *value, int64_t index, njs_value_t *setval, uint32_t hash) { njs_value_t key; njs_set_number(&key, index); return njs_value_create_data_prop(vm, value, &key, setval, hash); } njs_inline njs_int_t njs_object_length_set(njs_vm_t *vm, njs_value_t *value, int64_t length) { njs_value_t index; static const njs_value_t string_length = njs_string("length"); njs_value_number_set(&index, length); return njs_value_property_set(vm, value, njs_value_arg(&string_length), &index); } njs_inline njs_int_t njs_object_string_tag(njs_vm_t *vm, njs_value_t *value, njs_value_t *tag) { njs_int_t ret; static const njs_value_t to_string_tag = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG); ret = njs_value_property(vm, value, njs_value_arg(&to_string_tag), tag); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (!njs_is_string(tag)) { return NJS_DECLINED; } return NJS_OK; } njs_inline njs_object_t * _njs_object_proto_lookup(njs_object_t *proto, njs_value_type_t type) { do { if (njs_fast_path(proto->type == type)) { break; } proto = proto->__proto__; } while (proto != NULL); return proto; } #define njs_object_proto_lookup(proto, vtype, ctype) \ (ctype *) _njs_object_proto_lookup(proto, vtype) extern const njs_object_type_init_t njs_obj_type_init; #endif /* _NJS_OBJECT_H_INCLUDED_ */ njs-0.8.9/src/njs_object_hash.h000066400000000000000000001477401474132077100164300ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_OBJECT_HASH_H_INCLUDED_ #define _NJS_OBJECT_HASH_H_INCLUDED_ #define NJS___PROTO___HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ '_'), '_'), 'p'), 'r'), 'o'), 't'), 'o'), '_'), '_') #define NJS_ARRAY_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'A'), 'r'), 'r'), 'a'), 'y') #define NJS_ARGV_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'a'), 'r'), 'g'), 'v') #define NJS_BOOLEAN_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'B'), 'o'), 'o'), 'l'), 'e'), 'a'), 'n') #define NJS_CONFIGURABLE_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'c'), 'o'), 'n'), 'f'), 'i'), 'g'), 'u'), 'r'), 'a'), 'b'), 'l'), 'e') #define NJS_CONSTRUCTOR_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'c'), 'o'), 'n'), 's'), 't'), 'r'), 'u'), 'c'), 't'), 'o'), 'r') #define NJS_DATE_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'D'), 'a'), 't'), 'e') #define NJS_PROMISE_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'P'), 'r'), 'o'), 'm'), 'i'), 's'), 'e') #define NJS_ENUMERABLE_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'e'), 'n'), 'u'), 'm'), 'e'), 'r'), 'a'), 'b'), 'l'), 'e') #define NJS_ERRNO_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'e'), 'r'), 'r'), 'n'), 'o') #define NJS_ERROR_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'E'), 'r'), 'r'), 'o'), 'r') #define NJS_ENCODING_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'e'), 'n'), 'c'), 'o'), 'd'), 'i'), 'n'), 'g') #define NJS_ENV_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'e'), 'n'), 'v') #define NJS_EVAL_ERROR_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'E'), 'v'), 'a'), 'l'), 'E'), 'r'), 'r'), 'o'), 'r') #define NJS_FLAG_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'f'), 'l'), 'a'), 'g') #define NJS_GET_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'g'), 'e'), 't') #define NJS_GLOBAL_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'g'), 'l'), 'o'), 'b'), 'a'), 'l') #define NJS_GLOBAL_THIS_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'g'), 'l'), 'o'), 'b'), 'a'), 'l'), 'T'), 'h'), 'i'), 's') #define NJS_FUNCTION_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'F'), 'u'), 'n'), 'c'), 't'), 'i'), 'o'), 'n') #define NJS_INDEX_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'i'), 'n'), 'd'), 'e'), 'x') #define NJS_INPUT_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'i'), 'n'), 'p'), 'u'), 't') #define NJS_INTERNAL_ERROR_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'I'), 'n'), 't'), 'e'), 'r'), 'n'), 'a'), 'l'), \ 'E'), 'r'), 'r'), 'o'), 'r') #define NJS_GROUPS_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'g'), 'r'), 'o'), 'u'), 'p'), 's') #define NJS_JOIN_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'j'), 'o'), 'i'), 'n') #define NJS_JSON_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'J'), 'S'), 'O'), 'N') #define NJS_LENGTH_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'l'), 'e'), 'n'), 'g'), 't'), 'h') #define NJS_NAME_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'n'), 'a'), 'm'), 'e') #define NJS_NJS_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'n'), 'j'), 's') #define NJS_262_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ '$'), '2'), '6'), '2') #define NJS_NUMBER_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'N'), 'u'), 'm'), 'b'), 'e'), 'r') #define NJS_MATH_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'M'), 'a'), 't'), 'h') #define NJS_MEMORY_ERROR_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'M'), 'e'), 'm'), 'o'), 'r'), 'y'), \ 'E'), 'r'), 'r'), 'o'), 'r') #define NJS_AGGREGATE_ERROR_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'A'), 'g'), 'g'), 'r'), 'e'), 'g'), 'a'), 't'), 'e'), \ 'E'), 'r'), 'r'), 'o'), 'r') #define NJS_MESSAGE_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'm'), 'e'), 's'), 's'), 'a'), 'g'), 'e') #define NJS_ERRORS_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'e'), 'r'), 'r'), 'o'), 'r'), 's') #define NJS_MODE_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'm'), 'o'), 'd'), 'e') #define NJS_OBJECT_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'O'), 'b'), 'j'), 'e'), 'c'), 't') #define NJS_PATH_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'p'), 'a'), 't'), 'h') #define NJS_PROCESS_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'p'), 'r'), 'o'), 'c'), 'e'), 's'), 's') #define NJS_PROTOTYPE_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'p'), 'r'), 'o'), 't'), 'o'), 't'), 'y'), 'p'), 'e') #define NJS_RANGE_ERROR_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'R'), 'a'), 'n'), 'g'), 'e'), 'E'), 'r'), 'r'), 'o'), 'r') #define NJS_REF_ERROR_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'R'), 'e'), 'f'), 'e'), 'r'), 'e'), 'n'), 'c'), 'e'), \ 'E'), 'r'), 'r'), 'o'), 'r') #define NJS_REGEXP_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'R'), 'e'), 'g'), 'E'), 'x'), 'p') #define NJS_SET_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 's'), 'e'), 't') #define NJS_STACK_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 's'), 't'), 'a'), 'c'), 'k') #define NJS_STRING_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'S'), 't'), 'r'), 'i'), 'n'), 'g') #define NJS_SYMBOL_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'S'), 'y'), 'm'), 'b'), 'o'), 'l') #define NJS_SYNTAX_ERROR_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'S'), 'y'), 'n'), 't'), 'a'), 'x'), \ 'E'), 'r'), 'r'), 'o'), 'r') #define NJS_SYSCALL_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 's'), 'y'), 's'), 'c'), 'a'), 'l'), 'l') #define NJS_TO_JSON_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 't'), 'o'), 'J'), 'S'), 'O'), 'N') #define NJS_TO_STRING_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 't'), 'o'), 'S'), 't'), 'r'), 'i'), 'n'), 'g') #define NJS_TO_ISO_STRING_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 't'), 'o'), 'I'), 'S'), 'O'), 'S'), 't'), 'r'), 'i'), 'n'), 'g') #define NJS_TYPE_ERROR_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'T'), 'y'), 'p'), 'e'), 'E'), 'r'), 'r'), 'o'), 'r') #define NJS_VALUE_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'v'), 'a'), 'l'), 'u'), 'e') #define NJS_VALUE_OF_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'v'), 'a'), 'l'), 'u'), 'e'), 'O'), 'f') #define NJS_WRITABABLE_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'w'), 'r'), 'i'), 't'), 'a'), 'b'), 'l'), 'e') #define NJS_URI_ERROR_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'U'), 'R'), 'I'), 'E'), 'r'), 'r'), 'o'), 'r') #define NJS_ARRAY_BUFFER_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'A'), 'r'), 'r'), 'a'), 'y'), 'B'), 'u'), 'f'), 'f'), 'e'), 'r') #define NJS_DATA_VIEW_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'D'), 'a'), 't'), 'a'), 'V'), 'i'), 'e'), 'w') #define NJS_UINT8ARRAY_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'U'), 'i'), 'n'), 't'), '8'), 'A'), 'r'), 'r'), 'a'), 'y') #define NJS_UINT16ARRAY_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'U'), 'i'), 'n'), 't'), '1'), '6'), 'A'), 'r'), 'r'), 'a'), 'y') #define NJS_UINT32ARRAY_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'U'), 'i'), 'n'), 't'), '3'), '2'), 'A'), 'r'), 'r'), 'a'), 'y') #define NJS_INT8ARRAY_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'I'), 'n'), 't'), '8'), 'A'), 'r'), 'r'), 'a'), 'y') #define NJS_INT16ARRAY_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'I'), 'n'), 't'), '1'), '6'), 'A'), 'r'), 'r'), 'a'), 'y') #define NJS_INT32ARRAY_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'I'), 'n'), 't'), '3'), '2'), 'A'), 'r'), 'r'), 'a'), 'y') #define NJS_FLOAT32ARRAY_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'F'), 'l'), 'o'), 'a'), 't'), '3'), '2'), 'A'), 'r'), 'r'), 'a'), 'y') #define NJS_FLOAT64ARRAY_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'F'), 'l'), 'o'), 'a'), 't'), '6'), '4'), 'A'), 'r'), 'r'), 'a'), 'y') #define NJS_UINT8CLAMPEDARRAY_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'U'), 'i'), 'n'), 't'), '8'), 'C'), 'l'), 'a'), 'm'), 'p'), 'e'), \ 'd'), 'A'), 'r'), 'r'), 'a'), 'y') #define NJS_TEXT_DECODER_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'T'), 'e'), 'x'), 't'), 'D'), 'e'), 'c'), 'o'), 'd'), 'e'), 'r') #define NJS_TEXT_ENCODER_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'T'), 'e'), 'x'), 't'), 'E'), 'n'), 'c'), 'o'), 'd'), 'e'), 'r') #define NJS_BUFFER_HASH \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add( \ njs_djb_hash_add(NJS_DJB_HASH_INIT, \ 'B'), 'u'), 'f'), 'f'), 'e'), 'r') #endif /* _NJS_OBJECT_HASH_H_INCLUDED_ */ njs-0.8.9/src/njs_object_prop.c000066400000000000000000000740761474132077100164610ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include static njs_object_prop_t *njs_object_prop_alloc2(njs_vm_t *vm, const njs_value_t *name, njs_object_prop_type_t type, unsigned flags); static njs_object_prop_t *njs_descriptor_prop(njs_vm_t *vm, const njs_value_t *name, const njs_value_t *desc); njs_object_prop_t * njs_object_prop_alloc(njs_vm_t *vm, const njs_value_t *name, const njs_value_t *value, uint8_t attributes) { unsigned flags; njs_object_prop_t *prop; switch (attributes) { case NJS_ATTRIBUTE_FALSE: case NJS_ATTRIBUTE_TRUE: flags = attributes ? NJS_OBJECT_PROP_VALUE_ECW : 0; break; case NJS_ATTRIBUTE_UNSET: default: flags = NJS_OBJECT_PROP_UNSET; break; } prop = njs_object_prop_alloc2(vm, name, NJS_PROPERTY, flags); if (njs_slow_path(prop == NULL)) { return NULL; } njs_value_assign(njs_prop_value(prop), value); return prop; } static njs_object_prop_t * njs_object_prop_alloc2(njs_vm_t *vm, const njs_value_t *name, njs_object_prop_type_t type, unsigned flags) { njs_int_t ret; njs_object_prop_t *prop; prop = njs_mp_align(vm->mem_pool, sizeof(njs_value_t), sizeof(njs_object_prop_t)); if (njs_slow_path(prop == NULL)) { njs_memory_error(vm); return NULL; } njs_value_assign(&prop->name, name); if (njs_slow_path(!njs_is_key(&prop->name))) { ret = njs_value_to_key(vm, &prop->name, &prop->name); if (njs_slow_path(ret != NJS_OK)) { return NULL; } } prop->type = type; prop->enum_in_object_hash = 0; if (flags != NJS_OBJECT_PROP_UNSET) { prop->enumerable = !!(flags & NJS_OBJECT_PROP_ENUMERABLE); prop->configurable = !!(flags & NJS_OBJECT_PROP_CONFIGURABLE); if (type == NJS_PROPERTY) { prop->writable = !!(flags & NJS_OBJECT_PROP_WRITABLE); } else { prop->writable = NJS_ATTRIBUTE_UNSET; } } else { prop->enumerable = NJS_ATTRIBUTE_UNSET; prop->configurable = NJS_ATTRIBUTE_UNSET; prop->writable = NJS_ATTRIBUTE_UNSET; } return prop; } njs_int_t njs_object_property(njs_vm_t *vm, njs_object_t *object, njs_lvlhsh_query_t *lhq, njs_value_t *retval) { njs_int_t ret; njs_value_t value; njs_object_prop_t *prop; do { ret = njs_lvlhsh_find(&object->hash, lhq); if (njs_fast_path(ret == NJS_OK)) { prop = lhq->value; if (prop->type != NJS_WHITEOUT) { goto found; } } ret = njs_lvlhsh_find(&object->shared_hash, lhq); if (njs_fast_path(ret == NJS_OK)) { goto found; } object = object->__proto__; } while (object != NULL); njs_set_undefined(retval); return NJS_DECLINED; found: prop = lhq->value; if (njs_is_data_descriptor(prop)) { njs_value_assign(retval, njs_prop_value(prop)); return NJS_OK; } if (njs_prop_getter(prop) == NULL) { njs_set_undefined(retval); return NJS_OK; } njs_set_object(&value, object); return njs_function_apply(vm, njs_prop_getter(prop), &value, 1, retval); } njs_object_prop_t * njs_object_property_add(njs_vm_t *vm, njs_value_t *object, njs_value_t *key, njs_bool_t replace) { njs_int_t ret; njs_value_t key_value; njs_object_prop_t *prop; njs_lvlhsh_query_t lhq; prop = njs_object_prop_alloc(vm, key, &njs_value_invalid, 1); if (njs_slow_path(prop == NULL)) { return NULL; } ret = njs_value_to_key(vm, &key_value, key); if (njs_slow_path(ret != NJS_OK)) { return NULL; } lhq.proto = &njs_object_hash_proto; njs_string_get(&key_value, &lhq.key); lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length); lhq.value = prop; lhq.replace = replace; lhq.pool = vm->mem_pool; ret = njs_lvlhsh_insert(njs_object_hash(object), &lhq); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "lvlhsh insert failed"); return NULL; } return prop; } /* * ES5.1, 8.12.9: [[DefineOwnProperty]] */ njs_int_t njs_object_prop_define(njs_vm_t *vm, njs_value_t *object, njs_value_t *name, njs_value_t *value, unsigned flags, uint32_t hash) { uint32_t length, index; njs_int_t ret; njs_array_t *array; njs_value_t retval; njs_object_prop_t *prop, *prev; njs_property_query_t pq; static const njs_str_t length_key = njs_str("length"); if (njs_slow_path(!njs_is_index_or_key(name))) { ret = njs_value_to_key(vm, name, name); if (njs_slow_path(ret != NJS_OK)) { return ret; } } again: njs_property_query_init(&pq, NJS_PROPERTY_QUERY_SET, hash, 1); ret = (flags & NJS_OBJECT_PROP_CREATE) ? NJS_DECLINED : njs_property_query(vm, &pq, object, name); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } switch (njs_prop_type(flags)) { case NJS_OBJECT_PROP_DESCRIPTOR: prop = njs_descriptor_prop(vm, name, value); if (njs_slow_path(prop == NULL)) { return NJS_ERROR; } break; case NJS_OBJECT_PROP_VALUE: if (((flags & NJS_OBJECT_PROP_VALUE_ECW) == NJS_OBJECT_PROP_VALUE_ECW) && njs_is_fast_array(object) && njs_is_number(name)) { if (njs_number_is_integer_index(njs_number(name))) { array = njs_array(object); index = (uint32_t) njs_number(name); if (index < array->length) { njs_value_assign(&array->start[index], value); return NJS_OK; } } } prop = njs_object_prop_alloc2(vm, name, NJS_PROPERTY, flags & NJS_OBJECT_PROP_VALUE_ECW); if (njs_slow_path(prop == NULL)) { return NJS_ERROR; } njs_value_assign(njs_prop_value(prop), value); break; case NJS_OBJECT_PROP_GETTER: case NJS_OBJECT_PROP_SETTER: default: njs_assert(njs_is_function(value)); prop = njs_object_prop_alloc2(vm, name, NJS_ACCESSOR, NJS_OBJECT_PROP_VALUE_EC); if (njs_slow_path(prop == NULL)) { return NJS_ERROR; } if (njs_prop_type(flags) == NJS_OBJECT_PROP_GETTER) { njs_prop_getter(prop) = njs_function(value); njs_prop_setter(prop) = NJS_PROP_PTR_UNSET; } else { njs_prop_getter(prop) = NJS_PROP_PTR_UNSET; njs_prop_setter(prop) = njs_function(value); } break; } if (njs_fast_path(ret == NJS_DECLINED)) { set_prop: if (!njs_object(object)->extensible) { njs_key_string_get(vm, name, &pq.lhq.key); njs_type_error(vm, "Cannot add property \"%V\", " "object is not extensible", &pq.lhq.key); return NJS_ERROR; } if (njs_slow_path(njs_is_typed_array(object) && njs_is_string(name))) { /* Integer-Indexed Exotic Objects [[DefineOwnProperty]]. */ if (!isnan(njs_string_to_index(name))) { njs_type_error(vm, "Invalid typed array index"); return NJS_ERROR; } } /* 6.2.5.6 CompletePropertyDescriptor */ if (njs_is_accessor_descriptor(prop)) { if (njs_prop_getter(prop) == NJS_PROP_PTR_UNSET) { njs_prop_getter(prop) = NULL; } if (njs_prop_setter(prop) == NJS_PROP_PTR_UNSET) { njs_prop_setter(prop) = NULL; } } else { if (prop->writable == NJS_ATTRIBUTE_UNSET) { prop->writable = 0; } if (!njs_is_valid(njs_prop_value(prop))) { njs_set_undefined(njs_prop_value(prop)); } } if (prop->enumerable == NJS_ATTRIBUTE_UNSET) { prop->enumerable = 0; } if (prop->configurable == NJS_ATTRIBUTE_UNSET) { prop->configurable = 0; } if (njs_slow_path(pq.lhq.value != NULL)) { prev = pq.lhq.value; if (njs_slow_path(prev->type == NJS_WHITEOUT)) { /* Previously deleted property. */ *prev = *prop; } } else { if ((flags & NJS_OBJECT_PROP_CREATE)) { ret = njs_primitive_value_to_key(vm, &pq.key, name); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } if (njs_is_symbol(name)) { pq.lhq.key_hash = njs_symbol_key(name); pq.lhq.key.start = NULL; } else { njs_string_get(&pq.key, &pq.lhq.key); pq.lhq.key_hash = (hash == 0) ? njs_djb_hash(pq.lhq.key.start, pq.lhq.key.length) : hash; } pq.lhq.proto = &njs_object_hash_proto; } pq.lhq.value = prop; pq.lhq.replace = 0; pq.lhq.pool = vm->mem_pool; ret = njs_lvlhsh_insert(njs_object_hash(object), &pq.lhq); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "lvlhsh insert failed"); return NJS_ERROR; } } return NJS_OK; } /* Updating existing prop. */ prev = pq.lhq.value; switch (prev->type) { case NJS_PROPERTY: case NJS_ACCESSOR: case NJS_PROPERTY_HANDLER: break; case NJS_PROPERTY_REF: case NJS_PROPERTY_PLACE_REF: if (prev->type == NJS_PROPERTY_REF && !njs_is_accessor_descriptor(prop) && prop->configurable != NJS_ATTRIBUTE_FALSE && prop->enumerable != NJS_ATTRIBUTE_FALSE && prop->writable != NJS_ATTRIBUTE_FALSE) { if (njs_is_valid(njs_prop_value(prop))) { njs_value_assign(njs_prop_ref(prev), njs_prop_value(prop)); } return NJS_OK; } array = njs_array(object); length = array->length; ret = njs_array_convert_to_slow_array(vm, array); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_array_length_redefine(vm, object, length, 1); if (njs_slow_path(ret != NJS_OK)) { return ret; } flags &= ~NJS_OBJECT_PROP_CREATE; goto again; case NJS_PROPERTY_TYPED_ARRAY_REF: if (njs_is_accessor_descriptor(prop)) { goto exception; } if (prop->configurable == NJS_ATTRIBUTE_TRUE || prop->enumerable == NJS_ATTRIBUTE_FALSE || prop->writable == NJS_ATTRIBUTE_FALSE) { goto exception; } if (njs_is_valid(njs_prop_value(prop))) { return njs_typed_array_set_value(vm, njs_typed_array(njs_prop_value(prev)), njs_prop_magic32(prev), njs_prop_value(prop)); } return NJS_OK; default: njs_internal_error(vm, "unexpected property type \"%s\" " "while defining property", njs_prop_type_string(prev->type)); return NJS_ERROR; } /* 9.1.6.3 ValidateAndApplyPropertyDescriptor */ if (!prev->configurable) { if (prop->configurable == NJS_ATTRIBUTE_TRUE) { goto exception; } if (prop->enumerable != NJS_ATTRIBUTE_UNSET && prev->enumerable != prop->enumerable) { goto exception; } } if (njs_is_generic_descriptor(prop)) { goto done; } if (njs_is_data_descriptor(prev) != njs_is_data_descriptor(prop)) { if (!prev->configurable) { goto exception; } /* * 6.b-c Preserve the existing values of the converted property's * [[Configurable]] and [[Enumerable]] attributes and set the rest of * the property's attributes to their default values. */ if (pq.temp) { pq.lhq.value = NULL; prop->configurable = prev->configurable; prop->enumerable = prev->enumerable; goto set_prop; } if (njs_is_data_descriptor(prev)) { prev->writable = NJS_ATTRIBUTE_UNSET; njs_prop_getter(prev) = NULL; njs_prop_setter(prev) = NULL; } else { prev->writable = NJS_ATTRIBUTE_FALSE; njs_set_undefined(njs_prop_value(prev)); } prev->type = prop->type; } else if (njs_is_data_descriptor(prev) && njs_is_data_descriptor(prop)) { if (!prev->configurable && !prev->writable) { if (prop->writable == NJS_ATTRIBUTE_TRUE) { goto exception; } if (njs_is_valid(njs_prop_value(prop)) && prev->type != NJS_PROPERTY_HANDLER && !njs_values_same(njs_prop_value(prop), njs_prop_value(prev))) { goto exception; } } } else { if (!prev->configurable) { if (njs_prop_getter(prop) != NJS_PROP_PTR_UNSET && njs_prop_getter(prop) != njs_prop_getter(prev)) { goto exception; } if (njs_prop_setter(prop) != NJS_PROP_PTR_UNSET && njs_prop_setter(prop) != njs_prop_setter(prev)) { goto exception; } } } done: if (njs_slow_path(njs_is_fast_array(object) && pq.lhq.key_hash == NJS_LENGTH_HASH) && njs_strstr_eq(&pq.lhq.key, &length_key) && prop->writable == NJS_ATTRIBUTE_FALSE) { array = njs_array(object); length = array->length; ret = njs_array_convert_to_slow_array(vm, array); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_array_length_redefine(vm, object, length, 1); if (njs_slow_path(ret != NJS_OK)) { return ret; } goto again; } if (njs_is_accessor_descriptor(prop)) { prev->type = prop->type; if (njs_prop_getter(prop) != NJS_PROP_PTR_UNSET) { njs_prop_getter(prev) = njs_prop_getter(prop); } if (njs_prop_setter(prop) != NJS_PROP_PTR_UNSET) { njs_prop_setter(prev) = njs_prop_setter(prop); } } else if (njs_is_valid(njs_prop_value(prop))) { if (prev->type == NJS_PROPERTY_HANDLER) { if (prev->writable) { ret = njs_prop_handler(prev)(vm, prev, object, njs_prop_value(prop), &retval); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (ret == NJS_DECLINED) { pq.lhq.value = NULL; goto set_prop; } } else { prev->type = prop->type; njs_value_assign(njs_prop_value(prev), njs_prop_value(prop)); } } else { if (njs_slow_path(njs_is_array(object) && pq.lhq.key_hash == NJS_LENGTH_HASH) && njs_strstr_eq(&pq.lhq.key, &length_key)) { if (prev->configurable != NJS_ATTRIBUTE_TRUE && prev->writable != NJS_ATTRIBUTE_TRUE && !njs_values_strict_equal(njs_prop_value(prev), njs_prop_value(prop))) { njs_type_error(vm, "Cannot redefine property: \"length\""); return NJS_ERROR; } if (prop->writable != NJS_ATTRIBUTE_UNSET) { prev->writable = prop->writable; } return njs_array_length_set(vm, object, prev, njs_prop_value(prop)); } njs_value_assign(njs_prop_value(prev), njs_prop_value(prop)); } } /* * 9. For each field of Desc that is present, set the corresponding * attribute of the property named P of object O to the value of the field. */ if (prop->writable != NJS_ATTRIBUTE_UNSET) { prev->writable = prop->writable; } if (prop->enumerable != NJS_ATTRIBUTE_UNSET) { prev->enumerable = prop->enumerable; } if (prop->configurable != NJS_ATTRIBUTE_UNSET) { prev->configurable = prop->configurable; } return NJS_OK; exception: njs_key_string_get(vm, &pq.key, &pq.lhq.key); njs_type_error(vm, "Cannot redefine property: \"%V\"", &pq.lhq.key); return NJS_ERROR; } njs_int_t njs_prop_private_copy(njs_vm_t *vm, njs_property_query_t *pq, njs_object_t *proto) { njs_int_t ret; njs_value_t *value; njs_object_t *object; njs_function_t *function; njs_object_prop_t *prop, *shared; prop = njs_mp_align(vm->mem_pool, sizeof(njs_value_t), sizeof(njs_object_prop_t)); if (njs_slow_path(prop == NULL)) { njs_memory_error(vm); return NJS_ERROR; } shared = pq->lhq.value; *prop = *shared; pq->lhq.replace = 0; pq->lhq.value = prop; pq->lhq.pool = vm->mem_pool; ret = njs_lvlhsh_insert(&proto->hash, &pq->lhq); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "lvlhsh insert failed"); return NJS_ERROR; } if (njs_is_accessor_descriptor(prop)) { if (njs_prop_getter(prop) != NULL) { function = njs_function_copy(vm, njs_prop_getter(prop)); if (njs_slow_path(function == NULL)) { return NJS_ERROR; } njs_prop_getter(prop) = function; if (njs_prop_setter(prop) != NULL && function->native && njs_prop_setter(prop)->native && function->u.native == njs_prop_setter(prop)->u.native) { njs_prop_setter(prop) = njs_prop_getter(prop); return NJS_OK; } } if (njs_prop_setter(prop) != NULL) { function = njs_function_copy(vm, njs_prop_setter(prop)); if (njs_slow_path(function == NULL)) { return NJS_ERROR; } njs_prop_setter(prop) = function; } return NJS_OK; } value = njs_prop_value(prop); switch (value->type) { case NJS_OBJECT: case NJS_ARRAY: case NJS_OBJECT_VALUE: object = njs_object_value_copy(vm, value); if (njs_slow_path(object == NULL)) { return NJS_ERROR; } value->data.u.object = object; return NJS_OK; case NJS_FUNCTION: function = njs_function_value_copy(vm, value); if (njs_slow_path(function == NULL)) { return NJS_ERROR; } return njs_function_name_set(vm, function, &prop->name, NULL); default: break; } return NJS_OK; } static njs_object_prop_t * njs_descriptor_prop(njs_vm_t *vm, const njs_value_t *name, const njs_value_t *desc) { njs_int_t ret; njs_bool_t data, accessor; njs_value_t value; njs_object_t *desc_object; njs_function_t *getter, *setter; njs_object_prop_t *prop; njs_lvlhsh_query_t lhq; static const njs_value_t get_string = njs_string("get"); if (!njs_is_object(desc)) { njs_type_error(vm, "property descriptor must be an object"); return NULL; } prop = njs_object_prop_alloc(vm, name, &njs_value_invalid, NJS_ATTRIBUTE_UNSET); if (njs_slow_path(prop == NULL)) { return NULL; } data = 0; accessor = 0; getter = NJS_PROP_PTR_UNSET; setter = NJS_PROP_PTR_UNSET; desc_object = njs_object(desc); njs_object_property_init(&lhq, &get_string, NJS_GET_HASH); ret = njs_object_property(vm, desc_object, &lhq, &value); if (njs_slow_path(ret == NJS_ERROR)) { return NULL; } if (ret == NJS_OK) { if (njs_is_defined(&value) && !njs_is_function(&value)) { njs_type_error(vm, "Getter must be a function"); return NULL; } accessor = 1; getter = njs_is_function(&value) ? njs_function(&value) : NULL; } lhq.key = njs_str_value("set"); lhq.key_hash = NJS_SET_HASH; ret = njs_object_property(vm, desc_object, &lhq, &value); if (njs_slow_path(ret == NJS_ERROR)) { return NULL; } if (ret == NJS_OK) { if (njs_is_defined(&value) && !njs_is_function(&value)) { njs_type_error(vm, "Setter must be a function"); return NULL; } accessor = 1; setter = njs_is_function(&value) ? njs_function(&value) : NULL; } lhq.key = njs_str_value("value"); lhq.key_hash = NJS_VALUE_HASH; ret = njs_object_property(vm, desc_object, &lhq, &value); if (njs_slow_path(ret == NJS_ERROR)) { return NULL; } if (ret == NJS_OK) { data = 1; njs_value_assign(njs_prop_value(prop), &value); } lhq.key = njs_str_value("writable"); lhq.key_hash = NJS_WRITABABLE_HASH; ret = njs_object_property(vm, desc_object, &lhq, &value); if (njs_slow_path(ret == NJS_ERROR)) { return NULL; } if (ret == NJS_OK) { data = 1; prop->writable = njs_is_true(&value); } if (accessor && data) { njs_type_error(vm, "Cannot both specify accessors " "and a value or writable attribute"); return NULL; } lhq.key = njs_str_value("enumerable"); lhq.key_hash = NJS_ENUMERABLE_HASH; ret = njs_object_property(vm, desc_object, &lhq, &value); if (njs_slow_path(ret == NJS_ERROR)) { return NULL; } if (ret == NJS_OK) { prop->enumerable = njs_is_true(&value); } lhq.key = njs_str_value("configurable"); lhq.key_hash = NJS_CONFIGURABLE_HASH; ret = njs_object_property(vm, desc_object, &lhq, &value); if (njs_slow_path(ret == NJS_ERROR)) { return NULL; } if (ret == NJS_OK) { prop->configurable = njs_is_true(&value); } if (accessor) { prop->type = NJS_ACCESSOR; njs_prop_getter(prop) = getter; njs_prop_setter(prop) = setter; } return prop; } static const njs_value_t njs_object_value_string = njs_string("value"); static const njs_value_t njs_object_get_string = njs_string("get"); static const njs_value_t njs_object_set_string = njs_string("set"); static const njs_value_t njs_object_writable_string = njs_string("writable"); static const njs_value_t njs_object_enumerable_string = njs_string("enumerable"); static const njs_value_t njs_object_configurable_string = njs_string("configurable"); njs_int_t njs_object_prop_descriptor(njs_vm_t *vm, njs_value_t *dest, njs_value_t *value, njs_value_t *key) { njs_int_t ret; njs_object_t *desc; njs_object_prop_t *pr, *prop; const njs_value_t *setval; njs_lvlhsh_query_t lhq; njs_property_query_t pq; njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0, 1); if (njs_slow_path(!njs_is_key(key))) { ret = njs_value_to_key(vm, key, key); if (njs_slow_path(ret != NJS_OK)) { return ret; } } ret = njs_property_query(vm, &pq, value, key); switch (ret) { case NJS_OK: prop = pq.lhq.value; switch (prop->type) { case NJS_PROPERTY: case NJS_ACCESSOR: break; case NJS_PROPERTY_HANDLER: pq.scratch = *prop; prop = &pq.scratch; ret = njs_prop_handler(prop)(vm, prop, value, NULL, njs_prop_value(prop)); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } break; default: njs_type_error(vm, "unexpected property type: %s", njs_prop_type_string(prop->type)); return NJS_ERROR; } break; case NJS_DECLINED: njs_set_undefined(dest); return NJS_OK; case NJS_ERROR: default: return NJS_ERROR; } desc = njs_object_alloc(vm); if (njs_slow_path(desc == NULL)) { return NJS_ERROR; } lhq.proto = &njs_object_hash_proto; lhq.replace = 0; lhq.pool = vm->mem_pool; if (njs_is_data_descriptor(prop)) { lhq.key = njs_str_value("value"); lhq.key_hash = NJS_VALUE_HASH; pr = njs_object_prop_alloc(vm, &njs_object_value_string, njs_prop_value(prop), 1); if (njs_slow_path(pr == NULL)) { return NJS_ERROR; } lhq.value = pr; ret = njs_lvlhsh_insert(&desc->hash, &lhq); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "lvlhsh insert failed"); return NJS_ERROR; } lhq.key = njs_str_value("writable"); lhq.key_hash = NJS_WRITABABLE_HASH; setval = (prop->writable == 1) ? &njs_value_true : &njs_value_false; pr = njs_object_prop_alloc(vm, &njs_object_writable_string, setval, 1); if (njs_slow_path(pr == NULL)) { return NJS_ERROR; } lhq.value = pr; ret = njs_lvlhsh_insert(&desc->hash, &lhq); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "lvlhsh insert failed"); return NJS_ERROR; } } else { lhq.key = njs_str_value("get"); lhq.key_hash = NJS_GET_HASH; pr = njs_object_prop_alloc(vm, &njs_object_get_string, &njs_value_undefined, 1); if (njs_slow_path(pr == NULL)) { return NJS_ERROR; } if (njs_prop_getter(prop) != NULL) { njs_set_function(njs_prop_value(pr), njs_prop_getter(prop)); } lhq.value = pr; ret = njs_lvlhsh_insert(&desc->hash, &lhq); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "lvlhsh insert failed"); return NJS_ERROR; } lhq.key = njs_str_value("set"); lhq.key_hash = NJS_SET_HASH; pr = njs_object_prop_alloc(vm, &njs_object_set_string, &njs_value_undefined, 1); if (njs_slow_path(pr == NULL)) { return NJS_ERROR; } if (njs_prop_setter(prop) != NULL) { njs_set_function(njs_prop_value(pr), njs_prop_setter(prop)); } lhq.value = pr; ret = njs_lvlhsh_insert(&desc->hash, &lhq); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "lvlhsh insert failed"); return NJS_ERROR; } } lhq.key = njs_str_value("enumerable"); lhq.key_hash = NJS_ENUMERABLE_HASH; setval = (prop->enumerable == 1) ? &njs_value_true : &njs_value_false; pr = njs_object_prop_alloc(vm, &njs_object_enumerable_string, setval, 1); if (njs_slow_path(pr == NULL)) { return NJS_ERROR; } lhq.value = pr; ret = njs_lvlhsh_insert(&desc->hash, &lhq); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "lvlhsh insert failed"); return NJS_ERROR; } lhq.key = njs_str_value("configurable"); lhq.key_hash = NJS_CONFIGURABLE_HASH; setval = (prop->configurable == 1) ? &njs_value_true : &njs_value_false; pr = njs_object_prop_alloc(vm, &njs_object_configurable_string, setval, 1); if (njs_slow_path(pr == NULL)) { return NJS_ERROR; } lhq.value = pr; ret = njs_lvlhsh_insert(&desc->hash, &lhq); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "lvlhsh insert failed"); return NJS_ERROR; } njs_set_object(dest, desc); return NJS_OK; } const char * njs_prop_type_string(njs_object_prop_type_t type) { switch (type) { case NJS_PROPERTY_REF: case NJS_PROPERTY_PLACE_REF: return "property_ref"; case NJS_PROPERTY_HANDLER: return "property handler"; case NJS_WHITEOUT: return "whiteout"; case NJS_PROPERTY: return "property"; default: return "unknown"; } } njs_int_t njs_object_prop_init(njs_vm_t *vm, const njs_object_init_t* init, const njs_object_prop_t *base, njs_value_t *value, njs_value_t *retval) { njs_int_t ret; njs_object_t *object; njs_object_prop_t *prop; njs_lvlhsh_query_t lhq; object = njs_object_alloc(vm); if (object == NULL) { return NJS_ERROR; } ret = njs_object_hash_create(vm, &object->hash, init->properties, init->items); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } prop = njs_mp_align(vm->mem_pool, sizeof(njs_value_t), sizeof(njs_object_prop_t)); if (njs_slow_path(prop == NULL)) { njs_memory_error(vm); return NJS_ERROR; } *prop = *base; prop->type = NJS_PROPERTY; njs_set_object(njs_prop_value(prop), object); lhq.proto = &njs_object_hash_proto; njs_string_get(&prop->name, &lhq.key); lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length); lhq.value = prop; lhq.replace = 1; lhq.pool = vm->mem_pool; ret = njs_lvlhsh_insert(njs_object_hash(value), &lhq); if (njs_fast_path(ret == NJS_OK)) { njs_value_assign(retval, njs_prop_value(prop)); return NJS_OK; } njs_internal_error(vm, "lvlhsh insert failed"); return NJS_ERROR; } njs-0.8.9/src/njs_object_prop_declare.h000066400000000000000000000071521474132077100201340ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_OBJECT_PROP_DECLARE_H_INCLUDED_ #define _NJS_OBJECT_PROP_DECLARE_H_INCLUDED_ #define NJS_DECLARE_PROP_VALUE(_name, _v, _fl) \ { \ .type = NJS_PROPERTY, \ .name = njs_string(_name), \ .u.value = _v, \ .enumerable = !!(_fl & NJS_OBJECT_PROP_ENUMERABLE), \ .configurable = !!(_fl & NJS_OBJECT_PROP_CONFIGURABLE), \ .writable = !!(_fl & NJS_OBJECT_PROP_WRITABLE), \ } #define NJS_DECLARE_PROP_LVALUE(_name, _v, _fl) \ { \ .type = NJS_PROPERTY, \ .name = njs_long_string(_name), \ .u.value = _v, \ .enumerable = !!(_fl & NJS_OBJECT_PROP_ENUMERABLE), \ .configurable = !!(_fl & NJS_OBJECT_PROP_CONFIGURABLE), \ .writable = !!(_fl & NJS_OBJECT_PROP_WRITABLE), \ } #define NJS_DECLARE_PROP_NATIVE(_name, _native, _nargs, _magic) \ NJS_DECLARE_PROP_VALUE(_name, \ njs_native_function2(_native, _nargs, _magic), \ NJS_OBJECT_PROP_VALUE_CW) #define NJS_DECLARE_PROP_LNATIVE(_name, _native, _nargs, _magic) \ NJS_DECLARE_PROP_LVALUE(_name, \ njs_native_function2(_native, _nargs, _magic), \ NJS_OBJECT_PROP_VALUE_CW) #define NJS_DECLARE_PROP_HANDLER(_name, _native, _m16, _m32, _fl) \ { \ .type = NJS_PROPERTY_HANDLER, \ .name = njs_string(_name), \ .u.value = njs_prop_handler2(_native, _m16, _m32), \ .enumerable = !!(_fl & NJS_OBJECT_PROP_ENUMERABLE), \ .configurable = !!(_fl & NJS_OBJECT_PROP_CONFIGURABLE), \ .writable = !!(_fl & NJS_OBJECT_PROP_WRITABLE), \ } #define NJS_DECLARE_PROP_GETTER(_name, _native, _magic) \ { \ .type = NJS_ACCESSOR, \ .name = njs_string(_name), \ .u.accessor = njs_getter(_native, _magic), \ .writable = NJS_ATTRIBUTE_UNSET, \ .configurable = 1, \ } #define NJS_DECLARE_PROP_NAME(_name) \ NJS_DECLARE_PROP_VALUE("name", njs_string(_name), NJS_OBJECT_PROP_VALUE_C) #define NJS_DECLARE_PROP_LENGTH(_v) \ NJS_DECLARE_PROP_VALUE("length", njs_value(NJS_NUMBER, !!(_v), _v), \ NJS_OBJECT_PROP_VALUE_C) #endif /* _NJS_OBJECT_PROP_DECLARE_H_INCLUDED_ */ njs-0.8.9/src/njs_parser.c000066400000000000000000007716161474132077100154530ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) Alexander Borisov * Copyright (C) NGINX, Inc. */ #include static njs_int_t njs_parser_scope_begin(njs_parser_t *parser, njs_scope_t type, njs_bool_t init_this); static void njs_parser_scope_end(njs_parser_t *parser); static njs_int_t njs_parser_check_error_state(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_primary_expression_test(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_regexp_literal(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_template_literal(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_template_literal_string(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_template_literal_expression( njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_cover_parenthesized_expression( njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_binding_identifier_pattern(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_cover_parenthesized_expression_after( njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_cover_parenthesized_expression_end( njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_array_literal(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_array_element_list(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_array_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_array_spread_element(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_object_literal(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_object_literal_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_property_definition_list(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_property_definition_list_after( njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_property_definition(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_property_definition_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_computed_property_name_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_computed_property_async_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_computed_property_name_handler(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current, njs_bool_t async); static njs_int_t njs_parser_initializer(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_initializer_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_initializer_assign(njs_parser_t *parser, njs_token_type_t type); static njs_int_t njs_parser_member_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_member_expression_next(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_member_expression_bracket(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_member_expression_new_next(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_super_property(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_member_expression_import(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_member_expression_new(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_member_expression_new_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_member_expression_new_args(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_parser_node_t *njs_parser_create_call(njs_parser_t *parser, njs_parser_node_t *node, uint8_t ctor); static njs_int_t njs_parser_call_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_call_expression_args(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_call_expression_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_arguments(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_parenthesis_or_comma(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_argument_list(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_argument_list_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_optional_expression_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_optional_chain(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_optional_chain_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_new_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_new_expression_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_left_hand_side_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_left_hand_side_expression_after( njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_left_hand_side_expression_node( njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_left_hand_side_expression_optional( njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_update_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_update_expression_post(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_update_expression_unary(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_unary_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_unary_expression_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_unary_expression_next(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_await(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_await_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_exponentiation_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_exponentiation_expression_match( njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_multiplicative_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_multiplicative_expression_match( njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_additive_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_additive_expression_match(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_shift_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_shift_expression_match(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_relational_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_relational_expression_match(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_equality_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_equality_expression_match(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_bitwise_AND_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_bitwise_AND_expression_and(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_bitwise_XOR_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_bitwise_XOR_expression_xor(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_bitwise_OR_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_bitwise_OR_expression_or(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_logical_AND_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_logical_AND_expression_and(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_logical_OR_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_logical_OR_expression_or(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_coalesce_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_short_circuit_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_conditional_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_conditional_question_mark(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_conditional_colon(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_conditional_colon_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_assignment_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_match_arrow_expression(njs_parser_t *parser, njs_lexer_token_t *token); static njs_int_t njs_parser_assignment_expression_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_assignment_operator(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_assignment_operator_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_expression_comma(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_statement_wo_node(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_declaration(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_hoistable_declaration(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_block_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_block_statement_open_brace(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_block_statement_close_brace(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_statement_list(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_statement_list_next(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_statement_list_item(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_lexical_declaration(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_variable_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_variable_declaration_list(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_variable_declaration_list_next( njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_variable_declaration(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_binding_pattern(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_object_binding_pattern(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_array_binding_pattern(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_expression_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_expression_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_if_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_if_close_parenthesis(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_else_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_else_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_iteration_statement_do(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_iteration_statement_do_while(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_do_while_semicolon(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_iteration_statement_while(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_while_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_while_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_iteration_statement_for(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_for_expression_map_continue( njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_for_expression_map_reparse( njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_expression_continue_op(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_expression_continue_assign_comma( njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_for_in_statement_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_iteration_statement_for_map(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_for_var_binding_or_var_list(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current, njs_token_type_t token_type); static njs_int_t njs_parser_for_var_in_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_for_var_in_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_for_var_in_of_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_for_in_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_for_in_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_for_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_for_expression_end(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_for_end(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_switch_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_switch_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_switch_block(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_switch_block_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_switch_case(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_switch_case_wo_def(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_switch_case_def(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current, njs_bool_t with_default); static njs_int_t njs_parser_switch_case_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_switch_case_block(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_switch_case_after_wo_def(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_switch_case_block_wo_def(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_continue_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_break_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_break_continue(njs_parser_t *parser, njs_lexer_token_t *token, njs_token_type_t type); static njs_int_t njs_parser_return_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_return_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_with_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_labelled_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_labelled_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_throw_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_throw_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_try_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_catch_or_finally(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_catch_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_catch_parenthesis(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_catch_statement_open_brace(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_catch_finally(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_debugger_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_function_declaration(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_function_declaration_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_function_parse(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_function_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_function_expression_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_unique_formal_parameters(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_formal_parameters(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_formal_parameters_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_arrow_function(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_arrow_function_args_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_arrow_function_arrow(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_arrow_function_body_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_method_definition(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_get_set(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_get_set_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_get_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_set_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_function_lambda(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_function_lambda_args_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_function_lambda_body_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_export(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_export_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_import(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); static njs_int_t njs_parser_export_sink(njs_parser_t *parser); static njs_parser_node_t *njs_parser_return_set(njs_parser_t *parser, njs_parser_node_t *expr); static njs_parser_node_t *njs_parser_variable_node(njs_parser_t *parser, uintptr_t unique_id, njs_variable_type_t type, njs_variable_t **retvar); static njs_parser_node_t *njs_parser_reference(njs_parser_t *parser, njs_lexer_token_t *token); static njs_parser_node_t *njs_parser_argument(njs_parser_t *parser, njs_parser_node_t *expr, njs_index_t index); static njs_int_t njs_parser_object_property(njs_parser_t *parser, njs_parser_node_t *parent, njs_parser_node_t *property, njs_parser_node_t *value, njs_bool_t proto_init); static njs_int_t njs_parser_property_accessor(njs_parser_t *parser, njs_parser_node_t *parent, njs_parser_node_t *property, njs_parser_node_t *value, njs_token_type_t accessor); static njs_int_t njs_parser_array_item(njs_parser_t *parser, njs_parser_node_t *array, njs_parser_node_t *value); static njs_int_t njs_parser_template_string(njs_parser_t *parser, njs_lexer_token_t *token); static njs_token_type_t njs_parser_escape_string_create(njs_parser_t *parser, njs_lexer_token_t *token, njs_value_t *value); static njs_int_t njs_parser_escape_string_calc_length(njs_parser_t *parser, njs_lexer_token_t *token, size_t *out_size, size_t *out_length); static void njs_parser_serialize_tree(njs_chb_t *chain, njs_parser_node_t *node, njs_int_t *ret, size_t indent); static njs_int_t njs_parser_serialize_node(njs_chb_t *chain, njs_parser_node_t *node); #define njs_parser_chain_top(parser) \ ((parser)->scope->top) #define njs_parser_chain_top_set(parser, node) \ (parser)->scope->top = node njs_inline njs_int_t njs_parser_not_supported(njs_parser_t *parser, njs_lexer_token_t *token) { if (token->type != NJS_TOKEN_END) { njs_parser_syntax_error(parser, "Token \"%V\" not supported " "in this version", &token->text); } else { njs_parser_syntax_error(parser, "Not supported in this version"); } return NJS_DONE; } njs_inline njs_int_t njs_parser_reject(njs_parser_t *parser) { njs_queue_link_t *link; njs_parser_stack_entry_t *entry; while (!njs_queue_is_empty(&parser->stack)) { entry = njs_queue_link_data(njs_queue_first(&parser->stack), njs_parser_stack_entry_t, link); link = njs_queue_first(&parser->stack); njs_queue_remove(link); if (!entry->optional) { njs_parser_next(parser, entry->state); parser->target = entry->node; return NJS_DECLINED; } } return njs_parser_failed(parser); } njs_int_t njs_parser_init(njs_vm_t *vm, njs_parser_t *parser, njs_parser_scope_t *scope, njs_str_t *file, u_char *start, u_char *end, njs_uint_t runtime) { njs_lexer_t *lexer; njs_memzero(parser, sizeof(njs_parser_t)); parser->scope = scope; lexer = &parser->lexer0; parser->lexer = lexer; parser->use_lhs = 0; return njs_lexer_init(vm, lexer, file, start, end, runtime, 0); } njs_int_t njs_parser(njs_vm_t *vm, njs_parser_t *parser) { njs_int_t ret; njs_str_t str; njs_lexer_token_t *token; const njs_lexer_keyword_entry_t *keyword; parser->vm = vm; njs_set_invalid(&vm->exception); if (parser->scope == NULL) { ret = njs_parser_scope_begin(parser, parser->module ? NJS_SCOPE_FUNCTION : NJS_SCOPE_GLOBAL, 1); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } else { parser->scope->top = NULL; parser->node = NULL; parser->ret = NJS_OK; } /* Add this as first variable. */ njs_string_get(&njs_string_undefined, &str); keyword = njs_lexer_keyword(str.start, str.length); if (njs_slow_path(keyword == NULL)) { return NJS_ERROR; } parser->undefined_id = (uintptr_t) keyword->value; njs_queue_init(&parser->stack); parser->target = NULL; njs_parser_next(parser, njs_parser_statement_list); ret = njs_parser_after(parser, njs_queue_first(&parser->stack), NULL, 0, njs_parser_check_error_state); if (ret != NJS_OK) { return ret; } do { token = njs_lexer_token(parser->lexer, 0); if (njs_slow_path(token == NULL)) { return NJS_ERROR; } parser->ret = parser->state(parser, token, njs_queue_first(&parser->stack)); } while (parser->ret != NJS_DONE && parser->ret != NJS_ERROR); if (parser->ret != NJS_DONE) { return NJS_ERROR; } if (njs_is_error(&vm->exception)) { return NJS_ERROR; } if (parser->node == NULL) { /* Empty string, just semicolons or variables declarations. */ parser->node = njs_parser_node_new(parser, 0); if (njs_slow_path(parser->node == NULL)) { return NJS_ERROR; } } if (parser->module) { ret = njs_parser_export_sink(parser); if (ret != NJS_OK) { return NJS_ERROR; } } else { parser->node->token_type = NJS_TOKEN_END; parser->node->token_line = parser->lexer->line; njs_parser_chain_top_set(parser, parser->node); } return NJS_OK; } static njs_int_t njs_parser_check_error_state(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { return njs_parser_failed(parser); } njs_int_t njs_parser_failed_state(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_END) { njs_parser_syntax_error(parser, "Unexpected token \"%V\"", &token->text); } else { njs_parser_syntax_error(parser, "Unexpected end of input"); } return NJS_DONE; } static njs_int_t njs_parser_scope_begin(njs_parser_t *parser, njs_scope_t type, njs_bool_t init_this) { njs_variable_t *var; njs_parser_scope_t *scope, *parent; const njs_lexer_keyword_entry_t *keyword; static const njs_str_t njs_this_str = njs_str("this"); scope = njs_mp_zalloc(parser->vm->mem_pool, sizeof(njs_parser_scope_t)); if (njs_slow_path(scope == NULL)) { return NJS_ERROR; } scope->type = type; njs_rbtree_init(&scope->variables, njs_parser_scope_rbtree_compare); njs_rbtree_init(&scope->labels, njs_parser_scope_rbtree_compare); njs_rbtree_init(&scope->references, njs_parser_scope_rbtree_compare); parent = parser->scope; scope->parent = parent; parser->scope = scope; if (type == NJS_SCOPE_FUNCTION || type == NJS_SCOPE_GLOBAL) { if (init_this) { /* Add this as first variable. */ keyword = njs_lexer_keyword(njs_this_str.start, njs_this_str.length); if (njs_slow_path(keyword == NULL)) { return NJS_ERROR; } var = njs_variable_add(parser, scope, (uintptr_t) keyword->value, NJS_VARIABLE_VAR); if (njs_slow_path(var == NULL)) { return NJS_ERROR; } var->index = njs_scope_index(type, 0, NJS_LEVEL_LOCAL, NJS_VARIABLE_VAR); } } scope->items = 1; return NJS_OK; } static void njs_parser_scope_end(njs_parser_t *parser) { njs_parser_scope_t *scope, *parent; scope = parser->scope; parent = scope->parent; parser->scope = parent; } intptr_t njs_parser_scope_rbtree_compare(njs_rbtree_node_t *node1, njs_rbtree_node_t *node2) { njs_variable_node_t *lex_node1, *lex_node2; lex_node1 = (njs_variable_node_t *) node1; lex_node2 = (njs_variable_node_t *) node2; if (lex_node1->key < lex_node2->key) { return -1; } if (lex_node1->key > lex_node2->key) { return 1; } return 0; } static njs_int_t njs_parser_generator_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { return njs_parser_not_supported(parser, token); } static njs_int_t njs_parser_class_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { return njs_parser_not_supported(parser, token); } static njs_int_t njs_parser_async_generator_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { return njs_parser_not_supported(parser, token); } static njs_int_t njs_parser_generator_declaration(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { return njs_parser_not_supported(parser, token); } static njs_int_t njs_parser_class_declaration(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { return njs_parser_not_supported(parser, token); } static njs_int_t njs_parser_function_or_generator_handler(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current, njs_bool_t is_async) { njs_parser_node_t *node, *cur; cur = parser->node; if (token->type == NJS_TOKEN_MULTIPLICATION) { njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_generator_declaration); } else { if (is_async) { node = njs_parser_node_new(parser, NJS_TOKEN_ASYNC_FUNCTION_DECLARATION); } else { node = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_DECLARATION); } if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; parser->node = node; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_function_declaration); } return njs_parser_after(parser, current, cur, 1, njs_parser_statement_after); } static njs_int_t njs_parser_function_or_generator(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_FUNCTION) { return NJS_DECLINED; } return njs_parser_function_or_generator_handler(parser, token, current, 0); } static njs_int_t njs_parser_async_function_or_generator(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_ASYNC) { return NJS_DECLINED; } token = njs_lexer_peek_token(parser->lexer, token, 1); if (token == NULL) { return NJS_ERROR; } if (token->type != NJS_TOKEN_FUNCTION) { return NJS_DECLINED; } njs_lexer_consume_token(parser->lexer, 1); return njs_parser_function_or_generator_handler(parser, token, current, 1); } njs_inline njs_int_t njs_parser_expect_semicolon(njs_parser_t *parser, njs_lexer_token_t *token) { if (token->type != NJS_TOKEN_SEMICOLON) { if (parser->strict_semicolon || (token->type != NJS_TOKEN_END && token->type != NJS_TOKEN_CLOSE_BRACE && parser->lexer->prev_type != NJS_TOKEN_LINE_END)) { return NJS_DECLINED; } return NJS_OK; } njs_lexer_consume_token(parser->lexer, 1); return NJS_OK; } static njs_int_t njs_parser_semicolon(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (njs_parser_expect_semicolon(parser, token) != NJS_OK) { return njs_parser_failed(parser); } return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_close_bracked(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_CLOSE_BRACKET) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_close_parenthesis(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (parser->ret != NJS_OK) { return njs_parser_failed(parser); } if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_expression_parenthesis(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_OPEN_PARENTHESIS) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); parser->node = NULL; njs_parser_next(parser, njs_parser_expression); return njs_parser_after(parser, current, NULL, 0, njs_parser_close_parenthesis); } static njs_int_t njs_parser_iteration_statement_for_end(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->node->token_line = (uint32_t) (uintptr_t) parser->target; parser->target = NULL; njs_parser_scope_end(parser); return njs_parser_stack_pop(parser); } /* * 12.2 Primary Expression. */ static njs_int_t njs_parser_primary_expression_test(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; njs_lexer_token_t *next; njs_parser_node_t *node; switch (token->type) { /* IdentifierReference */ case NJS_TOKEN_THIS: case NJS_TOKEN_NULL: case NJS_TOKEN_NAME: case NJS_TOKEN_YIELD: case NJS_TOKEN_AWAIT: goto reference; case NJS_TOKEN_TRUE: node = njs_parser_node_new(parser, token->type); if (node == NULL) { return NJS_ERROR; } node->u.value = njs_value_true; node->token_line = token->line; parser->node = node; goto done; case NJS_TOKEN_FALSE: node = njs_parser_node_new(parser, token->type); if (node == NULL) { return NJS_ERROR; } node->u.value = njs_value_false; node->token_line = token->line; parser->node = node; goto done; case NJS_TOKEN_NUMBER: node = njs_parser_node_new(parser, NJS_TOKEN_NUMBER); if (node == NULL) { return NJS_ERROR; } njs_set_number(&node->u.value, token->number); node->token_line = token->line; parser->node = node; goto done; case NJS_TOKEN_STRING: node = njs_parser_node_new(parser, NJS_TOKEN_STRING); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; ret = njs_parser_string_create(parser->vm, token, &node->u.value); if (ret != NJS_OK) { return NJS_ERROR; } parser->node = node; goto done; case NJS_TOKEN_ESCAPE_STRING: /* Internal optimization. This is not in the specification. */ node = njs_parser_node_new(parser, NJS_TOKEN_STRING); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; ret = njs_parser_escape_string_create(parser, token, &node->u.value); if (ret != NJS_TOKEN_STRING) { return NJS_ERROR; } parser->node = node; goto done; case NJS_TOKEN_UNTERMINATED_STRING: /* Internal optimization. This is not in the specification. */ njs_parser_syntax_error(parser, "Unterminated string \"%V\"", &token->text); return NJS_ERROR; /* ArrayLiteral */ case NJS_TOKEN_OPEN_BRACKET: node = njs_parser_node_new(parser, NJS_TOKEN_ARRAY); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; parser->node = node; njs_parser_next(parser, njs_parser_array_literal); break; /* ObjectLiteral */ case NJS_TOKEN_OPEN_BRACE: node = njs_parser_node_new(parser, NJS_TOKEN_OBJECT); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; parser->node = node; njs_parser_next(parser, njs_parser_object_literal); break; /* FunctionExpression */ case NJS_TOKEN_FUNCTION: token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } /* GeneratorExpression */ if (token->type == NJS_TOKEN_MULTIPLICATION) { njs_parser_next(parser, njs_parser_generator_expression); } else { node = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_EXPRESSION); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; parser->node = node; njs_parser_next(parser, njs_parser_function_expression); } break; /* ClassExpression */ case NJS_TOKEN_CLASS: njs_parser_next(parser, njs_parser_class_expression); return NJS_OK; /* AsyncFunctionExpression */ case NJS_TOKEN_ASYNC: next = njs_lexer_peek_token(parser->lexer, token, 1); if (next == NULL) { return NJS_ERROR; } if (next->type != NJS_TOKEN_FUNCTION) { goto reference; } njs_lexer_consume_token(parser->lexer, 1); next = njs_lexer_peek_token(parser->lexer, next, 0); if (njs_slow_path(next == NULL)) { return NJS_ERROR; } /* GeneratorExpression */ if (next->type == NJS_TOKEN_MULTIPLICATION) { njs_parser_next(parser, njs_parser_async_generator_expression); } else { node = njs_parser_node_new(parser, NJS_TOKEN_ASYNC_FUNCTION_EXPRESSION); if (node == NULL) { return NJS_ERROR; } node->token_line = next->line; parser->node = node; njs_parser_next(parser, njs_parser_function_expression); } break; /* RegularExpressionLiteral */ case NJS_TOKEN_DIVISION: case NJS_TOKEN_DIVISION_ASSIGNMENT: node = njs_parser_node_new(parser, NJS_TOKEN_REGEXP); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; parser->node = node; ret = njs_parser_regexp_literal(parser, token, current); if (ret != NJS_OK) { return NJS_ERROR; } goto done; /* TemplateLiteral */ case NJS_TOKEN_GRAVE: node = njs_parser_node_new(parser, NJS_TOKEN_TEMPLATE_LITERAL); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; parser->node = node; njs_parser_next(parser, njs_parser_template_literal); return NJS_OK; /* CoverParenthesizedExpressionAndArrowParameterList */ case NJS_TOKEN_OPEN_PARENTHESIS: njs_lexer_consume_token(parser->lexer, 1); /* TODO: By specification. */ (void) njs_parser_cover_parenthesized_expression; parser->node = NULL; njs_parser_next(parser, njs_parser_expression); return njs_parser_after(parser, current, NULL, 0, njs_parser_close_parenthesis); default: if (njs_lexer_token_is_identifier_reference(token)) { goto reference; } return njs_parser_reject(parser); } njs_lexer_consume_token(parser->lexer, 1); return NJS_OK; reference: node = njs_parser_reference(parser, token); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; parser->node = node; done: njs_lexer_consume_token(parser->lexer, 1); return NJS_DONE; } static njs_int_t njs_parser_regexp_literal(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { u_char *p; njs_str_t text; njs_int_t ret; njs_lexer_t *lexer; njs_value_t *value, retval; njs_regex_flags_t flags; njs_regexp_pattern_t *pattern; static const njs_value_t string_message = njs_string("message"); value = &parser->node->u.value; lexer = parser->lexer; if (token->type == NJS_TOKEN_DIVISION_ASSIGNMENT) { lexer->start--; } for (p = lexer->start; p < lexer->end; p++) { switch (*p) { case '\n': case '\r': goto failed; case '[': while (1) { if (++p >= lexer->end) { goto failed; } if (*p == ']') { break; } switch (*p) { case '\n': case '\r': goto failed; case '\\': if (++p >= lexer->end || *p == '\n' || *p == '\r') { goto failed; } break; } } break; case '\\': if (++p >= lexer->end || *p == '\n' || *p == '\r') { goto failed; } break; case '/': text.start = lexer->start; text.length = p - text.start; p++; lexer->start = p; flags = njs_regexp_flags(&p, lexer->end); if (njs_slow_path(flags < 0)) { njs_parser_syntax_error(parser, "Invalid RegExp flags \"%*s\"", p - lexer->start, lexer->start); return NJS_ERROR; } lexer->start = p; pattern = njs_regexp_pattern_create(parser->vm, text.start, text.length, flags); if (njs_slow_path(pattern == NULL)) { retval = njs_vm_exception(parser->vm); ret = njs_value_property(parser->vm, &retval, njs_value_arg(&string_message), &retval); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_string_get(&retval, &text); njs_parser_syntax_error(parser, "%V", &text); return NJS_ERROR; } value->data.u.data = pattern; return NJS_OK; } } failed: njs_parser_syntax_error(parser, "Unterminated RegExp \"%*s\"", p - (lexer->start - 1), lexer->start - 1); return NJS_ERROR; } static njs_int_t njs_parser_template_literal(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_index_t index; njs_parser_node_t *node, *array, *template, *temp; temp = njs_parser_node_new(parser, 0); if (temp == NULL) { return NJS_ERROR; } array = njs_parser_node_new(parser, NJS_TOKEN_ARRAY); if (array == NULL) { return NJS_ERROR; } array->token_line = token->line; template = parser->node; index = njs_scope_temp_index(template->scope); if (njs_slow_path(index == NJS_INDEX_ERROR)) { return NJS_ERROR; } if (template->token_type != NJS_TOKEN_TEMPLATE_LITERAL) { node = njs_parser_argument(parser, array, index); if (node == NULL) { return NJS_ERROR; } node->temporary = 1; template->right = node; temp->right = node; index = njs_scope_temp_index(template->scope); if (njs_slow_path(index == NJS_INDEX_ERROR)) { return NJS_ERROR; } } else { template->left = array; temp->right = template; } temp->temporary = 1; temp->left = template; temp->index = index; parser->target = temp; token->text.start++; token->text.length = 0; njs_parser_next(parser, njs_parser_template_literal_string); return NJS_OK; } static njs_int_t njs_parser_template_literal_string(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret, ret_item; njs_parser_node_t *template; template = parser->target->left; ret = njs_parser_template_string(parser, token); if (ret == NJS_ERROR) { njs_parser_syntax_error(parser, "Unterminated template literal"); return NJS_DONE; } if (template->token_type != NJS_TOKEN_TEMPLATE_LITERAL) { ret_item = njs_parser_array_item(parser, template->right->left, parser->node); } else { ret_item = njs_parser_array_item(parser, template->left, parser->node); } if (ret_item != NJS_OK) { return NJS_ERROR; } if (ret == NJS_DONE) { parser->node = template; njs_parser_node_free(parser, parser->target); njs_lexer_consume_token(parser->lexer, 1); return njs_parser_stack_pop(parser); } parser->node = NULL; njs_parser_next(parser, njs_parser_expression); njs_lexer_consume_token(parser->lexer, 1); return njs_parser_after(parser, current, parser->target, 0, njs_parser_template_literal_expression); } static njs_int_t njs_parser_template_literal_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; njs_parser_node_t *template, *node, *parent; if (parser->ret != NJS_OK) { return njs_parser_failed(parser); } if (token->type != NJS_TOKEN_CLOSE_BRACE) { njs_parser_syntax_error(parser, "Missing \"}\" in template expression"); return NJS_DONE; } parent = parser->target->right; template = parser->target->left; if (template->token_type != NJS_TOKEN_TEMPLATE_LITERAL) { node = njs_parser_argument(parser, parser->node, parser->target->index); if (node == NULL) { return NJS_ERROR; } parent->right = node; parent = node; parser->target->index = njs_scope_temp_index(node->scope); if (njs_slow_path(parser->target->index == NJS_INDEX_ERROR)) { return NJS_ERROR; } } else { ret = njs_parser_array_item(parser, template->left, parser->node); if (ret != NJS_OK) { return NJS_ERROR; } } parser->target->right = parent; parser->node = NULL; njs_parser_next(parser, njs_parser_template_literal_string); token->text.length = 0; token->text.start += 1; return NJS_OK; } static njs_int_t njs_parser_cover_parenthesized_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { switch (token->type) { case NJS_TOKEN_CLOSE_PARENTHESIS: (void) njs_parser_stack_pop(parser); break; case NJS_TOKEN_ELLIPSIS: njs_parser_next(parser, njs_parser_binding_identifier_pattern); break; default: parser->node = NULL; njs_parser_next(parser, njs_parser_expression); return njs_parser_after(parser, current, NULL, 0, njs_parser_cover_parenthesized_expression_after); } njs_lexer_consume_token(parser->lexer, 1); return NJS_OK; } static njs_int_t njs_parser_binding_identifier_pattern(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { /* * BindingIdentifier ) * BindingPattern ) */ switch (token->type) { /* BindingIdentifier */ case NJS_TOKEN_NAME: njs_parser_next(parser, njs_parser_cover_parenthesized_expression_end); break; case NJS_TOKEN_YIELD: njs_parser_next(parser, njs_parser_cover_parenthesized_expression_end); break; case NJS_TOKEN_AWAIT: njs_parser_next(parser, njs_parser_cover_parenthesized_expression_end); break; /* BindingPattern */ case NJS_TOKEN_OPEN_BRACKET: njs_parser_next(parser, njs_parser_array_binding_pattern); njs_lexer_consume_token(parser->lexer, 1); return njs_parser_after(parser, current, NULL, 0, njs_parser_cover_parenthesized_expression_end); case NJS_TOKEN_OPEN_BRACE: njs_parser_next(parser, njs_parser_object_binding_pattern); njs_lexer_consume_token(parser->lexer, 1); return njs_parser_after(parser, current, NULL, 0, njs_parser_cover_parenthesized_expression_end); default: return NJS_ERROR; } njs_lexer_consume_token(parser->lexer, 1); return NJS_OK; } static njs_int_t njs_parser_cover_parenthesized_expression_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { /* * ) * ,) * , ... BindingIdentifier ) * , ... BindingPattern ) */ if (token->type == NJS_TOKEN_CLOSE_PARENTHESIS) { goto shift_stack; } if (token->type != NJS_TOKEN_COMMA) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (njs_slow_path(token == NULL)) { return NJS_ERROR; } if (token->type == NJS_TOKEN_CLOSE_PARENTHESIS) { goto shift_stack; } if(token->type != NJS_TOKEN_ELLIPSIS) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_binding_identifier_pattern); return NJS_OK; shift_stack: njs_lexer_consume_token(parser->lexer, 1); return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_cover_parenthesized_expression_end(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); return njs_parser_stack_pop(parser); } /* * 12.2.5 Array Initializer. */ static njs_int_t njs_parser_array_literal(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->target = parser->node; parser->node = NULL; njs_parser_next(parser, njs_parser_array_element_list); return NJS_OK; } static njs_int_t njs_parser_array_element_list(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *array; array = parser->target; switch (token->type) { case NJS_TOKEN_CLOSE_BRACKET: njs_lexer_consume_token(parser->lexer, 1); parser->node = array; return njs_parser_stack_pop(parser); case NJS_TOKEN_COMMA: njs_lexer_consume_token(parser->lexer, 1); array->ctor = 1; array->u.length++; return NJS_OK; case NJS_TOKEN_ELLIPSIS: #if 0 njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_assignment_expression); return njs_parser_after(parser, current, array, 0, njs_parser_array_spread_element); #else (void) njs_parser_array_spread_element; return njs_parser_failed(parser); #endif default: break; } njs_parser_next(parser, njs_parser_assignment_expression); return njs_parser_after(parser, current, array, 0, njs_parser_array_after); } static njs_int_t njs_parser_array_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; if (parser->ret != NJS_OK) { return njs_parser_failed(parser); } ret = njs_parser_array_item(parser, parser->target, parser->node); if (ret != NJS_OK) { return NJS_ERROR; } switch (token->type) { case NJS_TOKEN_COMMA: njs_lexer_consume_token(parser->lexer, 1); /* Fall through. */ case NJS_TOKEN_CLOSE_BRACKET: njs_parser_next(parser, njs_parser_array_element_list); break; default: return njs_parser_failed(parser); } return NJS_OK; } static njs_int_t njs_parser_array_spread_element(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (parser->ret != NJS_OK || token->type != NJS_TOKEN_CLOSE_BRACKET) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); parser->node = parser->target; return njs_parser_stack_pop(parser); } /* * 12.2.6 Object Initializer. */ static njs_int_t njs_parser_object_literal(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node; node = njs_parser_node_new(parser, 0); if (node == NULL) { return NJS_ERROR; } node->left = parser->node; parser->node = NULL; parser->target = node; njs_parser_next(parser, njs_parser_property_definition_list); return njs_parser_after(parser, current, node, 1, njs_parser_object_literal_after); } static njs_int_t njs_parser_object_literal_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type == NJS_TOKEN_COMMA) { njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 1); if (token == NULL) { return NJS_ERROR; } } if (token->type != NJS_TOKEN_CLOSE_BRACE) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); parser->node = parser->target->left; njs_parser_node_free(parser, parser->target); parser->target = NULL; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_property_definition_list(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_property_definition); return njs_parser_after(parser, current, parser->target, 1, njs_parser_property_definition_list_after); } static njs_int_t njs_parser_property_definition_list_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_COMMA) { return njs_parser_stack_pop(parser); } njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_property_definition); return njs_parser_after(parser, current, parser->target, 1, njs_parser_property_definition_list_after); } njs_inline njs_parser_node_t * njs_parser_property_name_node(njs_parser_t *parser, njs_lexer_token_t *token) { njs_int_t ret; njs_parser_node_t *property; if (token->type == NJS_TOKEN_NUMBER) { property = njs_parser_node_new(parser, NJS_TOKEN_NUMBER); if (property != NULL) { njs_set_number(&property->u.value, token->number); } } else if (token->type == NJS_TOKEN_ESCAPE_STRING) { property = njs_parser_node_new(parser, NJS_TOKEN_STRING); if (property != NULL) { ret = njs_parser_escape_string_create(parser, token, &property->u.value); if (ret != NJS_TOKEN_STRING) { return NULL; } } } else { property = njs_parser_node_string(parser->vm, token, parser); } if (property != NULL) { property->token_line = token->line; } return property; } njs_inline njs_int_t njs_parser_property_name(njs_parser_t *parser, njs_queue_link_t *current, unsigned consume) { njs_lexer_token_t *token; njs_parser_node_t *property; if (consume > 1) { token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } property = njs_parser_property_name_node(parser, token); if (property == NULL) { return NJS_ERROR; } parser->target->right = property; parser->target->index = (token->type == NJS_TOKEN_NAME); } njs_lexer_consume_token(parser->lexer, consume); parser->node = NULL; njs_parser_next(parser, njs_parser_assignment_expression); return njs_parser_after(parser, current, parser->target, 1, njs_parser_property_definition_after); } static njs_int_t njs_parser_property_definition_ident(njs_parser_t *parser, njs_lexer_token_t *token, njs_parser_node_t *temp) { temp->right = njs_parser_node_string(parser->vm, token, parser); if (temp->right == NULL) { return NJS_ERROR; } temp->right->index = NJS_TOKEN_OPEN_BRACKET; parser->node = njs_parser_reference(parser, token); if (parser->node == NULL) { return NJS_ERROR; } njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } /* CoverInitializedName */ if (token->type == NJS_TOKEN_ASSIGNMENT) { return njs_parser_not_supported(parser, token); } njs_parser_next(parser, njs_parser_property_definition_after); return NJS_OK; } static njs_int_t njs_parser_property_definition(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_str_t *name; njs_token_type_t accessor; njs_lexer_token_t *next; njs_parser_node_t *temp, *property; temp = parser->target; switch (token->type) { case NJS_TOKEN_CLOSE_BRACE: return njs_parser_stack_pop(parser); /* PropertyName */ case NJS_TOKEN_STRING: case NJS_TOKEN_ESCAPE_STRING: case NJS_TOKEN_NUMBER: next = njs_lexer_peek_token(parser->lexer, token, 0); if (next == NULL) { return NJS_ERROR; } if (next->type == NJS_TOKEN_COLON) { return njs_parser_property_name(parser, current, 2); } else if (next->type == NJS_TOKEN_OPEN_PARENTHESIS) { goto method_definition; } njs_lexer_consume_token(parser->lexer, 1); return njs_parser_failed(parser); /* ComputedPropertyName */ case NJS_TOKEN_OPEN_BRACKET: njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_assignment_expression); return njs_parser_after(parser, current, temp, 1, njs_parser_computed_property_name_after); case NJS_TOKEN_ELLIPSIS: return njs_parser_not_supported(parser, token); case NJS_TOKEN_ASYNC: token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } if (token->type == NJS_TOKEN_OPEN_BRACKET) { njs_lexer_consume_token(parser->lexer, 2); njs_parser_next(parser, njs_parser_assignment_expression); return njs_parser_after(parser, current, temp, 1, njs_parser_computed_property_async_after); } if (token->type == NJS_TOKEN_COLON) { return njs_parser_property_name(parser, current, 2); } if (!njs_lexer_token_is_identifier_name(token)) { return njs_parser_failed(parser); } next = njs_lexer_peek_token(parser->lexer, token, 0); if (next == NULL) { return NJS_ERROR; } if (next->type == NJS_TOKEN_OPEN_PARENTHESIS) { goto method_definition; } njs_lexer_consume_token(parser->lexer, 1); return njs_parser_failed(parser); default: if (!njs_lexer_token_is_identifier_name(token)) { return njs_parser_reject(parser); } next = njs_lexer_peek_token(parser->lexer, token, 0); if (next == NULL) { return NJS_ERROR; } /* PropertyName */ if (next->type == NJS_TOKEN_COLON) { return njs_parser_property_name(parser, current, 2); /* MethodDefinition */ } else if (next->type == NJS_TOKEN_OPEN_PARENTHESIS) { goto method_definition; } else if (njs_lexer_token_is_reserved(token)) { njs_lexer_consume_token(parser->lexer, 1); return njs_parser_failed(parser); } name = &token->text; if (name->length == 3 && (memcmp(name->start, "get", 3) == 0 || memcmp(name->start, "set", 3) == 0)) { accessor = (name->start[0] == 'g') ? NJS_TOKEN_PROPERTY_GETTER : NJS_TOKEN_PROPERTY_SETTER; temp->right = (njs_parser_node_t *) (uintptr_t) accessor; njs_parser_next(parser, njs_parser_get_set); return NJS_OK; } return njs_parser_property_definition_ident(parser, token, temp); } method_definition: property = njs_parser_property_name_node(parser, token); if (property == NULL) { return NJS_ERROR; } temp->right = property; temp->right->index = NJS_TOKEN_OPEN_BRACKET; njs_parser_next(parser, njs_parser_method_definition); return njs_parser_after(parser, current, temp, 1, njs_parser_property_definition_after); } static njs_int_t njs_parser_property_definition_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; njs_str_t name; njs_bool_t proto_init; njs_parser_node_t *property, *temp; static const njs_str_t proto_string = njs_str("__proto__"); temp = parser->target; property = temp->right; proto_init = 0; if (property->index != NJS_TOKEN_OPEN_BRACKET && njs_is_string(&property->u.value)) { njs_string_get(&property->u.value, &name); if (njs_slow_path(njs_strstr_eq(&name, &proto_string))) { if (temp->token_type == NJS_TOKEN_PROTO_INIT) { njs_parser_syntax_error(parser, "Duplicate __proto__ fields are not allowed " "in object literals"); return NJS_ERROR; } temp->token_type = NJS_TOKEN_PROTO_INIT; proto_init = 1; } } if (property->index != 0) { property->index = 0; } ret = njs_parser_object_property(parser, temp->left, property, parser->node, proto_init); if (ret != NJS_OK) { return NJS_ERROR; } temp->right = NULL; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_computed_property_name_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { return njs_parser_computed_property_name_handler(parser, token, current, 0); } static njs_int_t njs_parser_computed_property_async_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { return njs_parser_computed_property_name_handler(parser, token, current, 1); } static njs_int_t njs_parser_computed_property_name_handler(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current, njs_bool_t async) { njs_token_type_t type; njs_parser_node_t *expr, *target; if (token->type != NJS_TOKEN_CLOSE_BRACKET) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } target = parser->target; /* * For further identification. * In njs_parser_property_definition_after() index will be reset to zero. */ parser->node->index = NJS_TOKEN_OPEN_BRACKET; target->right = parser->node; if (!async && token->type == NJS_TOKEN_COLON) { return njs_parser_property_name(parser, current, 1); /* MethodDefinition */ } else if (token->type == NJS_TOKEN_OPEN_PARENTHESIS) { type = (async) ? NJS_TOKEN_ASYNC_FUNCTION : NJS_TOKEN_FUNCTION; expr = njs_parser_node_new(parser, type); if (expr == NULL) { return NJS_ERROR; } expr->token_line = token->line; parser->node = expr; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_function_lambda); return njs_parser_after(parser, current, parser->target, 1, njs_parser_property_definition_after); } return njs_parser_failed(parser); } static njs_int_t njs_parser_initializer(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node; if (token->type != NJS_TOKEN_ASSIGNMENT) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); node = parser->node; parser->node = NULL; njs_parser_next(parser, njs_parser_assignment_expression); return njs_parser_after(parser, current, node, 1, njs_parser_initializer_after); } static njs_int_t njs_parser_initializer_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *stmt; stmt = njs_parser_node_new(parser, NJS_TOKEN_STATEMENT); if (stmt == NULL) { return NJS_ERROR; } stmt->left = NULL; stmt->right = parser->target; parser->target->right = parser->node; parser->node = stmt; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_initializer_assign(njs_parser_t *parser, njs_token_type_t type) { njs_parser_node_t *assign; assign = njs_parser_node_new(parser, type); if (assign == NULL) { return NJS_ERROR; } assign->u.operation = NJS_VMCODE_MOVE; assign->left = parser->node; /* assign->right in njs_parser_initializer_after. */ parser->node = assign; return NJS_OK; } /* * 12.3 Left-Hand-Side Expressions. */ static njs_int_t njs_parser_property(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node, *prop_node; /* * [ Expression ] * . IdentifierName * TemplateLiteral */ switch (token->type) { case NJS_TOKEN_OPEN_BRACKET: node = njs_parser_node_new(parser, NJS_TOKEN_PROPERTY); if (node == NULL) { return NJS_ERROR; } node->u.operation = NJS_VMCODE_PROPERTY_GET; node->left = parser->node; node->token_line = token->line; parser->node = NULL; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_expression); return njs_parser_after(parser, current, node, 1, njs_parser_member_expression_bracket); case NJS_TOKEN_DOT: token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } if (njs_lexer_token_is_identifier_name(token)) { node = njs_parser_node_new(parser, NJS_TOKEN_PROPERTY); if (node == NULL) { return NJS_ERROR; } node->u.operation = NJS_VMCODE_PROPERTY_GET; node->token_line = token->line; prop_node = njs_parser_node_string(parser->vm, token, parser); if (prop_node == NULL) { return NJS_ERROR; } prop_node->token_line = token->line; node->left = parser->node; node->right = prop_node; parser->node = node; njs_lexer_consume_token(parser->lexer, 2); return NJS_AGAIN; } njs_lexer_consume_token(parser->lexer, 1); return NJS_DECLINED; case NJS_TOKEN_GRAVE: node = njs_parser_create_call(parser, parser->node, 0); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; parser->node = node; njs_parser_next(parser, njs_parser_template_literal); break; default: return NJS_DONE; } return NJS_OK; } static njs_int_t njs_parser_member_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; /* * PrimaryExpression * MemberExpression [ Expression ] * MemberExpression . IdentifierName * MemberExpression TemplateLiteral * SuperProperty * MetaProperty * new MemberExpression Arguments */ switch (token->type) { /* SuperProperty */ case NJS_TOKEN_SUPER: /* TODO: By specification. */ (void) njs_parser_super_property; return njs_parser_not_supported(parser, token); /* MetaProperty */ case NJS_TOKEN_IMPORT: /* TODO: By specification. */ (void) njs_parser_member_expression_import; return njs_parser_not_supported(parser, token); case NJS_TOKEN_NEW: njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_member_expression_new); break; default: ret = njs_parser_primary_expression_test(parser, token, current); if (ret != NJS_OK) { if (ret == NJS_DONE) { njs_parser_next(parser, njs_parser_member_expression_next); return NJS_OK; } if (njs_is_error(&parser->vm->exception)) { return NJS_DONE; } return ret; } break; } return njs_parser_after(parser, current, NULL, 1, njs_parser_member_expression_next); } static njs_int_t njs_parser_member_expression_next(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; /* * [ Expression ] * . IdentifierName * TemplateLiteral */ ret = njs_parser_property(parser, token, current); switch (ret) { case NJS_AGAIN: return NJS_OK; case NJS_DONE: return njs_parser_stack_pop(parser); case NJS_DECLINED: return njs_parser_failed(parser); default: break; } return njs_parser_after(parser, current, NULL, 1, njs_parser_member_expression_next); } static njs_int_t njs_parser_member_expression_bracket(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_CLOSE_BRACKET) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); parser->target->right = parser->node; parser->node = parser->target; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_member_expression_new_next(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; /* * PrimaryExpression * SuperProperty * MetaProperty * Arguments */ switch (token->type) { /* SuperProperty */ case NJS_TOKEN_SUPER: (void) njs_parser_super_property; return njs_parser_not_supported(parser, token); /* MetaProperty */ case NJS_TOKEN_IMPORT: (void) njs_parser_member_expression_import; return njs_parser_not_supported(parser, token); default: ret = njs_parser_primary_expression_test(parser, token, current); if (ret != NJS_OK) { if (ret == NJS_DONE) { njs_parser_next(parser, njs_parser_member_expression_next); return NJS_OK; } return ret; } return njs_parser_after(parser, current, NULL, 1, njs_parser_member_expression_next); } } static njs_int_t njs_parser_super_property(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { /* * [ Expression ] * . IdentifierName */ if (token->type == NJS_TOKEN_OPEN_BRACKET) { parser->node = NULL; njs_parser_next(parser, njs_parser_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_close_bracked); } if (token->type != NJS_TOKEN_DOT) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } if (!njs_lexer_token_is_identifier_name(token)) { return njs_parser_failed(parser); } return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_member_expression_import(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { /* * import . meta */ if (token->type != NJS_TOKEN_DOT) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } if (token->type != NJS_TOKEN_META) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_member_expression_new(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { /* * new MemberExpression Arguments * new . target */ if (token->type != NJS_TOKEN_DOT) { njs_parser_next(parser, njs_parser_member_expression_new_next); return njs_parser_after(parser, current, NULL, 1, njs_parser_member_expression_new_after); } /* njs_lexer_consume_token(parser->lexer, 1); */ token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } if (token->type != NJS_TOKEN_TARGET) { return njs_parser_failed(parser); } /* njs_lexer_consume_token(parser->lexer, 1); */ /* return njs_parser_stack_pop(parser); */ return njs_parser_not_supported(parser, token); } static njs_int_t njs_parser_member_expression_new_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *func; if (token->type != NJS_TOKEN_OPEN_PARENTHESIS) { parser->node = njs_parser_create_call(parser, parser->node, 1); if (parser->node == NULL) { return NJS_ERROR; } parser->node->token_line = token->line; return njs_parser_stack_pop(parser); } func = njs_parser_create_call(parser, parser->node, 1); if (func == NULL) { return NJS_ERROR; } func->token_line = token->line; parser->node = func; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_arguments); return njs_parser_after(parser, current, func, 1, njs_parser_member_expression_new_args); } static njs_int_t njs_parser_member_expression_new_args(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->node = parser->target; return njs_parser_stack_pop(parser); } static njs_parser_node_t * njs_parser_create_call(njs_parser_t *parser, njs_parser_node_t *node, uint8_t ctor) { njs_parser_node_t *func; switch (node->token_type) { case NJS_TOKEN_NAME: func = node; func->token_type = NJS_TOKEN_FUNCTION_CALL; break; case NJS_TOKEN_PROPERTY: func = njs_parser_node_new(parser, NJS_TOKEN_METHOD_CALL); if (func == NULL) { return NULL; } func->left = node; break; default: /* * NJS_TOKEN_METHOD_CALL, * NJS_TOKEN_FUNCTION_CALL, * NJS_TOKEN_FUNCTION_EXPRESSION, * NJS_TOKEN_OPEN_PARENTHESIS, * NJS_TOKEN_EVAL. */ func = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_CALL); if (func == NULL) { return NULL; } func->left = node; break; } func->ctor = ctor; return func; } static njs_int_t njs_parser_call_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; /* * CoverCallExpressionAndAsyncArrowHead * SuperCall * ImportCall * CallExpression Arguments * CallExpression [ Expression ] * CallExpression . IdentifierName * CallExpression TemplateLiteral */ switch (token->type) { /* MemberExpression or SuperCall */ case NJS_TOKEN_SUPER: return njs_parser_not_supported(parser, token); case NJS_TOKEN_IMPORT: return njs_parser_not_supported(parser, token); default: break; } njs_parser_next(parser, njs_parser_member_expression); ret = njs_parser_after(parser, current, NULL, 1, njs_parser_call_expression_args); if (ret != NJS_OK) { return NJS_ERROR; } return njs_parser_after(parser, current, NULL, 1, njs_parser_call_expression_after); } static njs_int_t njs_parser_call_expression_args(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *func; if (token->type != NJS_TOKEN_OPEN_PARENTHESIS) { return njs_parser_failed(parser); } func = njs_parser_create_call(parser, parser->node, 0); if (func == NULL) { return NJS_ERROR; } func->token_line = token->line; parser->node = func; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_arguments); return njs_parser_after(parser, current, func, 1, njs_parser_left_hand_side_expression_node); } static njs_int_t njs_parser_call_expression_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; njs_parser_node_t *func; /* * Arguments * [ Expression ] * . IdentifierName * TemplateLiteral */ switch (token->type) { case NJS_TOKEN_OPEN_PARENTHESIS: func = njs_parser_create_call(parser, parser->node, 0); if (func == NULL) { return NJS_ERROR; } func->token_line = token->line; parser->node = func; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_arguments); ret = njs_parser_after(parser, current, func, 1, njs_parser_left_hand_side_expression_node); if (ret != NJS_OK) { return NJS_ERROR; } break; default: ret = njs_parser_property(parser, token, current); switch (ret) { case NJS_AGAIN: return NJS_OK; case NJS_DONE: return njs_parser_stack_pop(parser); case NJS_DECLINED: return njs_parser_failed(parser); default: break; } break; } return njs_parser_after(parser, current, NULL, 1, njs_parser_call_expression_after); } static njs_int_t njs_parser_arguments(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { /* * ) * ArgumentList ) * ArgumentList , ) */ if (token->type == NJS_TOKEN_CLOSE_PARENTHESIS) { njs_lexer_consume_token(parser->lexer, 1); return njs_parser_stack_pop(parser); } parser->scope->in_args++; njs_parser_next(parser, njs_parser_argument_list); return njs_parser_after(parser, current, NULL, 1, njs_parser_parenthesis_or_comma); } static njs_int_t njs_parser_parenthesis_or_comma(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->scope->in_args--; if (token->type == NJS_TOKEN_CLOSE_PARENTHESIS) { njs_lexer_consume_token(parser->lexer, 1); return njs_parser_stack_pop(parser); } if (token->type != NJS_TOKEN_COMMA) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (njs_slow_path(token == NULL)) { return NJS_ERROR; } if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_argument_list(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { /* * AssignmentExpression * ... AssignmentExpression * ArgumentList , AssignmentExpression * ArgumentList , ... AssignmentExpression */ #if 0 /* TODO. */ if (token->type == NJS_TOKEN_ELLIPSIS) { njs_lexer_consume_token(parser->lexer, 1); } #endif njs_parser_next(parser, njs_parser_assignment_expression); return njs_parser_after(parser, current, parser->node, 1, njs_parser_argument_list_after); } static njs_int_t njs_parser_argument_list_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node; node = njs_parser_node_new(parser, NJS_TOKEN_ARGUMENT); if (node == NULL) { return NJS_ERROR; } node->index = njs_scope_temp_index(node->scope); if (njs_slow_path(node->index == NJS_INDEX_ERROR)) { return NJS_ERROR; } node->token_line = token->line; node->left = parser->node; parser->node->dest = node; parser->target->right = node; parser->node = node; if (token->type == NJS_TOKEN_COMMA) { njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } if (token->type == NJS_TOKEN_CLOSE_PARENTHESIS) { return njs_parser_stack_pop(parser); } return njs_parser_argument_list(parser, token, current); } return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_optional_expression_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_CONDITIONAL) { return njs_parser_stack_pop(parser); } token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } if (token->type != NJS_TOKEN_DOT) { return njs_parser_stack_pop(parser); } njs_parser_next(parser, njs_parser_optional_chain); return njs_parser_after(parser, current, NULL, 1, njs_parser_optional_expression_after); } static njs_int_t njs_parser_optional_chain(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; njs_parser_node_t *func; /* * ? . Arguments * ? . [ Expression ] * ? . IdentifierName * ? . TemplateLiteral * OptionalChain Arguments * OptionalChain [ Expression ] * OptionalChain . IdentifierName * OptionalChain TemplateLiteral */ if (token->type != NJS_TOKEN_CONDITIONAL) { return njs_parser_failed(parser); } token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } if (token->type != NJS_TOKEN_DOT) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } switch (token->type) { case NJS_TOKEN_OPEN_PARENTHESIS: func = njs_parser_create_call(parser, parser->node, 0); if (func == NULL) { return NJS_ERROR; } func->token_line = token->line; parser->node = func; njs_lexer_consume_token(parser->lexer, 2); njs_parser_next(parser, njs_parser_arguments); ret = njs_parser_after(parser, current, func, 1, njs_parser_left_hand_side_expression_node); if (ret != NJS_OK) { return NJS_ERROR; } break; default: ret = njs_parser_property(parser, token, current); switch (ret) { case NJS_DONE: case NJS_DECLINED: return njs_parser_failed(parser); default: break; } break; } return njs_parser_after(parser, current, NULL, 1, njs_parser_optional_chain_after); } static njs_int_t njs_parser_optional_chain_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; njs_parser_node_t *func; /* * OptionalChain Arguments * OptionalChain [ Expression ] * OptionalChain . IdentifierName * OptionalChain TemplateLiteral */ switch (token->type) { case NJS_TOKEN_OPEN_PARENTHESIS: func = njs_parser_create_call(parser, parser->node, 0); if (func == NULL) { return NJS_ERROR; } func->token_line = token->line; parser->node = func; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_arguments); ret = njs_parser_after(parser, current, func, 1, njs_parser_left_hand_side_expression_node); if (ret != NJS_OK) { return NJS_ERROR; } break; default: ret = njs_parser_property(parser, token, current); switch (ret) { case NJS_AGAIN: return NJS_OK; case NJS_DONE: return njs_parser_stack_pop(parser); case NJS_DECLINED: return njs_parser_failed(parser); default: break; } break; } return njs_parser_after(parser, current, NULL, 1, njs_parser_optional_chain_after); } static njs_int_t njs_parser_new_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_NEW) { parser->node = NULL; njs_parser_next(parser, njs_parser_member_expression_new); return NJS_OK; } njs_lexer_consume_token(parser->lexer, 1); return njs_parser_after(parser, current, NULL, 1, njs_parser_new_expression_after); } static njs_int_t njs_parser_new_expression_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *func; if (token->type == NJS_TOKEN_OPEN_PARENTHESIS) { njs_parser_next(parser, njs_parser_member_expression_new_after); return NJS_OK; } func = njs_parser_create_call(parser, parser->node, 1); if (func == NULL) { return NJS_ERROR; } func->token_line = token->line; parser->node = func; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_left_hand_side_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { /* * NewExpression = new MemberExpression * CallExpression = MemberExpression Arguments * OptionalExpression = MemberExpression OptionalChain */ switch (token->type) { /* NewExpression or MemberExpression */ case NJS_TOKEN_NEW: token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } if (token->type == NJS_TOKEN_NEW) { njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_new_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_left_hand_side_expression_after); } break; /* CallExpression or MemberExpression */ case NJS_TOKEN_SUPER: case NJS_TOKEN_IMPORT: token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } if (token->type == NJS_TOKEN_OPEN_PARENTHESIS) { njs_parser_next(parser, njs_parser_call_expression); return NJS_OK; } break; default: break; } njs_parser_next(parser, njs_parser_member_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_left_hand_side_expression_after); } static njs_int_t njs_parser_left_hand_side_expression_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; njs_parser_node_t *func; switch (token->type) { /* CallExpression */ case NJS_TOKEN_OPEN_PARENTHESIS: func = njs_parser_create_call(parser, parser->node, 0); if (func == NULL) { return NJS_ERROR; } func->token_line = token->line; parser->node = func; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_arguments); ret = njs_parser_after(parser, current, func, 1, njs_parser_left_hand_side_expression_node); if (ret != NJS_OK) { return NJS_ERROR; } return njs_parser_after(parser, current, NULL, 1, njs_parser_left_hand_side_expression_optional); /* OptionalExpression */ case NJS_TOKEN_CONDITIONAL: njs_parser_next(parser, njs_parser_optional_expression_after); break; default: return njs_parser_stack_pop(parser); } return NJS_OK; } static njs_int_t njs_parser_left_hand_side_expression_node(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->node = parser->target; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_left_hand_side_expression_optional(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { /* OptionalExpression */ if (token->type == NJS_TOKEN_CONDITIONAL) { njs_parser_next(parser, njs_parser_optional_expression_after); return NJS_OK; } njs_parser_next(parser, njs_parser_optional_chain_after); return NJS_OK; } static njs_int_t njs_parser_expression_node(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current, njs_token_type_t type, njs_vmcode_t operation, njs_parser_state_func_t after) { njs_parser_node_t *node; if (parser->target != NULL) { parser->target->right = parser->node; parser->target->right->dest = parser->target; parser->node = parser->target; } if (token->type != type) { return njs_parser_stack_pop(parser); } node = njs_parser_node_new(parser, type); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; node->u.operation = operation; node->left = parser->node; node->left->dest = node; njs_lexer_consume_token(parser->lexer, 1); return njs_parser_after(parser, current, node, 1, after); } /* * 12.4 Update Expressions. */ static njs_int_t njs_parser_update_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_vmcode_t operation; njs_parser_node_t *node; switch (token->type) { case NJS_TOKEN_INCREMENT: operation = NJS_VMCODE_INCREMENT; break; case NJS_TOKEN_DECREMENT: operation = NJS_VMCODE_DECREMENT; break; default: njs_parser_next(parser, njs_parser_left_hand_side_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_update_expression_post); } node = njs_parser_node_new(parser, token->type); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; node->u.operation = operation; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_left_hand_side_expression); return njs_parser_after(parser, current, node, 1, njs_parser_update_expression_unary); } static njs_int_t njs_parser_update_expression_post(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_vmcode_t operation; njs_token_type_t type; njs_parser_node_t *node; /* [no LineTerminator here] */ switch (token->type) { case NJS_TOKEN_INCREMENT: type = NJS_TOKEN_POST_INCREMENT; operation = NJS_VMCODE_POST_INCREMENT; break; case NJS_TOKEN_DECREMENT: type = NJS_TOKEN_POST_DECREMENT; operation = NJS_VMCODE_POST_DECREMENT; break; default: return njs_parser_stack_pop(parser); } if (parser->lexer->prev_type == NJS_TOKEN_LINE_END) { return njs_parser_stack_pop(parser); } if (!njs_parser_is_lvalue(parser->node)) { njs_lexer_consume_token(parser->lexer, 1); njs_parser_ref_error(parser, "Invalid left-hand side in postfix operation"); return NJS_DONE; } node = njs_parser_node_new(parser, type); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; node->u.operation = operation; node->left = parser->node; parser->node = node; njs_lexer_consume_token(parser->lexer, 1); return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_update_expression_unary(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (!njs_parser_is_lvalue(parser->node)) { njs_parser_ref_error(parser, "Invalid left-hand side in prefix operation"); return NJS_DONE; } parser->target->left = parser->node; parser->node = parser->target; return njs_parser_stack_pop(parser); } /* * 12.5 Unary Operators. */ static njs_int_t njs_parser_unary_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_vmcode_t operation; njs_token_type_t type; njs_parser_node_t *node; switch (token->type) { case NJS_TOKEN_DELETE: type = NJS_TOKEN_DELETE; operation = NJS_VMCODE_DELETE; break; case NJS_TOKEN_VOID: type = NJS_TOKEN_VOID; operation = NJS_VMCODE_VOID; break; case NJS_TOKEN_TYPEOF: type = NJS_TOKEN_TYPEOF; operation = NJS_VMCODE_TYPEOF; break; case NJS_TOKEN_ADDITION: type = NJS_TOKEN_UNARY_PLUS; operation = NJS_VMCODE_UNARY_PLUS; break; case NJS_TOKEN_SUBTRACTION: type = NJS_TOKEN_UNARY_NEGATION; operation = NJS_VMCODE_UNARY_NEGATION; break; case NJS_TOKEN_BITWISE_NOT: type = NJS_TOKEN_BITWISE_NOT; operation = NJS_VMCODE_BITWISE_NOT; break; case NJS_TOKEN_LOGICAL_NOT: type = NJS_TOKEN_LOGICAL_NOT; operation = NJS_VMCODE_LOGICAL_NOT; break; /* AwaitExpression */ case NJS_TOKEN_AWAIT: njs_parser_next(parser, njs_parser_await); return NJS_OK; default: njs_parser_next(parser, njs_parser_update_expression); return njs_parser_after(parser, current, parser->target, 1, njs_parser_unary_expression_after); } node = njs_parser_node_new(parser, type); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; node->u.operation = operation; parser->target = node; njs_lexer_consume_token(parser->lexer, 1); return njs_parser_after(parser, current, node, 1, njs_parser_unary_expression_next); } static njs_int_t njs_parser_unary_expression_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (parser->target == NULL && token->type == NJS_TOKEN_EXPONENTIATION) { return njs_parser_exponentiation_expression_match(parser, token, current); } return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_unary_expression_next(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { double num; njs_token_type_t type; njs_parser_node_t *node; type = parser->target->token_type; node = parser->node; if (token->type == NJS_TOKEN_EXPONENTIATION) { njs_parser_syntax_error(parser, "Either left-hand side or entire " "exponentiation must be parenthesized"); return NJS_DONE; } if (node->token_type == NJS_TOKEN_NUMBER) { if (type == NJS_TOKEN_UNARY_PLUS) { /* Skip the unary plus of number. */ return njs_parser_stack_pop(parser); } if (type == NJS_TOKEN_UNARY_NEGATION) { /* Optimization of common negative number. */ num = -njs_number(&node->u.value); njs_set_number(&node->u.value, num); return njs_parser_stack_pop(parser); } } if (type == NJS_TOKEN_DELETE) { switch (node->token_type) { case NJS_TOKEN_PROPERTY: node->token_type = NJS_TOKEN_PROPERTY_DELETE; node->u.operation = NJS_VMCODE_PROPERTY_DELETE; return njs_parser_stack_pop(parser); case NJS_TOKEN_NAME: njs_parser_syntax_error(parser, "Delete of an unqualified identifier"); return NJS_DONE; default: break; } } if (type == NJS_TOKEN_TYPEOF && node->token_type == NJS_TOKEN_NAME) { node->u.reference.type = NJS_TYPEOF; } parser->target->left = parser->node; parser->target->left->dest = parser->target; parser->node = parser->target; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_await(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node; if (!njs_function_scope(parser->scope)->async) { njs_parser_syntax_error(parser, "await is only valid in async functions"); return NJS_ERROR; } if (parser->scope->in_args > 0) { njs_parser_syntax_error(parser, "await in arguments not supported"); return NJS_ERROR; } node = njs_parser_node_new(parser, NJS_TOKEN_AWAIT); if (njs_slow_path(node == NULL)) { return NJS_ERROR; } node->token_line = token->line; njs_lexer_consume_token(parser->lexer, 1); parser->node = NULL; njs_parser_next(parser, njs_parser_unary_expression); return njs_parser_after(parser, current, node, 0, njs_parser_await_after); } static njs_int_t njs_parser_await_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (parser->ret != NJS_OK) { return njs_parser_failed(parser); } parser->target->right = parser->node; parser->node = parser->target; return njs_parser_stack_pop(parser); } /* * 12.6 Exponentiation Operator. */ static njs_int_t njs_parser_exponentiation_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->target = NULL; if (parser->use_lhs == 0) { njs_parser_next(parser, njs_parser_unary_expression); /* For UpdateExpression, see njs_parser_unary_expression_after. */ return NJS_OK; } else { parser->use_lhs = 0; return njs_parser_update_expression_post(parser, token, current); } } static njs_int_t njs_parser_exponentiation_expression_match(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node; if (parser->target != NULL) { parser->target->right = parser->node; parser->target->right->dest = parser->target; parser->node = parser->target; return njs_parser_stack_pop(parser); } if (token->type != NJS_TOKEN_EXPONENTIATION) { return njs_parser_stack_pop(parser); } node = njs_parser_node_new(parser, token->type); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; node->u.operation = NJS_VMCODE_EXPONENTIATION; node->left = parser->node; node->left->dest = node; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_exponentiation_expression); return njs_parser_after(parser, current, node, 1, njs_parser_exponentiation_expression_match); } /* * 12.7 Multiplicative Operators. */ static njs_int_t njs_parser_multiplicative_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_exponentiation_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_multiplicative_expression_match); } static njs_int_t njs_parser_multiplicative_expression_match(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_vmcode_t operation; njs_parser_node_t *node; if (parser->target != NULL) { parser->target->right = parser->node; parser->target->right->dest = parser->target; parser->node = parser->target; } switch (token->type) { case NJS_TOKEN_MULTIPLICATION: operation = NJS_VMCODE_MULTIPLICATION; break; case NJS_TOKEN_DIVISION: operation = NJS_VMCODE_DIVISION; break; case NJS_TOKEN_REMAINDER: operation = NJS_VMCODE_REMAINDER; break; default: return njs_parser_stack_pop(parser); } node = njs_parser_node_new(parser, token->type); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; node->u.operation = operation; node->left = parser->node; node->left->dest = node; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_exponentiation_expression); return njs_parser_after(parser, current, node, 1, njs_parser_multiplicative_expression_match); } /* * 12.8 Additive Operators. */ static njs_int_t njs_parser_additive_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_multiplicative_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_additive_expression_match); } static njs_int_t njs_parser_additive_expression_match(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_vmcode_t operation; njs_parser_node_t *node; if (parser->target != NULL) { parser->target->right = parser->node; parser->target->right->dest = parser->target; parser->node = parser->target; } switch (token->type) { case NJS_TOKEN_ADDITION: operation = NJS_VMCODE_ADDITION; break; case NJS_TOKEN_SUBTRACTION: operation = NJS_VMCODE_SUBTRACTION; break; default: return njs_parser_stack_pop(parser); } node = njs_parser_node_new(parser, token->type); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; node->u.operation = operation; node->left = parser->node; node->left->dest = node; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_multiplicative_expression); return njs_parser_after(parser, current, node, 1, njs_parser_additive_expression_match); } /* * 12.9 Bitwise Shift Operators */ static njs_int_t njs_parser_shift_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_additive_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_shift_expression_match); } static njs_int_t njs_parser_shift_expression_match(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_vmcode_t operation; njs_parser_node_t *node; if (parser->target != NULL) { parser->target->right = parser->node; parser->target->right->dest = parser->target; parser->node = parser->target; } switch (token->type) { case NJS_TOKEN_RIGHT_SHIFT: operation = NJS_VMCODE_RIGHT_SHIFT; break; case NJS_TOKEN_LEFT_SHIFT: operation = NJS_VMCODE_LEFT_SHIFT; break; case NJS_TOKEN_UNSIGNED_RIGHT_SHIFT: operation = NJS_VMCODE_UNSIGNED_RIGHT_SHIFT; break; default: return njs_parser_stack_pop(parser); } node = njs_parser_node_new(parser, token->type); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; node->u.operation = operation; node->left = parser->node; node->left->dest = node; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_additive_expression); return njs_parser_after(parser, current, node, 1, njs_parser_shift_expression_match); } /* * 12.10 Relational Operators. */ static njs_int_t njs_parser_relational_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_shift_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_relational_expression_match); } static njs_int_t njs_parser_relational_expression_match(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_vmcode_t operation; njs_parser_node_t *node; if (parser->target != NULL) { parser->target->right = parser->node; parser->target->right->dest = parser->target; parser->node = parser->target; } switch (token->type) { case NJS_TOKEN_LESS: operation = NJS_VMCODE_LESS; break; case NJS_TOKEN_GREATER: operation = NJS_VMCODE_GREATER; break; case NJS_TOKEN_LESS_OR_EQUAL: operation = NJS_VMCODE_LESS_OR_EQUAL; break; case NJS_TOKEN_GREATER_OR_EQUAL: operation = NJS_VMCODE_GREATER_OR_EQUAL; break; case NJS_TOKEN_INSTANCEOF: operation = NJS_VMCODE_INSTANCE_OF; break; case NJS_TOKEN_IN: if (njs_lexer_in_fail_get(parser->lexer)) { njs_parser_syntax_error(parser, "Invalid left-hand side in for-loop"); return NJS_ERROR; } operation = NJS_VMCODE_PROPERTY_IN; break; default: return njs_parser_stack_pop(parser); } node = njs_parser_node_new(parser, token->type); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; node->u.operation = operation; node->left = parser->node; node->left->dest = node; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_shift_expression); return njs_parser_after(parser, current, node, 1, njs_parser_relational_expression_match); } /* * 12.11 Equality Operators. */ static njs_int_t njs_parser_equality_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_relational_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_equality_expression_match); } static njs_int_t njs_parser_equality_expression_match(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_vmcode_t operation; njs_parser_node_t *node; if (parser->target != NULL) { parser->target->right = parser->node; parser->target->right->dest = parser->target; parser->node = parser->target; } switch (token->type) { case NJS_TOKEN_EQUAL: operation = NJS_VMCODE_EQUAL; break; case NJS_TOKEN_NOT_EQUAL: operation = NJS_VMCODE_NOT_EQUAL; break; case NJS_TOKEN_STRICT_EQUAL: operation = NJS_VMCODE_STRICT_EQUAL; break; case NJS_TOKEN_STRICT_NOT_EQUAL: operation = NJS_VMCODE_STRICT_NOT_EQUAL; break; default: return njs_parser_stack_pop(parser); } node = njs_parser_node_new(parser, token->type); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; node->u.operation = operation; node->left = parser->node; node->left->dest = node; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_relational_expression); return njs_parser_after(parser, current, node, 1, njs_parser_equality_expression_match); } /* * 12.12 Binary Bitwise Operators. */ static njs_int_t njs_parser_bitwise_AND_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_equality_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_bitwise_AND_expression_and); } static njs_int_t njs_parser_bitwise_AND_expression_and(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_equality_expression); return njs_parser_expression_node(parser, token, current, NJS_TOKEN_BITWISE_AND, NJS_VMCODE_BITWISE_AND, njs_parser_bitwise_AND_expression_and); } static njs_int_t njs_parser_bitwise_XOR_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_bitwise_AND_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_bitwise_XOR_expression_xor); } static njs_int_t njs_parser_bitwise_XOR_expression_xor(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_bitwise_AND_expression); return njs_parser_expression_node(parser, token, current, NJS_TOKEN_BITWISE_XOR, NJS_VMCODE_BITWISE_XOR, njs_parser_bitwise_XOR_expression_xor); } static njs_int_t njs_parser_bitwise_OR_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_bitwise_XOR_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_bitwise_OR_expression_or); } static njs_int_t njs_parser_bitwise_OR_expression_or(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_bitwise_XOR_expression); return njs_parser_expression_node(parser, token, current, NJS_TOKEN_BITWISE_OR, NJS_VMCODE_BITWISE_OR, njs_parser_bitwise_OR_expression_or); } /* * 12.13 Binary Logical Operators. */ static njs_int_t njs_parser_logical_AND_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_bitwise_OR_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_logical_AND_expression_and); } static njs_int_t njs_parser_logical_AND_expression_and(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_bitwise_OR_expression); return njs_parser_expression_node(parser, token, current, NJS_TOKEN_LOGICAL_AND, NJS_VMCODE_TEST_IF_FALSE, njs_parser_logical_AND_expression_and); } static njs_int_t njs_parser_logical_OR_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_logical_AND_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_logical_OR_expression_or); } static njs_int_t njs_parser_logical_OR_expression_or(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_logical_AND_expression); return njs_parser_expression_node(parser, token, current, NJS_TOKEN_LOGICAL_OR, NJS_VMCODE_TEST_IF_TRUE, njs_parser_logical_OR_expression_or); } static njs_int_t njs_parser_coalesce_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_token_type_t type; njs_parser_node_t *node; node = parser->node; if (parser->target != NULL) { parser->target->right = node; parser->target->right->dest = parser->target; parser->node = parser->target; } if (token->type != NJS_TOKEN_COALESCE) { return njs_parser_stack_pop(parser); } type = node->token_type; if (parser->lexer->prev_type != NJS_TOKEN_CLOSE_PARENTHESIS && (type == NJS_TOKEN_LOGICAL_OR || type == NJS_TOKEN_LOGICAL_AND)) { return njs_parser_failed(parser); } node = njs_parser_node_new(parser, NJS_TOKEN_COALESCE); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; node->u.operation = NJS_VMCODE_COALESCE; node->left = parser->node; node->left->dest = node; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_bitwise_OR_expression); return njs_parser_after(parser, current, node, 1, njs_parser_coalesce_expression); } static njs_int_t njs_parser_short_circuit_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_logical_OR_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_coalesce_expression); } /* * 12.14 Conditional Operator ( ? : ). */ static njs_int_t njs_parser_conditional_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_short_circuit_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_conditional_question_mark); } static njs_int_t njs_parser_conditional_question_mark(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node, *cond; if (token->type != NJS_TOKEN_CONDITIONAL) { return njs_parser_stack_pop(parser); } cond = njs_parser_node_new(parser, NJS_TOKEN_CONDITIONAL); if (cond == NULL) { return NJS_ERROR; } cond->token_line = token->line; cond->left = parser->node; node = njs_parser_node_new(parser, NJS_TOKEN_BRANCHING); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; cond->right = node; njs_lexer_consume_token(parser->lexer, 1); if (njs_lexer_in_stack_push(parser->lexer) != NJS_OK) { return NJS_ERROR; } njs_parser_next(parser, njs_parser_assignment_expression); return njs_parser_after(parser, current, cond, 1, njs_parser_conditional_colon); } static njs_int_t njs_parser_conditional_colon(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node; if (token->type != NJS_TOKEN_COLON) { return njs_parser_failed(parser); } njs_lexer_in_stack_pop(parser->lexer); njs_lexer_consume_token(parser->lexer, 1); node = parser->target->right; node->left = parser->node; node->left->dest = parser->target; njs_parser_next(parser, njs_parser_assignment_expression); return njs_parser_after(parser, current, parser->target, 1, njs_parser_conditional_colon_after); } static njs_int_t njs_parser_conditional_colon_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node; node = parser->target->right; node->right = parser->node; node->right->dest = parser->target; parser->node = parser->target; return njs_parser_stack_pop(parser); } /* * 12.15 Assignment Operators. */ static njs_int_t njs_parser_assignment_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; if (!parser->use_lhs) { ret = njs_parser_match_arrow_expression(parser, token); if (ret == NJS_OK) { njs_parser_next(parser, njs_parser_arrow_function); return NJS_OK; } else if (ret == NJS_ERROR) { return NJS_ERROR; } } njs_parser_next(parser, njs_parser_conditional_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_assignment_expression_after); } /* * TODO: this function is a crutch. * See NJS_TOKEN_OPEN_PARENTHESIS in njs_parser_primary_expression_test. * and look CoverParenthesizedExpressionAndArrowParameterList in spec. */ static njs_int_t njs_parser_match_arrow_expression(njs_parser_t *parser, njs_lexer_token_t *token) { njs_bool_t rest_parameters; if (token->type == NJS_TOKEN_ASYNC) { token = njs_lexer_peek_token(parser->lexer, token, 1); if (token == NULL) { return NJS_ERROR; } } if (token->type != NJS_TOKEN_OPEN_PARENTHESIS && !njs_lexer_token_is_binding_identifier(token)) { return NJS_DECLINED; } if (njs_lexer_token_is_binding_identifier(token)) { goto arrow; } token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } rest_parameters = 0; while (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { if (rest_parameters) { return NJS_DECLINED; } if (token->type == NJS_TOKEN_ELLIPSIS) { rest_parameters = 1; token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } } if (!njs_lexer_token_is_binding_identifier(token)) { return NJS_DECLINED; } token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } if (token->type == NJS_TOKEN_COMMA) { token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } } } arrow: token = njs_lexer_peek_token(parser->lexer, token, 1); if (token == NULL) { return NJS_ERROR; } if (token->type == NJS_TOKEN_LINE_END) { return NJS_DECLINED; } if (token->type != NJS_TOKEN_ARROW) { return NJS_DECLINED; } return NJS_OK; } static njs_int_t njs_parser_assignment_expression_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { return njs_parser_assignment_operator(parser, token, current); } static njs_int_t njs_parser_assignment_operator(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_vmcode_t operation; njs_token_type_t type; njs_parser_node_t *node; switch (token->type) { case NJS_TOKEN_ASSIGNMENT: njs_thread_log_debug("JS: ="); operation = NJS_VMCODE_MOVE; break; case NJS_TOKEN_MULTIPLICATION_ASSIGNMENT: njs_thread_log_debug("JS: *="); operation = NJS_VMCODE_MULTIPLICATION; break; case NJS_TOKEN_DIVISION_ASSIGNMENT: njs_thread_log_debug("JS: /="); operation = NJS_VMCODE_DIVISION; break; case NJS_TOKEN_REMAINDER_ASSIGNMENT: njs_thread_log_debug("JS: %="); operation = NJS_VMCODE_REMAINDER; break; case NJS_TOKEN_ADDITION_ASSIGNMENT: njs_thread_log_debug("JS: +="); operation = NJS_VMCODE_ADDITION; break; case NJS_TOKEN_SUBTRACTION_ASSIGNMENT: njs_thread_log_debug("JS: -="); operation = NJS_VMCODE_SUBTRACTION; break; case NJS_TOKEN_LEFT_SHIFT_ASSIGNMENT: njs_thread_log_debug("JS: <<="); operation = NJS_VMCODE_LEFT_SHIFT; break; case NJS_TOKEN_RIGHT_SHIFT_ASSIGNMENT: njs_thread_log_debug("JS: >>="); operation = NJS_VMCODE_RIGHT_SHIFT; break; case NJS_TOKEN_UNSIGNED_RIGHT_SHIFT_ASSIGNMENT: njs_thread_log_debug("JS: >>>="); operation = NJS_VMCODE_UNSIGNED_RIGHT_SHIFT; break; case NJS_TOKEN_BITWISE_AND_ASSIGNMENT: njs_thread_log_debug("JS: &="); operation = NJS_VMCODE_BITWISE_AND; break; case NJS_TOKEN_BITWISE_XOR_ASSIGNMENT: njs_thread_log_debug("JS: ^="); operation = NJS_VMCODE_BITWISE_XOR; break; case NJS_TOKEN_BITWISE_OR_ASSIGNMENT: njs_thread_log_debug("JS: |="); operation = NJS_VMCODE_BITWISE_OR; break; case NJS_TOKEN_EXPONENTIATION_ASSIGNMENT: njs_thread_log_debug("JS: **="); operation = NJS_VMCODE_EXPONENTIATION; break; default: return njs_parser_stack_pop(parser); } if (!njs_parser_is_lvalue(parser->node)) { type = parser->node->token_type; if (njs_parser_restricted_identifier(type)) { njs_parser_syntax_error(parser, "Identifier \"%s\" " "is forbidden as left-hand in assignment", (type == NJS_TOKEN_EVAL) ? "eval" : "arguments"); } else { njs_parser_ref_error(parser, "Invalid left-hand side in assignment"); } return NJS_DONE; } node = njs_parser_node_new(parser, token->type); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; node->u.operation = operation; node->left = parser->node; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_assignment_expression); return njs_parser_after(parser, current, node, 1, njs_parser_assignment_operator_after); } static njs_int_t njs_parser_assignment_operator_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->target->right = parser->node; parser->node = parser->target; return njs_parser_stack_pop(parser); } /* * 12.16 Comma Operator ( , ). */ static njs_int_t njs_parser_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_assignment_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_expression_comma); } static njs_int_t njs_parser_expression_comma(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_assignment_expression); return njs_parser_expression_node(parser, token, current, NJS_TOKEN_COMMA, 0, njs_parser_expression_comma); } /* * 13 Statements and Declarations. */ static njs_int_t njs_parser_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; njs_queue_link_t *lnk; njs_parser_stack_entry_t *entry; if (token->type == NJS_TOKEN_END) { lnk = njs_queue_next(njs_queue_first(&parser->stack)); if (lnk == njs_queue_head(&parser->stack)) { return njs_parser_reject(parser); } entry = njs_queue_link_data(lnk, njs_parser_stack_entry_t, link); if (entry->state == njs_parser_check_error_state) { return NJS_DONE; } return njs_parser_reject(parser); } switch (token->type) { case NJS_TOKEN_SEMICOLON: njs_lexer_consume_token(parser->lexer, 1); return njs_parser_stack_pop(parser); case NJS_TOKEN_EXPORT: parser->line = token->line; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_export); return njs_parser_after(parser, current, parser->node, 1, njs_parser_statement_after); case NJS_TOKEN_IMPORT: parser->line = token->line; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_import); return njs_parser_after(parser, current, parser->node, 1, njs_parser_statement_after); default: break; } ret = njs_parser_statement_wo_node(parser, token, current); if (ret != NJS_OK) { return ret; } return njs_parser_after(parser, current, parser->node, 1, njs_parser_statement_after); } static njs_int_t njs_parser_statement_wo_node(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { switch (token->type) { case NJS_TOKEN_OPEN_BRACE: njs_parser_next(parser, njs_parser_block_statement); break; case NJS_TOKEN_VAR: njs_lexer_consume_token(parser->lexer, 1); return njs_parser_variable_statement(parser, token, current); case NJS_TOKEN_SEMICOLON: njs_lexer_consume_token(parser->lexer, 1); return njs_parser_stack_pop(parser); case NJS_TOKEN_IF: njs_parser_next(parser, njs_parser_if_statement); break; /* BreakableStatement */ case NJS_TOKEN_DO: njs_parser_next(parser, njs_parser_iteration_statement_do); break; case NJS_TOKEN_WHILE: njs_parser_next(parser, njs_parser_iteration_statement_while); break; case NJS_TOKEN_FOR: njs_parser_next(parser, njs_parser_iteration_statement_for); break; case NJS_TOKEN_SWITCH: njs_parser_next(parser, njs_parser_switch_statement); break; case NJS_TOKEN_CONTINUE: njs_parser_next(parser, njs_parser_continue_statement); break; case NJS_TOKEN_BREAK: njs_parser_next(parser, njs_parser_break_statement); break; case NJS_TOKEN_RETURN: njs_parser_next(parser, njs_parser_return_statement); break; case NJS_TOKEN_WITH: njs_parser_next(parser, njs_parser_with_statement); break; case NJS_TOKEN_THROW: njs_parser_next(parser, njs_parser_throw_statement); break; case NJS_TOKEN_TRY: njs_parser_next(parser, njs_parser_try_statement); break; case NJS_TOKEN_DEBUGGER: njs_parser_next(parser, njs_parser_debugger_statement); break; case NJS_TOKEN_END: return njs_parser_failed(parser); default: if (njs_lexer_token_is_identifier_reference(token)) { token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } if (token->type == NJS_TOKEN_COLON) { njs_parser_next(parser, njs_parser_labelled_statement); return NJS_OK; } } njs_parser_next(parser, njs_parser_expression_statement); return NJS_OK; } parser->line = token->line; njs_lexer_consume_token(parser->lexer, 1); return NJS_OK; } static njs_int_t njs_parser_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *stmt, *last, *new_node, **child; njs_parser_node_t *node, *top; child = &parser->target; last = *child; new_node = parser->node; if (new_node != NULL) { if (new_node->hoist) { child = &njs_parser_chain_top(parser); while (*child != NULL) { node = *child; if (node->hoist) { break; } child = &node->left; } last = *child; } stmt = njs_parser_node_new(parser, NJS_TOKEN_STATEMENT); if (njs_slow_path(stmt == NULL)) { return NJS_ERROR; } stmt->hoist = new_node->hoist; stmt->left = last; stmt->right = new_node; *child = stmt; top = (child != &parser->target) ? njs_parser_chain_top(parser) : stmt; parser->node = top; njs_parser_chain_top_set(parser, top); } return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_declaration(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; ret = njs_parser_hoistable_declaration(parser, token, current); if (ret == NJS_OK) { return NJS_OK; } switch (token->type) { case NJS_TOKEN_CLASS: njs_parser_next(parser, njs_parser_class_declaration); return NJS_OK; case NJS_TOKEN_LET: case NJS_TOKEN_CONST: token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } switch (token->type) { case NJS_TOKEN_OPEN_BRACE: case NJS_TOKEN_OPEN_BRACKET: njs_parser_next(parser, njs_parser_lexical_declaration); break; default: if (njs_lexer_token_is_reserved(token)) { njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_failed_state); return NJS_OK; } if (njs_lexer_token_is_binding_identifier(token)) { njs_parser_next(parser, njs_parser_lexical_declaration); break; } return NJS_DECLINED; } break; default: return NJS_DECLINED; } return njs_parser_after(parser, current, parser->node, 1, njs_parser_statement_after); } static njs_int_t njs_parser_hoistable_declaration(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; ret = njs_parser_function_or_generator(parser, token, current); if (ret == NJS_OK) { return NJS_OK; } ret = njs_parser_async_function_or_generator(parser, token, current); if (ret == NJS_OK) { return NJS_OK; } return NJS_DECLINED; } /* * 13.2 Block. */ static njs_int_t njs_parser_block_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { void *target; njs_int_t ret; ret = njs_parser_scope_begin(parser, NJS_SCOPE_BLOCK, 0); if (ret != NJS_OK) { return NJS_ERROR; } target = (void *) (uintptr_t) parser->line; parser->node = NULL; if (token->type == NJS_TOKEN_CLOSE_BRACE) { parser->target = target; njs_parser_next(parser, njs_parser_block_statement_close_brace); return NJS_OK; } njs_parser_next(parser, njs_parser_statement_list); return njs_parser_after(parser, current, target, 0, njs_parser_block_statement_close_brace); } static njs_int_t njs_parser_block_statement_open_brace(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_OPEN_BRACE) { return njs_parser_failed(parser); } parser->line = token->line; njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } return njs_parser_block_statement(parser, token, current); } static njs_int_t njs_parser_block_statement_close_brace(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node; if (parser->ret != NJS_OK) { return njs_parser_failed(parser); } if (token->type != NJS_TOKEN_CLOSE_BRACE) { return njs_parser_failed(parser); } node = njs_parser_node_new(parser, NJS_TOKEN_BLOCK); if (node == NULL) { return NJS_ERROR; } node->token_line = (uint32_t) (uintptr_t) parser->target; node->left = parser->node; node->right = NULL; parser->target = NULL; parser->node = node; njs_parser_scope_end(parser); njs_lexer_consume_token(parser->lexer, 1); return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_statement_list(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_statement_list_item); return njs_parser_after(parser, current, NULL, 1, njs_parser_statement_list_next); } static njs_int_t njs_parser_statement_list_next(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (parser->ret != NJS_OK) { if (token->type != NJS_TOKEN_CLOSE_BRACE) { parser->node = parser->target; (void) njs_parser_stack_pop(parser); return parser->ret; } return njs_parser_failed(parser); } if (token->type == NJS_TOKEN_CLOSE_BRACE) { return njs_parser_stack_pop(parser); } njs_parser_next(parser, njs_parser_statement_list_item); return njs_parser_after(parser, current, parser->node, 0, njs_parser_statement_list_next); } static njs_int_t njs_parser_statement_list_item(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; ret = njs_parser_declaration(parser, token, current); if (ret == NJS_OK) { return NJS_OK; } njs_parser_next(parser, njs_parser_statement); return NJS_OK; } /* * 13.3.1 Let and Const Declarations */ static njs_int_t njs_parser_lexical_declaration(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->var_type = (token->type == NJS_TOKEN_LET) ? NJS_VARIABLE_LET : NJS_VARIABLE_CONST; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_variable_declaration_list); return njs_parser_after(parser, current, NULL, 1, njs_parser_semicolon); } /* * 13.3.2 Variable Statement */ static njs_int_t njs_parser_variable_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->var_type = NJS_VARIABLE_VAR; njs_parser_next(parser, njs_parser_variable_declaration_list); return njs_parser_after(parser, current, NULL, 1, njs_parser_semicolon); } static njs_int_t njs_parser_variable_declaration_list(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_next(parser, njs_parser_variable_declaration); return njs_parser_after(parser, current, NULL, 1, njs_parser_variable_declaration_list_next); } static njs_int_t njs_parser_variable_declaration_list_next(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node; if (parser->target != NULL) { parser->node->left = parser->target; } if (token->type != NJS_TOKEN_COMMA) { return njs_parser_stack_pop(parser); } njs_lexer_consume_token(parser->lexer, 1); node = parser->node; parser->node = NULL; njs_parser_next(parser, njs_parser_variable_declaration); return njs_parser_after(parser, current, node, 1, njs_parser_variable_declaration_list_next); } static njs_int_t njs_parser_variable_declaration(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; njs_variable_t *var; njs_token_type_t type; njs_parser_node_t *name; ret = njs_parser_binding_pattern(parser, token, current); if (ret == NJS_OK) { return njs_parser_after(parser, current, NULL, 1, njs_parser_initializer); } if (!njs_lexer_token_is_binding_identifier(token)) { return njs_parser_failed(parser); } if (njs_parser_restricted_identifier(token->type)) { njs_parser_syntax_error(parser, "Identifier \"%V\" is forbidden in" " var declaration", &token->text); return NJS_DONE; } name = njs_parser_variable_node(parser, token->unique_id, parser->var_type, &var); if (name == NULL) { return NJS_ERROR; } if (var->self) { var->type = parser->var_type; var->self = 0; } name->token_line = token->line; parser->node = name; njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } switch (parser->var_type) { case NJS_VARIABLE_LET: type = NJS_TOKEN_LET; break; case NJS_VARIABLE_CONST: type = NJS_TOKEN_CONST; break; default: type = NJS_TOKEN_VAR; break; } ret = njs_parser_initializer_assign(parser, type); if (ret != NJS_OK) { return ret; } parser->node->token_line = token->line; if (token->type == NJS_TOKEN_ASSIGNMENT) { njs_parser_next(parser, njs_parser_initializer); return NJS_OK; } parser->target = parser->node; parser->node = NULL; njs_parser_next(parser, njs_parser_initializer_after); return NJS_OK; } /* * 13.3.3 Destructuring Binding Patterns. */ static njs_int_t njs_parser_binding_pattern(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type == NJS_TOKEN_OPEN_BRACE) { njs_parser_next(parser, njs_parser_object_binding_pattern); } else if (token->type == NJS_TOKEN_OPEN_BRACKET) { njs_parser_next(parser, njs_parser_array_binding_pattern); } else { return NJS_DECLINED; } njs_lexer_consume_token(parser->lexer, 1); return NJS_OK; } static njs_int_t njs_parser_object_binding_pattern(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { return njs_parser_not_supported(parser, token); } static njs_int_t njs_parser_array_binding_pattern(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { return njs_parser_not_supported(parser, token); } /* * 13.5 Expression Statement. */ static njs_int_t njs_parser_expression_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_token_type_t type; njs_lexer_token_t *next; switch (token->type) { case NJS_TOKEN_FUNCTION: njs_parser_syntax_error(parser, "Functions can only be declared " "at top level or inside a block"); return NJS_DONE; case NJS_TOKEN_CLASS: njs_parser_syntax_error(parser, "Class can only be declared " "at top level or inside a block"); return NJS_DONE; case NJS_TOKEN_OPEN_BRACE: return njs_parser_reject(parser); case NJS_TOKEN_ASYNC: next = njs_lexer_peek_token(parser->lexer, token, 1); if (next == NULL) { return NJS_ERROR; } if (next->type == NJS_TOKEN_FUNCTION) { return njs_parser_not_supported(parser, token); } break; case NJS_TOKEN_CONST: case NJS_TOKEN_LET: type = token->type; token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } if (token->type == NJS_TOKEN_NAME) { njs_parser_syntax_error(parser, "%s declaration cannot appear " "in a single-statement context", (type == NJS_TOKEN_CONST ? "const" : "let" )); return NJS_DONE; } if (token->type == NJS_TOKEN_OPEN_BRACKET) { return njs_parser_failed(parser); } break; default: break; } parser->node = NULL; njs_parser_next(parser, njs_parser_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_expression_statement_after); } static njs_int_t njs_parser_expression_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (njs_parser_expect_semicolon(parser, token) != NJS_OK) { return njs_parser_failed(parser); } return njs_parser_stack_pop(parser); } /* * 13.6 if Statement. */ static njs_int_t njs_parser_if_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; njs_parser_node_t *node; if (token->type != NJS_TOKEN_OPEN_PARENTHESIS) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); node = njs_parser_node_new(parser, NJS_TOKEN_IF); if (node == NULL) { return NJS_ERROR; } node->token_line = parser->line; parser->node = NULL; njs_parser_next(parser, njs_parser_expression); ret = njs_parser_after(parser, current, node, 1, njs_parser_if_close_parenthesis); if (ret != NJS_OK) { return ret; } ret = njs_parser_after(parser, current, NULL, 1, njs_parser_statement_wo_node); if (ret != NJS_OK) { return ret; } return njs_parser_after(parser, current, node, 1, njs_parser_else_statement); } static njs_int_t njs_parser_if_close_parenthesis(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); parser->target->left = parser->node; parser->node = NULL; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_else_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node; parser->target->right = parser->node; parser->node = NULL; if (token->type == NJS_TOKEN_ELSE) { node = njs_parser_node_new(parser, NJS_TOKEN_BRANCHING); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; node->left = parser->target->right; parser->target->right = node; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_statement_wo_node); return njs_parser_after(parser, current, parser->target, 1, njs_parser_else_statement_after); } parser->node = parser->target; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_else_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->target->right->right = parser->node; parser->node = parser->target; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_iteration_statement_do(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node; node = njs_parser_node_new(parser, NJS_TOKEN_DO); if (node == NULL) { return NJS_ERROR; } node->token_line = parser->line; parser->node = NULL; njs_parser_next(parser, njs_parser_statement_wo_node); return njs_parser_after(parser, current, node, 1, njs_parser_iteration_statement_do_while); } static njs_int_t njs_parser_iteration_statement_do_while(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_WHILE) { return njs_parser_failed(parser); } parser->target->left = parser->node; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_expression_parenthesis); return njs_parser_after(parser, current, parser->target, 1, njs_parser_do_while_semicolon); } static njs_int_t njs_parser_do_while_semicolon(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (parser->strict_semicolon) { return njs_parser_failed(parser); } parser->target->right = parser->node; parser->node = parser->target; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_iteration_statement_while(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node; node = njs_parser_node_new(parser, NJS_TOKEN_WHILE); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; njs_parser_next(parser, njs_parser_expression_parenthesis); return njs_parser_after(parser, current, node, 1, njs_parser_while_statement); } static njs_int_t njs_parser_while_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->target->right = parser->node; parser->node = NULL; njs_parser_next(parser, njs_parser_statement_wo_node); return njs_parser_after(parser, current, parser->target, 1, njs_parser_while_after); } static njs_int_t njs_parser_while_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->target->left = parser->node; parser->node = parser->target; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_iteration_statement_for(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; if (token->type == NJS_TOKEN_OPEN_PARENTHESIS) { njs_lexer_consume_token(parser->lexer, 1); ret = njs_parser_scope_begin(parser, NJS_SCOPE_BLOCK, 0); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_parser_next(parser, njs_parser_iteration_statement_for_map); return njs_parser_after(parser, current, (void *) (uintptr_t) parser->line, 1, njs_parser_iteration_statement_for_end); } if (token->type == NJS_TOKEN_AWAIT) { return njs_parser_not_supported(parser, token); } return njs_parser_failed(parser); } static njs_int_t njs_parser_for_expression_map_reparse(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (parser->ret != NJS_OK && parser->node != NULL) { return njs_parser_failed(parser); } if (parser->node == NULL) { njs_lexer_in_fail_set(parser->lexer, 1); njs_parser_next(parser, njs_parser_expression); return NJS_OK; } return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_for_expression_map_continue(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t operation; njs_str_t *text; njs_parser_node_t *node; if (token->type != NJS_TOKEN_IN) { njs_lexer_in_fail_set(parser->lexer, 1); /* Continue parsing of expr1 in "for (expr1;[expr2];[expr3])". */ njs_parser_next(parser, njs_parser_expression_continue_op); /* * Here we pass not a node, but a token, this is important. * This is necessary for correct error output. */ text = njs_mp_alloc(parser->vm->mem_pool, sizeof(njs_str_t)); if (text == NULL) { return NJS_ERROR; } *text = token->text; return njs_parser_after(parser, current, text, 1, njs_parser_for_var_in_of_expression); } else { /* for-in */ if (parser->node->token_type != NJS_TOKEN_NAME && parser->node->token_type != NJS_TOKEN_PROPERTY) { text = (njs_str_t *) parser->target; njs_parser_ref_error(parser, "Invalid left-hand side \"%V\" " "in for-in statement", text); njs_mp_free(parser->vm->mem_pool, text); return NJS_DONE; } operation = NJS_VMCODE_PROPERTY_IN; node = njs_parser_node_new(parser, token->type); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; node->u.operation = operation; node->left = parser->node; node->left->dest = node; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_expression); return njs_parser_after(parser, current, node, 0, njs_parser_for_in_statement_statement); } } static njs_int_t njs_parser_after_expr(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->target->right = parser->node; parser->node = parser->target; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_comma_expression_comma(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node; if (parser->target != NULL) { parser->target->right = parser->node; parser->target->right->dest = parser->target; parser->node = parser->target; } if (token->type != NJS_TOKEN_COMMA) { return njs_parser_stack_pop(parser); } node = njs_parser_node_new(parser, NJS_TOKEN_COMMA); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; node->u.operation = 0; node->left = parser->node; node->left->dest = node; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_expression); return njs_parser_after(parser, current, node, 1, njs_parser_after_expr); } static njs_int_t njs_parser_expression_continue_op(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type == NJS_TOKEN_CONDITIONAL) { njs_parser_next(parser, njs_parser_conditional_question_mark); return njs_parser_after(parser, current, NULL, 0, njs_parser_expression_continue_assign_comma); } else { parser->target = NULL; parser->use_lhs = 1; njs_parser_next(parser, njs_parser_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_comma_expression_comma); } } static njs_int_t njs_parser_expression_continue_assign_comma(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (parser->ret != NJS_OK) { return njs_parser_failed(parser); } njs_parser_next(parser, njs_parser_assignment_expression_after); return njs_parser_after(parser, current, NULL, 1, njs_parser_expression_comma); } static njs_int_t njs_parser_iteration_statement_for_map(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; njs_str_t *text; njs_token_type_t token_type; /* * "var" ";" ? ";" ? ")" * * "var" "in" ")" * "var" "of" ")" * "in" ")" * "of" ")" * ? ";" ? ";" ? ")" * ? ";" ? ")" * "in" ")" * "of" ")" */ parser->node = NULL; switch (token->type) { case NJS_TOKEN_SEMICOLON: token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } if (token->type != NJS_TOKEN_SEMICOLON) { njs_lexer_consume_token(parser->lexer, 1); parser->target = NULL; njs_parser_next(parser, njs_parser_expression); return njs_parser_after(parser, current, NULL, 1, njs_parser_for_expression); } parser->node = NULL; parser->target = NULL; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_for_expression); return NJS_OK; case NJS_TOKEN_VAR: case NJS_TOKEN_LET: case NJS_TOKEN_CONST: token_type = token->type; token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } njs_lexer_consume_token(parser->lexer, 1); ret = njs_parser_for_var_binding_or_var_list(parser, token, current, token_type); if (ret != NJS_OK) { if (ret == NJS_DONE) { return NJS_OK; } return ret; } goto expression_after; case NJS_TOKEN_AWAIT: njs_parser_next(parser, njs_parser_expression); goto expression_after; default: ret = njs_parser_match_arrow_expression(parser, token); if (ret == NJS_OK) { parser->target = NULL; njs_parser_next(parser, njs_parser_expression); goto expression_after; } else if (ret == NJS_ERROR) { return NJS_ERROR; } parser->target = NULL; njs_parser_next(parser, njs_parser_left_hand_side_expression); /* * Here we pass not a node, but a token, this is important. * This is necessary for correct error output. */ text = njs_mp_alloc(parser->vm->mem_pool, sizeof(njs_str_t)); if (text == NULL) { return NJS_ERROR; } *text = token->text; ret = njs_parser_after(parser, current, text, 0, njs_parser_for_expression_map_reparse); if (ret != NJS_OK) { return NJS_ERROR; } return njs_parser_after(parser, current, text, 1, njs_parser_for_expression_map_continue); } expression_after: /* * Here we pass not a node, but a token, this is important. * This is necessary for correct error output. */ text = njs_mp_alloc(parser->vm->mem_pool, sizeof(njs_str_t)); if (text == NULL) { return NJS_ERROR; } *text = token->text; return njs_parser_after(parser, current, text, 1, njs_parser_for_var_in_of_expression); } static njs_int_t njs_parser_for_var_binding_or_var_list(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current, njs_token_type_t token_type) { njs_int_t ret; njs_lexer_token_t *next; njs_parser_node_t *node, *var, *node_type, *statement; njs_variable_type_t type; switch (token_type) { case NJS_TOKEN_LET: type = NJS_VARIABLE_LET; break; case NJS_TOKEN_CONST: type = NJS_VARIABLE_CONST; break; default: type = NJS_VARIABLE_VAR; break; } switch (token->type) { /* BindingPattern */ case NJS_TOKEN_OPEN_BRACE: njs_parser_next(parser, njs_parser_object_binding_pattern); return NJS_DONE; case NJS_TOKEN_OPEN_BRACKET: njs_parser_next(parser, njs_parser_array_binding_pattern); return NJS_DONE; default: if (njs_lexer_token_is_binding_identifier(token)) { if (njs_parser_restricted_identifier(token->type)) { njs_parser_syntax_error(parser, "Identifier \"%V\" is forbidden" " in var declaration", &token->text); return NJS_DONE; } next = njs_lexer_peek_token(parser->lexer, token, 0); if (next == NULL) { return NJS_ERROR; } if (next->type != NJS_TOKEN_IN) { parser->var_type = type; njs_lexer_in_fail_set(parser->lexer, 1); njs_parser_next(parser, njs_parser_variable_declaration_list); return NJS_OK; } statement = njs_parser_node_new(parser, NJS_TOKEN_STATEMENT); if (njs_slow_path(statement == NULL)) { return NJS_ERROR; } node_type = njs_parser_node_new(parser, token_type); if (njs_slow_path(node_type == NULL)) { return NJS_ERROR; } var = njs_parser_variable_node(parser, token->unique_id, type, NULL); if (var == NULL) { return NJS_ERROR; } node_type->token_line = token->line; var->token_line = token->line; statement->right = node_type; node_type->left = var; parser->node = NULL; node = njs_parser_node_new(parser, NJS_TOKEN_IN); if (node == NULL) { return NJS_ERROR; } node->token_line = next->line; node->left = statement; njs_parser_next(parser, njs_parser_expression); ret = njs_parser_after(parser, current, node, 1, njs_parser_for_var_in_statement); if (ret != NJS_OK) { return NJS_ERROR; } njs_lexer_consume_token(parser->lexer, 2); return NJS_DONE; } else { return njs_parser_failed(parser); } } } static njs_int_t njs_parser_for_var_in_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); parser->target->right = parser->node; parser->node = NULL; njs_parser_next(parser, njs_parser_statement_wo_node); return njs_parser_after(parser, current, parser->target, 1, njs_parser_for_var_in_statement_after); } static njs_int_t njs_parser_for_var_in_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *foreach; foreach = njs_parser_node_new(parser, NJS_TOKEN_FOR_IN); if (foreach == NULL) { return NJS_ERROR; } foreach->left = parser->target; foreach->right = parser->node; parser->node = foreach; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_for_var_in_of_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_str_t *text; njs_parser_node_t *node; /* * ";" ? ";" ? ")" * "in" ")" * "of" ")" */ if (token->type != NJS_TOKEN_SEMICOLON && token->type != NJS_TOKEN_CLOSE_PARENTHESIS && parser->node != NULL && parser->node->token_type == NJS_TOKEN_IN) { node = parser->node->left; if (node->token_type != NJS_TOKEN_NAME && node->token_type != NJS_TOKEN_PROPERTY) { text = (njs_str_t *) parser->target; njs_parser_ref_error(parser, "Invalid left-hand side \"%V\" " "in for-in statement", text); njs_mp_free(parser->vm->mem_pool, text); return NJS_DONE; } njs_parser_next(parser, njs_parser_for_in_statement); return NJS_OK; } if (parser->target != NULL) { text = (njs_str_t *) parser->target; njs_mp_free(parser->vm->mem_pool, text); } switch (token->type) { case NJS_TOKEN_SEMICOLON: njs_lexer_in_fail_set(parser->lexer, 0); token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } node = parser->node; parser->node = NULL; if (token->type != NJS_TOKEN_SEMICOLON) { njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_expression); return njs_parser_after(parser, current, node, 1, njs_parser_for_expression); } parser->target = node; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_for_expression); return NJS_OK; case NJS_TOKEN_OF: return njs_parser_not_supported(parser, token); default: return njs_parser_failed(parser); } } static njs_int_t njs_parser_for_in_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node, *forin; if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); node = parser->node; if (node->right != NULL && node->right->token_type == NJS_TOKEN_VAR) { return NJS_ERROR; } forin = njs_parser_node_new(parser, NJS_TOKEN_FOR_IN); if (forin == NULL) { return NJS_ERROR; } forin->left = parser->node; parser->node = NULL; njs_parser_next(parser, njs_parser_statement_wo_node); return njs_parser_after(parser, current, forin, 1, njs_parser_for_in_statement_after); } static njs_int_t njs_parser_for_in_statement_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *forin; if (parser->ret != NJS_OK || token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); parser->target->right = parser->node; forin = njs_parser_node_new(parser, NJS_TOKEN_FOR_IN); if (forin == NULL) { return NJS_ERROR; } forin->left = parser->target; parser->node = NULL; njs_parser_next(parser, njs_parser_statement_wo_node); return njs_parser_after(parser, current, forin, 1, njs_parser_for_in_statement_after); } static njs_int_t njs_parser_for_in_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->target->right = parser->node; parser->node = parser->target; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_for_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *body, *cond, *for_node; if (token->type != NJS_TOKEN_SEMICOLON) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); for_node = njs_parser_node_new(parser, NJS_TOKEN_FOR); if (for_node == NULL) { return NJS_ERROR; } cond = njs_parser_node_new(parser, 0); if (cond == NULL) { return NJS_ERROR; } body = njs_parser_node_new(parser, 0); if (body == NULL) { return NJS_ERROR; } for_node->left = parser->target; for_node->right = cond; cond->left = parser->node; cond->right = body; parser->node = NULL; token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } if (token->type == NJS_TOKEN_CLOSE_PARENTHESIS) { parser->target = for_node; njs_parser_next(parser, njs_parser_for_expression_end); return NJS_OK; } njs_parser_next(parser, njs_parser_expression); return njs_parser_after(parser, current, for_node, 1, njs_parser_for_expression_end); } static njs_int_t njs_parser_for_expression_end(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *body; if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); body = parser->target->right->right; body->right = parser->node; parser->node = NULL; njs_parser_next(parser, njs_parser_statement_wo_node); return njs_parser_after(parser, current, parser->target, 1, njs_parser_for_end); } static njs_int_t njs_parser_for_end(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *body; body = parser->target->right->right; body->left = parser->node; parser->node = parser->target; return njs_parser_stack_pop(parser); } /* * 13.8 continue Statement. */ static njs_int_t njs_parser_continue_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { return njs_parser_break_continue(parser, token, NJS_TOKEN_CONTINUE); } /* * 13.9 break Statement. */ static njs_int_t njs_parser_break_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { return njs_parser_break_continue(parser, token, NJS_TOKEN_BREAK); } static njs_int_t njs_parser_break_continue(njs_parser_t *parser, njs_lexer_token_t *token, njs_token_type_t type) { njs_int_t ret; parser->node = njs_parser_node_new(parser, type); if (parser->node == NULL) { return NJS_ERROR; } parser->node->token_line = parser->line; switch (token->type) { case NJS_TOKEN_SEMICOLON: break; case NJS_TOKEN_LINE_END: return njs_parser_failed(parser); default: if (njs_lexer_token_is_label_identifier(token)) { if (parser->lexer->prev_type == NJS_TOKEN_LINE_END) { return njs_parser_stack_pop(parser); } if (njs_label_find(parser->vm, parser->scope, token->unique_id) == NULL) { njs_parser_syntax_error(parser, "Undefined label \"%V\"", &token->text); return NJS_DONE; } ret = njs_name_copy(parser->vm, &parser->node->name, &token->text); if (ret != NJS_OK) { return NJS_ERROR; } break; } if (parser->strict_semicolon || (token->type != NJS_TOKEN_END && token->type != NJS_TOKEN_CLOSE_BRACE && parser->lexer->prev_type != NJS_TOKEN_LINE_END)) { return njs_parser_failed(parser); } return njs_parser_stack_pop(parser); } njs_lexer_consume_token(parser->lexer, 1); return njs_parser_stack_pop(parser); } /* * 13.10 return Statement. */ static njs_int_t njs_parser_return_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node; njs_parser_scope_t *scope; for (scope = parser->scope; scope != NULL; scope = scope->parent) { if (scope->type == NJS_SCOPE_FUNCTION) { break; } if (scope->parent == NULL) { njs_parser_syntax_error(parser, "Illegal return statement"); return NJS_ERROR; } } node = njs_parser_node_new(parser, NJS_TOKEN_RETURN); if (njs_slow_path(node == NULL)) { return NJS_ERROR; } node->token_line = parser->line; switch (token->type) { case NJS_TOKEN_SEMICOLON: njs_lexer_consume_token(parser->lexer, 1); break; case NJS_TOKEN_LINE_END: return njs_parser_failed(parser); default: if (!parser->strict_semicolon && parser->lexer->prev_type == NJS_TOKEN_LINE_END) { break; } parser->node = NULL; if (token->type != NJS_TOKEN_CLOSE_BRACE) { njs_parser_next(parser, njs_parser_expression); return njs_parser_after(parser, current, node, 0, njs_parser_return_statement_after); } } parser->node = node; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_return_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (parser->ret != NJS_OK) { njs_parser_syntax_error(parser, "Unexpected token \"%V\"", &token->text); return NJS_DONE; } if (njs_parser_expect_semicolon(parser, token) != NJS_OK) { return njs_parser_failed(parser); } parser->target->right = parser->node; parser->node = parser->target; return njs_parser_stack_pop(parser); } /* * 13.11 with Statement */ static njs_int_t njs_parser_with_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { return njs_parser_not_supported(parser, token); } /* * 13.12 switch Statement */ static njs_int_t njs_parser_switch_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; njs_parser_node_t *swtch; swtch = njs_parser_node_new(parser, NJS_TOKEN_SWITCH); if (swtch == NULL) { return NJS_ERROR; } swtch->token_line = parser->line; njs_parser_next(parser, njs_parser_expression_parenthesis); ret = njs_parser_after(parser, current, swtch, 1, njs_parser_switch_block); if (ret != NJS_OK) { return ret; } return njs_parser_after(parser, current, swtch, 1, njs_parser_switch_statement_after); } static njs_int_t njs_parser_switch_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->node = parser->target; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_switch_block(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; if (token->type != NJS_TOKEN_OPEN_BRACE) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); parser->target->left = parser->node; ret = njs_parser_scope_begin(parser, NJS_SCOPE_BLOCK, 0); if (ret != NJS_OK) { return NJS_ERROR; } njs_parser_next(parser, njs_parser_switch_case); return njs_parser_after(parser, current, NULL, 1, njs_parser_switch_block_after); } static njs_int_t njs_parser_switch_block_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_scope_end(parser); return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_switch_case(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { return njs_parser_switch_case_def(parser, token, current, 1); } static njs_int_t njs_parser_switch_case_wo_def(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { return njs_parser_switch_case_def(parser, token, current, 0); } static njs_int_t njs_parser_switch_case_def(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current, njs_bool_t with_default) { njs_parser_node_t *node, *branch; node = njs_parser_node_new(parser, 0); if (node == NULL) { return NJS_ERROR; } parser->node = NULL; switch (token->type) { case NJS_TOKEN_CASE: branch = njs_parser_node_new(parser, 0); if (branch == NULL) { return NJS_ERROR; } branch->token_line = token->line; branch->right = node; njs_parser_next(parser, njs_parser_expression); njs_lexer_consume_token(parser->lexer, 1); if (parser->target->token_type == NJS_TOKEN_SWITCH) { parser->target->right = branch; } else { parser->target->left = branch; } if (with_default) { return njs_parser_after(parser, current, branch, 1, njs_parser_switch_case_after); } else { return njs_parser_after(parser, current, branch, 1, njs_parser_switch_case_after_wo_def); } case NJS_TOKEN_DEFAULT: if (!with_default) { njs_parser_syntax_error(parser, "More than one default clause " "in switch statement"); return NJS_DONE; } if (parser->target->token_type == NJS_TOKEN_SWITCH) { parser->target->right = node; } else { parser->target->left = node; } node->token_line = token->line; node->token_type = NJS_TOKEN_DEFAULT; parser->target = node; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_switch_case_after_wo_def); break; case NJS_TOKEN_CLOSE_BRACE: njs_lexer_consume_token(parser->lexer, 1); return njs_parser_stack_pop(parser); default: return njs_parser_failed(parser); } return NJS_OK; } static njs_int_t njs_parser_switch_case_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_COLON) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); parser->target->right->left = parser->node; parser->node = NULL; token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } switch (token->type) { case NJS_TOKEN_CASE: case NJS_TOKEN_DEFAULT: case NJS_TOKEN_CLOSE_BRACE: njs_parser_next(parser, njs_parser_switch_case_block); return NJS_OK; default: njs_parser_next(parser, njs_parser_statement_list); break; } return njs_parser_after(parser, current, parser->target, 1, njs_parser_switch_case_block); } static njs_int_t njs_parser_switch_case_block(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->target->right->right = parser->node; if (parser->ret != NJS_OK && parser->target->scope != parser->scope) { return njs_parser_failed(parser); } njs_parser_next(parser, njs_parser_switch_case); return NJS_OK; } static njs_int_t njs_parser_switch_case_after_wo_def(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_COLON) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); if (parser->target->right != NULL) { parser->target->right->left = parser->node; } parser->node = NULL; token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } switch (token->type) { case NJS_TOKEN_CASE: case NJS_TOKEN_DEFAULT: case NJS_TOKEN_CLOSE_BRACE: njs_parser_next(parser, njs_parser_switch_case_block_wo_def); return NJS_OK; default: njs_parser_next(parser, njs_parser_statement_list); break; } return njs_parser_after(parser, current, parser->target, 1, njs_parser_switch_case_block_wo_def); } static njs_int_t njs_parser_switch_case_block_wo_def(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (parser->target->right != NULL) { parser->target->right->right = parser->node; } else { parser->target->right = parser->node; } if (parser->ret != NJS_OK && parser->target->scope != parser->scope) { return njs_parser_failed(parser); } njs_parser_next(parser, njs_parser_switch_case_wo_def); return NJS_OK; } /* * 13.13 Labelled Statements */ static njs_int_t njs_parser_labelled_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { uintptr_t unique_id; njs_variable_t *label; unique_id = token->unique_id; label = njs_label_find(parser->vm, parser->scope, unique_id); if (label != NULL) { njs_parser_syntax_error(parser, "Label \"%V\" " "has already been declared", &token->text); return NJS_DONE; } label = njs_label_add(parser->vm, parser->scope, unique_id); if (label == NULL) { return NJS_ERROR; } njs_lexer_consume_token(parser->lexer, 2); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } parser->node = NULL; if (token->type == NJS_TOKEN_FUNCTION) { njs_syntax_error(parser->vm, "In strict mode code, functions can only " "be declared at top level or inside a block."); return NJS_DONE; } else { njs_parser_next(parser, njs_parser_statement_wo_node); } return njs_parser_after(parser, current, (void *) unique_id, 1, njs_parser_labelled_statement_after); } static njs_int_t njs_parser_labelled_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; uintptr_t unique_id; njs_parser_node_t *node; const njs_lexer_entry_t *entry; node = parser->node; if (node == NULL) { node = njs_parser_node_new(parser, NJS_TOKEN_BLOCK); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; parser->node = node; } unique_id = (uintptr_t) parser->target; entry = (const njs_lexer_entry_t *) unique_id; ret = njs_name_copy(parser->vm, &parser->node->name, &entry->name); if (ret != NJS_OK) { return NJS_ERROR; } ret = njs_label_remove(parser->vm, parser->scope, unique_id); if (ret != NJS_OK) { return NJS_ERROR; } return njs_parser_stack_pop(parser); } /* * 13.14 throw Statement */ static njs_int_t njs_parser_throw_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node; node = njs_parser_node_new(parser, NJS_TOKEN_THROW); if (node == NULL) { return NJS_ERROR; } node->token_line = parser->line; if (parser->lexer->prev_type == NJS_TOKEN_LINE_END) { njs_parser_syntax_error(parser, "Illegal newline after throw"); return NJS_DONE; } parser->node = NULL; njs_parser_next(parser, njs_parser_expression); return njs_parser_after(parser, current, node, 1, njs_parser_throw_statement_after); } static njs_int_t njs_parser_throw_statement_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (parser->ret != NJS_OK) { parser->node = parser->target; return njs_parser_reject(parser); } if (njs_parser_expect_semicolon(parser, token) != NJS_OK) { return njs_parser_failed(parser); } parser->target->right = parser->node; parser->node = parser->target; return njs_parser_stack_pop(parser); } /* * 13.15 try Statement. */ static njs_int_t njs_parser_try_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *try; try = njs_parser_node_new(parser, NJS_TOKEN_TRY); if (try == NULL) { return NJS_ERROR; } try->token_line = parser->line; parser->node = NULL; njs_parser_next(parser, njs_parser_block_statement_open_brace); return njs_parser_after(parser, current, try, 1, njs_parser_catch_or_finally); } static njs_int_t njs_parser_catch_or_finally(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; njs_parser_node_t *catch, *node, *try; try = parser->target; try->left = parser->node; if (token->type == NJS_TOKEN_FINALLY) { node = njs_parser_node_new(parser, NJS_TOKEN_FINALLY); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; if (try->right != NULL) { node->left = try->right; } try->right = node; parser->node = NULL; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_block_statement_open_brace); return njs_parser_after(parser, current, try, 0, njs_parser_catch_finally); } else if (token->type != NJS_TOKEN_CATCH) { njs_parser_syntax_error(parser, "Missing catch or finally after try"); return NJS_DONE; } catch = njs_parser_node_new(parser, NJS_TOKEN_CATCH); if (catch == NULL) { return NJS_ERROR; } catch->token_line = token->line; njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } ret = njs_parser_scope_begin(parser, NJS_SCOPE_BLOCK, 0); if (ret != NJS_OK) { return NJS_ERROR; } if (token->type != NJS_TOKEN_OPEN_PARENTHESIS) { parser->node = NULL; njs_parser_next(parser, njs_parser_block_statement_open_brace); try->right = catch; /* TODO: it is necessary to change the generator. */ return njs_parser_not_supported(parser, token); /* * return njs_parser_after(parser, current, parser->target, 0, * njs_parser_catch_after); */ } njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } try->right = catch; if (njs_lexer_token_is_binding_identifier(token)) { node = njs_parser_variable_node(parser, token->unique_id, NJS_VARIABLE_CATCH, NULL); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; catch->left = node; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_catch_parenthesis); return NJS_OK; } if (token->type != NJS_TOKEN_OPEN_BRACE) { return njs_parser_failed(parser); } /* * njs_parser_next(parser, njs_parser_block_statement_open_brace); * * return njs_parser_after(parser, current, try, 1, * njs_parser_catch_parenthesis); */ return njs_parser_not_supported(parser, token); } static njs_int_t njs_parser_catch_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node; parser->target->right->right = parser->node; if (token->type == NJS_TOKEN_FINALLY) { node = njs_parser_node_new(parser, NJS_TOKEN_FINALLY); if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; if (parser->target->right != NULL) { node->left = parser->target->right; } parser->target->right = node; parser->node = NULL; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_block_statement_open_brace); return njs_parser_after(parser, current, parser->target, 1, njs_parser_catch_finally); } parser->node = parser->target; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_catch_parenthesis(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); parser->target->right->right = parser->node; parser->node = NULL; njs_parser_next(parser, njs_parser_catch_statement_open_brace); return njs_parser_after(parser, current, parser->target, 1, njs_parser_catch_after); } static njs_int_t njs_parser_catch_statement_open_brace(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { void *target; if (token->type != NJS_TOKEN_OPEN_BRACE) { return njs_parser_failed(parser); } parser->line = token->line; njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } target = (void *) (uintptr_t) parser->line; parser->node = NULL; if (token->type == NJS_TOKEN_CLOSE_BRACE) { parser->target = target; njs_parser_next(parser, njs_parser_block_statement_close_brace); return NJS_OK; } njs_parser_next(parser, njs_parser_statement_list); return njs_parser_after(parser, current, target, 0, njs_parser_block_statement_close_brace); } static njs_int_t njs_parser_catch_finally(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (parser->ret != NJS_OK) { return njs_parser_failed(parser); } parser->target->right->right = parser->node; parser->node = parser->target; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_debugger_statement(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->node = njs_parser_node_new(parser, NJS_TOKEN_DEBUGGER); if (parser->node == NULL) { return NJS_ERROR; } parser->node->token_line = parser->line; if (token->type != NJS_TOKEN_SEMICOLON && token->type != NJS_TOKEN_END) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); return njs_parser_stack_pop(parser); } /* * 14.1 Function Definitions. */ static njs_int_t njs_parser_function_declaration(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; uintptr_t unique_id; njs_bool_t async; njs_variable_t *var; njs_parser_node_t *node; if (!njs_lexer_token_is_binding_identifier(token)) { return njs_parser_failed(parser); } if (njs_parser_restricted_identifier(token->type)) { njs_parser_syntax_error(parser, "Identifier \"%V\" is forbidden" " in function declaration", &token->text); return NJS_DONE; } node = parser->node; unique_id = token->unique_id; njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } if (token->type != NJS_TOKEN_OPEN_PARENTHESIS) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); var = njs_variable_function_add(parser, parser->scope, unique_id, NJS_VARIABLE_FUNCTION); if (var == NULL) { return NJS_ERROR; } node->u.value.data.u.lambda = njs_variable_lambda(var); node->left = (njs_parser_node_t *) unique_id; parser->node = node; ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION, 1); if (ret != NJS_OK) { return NJS_ERROR; } async = (node->token_type == NJS_TOKEN_ASYNC_FUNCTION_DECLARATION); parser->scope->async = async; njs_parser_next(parser, njs_parser_function_parse); return njs_parser_after(parser, current, node, 1, njs_parser_function_declaration_after); } static njs_int_t njs_parser_function_declaration_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; uintptr_t unique_id; unique_id = (uintptr_t) parser->node->left; parser->node->left = NULL; njs_value_null_set(&parser->node->u.value); ret = njs_parser_variable_reference(parser, parser->scope, parser->node, unique_id, NJS_DECLARATION); if (ret != NJS_OK) { return NJS_ERROR; } return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_function_parse(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->target = parser->node; parser->node = NULL; njs_parser_next(parser, njs_parser_formal_parameters); return njs_parser_after(parser, current, parser->target, 1, njs_parser_function_lambda_args_after); } static const njs_lexer_entry_t njs_parser_empty_entry = { .name = njs_str("") }; static njs_int_t njs_parser_function_expression(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; uintptr_t unique_id; njs_bool_t async; njs_variable_t *var; njs_function_lambda_t *lambda; ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION, 1); if (ret != NJS_OK) { return NJS_ERROR; } async = (parser->node->token_type == NJS_TOKEN_ASYNC_FUNCTION_EXPRESSION); parser->scope->async = async; var = NULL; if (njs_lexer_token_is_binding_identifier(token)) { unique_id = token->unique_id; njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } } else { unique_id = (uintptr_t) &njs_parser_empty_entry; } if (token->type != NJS_TOKEN_OPEN_PARENTHESIS) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); parser->node->left = njs_parser_node_new(parser, NJS_TOKEN_NAME); if (parser->node->left == NULL) { return NJS_ERROR; } var = njs_variable_scope_add(parser, parser->scope, parser->scope, unique_id, NJS_VARIABLE_FUNCTION, 1); if (var == NULL) { return NJS_ERROR; } var->self = 1; ret = njs_parser_variable_reference(parser, parser->scope, parser->node->left, unique_id, NJS_DECLARATION); if (ret != NJS_OK) { return NJS_ERROR; } lambda = njs_function_lambda_alloc(parser->vm, !async); if (lambda == NULL) { return NJS_ERROR; } parser->node->u.value.data.u.lambda = lambda; njs_parser_next(parser, njs_parser_function_parse); return njs_parser_after(parser, current, var, 1, njs_parser_function_expression_after); } static njs_int_t njs_parser_function_expression_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_variable_t *var; var = (njs_variable_t *) parser->target; if (var->self) { var->init = 1; var->type = NJS_VARIABLE_CONST; } var->index = njs_scope_index(var->scope->type, var->scope->items, NJS_LEVEL_LOCAL, var->type); var->scope->items++; if (var->self) { parser->node->u.value.data.u.lambda->self = var->index; } return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_unique_formal_parameters(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { parser->node = NULL; njs_parser_next(parser, njs_parser_formal_parameters); return NJS_OK; } static njs_int_t njs_parser_formal_parameters(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_variable_t *arg; njs_rbtree_node_t *rb_node; njs_variable_node_t var_node; njs_function_lambda_t *lambda; lambda = parser->target->u.value.data.u.lambda; switch (token->type) { /* BindingRestElement */ case NJS_TOKEN_ELLIPSIS: if (lambda->rest_parameters != 0) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } if (!njs_lexer_token_is_binding_identifier(token)) { return njs_parser_failed(parser); } lambda->rest_parameters = 1; return NJS_OK; /* BindingElement */ case NJS_TOKEN_OPEN_BRACE: return njs_parser_not_supported(parser, token); case NJS_TOKEN_OPEN_BRACKET: return njs_parser_not_supported(parser, token); default: /* SingleNameBinding */ if (njs_lexer_token_is_binding_identifier(token)) { var_node.key = token->unique_id; rb_node = njs_rbtree_find(&parser->scope->variables, &var_node.node); if (rb_node != NULL) { arg = ((njs_variable_node_t *) rb_node)->variable; if (!arg->self) { njs_parser_syntax_error(parser, "Duplicate parameter names"); return NJS_DONE; } arg->self = 0; } else { arg = njs_variable_add(parser, parser->scope, token->unique_id, NJS_VARIABLE_VAR); } if (arg == NULL) { return NJS_ERROR; } arg->argument = 1; lambda->nargs++; /* Crutch for temporary storage. */ parser->node = (njs_parser_node_t *) arg; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_formal_parameters_after); break; } return njs_parser_stack_pop(parser); } return NJS_OK; } static njs_int_t njs_parser_formal_parameters_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_function_lambda_t *lambda; if (token->type != NJS_TOKEN_COMMA) { return njs_parser_stack_pop(parser); } lambda = parser->target->u.value.data.u.lambda; if (lambda->rest_parameters) { njs_parser_syntax_error(parser, "Rest parameter must be " "last formal parameter"); return NJS_DONE; } njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_formal_parameters); return NJS_OK; } /* * 14.2 Arrow Function Definitions * * TODO: implement according to specification. */ static njs_int_t njs_parser_arrow_function(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; uintptr_t unique_id; njs_bool_t async; njs_variable_t *arg, *var; njs_parser_node_t *node, *name; njs_function_lambda_t *lambda; if (token->type == NJS_TOKEN_ASYNC) { njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } async = 1; node = njs_parser_node_new(parser, NJS_TOKEN_ASYNC_FUNCTION_EXPRESSION); } else { async = 0; node = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_EXPRESSION); } if (node == NULL) { return NJS_ERROR; } node->token_line = token->line; parser->node = node; ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION, 0); if (ret != NJS_OK) { return NJS_ERROR; } parser->scope->async = async; name = njs_parser_node_new(parser, NJS_TOKEN_NAME); if (name == NULL) { return NJS_ERROR; } node->left = name; unique_id = (uintptr_t) &njs_parser_empty_entry; var = njs_variable_scope_add(parser, parser->scope, parser->scope, unique_id, NJS_VARIABLE_FUNCTION, 1); if (var == NULL) { return NJS_ERROR; } ret = njs_parser_variable_reference(parser, parser->scope, node->left, unique_id, NJS_DECLARATION); if (ret != NJS_OK) { return NJS_ERROR; } node->left->u.reference.variable = var; lambda = njs_function_lambda_alloc(parser->vm, 0); if (lambda == NULL) { return NJS_ERROR; } node->u.value.data.u.lambda = lambda; parser->scope->arrow_function = 1; if (token->type == NJS_TOKEN_OPEN_PARENTHESIS) { njs_lexer_consume_token(parser->lexer, 1); parser->node = NULL; parser->target = node; njs_parser_next(parser, njs_parser_formal_parameters); return njs_parser_after(parser, current, node, 1, njs_parser_arrow_function_args_after); } else if (njs_lexer_token_is_binding_identifier(token)) { arg = njs_variable_add(parser, parser->scope, token->unique_id, NJS_VARIABLE_VAR); if (arg == NULL) { return NJS_ERROR; } arg->argument = 1; var->index = njs_scope_index(parser->scope->type, parser->scope->items, NJS_LEVEL_LOCAL, NJS_VARIABLE_VAR); parser->scope->items++; lambda->self = var->index; lambda->nargs++; njs_lexer_consume_token(parser->lexer, 1); parser->target = node; njs_parser_next(parser, njs_parser_arrow_function_arrow); } else { return njs_parser_failed(parser); } return NJS_OK; } static njs_int_t njs_parser_arrow_function_args_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_variable_t *var, **vv; if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); vv = &parser->target->left->u.reference.variable; var = *vv; *vv = NULL; var->index = njs_scope_index(var->scope->type, var->scope->items, NJS_LEVEL_LOCAL, NJS_VARIABLE_VAR); var->scope->items++; parser->target->u.value.data.u.lambda->self = var->index; njs_parser_next(parser, njs_parser_arrow_function_arrow); return NJS_OK; } static njs_int_t njs_parser_arrow_function_arrow(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_ARROW) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } if (token->type == NJS_TOKEN_OPEN_BRACE) { njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } if (token->type == NJS_TOKEN_CLOSE_BRACE) { parser->node = NULL; njs_parser_next(parser, njs_parser_function_lambda_body_after); return NJS_OK; } parser->node = NULL; njs_parser_next(parser, njs_parser_statement_list); return njs_parser_after(parser, current, parser->target, 1, njs_parser_function_lambda_body_after); } parser->node = NULL; njs_parser_next(parser, njs_parser_assignment_expression); return njs_parser_after(parser, current, parser->target, 1, njs_parser_arrow_function_body_after); } static njs_int_t njs_parser_arrow_function_body_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *body; body = njs_parser_return_set(parser, parser->node); if (body == NULL) { return NJS_ERROR; } parser->target->right = body; parser->node = parser->target; njs_parser_scope_end(parser); return njs_parser_stack_pop(parser); } /* * 14.3 Method Definitions. */ static njs_int_t njs_parser_method_definition(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_token_type_t type; njs_lexer_token_t *next; njs_parser_node_t *expr; type = NJS_TOKEN_FUNCTION; if (token->type == NJS_TOKEN_ASYNC) { njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } type = NJS_TOKEN_ASYNC_FUNCTION; } switch (token->type) { /* PropertyName */ case NJS_TOKEN_STRING: case NJS_TOKEN_ESCAPE_STRING: case NJS_TOKEN_NUMBER: break; default: if (njs_lexer_token_is_identifier_name(token)) { break; } return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); next = njs_lexer_token(parser->lexer, 0); if (next == NULL) { return NJS_ERROR; } if (next->type != NJS_TOKEN_OPEN_PARENTHESIS) { return njs_parser_failed(parser); } expr = njs_parser_node_new(parser, type); if (expr == NULL) { return NJS_ERROR; } expr->token_line = next->line; parser->node = expr; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_function_lambda); return NJS_OK; } static njs_int_t njs_parser_get_set(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_token_type_t accessor; njs_lexer_token_t *name; njs_parser_node_t *property, *expression, *temp; temp = parser->target; accessor = (njs_token_type_t) (uintptr_t) temp->right; name = token; token = njs_lexer_peek_token(parser->lexer, token, 0); if (token == NULL) { return NJS_ERROR; } switch (token->type) { /* IdentifierReference */ case NJS_TOKEN_NAME: case NJS_TOKEN_STRING: case NJS_TOKEN_ESCAPE_STRING: case NJS_TOKEN_NUMBER: break; case NJS_TOKEN_OPEN_BRACKET: njs_lexer_consume_token(parser->lexer, 2); njs_parser_next(parser, njs_parser_assignment_expression); return njs_parser_after(parser, current, temp, 1, njs_parser_get_set_after); default: if (njs_lexer_token_is_identifier_name(token)) { break; } return njs_parser_property_definition_ident(parser, name, temp); } property = njs_parser_property_name_node(parser, token); if (property == NULL) { return NJS_ERROR; } njs_lexer_consume_token(parser->lexer, 2); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } if (token->type != NJS_TOKEN_OPEN_PARENTHESIS) { return njs_parser_failed(parser); } expression = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION); if (expression == NULL) { return NJS_ERROR; } expression->token_line = token->line; temp->right = property; parser->node = expression; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_function_lambda); if (accessor == NJS_TOKEN_PROPERTY_GETTER) { return njs_parser_after(parser, current, temp, 1, njs_parser_get_after); } return njs_parser_after(parser, current, temp, 1, njs_parser_set_after); } static njs_int_t njs_parser_get_set_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_token_type_t accessor; njs_parser_node_t *expression, *temp; if (token->type != NJS_TOKEN_CLOSE_BRACKET) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } if (token->type != NJS_TOKEN_OPEN_PARENTHESIS) { return njs_parser_failed(parser); } expression = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION); if (expression == NULL) { return NJS_ERROR; } expression->token_line = token->line; temp = parser->target; accessor = (njs_token_type_t) (uintptr_t) temp->right; temp->right = parser->node; parser->node = expression; njs_lexer_consume_token(parser->lexer, 1); njs_parser_next(parser, njs_parser_function_lambda); if (accessor == NJS_TOKEN_PROPERTY_GETTER) { return njs_parser_after(parser, current, temp, 1, njs_parser_get_after); } return njs_parser_after(parser, current, temp, 1, njs_parser_set_after); } static njs_int_t njs_parser_get_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; njs_parser_node_t *expression, *temp; njs_function_lambda_t *lambda; temp = parser->target; expression = parser->node; lambda = expression->u.value.data.u.lambda; if (lambda->nargs != 0) { njs_parser_syntax_error(parser, "Getter must not have any formal parameters"); return NJS_DONE; } ret = njs_parser_property_accessor(parser, temp->left, temp->right, expression, NJS_TOKEN_PROPERTY_GETTER); if (ret != NJS_OK) { return NJS_ERROR; } parser->node = temp->left; parser->target = NULL; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_set_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; njs_parser_node_t *expression, *temp; njs_function_lambda_t *lambda; temp = parser->target; expression = parser->node; lambda = expression->u.value.data.u.lambda; if (lambda->nargs != 1) { njs_parser_syntax_error(parser, "Setter must have exactly one formal parameter"); return NJS_DONE; } ret = njs_parser_property_accessor(parser, temp->left, temp->right, expression, NJS_TOKEN_PROPERTY_SETTER); if (ret != NJS_OK) { return NJS_ERROR; } parser->node = temp->left; parser->target = NULL; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_function_lambda(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_int_t ret; njs_parser_node_t *expr; njs_function_lambda_t *lambda; lambda = njs_function_lambda_alloc(parser->vm, 0); if (lambda == NULL) { return NJS_ERROR; } expr = parser->node; expr->u.value.data.u.lambda = lambda; ret = njs_parser_scope_begin(parser, NJS_SCOPE_FUNCTION, 1); if (ret != NJS_OK) { return NJS_ERROR; } parser->scope->async = (parser->node->token_type == NJS_TOKEN_ASYNC_FUNCTION); parser->node = NULL; parser->target = expr; njs_parser_next(parser, njs_parser_unique_formal_parameters); return njs_parser_after(parser, current, expr, 1, njs_parser_function_lambda_args_after); } static njs_int_t njs_parser_function_lambda_args_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (token->type != NJS_TOKEN_CLOSE_PARENTHESIS) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } if (token->type != NJS_TOKEN_OPEN_BRACE) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } if (token->type == NJS_TOKEN_CLOSE_BRACE) { parser->node = NULL; njs_parser_next(parser, njs_parser_function_lambda_body_after); return NJS_OK; } parser->node = NULL; njs_parser_next(parser, njs_parser_statement_list); return njs_parser_after(parser, current, parser->target, 1, njs_parser_function_lambda_body_after); } static njs_int_t njs_parser_function_lambda_body_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *body, *last, *parent; if (token->type != NJS_TOKEN_CLOSE_BRACE) { return njs_parser_failed(parser); } parent = parser->target; last = NULL; body = njs_parser_chain_top(parser); if (body != NULL) { /* Take the last function body statement. */ last = body->right; if (last == NULL) { /* * The last statement is terminated by semicolon. * Take the last statement itself. */ last = body->left; } } if (last == NULL || last->token_type != NJS_TOKEN_RETURN) { /* * There is no function body or the last function body * body statement is not "return" statement. */ body = njs_parser_return_set(parser, NULL); if (body == NULL) { return NJS_ERROR; } body->right->token_line = token->line; } parent->right = body; parser->node = parent; njs_parser_scope_end(parser); njs_lexer_consume_token(parser->lexer, 1); return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_export(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_parser_node_t *node; njs_lexer_token_t *peek; static const njs_str_t as_string = njs_str("as"); if (!parser->module) { njs_parser_syntax_error(parser, "Illegal export statement"); return NJS_DONE; } if (token->type != NJS_TOKEN_DEFAULT) { if (token->type != NJS_TOKEN_OPEN_BRACE) { njs_parser_syntax_error(parser, "Non-default export is not supported"); return NJS_DONE; } /* * 'export {' * supported only: export {identifier as default}; */ njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (njs_slow_path(token == NULL)) { return NJS_ERROR; } if (token->type != NJS_TOKEN_NAME) { njs_parser_syntax_error(parser, "Identifier expected"); return NJS_DONE; } peek = njs_lexer_peek_token(parser->lexer, token, 0); if (njs_slow_path(peek == NULL)) { return NJS_ERROR; } if (peek->type != NJS_TOKEN_NAME || !njs_strstr_eq(&peek->text, &as_string)) { njs_parser_syntax_error(parser, "'as' expected"); return NJS_DONE; } peek = njs_lexer_peek_token(parser->lexer, peek, 0); if (njs_slow_path(peek == NULL)) { return NJS_ERROR; } if (peek->type != NJS_TOKEN_DEFAULT) { njs_parser_syntax_error(parser, "Non-default export is not supported"); return NJS_DONE; } peek = njs_lexer_peek_token(parser->lexer, peek, 0); if (njs_slow_path(peek == NULL)) { return NJS_ERROR; } if (peek->type != NJS_TOKEN_CLOSE_BRACE) { njs_parser_syntax_error(parser, "Close brace is expected"); return NJS_DONE; } node = njs_parser_node_new(parser, NJS_TOKEN_EXPORT); if (node == NULL) { return NJS_ERROR; } node->token_line = parser->line; node->right = njs_parser_reference(parser, token); if (node->right == NULL) { return NJS_ERROR; } parser->node = node; njs_lexer_consume_token(parser->lexer, 4); return njs_parser_stack_pop(parser); } njs_lexer_consume_token(parser->lexer, 1); node = njs_parser_node_new(parser, NJS_TOKEN_EXPORT); if (node == NULL) { return NJS_ERROR; } node->token_line = parser->line; parser->node = node; njs_parser_next(parser, njs_parser_assignment_expression); return njs_parser_after(parser, current, node, 1, njs_parser_export_after); } static njs_int_t njs_parser_export_after(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { if (njs_parser_expect_semicolon(parser, token) != NJS_OK) { return njs_parser_failed(parser); } parser->target->right = parser->node; parser->node = parser->target; return njs_parser_stack_pop(parser); } static njs_mod_t * njs_parser_module(njs_parser_t *parser, njs_str_t *name) { njs_vm_t *vm; njs_mod_t *module; vm = parser->vm; if (name->length == 0) { njs_parser_ref_error(parser, "Cannot load module \"%V\"", name); return NULL; } module = njs_module_find(vm, name, 1); if (module != NULL) { goto done; } if (vm->module_loader == NULL) { njs_parser_ref_error(parser, "Module loader callback is not provided"); return NULL; } module = vm->module_loader(vm, vm->module_loader_opaque, name); if (module == NULL) { if (!njs_is_valid(&vm->exception)) { njs_parser_ref_error(parser, "Cannot load module \"%V\"", name); } return NULL; } done: if (module->index == 0) { module->index = vm->shared->module_items++; } return module; } static njs_int_t njs_parser_import(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current) { njs_variable_t *var; njs_parser_node_t *name, *import; if (parser->scope->parent != NULL) { njs_parser_syntax_error(parser, "Illegal import statement"); return NJS_DONE; } if (token->type == NJS_TOKEN_MULTIPLICATION || token->type == NJS_TOKEN_OPEN_BRACE || token->type == NJS_TOKEN_STRING) { njs_parser_syntax_error(parser, "Non-default import is not supported"); return NJS_DONE; } if (token->type != NJS_TOKEN_NAME) { return njs_parser_failed(parser); } name = njs_parser_variable_node(parser, token->unique_id, NJS_VARIABLE_LET, &var); if (name == NULL) { return NJS_ERROR; } var->init = 1; name->token_line = token->line; njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } if (token->type != NJS_TOKEN_FROM) { return njs_parser_failed(parser); } njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } if (token->type != NJS_TOKEN_STRING) { return njs_parser_failed(parser); } import = njs_parser_node_new(parser, NJS_TOKEN_IMPORT); if (import == NULL) { return NJS_ERROR; } import->hoist = 1; import->token_line = parser->line; import->left = name; import->u.module = njs_parser_module(parser, &token->text); if (njs_slow_path(import->u.module == NULL)) { return NJS_ERROR; } njs_lexer_consume_token(parser->lexer, 1); token = njs_lexer_token(parser->lexer, 0); if (token == NULL) { return NJS_ERROR; } if (njs_parser_expect_semicolon(parser, token) != NJS_OK) { return njs_parser_failed(parser); } parser->node = import; return njs_parser_stack_pop(parser); } static njs_int_t njs_parser_export_sink(njs_parser_t *parser) { njs_uint_t n; njs_parser_node_t *node, *prev; n = 0; for (node = njs_parser_chain_top(parser); node != NULL; node = node->left) { if (node->right != NULL && node->right->token_type == NJS_TOKEN_EXPORT) { n++; } } if (n != 1) { njs_parser_syntax_error(parser, (n == 0) ? "export statement is required" : "Identifier \"default\" has already been declared"); return NJS_ERROR; } node = njs_parser_chain_top(parser); if (node->right && node->right->token_type == NJS_TOKEN_EXPORT) { return NJS_OK; } prev = njs_parser_chain_top(parser); while (prev->left != NULL) { node = prev->left; if (node->right != NULL && node->right->token_type == NJS_TOKEN_EXPORT) { prev->left = node->left; break; } prev = prev->left; } node->left = njs_parser_chain_top(parser); njs_parser_chain_top_set(parser, node); return NJS_OK; } static njs_parser_node_t * njs_parser_return_set(njs_parser_t *parser, njs_parser_node_t *expr) { njs_parser_node_t *stmt, *node; node = njs_parser_node_new(parser, NJS_TOKEN_RETURN); if (njs_slow_path(node == NULL)) { return NULL; } if (expr != NULL) { node->token_line = expr->token_line; } node->right = expr; stmt = njs_parser_node_new(parser, NJS_TOKEN_STATEMENT); if (njs_slow_path(stmt == NULL)) { return NULL; } stmt->left = njs_parser_chain_top(parser); stmt->right = node; njs_parser_chain_top_set(parser, stmt); return stmt; } static njs_parser_node_t * njs_parser_variable_node(njs_parser_t *parser, uintptr_t unique_id, njs_variable_type_t type, njs_variable_t **retvar) { njs_int_t ret; njs_variable_t *var; njs_parser_node_t *node; var = njs_variable_add(parser, parser->scope, unique_id, type); if (njs_slow_path(var == NULL)) { return NULL; } if (retvar != NULL) { *retvar = var; } node = njs_parser_node_new(parser, NJS_TOKEN_NAME); if (njs_slow_path(node == NULL)) { return NULL; } ret = njs_parser_variable_reference(parser, parser->scope, node, unique_id, NJS_DECLARATION); if (njs_slow_path(ret != NJS_OK)) { return NULL; } return node; } static njs_parser_node_t * njs_parser_reference(njs_parser_t *parser, njs_lexer_token_t *token) { njs_int_t ret; njs_index_t index; njs_variable_t *var; njs_parser_node_t *node; njs_parser_scope_t *scope; const njs_lexer_keyword_entry_t *keyword; static const njs_str_t njs_undefined_str = njs_str("undefined"); node = njs_parser_node_new(parser, token->type); if (njs_slow_path(node == NULL)) { return NULL; } switch (token->type) { case NJS_TOKEN_NULL: njs_thread_log_debug("JS: null"); break; case NJS_TOKEN_THIS: njs_thread_log_debug("JS: this"); scope = njs_function_scope(parser->scope); if (njs_slow_path(scope == NULL)) { njs_parser_syntax_error(parser, "function or global scope not found"); return NULL; } if (parser->vm->options.module) { keyword = njs_lexer_keyword(njs_undefined_str.start, njs_undefined_str.length); if (njs_slow_path(keyword == NULL)) { return NULL; } token->unique_id = (uintptr_t) keyword->value; } else if (!scope->arrow_function) { index = njs_scope_index(scope->type, 0, NJS_LEVEL_LOCAL, NJS_VARIABLE_VAR); var = njs_variable_scope_add(parser, scope, scope, token->unique_id, NJS_VARIABLE_VAR, index); if (njs_slow_path(var == NULL)) { return NULL; } } node->token_type = NJS_TOKEN_THIS; node->token_line = token->line; ret = njs_parser_variable_reference(parser, parser->scope, node, token->unique_id, NJS_REFERENCE); if (njs_slow_path(ret != NJS_OK)) { return NULL; } break; case NJS_TOKEN_ARGUMENTS: njs_thread_log_debug("JS: arguments"); scope = njs_function_scope(parser->scope); while (scope->arrow_function) { scope = njs_function_scope(scope->parent); } if (scope->parent == NULL) { njs_parser_syntax_error(parser, "\"%V\" object in global scope", &token->text); return NULL; } node->token_line = token->line; ret = njs_parser_variable_reference(parser, parser->scope, node, token->unique_id, NJS_REFERENCE); if (njs_slow_path(ret != NJS_OK)) { return NULL; } var = njs_variable_add(parser, scope, token->unique_id, NJS_VARIABLE_VAR); if (njs_slow_path(var == NULL)) { return NULL; } var->arguments_object = 1; break; default: if (token->type == NJS_TOKEN_EVAL || njs_lexer_token_is_identifier_reference(token)) { njs_thread_log_debug("JS: %V", name); if (token->type != NJS_TOKEN_EVAL) { node->token_type = NJS_TOKEN_NAME; } node->token_line = token->line; ret = njs_parser_variable_reference(parser, parser->scope, node, token->unique_id, NJS_REFERENCE); if (njs_slow_path(ret != NJS_OK)) { return NULL; } break; } (void) njs_parser_unexpected_token(parser->vm, parser, &token->text, token->type); return NULL; } return node; } static njs_parser_node_t * njs_parser_argument(njs_parser_t *parser, njs_parser_node_t *expr, njs_index_t index) { njs_parser_node_t *node; node = njs_parser_node_new(parser, NJS_TOKEN_ARGUMENT); if (njs_slow_path(node == NULL)) { return NULL; } node->token_line = expr->token_line; node->index = index; node->left = expr; expr->dest = node; return node; } static njs_int_t njs_parser_object_property(njs_parser_t *parser, njs_parser_node_t *parent, njs_parser_node_t *property, njs_parser_node_t *value, njs_bool_t proto_init) { njs_token_type_t type; njs_parser_node_t *stmt, *assign, *object, *propref; object = njs_parser_node_new(parser, NJS_TOKEN_OBJECT_VALUE); if (njs_slow_path(object == NULL)) { return NJS_TOKEN_ERROR; } object->token_line = value->token_line; object->u.object = parent; type = proto_init ? NJS_TOKEN_PROTO_INIT : NJS_TOKEN_PROPERTY_INIT; propref = njs_parser_node_new(parser, type); if (njs_slow_path(propref == NULL)) { return NJS_ERROR; } propref->token_line = value->token_line; propref->left = object; propref->right = property; assign = njs_parser_node_new(parser, NJS_TOKEN_ASSIGNMENT); if (njs_slow_path(assign == NULL)) { return NJS_ERROR; } assign->token_line = value->token_line; assign->u.operation = NJS_VMCODE_MOVE; assign->left = propref; assign->right = value; stmt = njs_parser_node_new(parser, NJS_TOKEN_STATEMENT); if (njs_slow_path(stmt == NULL)) { return NJS_ERROR; } stmt->right = assign; stmt->left = parent->left; parent->left = stmt; return NJS_OK; } static njs_int_t njs_parser_property_accessor(njs_parser_t *parser, njs_parser_node_t *parent, njs_parser_node_t *property, njs_parser_node_t *value, njs_token_type_t accessor) { njs_parser_node_t *node, *stmt, *object, *propref; object = njs_parser_node_new(parser, NJS_TOKEN_OBJECT_VALUE); if (njs_slow_path(object == NULL)) { return NJS_TOKEN_ERROR; } object->token_line = value->token_line; object->u.object = parent; propref = njs_parser_node_new(parser, 0); if (njs_slow_path(propref == NULL)) { return NJS_ERROR; } propref->left = object; propref->right = property; node = njs_parser_node_new(parser, accessor); if (njs_slow_path(node == NULL)) { return NJS_ERROR; } node->token_line = value->token_line; node->left = propref; node->right = value; stmt = njs_parser_node_new(parser, NJS_TOKEN_STATEMENT); if (njs_slow_path(stmt == NULL)) { return NJS_ERROR; } stmt->right = node; stmt->left = parent->left; parent->left = stmt; return NJS_OK; } static njs_int_t njs_parser_array_item(njs_parser_t *parser, njs_parser_node_t *array, njs_parser_node_t *value) { njs_int_t ret; njs_parser_node_t *number; number = njs_parser_node_new(parser, NJS_TOKEN_NUMBER); if (njs_slow_path(number == NULL)) { return NJS_ERROR; } njs_set_number(&number->u.value, array->u.length); number->token_line = value->token_line; ret = njs_parser_object_property(parser, array, number, value, 0); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } array->ctor = 0; array->u.length++; return NJS_OK; } static njs_int_t njs_parser_template_string(njs_parser_t *parser, njs_lexer_token_t *token) { u_char *p, c; njs_int_t ret; njs_str_t *text; njs_bool_t escape; njs_lexer_t *lexer; njs_parser_node_t *node; lexer = parser->lexer; text = &token->text; escape = 0; p = text->start; if (p == NULL) { return NJS_ERROR; } while (p < lexer->end) { c = *p++; switch (c) { case '\\': if (p == lexer->end) { return NJS_ERROR; } p++; escape = 1; continue; case '`': text->length = p - text->start - 1; goto done; case '$': if (p < lexer->end && *p == '{') { p++; text->length = p - text->start - 2; ret = njs_lexer_in_stack_push(lexer); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } goto done; } break; case '\n': parser->lexer->line++; break; } } return NJS_ERROR; done: node = njs_parser_node_new(parser, NJS_TOKEN_STRING); if (njs_slow_path(node == NULL)) { return NJS_ERROR; } node->token_line = token->line; if (escape) { ret = njs_parser_escape_string_create(parser, token, &node->u.value); if (njs_slow_path(ret != NJS_TOKEN_STRING)) { return NJS_ERROR; } } else { ret = njs_parser_string_create(parser->vm, token, &node->u.value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } lexer->start = p; parser->node = node; return c == '`' ? NJS_DONE : NJS_OK; } njs_int_t njs_parser_string_create(njs_vm_t *vm, njs_lexer_token_t *token, njs_value_t *value) { size_t length; njs_str_t dst; length = njs_decode_utf8_length(&token->text, &dst.length); dst.start = njs_string_alloc(vm, value, dst.length, length); if (njs_slow_path(dst.start == NULL)) { return NJS_ERROR; } njs_decode_utf8(&dst, &token->text); if (length > NJS_STRING_MAP_STRIDE && dst.length != length) { njs_string_utf8_offset_map_init(value->long_string.data->start, dst.length); } return NJS_OK; } static njs_token_type_t njs_parser_escape_string_create(njs_parser_t *parser, njs_lexer_token_t *token, njs_value_t *value) { u_char c, *start, *dst; size_t size, length, hex_length; uint64_t cp, cp_pair; njs_int_t ret; njs_str_t *string; const u_char *src, *end, *hex_end; njs_unicode_decode_t ctx; ret = njs_parser_escape_string_calc_length(parser, token, &size, &length); if (njs_slow_path(ret != NJS_OK)) { return NJS_TOKEN_ILLEGAL; } start = njs_string_alloc(parser->vm, value, size, length); if (njs_slow_path(start == NULL)) { return NJS_TOKEN_ERROR; } dst = start; cp_pair = 0; string = &token->text; src = string->start; end = src + string->length; while (src < end) { c = *src++; if (c == '\\') { /* * Testing "src == end" is not required here * since this has been already tested by lexer. */ c = *src++; switch (c) { case 'u': /* * A character after "u" can be safely tested here * because there is always a closing quote at the * end of string: ...\u". */ if (*src != '{') { hex_length = 4; goto hex_length; } src++; hex_length = 0; hex_end = end; goto hex; case 'x': hex_length = 2; goto hex_length; case '0': c = '\0'; break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': if (parser->node != NULL) { switch (parser->node->token_type) { case NJS_TOKEN_METHOD_CALL: case NJS_TOKEN_FUNCTION_CALL: case NJS_TOKEN_FUNCTION_EXPRESSION: case NJS_TOKEN_EVAL: goto next_char; default: break; } } njs_parser_syntax_error(parser, "Octal escape sequences can't be used " "in untagged template literals " "or in strict mode code"); return NJS_TOKEN_ILLEGAL; case '8': case '9': if (parser->node != NULL) { switch (parser->node->token_type) { case NJS_TOKEN_METHOD_CALL: case NJS_TOKEN_FUNCTION_CALL: case NJS_TOKEN_FUNCTION_EXPRESSION: case NJS_TOKEN_EVAL: goto next_char; default: break; } } njs_parser_syntax_error(parser, "The escapes \\8 and \\9 can't be used " "in untagged template literals " "or in strict mode code"); return NJS_TOKEN_ILLEGAL; next_char: break; case 'b': c = '\b'; break; case 'f': c = '\f'; break; case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case 'v': c = '\v'; break; case '\r': /* * A character after "\r" can be safely tested here * because there is always a closing quote at the * end of string: ...\\r". */ if (*src == '\n') { src++; } continue; case '\n': continue; default: if (c >= 0x80) { goto utf8_copy; } break; } } if (c < 0x80) { *dst++ = c; continue; } utf8_copy: src--; njs_utf8_decode_init(&ctx); cp = njs_utf8_decode(&ctx, &src, end); if (cp > NJS_UNICODE_MAX_CODEPOINT) { cp = NJS_UNICODE_REPLACEMENT; } dst = njs_utf8_encode(dst, cp); continue; hex_length: hex_end = src + hex_length; hex: cp = njs_number_hex_parse(&src, hex_end, 0); /* Skip '}' character. */ if (hex_length == 0) { src++; } if (cp_pair != 0) { if (njs_fast_path(njs_surrogate_trailing(cp))) { cp = njs_surrogate_pair(cp_pair, cp); } else if (njs_slow_path(njs_surrogate_leading(cp))) { cp = NJS_UNICODE_REPLACEMENT; dst = njs_utf8_encode(dst, (uint32_t) cp); } else { dst = njs_utf8_encode(dst, NJS_UNICODE_REPLACEMENT); } cp_pair = 0; } else if (njs_surrogate_any(cp)) { if (cp <= 0xdbff && src[0] == '\\' && src[1] == 'u') { cp_pair = cp; continue; } cp = NJS_UNICODE_REPLACEMENT; } dst = njs_utf8_encode(dst, (uint32_t) cp); if (njs_slow_path(dst == NULL)) { njs_parser_syntax_error(parser, "Invalid Unicode code point \"%V\"", &token->text); return NJS_TOKEN_ILLEGAL; } } if (length > NJS_STRING_MAP_STRIDE && length != size) { njs_string_utf8_offset_map_init(start, size); } return NJS_TOKEN_STRING; } static njs_int_t njs_parser_escape_string_calc_length(njs_parser_t *parser, njs_lexer_token_t *token, size_t *out_size, size_t *out_length) { size_t size, length, hex_length; uint64_t cp, cp_pair; njs_str_t *string; const u_char *ptr, *src, *end, *hex_end; njs_unicode_decode_t ctx; size = 0; length = 0; cp_pair = 0; string = &token->text; src = string->start; end = src + string->length; while (src < end) { if (*src == '\\') { src++; switch (*src) { case 'u': src++; if (*src != '{') { hex_length = 4; goto hex_length; } src++; hex_length = 0; hex_end = end; goto hex; case 'x': src++; hex_length = 2; goto hex_length; case '\r': src++; if (*src == '\n') { src++; } continue; case '\n': src++; continue; default: break; } } if (*src >= 0x80) { njs_utf8_decode_init(&ctx); cp = njs_utf8_decode(&ctx, &src, end); if (cp > NJS_UNICODE_MAX_CODEPOINT) { cp = NJS_UNICODE_REPLACEMENT; } size += njs_utf8_size(cp); length++; continue; } src++; size++; length++; continue; hex_length: hex_end = src + hex_length; if (njs_slow_path(hex_end > end)) { goto invalid; } hex: ptr = src; cp = njs_number_hex_parse(&src, hex_end, 0); if (hex_length != 0) { if (src != hex_end) { goto invalid; } } else { if (src == ptr || (src - ptr) > 6) { goto invalid; } if (src == end || *src++ != '}') { goto invalid; } } if (cp_pair != 0) { if (njs_fast_path(njs_surrogate_trailing(cp))) { cp = njs_surrogate_pair(cp_pair, cp); } else if (njs_slow_path(njs_surrogate_leading(cp))) { cp = NJS_UNICODE_REPLACEMENT; size += njs_utf8_size(cp); length++; } else { size += njs_utf8_size(NJS_UNICODE_REPLACEMENT); length++; } cp_pair = 0; } else if (njs_surrogate_any(cp)) { if (cp <= 0xdbff && src[0] == '\\' && src[1] == 'u') { cp_pair = cp; continue; } cp = NJS_UNICODE_REPLACEMENT; } size += njs_utf8_size(cp); length++; } *out_size = size; *out_length = length; return NJS_OK; invalid: njs_parser_syntax_error(parser, "Invalid Unicode code point \"%V\"", &token->text); return NJS_ERROR; } njs_bool_t njs_parser_has_side_effect(njs_parser_node_t *node) { njs_bool_t side_effect; if (node == NULL) { return 0; } if (node->token_type >= NJS_TOKEN_ASSIGNMENT && node->token_type <= NJS_TOKEN_LAST_ASSIGNMENT) { return 1; } if (node->token_type == NJS_TOKEN_FUNCTION_CALL || node->token_type == NJS_TOKEN_METHOD_CALL) { return 1; } side_effect = njs_parser_has_side_effect(node->left); if (njs_fast_path(!side_effect)) { return njs_parser_has_side_effect(node->right); } return side_effect; } njs_int_t njs_parser_variable_reference(njs_parser_t *parser, njs_parser_scope_t *scope, njs_parser_node_t *node, uintptr_t unique_id, njs_reference_type_t type) { njs_rbtree_node_t *rb_node; njs_variable_reference_t *vr; njs_parser_rbtree_node_t parse_node, *rb_parse_node; vr = &node->u.reference; vr->unique_id = unique_id; vr->type = type; parse_node.key = unique_id; rb_node = njs_rbtree_find(&scope->references, &parse_node.node); if (rb_node != NULL) { return NJS_OK; } rb_parse_node = njs_mp_alloc(parser->vm->mem_pool, sizeof(njs_parser_rbtree_node_t)); if (njs_slow_path(rb_parse_node == NULL)) { return NJS_ERROR; } rb_parse_node->key = unique_id; rb_parse_node->index = NJS_INDEX_NONE; njs_rbtree_insert(&scope->references, &rb_parse_node->node); return NJS_OK; } njs_token_type_t njs_parser_unexpected_token(njs_vm_t *vm, njs_parser_t *parser, njs_str_t *name, njs_token_type_t type) { if (type != NJS_TOKEN_END) { njs_parser_syntax_error(parser, "Unexpected token \"%V\"", name); } else { njs_parser_syntax_error(parser, "Unexpected end of input"); } return NJS_DONE; } static void njs_parser_error(njs_vm_t *vm, njs_object_type_t type, njs_str_t *file, uint32_t line, const char *fmt, va_list args) { size_t width; u_char msg[NJS_MAX_ERROR_STR]; u_char *p, *end; njs_int_t ret; njs_value_t value, error; static const njs_value_t file_name = njs_string("fileName"); static const njs_value_t line_number = njs_string("lineNumber"); if (njs_slow_path(vm->top_frame == NULL)) { njs_vm_runtime_init(vm); } p = msg; end = msg + NJS_MAX_ERROR_STR; p = njs_vsprintf(p, end, fmt, args); width = njs_length(" in ") + file->length + NJS_INT_T_LEN; if (p > end - width) { p = end - width; } if (file->length != 0 && !vm->options.quiet) { p = njs_sprintf(p, end, " in %V:%uD", file, line); } else { p = njs_sprintf(p, end, " in %uD", line); } njs_error_new(vm, &error, njs_vm_proto(vm, type), msg, p - msg); njs_set_number(&value, line); njs_value_property_set(vm, &error, njs_value_arg(&line_number), &value); if (file->length != 0) { ret = njs_string_create(vm, &value, file->start, file->length); if (ret == NJS_OK) { njs_value_property_set(vm, &error, njs_value_arg(&file_name), &value); } } njs_vm_throw(vm, &error); } void njs_parser_lexer_error(njs_parser_t *parser, njs_object_type_t type, const char *fmt, ...) { va_list args; if (njs_is_error(&parser->vm->exception)) { return; } va_start(args, fmt); njs_parser_error(parser->vm, type, &parser->lexer->file, parser->lexer->line, fmt, args); va_end(args); } void njs_parser_node_error(njs_vm_t *vm, njs_object_type_t type, njs_parser_node_t *node, njs_str_t *file, const char *fmt, ...) { va_list args; va_start(args, fmt); njs_parser_error(vm, type, file, node->token_line, fmt, args); va_end(args); } njs_int_t njs_parser_traverse(njs_vm_t *vm, njs_parser_node_t *root, void *ctx, njs_parser_traverse_cb_t cb) { njs_int_t ret; njs_arr_t *stack; njs_parser_node_t *node, **ref; if (root == NULL) { return NJS_OK; } stack = njs_arr_create(vm->mem_pool, 8, sizeof(njs_parser_node_t *)); if (njs_slow_path(stack == NULL)) { return NJS_ERROR; } ref = njs_arr_add(stack); if (njs_slow_path(ref == NULL)) { goto failed; } *ref = root; while (1) { if (njs_arr_is_empty(stack)) { break; } ref = njs_arr_remove_last(stack); node = *ref; ret = cb(vm, node, ctx); if (njs_slow_path(ret != NJS_OK)) { goto failed; } if (node->left != NULL) { ref = njs_arr_add(stack); if (njs_slow_path(ref == NULL)) { goto failed; } *ref = node->left; } if (node->right != NULL) { ref = njs_arr_add(stack); if (njs_slow_path(ref == NULL)) { goto failed; } *ref = node->right; } } njs_arr_destroy(stack); return NJS_OK; failed: njs_arr_destroy(stack); return NJS_ERROR; } njs_int_t njs_parser_serialize_ast(njs_parser_node_t *node, njs_chb_t *chain) { njs_int_t ret; ret = NJS_OK; njs_parser_serialize_tree(chain, node, &ret, 0); njs_chb_append_literal(chain, "\n"); return ret; } njs_inline void njs_parser_serialize_indent(njs_chb_t *chain, size_t indent) { size_t i; for (i = 0; i < indent; i++) { njs_chb_append_literal(chain, " "); } } static void njs_parser_serialize_tree(njs_chb_t *chain, njs_parser_node_t *node, njs_int_t *ret, size_t indent) { njs_str_t str; njs_chb_append_literal(chain, "{\"name\": \""); *ret |= njs_parser_serialize_node(chain, node); njs_chb_append_literal(chain, "\",\n"); njs_parser_serialize_indent(chain, indent); njs_chb_sprintf(chain, 32, " \"line\": %d", node->token_line); switch (node->token_type) { case NJS_TOKEN_NUMBER: case NJS_TOKEN_STRING: case NJS_TOKEN_NAME: case NJS_TOKEN_FUNCTION_CALL: njs_chb_append_literal(chain, ",\n"); njs_parser_serialize_indent(chain, indent); njs_chb_sprintf(chain, 32, " \"index\": \"%p\"", node->index); switch (node->token_type) { case NJS_TOKEN_NUMBER: case NJS_TOKEN_STRING: njs_chb_append_literal(chain, ",\n"); njs_parser_serialize_indent(chain, indent); if (node->token_type == NJS_TOKEN_NUMBER) { njs_chb_sprintf(chain, 32, " \"value\": %f", njs_number(&node->u.value)); } else { njs_string_get(&node->u.value, &str); njs_chb_append_literal(chain, " \"value\": \""); njs_chb_append_str(chain, &str); njs_chb_append_literal(chain, "\""); } break; default: break; } break; default: break; } if (node->left != NULL) { njs_chb_append_literal(chain, ",\n"); njs_parser_serialize_indent(chain, indent); njs_chb_append_literal(chain, " \"left\": "); njs_parser_serialize_tree(chain, node->left, ret, indent + 1); } if (node->right != NULL) { njs_chb_append_literal(chain, ",\n"); njs_parser_serialize_indent(chain, indent); njs_chb_append_literal(chain, " \"right\": "); njs_parser_serialize_tree(chain, node->right, ret, indent + 1); } njs_chb_append_literal(chain, "}"); } static njs_int_t njs_parser_serialize_node(njs_chb_t *chain, njs_parser_node_t *node) { const char *name; #define njs_token_serialize(token) \ case token: \ name = &njs_stringify(token)[njs_length("NJS_TOKEN_")]; \ njs_chb_append(chain, name, njs_strlen(name)); \ break switch (node->token_type) { njs_token_serialize(NJS_TOKEN_END); /* FIXME: NJS_TOKEN_ILLEGAL should not be present in AST */ njs_token_serialize(NJS_TOKEN_ILLEGAL); njs_token_serialize(NJS_TOKEN_COMMA); njs_token_serialize(NJS_TOKEN_CONDITIONAL); njs_token_serialize(NJS_TOKEN_ASSIGNMENT); njs_token_serialize(NJS_TOKEN_ADDITION_ASSIGNMENT); njs_token_serialize(NJS_TOKEN_SUBTRACTION_ASSIGNMENT); njs_token_serialize(NJS_TOKEN_MULTIPLICATION_ASSIGNMENT); njs_token_serialize(NJS_TOKEN_EXPONENTIATION_ASSIGNMENT); njs_token_serialize(NJS_TOKEN_DIVISION_ASSIGNMENT); njs_token_serialize(NJS_TOKEN_REMAINDER_ASSIGNMENT); njs_token_serialize(NJS_TOKEN_LEFT_SHIFT_ASSIGNMENT); njs_token_serialize(NJS_TOKEN_RIGHT_SHIFT_ASSIGNMENT); njs_token_serialize(NJS_TOKEN_UNSIGNED_RIGHT_SHIFT_ASSIGNMENT); njs_token_serialize(NJS_TOKEN_BITWISE_OR_ASSIGNMENT); njs_token_serialize(NJS_TOKEN_BITWISE_XOR_ASSIGNMENT); njs_token_serialize(NJS_TOKEN_BITWISE_AND_ASSIGNMENT); njs_token_serialize(NJS_TOKEN_EQUAL); njs_token_serialize(NJS_TOKEN_NOT_EQUAL); njs_token_serialize(NJS_TOKEN_STRICT_EQUAL); njs_token_serialize(NJS_TOKEN_STRICT_NOT_EQUAL); njs_token_serialize(NJS_TOKEN_ADDITION); njs_token_serialize(NJS_TOKEN_UNARY_PLUS); njs_token_serialize(NJS_TOKEN_INCREMENT); njs_token_serialize(NJS_TOKEN_POST_INCREMENT); njs_token_serialize(NJS_TOKEN_SUBTRACTION); njs_token_serialize(NJS_TOKEN_UNARY_NEGATION); njs_token_serialize(NJS_TOKEN_DECREMENT); njs_token_serialize(NJS_TOKEN_POST_DECREMENT); njs_token_serialize(NJS_TOKEN_MULTIPLICATION); njs_token_serialize(NJS_TOKEN_EXPONENTIATION); njs_token_serialize(NJS_TOKEN_DIVISION); njs_token_serialize(NJS_TOKEN_REMAINDER); njs_token_serialize(NJS_TOKEN_LESS); njs_token_serialize(NJS_TOKEN_LESS_OR_EQUAL); njs_token_serialize(NJS_TOKEN_LEFT_SHIFT); njs_token_serialize(NJS_TOKEN_GREATER); njs_token_serialize(NJS_TOKEN_GREATER_OR_EQUAL); njs_token_serialize(NJS_TOKEN_RIGHT_SHIFT); njs_token_serialize(NJS_TOKEN_UNSIGNED_RIGHT_SHIFT); njs_token_serialize(NJS_TOKEN_BITWISE_OR); njs_token_serialize(NJS_TOKEN_LOGICAL_OR); njs_token_serialize(NJS_TOKEN_BITWISE_XOR); njs_token_serialize(NJS_TOKEN_BITWISE_AND); njs_token_serialize(NJS_TOKEN_LOGICAL_AND); njs_token_serialize(NJS_TOKEN_BITWISE_NOT); njs_token_serialize(NJS_TOKEN_LOGICAL_NOT); njs_token_serialize(NJS_TOKEN_COALESCE); njs_token_serialize(NJS_TOKEN_IN); njs_token_serialize(NJS_TOKEN_OF); njs_token_serialize(NJS_TOKEN_INSTANCEOF); njs_token_serialize(NJS_TOKEN_TYPEOF); njs_token_serialize(NJS_TOKEN_VOID); njs_token_serialize(NJS_TOKEN_NEW); njs_token_serialize(NJS_TOKEN_DELETE); njs_token_serialize(NJS_TOKEN_YIELD); njs_token_serialize(NJS_TOKEN_NULL); njs_token_serialize(NJS_TOKEN_NUMBER); njs_token_serialize(NJS_TOKEN_TRUE); njs_token_serialize(NJS_TOKEN_FALSE); njs_token_serialize(NJS_TOKEN_STRING); njs_token_serialize(NJS_TOKEN_TEMPLATE_LITERAL); njs_token_serialize(NJS_TOKEN_NAME); njs_token_serialize(NJS_TOKEN_OBJECT); njs_token_serialize(NJS_TOKEN_OBJECT_VALUE); njs_token_serialize(NJS_TOKEN_ARRAY); njs_token_serialize(NJS_TOKEN_REGEXP); njs_token_serialize(NJS_TOKEN_PROPERTY); njs_token_serialize(NJS_TOKEN_PROPERTY_INIT); njs_token_serialize(NJS_TOKEN_PROPERTY_DELETE); njs_token_serialize(NJS_TOKEN_PROPERTY_GETTER); njs_token_serialize(NJS_TOKEN_PROPERTY_SETTER); njs_token_serialize(NJS_TOKEN_PROTO_INIT); njs_token_serialize(NJS_TOKEN_FUNCTION); njs_token_serialize(NJS_TOKEN_ASYNC_FUNCTION); njs_token_serialize(NJS_TOKEN_FUNCTION_DECLARATION); njs_token_serialize(NJS_TOKEN_ASYNC_FUNCTION_DECLARATION); njs_token_serialize(NJS_TOKEN_FUNCTION_EXPRESSION); njs_token_serialize(NJS_TOKEN_ASYNC_FUNCTION_EXPRESSION); njs_token_serialize(NJS_TOKEN_FUNCTION_CALL); njs_token_serialize(NJS_TOKEN_METHOD_CALL); njs_token_serialize(NJS_TOKEN_ARGUMENT); njs_token_serialize(NJS_TOKEN_RETURN); njs_token_serialize(NJS_TOKEN_STATEMENT); njs_token_serialize(NJS_TOKEN_BLOCK); njs_token_serialize(NJS_TOKEN_VAR); njs_token_serialize(NJS_TOKEN_LET); njs_token_serialize(NJS_TOKEN_CONST); njs_token_serialize(NJS_TOKEN_IF); njs_token_serialize(NJS_TOKEN_ELSE); njs_token_serialize(NJS_TOKEN_BRANCHING); njs_token_serialize(NJS_TOKEN_WHILE); njs_token_serialize(NJS_TOKEN_DO); njs_token_serialize(NJS_TOKEN_FOR); njs_token_serialize(NJS_TOKEN_FOR_IN); njs_token_serialize(NJS_TOKEN_BREAK); njs_token_serialize(NJS_TOKEN_CONTINUE); njs_token_serialize(NJS_TOKEN_SWITCH); njs_token_serialize(NJS_TOKEN_CASE); njs_token_serialize(NJS_TOKEN_DEFAULT); njs_token_serialize(NJS_TOKEN_WITH); njs_token_serialize(NJS_TOKEN_TRY); njs_token_serialize(NJS_TOKEN_CATCH); njs_token_serialize(NJS_TOKEN_FINALLY); njs_token_serialize(NJS_TOKEN_THROW); njs_token_serialize(NJS_TOKEN_THIS); njs_token_serialize(NJS_TOKEN_ARGUMENTS); njs_token_serialize(NJS_TOKEN_EVAL); njs_token_serialize(NJS_TOKEN_IMPORT); njs_token_serialize(NJS_TOKEN_EXPORT); njs_token_serialize(NJS_TOKEN_DEBUGGER); #if 0 njs_token_serialize(NJS_TOKEN_TARGET); njs_token_serialize(NJS_TOKEN_META); njs_token_serialize(NJS_TOKEN_ASYNC); njs_token_serialize(NJS_TOKEN_AWAIT); njs_token_serialize(NJS_TOKEN_ENUM); njs_token_serialize(NJS_TOKEN_CLASS); njs_token_serialize(NJS_TOKEN_EXTENDS); njs_token_serialize(NJS_TOKEN_IMPLEMENTS); njs_token_serialize(NJS_TOKEN_INTERFACE); njs_token_serialize(NJS_TOKEN_PACKAGE); njs_token_serialize(NJS_TOKEN_PRIVATE); njs_token_serialize(NJS_TOKEN_PROTECTED); njs_token_serialize(NJS_TOKEN_PUBLIC); njs_token_serialize(NJS_TOKEN_STATIC); njs_token_serialize(NJS_TOKEN_SUPER); #endif default: njs_chb_sprintf(chain, 32, "#UNDEF(%d)", (int) node->token_type); return NJS_DECLINED; } return NJS_OK; } njs-0.8.9/src/njs_parser.h000066400000000000000000000244561474132077100154510ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) Alexander Borisov * Copyright (C) NGINX, Inc. */ #ifndef _NJS_PARSER_H_INCLUDED_ #define _NJS_PARSER_H_INCLUDED_ struct njs_parser_scope_s { njs_parser_node_t *top; njs_parser_scope_t *parent; njs_rbtree_t variables; njs_rbtree_t labels; njs_rbtree_t references; njs_arr_t *closures; njs_arr_t *declarations; uint32_t items; njs_scope_t type:8; uint8_t arrow_function; uint8_t dest_disable; uint8_t async; uint32_t in_args; }; struct njs_parser_node_s { njs_token_type_t token_type:16; uint8_t ctor:1; uint8_t hoist:1; uint8_t temporary; /* 1 bit */ uint32_t token_line; union { uint32_t length; njs_variable_reference_t reference; njs_value_t value; njs_vmcode_t operation; njs_parser_node_t *object; njs_mod_t *module; } u; njs_str_t name; njs_index_t index; /* * The scope points to * in global and function node: global or function scopes; * in variable node: a scope where variable was referenced; * in operation node: a scope to allocate indexes for temporary values. */ njs_parser_scope_t *scope; njs_parser_node_t *left; njs_parser_node_t *right; njs_parser_node_t *dest; }; typedef njs_int_t (*njs_parser_state_func_t)(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); struct njs_parser_s { njs_parser_state_func_t state; njs_queue_t stack; njs_lexer_t lexer0; njs_lexer_t *lexer; njs_vm_t *vm; njs_parser_node_t *node; njs_parser_node_t *target; njs_parser_scope_t *scope; njs_variable_type_t var_type; njs_int_t ret; uintptr_t undefined_id; uint8_t use_lhs; uint8_t module; njs_bool_t strict_semicolon; njs_str_t file; uint32_t line; }; typedef struct { njs_parser_state_func_t state; njs_queue_link_t link; njs_parser_node_t *node; njs_bool_t optional; } njs_parser_stack_entry_t; typedef struct { NJS_RBTREE_NODE (node); uintptr_t key; njs_index_t index; } njs_parser_rbtree_node_t; typedef struct { njs_value_t *value; njs_index_t index; } njs_declaration_t; typedef njs_int_t (*njs_parser_traverse_cb_t)(njs_vm_t *vm, njs_parser_node_t *node, void *ctx); njs_int_t njs_parser_failed_state(njs_parser_t *parser, njs_lexer_token_t *token, njs_queue_link_t *current); intptr_t njs_parser_scope_rbtree_compare(njs_rbtree_node_t *node1, njs_rbtree_node_t *node2); njs_int_t njs_parser_init(njs_vm_t *vm, njs_parser_t *parser, njs_parser_scope_t *scope, njs_str_t *file, u_char *start, u_char *end, njs_uint_t runtime); njs_int_t njs_parser(njs_vm_t *vm, njs_parser_t *parser); njs_bool_t njs_variable_closure_test(njs_parser_scope_t *root, njs_parser_scope_t *scope); njs_variable_t *njs_variable_resolve(njs_vm_t *vm, njs_parser_node_t *node); njs_index_t njs_variable_index(njs_vm_t *vm, njs_parser_node_t *node); njs_bool_t njs_parser_has_side_effect(njs_parser_node_t *node); njs_int_t njs_parser_variable_reference(njs_parser_t *parser, njs_parser_scope_t *scope, njs_parser_node_t *node, uintptr_t unique_id, njs_reference_type_t type); njs_token_type_t njs_parser_unexpected_token(njs_vm_t *vm, njs_parser_t *parser, njs_str_t *name, njs_token_type_t type); njs_int_t njs_parser_string_create(njs_vm_t *vm, njs_lexer_token_t *token, njs_value_t *value); void njs_parser_lexer_error(njs_parser_t *parser, njs_object_type_t type, const char *fmt, ...); void njs_parser_node_error(njs_vm_t *vm, njs_object_type_t type, njs_parser_node_t *node, njs_str_t *file, const char *fmt, ...); njs_int_t njs_parser_traverse(njs_vm_t *vm, njs_parser_node_t *root, void *ctx, njs_parser_traverse_cb_t cb); njs_int_t njs_parser_serialize_ast(njs_parser_node_t *node, njs_chb_t *chain); #define njs_parser_restricted_identifier(token) \ (token == NJS_TOKEN_ARGUMENTS || token == NJS_TOKEN_EVAL) #define njs_parser_is_lvalue(node) \ ((node)->token_type == NJS_TOKEN_NAME \ || (node)->token_type == NJS_TOKEN_PROPERTY) #define njs_parser_is_primitive(node) \ ((node)->token_type >= NJS_TOKEN_NULL \ && (node)->token_type <= NJS_TOKEN_STRING) #define njs_parser_syntax_error(parser, fmt, ...) \ njs_parser_lexer_error(parser, NJS_OBJ_TYPE_SYNTAX_ERROR, fmt, \ ##__VA_ARGS__) #define njs_parser_ref_error(parser, fmt, ...) \ njs_parser_lexer_error(parser, NJS_OBJ_TYPE_REF_ERROR, fmt, \ ##__VA_ARGS__) njs_inline njs_parser_node_t * njs_parser_node_new(njs_parser_t *parser, njs_token_type_t type) { njs_parser_node_t *node; node = njs_mp_zalloc(parser->vm->mem_pool, sizeof(njs_parser_node_t)); if (njs_fast_path(node != NULL)) { node->token_type = type; node->scope = parser->scope; } return node; } njs_inline void njs_parser_node_free(njs_parser_t *parser, njs_parser_node_t *node) { njs_mp_free(parser->vm->mem_pool, node); } njs_inline njs_parser_node_t * njs_parser_node_string(njs_vm_t *vm, njs_lexer_token_t *token, njs_parser_t *parser) { njs_int_t ret; njs_parser_node_t *node; node = njs_parser_node_new(parser, NJS_TOKEN_STRING); if (njs_slow_path(node == NULL)) { return NULL; } ret = njs_parser_string_create(vm, token, &node->u.value); if (njs_slow_path(ret != NJS_OK)) { return NULL; } node->token_line = token->line; return node; } njs_inline njs_parser_scope_t * njs_function_scope(njs_parser_scope_t *scope) { do { if (scope->type == NJS_SCOPE_GLOBAL || scope->type == NJS_SCOPE_FUNCTION) { return scope; } scope = scope->parent; } while (scope != NULL); return NULL; } #ifndef NJS_PARSER_DEBUG njs_inline void njs_parser_next(njs_parser_t *parser, njs_parser_state_func_t state) { parser->state = state; } #else njs_inline int njs_parser_height(njs_parser_t *parser, njs_queue_link_t *link) { int height; njs_queue_link_t *lnk; height = 0; for (lnk = njs_queue_first(&parser->stack); lnk != njs_queue_tail(&parser->stack); lnk = njs_queue_next(lnk)) { if (link != lnk) { height++; continue; } return height; } return -1; } #define njs_parser_next(parser, _state) \ do { \ const char *name = njs_stringify(_state); \ if (memcmp(name, "entry->state", njs_min(njs_strlen(name), 12))) { \ njs_printf("next(%s)\n", name + njs_length("njs_parser_")); \ } \ \ parser->state = _state; \ } while(0) #endif njs_inline njs_int_t njs_parser_stack_pop(njs_parser_t *parser) { njs_queue_link_t *link; njs_parser_stack_entry_t *entry; entry = njs_queue_link_data(njs_queue_first(&parser->stack), njs_parser_stack_entry_t, link); link = njs_queue_first(&parser->stack); njs_queue_remove(link); #ifdef NJS_PARSER_DEBUG njs_printf(" stack_pop(%d)\n", njs_parser_height(parser, njs_queue_last(&parser->stack))); #endif njs_parser_next(parser, entry->state); parser->target = entry->node; njs_mp_free(parser->vm->mem_pool, entry); return NJS_OK; } #ifndef NJS_PARSER_DEBUG #define njs_parser_after(_p, _l, _n, _opt, _state) \ _njs_parser_after(_p, _l, _n, _opt, _state) #else #define njs_parser_after(__p, _l, _n, _opt, _state) \ ( \ njs_printf(" after(%s, link:%d, height:%d)\n", \ &njs_stringify(_state)[njs_min(njs_strlen(_state), 11)], \ njs_parser_height(__p, _l), \ njs_parser_height(__p, njs_queue_last(&(__p)->stack))), \ _njs_parser_after(__p, _l, _n, _opt, _state) \ ) #endif njs_inline njs_int_t _njs_parser_after(njs_parser_t *parser, njs_queue_link_t *link, void *node, njs_bool_t is_optional, njs_parser_state_func_t state) { njs_parser_stack_entry_t *entry; entry = njs_mp_alloc(parser->vm->mem_pool, sizeof(njs_parser_stack_entry_t)); if (njs_slow_path(entry == NULL)) { return NJS_ERROR; } entry->state = state; entry->node = node; entry->optional = is_optional; njs_queue_insert_before(link, &entry->link); return NJS_OK; } njs_inline njs_int_t njs_parser_failed(njs_parser_t *parser) { njs_parser_next(parser, njs_parser_failed_state); parser->target = NULL; return NJS_DECLINED; } #endif /* _NJS_PARSER_H_INCLUDED_ */ njs-0.8.9/src/njs_promise.c000066400000000000000000001462431474132077100156250ustar00rootroot00000000000000 /* * Copyright (C) Alexander Borisov * Copyright (C) Nginx, Inc. */ #include typedef enum { NJS_PROMISE_HANDLE = 0, NJS_PROMISE_REJECT } njs_promise_rejection_type_t; typedef enum { NJS_PROMISE_ALL = 0, NJS_PROMISE_ALL_SETTLED, NJS_PROMISE_ANY } njs_promise_function_type_t; typedef struct { njs_promise_capability_t *capability; njs_promise_type_t type; njs_queue_link_t link; njs_value_t handler; } njs_promise_reaction_t; typedef struct { njs_value_t promise; njs_value_t finally; njs_value_t constructor; njs_bool_t resolved; njs_bool_t *resolved_ref; njs_promise_capability_t *capability; njs_function_native_t handler; } njs_promise_context_t; typedef struct { njs_bool_t already_called; uint32_t index; uint32_t *remaining_elements; njs_array_t *values; njs_promise_capability_t *capability; } njs_promise_all_context_t; typedef struct { njs_iterator_args_t args; uint32_t *remaining; njs_value_t *constructor; njs_function_t *function; njs_promise_capability_t *capability; } njs_promise_iterator_args_t; static njs_promise_t *njs_promise_constructor_call(njs_vm_t *vm, njs_function_t *function); static njs_int_t njs_promise_create_resolving_functions(njs_vm_t *vm, njs_promise_t *promise, njs_value_t *dst); static njs_int_t njs_promise_value_constructor(njs_vm_t *vm, njs_value_t *value, njs_value_t *dst); static njs_int_t njs_promise_capability_executor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_promise_resolve_function(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_promise_reject_function(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_promise_then_finally_function(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_promise_then_finally_return(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_promise_catch_finally_return(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_promise_reaction_job(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_promise_resolve_thenable_job(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_promise_perform_all(njs_vm_t *vm, njs_value_t *iterator, njs_promise_iterator_args_t *pargs, njs_iterator_handler_t handler, njs_value_t *retval); static njs_int_t njs_promise_perform_all_handler(njs_vm_t *vm, njs_iterator_args_t *args, njs_value_t *value, int64_t index, njs_value_t *retval); static njs_int_t njs_promise_all_resolve_element_functions(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_promise_perform_all_settled_handler(njs_vm_t *vm, njs_iterator_args_t *args, njs_value_t *value, int64_t index, njs_value_t *retval); static njs_int_t njs_promise_all_settled_element_functions(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t rejected, njs_value_t *retval); static njs_int_t njs_promise_perform_any_handler(njs_vm_t *vm, njs_iterator_args_t *args, njs_value_t *value, int64_t index, njs_value_t *retval); static njs_int_t njs_promise_any_reject_element_functions(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_promise_perform_race_handler(njs_vm_t *vm, njs_iterator_args_t *args, njs_value_t *value, int64_t index, njs_value_t *retval); static const njs_value_t string_resolve = njs_string("resolve"); static const njs_value_t string_any_rejected = njs_long_string("All promises were rejected"); static njs_promise_t * njs_promise_alloc(njs_vm_t *vm) { njs_promise_t *promise; njs_promise_data_t *data; promise = njs_mp_alloc(vm->mem_pool, sizeof(njs_promise_t) + sizeof(njs_promise_data_t)); if (njs_slow_path(promise == NULL)) { goto memory_error; } njs_lvlhsh_init(&promise->object.hash); njs_lvlhsh_init(&promise->object.shared_hash); promise->object.type = NJS_PROMISE; promise->object.shared = 0; promise->object.extensible = 1; promise->object.error_data = 0; promise->object.fast_array = 0; promise->object.__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_PROMISE); promise->object.slots = NULL; data = (njs_promise_data_t *) ((uint8_t *) promise + sizeof(njs_promise_t)); data->state = NJS_PROMISE_PENDING; data->is_handled = 0; njs_queue_init(&data->fulfill_queue); njs_queue_init(&data->reject_queue); njs_set_data(&promise->value, data, 0); return promise; memory_error: njs_memory_error(vm); return NULL; } njs_int_t njs_promise_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_promise_t *promise; njs_function_t *function; if (njs_slow_path(!vm->top_frame->ctor)) { njs_type_error(vm, "the Promise constructor must be called with new"); return NJS_ERROR; } if (njs_slow_path(!njs_is_function(njs_arg(args, nargs, 1)))) { njs_type_error(vm, "unexpected arguments"); return NJS_ERROR; } function = njs_function(njs_argument(args, 1)); promise = njs_promise_constructor_call(vm, function); if (njs_slow_path(promise == NULL)) { return NJS_ERROR; } njs_set_promise(retval, promise); return NJS_OK; } njs_int_t njs_vm_promise_create(njs_vm_t *vm, njs_value_t *retval, njs_value_t *callbacks) { njs_int_t ret; njs_promise_t *promise; promise = njs_promise_alloc(vm); if (njs_slow_path(promise == NULL)) { return NJS_ERROR; } ret = njs_promise_create_resolving_functions(vm, promise, callbacks); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_set_promise(retval, promise); return NJS_OK; } static njs_promise_t * njs_promise_constructor_call(njs_vm_t *vm, njs_function_t *function) { njs_int_t ret; njs_value_t retval, arguments[2]; njs_promise_t *promise; promise = njs_promise_alloc(vm); if (njs_slow_path(promise == NULL)) { return NULL; } ret = njs_promise_create_resolving_functions(vm, promise, arguments); if (njs_slow_path(ret != NJS_OK)) { return NULL; } ret = njs_function_call(vm, function, &njs_value_undefined, arguments, 2, &retval); if (njs_slow_path(ret != NJS_OK)) { if (njs_slow_path(njs_is_memory_error(vm, &vm->exception))) { return NULL; } retval = njs_vm_exception(vm); ret = njs_function_call(vm, njs_function(&arguments[1]), &njs_value_undefined, &retval, 1, &retval); if (njs_slow_path(ret != NJS_OK)) { return NULL; } } return promise; } njs_function_t * njs_promise_create_function(njs_vm_t *vm, size_t context_size) { njs_function_t *function; njs_promise_context_t *context; function = njs_mp_zalloc(vm->mem_pool, sizeof(njs_function_t)); if (njs_slow_path(function == NULL)) { goto memory_error; } if (context_size > 0) { context = njs_mp_zalloc(vm->mem_pool, context_size); if (njs_slow_path(context == NULL)) { njs_mp_free(vm->mem_pool, function); goto memory_error; } } else { context = NULL; } function->object.__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_FUNCTION); function->object.shared_hash = vm->shared->arrow_instance_hash; function->object.type = NJS_FUNCTION; function->object.extensible = 1; function->native = 1; function->context = context; return function; memory_error: njs_memory_error(vm); return NULL; } static njs_int_t njs_promise_create_resolving_functions(njs_vm_t *vm, njs_promise_t *promise, njs_value_t *dst) { unsigned i; njs_function_t *function; njs_promise_context_t *context, *resolve_context; i = 0; /* Some compilers give at error an uninitialized context if using for. */ do { function = njs_promise_create_function(vm, sizeof(njs_promise_context_t)); if (njs_slow_path(function == NULL)) { return NJS_ERROR; } function->args_count = 1; context = function->context; context->resolved_ref = &context->resolved; njs_set_promise(&context->promise, promise); njs_set_function(&dst[i], function); } while (++i < 2); njs_function(&dst[0])->u.native = njs_promise_resolve_function; njs_function(&dst[1])->u.native = njs_promise_reject_function; resolve_context = njs_function(&dst[0])->context; resolve_context->resolved_ref = &context->resolved; return NJS_OK; } njs_promise_capability_t * njs_promise_new_capability(njs_vm_t *vm, njs_value_t *constructor) { njs_int_t ret; njs_value_t argument, this; njs_object_t *object; njs_function_t *function; njs_promise_context_t *context; njs_promise_capability_t *capability; object = NULL; function = NULL; ret = njs_promise_value_constructor(vm, constructor, constructor); if (njs_slow_path(ret != NJS_OK)) { return NULL; } capability = njs_mp_zalloc(vm->mem_pool, sizeof(njs_promise_capability_t)); if (njs_slow_path(capability == NULL)) { njs_memory_error(vm); return NULL; } function = njs_promise_create_function(vm, sizeof(njs_promise_context_t)); if (njs_slow_path(function == NULL)) { return NULL; } njs_set_undefined(&capability->resolve); njs_set_undefined(&capability->reject); function->u.native = njs_promise_capability_executor; function->args_count = 2; context = function->context; context->capability = capability; njs_set_function(&argument, function); object = njs_function_new_object(vm, constructor); if (njs_slow_path(object == NULL)) { return NULL; } njs_set_object(&this, object); ret = njs_function_call2(vm, njs_function(constructor), &this, &argument, 1, &capability->promise, 1); if (njs_slow_path(ret != NJS_OK)) { return NULL; } if (njs_slow_path(!njs_is_function(&capability->resolve))) { njs_type_error(vm, "capability resolve slot is not callable"); return NULL; } if (njs_slow_path(!njs_is_function(&capability->reject))) { njs_type_error(vm, "capability reject slot is not callable"); return NULL; } return capability; } static njs_int_t njs_promise_value_constructor(njs_vm_t *vm, njs_value_t *value, njs_value_t *dst) { njs_int_t ret; static const njs_value_t string_constructor = njs_string("constructor"); if (njs_is_function(value)) { *dst = *value; return NJS_OK; } ret = njs_value_property(vm, value, njs_value_arg(&string_constructor), dst); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (!njs_is_function(dst)) { njs_type_error(vm, "the object does not contain a constructor"); return NJS_ERROR; } return NJS_OK; } static njs_int_t njs_promise_capability_executor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_promise_context_t *context; njs_promise_capability_t *capability; context = vm->top_frame->function->context; capability = context->capability; if (njs_slow_path(capability == NULL)) { njs_type_error(vm, "failed to get function capability"); return NJS_ERROR; } if (!njs_is_undefined(&capability->resolve)) { njs_type_error(vm, "capability resolve slot is not undefined"); return NJS_ERROR; } if (!njs_is_undefined(&capability->reject)) { njs_type_error(vm, "capability reject slot is not undefined"); return NJS_ERROR; } capability->resolve = *njs_arg(args, nargs, 1); capability->reject = *njs_arg(args, nargs, 2); njs_value_assign(retval, &njs_value_undefined); return NJS_OK; } njs_inline njs_value_t * njs_promise_trigger_reactions(njs_vm_t *vm, njs_value_t *value, njs_queue_t *queue) { njs_int_t ret; njs_value_t arguments[2]; njs_function_t *function; njs_queue_link_t *link; njs_promise_reaction_t *reaction; for (link = njs_queue_first(queue); link != njs_queue_tail(queue); link = njs_queue_next(link)) { reaction = njs_queue_link_data(link, njs_promise_reaction_t, link); function = njs_promise_create_function(vm, sizeof(njs_promise_context_t)); function->u.native = njs_promise_reaction_job; njs_set_data(&arguments[0], reaction, 0); arguments[1] = *value; ret = njs_vm_enqueue_job(vm, function, arguments, 2); if (njs_slow_path(ret != NJS_OK)) { return njs_value_arg(&njs_value_null); } } return njs_value_arg(&njs_value_undefined); } njs_inline njs_value_t * njs_promise_fulfill(njs_vm_t *vm, njs_promise_t *promise, njs_value_t *value) { njs_queue_t queue; njs_promise_data_t *data; data = njs_data(&promise->value); data->result = *value; data->state = NJS_PROMISE_FULFILL; if (njs_queue_is_empty(&data->fulfill_queue)) { return njs_value_arg(&njs_value_undefined); } else { queue = data->fulfill_queue; queue.head.prev->next = &queue.head; queue.head.next->prev = &queue.head; } njs_queue_init(&data->fulfill_queue); njs_queue_init(&data->reject_queue); return njs_promise_trigger_reactions(vm, value, &queue); } njs_inline njs_value_t * njs_promise_reject(njs_vm_t *vm, njs_promise_t *promise, njs_value_t *reason) { njs_queue_t queue; njs_value_t promise_value; njs_promise_data_t *data; data = njs_data(&promise->value); data->result = *reason; data->state = NJS_PROMISE_REJECTED; if (!data->is_handled) { if (vm->rejection_tracker != NULL) { njs_set_promise(&promise_value, promise); vm->rejection_tracker(vm, vm->rejection_tracker_opaque, 0, &promise_value, reason); } } if (njs_queue_is_empty(&data->reject_queue)) { return njs_value_arg(&njs_value_undefined); } else { queue = data->reject_queue; queue.head.prev->next = &queue.head; queue.head.next->prev = &queue.head; } njs_queue_init(&data->fulfill_queue); njs_queue_init(&data->reject_queue); return njs_promise_trigger_reactions(vm, reason, &queue); } static njs_int_t njs_promise_invoke_then(njs_vm_t *vm, njs_value_t *promise, njs_value_t *args, njs_int_t nargs, njs_value_t *retval) { njs_int_t ret; njs_value_t function; static const njs_value_t string_then = njs_string("then"); ret = njs_value_property(vm, promise, njs_value_arg(&string_then), &function); if (njs_slow_path(ret != NJS_OK)) { if (ret == NJS_DECLINED) { goto failed; } return NJS_ERROR; } if (njs_fast_path(njs_is_function(&function))) { return njs_function_call(vm, njs_function(&function), promise, args, nargs, retval); } failed: njs_type_error(vm, "is not a function"); return NJS_ERROR; } static njs_int_t njs_promise_resolve_function(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_value_t *resolution, error, then, arguments[3]; njs_promise_t *promise; njs_function_t *function; njs_native_frame_t *active_frame; njs_promise_context_t *context; static const njs_value_t string_then = njs_string("then"); active_frame = vm->top_frame; context = active_frame->function->context; promise = njs_promise(&context->promise); if (*context->resolved_ref) { njs_value_assign(retval, &njs_value_undefined); return NJS_OK; } *context->resolved_ref = 1; resolution = njs_arg(args, nargs, 1); if (njs_values_same(resolution, &context->promise)) { njs_error_fmt_new(vm, &error, NJS_OBJ_TYPE_TYPE_ERROR, "promise self resolution"); if (njs_slow_path(!njs_is_error(&error))) { return NJS_ERROR; } njs_value_assign(retval, njs_promise_reject(vm, promise, &error)); return NJS_OK; } if (!njs_is_object(resolution)) { goto fulfill; } ret = njs_value_property(vm, resolution, njs_value_arg(&string_then), &then); if (njs_slow_path(ret == NJS_ERROR)) { if (njs_slow_path(njs_is_memory_error(vm, &vm->exception))) { return NJS_ERROR; } error = njs_vm_exception(vm); njs_value_assign(retval, njs_promise_reject(vm, promise, &error)); if (njs_slow_path(njs_is_null(retval))) { return NJS_ERROR; } return NJS_OK; } if (!njs_is_function(&then)) { goto fulfill; } arguments[0] = context->promise; arguments[1] = *resolution; arguments[2] = then; function = njs_promise_create_function(vm, sizeof(njs_promise_context_t)); if (njs_slow_path(function == NULL)) { return NJS_ERROR; } function->u.native = njs_promise_resolve_thenable_job; ret = njs_vm_enqueue_job(vm, function, arguments, 3); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_value_assign(retval, &njs_value_undefined); return NJS_OK; fulfill: njs_value_assign(retval, njs_promise_fulfill(vm, promise, resolution)); if (njs_slow_path(njs_is_null(retval))) { return NJS_ERROR; } return NJS_OK; } static njs_int_t njs_promise_object_resolve(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { if (njs_slow_path(!njs_is_object(njs_argument(args, 0)))) { njs_type_error(vm, "this value is not an object"); return NJS_ERROR; } return njs_promise_resolve(vm, njs_argument(args, 0), njs_arg(args, nargs, 1), retval); } njs_int_t njs_promise_resolve(njs_vm_t *vm, njs_value_t *constructor, njs_value_t *x, njs_value_t *retval) { njs_int_t ret; njs_value_t value; njs_promise_capability_t *capability; static const njs_value_t string_constructor = njs_string("constructor"); if (njs_is_promise(x)) { ret = njs_value_property(vm, x, njs_value_arg(&string_constructor), &value); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } if (njs_values_same(&value, constructor)) { njs_value_assign(retval, x); return NJS_OK; } } capability = njs_promise_new_capability(vm, constructor); if (njs_slow_path(capability == NULL)) { return NJS_ERROR; } ret = njs_function_call(vm, njs_function(&capability->resolve), &njs_value_undefined, x, 1, &value); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_value_assign(retval, &capability->promise); return NJS_OK; } static njs_int_t njs_promise_reject_function(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_value_t *value; njs_native_frame_t *active_frame; njs_promise_context_t *context; active_frame = vm->top_frame; context = active_frame->function->context; if (*context->resolved_ref) { njs_value_assign(retval, &njs_value_undefined); return NJS_OK; } *context->resolved_ref = 1; value = njs_promise_reject(vm, njs_promise(&context->promise), njs_arg(args, nargs, 1)); if (njs_slow_path(value->type == NJS_NULL)) { return NJS_ERROR; } njs_value_assign(retval, value); return NJS_OK; } static njs_int_t njs_promise_object_reject(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_value_t value; njs_promise_capability_t *capability; if (njs_slow_path(!njs_is_object(njs_argument(args, 0)))) { njs_type_error(vm, "this value is not an object"); return NJS_ERROR; } capability = njs_promise_new_capability(vm, njs_argument(args, 0)); if (njs_slow_path(capability == NULL)) { return NJS_ERROR; } ret = njs_function_call(vm, njs_function(&capability->reject), &njs_value_undefined, njs_arg(args, nargs, 1), 1, &value); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_value_assign(retval, &capability->promise); return NJS_OK; } static njs_int_t njs_promise_prototype_then(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_value_t *promise, *fulfilled, *rejected, constructor; njs_function_t *function; njs_promise_capability_t *capability; promise = njs_argument(args, 0); if (njs_slow_path(!njs_is_promise(promise))) { goto failed; } function = njs_promise_create_function(vm, sizeof(njs_promise_context_t)); function->u.native = njs_promise_constructor; njs_set_function(&constructor, function); ret = njs_value_species_constructor(vm, promise, &constructor, &constructor); if (njs_slow_path(ret != NJS_OK)) { return ret; } capability = njs_promise_new_capability(vm, &constructor); if (njs_slow_path(capability == NULL)) { return NJS_ERROR; } fulfilled = njs_arg(args, nargs, 1); rejected = njs_arg(args, nargs, 2); return njs_promise_perform_then(vm, promise, fulfilled, rejected, capability, retval); failed: njs_type_error(vm, "required a promise object"); return NJS_ERROR; } njs_int_t njs_promise_perform_then(njs_vm_t *vm, njs_value_t *value, njs_value_t *fulfilled, njs_value_t *rejected, njs_promise_capability_t *capability, njs_value_t *retval) { njs_int_t ret; njs_value_t arguments[2], promise_value; njs_promise_t *promise; njs_function_t *function; njs_promise_data_t *data; njs_promise_reaction_t *fulfilled_reaction, *rejected_reaction; njs_assert(njs_is_promise(value)); if (!njs_is_function(fulfilled)) { fulfilled = njs_value_arg(&njs_value_undefined); } if (!njs_is_function(rejected)) { rejected = njs_value_arg(&njs_value_undefined); } promise = njs_promise(value); data = njs_data(&promise->value); fulfilled_reaction = njs_mp_alloc(vm->mem_pool, sizeof(njs_promise_reaction_t)); if (njs_slow_path(fulfilled_reaction == NULL)) { njs_memory_error(vm); return NJS_ERROR; } fulfilled_reaction->capability = capability; fulfilled_reaction->handler = *fulfilled; fulfilled_reaction->type = NJS_PROMISE_FULFILL; rejected_reaction = njs_mp_alloc(vm->mem_pool, sizeof(njs_promise_reaction_t)); if (njs_slow_path(rejected_reaction == NULL)) { njs_memory_error(vm); return NJS_ERROR; } rejected_reaction->capability = capability; rejected_reaction->handler = *rejected; rejected_reaction->type = NJS_PROMISE_REJECTED; if (data->state == NJS_PROMISE_PENDING) { njs_queue_insert_tail(&data->fulfill_queue, &fulfilled_reaction->link); njs_queue_insert_tail(&data->reject_queue, &rejected_reaction->link); } else { function = njs_promise_create_function(vm, sizeof(njs_promise_context_t)); function->u.native = njs_promise_reaction_job; if (data->state == NJS_PROMISE_REJECTED) { njs_set_data(&arguments[0], rejected_reaction, 0); if (vm->rejection_tracker != NULL) { njs_set_promise(&promise_value, promise); vm->rejection_tracker(vm, vm->rejection_tracker_opaque, 1, &promise_value, &data->result); } } else { njs_set_data(&arguments[0], fulfilled_reaction, 0); } arguments[1] = data->result; ret = njs_vm_enqueue_job(vm, function, arguments, 2); if (njs_slow_path(ret != NJS_OK)) { return ret; } } data->is_handled = 1; if (capability == NULL) { njs_set_undefined(retval); } else { njs_value_assign(retval, &capability->promise); } return NJS_OK; } static njs_int_t njs_promise_prototype_catch(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_value_t arguments[2]; arguments[0] = njs_value_undefined; arguments[1] = *njs_arg(args, nargs, 1); return njs_promise_invoke_then(vm, njs_argument(args, 0), arguments, 2, retval); } static njs_int_t njs_promise_prototype_finally(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_value_t *promise, *finally, constructor, arguments[2]; njs_function_t *function; njs_promise_context_t *context; promise = njs_argument(args, 0); if (njs_slow_path(!njs_is_object(promise))) { njs_type_error(vm, "required a object"); return NJS_ERROR; } finally = njs_arg(args, nargs, 1); function = njs_promise_create_function(vm, sizeof(njs_promise_context_t)); function->u.native = njs_promise_constructor; njs_set_function(&constructor, function); ret = njs_value_species_constructor(vm, promise, &constructor, &constructor); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (!njs_is_function(finally)) { arguments[0] = *finally; arguments[1] = *finally; return njs_promise_invoke_then(vm, promise, arguments, 2, retval); } function = njs_promise_create_function(vm, sizeof(njs_promise_context_t)); if (njs_slow_path(function == NULL)) { return NJS_ERROR; } function->u.native = njs_promise_then_finally_function; function->args_count = 1; context = function->context; context->constructor = constructor; context->finally = *finally; context->handler = njs_promise_then_finally_return; njs_set_function(&arguments[0], function); function = njs_promise_create_function(vm, sizeof(njs_promise_context_t)); if (njs_slow_path(function == NULL)) { njs_mp_free(vm->mem_pool, njs_function(&arguments[0])); return NJS_ERROR; } function->u.native = njs_promise_then_finally_function; function->args_count = 1; context = function->context; context->constructor = constructor; context->finally = *finally; context->handler = njs_promise_catch_finally_return; njs_set_function(&arguments[1], function); return njs_promise_invoke_then(vm, promise, arguments, 2, retval); } static njs_int_t njs_promise_then_finally_function(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_value_t value, argument; njs_function_t *function; njs_native_frame_t *frame; njs_promise_context_t *context; frame = vm->top_frame; context = frame->function->context; ret = njs_function_call(vm, njs_function(&context->finally), &njs_value_undefined, args, 0, &value); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_promise_resolve(vm, &context->constructor, &value, &value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } function = njs_promise_create_function(vm, sizeof(njs_value_t)); if (njs_slow_path(function == NULL)) { return NJS_ERROR; } function->u.native = context->handler; *((njs_value_t *) function->context) = *njs_arg(args, nargs, 1); njs_set_function(&argument, function); return njs_promise_invoke_then(vm, &value, &argument, 1, retval); } static njs_int_t njs_promise_then_finally_return(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_value_assign(retval, vm->top_frame->function->context); return NJS_OK; } static njs_int_t njs_promise_catch_finally_return(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_vm_throw(vm, vm->top_frame->function->context); return NJS_ERROR; } static njs_int_t njs_promise_reaction_job(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_bool_t is_error; njs_value_t value, *argument; njs_promise_reaction_t *reaction; njs_promise_capability_t *capability; reaction = njs_data(njs_arg(args, nargs, 1)); argument = njs_arg(args, nargs, 2); capability = reaction->capability; is_error = 0; if (njs_is_undefined(&reaction->handler)) { if (reaction->type == NJS_PROMISE_REJECTED) { is_error = 1; } njs_value_assign(&value, argument); } else { ret = njs_function_call(vm, njs_function(&reaction->handler), &njs_value_undefined, argument, 1, &value); if (njs_slow_path(ret != NJS_OK)) { if (njs_slow_path(njs_is_memory_error(vm, &vm->exception))) { return NJS_ERROR; } value = njs_vm_exception(vm); is_error = 1; } } if (capability == NULL) { njs_value_assign(retval, &value); return NJS_OK; } if (is_error) { ret = njs_function_call(vm, njs_function(&capability->reject), &njs_value_undefined, &value, 1, retval); } else { ret = njs_function_call(vm, njs_function(&capability->resolve), &njs_value_undefined, &value, 1, retval); } if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } return NJS_OK; } static njs_int_t njs_promise_resolve_thenable_job(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_value_t *promise, value, arguments[2]; promise = njs_arg(args, nargs, 1); ret = njs_promise_create_resolving_functions(vm, njs_promise(promise), arguments); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_function_call(vm, njs_function(njs_arg(args, nargs, 3)), njs_arg(args, nargs, 2), arguments, 2, &value); if (njs_slow_path(ret != NJS_OK)) { if (njs_slow_path(njs_is_memory_error(vm, &vm->exception))) { return NJS_ERROR; } value = njs_vm_exception(vm); ret = njs_function_call(vm, njs_function(&arguments[1]), &njs_value_undefined, &value, 1, retval); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } return NJS_OK; } static njs_int_t njs_promise_all(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t function_type, njs_value_t *retval) { njs_int_t ret; njs_value_t *promise_ctor, resolve; njs_iterator_handler_t handler; njs_promise_iterator_args_t pargs; promise_ctor = njs_argument(args, 0); pargs.capability = njs_promise_new_capability(vm, promise_ctor); if (njs_slow_path(pargs.capability == NULL)) { return NJS_ERROR; } ret = njs_value_property(vm, promise_ctor, njs_value_arg(&string_resolve), &resolve); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (njs_slow_path(!njs_is_function(&resolve))) { njs_type_error(vm, "resolve is not callable"); return NJS_ERROR; } pargs.function = njs_function(&resolve); pargs.constructor = promise_ctor; switch (function_type) { case NJS_PROMISE_ALL_SETTLED: handler = njs_promise_perform_all_settled_handler; break; case NJS_PROMISE_ANY: handler = njs_promise_perform_any_handler; break; default: handler = njs_promise_perform_all_handler; break; } return njs_promise_perform_all(vm, njs_arg(args, nargs, 1), &pargs, handler, retval); } static njs_int_t njs_promise_perform_all(njs_vm_t *vm, njs_value_t *iterator, njs_promise_iterator_args_t *pargs, njs_iterator_handler_t handler, njs_value_t *retval) { int64_t length; njs_int_t ret; njs_value_t argument; njs_object_t *error; if (njs_slow_path(!njs_is_object(pargs->constructor))) { njs_type_error(vm, "constructor is not object"); return NJS_ERROR; } njs_memzero(&pargs->args, sizeof(njs_iterator_args_t)); ret = njs_object_length(vm, iterator, &length); if (njs_slow_path(ret != NJS_OK)) { return ret; } pargs->args.data = njs_array_alloc(vm, 1, 0, NJS_ARRAY_SPARE); if (njs_slow_path(pargs->args.data == NULL)) { return NJS_ERROR; } pargs->remaining = njs_mp_alloc(vm->mem_pool, sizeof(uint32_t)); if (njs_slow_path(pargs->remaining == NULL)) { njs_memory_error(vm); return NJS_ERROR; } (*pargs->remaining) = 1; njs_value_assign(&pargs->args.value, iterator); pargs->args.to = length; ret = njs_object_iterate(vm, &pargs->args, handler, retval); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (--(*pargs->remaining) == 0) { njs_mp_free(vm->mem_pool, pargs->remaining); njs_set_array(&argument, pargs->args.data); if (handler == njs_promise_perform_any_handler) { error = njs_error_alloc(vm, njs_vm_proto(vm, NJS_OBJ_TYPE_AGGREGATE_ERROR), NULL, &string_any_rejected, &argument); if (njs_slow_path(error == NULL)) { return NJS_ERROR; } njs_set_object(&argument, error); } ret = njs_function_call(vm, njs_function(&pargs->capability->resolve), &njs_value_undefined, &argument, 1, retval); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } } njs_value_assign(retval, &pargs->capability->promise); return NJS_OK; } static njs_int_t njs_promise_perform_all_handler(njs_vm_t *vm, njs_iterator_args_t *args, njs_value_t *value, int64_t index, njs_value_t *retval) { njs_int_t ret; njs_array_t *array; njs_value_t arguments[2], next, arr_value; njs_function_t *on_fulfilled; njs_promise_capability_t *capability; njs_promise_all_context_t *context; njs_promise_iterator_args_t *pargs; if (!njs_is_valid(value)) { value = njs_value_arg(&njs_value_undefined); } pargs = (njs_promise_iterator_args_t *) args; capability = pargs->capability; array = args->data; njs_set_array(&arr_value, array); ret = njs_value_property_i64_set(vm, &arr_value, index, njs_value_arg(&njs_value_undefined)); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } ret = njs_function_call(vm, pargs->function, pargs->constructor, value, 1, &next); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } on_fulfilled = njs_promise_create_function(vm, sizeof(njs_promise_all_context_t)); if (njs_slow_path(on_fulfilled == NULL)) { return NJS_ERROR; } on_fulfilled->u.native = njs_promise_all_resolve_element_functions; on_fulfilled->args_count = 1; context = on_fulfilled->context; context->already_called = 0; context->index = (uint32_t) index; context->values = pargs->args.data; context->capability = capability; context->remaining_elements = pargs->remaining; (*pargs->remaining)++; njs_set_function(&arguments[0], on_fulfilled); arguments[1] = capability->reject; ret = njs_promise_invoke_then(vm, &next, arguments, 2, retval); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } return NJS_OK; } static njs_int_t njs_promise_all_resolve_element_functions(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_value_t arr_value; njs_promise_all_context_t *context; context = vm->top_frame->function->context; if (context->already_called) { njs_value_assign(retval, &njs_value_undefined); return NJS_OK; } context->already_called = 1; njs_set_array(&arr_value, context->values); ret = njs_value_property_i64_set(vm, &arr_value, context->index, njs_arg(args, nargs, 1)); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (--(*context->remaining_elements) == 0) { njs_mp_free(vm->mem_pool, context->remaining_elements); return njs_function_call(vm, njs_function(&context->capability->resolve), &njs_value_undefined, &arr_value, 1, retval); } njs_value_assign(retval, &njs_value_undefined); return NJS_OK; } static njs_int_t njs_promise_perform_all_settled_handler(njs_vm_t *vm, njs_iterator_args_t *args, njs_value_t *value, int64_t index, njs_value_t *retval) { njs_int_t ret; njs_array_t *array; njs_value_t arguments[2], next, arr_value; njs_function_t *on_fulfilled, *on_rejected; njs_promise_capability_t *capability; njs_promise_all_context_t *context; njs_promise_iterator_args_t *pargs; if (!njs_is_valid(value)) { value = njs_value_arg(&njs_value_undefined); } pargs = (njs_promise_iterator_args_t *) args; capability = pargs->capability; array = args->data; njs_set_array(&arr_value, array); ret = njs_value_property_i64_set(vm, &arr_value, index, njs_value_arg(&njs_value_undefined)); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } ret = njs_function_call(vm, pargs->function, pargs->constructor, value, 1, &next); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } on_fulfilled = njs_promise_create_function(vm, sizeof(njs_promise_all_context_t)); if (njs_slow_path(on_fulfilled == NULL)) { return NJS_ERROR; } context = on_fulfilled->context; context->already_called = 0; context->index = (uint32_t) index; context->values = pargs->args.data; context->capability = capability; context->remaining_elements = pargs->remaining; on_rejected = njs_promise_create_function(vm, 0); if (njs_slow_path(on_rejected == NULL)) { return NJS_ERROR; } on_fulfilled->u.native = njs_promise_all_settled_element_functions; on_rejected->u.native = njs_promise_all_settled_element_functions; on_rejected->magic8 = 1; /* rejected. */ on_fulfilled->args_count = 1; on_rejected->args_count = 1; on_rejected->context = context; (*pargs->remaining)++; njs_set_function(&arguments[0], on_fulfilled); njs_set_function(&arguments[1], on_rejected); ret = njs_promise_invoke_then(vm, &next, arguments, 2, retval); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } return NJS_OK; } static njs_int_t njs_promise_all_settled_element_functions(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t rejected, njs_value_t *retval) { njs_int_t ret; njs_value_t obj_value, arr_value; njs_object_t *obj; const njs_value_t *status, *set; njs_promise_all_context_t *context; static const njs_value_t string_status = njs_string("status"); static const njs_value_t string_fulfilled = njs_string("fulfilled"); static const njs_value_t string_value = njs_string("value"); static const njs_value_t string_rejected = njs_string("rejected"); static const njs_value_t string_reason = njs_string("reason"); context = vm->top_frame->function->context; if (context->already_called) { njs_value_assign(retval, &njs_value_undefined); return NJS_OK; } context->already_called = 1; obj = njs_object_alloc(vm); if (njs_slow_path(obj == NULL)) { return NJS_ERROR; } njs_set_object(&obj_value, obj); if (rejected) { status = &string_rejected; set = &string_reason; } else { status = &string_fulfilled; set = &string_value; } ret = njs_value_property_set(vm, &obj_value, njs_value_arg(&string_status), njs_value_arg(status)); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } ret = njs_value_property_set(vm, &obj_value, njs_value_arg(set), njs_arg(args, nargs, 1)); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } njs_set_array(&arr_value, context->values); ret = njs_value_property_i64_set(vm, &arr_value, context->index, &obj_value); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (--(*context->remaining_elements) == 0) { njs_mp_free(vm->mem_pool, context->remaining_elements); return njs_function_call(vm, njs_function(&context->capability->resolve), &njs_value_undefined, &arr_value, 1, retval); } njs_value_assign(retval, &njs_value_undefined); return NJS_OK; } static njs_int_t njs_promise_perform_any_handler(njs_vm_t *vm, njs_iterator_args_t *args, njs_value_t *value, int64_t index, njs_value_t *retval) { njs_int_t ret; njs_array_t *array; njs_value_t arguments[2], next, arr_value; njs_function_t *on_rejected; njs_promise_capability_t *capability; njs_promise_all_context_t *context; njs_promise_iterator_args_t *pargs; if (!njs_is_valid(value)) { value = njs_value_arg(&njs_value_undefined); } pargs = (njs_promise_iterator_args_t *) args; capability = pargs->capability; array = args->data; njs_set_array(&arr_value, array); ret = njs_value_property_i64_set(vm, &arr_value, index, njs_value_arg(&njs_value_undefined)); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } ret = njs_function_call(vm, pargs->function, pargs->constructor, value, 1, &next); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } on_rejected = njs_promise_create_function(vm, sizeof(njs_promise_all_context_t)); if (njs_slow_path(on_rejected == NULL)) { return NJS_ERROR; } on_rejected->u.native = njs_promise_any_reject_element_functions; on_rejected->args_count = 1; context = on_rejected->context; context->already_called = 0; context->index = (uint32_t) index; context->values = pargs->args.data; context->capability = capability; context->remaining_elements = pargs->remaining; (*pargs->remaining)++; arguments[0] = capability->resolve; njs_set_function(&arguments[1], on_rejected); ret = njs_promise_invoke_then(vm, &next, arguments, 2, retval); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } return NJS_OK; } static njs_int_t njs_promise_any_reject_element_functions(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_value_t argument, arr_value; njs_object_t *error; njs_promise_all_context_t *context; context = vm->top_frame->function->context; if (context->already_called) { njs_value_assign(retval, &njs_value_undefined); return NJS_OK; } context->already_called = 1; njs_set_array(&arr_value, context->values); ret = njs_value_property_i64_set(vm, &arr_value, context->index, njs_arg(args, nargs, 1)); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (--(*context->remaining_elements) == 0) { njs_mp_free(vm->mem_pool, context->remaining_elements); error = njs_error_alloc(vm, njs_vm_proto(vm, NJS_OBJ_TYPE_AGGREGATE_ERROR), NULL, &string_any_rejected, &arr_value); if (njs_slow_path(error == NULL)) { return NJS_ERROR; } njs_set_object(&argument, error); return njs_function_call(vm, njs_function(&context->capability->reject), &njs_value_undefined, &argument, 1, retval); } njs_value_assign(retval, &njs_value_undefined); return NJS_OK; } static njs_int_t njs_promise_race(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t length; njs_int_t ret; njs_value_t *promise_ctor, *iterator, resolve; njs_promise_iterator_args_t pargs; promise_ctor = njs_argument(args, 0); iterator = njs_arg(args, nargs, 1); pargs.capability = njs_promise_new_capability(vm, promise_ctor); if (njs_slow_path(pargs.capability == NULL)) { return NJS_ERROR; } ret = njs_value_property(vm, promise_ctor, njs_value_arg(&string_resolve), &resolve); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (njs_slow_path(!njs_is_function(&resolve))) { njs_type_error(vm, "resolve is not callable"); return NJS_ERROR; } ret = njs_object_length(vm, iterator, &length); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_memzero(&pargs.args, sizeof(njs_iterator_args_t)); pargs.function = njs_function(&resolve); pargs.constructor = promise_ctor; njs_value_assign(&pargs.args.value, iterator); pargs.args.to = length; ret = njs_object_iterate(vm, &pargs.args, njs_promise_perform_race_handler, retval); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } njs_value_assign(retval, &pargs.capability->promise); return NJS_OK; } static njs_int_t njs_promise_perform_race_handler(njs_vm_t *vm, njs_iterator_args_t *args, njs_value_t *value, int64_t index, njs_value_t *retval) { njs_int_t ret; njs_value_t arguments[2], next; njs_promise_capability_t *capability; njs_promise_iterator_args_t *pargs; if (!njs_is_valid(value)) { value = njs_value_arg(&njs_value_undefined); } pargs = (njs_promise_iterator_args_t *) args; ret = njs_function_call(vm, pargs->function, pargs->constructor, value, 1, &next); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } capability = pargs->capability; arguments[0] = capability->resolve; arguments[1] = capability->reject; (void) njs_promise_invoke_then(vm, &next, arguments, 2, retval); return NJS_OK; } static njs_int_t njs_promise_species(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_value_assign(retval, njs_argument(args, 0)); return NJS_OK; } static const njs_object_prop_t njs_promise_constructor_properties[] = { NJS_DECLARE_PROP_LENGTH(1), NJS_DECLARE_PROP_NAME("Promise"), NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), NJS_DECLARE_PROP_NATIVE("resolve", njs_promise_object_resolve, 1, 0), NJS_DECLARE_PROP_NATIVE("reject", njs_promise_object_reject, 1, 0), NJS_DECLARE_PROP_NATIVE("all", njs_promise_all, 1, NJS_PROMISE_ALL), NJS_DECLARE_PROP_NATIVE("allSettled", njs_promise_all, 1, NJS_PROMISE_ALL_SETTLED), NJS_DECLARE_PROP_NATIVE("any", njs_promise_all, 1, NJS_PROMISE_ANY), NJS_DECLARE_PROP_NATIVE("race", njs_promise_race, 1, 0), { .type = NJS_ACCESSOR, .name = njs_wellknown_symbol(NJS_SYMBOL_SPECIES), .u.accessor = njs_getter(njs_promise_species, 0), .writable = NJS_ATTRIBUTE_UNSET, .configurable = 1, }, }; const njs_object_init_t njs_promise_constructor_init = { njs_promise_constructor_properties, njs_nitems(njs_promise_constructor_properties), }; static const njs_object_prop_t njs_promise_prototype_properties[] = { NJS_DECLARE_PROP_HANDLER("constructor", njs_object_prototype_create_constructor, 0, 0, NJS_OBJECT_PROP_VALUE_CW), { .type = NJS_PROPERTY, .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG), .u.value = njs_string("Promise"), .configurable = 1, }, NJS_DECLARE_PROP_NATIVE("then", njs_promise_prototype_then, 2, 0), NJS_DECLARE_PROP_NATIVE("catch", njs_promise_prototype_catch, 1, 0), NJS_DECLARE_PROP_NATIVE("finally", njs_promise_prototype_finally, 1, 0), }; const njs_object_init_t njs_promise_prototype_init = { njs_promise_prototype_properties, njs_nitems(njs_promise_prototype_properties), }; const njs_object_type_init_t njs_promise_type_init = { .constructor = njs_native_ctor(njs_promise_constructor, 1, 0), .prototype_props = &njs_promise_prototype_init, .constructor_props = &njs_promise_constructor_init, .prototype_value = { .object = { .type = NJS_OBJECT } }, }; njs-0.8.9/src/njs_promise.h000066400000000000000000000024731474132077100156260ustar00rootroot00000000000000 /* * Copyright (C) Nginx, Inc. */ #ifndef _NJS_PROMISE_H_INCLUDED_ #define _NJS_PROMISE_H_INCLUDED_ typedef enum { NJS_PROMISE_PENDING = 0, NJS_PROMISE_FULFILL, NJS_PROMISE_REJECTED } njs_promise_type_t; typedef struct { njs_value_t promise; njs_value_t resolve; njs_value_t reject; } njs_promise_capability_t; typedef struct { njs_promise_type_t state; njs_value_t result; njs_queue_t fulfill_queue; njs_queue_t reject_queue; njs_bool_t is_handled; } njs_promise_data_t; njs_int_t njs_promise_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); njs_promise_capability_t *njs_promise_new_capability(njs_vm_t *vm, njs_value_t *constructor); njs_function_t *njs_promise_create_function(njs_vm_t *vm, size_t context_size); njs_int_t njs_promise_perform_then(njs_vm_t *vm, njs_value_t *value, njs_value_t *fulfilled, njs_value_t *rejected, njs_promise_capability_t *capability, njs_value_t *retval); njs_int_t njs_promise_resolve(njs_vm_t *vm, njs_value_t *constructor, njs_value_t *x, njs_value_t *retval); extern const njs_object_type_init_t njs_promise_type_init; #endif /* _NJS_PROMISE_H_INCLUDED_ */ njs-0.8.9/src/njs_queue.c000066400000000000000000000032351474132077100152640ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include /* * Find the middle queue element if the queue has odd number of elements, * or the first element of the queue's second part otherwise. */ njs_queue_link_t * njs_queue_middle(njs_queue_t *queue) { njs_queue_link_t *middle, *next; middle = njs_queue_first(queue); if (middle == njs_queue_last(queue)) { return middle; } next = middle; for ( ;; ) { middle = njs_queue_next(middle); next = njs_queue_next(next); if (next == njs_queue_last(queue)) { return middle; } next = njs_queue_next(next); if (next == njs_queue_last(queue)) { return middle; } } } /* * njs_queue_sort() provides a stable sort because it uses the insertion * sort algorithm. Its worst and average computational complexity is O^2. */ void njs_queue_sort(njs_queue_t *queue, njs_int_t (*compare)(const void *data, const njs_queue_link_t *, const njs_queue_link_t *), const void *data) { njs_queue_link_t *link, *prev, *next; link = njs_queue_first(queue); if (link == njs_queue_last(queue)) { return; } for (link = njs_queue_next(link); link != njs_queue_tail(queue); link = next) { prev = njs_queue_prev(link); next = njs_queue_next(link); njs_queue_remove(link); do { if (compare(data, prev, link) <= 0) { break; } prev = njs_queue_prev(prev); } while (prev != njs_queue_head(queue)); njs_queue_insert_after(prev, link); } } njs-0.8.9/src/njs_queue.h000066400000000000000000000164211474132077100152720ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_QUEUE_H_INCLUDED_ #define _NJS_QUEUE_H_INCLUDED_ typedef struct njs_queue_link_s njs_queue_link_t; struct njs_queue_link_s { njs_queue_link_t *prev; njs_queue_link_t *next; }; typedef struct { njs_queue_link_t head; } njs_queue_t; #define njs_queue_init(queue) \ do { \ (queue)->head.prev = &(queue)->head; \ (queue)->head.next = &(queue)->head; \ } while (0) #define njs_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 njs_queue_remove(). */ #define njs_queue_self(link) \ njs_queue_sentinel(link) #define njs_queue_is_empty(queue) \ (&(queue)->head == (queue)->head.prev) /* * A loop to iterate all queue links starting from head: * * njs_queue_link_t link; * } njs_type_t *tp; * * * for (lnk = njs_queue_first(queue); * lnk != njs_queue_tail(queue); * lnk = njs_queue_next(lnk)) * { * tp = njs_queue_link_data(lnk, njs_type_t, link); * * or starting from tail: * * for (lnk = njs_queue_last(queue); * lnk != njs_queue_head(queue); * lnk = njs_queue_next(lnk)) * { * tp = njs_queue_link_data(lnk, njs_type_t, link); */ #define njs_queue_first(queue) \ (queue)->head.next #define njs_queue_last(queue) \ (queue)->head.prev #define njs_queue_head(queue) \ (&(queue)->head) #define njs_queue_tail(queue) \ (&(queue)->head) #define njs_queue_next(link) \ (link)->next #define njs_queue_prev(link) \ (link)->prev #define njs_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 njs_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 njs_queue_insert_after(target, link) \ do { \ (link)->next = (target)->next; \ (link)->next->prev = (link); \ (link)->prev = (target); \ (target)->next = (link); \ } while (0) #define njs_queue_insert_before(target, link) \ do { \ (link)->next = (target); \ (link)->prev = (target)->prev; \ (target)->prev = (link); \ (link)->prev->next = (link); \ } while (0) #if (NJS_DEBUG) #define njs_queue_remove(link) \ do { \ (link)->next->prev = (link)->prev; \ (link)->prev->next = (link)->next; \ (link)->prev = NULL; \ (link)->next = NULL; \ } while (0) #else #define njs_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 njs_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 njs_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 njs_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 njs_queue_link_data(lnk, type, link) \ njs_container_of(lnk, type, link) NJS_EXPORT njs_queue_link_t *njs_queue_middle(njs_queue_t *queue); NJS_EXPORT void njs_queue_sort(njs_queue_t *queue, njs_int_t (*compare)(const void *, const njs_queue_link_t *, const njs_queue_link_t *), const void *data); #endif /* _NJS_QUEUE_H_INCLUDED_ */ njs-0.8.9/src/njs_random.c000066400000000000000000000073211474132077100154200ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include #if (NJS_HAVE_GETRANDOM) #include #elif (NJS_HAVE_CCRANDOMGENERATEBYTES) #include #include #elif (NJS_HAVE_LINUX_SYS_GETRANDOM) #include #include #elif (NJS_HAVE_GETENTROPY_SYS_RANDOM) #include #endif /* * The pseudorandom generator based on OpenBSD arc4random. Although * it is usually stated that arc4random uses RC4 pseudorandom generation * algorithm they are actually different in njs_random_add(). */ #define NJS_RANDOM_KEY_SIZE 128 njs_inline uint8_t njs_random_byte(njs_random_t *r); void njs_random_init(njs_random_t *r, njs_pid_t pid) { njs_uint_t i; r->count = 0; r->pid = pid; r->i = 0; r->j = 0; for (i = 0; i < 256; i++) { r->s[i] = i; } } void njs_random_stir(njs_random_t *r, njs_pid_t pid) { int fd; ssize_t n; struct timeval tv; union { uint32_t value[3]; u_char bytes[NJS_RANDOM_KEY_SIZE]; } key; if (r->pid == 0) { njs_random_init(r, pid); } r->pid = pid; #if (NJS_HAVE_GETRANDOM) n = getrandom(&key, NJS_RANDOM_KEY_SIZE, 0); #elif (NJS_HAVE_LINUX_SYS_GETRANDOM) /* Linux 3.17 SYS_getrandom, not available in Glibc prior to 2.25. */ n = syscall(SYS_getrandom, &key, NJS_RANDOM_KEY_SIZE, 0); #elif (NJS_HAVE_CCRANDOMGENERATEBYTES) /* Apple discourages the use of getentropy. */ n = 0; if (CCRandomGenerateBytes(&key, NJS_RANDOM_KEY_SIZE) == kCCSuccess) { n = NJS_RANDOM_KEY_SIZE; } #elif (NJS_HAVE_GETENTROPY || NJS_HAVE_GETENTROPY_SYS_RANDOM) n = 0; if (getentropy(&key, NJS_RANDOM_KEY_SIZE) == 0) { n = NJS_RANDOM_KEY_SIZE; } #else n = 0; #endif if (n != NJS_RANDOM_KEY_SIZE) { fd = open("/dev/urandom", O_RDONLY); if (fd >= 0) { n = read(fd, &key, NJS_RANDOM_KEY_SIZE); (void) close(fd); } } if (n != NJS_RANDOM_KEY_SIZE) { (void) gettimeofday(&tv, NULL); /* XOR with stack garbage. */ key.value[0] ^= tv.tv_usec; key.value[1] ^= tv.tv_sec; key.value[2] ^= getpid(); } njs_msan_unpoison(&key, NJS_RANDOM_KEY_SIZE); njs_random_add(r, key.bytes, NJS_RANDOM_KEY_SIZE); /* Drop the first 3072 bytes. */ for (n = 3072; n != 0; n--) { (void) njs_random_byte(r); } /* Stir again after 1,600,000 bytes. */ r->count = 400000; } void njs_random_add(njs_random_t *r, const u_char *key, uint32_t len) { uint8_t val; uint32_t n; for (n = 0; n < 256; n++) { val = r->s[r->i]; r->j += val + key[n % len]; r->s[r->i] = r->s[r->j]; r->s[r->j] = val; r->i++; } /* This index is not decremented in RC4 algorithm. */ r->i--; r->j = r->i; } uint32_t njs_random(njs_random_t *r) { uint32_t val; njs_pid_t pid; njs_bool_t new_pid; new_pid = 0; pid = r->pid; if (pid != -1) { pid = getpid(); if (pid != r->pid) { new_pid = 1; } } r->count--; if (r->count <= 0 || new_pid) { njs_random_stir(r, pid); } val = (uint32_t) njs_random_byte(r) << 24; val |= (uint32_t) njs_random_byte(r) << 16; val |= (uint32_t) njs_random_byte(r) << 8; val |= (uint32_t) njs_random_byte(r); return val; } njs_inline uint8_t njs_random_byte(njs_random_t *r) { uint8_t si, sj; r->i++; si = r->s[r->i]; r->j += si; sj = r->s[r->j]; r->s[r->i] = sj; r->s[r->j] = si; si += sj; return r->s[si]; } njs-0.8.9/src/njs_random.h000066400000000000000000000020371474132077100154240ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_RANDOM_H_INCLUDED_ #define _NJS_RANDOM_H_INCLUDED_ typedef struct { int32_t count; njs_pid_t pid; uint8_t i; uint8_t j; uint8_t s[256]; } njs_random_t; /* * The njs_random_t structure must be either initialized with zeros * or initialized by njs_random_init() function. The later is intended * mainly for unit test. njs_random() automatically stirs itself if * process pid changed after fork(). This pid testing can be disabled by * passing -1 as the pid argument to njs_random_init() or njs_random_stir() * functions. The testing can be later enabled by passing any positive * number, for example, a real pid number. */ NJS_EXPORT void njs_random_init(njs_random_t *r, njs_pid_t pid); NJS_EXPORT void njs_random_stir(njs_random_t *r, njs_pid_t pid); NJS_EXPORT void njs_random_add(njs_random_t *r, const u_char *key, uint32_t len); NJS_EXPORT uint32_t njs_random(njs_random_t *r); #endif /* _NJS_RANDOM_H_INCLUDED_ */ njs-0.8.9/src/njs_rbtree.c000066400000000000000000000326731474132077100154330ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include /* * The red-black tree code is based on the algorithm described in * the "Introduction to Algorithms" by Cormen, Leiserson and Rivest. */ static void njs_rbtree_insert_fixup(njs_rbtree_node_t *node); static void njs_rbtree_delete_fixup(njs_rbtree_t *tree, njs_rbtree_node_t *node); njs_inline void njs_rbtree_left_rotate(njs_rbtree_node_t *node); njs_inline void njs_rbtree_right_rotate(njs_rbtree_node_t *node); njs_inline void njs_rbtree_parent_relink(njs_rbtree_node_t *subst, njs_rbtree_node_t *node); #define NJS_RBTREE_BLACK 0 #define NJS_RBTREE_RED 1 #define njs_rbtree_comparison_callback(tree) \ ((njs_rbtree_compare_t) (tree)->sentinel.right) void njs_rbtree_init(njs_rbtree_t *tree, njs_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 njs_rbtree_node_successor() and eliminates explicit * root node test before or inside njs_rbtree_min(). */ /* The root is empty. */ tree->sentinel.left = &tree->sentinel; /* * The sentinel's right child is never used so * comparison callback can be safely stored here. */ tree->sentinel.right = (void *) compare; /* The root and leaf sentinel must be black. */ tree->sentinel.color = NJS_RBTREE_BLACK; } void njs_rbtree_insert(njs_rbtree_t *tree, njs_rbtree_part_t *part) { njs_rbtree_node_t *node, *new_node, *sentinel, **child; njs_rbtree_compare_t compare; new_node = (njs_rbtree_node_t *) part; node = njs_rbtree_root(tree); sentinel = njs_rbtree_sentinel(tree); new_node->left = sentinel; new_node->right = sentinel; new_node->color = NJS_RBTREE_RED; compare = (njs_rbtree_compare_t) tree->sentinel.right; child = &njs_rbtree_root(tree); while (*child != sentinel) { node = *child; njs_prefetch(node->left); njs_prefetch(node->right); child = (compare(new_node, node) < 0) ? &node->left : &node->right; } *child = new_node; new_node->parent = node; njs_rbtree_insert_fixup(new_node); node = njs_rbtree_root(tree); node->color = NJS_RBTREE_BLACK; } static void njs_rbtree_insert_fixup(njs_rbtree_node_t *node) { njs_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 == NJS_RBTREE_BLACK) { return; } grandparent = parent->parent; if (parent == grandparent->left) { uncle = grandparent->right; if (uncle->color == NJS_RBTREE_BLACK) { if (node == parent->right) { node = parent; njs_rbtree_left_rotate(node); } /* * njs_rbtree_left_rotate() swaps parent and * child whilst keeps grandparent the same. */ parent = node->parent; parent->color = NJS_RBTREE_BLACK; grandparent->color = NJS_RBTREE_RED; njs_rbtree_right_rotate(grandparent); /* * njs_rbtree_right_rotate() does not change node->parent * color which is now black, so testing color is not required * to return from function. */ return; } } else { uncle = grandparent->left; if (uncle->color == NJS_RBTREE_BLACK) { if (node == parent->left) { node = parent; njs_rbtree_right_rotate(node); } /* See the comment in the symmetric branch above. */ parent = node->parent; parent->color = NJS_RBTREE_BLACK; grandparent->color = NJS_RBTREE_RED; njs_rbtree_left_rotate(grandparent); /* See the comment in the symmetric branch above. */ return; } } uncle->color = NJS_RBTREE_BLACK; parent->color = NJS_RBTREE_BLACK; grandparent->color = NJS_RBTREE_RED; node = grandparent; } } njs_rbtree_node_t * njs_rbtree_find(njs_rbtree_t *tree, njs_rbtree_part_t *part) { intptr_t n; njs_rbtree_node_t *node, *next, *sentinel; njs_rbtree_compare_t compare; node = (njs_rbtree_node_t *) part; next = njs_rbtree_root(tree); sentinel = njs_rbtree_sentinel(tree); compare = njs_rbtree_comparison_callback(tree); while (next != sentinel) { njs_prefetch(next->left); njs_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; } njs_rbtree_node_t * njs_rbtree_find_less_or_equal(njs_rbtree_t *tree, njs_rbtree_part_t *part) { intptr_t n; njs_rbtree_node_t *node, *retval, *next, *sentinel; njs_rbtree_compare_t compare; node = (njs_rbtree_node_t *) part; retval = NULL; next = njs_rbtree_root(tree); sentinel = njs_rbtree_sentinel(tree); compare = njs_rbtree_comparison_callback(tree); while (next != sentinel) { njs_prefetch(next->left); njs_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; } njs_rbtree_node_t * njs_rbtree_find_greater_or_equal(njs_rbtree_t *tree, njs_rbtree_part_t *part) { intptr_t n; njs_rbtree_node_t *node, *retval, *next, *sentinel; njs_rbtree_compare_t compare; node = (njs_rbtree_node_t *) part; retval = NULL; next = njs_rbtree_root(tree); sentinel = njs_rbtree_sentinel(tree); compare = njs_rbtree_comparison_callback(tree); while (next != sentinel) { njs_prefetch(next->left); njs_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 njs_rbtree_delete(njs_rbtree_t *tree, njs_rbtree_part_t *part) { uint8_t color; njs_rbtree_node_t *node, *sentinel, *subst, *child; node = (njs_rbtree_node_t *) part; subst = node; sentinel = njs_rbtree_sentinel(tree); if (node->left == sentinel) { child = node->right; } else if (node->right == sentinel) { child = node->left; } else { subst = njs_rbtree_branch_min(tree, node->right); child = subst->right; } njs_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; njs_rbtree_parent_relink(subst, node); } #if (NJS_DEBUG) node->left = NULL; node->right = NULL; node->parent = NULL; #endif if (color == NJS_RBTREE_BLACK) { njs_rbtree_delete_fixup(tree, child); } } static void njs_rbtree_delete_fixup(njs_rbtree_t *tree, njs_rbtree_node_t *node) { njs_rbtree_node_t *parent, *sibling; while (node != njs_rbtree_root(tree) && node->color == NJS_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 != NJS_RBTREE_BLACK) { sibling->color = NJS_RBTREE_BLACK; parent->color = NJS_RBTREE_RED; njs_rbtree_left_rotate(parent); sibling = parent->right; } if (sibling->right->color == NJS_RBTREE_BLACK) { sibling->color = NJS_RBTREE_RED; if (sibling->left->color == NJS_RBTREE_BLACK) { node = parent; continue; } sibling->left->color = NJS_RBTREE_BLACK; njs_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 njs_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 = NJS_RBTREE_BLACK; sibling->right->color = NJS_RBTREE_BLACK; njs_rbtree_left_rotate(parent); return; } else { sibling = parent->left; if (sibling->color != NJS_RBTREE_BLACK) { sibling->color = NJS_RBTREE_BLACK; parent->color = NJS_RBTREE_RED; njs_rbtree_right_rotate(parent); sibling = parent->left; } if (sibling->left->color == NJS_RBTREE_BLACK) { sibling->color = NJS_RBTREE_RED; if (sibling->right->color == NJS_RBTREE_BLACK) { node = parent; continue; } sibling->right->color = NJS_RBTREE_BLACK; njs_rbtree_left_rotate(sibling); /* See the comment in the symmetric branch above. */ sibling = parent->left; } sibling->color = parent->color; parent->color = NJS_RBTREE_BLACK; sibling->left->color = NJS_RBTREE_BLACK; njs_rbtree_right_rotate(parent); return; } } node->color = NJS_RBTREE_BLACK; } njs_inline void njs_rbtree_left_rotate(njs_rbtree_node_t *node) { njs_rbtree_node_t *child; child = node->right; node->right = child->left; child->left->parent = node; child->left = node; njs_rbtree_parent_relink(child, node); node->parent = child; } njs_inline void njs_rbtree_right_rotate(njs_rbtree_node_t *node) { njs_rbtree_node_t *child; child = node->left; node->left = child->right; child->right->parent = node; child->right = node; njs_rbtree_parent_relink(child, node); node->parent = child; } /* Relink a parent from the node to the subst node. */ njs_inline void njs_rbtree_parent_relink(njs_rbtree_node_t *subst, njs_rbtree_node_t *node) { njs_rbtree_node_t *parent, **link; parent = node->parent; /* * The leaf sentinel's parent can be safely changed here. * See the comment in njs_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; } njs_rbtree_node_t * njs_rbtree_destroy_next(njs_rbtree_t *tree, njs_rbtree_node_t **next) { njs_rbtree_node_t *node, *subst, *parent, *sentinel; sentinel = njs_rbtree_sentinel(tree); /* Find the leftmost node. */ for (node = *next; node->left != sentinel; node = node->left); /* Replace the leftmost node with its right child. */ subst = node->right; parent = node->parent; parent->left = subst; subst->parent = parent; /* * The right child is used as the next start node. If the right child * is the sentinel then parent of the leftmost node is used as the next * start node. The parent of the root node is the sentinel so after * the single root node will be replaced with the sentinel, the next * start node will be equal to the sentinel and iteration will stop. */ if (subst == sentinel) { subst = parent; } *next = subst; return node; } njs-0.8.9/src/njs_rbtree.h000066400000000000000000000066711474132077100154370ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_RBTREE_H_INCLUDED_ #define _NJS_RBTREE_H_INCLUDED_ typedef struct njs_rbtree_node_s njs_rbtree_node_t; struct njs_rbtree_node_s { njs_rbtree_node_t *left; njs_rbtree_node_t *right; njs_rbtree_node_t *parent; uint8_t color; }; typedef struct { njs_rbtree_node_t *left; njs_rbtree_node_t *right; njs_rbtree_node_t *parent; } njs_rbtree_part_t; #define NJS_RBTREE_NODE(node) \ njs_rbtree_part_t node; \ uint8_t node##_color #define NJS_RBTREE_NODE_INIT { NULL, NULL, NULL }, 0 typedef struct { njs_rbtree_node_t sentinel; } njs_rbtree_t; typedef intptr_t (*njs_rbtree_compare_t)(njs_rbtree_node_t *node1, njs_rbtree_node_t *node2); #define njs_rbtree_root(tree) \ ((tree)->sentinel.left) #define njs_rbtree_sentinel(tree) \ (&(tree)->sentinel) #define njs_rbtree_is_empty(tree) \ (njs_rbtree_root(tree) == njs_rbtree_sentinel(tree)) #define njs_rbtree_min(tree) \ njs_rbtree_branch_min(tree, &(tree)->sentinel) njs_inline njs_rbtree_node_t * njs_rbtree_branch_min(njs_rbtree_t *tree, njs_rbtree_node_t *node) { while (node->left != njs_rbtree_sentinel(tree)) { node = node->left; } return node; } #define njs_rbtree_is_there_successor(tree, node) \ ((node) != njs_rbtree_sentinel(tree)) njs_inline njs_rbtree_node_t * njs_rbtree_node_successor(njs_rbtree_t *tree, njs_rbtree_node_t *node) { njs_rbtree_node_t *parent; if (node->right != njs_rbtree_sentinel(tree)) { return njs_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; } } NJS_EXPORT void njs_rbtree_init(njs_rbtree_t *tree, njs_rbtree_compare_t compare); NJS_EXPORT void njs_rbtree_insert(njs_rbtree_t *tree, njs_rbtree_part_t *node); NJS_EXPORT njs_rbtree_node_t *njs_rbtree_find(njs_rbtree_t *tree, njs_rbtree_part_t *node); NJS_EXPORT njs_rbtree_node_t *njs_rbtree_find_less_or_equal(njs_rbtree_t *tree, njs_rbtree_part_t *node); NJS_EXPORT njs_rbtree_node_t *njs_rbtree_find_greater_or_equal(njs_rbtree_t *tree, njs_rbtree_part_t *node); NJS_EXPORT void njs_rbtree_delete(njs_rbtree_t *tree, njs_rbtree_part_t *node); /* * njs_rbtree_destroy_next() is iterator to use only while rbtree destruction. * It deletes a node from rbtree and returns the node. The rbtree is not * rebalanced after deletion. At the beginning the "next" parameter should * be equal to rbtree root. The iterator should be called in loop until * the "next" parameter will be equal to the rbtree sentinel. No other * operations must be performed on the rbtree while destruction. */ NJS_EXPORT njs_rbtree_node_t *njs_rbtree_destroy_next(njs_rbtree_t *tree, njs_rbtree_node_t **next); #endif /* _NJS_RBTREE_H_INCLUDED_ */ njs-0.8.9/src/njs_regex.h000066400000000000000000000052331474132077100152570ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_REGEX_H_INCLUDED_ #define _NJS_REGEX_H_INCLUDED_ #define NJS_REGEX_UNSET (size_t) (-1) typedef enum { NJS_REGEX_INVALID_FLAG = -1, NJS_REGEX_NO_FLAGS = 0, NJS_REGEX_GLOBAL = 1, NJS_REGEX_IGNORE_CASE = 2, NJS_REGEX_MULTILINE = 4, NJS_REGEX_STICKY = 8, NJS_REGEX_UTF8 = 16, } njs_regex_flags_t; typedef void *(*njs_pcre_malloc_t)(size_t size, void *memory_data); typedef void (*njs_pcre_free_t)(void *p, void *memory_data); typedef struct { void *code; void *extra; int ncaptures; int backrefmax; int nentries; int entry_size; char *entries; } njs_regex_t; #ifdef NJS_HAVE_PCRE2 #define njs_regex_generic_ctx_t void #define njs_regex_compile_ctx_t void #define njs_regex_match_data_t void #else typedef struct { njs_pcre_malloc_t private_malloc; njs_pcre_free_t private_free; void *memory_data; } njs_regex_generic_ctx_t; #define njs_regex_compile_ctx_t void typedef struct { int ncaptures; /* * Each capture is stored in 3 "int" vector elements. * The N capture positions are stored in [n * 2] and [n * 2 + 1] elements. * The 3rd bookkeeping elements are at the end of the vector. * The first vector is for the "$0" capture and it is always allocated. */ int captures[3]; } njs_regex_match_data_t; #endif NJS_EXPORT njs_regex_generic_ctx_t * njs_regex_generic_ctx_create(njs_pcre_malloc_t private_malloc, njs_pcre_free_t private_free, void *memory_data); NJS_EXPORT njs_regex_compile_ctx_t *njs_regex_compile_ctx_create( njs_regex_generic_ctx_t *ctx); NJS_EXPORT njs_int_t njs_regex_escape(njs_mp_t *mp, njs_str_t *text); NJS_EXPORT njs_int_t njs_regex_compile(njs_regex_t *regex, u_char *source, size_t len, njs_regex_flags_t flags, njs_regex_compile_ctx_t *ctx, njs_trace_t *trace); NJS_EXPORT njs_bool_t njs_regex_is_valid(njs_regex_t *regex); NJS_EXPORT njs_int_t njs_regex_named_captures(njs_regex_t *regex, njs_str_t *name, int n); NJS_EXPORT njs_regex_match_data_t *njs_regex_match_data(njs_regex_t *regex, njs_regex_generic_ctx_t *ctx); NJS_EXPORT void njs_regex_match_data_free(njs_regex_match_data_t *match_data, njs_regex_generic_ctx_t *ctx); NJS_EXPORT njs_int_t njs_regex_match(njs_regex_t *regex, const u_char *subject, size_t off, size_t len, njs_regex_match_data_t *match_data, njs_trace_t *trace); NJS_EXPORT size_t njs_regex_capture(njs_regex_match_data_t *match_data, njs_uint_t n); #endif /* _NJS_REGEX_H_INCLUDED_ */ njs-0.8.9/src/njs_regexp.c000066400000000000000000001423561474132077100154420ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include struct njs_regexp_group_s { njs_str_t name; uint32_t hash; uint32_t capture; }; static void *njs_regexp_malloc(size_t size, void *memory_data); static void njs_regexp_free(void *p, void *memory_data); static njs_int_t njs_regexp_prototype_source(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static int njs_regexp_pattern_compile(njs_vm_t *vm, njs_regex_t *regex, u_char *source, size_t len, njs_regex_flags_t flags); static u_char *njs_regexp_compile_trace_handler(njs_trace_t *trace, njs_trace_data_t *td, u_char *start); static u_char *njs_regexp_match_trace_handler(njs_trace_t *trace, njs_trace_data_t *td, u_char *start); #define NJS_REGEXP_FLAG_TEST 1 static njs_int_t njs_regexp_exec(njs_vm_t *vm, njs_value_t *r, njs_value_t *s, unsigned flags, njs_value_t *retval); static njs_array_t *njs_regexp_exec_result(njs_vm_t *vm, njs_value_t *r, njs_utf8_t utf8, njs_string_prop_t *string, njs_regex_match_data_t *data); const njs_value_t njs_string_lindex = njs_string("lastIndex"); njs_int_t njs_regexp_init(njs_vm_t *vm) { vm->regex_generic_ctx = njs_regex_generic_ctx_create(njs_regexp_malloc, njs_regexp_free, vm->mem_pool); if (njs_slow_path(vm->regex_generic_ctx == NULL)) { njs_memory_error(vm); return NJS_ERROR; } vm->regex_compile_ctx = njs_regex_compile_ctx_create(vm->regex_generic_ctx); if (njs_slow_path(vm->regex_compile_ctx == NULL)) { njs_memory_error(vm); return NJS_ERROR; } vm->single_match_data = njs_regex_match_data(NULL, vm->regex_generic_ctx); if (njs_slow_path(vm->single_match_data == NULL)) { njs_memory_error(vm); return NJS_ERROR; } return NJS_OK; } static void * njs_regexp_malloc(size_t size, void *memory_data) { return njs_mp_alloc(memory_data, size); } static void njs_regexp_free(void *p, void *memory_data) { njs_mp_free(memory_data, p); } static njs_regex_flags_t njs_regexp_value_flags(njs_vm_t *vm, const njs_value_t *regexp) { njs_regex_flags_t flags; njs_regexp_pattern_t *pattern; flags = 0; pattern = njs_regexp_pattern(regexp); if (pattern->global) { flags |= NJS_REGEX_GLOBAL; } if (pattern->ignore_case) { flags |= NJS_REGEX_IGNORE_CASE; } if (pattern->multiline) { flags |= NJS_REGEX_MULTILINE; } if (pattern->sticky) { flags |= NJS_REGEX_STICKY; } return flags; } static njs_int_t njs_regexp_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { u_char *start; njs_int_t ret; njs_str_t string; njs_value_t source, *pattern, *flags; njs_regex_flags_t re_flags; pattern = njs_arg(args, nargs, 1); if (njs_is_regexp(pattern)) { ret = njs_regexp_prototype_source(vm, pattern, 1, 0, &source); if (njs_slow_path(ret != NJS_OK)) { return ret; } re_flags = njs_regexp_value_flags(vm, pattern); pattern = &source; } else { if (njs_is_defined(pattern)) { ret = njs_value_to_string(vm, pattern, pattern); if (njs_slow_path(ret != NJS_OK)) { return ret; } } else { pattern = njs_value_arg(&njs_string_empty); } re_flags = 0; } flags = njs_arg(args, nargs, 2); if (njs_is_defined(flags)) { ret = njs_value_to_string(vm, flags, flags); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_string_get(flags, &string); start = string.start; re_flags = njs_regexp_flags(&start, start + string.length); if (njs_slow_path(re_flags < 0 || (size_t) (start - string.start) != string.length)) { njs_syntax_error(vm, "Invalid RegExp flags \"%V\"", &string); return NJS_ERROR; } } njs_string_get(pattern, &string); return njs_regexp_create(vm, retval, string.start, string.length, re_flags); } njs_int_t njs_regexp_create(njs_vm_t *vm, njs_value_t *value, u_char *start, size_t length, njs_regex_flags_t flags) { njs_regexp_t *regexp; njs_regexp_pattern_t *pattern; if (length != 0 || flags != 0) { if (length == 0) { start = (u_char *) "(?:)"; length = njs_length("(?:)"); } pattern = njs_regexp_pattern_create(vm, start, length, flags); if (njs_slow_path(pattern == NULL)) { return NJS_ERROR; } } else { pattern = vm->shared->empty_regexp_pattern; } regexp = njs_regexp_alloc(vm, pattern); if (njs_fast_path(regexp != NULL)) { njs_set_regexp(value, regexp); return NJS_OK; } return NJS_ERROR; } njs_regex_flags_t njs_regexp_flags(u_char **start, u_char *end) { u_char *p; njs_regex_flags_t flags, flag; flags = NJS_REGEX_NO_FLAGS; for (p = *start; p < end; p++) { switch (*p) { case 'g': flag = NJS_REGEX_GLOBAL; break; case 'i': flag = NJS_REGEX_IGNORE_CASE; break; case 'm': flag = NJS_REGEX_MULTILINE; break; case 'y': flag = NJS_REGEX_STICKY; break; default: if (*p >= 'a' && *p <= 'z') { goto invalid; } goto done; } if (njs_slow_path((flags & flag) != 0)) { goto invalid; } flags |= flag; } done: *start = p; return flags; invalid: *start = p + 1; return NJS_REGEX_INVALID_FLAG; } njs_regexp_pattern_t * njs_regexp_pattern_create(njs_vm_t *vm, u_char *start, size_t length, njs_regex_flags_t flags) { int ret; u_char *p, *end; size_t size; njs_str_t text; njs_bool_t in; njs_uint_t n; njs_regex_t *regex; njs_regexp_group_t *group; njs_regexp_pattern_t *pattern; text.start = start; text.length = length; in = 0; end = start + length; for (p = start; p < end; p++) { switch (*p) { case '[': in = 1; break; case ']': in = 0; break; case '\\': p++; break; case '+': if (njs_slow_path(!in && (p - 1 > start) && (p[-1] == '+'|| p[-1] == '*' || p[-1] == '?')) && (p - 2 >= start && p[-2] != '\\')) { /** * PCRE possessive quantifiers `++`, `*+`, `?+` * are not allowed in JavaScript. Whereas `[++]` or `\?+` are * allowed. */ goto nothing_to_repeat; } break; } } ret = njs_regex_escape(vm->mem_pool, &text); if (njs_slow_path(ret != NJS_OK)) { njs_memory_error(vm); return NULL; } pattern = njs_mp_alloc(vm->mem_pool, sizeof(njs_regexp_pattern_t) + text.length + 1); if (njs_slow_path(pattern == NULL)) { njs_memory_error(vm); return NULL; } njs_memzero(pattern, sizeof(njs_regexp_pattern_t)); p = (u_char *) pattern + sizeof(njs_regexp_pattern_t); pattern->source = p; p = njs_cpymem(p, text.start, text.length); *p++ = '\0'; pattern->global = ((flags & NJS_REGEX_GLOBAL) != 0); pattern->ignore_case = ((flags & NJS_REGEX_IGNORE_CASE) != 0); pattern->multiline = ((flags & NJS_REGEX_MULTILINE) != 0); pattern->sticky = ((flags & NJS_REGEX_STICKY) != 0); ret = njs_regexp_pattern_compile(vm, &pattern->regex[0], &pattern->source[0], text.length, flags); if (njs_fast_path(ret >= 0)) { pattern->ncaptures = ret; } else if (ret < 0 && ret != NJS_DECLINED) { goto fail; } njs_set_invalid(&vm->exception); ret = njs_regexp_pattern_compile(vm, &pattern->regex[1], &pattern->source[0], text.length, flags | NJS_REGEX_UTF8); if (njs_fast_path(ret >= 0)) { if (njs_slow_path(njs_regex_is_valid(&pattern->regex[0]) && (u_int) ret != pattern->ncaptures)) { njs_internal_error(vm, "regexp pattern compile failed"); goto fail; } pattern->ncaptures = ret; } else if (ret != NJS_DECLINED) { goto fail; } if (njs_regex_is_valid(&pattern->regex[0])) { regex = &pattern->regex[0]; } else if (njs_regex_is_valid(&pattern->regex[1])) { regex = &pattern->regex[1]; } else { goto fail; } pattern->ngroups = njs_regex_named_captures(regex, NULL, 0); if (pattern->ngroups != 0) { size = sizeof(njs_regexp_group_t) * pattern->ngroups; pattern->groups = njs_mp_alloc(vm->mem_pool, size); if (njs_slow_path(pattern->groups == NULL)) { njs_memory_error(vm); return NULL; } n = 0; do { group = &pattern->groups[n]; group->capture = njs_regex_named_captures(regex, &group->name, n); group->hash = njs_djb_hash(group->name.start, group->name.length); n++; } while (n != pattern->ngroups); } return pattern; fail: njs_mp_free(vm->mem_pool, pattern); return NULL; nothing_to_repeat: njs_syntax_error(vm, "Invalid regular expression \"%V\" nothing to repeat", &text); return NULL; } static int njs_regexp_pattern_compile(njs_vm_t *vm, njs_regex_t *regex, u_char *source, size_t len, njs_regex_flags_t flags) { njs_int_t ret; njs_trace_handler_t handler; handler = vm->trace.handler; vm->trace.handler = njs_regexp_compile_trace_handler; ret = njs_regex_compile(regex, source, len, flags, vm->regex_compile_ctx, &vm->trace); vm->trace.handler = handler; if (njs_fast_path(ret == NJS_OK)) { return regex->ncaptures; } return ret; } static u_char * njs_regexp_compile_trace_handler(njs_trace_t *trace, njs_trace_data_t *td, u_char *start) { u_char *p; njs_vm_t *vm; vm = trace->data; trace = trace->next; p = trace->handler(trace, td, start); njs_syntax_error(vm, "%*s", p - start, start); return p; } njs_int_t njs_regexp_match(njs_vm_t *vm, njs_regex_t *regex, const u_char *subject, size_t off, size_t len, njs_regex_match_data_t *match_data) { njs_int_t ret; njs_trace_handler_t handler; handler = vm->trace.handler; vm->trace.handler = njs_regexp_match_trace_handler; ret = njs_regex_match(regex, subject, off, len, match_data, &vm->trace); vm->trace.handler = handler; return ret; } static u_char * njs_regexp_match_trace_handler(njs_trace_t *trace, njs_trace_data_t *td, u_char *start) { u_char *p; njs_vm_t *vm; vm = trace->data; trace = trace->next; p = trace->handler(trace, td, start); njs_internal_error(vm, "%*s", p - start, start); return p; } njs_regexp_t * njs_regexp_alloc(njs_vm_t *vm, njs_regexp_pattern_t *pattern) { njs_regexp_t *regexp; regexp = njs_mp_alloc(vm->mem_pool, sizeof(njs_regexp_t)); if (njs_fast_path(regexp != NULL)) { njs_lvlhsh_init(®exp->object.hash); regexp->object.shared_hash = vm->shared->regexp_instance_hash; regexp->object.__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_REGEXP); regexp->object.slots = NULL; regexp->object.type = NJS_REGEXP; regexp->object.shared = 0; regexp->object.extensible = 1; regexp->object.fast_array = 0; regexp->object.error_data = 0; njs_set_number(®exp->last_index, 0); regexp->pattern = pattern; njs_string_short_set(®exp->string, 0, 0); return regexp; } njs_memory_error(vm); return NULL; } static njs_int_t njs_regexp_prototype_last_index(njs_vm_t *vm, njs_object_prop_t *unused, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { njs_regexp_t *regexp; regexp = njs_object_proto_lookup(njs_object(value), NJS_REGEXP, njs_regexp_t); if (njs_slow_path(regexp == NULL)) { njs_set_undefined(retval); return NJS_DECLINED; } if (setval != NULL) { regexp->last_index = *setval; njs_value_assign(retval, setval); return NJS_OK; } njs_value_assign(retval, ®exp->last_index); return NJS_OK; } static njs_int_t njs_regexp_prototype_flags(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { u_char *p; njs_int_t ret; njs_value_t *this, value; u_char dst[4]; static const njs_value_t string_global = njs_string("global"); static const njs_value_t string_ignore_case = njs_string("ignoreCase"); static const njs_value_t string_multiline = njs_string("multiline"); static const njs_value_t string_sticky = njs_string("sticky"); this = njs_argument(args, 0); if (njs_slow_path(!njs_is_object(this))) { njs_type_error(vm, "\"this\" argument is not an object"); return NJS_ERROR; } p = &dst[0]; ret = njs_value_property(vm, this, njs_value_arg(&string_global), &value); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } if (njs_bool(&value)) { *p++ = 'g'; } ret = njs_value_property(vm, this, njs_value_arg(&string_ignore_case), &value); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } if (njs_bool(&value)) { *p++ = 'i'; } ret = njs_value_property(vm, this, njs_value_arg(&string_multiline), &value); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } if (njs_bool(&value)) { *p++ = 'm'; } ret = njs_value_property(vm, this, njs_value_arg(&string_sticky), &value); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } if (njs_bool(&value)) { *p++ = 'y'; } return njs_string_new(vm, retval, dst, p - dst, p - dst); } static njs_int_t njs_regexp_prototype_flag(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t flag, njs_value_t *retval) { unsigned yn; njs_value_t *this; njs_regexp_pattern_t *pattern; this = njs_argument(args, 0); if (njs_slow_path(!njs_is_object(this))) { njs_type_error(vm, "\"this\" argument is not an object"); return NJS_ERROR; } if (njs_slow_path(!njs_is_regexp(this))) { if (njs_object(this) == njs_vm_proto(vm, NJS_OBJ_TYPE_REGEXP)) { njs_set_undefined(retval); return NJS_OK; } njs_type_error(vm, "\"this\" argument is not a regexp"); return NJS_ERROR; } pattern = njs_regexp_pattern(this); switch (flag) { case NJS_REGEX_GLOBAL: yn = pattern->global; break; case NJS_REGEX_IGNORE_CASE: yn = pattern->ignore_case; break; case NJS_REGEX_MULTILINE: yn = pattern->multiline; break; case NJS_REGEX_STICKY: default: yn = pattern->sticky; break; } njs_set_boolean(retval, yn); return NJS_OK; } static njs_int_t njs_regexp_prototype_source(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_str_t src; njs_value_t *this; njs_regexp_pattern_t *pattern; this = njs_argument(args, 0); if (njs_slow_path(!njs_is_object(this))) { njs_type_error(vm, "\"this\" argument is not an object"); return NJS_ERROR; } if (njs_slow_path(!njs_is_regexp(this))) { if (njs_object(this) == njs_vm_proto(vm, NJS_OBJ_TYPE_REGEXP)) { njs_value_assign(retval, &njs_string_empty_regexp); return NJS_OK; } njs_type_error(vm, "\"this\" argument is not a regexp"); return NJS_ERROR; } pattern = njs_regexp_pattern(this); src.start = pattern->source; src.length = njs_strlen(pattern->source); return njs_string_decode_utf8(vm, retval, &src); } static njs_int_t njs_regexp_prototype_to_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { u_char *p; size_t size, length; njs_int_t ret; njs_value_t *r, source, flags; njs_string_prop_t source_string, flags_string; static const njs_value_t string_source = njs_string("source"); static const njs_value_t string_flags = njs_string("flags"); r = njs_argument(args, 0); if (njs_slow_path(!njs_is_object(r))) { njs_type_error(vm, "\"this\" argument is not an object"); return NJS_ERROR; } ret = njs_value_property(vm, r, njs_value_arg(&string_source), &source); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } ret = njs_value_to_string(vm, &source, &source); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_value_property(vm, r, njs_value_arg(&string_flags), &flags); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } ret = njs_value_to_string(vm, &flags, &flags); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } (void) njs_string_prop(&source_string, &source); (void) njs_string_prop(&flags_string, &flags); size = source_string.size + flags_string.size + njs_length("//"); length = source_string.length + flags_string.length + njs_length("//"); p = njs_string_alloc(vm, retval, size, length); if (njs_slow_path(p == NULL)) { return NJS_ERROR; } *p++ = '/'; p = njs_cpymem(p, source_string.start, source_string.size); *p++ = '/'; memcpy(p, flags_string.start, flags_string.size); return NJS_OK; } njs_int_t njs_regexp_to_string(njs_vm_t *vm, njs_value_t *retval, const njs_value_t *value) { u_char *p, *start; size_t size, extra; int32_t length; njs_str_t s; njs_regexp_pattern_t *pattern; njs_unicode_decode_t ctx; pattern = njs_regexp_pattern(value); s.start = pattern->source; s.length = njs_strlen(pattern->source); length = njs_decode_utf8_length(&s, &size); extra = njs_length("//"); extra += (pattern->global != 0); extra += (pattern->ignore_case != 0); extra += (pattern->multiline != 0); extra += (pattern->sticky != 0); size += extra; length = (length >= 0) ? (length + extra) : 0; start = njs_string_alloc(vm, retval, size, length); if (njs_slow_path(start == NULL)) { return NJS_ERROR; } njs_utf8_decode_init(&ctx); p = start; *p++ = '/'; p = njs_utf8_stream_encode(&ctx, s.start, &s.start[s.length], p, 1, 0); *p++ = '/'; if (pattern->global) { *p++ = 'g'; } if (pattern->ignore_case) { *p++ = 'i'; } if (pattern->multiline) { *p++ = 'm'; } if (pattern->sticky) { *p++ = 'y'; } return NJS_OK; } static njs_int_t njs_regexp_prototype_test(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_value_t *r, *string, lvalue, value; r = njs_argument(args, 0); if (njs_slow_path(!njs_is_object(r))) { njs_type_error(vm, "\"this\" argument is not an object"); return NJS_ERROR; } string = njs_lvalue_arg(&lvalue, args, nargs, 1); ret = njs_value_to_string(vm, string, string); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_regexp_exec(vm, r, string, NJS_REGEXP_FLAG_TEST, &value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_set_boolean(retval, !njs_is_null(&value)); return NJS_OK; } /** * TODO: unicode flags. */ static njs_int_t njs_regexp_builtin_exec(njs_vm_t *vm, njs_value_t *r, njs_value_t *s, unsigned flags, njs_value_t *retval) { size_t c, length, offset; int64_t last_index; uint32_t index; njs_int_t ret; njs_utf8_t utf8; njs_value_t value; njs_array_t *result; njs_regexp_t *regexp; njs_string_prop_t string; njs_regexp_utf8_t type; njs_regexp_pattern_t *pattern; njs_regex_match_data_t *match_data; regexp = njs_regexp(r); regexp->string = *s; pattern = regexp->pattern; ret = njs_value_property(vm, r, njs_value_arg(&njs_string_lindex), &value); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } ret = njs_value_to_length(vm, &value, &last_index); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } if (!pattern->global && !pattern->sticky) { last_index = 0; } length = njs_string_prop(&string, s); if (njs_slow_path((size_t) last_index > length)) { goto not_found; } utf8 = NJS_STRING_ASCII; type = NJS_REGEXP_BYTE; if (string.length != 0) { type = NJS_REGEXP_UTF8; if (!njs_is_ascii_string(&string)) { utf8 = NJS_STRING_UTF8; } } pattern = regexp->pattern; if (njs_slow_path(!njs_regex_is_valid(&pattern->regex[type]))) { goto not_found; } match_data = njs_regex_match_data(&pattern->regex[type], vm->regex_generic_ctx); if (njs_slow_path(match_data == NULL)) { njs_memory_error(vm); return NJS_ERROR; } if (utf8 != NJS_STRING_UTF8) { offset = last_index; } else { if ((size_t) last_index < string.length) { offset = njs_string_utf8_offset(string.start, string.start + string.size, last_index) - string.start; } else { offset = string.size; } } ret = njs_regexp_match(vm, &pattern->regex[type], string.start, offset, string.size, match_data); if (ret >= 0) { if (pattern->global || pattern->sticky) { c = njs_regex_capture(match_data, 1); if (utf8 == NJS_STRING_UTF8) { index = njs_string_index(&string, c); } else { index = c; } njs_set_number(&value, index); ret = njs_value_property_set(vm, r, njs_value_arg(&njs_string_lindex), &value); if (njs_slow_path(ret != NJS_OK)) { njs_regex_match_data_free(match_data, vm->regex_generic_ctx); return NJS_ERROR; } } if (flags & NJS_REGEXP_FLAG_TEST) { njs_regex_match_data_free(match_data, vm->regex_generic_ctx); njs_set_boolean(retval, 1); return NJS_OK; } result = njs_regexp_exec_result(vm, r, utf8, &string, match_data); njs_regex_match_data_free(match_data, vm->regex_generic_ctx); if (njs_slow_path(result == NULL)) { return NJS_ERROR; } njs_set_array(retval, result); return NJS_OK; } njs_regex_match_data_free(match_data, vm->regex_generic_ctx); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } not_found: if (pattern->global || pattern->sticky) { njs_set_number(&value, 0); ret = njs_value_property_set(vm, r, njs_value_arg(&njs_string_lindex), &value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } njs_set_null(retval); return NJS_OK; } static njs_exotic_slots_t njs_regexp_prototype_exotic_slots = { .prop_handler = NULL, .keys = NULL, }; static njs_array_t * njs_regexp_exec_result(njs_vm_t *vm, njs_value_t *r, njs_utf8_t utf8, njs_string_prop_t *string, njs_regex_match_data_t *match_data) { u_char *start; size_t c; int32_t size; uint32_t index; njs_int_t ret; njs_uint_t i, n; njs_array_t *array; njs_value_t name; njs_object_t *groups; njs_regexp_t *regexp; njs_object_prop_t *prop; njs_regexp_group_t *group; njs_lvlhsh_query_t lhq; njs_regexp_pattern_t *pattern; static const njs_value_t string_index = njs_string("index"); static const njs_value_t string_input = njs_string("input"); static const njs_value_t string_groups = njs_string("groups"); regexp = njs_regexp(r); pattern = regexp->pattern; array = njs_array_alloc(vm, 0, pattern->ncaptures, 0); if (njs_slow_path(array == NULL)) { goto fail; } array->object.slots = &njs_regexp_prototype_exotic_slots; for (i = 0; i < pattern->ncaptures; i++) { n = 2 * i; c = njs_regex_capture(match_data, n); if (c != NJS_REGEX_UNSET) { start = &string->start[c]; size = njs_regex_capture(match_data, n + 1) - c; ret = njs_string_create(vm, &array->start[i], start, size); if (njs_slow_path(ret != NJS_OK)) { goto fail; } } else { njs_set_undefined(&array->start[i]); } } /* FIXME: implement fast CreateDataPropertyOrThrow(). */ prop = njs_object_prop_alloc(vm, &string_index, &njs_value_undefined, 1); if (njs_slow_path(prop == NULL)) { goto fail; } c = njs_regex_capture(match_data, 0); if (utf8 == NJS_STRING_UTF8) { index = njs_string_index(string, c); } else { index = c; } njs_set_number(&prop->u.value, index); lhq.key_hash = NJS_INDEX_HASH; lhq.key = njs_str_value("index"); lhq.replace = 0; lhq.value = prop; lhq.pool = vm->mem_pool; lhq.proto = &njs_object_hash_proto; ret = njs_lvlhsh_insert(&array->object.hash, &lhq); if (njs_slow_path(ret != NJS_OK)) { goto insert_fail; } prop = njs_object_prop_alloc(vm, &string_input, ®exp->string, 1); if (njs_slow_path(prop == NULL)) { goto fail; } lhq.key_hash = NJS_INPUT_HASH; lhq.key = njs_str_value("input"); lhq.value = prop; ret = njs_lvlhsh_insert(&array->object.hash, &lhq); if (njs_slow_path(ret != NJS_OK)) { goto insert_fail; } prop = njs_object_prop_alloc(vm, &string_groups, &njs_value_undefined, 1); if (njs_slow_path(prop == NULL)) { goto fail; } lhq.key_hash = NJS_GROUPS_HASH; lhq.key = njs_str_value("groups"); lhq.value = prop; ret = njs_lvlhsh_insert(&array->object.hash, &lhq); if (njs_slow_path(ret != NJS_OK)) { goto insert_fail; } if (pattern->ngroups != 0) { groups = njs_object_alloc(vm); if (njs_slow_path(groups == NULL)) { goto fail; } njs_set_object(&prop->u.value, groups); i = 0; do { group = &pattern->groups[i]; ret = njs_string_create(vm, &name, group->name.start, group->name.length); if (njs_slow_path(ret != NJS_OK)) { goto fail; } prop = njs_object_prop_alloc(vm, &name, &array->start[group->capture], 1); if (njs_slow_path(prop == NULL)) { goto fail; } lhq.key_hash = group->hash; lhq.key = group->name; lhq.value = prop; ret = njs_lvlhsh_insert(&groups->hash, &lhq); if (njs_slow_path(ret != NJS_OK)) { goto insert_fail; } i++; } while (i < pattern->ngroups); } ret = NJS_OK; goto done; insert_fail: njs_internal_error(vm, "lvlhsh insert failed"); fail: ret = NJS_ERROR; done: return (ret == NJS_OK) ? array : NULL; } static void njs_regexp_exec_result_free(njs_vm_t *vm, njs_array_t *result) { njs_uint_t n; njs_value_t *start; njs_flathsh_t *hash; njs_object_prop_t *prop; njs_lvlhsh_each_t lhe; njs_lvlhsh_query_t lhq; if (result->object.fast_array) { start = result->start; for (n = 0; n < result->length; n++) { if (start[n].short_string.size == NJS_STRING_LONG) { njs_mp_free(vm->mem_pool, start[n].long_string.data); } } } njs_lvlhsh_each_init(&lhe, &njs_object_hash_proto); hash = &result->object.hash; for ( ;; ) { prop = njs_flathsh_each(hash, &lhe); if (prop == NULL) { break; } njs_mp_free(vm->mem_pool, prop); } lhq.pool = vm->mem_pool; lhq.proto = &njs_object_hash_proto; njs_flathsh_destroy(hash, &lhq); njs_array_destroy(vm, result); } njs_int_t njs_regexp_prototype_exec(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { unsigned flags; njs_int_t ret; njs_value_t *r, *s; njs_value_t string_lvalue; r = njs_argument(args, 0); if (njs_slow_path(!njs_is_regexp(r))) { njs_type_error(vm, "\"this\" argument is not a regexp"); return NJS_ERROR; } s = njs_lvalue_arg(&string_lvalue, args, nargs, 1); ret = njs_value_to_string(vm, s, s); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (nargs > 2) { flags = njs_number(njs_arg(args, nargs, 2)); } else { flags = 0; } return njs_regexp_builtin_exec(vm, r, s, flags, retval); } static njs_int_t njs_regexp_exec(njs_vm_t *vm, njs_value_t *r, njs_value_t *s, unsigned flags, njs_value_t *retval) { njs_int_t ret; njs_value_t exec; njs_value_t arguments[2]; static const njs_value_t string_exec = njs_string("exec"); ret = njs_value_property(vm, r, njs_value_arg(&string_exec), &exec); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } if (njs_is_function(&exec)) { njs_value_assign(&arguments[0], s); if (flags) { njs_set_number(&arguments[1], flags); } ret = njs_function_call(vm, njs_function(&exec), r, arguments, flags ? 2 : 1, retval); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } if (njs_slow_path(!njs_is_null(retval) && (flags & NJS_REGEXP_FLAG_TEST && !njs_is_boolean(retval)))) { njs_type_error(vm, "unexpected \"%s\" retval in njs_regexp_exec()", njs_type_string(retval->type)); return NJS_ERROR; } if (njs_slow_path(!njs_is_null(retval) && (!flags && !njs_is_object(retval)))) { njs_type_error(vm, "unexpected \"%s\" retval in njs_regexp_exec()", njs_type_string(retval->type)); return NJS_ERROR; } return NJS_OK; } if (njs_slow_path(!njs_is_regexp(r))) { njs_type_error(vm, "receiver argument is not a regexp"); return NJS_ERROR; } return njs_regexp_builtin_exec(vm, r, s, flags, retval); } njs_int_t njs_regexp_prototype_symbol_replace(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t n, last_index, ncaptures, pos, length; const u_char *p, *next; njs_str_t rep, m; njs_int_t ret; njs_arr_t results; njs_chb_t chain; njs_uint_t i; njs_bool_t global; njs_array_t *array; njs_value_t *arguments, *r, *rx, *string, *replace; njs_value_t s_lvalue, r_lvalue, value, matched, groups; njs_function_t *func_replace; njs_string_prop_t s; static const njs_value_t string_global = njs_string("global"); static const njs_value_t string_groups = njs_string("groups"); static const njs_value_t string_index = njs_string("index"); rx = njs_argument(args, 0); if (njs_slow_path(!njs_is_object(rx))) { njs_type_error(vm, "\"this\" is not object"); return NJS_ERROR; } string = njs_lvalue_arg(&s_lvalue, args, nargs, 1); ret = njs_value_to_string(vm, string, string); if (njs_slow_path(ret != NJS_OK)) { return ret; } length = njs_string_prop(&s, string); rep.start = NULL; rep.length = 0; replace = njs_lvalue_arg(&r_lvalue, args, nargs, 2); func_replace = njs_is_function(replace) ? njs_function(replace) : NULL; if (!func_replace) { ret = njs_value_to_string(vm, replace, replace); if (njs_slow_path(ret != NJS_OK)) { return ret; } } ret = njs_value_property(vm, rx, njs_value_arg(&string_global), &value); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } global = njs_bool(&value); if (global) { njs_set_number(&value, 0); ret = njs_value_property_set(vm, rx, njs_value_arg(&njs_string_lindex), &value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } NJS_CHB_MP_INIT(&chain, vm); results.separate = 0; results.pointer = 0; r = njs_arr_init(vm->mem_pool, &results, NULL, 4, sizeof(njs_value_t)); if (njs_slow_path(r == NULL)) { return NJS_ERROR; } for ( ;; ) { r = njs_arr_add(&results); if (njs_slow_path(r == NULL)) { ret = NJS_ERROR; goto exception; } ret = njs_regexp_exec(vm, rx, string, 0, r); if (njs_slow_path(ret != NJS_OK)) { goto exception; } if (njs_is_null(r) || !global) { break; } if (njs_fast_path(njs_is_fast_array(r) && njs_array_len(r) != 0)) { value = njs_array_start(r)[0]; if (!njs_is_valid(&value)) { njs_set_undefined(&value); } } else { ret = njs_value_property_i64(vm, r, 0, &value); if (njs_slow_path(ret == NJS_ERROR)) { goto exception; } } ret = njs_value_to_string(vm, &value, &value); if (njs_slow_path(ret != NJS_OK)) { goto exception; } if (njs_string_length(&value) != 0) { continue; } ret = njs_value_property(vm, rx, njs_value_arg(&njs_string_lindex), &value); if (njs_slow_path(ret == NJS_ERROR)) { goto exception; } ret = njs_value_to_length(vm, &value, &last_index); if (njs_slow_path(ret != NJS_OK)) { goto exception; } njs_set_number(&value, last_index + 1); ret = njs_value_property_set(vm, rx, njs_value_arg(&njs_string_lindex), &value); if (njs_slow_path(ret != NJS_OK)) { goto exception; } } i = 0; p = s.start; next = p; while (i < results.items) { r = njs_arr_item(&results, i++); if (njs_slow_path(njs_is_null(r))) { break; } ret = njs_value_property_i64(vm, r, 0, &matched); if (njs_slow_path(ret == NJS_ERROR)) { goto exception; } ret = njs_value_to_string(vm, &matched, &matched); if (njs_slow_path(ret != NJS_OK)) { goto exception; } ret = njs_value_property(vm, r, njs_value_arg(&string_index), &value); if (njs_slow_path(ret == NJS_ERROR)) { goto exception; } ret = njs_value_to_integer(vm, &value, &pos); if (njs_slow_path(ret != NJS_OK)) { goto exception; } pos = njs_max(njs_min(pos, (int64_t) length), 0); ret = njs_object_length(vm, r, &ncaptures); if (njs_slow_path(ret != NJS_OK)) { goto exception; } ncaptures = njs_min(njs_max(ncaptures - 1, 0), 99); array = njs_array_alloc(vm, 1, ncaptures + 1, 0); if (njs_slow_path(array == NULL)) { goto exception; } arguments = array->start; arguments[0] = matched; for (n = 1; n <= ncaptures; n++) { ret = njs_value_property_i64(vm, r, n, &arguments[n]); if (njs_slow_path(ret == NJS_ERROR)) { goto exception; } if (njs_is_undefined(&arguments[n])) { continue; } ret = njs_value_to_string(vm, &arguments[n], &arguments[n]); if (njs_slow_path(ret == NJS_ERROR)) { goto exception; } } ret = njs_value_property(vm, r, njs_value_arg(&string_groups), &groups); if (njs_slow_path(ret == NJS_ERROR)) { goto exception; } if (!func_replace) { if (njs_is_defined(&groups)) { ret = njs_value_to_object(vm, &groups); if (njs_slow_path(ret != NJS_OK)) { return ret; } } ret = njs_string_get_substitution(vm, &matched, string, pos, arguments, ncaptures, &groups, replace, retval); } else { ret = njs_array_expand(vm, array, 0, njs_is_defined(&groups) ? 3 : 2); if (njs_slow_path(ret != NJS_OK)) { goto exception; } arguments = array->start; njs_set_number(&arguments[n++], pos); arguments[n++] = *string; if (njs_is_defined(&groups)) { arguments[n++] = groups; } ret = njs_function_call(vm, func_replace, njs_value_arg(&njs_value_undefined), arguments, n, retval); } njs_array_destroy(vm, array); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } ret = njs_value_to_string(vm, retval, retval); if (njs_slow_path(ret != NJS_OK)) { goto exception; } p = njs_string_offset(&s, pos); if (p >= next) { njs_chb_append(&chain, next, p - next); njs_string_get(retval, &rep); njs_chb_append_str(&chain, &rep); njs_string_get(&matched, &m); next = p + m.length; } if (!func_replace && njs_object_slots(r)) { /* * Doing free here ONLY for non-function replace, because * otherwise we cannot be certain the result of match * was not stored elsewhere. */ njs_regexp_exec_result_free(vm, njs_array(r)); } } if (next < s.start + s.size) { njs_chb_append(&chain, next, s.start + s.size - next); } ret = njs_string_create_chb(vm, retval, &chain); if (njs_slow_path(ret != NJS_OK)) { ret = NJS_ERROR; goto exception; } ret = NJS_OK; exception: njs_chb_destroy(&chain); njs_arr_destroy(&results); return ret; } static njs_int_t njs_regexp_prototype_symbol_split(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { u_char *dst; int64_t e, i, p, q, ncaptures, length; ssize_t len; uint32_t limit; njs_int_t ret; njs_bool_t sticky; njs_utf8_t utf8; njs_array_t *array; njs_value_t *rx, *string, *value; njs_value_t r, z, this, s_lvalue, setval, constructor; njs_object_t *object; const u_char *start, *end; njs_string_prop_t s, sv; njs_value_t arguments[2]; static const njs_value_t string_lindex = njs_string("lastIndex"); static const njs_value_t string_flags = njs_string("flags"); rx = njs_argument(args, 0); if (njs_slow_path(!njs_is_object(rx))) { njs_type_error(vm, "\"this\" is not object"); return NJS_ERROR; } string = njs_lvalue_arg(&s_lvalue, args, nargs, 1); ret = njs_value_to_string(vm, string, string); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_set_function(&constructor, &njs_vm_ctor(vm, NJS_OBJ_TYPE_REGEXP)); ret = njs_value_species_constructor(vm, rx, &constructor, &constructor); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_value_property(vm, rx, njs_value_arg(&string_flags), retval); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } ret = njs_value_to_string(vm, retval, retval); if (njs_slow_path(ret != NJS_OK)) { return ret; } (void) njs_string_prop(&s, retval); sticky = memchr(s.start, 'y', s.size) != NULL; object = njs_function_new_object(vm, &constructor); if (njs_slow_path(object == NULL)) { return NJS_ERROR; } njs_set_object(&this, object); arguments[0] = *rx; if (!sticky) { length = s.length + 1; dst = njs_string_alloc(vm, &arguments[1], s.size + 1, length); if (njs_slow_path(dst == NULL)) { return NJS_ERROR; } dst = njs_cpymem(dst, s.start, s.size); *dst++ = 'y'; } else { njs_value_assign(&arguments[1], retval); } ret = njs_function_call2(vm, njs_function(&constructor), &this, njs_value_arg(&arguments), 2, &r, 1); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } rx = &r; array = njs_array_alloc(vm, 0, 0, NJS_ARRAY_SPARE); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } value = njs_arg(args, nargs, 2); limit = UINT32_MAX; if (njs_is_defined(value)) { ret = njs_value_to_uint32(vm, value, &limit); if (njs_slow_path(ret != NJS_OK)) { return ret; } } if (njs_slow_path(limit == 0)) { goto done; } length = njs_string_prop(&s, string); if (njs_slow_path(s.size == 0)) { ret = njs_regexp_exec(vm, rx, string, NJS_REGEXP_FLAG_TEST, &z); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } if (!njs_is_null(&z)) { goto done; } goto single; } utf8 = NJS_STRING_ASCII; if (!njs_is_ascii_string(&s)) { utf8 = NJS_STRING_UTF8; } p = 0; q = 0; while (q < length) { njs_set_number(&setval, q); ret = njs_value_property_set(vm, rx, njs_value_arg(&string_lindex), &setval); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_regexp_exec(vm, rx, string, 0, &z); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } if (njs_is_null(&z)) { q = q + 1; continue; } ret = njs_value_property(vm, rx, njs_value_arg(&string_lindex), retval); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } ret = njs_value_to_length(vm, retval, &e); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } e = njs_min(e, length); if (e == p) { if (njs_object_slots(&z)) { njs_regexp_exec_result_free(vm, njs_array(&z)); } q = q + 1; continue; } if (utf8 == NJS_STRING_UTF8) { start = njs_string_utf8_offset(s.start, s.start + s.size, p); end = njs_string_utf8_offset(s.start, s.start + s.size, q); } else { start = &s.start[p]; end = &s.start[q]; } len = njs_string_calc_length(utf8, start, end - start); ret = njs_array_string_add(vm, array, start, end - start, len); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (array->length == limit) { if (njs_object_slots(&z)) { njs_regexp_exec_result_free(vm, njs_array(&z)); } goto done; } p = e; ret = njs_object_length(vm, &z, &ncaptures); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ncaptures = njs_max(ncaptures - 1, 0); for (i = 1; i <= ncaptures; i++) { ret = njs_value_property_i64(vm, &z, i, retval); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } (void) njs_string_prop(&sv, retval); ret = njs_array_string_add(vm, array, sv.start, sv.size, sv.length); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (array->length == limit) { if (njs_object_slots(&z)) { njs_regexp_exec_result_free(vm, njs_array(&z)); } goto done; } } if (njs_object_slots(&z)) { njs_regexp_exec_result_free(vm, njs_array(&z)); } q = p; } end = &s.start[s.size]; if (utf8 == NJS_STRING_UTF8) { start = (p < length) ? njs_string_utf8_offset(s.start, s.start + s.size, p) : end; } else { start = &s.start[p]; } len = njs_string_calc_length(utf8, start, end - start); ret = njs_array_string_add(vm, array, start, end - start, len); if (njs_slow_path(ret != NJS_OK)) { return ret; } goto done; single: value = njs_array_push(vm, array); if (njs_slow_path(value == NULL)) { return NJS_ERROR; } *value = *string; done: njs_set_array(retval, array); return NJS_OK; } static const njs_object_prop_t njs_regexp_constructor_properties[] = { NJS_DECLARE_PROP_LENGTH(2), NJS_DECLARE_PROP_NAME("RegExp"), NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), }; const njs_object_init_t njs_regexp_constructor_init = { njs_regexp_constructor_properties, njs_nitems(njs_regexp_constructor_properties), }; static const njs_object_prop_t njs_regexp_prototype_properties[] = { NJS_DECLARE_PROP_HANDLER("constructor", njs_object_prototype_create_constructor, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_GETTER("flags", njs_regexp_prototype_flags, 0), NJS_DECLARE_PROP_GETTER("global", njs_regexp_prototype_flag, NJS_REGEX_GLOBAL), NJS_DECLARE_PROP_GETTER("ignoreCase", njs_regexp_prototype_flag, NJS_REGEX_IGNORE_CASE), NJS_DECLARE_PROP_GETTER("multiline", njs_regexp_prototype_flag, NJS_REGEX_MULTILINE), NJS_DECLARE_PROP_GETTER("source", njs_regexp_prototype_source, 0), NJS_DECLARE_PROP_GETTER("sticky", njs_regexp_prototype_flag, NJS_REGEX_STICKY), NJS_DECLARE_PROP_NATIVE("toString", njs_regexp_prototype_to_string, 0, 0), NJS_DECLARE_PROP_NATIVE("test", njs_regexp_prototype_test, 1, 0), NJS_DECLARE_PROP_NATIVE("exec", njs_regexp_prototype_exec, 1, 0), { .type = NJS_PROPERTY, .name = njs_wellknown_symbol(NJS_SYMBOL_REPLACE), .u.value = njs_native_function(njs_regexp_prototype_symbol_replace, 2), .writable = 1, .configurable = 1, }, { .type = NJS_PROPERTY, .name = njs_wellknown_symbol(NJS_SYMBOL_SPLIT), .u.value = njs_native_function(njs_regexp_prototype_symbol_split, 2), .writable = 1, .configurable = 1, }, }; const njs_object_prop_t njs_regexp_instance_properties[] = { NJS_DECLARE_PROP_HANDLER("lastIndex", njs_regexp_prototype_last_index, 0, 0, NJS_OBJECT_PROP_VALUE_W), }; const njs_object_init_t njs_regexp_instance_init = { njs_regexp_instance_properties, njs_nitems(njs_regexp_instance_properties), }; const njs_object_init_t njs_regexp_prototype_init = { njs_regexp_prototype_properties, njs_nitems(njs_regexp_prototype_properties), }; const njs_object_type_init_t njs_regexp_type_init = { .constructor = njs_native_ctor(njs_regexp_constructor, 2, 0), .constructor_props = &njs_regexp_constructor_init, .prototype_props = &njs_regexp_prototype_init, .prototype_value = { .object = { .type = NJS_OBJECT } }, }; njs-0.8.9/src/njs_regexp.h000066400000000000000000000023261474132077100154370ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_REGEXP_H_INCLUDED_ #define _NJS_REGEXP_H_INCLUDED_ njs_int_t njs_regexp_init(njs_vm_t *vm); njs_int_t njs_regexp_create(njs_vm_t *vm, njs_value_t *value, u_char *start, size_t length, njs_regex_flags_t flags); njs_regex_flags_t njs_regexp_flags(u_char **start, u_char *end); njs_regexp_pattern_t *njs_regexp_pattern_create(njs_vm_t *vm, u_char *string, size_t length, njs_regex_flags_t flags); njs_int_t njs_regexp_match(njs_vm_t *vm, njs_regex_t *regex, const u_char *subject, size_t off, size_t len, njs_regex_match_data_t *d); njs_regexp_t *njs_regexp_alloc(njs_vm_t *vm, njs_regexp_pattern_t *pattern); njs_int_t njs_regexp_prototype_exec(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); njs_int_t njs_regexp_prototype_symbol_replace(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); njs_int_t njs_regexp_to_string(njs_vm_t *vm, njs_value_t *retval, const njs_value_t *regexp); extern const njs_object_init_t njs_regexp_instance_init; extern const njs_object_type_init_t njs_regexp_type_init; #endif /* _NJS_REGEXP_H_INCLUDED_ */ njs-0.8.9/src/njs_regexp_pattern.h000066400000000000000000000014361474132077100171750ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_REGEXP_PATTERN_H_INCLUDED_ #define _NJS_REGEXP_PATTERN_H_INCLUDED_ typedef enum { NJS_REGEXP_BYTE = 0, NJS_REGEXP_UTF8, } njs_regexp_utf8_t; typedef struct njs_regexp_group_s njs_regexp_group_t; struct njs_regexp_pattern_s { njs_regex_t regex[2]; /* A zero-terminated C string. */ u_char *source; uint16_t ncaptures; uint16_t ngroups; uint8_t global; /* 1 bit */ uint8_t ignore_case; /* 1 bit */ uint8_t multiline; /* 1 bit */ uint8_t sticky; /* 1 bit */ njs_regexp_group_t *groups; }; #endif /* _NJS_REGEXP_PATTERN_H_INCLUDED_ */ njs-0.8.9/src/njs_scope.c000066400000000000000000000144701474132077100152540ustar00rootroot00000000000000 /* * Copyright (C) Alexander Borisov * Copyright (C) NGINX, Inc. */ #include static njs_value_t *njs_scope_value_index(njs_vm_t *vm, const njs_value_t *src, njs_uint_t runtime, njs_index_t **index); njs_index_t njs_scope_temp_index(njs_parser_scope_t *scope) { scope = njs_function_scope(scope); if (njs_slow_path(scope == NULL)) { return NJS_INDEX_ERROR; } return njs_scope_index(scope->type, scope->items++, NJS_LEVEL_LOCAL, NJS_VARIABLE_VAR); } njs_value_t * njs_scope_create_index_value(njs_vm_t *vm, njs_index_t index) { njs_value_t *value; value = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t)); if (njs_slow_path(value == NULL)) { return NULL; } njs_scope_value_set(vm, index, value); return value; } njs_value_t ** njs_scope_make(njs_vm_t *vm, uint32_t count) { size_t size; njs_value_t **refs, *values; size = (count * sizeof(njs_value_t *)) + (count * sizeof(njs_value_t)); refs = njs_mp_alloc(vm->mem_pool, size); if (njs_slow_path(refs == NULL)) { njs_memory_error(vm); return NULL; } values = (njs_value_t *) ((u_char *) refs + (count * sizeof(njs_value_t *))); while (count != 0) { count--; refs[count] = &values[count]; njs_set_invalid(refs[count]); } return refs; } njs_index_t njs_scope_global_index(njs_vm_t *vm, const njs_value_t *src, njs_uint_t runtime) { njs_index_t index, *retval; njs_value_t **values, *value; value = njs_scope_value_index(vm, src, runtime, &retval); if (njs_slow_path(value == NULL)) { return NJS_INDEX_ERROR; } if (*retval != NJS_INDEX_ERROR) { return *retval; } if (vm->scope_absolute == NULL) { vm->scope_absolute = njs_arr_create(vm->mem_pool, 8, sizeof(njs_value_t *)); if (njs_slow_path(vm->scope_absolute == NULL)) { return NJS_INDEX_ERROR; } } index = vm->scope_absolute->items; values = njs_arr_add(vm->scope_absolute); if (njs_slow_path(values == NULL)) { return NJS_INDEX_ERROR; } *values = value; vm->levels[NJS_LEVEL_STATIC] = vm->scope_absolute->start; *retval = njs_scope_index(NJS_SCOPE_GLOBAL, index, NJS_LEVEL_STATIC, NJS_VARIABLE_VAR); return *retval; } njs_value_t * njs_scope_value_get(njs_vm_t *vm, njs_index_t index) { return njs_scope_value(vm, index); } static njs_int_t njs_scope_values_hash_test(njs_lvlhsh_query_t *lhq, void *data) { njs_str_t string; njs_value_t *value; value = data; if (njs_is_string(value)) { njs_string_get(value, &string); } else { string.start = (u_char *) value; string.length = sizeof(njs_value_t); } if (lhq->key.length == string.length && memcmp(lhq->key.start, string.start, string.length) == 0) { return NJS_OK; } return NJS_DECLINED; } static const njs_lvlhsh_proto_t njs_values_hash_proto njs_aligned(64) = { NJS_LVLHSH_DEFAULT, njs_scope_values_hash_test, njs_lvlhsh_alloc, njs_lvlhsh_free, }; /* * Constant values such as njs_value_true are copied to values_hash during * code generation when they are used as operands to guarantee aligned value. */ static njs_value_t * njs_scope_value_index(njs_vm_t *vm, const njs_value_t *src, njs_uint_t runtime, njs_index_t **index) { u_char *start; uint32_t value_size, size, length; njs_int_t ret; njs_str_t str; njs_bool_t long_string; njs_value_t *value; njs_string_t *string; njs_lvlhsh_t *values_hash; njs_lvlhsh_query_t lhq; long_string = 0; value_size = sizeof(njs_value_t); if (njs_is_string(src)) { njs_string_get(src, &str); size = (uint32_t) str.length; start = str.start; if (src->short_string.size == NJS_STRING_LONG) { long_string = 1; } } else { size = value_size; start = (u_char *) src; } lhq.key_hash = njs_djb_hash(start, size); lhq.key.length = size; lhq.key.start = start; lhq.proto = &njs_values_hash_proto; if (njs_lvlhsh_find(&vm->shared->values_hash, &lhq) == NJS_OK) { value = lhq.value; *index = (njs_index_t *) ((u_char *) value + sizeof(njs_value_t)); } else if (runtime && njs_lvlhsh_find(&vm->values_hash, &lhq) == NJS_OK) { value = lhq.value; *index = (njs_index_t *) ((u_char *) value + sizeof(njs_value_t)); } else { if (long_string) { length = src->long_string.data->length; if (size != length && length > NJS_STRING_MAP_STRIDE) { size = njs_string_map_offset(size) + njs_string_map_size(length); } value_size += sizeof(njs_string_t) + size; } value_size += sizeof(njs_index_t); value = njs_mp_align(vm->mem_pool, sizeof(njs_value_t), value_size); if (njs_slow_path(value == NULL)) { return NULL; } *value = *src; if (long_string) { string = (njs_string_t *) ((u_char *) value + sizeof(njs_value_t) + sizeof(njs_index_t)); value->long_string.data = string; string->start = (u_char *) string + sizeof(njs_string_t); string->length = src->long_string.data->length; memcpy(string->start, start, size); } *index = (njs_index_t *) ((u_char *) value + sizeof(njs_value_t)); **index = NJS_INDEX_ERROR; lhq.replace = 0; lhq.value = value; lhq.pool = vm->mem_pool; values_hash = runtime ? &vm->values_hash : &vm->shared->values_hash; ret = njs_lvlhsh_insert(values_hash, &lhq); if (njs_slow_path(ret != NJS_OK)) { return NULL; } } 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 value; } njs-0.8.9/src/njs_scope.h000066400000000000000000000072371474132077100152640ustar00rootroot00000000000000 /* * Copyright (C) Alexander Borisov * Copyright (C) NGINX, Inc. */ #ifndef _NJS_SCOPE_H_INCLUDED_ #define _NJS_SCOPE_H_INCLUDED_ #define NJS_SCOPE_VAR_OFFSET 0 #define NJS_SCOPE_VAR_SIZE 4 #define NJS_SCOPE_TYPE_OFFSET (NJS_SCOPE_VAR_OFFSET + NJS_SCOPE_VAR_SIZE) #define NJS_SCOPE_TYPE_SIZE 4 #define NJS_SCOPE_VALUE_OFFSET (NJS_SCOPE_TYPE_OFFSET + NJS_SCOPE_TYPE_SIZE) #define NJS_SCOPE_VALUE_SIZE 24 #define NJS_SCOPE_VALUE_MASK ((1 << NJS_SCOPE_VALUE_SIZE) - 1) #define NJS_SCOPE_VAR_MASK ((1 << NJS_SCOPE_VAR_SIZE) - 1) #define NJS_SCOPE_TYPE_MASK ((1 << NJS_SCOPE_TYPE_SIZE) - 1) #define NJS_SCOPE_VALUE_MAX NJS_SCOPE_VALUE_MASK #define NJS_INDEX_NONE ((njs_index_t) 0) #define NJS_INDEX_ERROR ((njs_index_t) -1) njs_index_t njs_scope_temp_index(njs_parser_scope_t *scope); njs_value_t *njs_scope_create_index_value(njs_vm_t *vm, njs_index_t index); njs_value_t **njs_scope_make(njs_vm_t *vm, uint32_t count); njs_index_t njs_scope_global_index(njs_vm_t *vm, const njs_value_t *src, njs_uint_t runtime); njs_value_t *njs_scope_value_get(njs_vm_t *vm, njs_index_t index); njs_inline njs_index_t njs_scope_index(njs_scope_t scope, njs_index_t index, njs_level_type_t type, njs_variable_type_t var_type) { njs_assert(type < NJS_LEVEL_MAX); njs_assert(scope == NJS_SCOPE_GLOBAL || scope == NJS_SCOPE_FUNCTION); if (index > NJS_SCOPE_VALUE_MAX) { return NJS_INDEX_ERROR; } if (scope == NJS_SCOPE_GLOBAL && type == NJS_LEVEL_LOCAL) { type = NJS_LEVEL_GLOBAL; } return (index << NJS_SCOPE_VALUE_OFFSET) | (type << NJS_SCOPE_TYPE_OFFSET) | var_type; } njs_inline njs_variable_type_t njs_scope_index_var(njs_index_t index) { return (njs_variable_type_t) (index & NJS_SCOPE_VAR_MASK); } njs_inline njs_level_type_t njs_scope_index_type(njs_index_t index) { return (njs_level_type_t) ((index >> NJS_SCOPE_TYPE_OFFSET) & NJS_SCOPE_TYPE_MASK); } njs_inline uint32_t njs_scope_index_value(njs_index_t index) { return (uint32_t) (index >> NJS_SCOPE_VALUE_OFFSET); } njs_inline njs_value_t * njs_scope_value(njs_vm_t *vm, njs_index_t index) { return vm->levels[njs_scope_index_type(index)] [njs_scope_index_value(index)]; } njs_inline njs_value_t * njs_scope_valid_value(njs_vm_t *vm, njs_index_t index) { njs_value_t *value; value = njs_scope_value(vm, index); if (!njs_is_valid(value)) { if (njs_scope_index_var(index) <= NJS_VARIABLE_LET) { njs_reference_error(vm, "cannot access variable " "before initialization"); return NULL; } njs_set_undefined(value); } return value; } njs_inline void njs_scope_value_set(njs_vm_t *vm, njs_index_t index, njs_value_t *value) { vm->levels[njs_scope_index_type(index)] [njs_scope_index_value(index)] = value; } njs_inline njs_value_t * njs_scope_value_clone(njs_vm_t *vm, njs_index_t index, njs_value_t *value) { njs_value_t *newval; newval = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t)); if (njs_slow_path(newval == NULL)) { njs_memory_error(vm); return NULL; } *newval = *value; njs_scope_value_set(vm, index, newval); return newval; } njs_inline njs_index_t njs_scope_undefined_index(njs_vm_t *vm, njs_uint_t runtime) { return njs_scope_global_index(vm, &njs_value_undefined, runtime); } njs_inline njs_index_t njs_scope_global_this_index() { return njs_scope_index(NJS_SCOPE_GLOBAL, 0, NJS_LEVEL_LOCAL, NJS_VARIABLE_VAR); } #endif /* _NJS_PARSER_H_INCLUDED_ */ njs-0.8.9/src/njs_sprintf.c000066400000000000000000000332251474132077100156270ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include /* * Supported formats: * * %[0][width][x][X]O njs_off_t * %[0][width]T njs_time_t * %[0][width][u][x|X]z ssize_t/size_t * %[0][width][u][x|X]d int/u_int * %[0][width][u][x|X]l long * %[0][width|m][u][x|X]i njs_int_t/njs_uint_t * %[0][width][u][x|X]D int32_t/uint32_t * %[0][width][u][x|X]L int64_t/uint64_t * %[0][width][.width]f double, max valid number fits to %18.15f * * %d int * * %s null-terminated string * %*s length and string * * %p void * * %P symbolized function address * %b njs_bool_t * %V njs_str_t * * %Z '\0' * %n '\n' * %c char * %% % * * Reserved: * %t ptrdiff_t * %S null-terminated wchar string * %C wchar * %[0][width][u][x|X]Q int128_t/uint128_t */ u_char * njs_sprintf(u_char *buf, u_char *end, const char *fmt, ...) { u_char *p; va_list args; va_start(args, fmt); p = njs_vsprintf(buf, end, fmt, args); va_end(args); return p; } /* * njs_sprintf_t is used: * to pass several parameters of njs_integer() via single pointer * and to store little used variables of njs_vsprintf(). */ typedef struct { u_char *end; const u_char *hex; uint32_t width; int32_t frac_width; uint8_t max_width; u_char padding; } njs_sprintf_t; static u_char *njs_integer(njs_sprintf_t *spf, u_char *buf, uint64_t ui64); static u_char *njs_float(njs_sprintf_t *spf, u_char *buf, double n); /* A right way of "f == 0.0". */ #define njs_double_is_zero(f) (fabs(f) <= FLT_EPSILON) u_char * njs_vsprintf(u_char *buf, u_char *end, const char *fmt, va_list args) { u_char *p; int d; double f, i; size_t size, length; int64_t i64; uint64_t ui64, frac; njs_str_t *v; njs_uint_t scale, n; njs_bool_t sign; njs_sprintf_t spf; static const u_char hexadecimal[16] = "0123456789abcdef"; static const u_char HEXADECIMAL[16] = "0123456789ABCDEF"; static const u_char nan[] = "[nan]"; static const u_char infinity[] = "[infinity]"; spf.end = end; while (*fmt != '\0' && buf < end) { /* * "buf < end" means that we could copy at least one character: * a plain character, "%%", "%c", or a minus without test. */ if (*fmt != '%') { *buf++ = *fmt++; continue; } fmt++; /* Test some often used text formats first. */ switch (*fmt) { case 'V': fmt++; v = va_arg(args, njs_str_t *); if (njs_fast_path(v != NULL)) { length = v->length; p = v->start; goto copy; } continue; case 's': p = va_arg(args, u_char *); if (njs_fast_path(p != NULL)) { while (*p != '\0' && buf < end) { *buf++ = *p++; } } fmt++; continue; case '*': length = va_arg(args, size_t); fmt++; if (*fmt == 's') { fmt++; p = va_arg(args, u_char *); if (njs_fast_path(p != NULL)) { goto copy; } } continue; default: break; } spf.hex = NULL; spf.width = 0; spf.frac_width = -1; spf.max_width = 0; spf.padding = (*fmt == '0') ? '0' : ' '; sign = 1; i64 = 0; ui64 = 0; while (*fmt >= '0' && *fmt <= '9') { spf.width = spf.width * 10 + (*fmt++ - '0'); } for ( ;; ) { switch (*fmt) { case 'u': sign = 0; fmt++; continue; case 'm': spf.max_width = 1; fmt++; continue; case 'X': spf.hex = HEXADECIMAL; sign = 0; fmt++; continue; case 'x': spf.hex = hexadecimal; sign = 0; fmt++; continue; case '.': fmt++; spf.frac_width = 0; while (*fmt >= '0' && *fmt <= '9') { spf.frac_width = spf.frac_width * 10 + *fmt++ - '0'; } break; default: break; } break; } switch (*fmt) { case 'O': i64 = (int64_t) va_arg(args, njs_off_t); sign = 1; goto number; case 'T': i64 = (int64_t) va_arg(args, njs_time_t); sign = 1; goto number; case 'z': if (sign) { i64 = (int64_t) va_arg(args, ssize_t); } else { ui64 = (uint64_t) va_arg(args, size_t); } goto number; case 'i': if (sign) { i64 = (int64_t) va_arg(args, njs_int_t); } else { ui64 = (uint64_t) va_arg(args, njs_uint_t); } if (spf.max_width != 0) { spf.width = NJS_INT_T_LEN; } goto number; case 'd': if (sign) { i64 = (int64_t) va_arg(args, int); } else { ui64 = (uint64_t) va_arg(args, u_int); } goto number; case 'l': if (sign) { i64 = (int64_t) va_arg(args, long); } else { ui64 = (uint64_t) va_arg(args, u_long); } goto number; case 'D': if (sign) { i64 = (int64_t) va_arg(args, int32_t); } else { ui64 = (uint64_t) va_arg(args, uint32_t); } goto number; case 'L': if (sign) { i64 = va_arg(args, int64_t); } else { ui64 = va_arg(args, uint64_t); } goto number; case 'b': ui64 = (uint64_t) va_arg(args, njs_bool_t); sign = 0; goto number; case 'f': fmt++; f = va_arg(args, double); if (f < 0) { *buf++ = '-'; f = -f; } if (njs_slow_path(isnan(f))) { p = (u_char *) nan; length = njs_length(nan); goto copy; } else if (njs_slow_path(isinf(f))) { p = (u_char *) infinity; length = njs_length(infinity); goto copy; } (void) modf(f, &i); frac = 0; if (spf.frac_width > 0) { scale = 1; for (n = spf.frac_width; n != 0; n--) { scale *= 10; } frac = (uint64_t) ((f - i) * scale + 0.5); if (frac == scale) { i += 1; frac = 0; } } buf = njs_float(&spf, buf, i); if (spf.frac_width > 0) { if (buf < end) { *buf++ = '.'; spf.hex = NULL; spf.padding = '0'; spf.width = spf.frac_width; buf = njs_integer(&spf, buf, frac); } } else if (spf.frac_width < 0) { f = modf(f, &i); if (!njs_double_is_zero(f) && buf < end) { *buf++ = '.'; while (!njs_double_is_zero(f) && buf < end) { f *= 10; f = modf(f, &i); *buf++ = (u_char) i + '0'; } } } continue; case 'p': ui64 = (uintptr_t) va_arg(args, void *); sign = 0; spf.hex = HEXADECIMAL; /* * spf.width = NJS_PTR_SIZE * 2; * spf.padding = '0'; */ goto number; case 'P': buf = njs_addr2line(buf, end, va_arg(args, void *)); fmt++; continue; case 'c': d = va_arg(args, int); *buf++ = (u_char) (d & 0xFF); fmt++; continue; case 'Z': *buf++ = '\0'; fmt++; continue; case 'n': *buf++ = '\n'; fmt++; continue; case '%': *buf++ = '%'; fmt++; continue; default: *buf++ = *fmt++; continue; } number: if (sign) { if (i64 < 0) { *buf++ = '-'; ui64 = (uint64_t) -i64; } else { ui64 = (uint64_t) i64; } } buf = njs_integer(&spf, buf, ui64); fmt++; continue; copy: size = njs_min((size_t) (end - buf), length); if (size != 0) { buf = njs_cpymem(buf, p, size); } continue; } return buf; } static u_char * njs_integer(njs_sprintf_t *spf, u_char *buf, uint64_t ui64) { u_char *p, *end; size_t length; u_char temp[NJS_INT64_T_LEN]; p = temp + NJS_INT64_T_LEN; if (spf->hex == NULL) { #if (NJS_32BIT) for ( ;; ) { u_char *start; uint32_t ui32; /* * 32-bit platforms usually lack hardware support of 64-bit * division and remainder operations. For this reason C compiler * adds calls to the runtime library functions which provides * these operations. These functions usually have about hundred * lines of code. * * For 32-bit numbers and some constant divisors GCC, Clang and * other compilers can use inlined multiplications and shifts * which are faster than division or remainder operations. * For example, unsigned "ui32 / 10" is compiled to * * ((uint64_t) ui32 * 0xCCCCCCCD) >> 35 * * So a 64-bit number is split to parts by 10^9. The parts fit * to 32 bits and are processed separately as 32-bit numbers. A * number of 64-bit division/remainder operations is significantly * decreased depending on the 64-bit number's value, it is * 0 if the 64-bit value is less than 4294967296, * 1 if the 64-bit value is greater than 4294967295 * and less than 4294967296000000000, * 2 otherwise. */ if (ui64 <= 0xFFFFFFFF) { ui32 = (uint32_t) ui64; start = NULL; } else { ui32 = (uint32_t) (ui64 % 1000000000); start = p - 9; } do { *(--p) = (u_char) (ui32 % 10 + '0'); ui32 /= 10; } while (ui32 != 0); if (start == NULL) { break; } /* Add leading zeros of part. */ while (p > start) { *(--p) = '0'; } ui64 /= 1000000000; } #else /* NJS_64BIT */ do { *(--p) = (u_char) (ui64 % 10 + '0'); ui64 /= 10; } while (ui64 != 0); #endif } else { do { *(--p) = spf->hex[ui64 & 0xF]; ui64 >>= 4; } while (ui64 != 0); } length = (temp + NJS_INT64_T_LEN) - p; /* Zero or space padding. */ if (length < spf->width) { end = buf + spf->width - length; end = njs_min(end, spf->end); while (buf < end) { *buf++ = spf->padding; } } /* Number copying. */ end = buf + length; end = njs_min(end, spf->end); while (buf < end) { *buf++ = *p++; } return buf; } static u_char * njs_float(njs_sprintf_t *spf, u_char *buf, double n) { u_char *p, *end; size_t length; u_char temp[NJS_DOUBLE_LEN]; p = temp + NJS_DOUBLE_LEN; do { *(--p) = (u_char) (fmod(n, 10) + '0'); n = trunc(n / 10); } while (!njs_double_is_zero(n)); /* Zero or space padding. */ if (spf->width != 0) { length = (temp + NJS_DOUBLE_LEN) - p; end = buf + (spf->width - length); end = njs_min(end, spf->end); while (buf < end) { *buf++ = spf->padding; } } /* Number copying. */ length = (temp + NJS_DOUBLE_LEN) - p; end = buf + length; end = njs_min(end, spf->end); while (buf < end) { *buf++ = *p++; } return buf; } NJS_EXPORT int njs_dprint(int fd, u_char *buf, size_t size) { return write(fd, buf, size); } int njs_dprintf(int fd, const char *fmt, ...) { size_t size; u_char text[16384], *p; va_list args; va_start(args, fmt); p = njs_vsprintf(text, text + sizeof(text), fmt, args); va_end(args); size = p - text; return write(fd, text, size); } njs-0.8.9/src/njs_sprintf.h000066400000000000000000000015661474132077100156370ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_SPRINTF_H_INCLUDED_ #define _NJS_SPRINTF_H_INCLUDED_ NJS_EXPORT u_char *njs_sprintf(u_char *buf, u_char *end, const char *fmt, ...); NJS_EXPORT u_char *njs_vsprintf(u_char *buf, u_char *end, const char *fmt, va_list args); NJS_EXPORT int njs_dprint(int fd, u_char *buf, size_t size); NJS_EXPORT int njs_dprintf(int fd, const char *fmt, ...); #define njs_print(buf, size) \ njs_dprint(1 /* STDOUT_FILENO */, (u_char *) buf, size) #define njs_printf(fmt, ...) \ njs_dprintf(1 /* STDOUT_FILENO */, fmt, ##__VA_ARGS__) #define njs_stderror(fmt, ...) \ njs_dprintf(2 /* STDERR_FILENO */, fmt, ##__VA_ARGS__) #endif /* _NJS_SPRINTF_H_INCLUDED_ */ njs-0.8.9/src/njs_str.c000066400000000000000000000010751474132077100147500ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #include njs_int_t njs_strncasecmp(u_char *s1, u_char *s2, size_t n) { njs_uint_t c1, c2; while (n) { c1 = (njs_uint_t) *s1++; c2 = (njs_uint_t) *s2++; c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1; c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2; if (c1 == c2) { if (c1) { n--; continue; } return 0; } return c1 - c2; } return 0; } njs-0.8.9/src/njs_str.h000066400000000000000000000076441474132077100147650ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_STR_H_INCLUDED_ #define _NJS_STR_H_INCLUDED_ typedef struct { size_t length; u_char *start; } njs_str_t; /* * C99 allows to assign struct as compound literal with struct name cast only. * SunC however issues error on the cast in struct static initialization: * non-constant initializer: op "NAME" * So a separate njs_str_value() macro is intended to use in assignment. */ #define njs_length(s) (sizeof(s) - 1) #define njs_str(s) { njs_length(s), (u_char *) s } #define njs_null_str { 0, NULL } #define njs_str_value(s) (njs_str_t) njs_str(s) njs_inline u_char njs_lower_case(u_char c) { return (u_char) ((c >= 'A' && c <= 'Z') ? c | 0x20 : c); } njs_inline u_char njs_upper_case(u_char c) { return (u_char) ((c >= 'a' && c <= 'z') ? c & 0xDF : c); } njs_inline njs_bool_t njs_is_whitespace(u_char c) { switch (c) { case 0x09: /* */ case 0x0A: /* */ case 0x0B: /* */ case 0x0C: /* */ case 0x0D: /* */ case 0x20: /* */ return 1; default: return 0; } } njs_inline u_char * njs_strlchr(u_char *p, u_char *last, u_char c) { while (p < last) { if (*p == c) { return p; } p++; } return NULL; } #define \ njs_strlen(s) \ strlen((char *) s) #define \ njs_cpymem(dst, src, n) \ (((u_char *) memcpy(dst, src, n)) + (n)) #define \ njs_strncmp(s1, s2, n) \ strncmp((char *) s1, (char *) s2, n) #define \ njs_strchr(s1, c) \ (u_char *) strchr((const char *) s1, (int) c) #define \ njs_memset(buf, c, length) \ (void) memset(buf, c, length) #define \ njs_memzero(buf, length) \ (void) memset(buf, 0, length) #if (NJS_HAVE_EXPLICIT_BZERO && !NJS_HAVE_MEMORY_SANITIZER) #define \ njs_explicit_memzero(buf, length) \ explicit_bzero(buf, length) #elif (NJS_HAVE_EXPLICIT_MEMSET) #define \ njs_explicit_memzero(buf, length) \ (void) explicit_memset(buf, 0, length) #else njs_inline void njs_explicit_memzero(void *buf, size_t length) { volatile u_char *p = (volatile u_char *) buf; while (length != 0) { *p++ = 0; length--; } } #endif #define \ njs_strstr_eq(s1, s2) \ (((s1)->length == (s2)->length) \ && (memcmp((s1)->start, (s2)->start, (s1)->length) == 0)) #define \ njs_strstr_case_eq(s1, s2) \ (((s1)->length == (s2)->length) \ && (njs_strncasecmp((s1)->start, (s2)->start, (s1)->length) == 0)) NJS_EXPORT njs_int_t njs_strncasecmp(u_char *s1, u_char *s2, size_t n); #endif /* _NJS_STR_H_INCLUDED_ */ njs-0.8.9/src/njs_string.c000066400000000000000000003137331474132077100154550ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include static u_char njs_basis64[] = { 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 62, 77, 77, 77, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 77, 77, 77, 77, 77, 77, 77, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 77, 77, 77, 77, 77, 77, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77 }; static u_char njs_basis64url[] = { 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 62, 77, 77, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 77, 77, 77, 77, 77, 77, 77, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 77, 77, 77, 77, 63, 77, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77 }; static u_char njs_basis64_enc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static u_char njs_basis64url_enc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; static void njs_encode_base64_core(njs_str_t *dst, const njs_str_t *src, const u_char *basis, njs_uint_t padding); static njs_int_t njs_string_decode_base64_core(njs_vm_t *vm, njs_value_t *value, const njs_str_t *src, njs_bool_t url); static njs_int_t njs_string_slice_prop(njs_vm_t *vm, njs_string_prop_t *string, njs_slice_prop_t *slice, njs_value_t *args, njs_uint_t nargs); static njs_int_t njs_string_slice_args(njs_vm_t *vm, njs_slice_prop_t *slice, njs_value_t *args, njs_uint_t nargs); static njs_int_t njs_string_from_char_code(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t is_point, njs_value_t *retval); static njs_int_t njs_string_match_multiple(njs_vm_t *vm, njs_value_t *args, njs_regexp_pattern_t *pattern, njs_value_t *retval); #define njs_base64_encoded_length(len) (((len + 2) / 3) * 4) #define njs_base64_decoded_length(len, pad) (((len / 4) * 3) - pad) njs_int_t njs_string_create(njs_vm_t *vm, njs_value_t *value, const u_char *src, size_t size) { u_char *p, *p_end; njs_str_t str; p = (u_char *) src; p_end = p + size; while (p < p_end && *p < 0x80) { p++; } if (p == p_end) { return njs_string_new(vm, value, (u_char *) src, size, size); } str.start = (u_char *) src; str.length = size; return njs_string_decode_utf8(vm, value, &str); } njs_int_t njs_string_create_chb(njs_vm_t *vm, njs_value_t *value, njs_chb_t *chain) { u_char *p; ssize_t size, length; size = njs_chb_size(chain); if (njs_slow_path(size < 0)) { njs_memory_error(vm); return NJS_ERROR; } length = njs_chb_utf8_length(chain); if (njs_slow_path(length < 0)) { njs_internal_error(vm, "invalid UTF-8 string"); return NJS_ERROR; } p = njs_string_alloc(vm, value, size, length); if (njs_slow_path(p == NULL)) { return NJS_ERROR; } njs_chb_join_to(chain, p); return NJS_OK; } njs_int_t njs_string_new(njs_vm_t *vm, njs_value_t *value, const u_char *start, uint32_t size, uint32_t length) { u_char *p; njs_assert((size == 0 && length == 0) || (size != 0 && length != 0)); p = njs_string_alloc(vm, value, size, length); if (njs_fast_path(p != NULL)) { memcpy(p, start, size); return NJS_OK; } return NJS_ERROR; } u_char * njs_string_alloc(njs_vm_t *vm, njs_value_t *value, uint64_t size, uint64_t length) { uint32_t total, map_offset, *map; njs_string_t *string; if (njs_slow_path(size > NJS_STRING_MAX_LENGTH)) { njs_range_error(vm, "invalid string length"); return NULL; } 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->long_string.external = 0; value->long_string.size = size; if (size != length && length > NJS_STRING_MAP_STRIDE) { map_offset = njs_string_map_offset(size); total = map_offset + njs_string_map_size(length); } else { map_offset = 0; total = size; } string = njs_mp_alloc(vm->mem_pool, sizeof(njs_string_t) + total); if (njs_fast_path(string != NULL)) { value->long_string.data = string; string->start = (u_char *) string + sizeof(njs_string_t); string->length = length; if (map_offset != 0) { map = (uint32_t *) (string->start + map_offset); map[0] = 0; } return string->start; } njs_memory_error(vm); return NULL; } uint32_t njs_string_length(njs_value_t *string) { uint32_t length, size; if (string->short_string.size != NJS_STRING_LONG) { size = string->short_string.size; length = string->short_string.length; } else { size = string->long_string.size; length = string->long_string.data->length; } return (length == 0) ? size : length; } size_t njs_string_prop(njs_string_prop_t *string, const njs_value_t *value) { size_t size; uintptr_t length; size = value->short_string.size; if (size != NJS_STRING_LONG) { string->start = (u_char *) value->short_string.start; length = value->short_string.length; } else { string->start = (u_char *) value->long_string.data->start; size = value->long_string.size; length = value->long_string.data->length; } string->size = size; string->length = length; return (length == 0) ? size : length; } void njs_string_truncate(njs_value_t *value, uint32_t size, uint32_t length) { u_char *dst, *src; uint32_t n; if (size <= NJS_STRING_SHORT) { if (value->short_string.size == NJS_STRING_LONG) { dst = value->short_string.start; src = value->long_string.data->start; n = size; while (n != 0) { /* The maximum size is just 14 bytes. */ njs_pragma_loop_disable_vectorization; *dst++ = *src++; n--; } } value->short_string.size = size; value->short_string.length = length; } else { value->long_string.size = size; value->long_string.data->length = length; } } njs_int_t njs_string_hex(njs_vm_t *vm, njs_value_t *retval, const njs_str_t *src) { size_t length; njs_str_t dst; length = njs_encode_hex_length(src, &dst.length); dst.start = njs_string_alloc(vm, retval, dst.length, length); if (njs_fast_path(dst.start != NULL)) { njs_encode_hex(&dst, src); return NJS_OK; } return NJS_ERROR; } void njs_encode_hex(njs_str_t *dst, const njs_str_t *src) { u_char *p, c; size_t i, len; const u_char *start; static const u_char hex[16] = "0123456789abcdef"; len = src->length; start = src->start; p = dst->start; for (i = 0; i < len; i++) { c = start[i]; *p++ = hex[c >> 4]; *p++ = hex[c & 0x0f]; } } size_t njs_encode_hex_length(const njs_str_t *src, size_t *out_size) { size_t size; size = src->length * 2; if (out_size != NULL) { *out_size = size; } return size; } void njs_encode_base64(njs_str_t *dst, const njs_str_t *src) { njs_encode_base64_core(dst, src, njs_basis64_enc, 1); } size_t njs_encode_base64_length(const njs_str_t *src, size_t *out_size) { size_t size; size = (src->length == 0) ? 0 : njs_base64_encoded_length(src->length); if (out_size != NULL) { *out_size = size; } return size; } static void njs_encode_base64url(njs_str_t *dst, const njs_str_t *src) { njs_encode_base64_core(dst, src, njs_basis64url_enc, 0); } static void njs_encode_base64_core(njs_str_t *dst, const njs_str_t *src, const u_char *basis, njs_bool_t padding) { u_char *d, *s, c0, c1, c2; size_t len; len = src->length; s = src->start; d = dst->start; while (len > 2) { c0 = s[0]; c1 = s[1]; c2 = s[2]; *d++ = basis[c0 >> 2]; *d++ = basis[((c0 & 0x03) << 4) | (c1 >> 4)]; *d++ = basis[((c1 & 0x0f) << 2) | (c2 >> 6)]; *d++ = basis[c2 & 0x3f]; s += 3; len -= 3; } if (len > 0) { c0 = s[0]; *d++ = basis[c0 >> 2]; if (len == 1) { *d++ = basis[(c0 & 0x03) << 4]; if (padding) { *d++ = '='; *d++ = '='; } } else { c1 = s[1]; *d++ = basis[((c0 & 0x03) << 4) | (c1 >> 4)]; *d++ = basis[(c1 & 0x0f) << 2]; if (padding) { *d++ = '='; } } } dst->length = d - dst->start; } njs_int_t njs_string_base64(njs_vm_t *vm, njs_value_t *retval, const njs_str_t *src) { size_t length; njs_str_t dst; length = njs_encode_base64_length(src, &dst.length); if (njs_slow_path(dst.length == 0)) { njs_value_assign(retval, &njs_string_empty); return NJS_OK; } dst.start = njs_string_alloc(vm, retval, dst.length, length); if (njs_slow_path(dst.start == NULL)) { return NJS_ERROR; } njs_encode_base64(&dst, src); return NJS_OK; } njs_int_t njs_string_base64url(njs_vm_t *vm, njs_value_t *retval, const njs_str_t *src) { size_t padding; njs_str_t dst; if (njs_slow_path(src->length == 0)) { njs_value_assign(retval, &njs_string_empty); return NJS_OK; } padding = src->length % 3; /* * Calculating the padding length: 0 -> 0, 1 -> 2, 2 -> 1. */ padding = (4 >> padding) & 0x03; dst.length = njs_base64_encoded_length(src->length) - padding; dst.start = njs_string_alloc(vm, retval, dst.length, dst.length); if (njs_slow_path(dst.start == NULL)) { return NJS_ERROR; } njs_encode_base64url(&dst, src); return NJS_OK; } void njs_string_copy(njs_value_t *dst, njs_value_t *src) { *dst = *src; } static njs_int_t njs_string_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_value_t *value; njs_object_value_t *object; if (nargs == 1) { value = njs_value_arg(&njs_string_empty); } else { value = &args[1]; if (njs_slow_path(!njs_is_string(value))) { if (!vm->top_frame->ctor && njs_is_symbol(value)) { return njs_symbol_descriptive_string(vm, retval, value); } ret = njs_value_to_string(vm, value, value); if (njs_slow_path(ret != NJS_OK)) { return ret; } } } if (vm->top_frame->ctor) { object = njs_object_value_alloc(vm, NJS_OBJ_TYPE_STRING, 0, value); if (njs_slow_path(object == NULL)) { return NJS_ERROR; } njs_set_object_value(retval, object); } else { njs_value_assign(retval, value); } return NJS_OK; } static const njs_object_prop_t njs_string_constructor_properties[] = { NJS_DECLARE_PROP_LENGTH(1), NJS_DECLARE_PROP_NAME("String"), NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), NJS_DECLARE_PROP_NATIVE("fromCharCode", njs_string_from_char_code, 1, 0), NJS_DECLARE_PROP_NATIVE("fromCodePoint", njs_string_from_char_code, 1, 1), }; const njs_object_init_t njs_string_constructor_init = { njs_string_constructor_properties, njs_nitems(njs_string_constructor_properties), }; static njs_int_t njs_string_instance_length(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { size_t size; uintptr_t length; njs_object_value_t *ov; /* * This getter can be called for string primitive, String object, * String.prototype. The zero should be returned for the latter case. */ length = 0; if (njs_slow_path(njs_is_object(value))) { ov = njs_object_proto_lookup(njs_object(value), NJS_OBJECT_VALUE, njs_object_value_t); if (ov != NULL) { value = &ov->value; } } if (njs_is_string(value)) { size = value->short_string.size; length = value->short_string.length; if (size == NJS_STRING_LONG) { size = value->long_string.size; length = value->long_string.data->length; } length = (length == 0) ? size : length; } njs_set_number(retval, length); return NJS_OK; } njs_bool_t njs_string_eq(const njs_value_t *v1, const njs_value_t *v2) { size_t size, length1, length2; const u_char *start1, *start2; size = v1->short_string.size; if (size != v2->short_string.size) { return 0; } if (size != NJS_STRING_LONG) { length1 = v1->short_string.length; length2 = v2->short_string.length; /* * Using full memcmp() comparison if at least one string * is a Byte string. */ if (length1 != 0 && length2 != 0 && length1 != length2) { return 0; } start1 = v1->short_string.start; start2 = v2->short_string.start; } else { size = v1->long_string.size; if (size != v2->long_string.size) { return 0; } length1 = v1->long_string.data->length; length2 = v2->long_string.data->length; /* * Using full memcmp() comparison if at least one string * is a Byte string. */ if (length1 != 0 && length2 != 0 && length1 != length2) { return 0; } start1 = v1->long_string.data->start; start2 = v2->long_string.data->start; } return (memcmp(start1, start2, size) == 0); } njs_int_t njs_string_cmp(const njs_value_t *v1, const njs_value_t *v2) { size_t size, size1, size2; njs_int_t ret; const u_char *start1, *start2; njs_assert(njs_is_string(v1)); njs_assert(njs_is_string(v2)); size1 = v1->short_string.size; if (size1 != NJS_STRING_LONG) { start1 = v1->short_string.start; } else { size1 = v1->long_string.size; start1 = v1->long_string.data->start; } size2 = v2->short_string.size; if (size2 != NJS_STRING_LONG) { start2 = v2->short_string.start; } else { size2 = v2->long_string.size; start2 = v2->long_string.data->start; } size = njs_min(size1, size2); ret = memcmp(start1, start2, size); if (ret != 0) { return ret; } return (size1 - size2); } static njs_int_t njs_string_prototype_value_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_value_t *value; value = &args[0]; if (value->type != NJS_STRING) { if (njs_is_object_string(value)) { value = njs_object_value(value); } else { njs_type_error(vm, "unexpected value type:%s", njs_type_string(value->type)); return NJS_ERROR; } } njs_value_assign(retval, value); return NJS_OK; } static njs_int_t njs_string_prototype_to_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { return njs_string_prototype_value_of(vm, args, nargs, unused, retval); } njs_int_t njs_string_prototype_concat(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { u_char *p, *start; uint64_t size, length, mask; njs_int_t ret; njs_uint_t i; njs_string_prop_t string; if (njs_is_null_or_undefined(&args[0])) { njs_type_error(vm, "\"this\" argument is null or undefined"); return NJS_ERROR; } for (i = 0; i < nargs; i++) { if (!njs_is_string(&args[i])) { ret = njs_value_to_string(vm, &args[i], &args[i]); if (ret != NJS_OK) { return ret; } } } if (nargs == 1) { njs_string_copy(retval, &args[0]); return NJS_OK; } size = 0; length = 0; mask = -1; for (i = 0; i < nargs; i++) { (void) njs_string_prop(&string, &args[i]); size += string.size; length += string.length; } length &= mask; start = njs_string_alloc(vm, retval, size, length); if (njs_slow_path(start == NULL)) { return NJS_ERROR; } p = start; for (i = 0; i < nargs; i++) { (void) njs_string_prop(&string, &args[i]); p = memcpy(p, string.start, string.size); p += string.size; } return NJS_OK; } njs_inline njs_int_t njs_string_object_validate(njs_vm_t *vm, njs_value_t *object) { njs_int_t ret; if (njs_slow_path(njs_is_null_or_undefined(object))) { njs_type_error(vm, "cannot convert undefined to object"); return NJS_ERROR; } if (njs_slow_path(!njs_is_string(object))) { ret = njs_value_to_string(vm, object, object); if (njs_slow_path(ret != NJS_OK)) { return ret; } } return NJS_OK; } static njs_int_t njs_string_prototype_slice(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_slice_prop_t slice; njs_string_prop_t string; ret = njs_string_object_validate(vm, njs_argument(args, 0)); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_string_slice_prop(vm, &string, &slice, args, nargs); if (njs_slow_path(ret != NJS_OK)) { return ret; } return njs_string_slice(vm, retval, &string, &slice); } static njs_int_t njs_string_prototype_substring(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t start, end, length; njs_int_t ret; njs_value_t *value; njs_slice_prop_t slice; njs_string_prop_t string; ret = njs_string_object_validate(vm, njs_argument(args, 0)); if (njs_slow_path(ret != NJS_OK)) { return ret; } length = njs_string_prop(&string, njs_argument(args, 0)); slice.string_length = length; start = 0; if (nargs > 1) { value = njs_argument(args, 1); if (njs_slow_path(!njs_is_number(value))) { ret = njs_value_to_integer(vm, value, &start); if (njs_slow_path(ret != NJS_OK)) { return ret; } } else { start = njs_number_to_integer(njs_number(value)); } if (start < 0) { start = 0; } else if (start > length) { start = length; } end = length; if (nargs > 2) { value = njs_arg(args, nargs, 2); if (njs_slow_path(!njs_is_number(value))) { ret = njs_value_to_integer(vm, value, &end); if (njs_slow_path(ret != NJS_OK)) { return ret; } } else { end = njs_number_to_integer(njs_number(value)); } if (end < 0) { end = 0; } else if (end >= length) { end = length; } } length = end - start; if (length < 0) { length = -length; start = end; } } slice.start = start; slice.length = length; return njs_string_slice(vm, retval, &string, &slice); } static njs_int_t njs_string_prototype_substr(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t start, length, n; njs_int_t ret; njs_value_t *value; njs_slice_prop_t slice; njs_string_prop_t string; ret = njs_string_object_validate(vm, njs_argument(args, 0)); if (njs_slow_path(ret != NJS_OK)) { return ret; } length = njs_string_prop(&string, njs_argument(args, 0)); slice.string_length = length; start = 0; if (nargs > 1) { value = njs_arg(args, nargs, 1); if (njs_slow_path(!njs_is_number(value))) { ret = njs_value_to_integer(vm, value, &start); if (njs_slow_path(ret != NJS_OK)) { return ret; } } else { start = njs_number_to_integer(njs_number(value)); } if (start < length) { if (start < 0) { start += length; if (start < 0) { start = 0; } } length -= start; if (nargs > 2) { value = njs_arg(args, nargs, 2); if (njs_slow_path(!njs_is_number(value))) { ret = njs_value_to_integer(vm, value, &n); if (njs_slow_path(ret != NJS_OK)) { return ret; } } else { n = njs_number_to_integer(njs_number(value)); } if (n < 0) { length = 0; } else if (n < length) { length = n; } } } else { start = 0; length = 0; } } slice.start = start; slice.length = length; return njs_string_slice(vm, retval, &string, &slice); } static njs_int_t njs_string_prototype_char_at(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { size_t length; int64_t start; njs_int_t ret; njs_slice_prop_t slice; njs_string_prop_t string; ret = njs_string_object_validate(vm, njs_argument(args, 0)); if (njs_slow_path(ret != NJS_OK)) { return ret; } slice.string_length = njs_string_prop(&string, njs_argument(args, 0)); ret = njs_value_to_integer(vm, njs_arg(args, nargs, 1), &start); if (njs_slow_path(ret != NJS_OK)) { return ret; } length = 1; if (start < 0 || start >= (int64_t) slice.string_length) { start = 0; length = 0; } slice.start = start; slice.length = length; return njs_string_slice(vm, retval, &string, &slice); } static njs_int_t njs_string_slice_prop(njs_vm_t *vm, njs_string_prop_t *string, njs_slice_prop_t *slice, njs_value_t *args, njs_uint_t nargs) { slice->string_length = njs_string_prop(string, &args[0]); return njs_string_slice_args(vm, slice, args, nargs); } static njs_int_t njs_string_slice_args(njs_vm_t *vm, njs_slice_prop_t *slice, njs_value_t *args, njs_uint_t nargs) { int64_t start, end, length; njs_int_t ret; njs_value_t *value; length = slice->string_length; value = njs_arg(args, nargs, 1); if (njs_slow_path(!njs_is_number(value))) { ret = njs_value_to_integer(vm, value, &start); if (njs_slow_path(ret != NJS_OK)) { return ret; } } else { start = njs_number_to_integer(njs_number(value)); } if (start < 0) { start += length; if (start < 0) { start = 0; } } if (start >= length) { start = 0; length = 0; } else { value = njs_arg(args, nargs, 2); if (njs_slow_path(!njs_is_number(value))) { if (njs_is_defined(value)) { ret = njs_value_to_integer(vm, value, &end); if (njs_slow_path(ret != NJS_OK)) { return ret; } } else { end = length; } } else { end = njs_number_to_integer(njs_number(value)); } if (end < 0) { end += length; } if (length >= end) { length = end - start; if (length < 0) { start = 0; length = 0; } } else { length -= start; } } slice->start = start; slice->length = length; return NJS_OK; } void njs_string_slice_string_prop(njs_string_prop_t *dst, const njs_string_prop_t *string, const njs_slice_prop_t *slice) { size_t size, n, length; const u_char *p, *start, *end; length = slice->length; start = string->start; if (string->size == slice->string_length) { /* ASCII string. */ start += slice->start; size = slice->length; } else { /* UTF-8 string. */ end = start + string->size; if (slice->start < slice->string_length) { start = njs_string_utf8_offset(start, end, slice->start); /* Evaluate size of the slice in bytes and adjust length. */ p = start; n = length; while (n != 0 && p < end) { p = njs_utf8_next(p, end); n--; } size = p - start; length -= n; } else { length = 0; size = 0; } } dst->start = (u_char *) start; dst->length = length; dst->size = size; } njs_int_t njs_string_slice(njs_vm_t *vm, njs_value_t *retval, const njs_string_prop_t *string, const njs_slice_prop_t *slice) { njs_string_prop_t prop; njs_string_slice_string_prop(&prop, string, slice); if (njs_fast_path(prop.size != 0)) { return njs_string_new(vm, retval, prop.start, prop.size, prop.length); } njs_value_assign(retval, &njs_string_empty); return NJS_OK; } static njs_int_t njs_string_prototype_char_code_at(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double num; size_t length; int64_t index; uint32_t code; njs_int_t ret; const u_char *start, *end; njs_string_prop_t string; njs_unicode_decode_t ctx; ret = njs_string_object_validate(vm, njs_argument(args, 0)); if (njs_slow_path(ret != NJS_OK)) { return ret; } length = njs_string_prop(&string, njs_argument(args, 0)); ret = njs_value_to_integer(vm, njs_arg(args, nargs, 1), &index); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_slow_path(index < 0 || index >= (int64_t) length)) { num = NAN; goto done; } if (length == string.size) { /* ASCII string. */ code = string.start[index]; } else { njs_utf8_decode_init(&ctx); end = string.start + string.size; start = njs_string_utf8_offset(string.start, end, index); code = njs_utf8_decode(&ctx, &start, end); } num = code; done: njs_set_number(retval, num); return NJS_OK; } size_t njs_decode_hex_length(const njs_str_t *src, size_t *out_size) { if (out_size != NULL) { *out_size = src->length / 2; } return 0; } void njs_decode_hex(njs_str_t *dst, const njs_str_t *src) { u_char *p; size_t len; njs_int_t c; njs_uint_t i, n; const u_char *start; n = 0; p = dst->start; start = src->start; len = src->length; for (i = 0; i < len; i++) { c = njs_char_to_hex(start[i]); if (njs_slow_path(c < 0)) { break; } n = n * 16 + c; if ((i & 1) != 0) { *p++ = (u_char) n; n = 0; } } dst->length -= (dst->start + dst->length) - p; } void njs_decode_utf8(njs_str_t *dst, const njs_str_t *src) { njs_unicode_decode_t ctx; njs_utf8_decode_init(&ctx); (void) njs_utf8_stream_encode(&ctx, src->start, src->start + src->length, dst->start, 1, 0); } size_t njs_decode_utf8_length(const njs_str_t *src, size_t *out_size) { njs_unicode_decode_t ctx; njs_utf8_decode_init(&ctx); return njs_utf8_stream_length(&ctx, src->start, src->length, 1, 0, out_size); } njs_int_t njs_string_decode_utf8(njs_vm_t *vm, njs_value_t *value, const njs_str_t *src) { size_t length; njs_str_t dst; length = njs_decode_utf8_length(src, &dst.length); dst.start = njs_string_alloc(vm, value, dst.length, length); if (njs_fast_path(dst.start != NULL)) { njs_decode_utf8(&dst, src); return NJS_OK; } return NJS_ERROR; } njs_int_t njs_string_decode_hex(njs_vm_t *vm, njs_value_t *retval, const njs_str_t *src) { size_t size, length; njs_str_t dst; length = njs_decode_hex_length(src, &size); if (njs_slow_path(size == 0)) { njs_value_assign(retval, &njs_string_empty); return NJS_OK; } dst.start = njs_string_alloc(vm, retval, size, length); if (njs_slow_path(dst.start == NULL)) { return NJS_ERROR; } dst.length = size; njs_decode_hex(&dst, src); if (njs_slow_path(dst.length != size)) { njs_string_truncate(retval, dst.length, 0); } return NJS_OK; } static size_t njs_decode_base64_length_core(const njs_str_t *src, const u_char *basis, size_t *out_size) { uint pad; size_t len; for (len = 0; len < src->length; len++) { if (basis[src->start[len]] == 77) { break; } } pad = 0; if (len % 4 != 0) { pad = 4 - (len % 4); len += pad; } len = njs_base64_decoded_length(len, pad); if (out_size != NULL) { *out_size = len; } return 0; } size_t njs_decode_base64_length(const njs_str_t *src, size_t *out_size) { return njs_decode_base64_length_core(src, njs_basis64, out_size); } size_t njs_decode_base64url_length(const njs_str_t *src, size_t *out_size) { return njs_decode_base64_length_core(src, njs_basis64url, out_size); } static void njs_decode_base64_core(njs_str_t *dst, const njs_str_t *src, const u_char *basis) { size_t len; u_char *d, *s; s = src->start; d = dst->start; len = dst->length; while (len >= 3) { *d++ = (u_char) (basis[s[0]] << 2 | basis[s[1]] >> 4); *d++ = (u_char) (basis[s[1]] << 4 | basis[s[2]] >> 2); *d++ = (u_char) (basis[s[2]] << 6 | basis[s[3]]); s += 4; len -= 3; } if (len >= 1) { *d++ = (u_char) (basis[s[0]] << 2 | basis[s[1]] >> 4); } if (len >= 2) { *d++ = (u_char) (basis[s[1]] << 4 | basis[s[2]] >> 2); } } void njs_decode_base64(njs_str_t *dst, const njs_str_t *src) { njs_decode_base64_core(dst, src, njs_basis64); } void njs_decode_base64url(njs_str_t *dst, const njs_str_t *src) { njs_decode_base64_core(dst, src, njs_basis64url); } static njs_int_t njs_string_decode_base64_core(njs_vm_t *vm, njs_value_t *retval, const njs_str_t *src, njs_bool_t url) { size_t length; const u_char *basis; njs_str_t dst; basis = (url) ? njs_basis64url : njs_basis64; length = njs_decode_base64_length_core(src, basis, &dst.length); if (njs_slow_path(dst.length == 0)) { njs_value_assign(retval, &njs_string_empty); return NJS_OK; } dst.start = njs_string_alloc(vm, retval, dst.length, length); if (njs_slow_path(dst.start == NULL)) { return NJS_ERROR; } njs_decode_base64_core(&dst, src, basis); return NJS_OK; } njs_int_t njs_string_decode_base64(njs_vm_t *vm, njs_value_t *value, const njs_str_t *src) { return njs_string_decode_base64_core(vm, value, src, 0); } njs_int_t njs_string_decode_base64url(njs_vm_t *vm, njs_value_t *value, const njs_str_t *src) { return njs_string_decode_base64_core(vm, value, src, 1); } static njs_int_t njs_string_from_char_code(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t is_point, njs_value_t *retval) { double num; u_char *p, *start, *end; ssize_t len; int32_t code; uint32_t cp; uint64_t length, size; njs_int_t ret; njs_uint_t i; njs_unicode_decode_t ctx; u_char buf[4]; size = 0; length = 0; cp = 0x00; end = buf + sizeof(buf); njs_utf16_decode_init(&ctx); for (i = 1; i < nargs; i++) { if (!njs_is_numeric(&args[i])) { ret = njs_value_to_numeric(vm, &args[i], &args[i]); if (ret != NJS_OK) { return ret; } } if (is_point) { num = njs_number(&args[i]); if (isnan(num) || isinf(num)) { goto range_error; } code = num; if (code != num || code < 0 || code > 0x10FFFF) { goto range_error; } } else { code = njs_number_to_uint16(njs_number(&args[i])); } start = buf; len = njs_utf16_encode(code, &start, end); start = buf; cp = njs_utf16_decode(&ctx, (const u_char **) &start, start + len); if (cp > NJS_UNICODE_MAX_CODEPOINT) { if (cp == NJS_UNICODE_CONTINUE) { continue; } cp = NJS_UNICODE_REPLACEMENT; } size += njs_utf8_size(cp); length++; } if (cp == NJS_UNICODE_CONTINUE) { size += njs_utf8_size(NJS_UNICODE_REPLACEMENT); length++; } p = njs_string_alloc(vm, retval, size, length); if (njs_slow_path(p == NULL)) { return NJS_ERROR; } njs_utf16_decode_init(&ctx); for (i = 1; i < nargs; i++) { if (is_point) { code = njs_number(&args[i]); } else { code = njs_number_to_uint16(njs_number(&args[i])); } start = buf; len = njs_utf16_encode(code, &start, end); start = buf; cp = njs_utf16_decode(&ctx, (const u_char **) &start, start + len); if (cp > NJS_UNICODE_MAX_CODEPOINT) { if (cp == NJS_UNICODE_CONTINUE && i + 1 != nargs) { continue; } cp = NJS_UNICODE_REPLACEMENT; } p = njs_utf8_encode(p, cp); } return NJS_OK; range_error: njs_range_error(vm, "invalid code point"); return NJS_ERROR; } static int64_t njs_string_index_of(njs_string_prop_t *string, njs_string_prop_t *search, size_t from) { size_t index, length, search_length; const u_char *p, *end; length = string->length; if (search->length == 0 && from <= length) { return from; } index = from; search_length = search->length; if (length - index >= search_length) { end = string->start + string->size; if (string->size == length) { /* ASCII string. */ end -= (search->size - 1); for (p = string->start + index; p < end; p++) { if (memcmp(p, search->start, search->size) == 0) { return index; } index++; } } else { /* UTF-8 string. */ p = (index < string->length) ? njs_string_utf8_offset(string->start, end, index) : end; end -= search->size - 1; while (p < end) { if (memcmp(p, search->start, search->size) == 0) { return index; } index++; p = njs_utf8_next(p, end); } } } return -1; } static njs_int_t njs_string_prototype_index_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t from, length; njs_int_t ret; njs_value_t *this, *search, *pos, search_lvalue, pos_lvalue; njs_string_prop_t string, s; this = njs_argument(args, 0); if (njs_slow_path(njs_is_null_or_undefined(this))) { njs_type_error(vm, "cannot convert \"%s\"to object", njs_type_string(this->type)); return NJS_ERROR; } ret = njs_value_to_string(vm, this, this); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } search = njs_lvalue_arg(&search_lvalue, args, nargs, 1); ret = njs_value_to_string(vm, search, search); if (njs_slow_path(ret != NJS_OK)) { return ret; } pos = njs_lvalue_arg(&pos_lvalue, args, nargs, 2); ret = njs_value_to_integer(vm, pos, &from); if (njs_slow_path(ret != NJS_OK)) { return ret; } length = njs_string_prop(&string, this); (void) njs_string_prop(&s, search); from = njs_min(njs_max(from, 0), length); njs_set_number(retval, njs_string_index_of(&string, &s, from)); return NJS_OK; } static njs_int_t njs_string_prototype_last_index_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double pos; int64_t index, start, length, search_length; njs_int_t ret; njs_value_t *this, *search, search_lvalue; const u_char *p, *end; njs_string_prop_t string, s; this = njs_argument(args, 0); if (njs_slow_path(njs_is_null_or_undefined(this))) { njs_type_error(vm, "cannot convert \"%s\"to object", njs_type_string(this->type)); return NJS_ERROR; } ret = njs_value_to_string(vm, this, this); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } search = njs_lvalue_arg(&search_lvalue, args, nargs, 1); ret = njs_value_to_string(vm, search, search); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_value_to_number(vm, njs_arg(args, nargs, 2), &pos); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (!isnan(pos)) { start = njs_number_to_integer(pos); } else { start = INT64_MAX; } length = njs_string_prop(&string, this); start = njs_min(njs_max(start, 0), length); search_length = njs_string_prop(&s, search); index = length - search_length; if (index > start) { index = start; } end = string.start + string.size; if (string.size == (size_t) length) { /* ASCII string. */ p = &string.start[index]; if (p > end - s.size) { p = end - s.size; } for (; p >= string.start; p--) { if (memcmp(p, s.start, s.size) == 0) { index = p - string.start; goto done; } } index = -1; } else { /* UTF-8 string. */ if (index < 0 || index == length) { index = (search_length == 0) ? index : -1; goto done; } p = njs_string_utf8_offset(string.start, end, index); for (; p >= string.start; p = njs_utf8_prev(p, string.start)) { if ((p + s.size) <= end && memcmp(p, s.start, s.size) == 0) { goto done; } index--; } index = -1; } done: njs_set_number(retval, index); return NJS_OK; } static njs_int_t njs_string_prototype_includes(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t index, length, search_length; njs_int_t ret; njs_value_t *value; const u_char *p, *end; njs_string_prop_t string, search; ret = njs_string_object_validate(vm, njs_argument(args, 0)); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_set_boolean(retval, 1); if (nargs > 1) { value = njs_argument(args, 1); if (njs_slow_path(!njs_is_string(value))) { ret = njs_value_to_string(vm, value, value); if (njs_slow_path(ret != NJS_OK)) { return ret; } } search_length = njs_string_prop(&search, value); if (nargs > 2) { value = njs_argument(args, 2); if (njs_slow_path(!njs_is_number(value))) { ret = njs_value_to_integer(vm, value, &index); if (njs_slow_path(ret != NJS_OK)) { return ret; } } else { index = njs_number_to_integer(njs_number(value)); } if (index < 0) { index = 0; } } else { index = 0; } if (search_length == 0) { return NJS_OK; } length = njs_string_prop(&string, &args[0]); if (length - index >= search_length) { end = string.start + string.size; p = njs_string_offset(&string, index); end -= search.size - 1; while (p < end) { if (memcmp(p, search.start, search.size) == 0) { return NJS_OK; } p++; } } } njs_set_boolean(retval, 0); return NJS_OK; } static njs_int_t njs_string_prototype_starts_or_ends_with(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t starts, njs_value_t *retval) { int64_t index, length, search_length; njs_int_t ret; njs_bool_t yn; njs_value_t *value, lvalue; const u_char *p, *end; njs_string_prop_t string, search; yn = 1; ret = njs_string_object_validate(vm, njs_argument(args, 0)); if (njs_slow_path(ret != NJS_OK)) { return ret; } value = njs_lvalue_arg(&lvalue, args, nargs, 1); if (njs_slow_path(!njs_is_string(value))) { ret = njs_value_to_string(vm, value, value); if (njs_slow_path(ret != NJS_OK)) { return ret; } } search_length = njs_string_prop(&search, value); value = njs_arg(args, nargs, 2); if (njs_slow_path(!njs_is_number(value))) { index = -1; if (!njs_is_undefined(value)) { ret = njs_value_to_integer(vm, value, &index); if (njs_slow_path(ret != NJS_OK)) { return ret; } } } else { index = njs_number_to_integer(njs_number(value)); } if (search_length == 0) { goto done; } if (nargs > 1) { length = njs_string_prop(&string, &args[0]); if (starts) { if (index < 0) { index = 0; } if (length - index < search_length) { goto small; } } else { if (index < 0 || index > length) { index = length; } index -= search_length; if (index < 0) { goto small; } } end = string.start + string.size; p = njs_string_offset(&string, index); if ((size_t) (end - p) >= search.size && memcmp(p, search.start, search.size) == 0) { goto done; } } small: yn = 0; done: njs_set_boolean(retval, yn); return NJS_OK; } /* * njs_string_utf8_offset() assumes that index is correct. */ const u_char * njs_string_utf8_offset(const u_char *start, const u_char *end, size_t index) { uint32_t *map; njs_uint_t skip; if (index >= NJS_STRING_MAP_STRIDE) { map = njs_string_map_start(end); if (map[0] == 0) { njs_string_utf8_offset_map_init(start, end - start); } start += map[index / NJS_STRING_MAP_STRIDE - 1]; } for (skip = index % NJS_STRING_MAP_STRIDE; skip != 0; skip--) { start = njs_utf8_next(start, end); } return start; } /* * njs_string_index() assumes that offset is correct. */ 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_STRIDE) { end = string->start + string->size; map = njs_string_map_start(end); if (map[0] == 0) { njs_string_utf8_offset_map_init(string->start, string->size); } while (index + NJS_STRING_MAP_STRIDE < string->length && *map <= offset) { last = *map++; index += NJS_STRING_MAP_STRIDE; } } p = string->start + last; start = string->start + offset; end = string->start + string->size; while (p < start) { index++; p = njs_utf8_next(p, end); } return index; } void njs_string_utf8_offset_map_init(const u_char *start, size_t size) { size_t offset; uint32_t *map; njs_uint_t n; const u_char *p, *end; end = start + size; map = njs_string_map_start(end); p = start; n = 0; offset = NJS_STRING_MAP_STRIDE; do { if (offset == 0) { map[n++] = p - start; offset = NJS_STRING_MAP_STRIDE; } /* The UTF-8 string should be valid since its length is known. */ p = njs_utf8_next(p, end); offset--; } while (p < end); } /* * The method supports only simple folding. For example, Turkish "İ" * folding "\u0130" to "\u0069\u0307" is not supported. */ static njs_int_t njs_string_prototype_to_lower_case(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { size_t size, length; u_char *p; uint32_t code; njs_int_t ret; const u_char *s, *end; njs_string_prop_t string; ret = njs_string_object_validate(vm, njs_argument(args, 0)); if (njs_slow_path(ret != NJS_OK)) { return ret; } (void) njs_string_prop(&string, njs_argument(args, 0)); if (njs_is_ascii_string(&string)) { p = njs_string_alloc(vm, retval, string.size, string.length); if (njs_slow_path(p == NULL)) { return NJS_ERROR; } s = string.start; size = string.size; while (size != 0) { *p++ = njs_lower_case(*s++); size--; } } else { /* UTF-8 string. */ s = string.start; end = s + string.size; length = string.length; size = 0; while (length != 0) { code = njs_utf8_lower_case(&s, end); size += njs_utf8_size(code); length--; } p = njs_string_alloc(vm, retval, size, string.length); if (njs_slow_path(p == NULL)) { return NJS_ERROR; } s = string.start; length = string.length; while (length != 0) { code = njs_utf8_lower_case(&s, end); p = njs_utf8_encode(p, code); length--; } } return NJS_OK; } /* * The method supports only simple folding. For example, German "ß" * folding "\u00DF" to "\u0053\u0053" is not supported. */ static njs_int_t njs_string_prototype_to_upper_case(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { size_t size, length; u_char *p; uint32_t code; njs_int_t ret; const u_char *s, *end; njs_string_prop_t string; ret = njs_string_object_validate(vm, njs_argument(args, 0)); if (njs_slow_path(ret != NJS_OK)) { return ret; } (void) njs_string_prop(&string, njs_argument(args, 0)); if (njs_is_ascii_string(&string)) { p = njs_string_alloc(vm, retval, string.size, string.length); if (njs_slow_path(p == NULL)) { return NJS_ERROR; } s = string.start; size = string.size; while (size != 0) { *p++ = njs_upper_case(*s++); size--; } } else { /* UTF-8 string. */ s = string.start; end = s + string.size; length = string.length; size = 0; while (length != 0) { code = njs_utf8_upper_case(&s, end); size += njs_utf8_size(code); length--; } p = njs_string_alloc(vm, retval, size, string.length); if (njs_slow_path(p == NULL)) { return NJS_ERROR; } s = string.start; length = string.length; while (length != 0) { code = njs_utf8_upper_case(&s, end); p = njs_utf8_encode(p, code); length--; } } return NJS_OK; } uint32_t njs_string_trim(const njs_value_t *value, njs_string_prop_t *string, unsigned mode) { uint32_t cp, trim; const u_char *p, *prev, *start, *end; njs_unicode_decode_t ctx; trim = 0; njs_string_prop(string, value); start = string->start; end = string->start + string->size; if (njs_is_ascii_string(string)) { if (mode & NJS_TRIM_START) { for ( ;; ) { if (start == end) { break; } if (njs_is_whitespace(*start)) { start++; trim++; continue; } break; } } if (mode & NJS_TRIM_END) { for ( ;; ) { if (start == end) { break; } end--; if (njs_is_whitespace(*end)) { trim++; continue; } end++; break; } } } else { /* UTF-8 string. */ if (mode & NJS_TRIM_START) { njs_utf8_decode_init(&ctx); for ( ;; ) { if (start == end) { break; } p = start; cp = njs_utf8_decode(&ctx, &start, end); if (njs_utf8_is_whitespace(cp)) { trim++; continue; } start = p; break; } } if (mode & NJS_TRIM_END) { prev = end; njs_utf8_decode_init(&ctx); for ( ;; ) { if (start == prev) { end = prev; break; } prev = njs_utf8_prev(prev, start); p = prev; cp = njs_utf8_decode(&ctx, &p, end); if (njs_utf8_is_whitespace(cp)) { trim++; continue; } end = p; break; } } } if (start == end) { string->length = 0; string->size = 0; return trim; } string->start = (u_char *) start; string->size = end - start; if (string->length != 0) { string->length -= trim; } return trim; } static njs_int_t njs_string_prototype_trim(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t mode, njs_value_t *retval) { uint32_t trim; njs_int_t ret; njs_value_t *value; njs_string_prop_t string; value = njs_argument(args, 0); ret = njs_string_object_validate(vm, value); if (njs_slow_path(ret != NJS_OK)) { return ret; } trim = njs_string_trim(value, &string, mode); if (trim == 0) { njs_value_assign(retval, value); return NJS_OK; } if (string.size == 0) { njs_value_assign(retval, &njs_string_empty); return NJS_OK; } return njs_string_new(vm, retval, string.start, string.size, string.length); } static njs_int_t njs_string_prototype_repeat(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { u_char *p; int64_t n, max; uint64_t size, length; njs_int_t ret; njs_value_t *this; njs_string_prop_t string; this = njs_argument(args, 0); if (njs_slow_path(njs_is_null_or_undefined(this))) { njs_type_error(vm, "cannot convert \"%s\"to object", njs_type_string(this->type)); return NJS_ERROR; } ret = njs_value_to_string(vm, this, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_value_to_integer(vm, njs_arg(args, nargs, 1), &n); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_slow_path(n < 0 || n == INT64_MAX)) { njs_range_error(vm, "invalid count value"); return NJS_ERROR; } (void) njs_string_prop(&string, this); if (njs_slow_path(n == 0 || string.size == 0)) { njs_value_assign(retval, &njs_string_empty); return NJS_OK; } max = NJS_STRING_MAX_LENGTH / string.size; if (njs_slow_path(n >= max)) { njs_range_error(vm, "invalid string length"); return NJS_ERROR; } size = string.size * n; length = string.length * n; p = njs_string_alloc(vm, retval, size, length); if (njs_slow_path(p == NULL)) { return NJS_ERROR; } while (n != 0) { p = memcpy(p, string.start, string.size); p += string.size; n--; } return NJS_OK; } static njs_int_t njs_string_prototype_pad(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t pad_start, njs_value_t *retval) { u_char *p, *start; size_t padding, trunc, new_size; int64_t length, new_length; uint32_t n, pad_length; njs_int_t ret; njs_value_t *value, *pad; const u_char *end; njs_string_prop_t string, pad_string; static const njs_value_t string_space = njs_string(" "); ret = njs_string_object_validate(vm, njs_argument(args, 0)); if (njs_slow_path(ret != NJS_OK)) { return ret; } length = njs_string_prop(&string, njs_argument(args, 0)); new_length = 0; if (nargs > 1) { value = njs_argument(args, 1); if (njs_slow_path(!njs_is_number(value))) { ret = njs_value_to_integer(vm, value, &new_length); if (njs_slow_path(ret != NJS_OK)) { return ret; } } else { new_length = njs_number_to_integer(njs_number(value)); } } if (new_length <= length) { njs_value_assign(retval, njs_argument(args, 0)); return NJS_OK; } if (njs_slow_path(new_length >= NJS_STRING_MAX_LENGTH)) { njs_range_error(vm, "invalid string length"); return NJS_ERROR; } padding = new_length - length; /* GCC and Clang complain about uninitialized n and trunc. */ n = 0; trunc = 0; pad = njs_arg(args, nargs, 2); if (njs_slow_path(!njs_is_string(pad))) { if (njs_is_undefined(pad)) { pad = njs_value_arg(&string_space); } else { ret = njs_value_to_string(vm, pad, pad); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } } pad_length = njs_string_prop(&pad_string, pad); if (pad_string.size == 0) { njs_value_assign(retval, njs_argument(args, 0)); return NJS_OK; } if (pad_string.size > 1) { n = padding / pad_length; trunc = padding % pad_length; if (pad_string.size != (size_t) pad_length) { /* UTF-8 string. */ end = pad_string.start + pad_string.size; end = njs_string_utf8_offset(pad_string.start, end, trunc); trunc = end - pad_string.start; padding = pad_string.size * n + trunc; } } new_size = string.size + padding; start = njs_string_alloc(vm, retval, new_size, new_length); if (njs_slow_path(start == NULL)) { return NJS_ERROR; } p = start; if (pad_start) { start += padding; } else { p += string.size; } memcpy(start, string.start, string.size); if (pad_string.size == 1) { njs_memset(p, pad_string.start[0], padding); } else { while (n != 0) { memcpy(p, pad_string.start, pad_string.size); p += pad_string.size; n--; } memcpy(p, pad_string.start, trunc); } return NJS_OK; } static njs_int_t njs_string_prototype_search(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { size_t c; njs_int_t ret, index; njs_uint_t n; njs_value_t *value; njs_string_prop_t string; njs_regexp_pattern_t *pattern; ret = njs_string_object_validate(vm, njs_argument(args, 0)); if (njs_slow_path(ret != NJS_OK)) { return ret; } index = 0; if (nargs > 1) { value = njs_argument(args, 1); switch (value->type) { case NJS_REGEXP: pattern = njs_regexp_pattern(value); break; case NJS_UNDEFINED: goto done; default: if (njs_slow_path(!njs_is_string(value))) { ret = njs_value_to_string(vm, value, value); if (njs_slow_path(ret != NJS_OK)) { return ret; } } (void) njs_string_prop(&string, value); if (string.size != 0) { pattern = njs_regexp_pattern_create(vm, string.start, string.size, 0); if (njs_slow_path(pattern == NULL)) { return NJS_ERROR; } break; } goto done; } index = -1; (void) njs_string_prop(&string, &args[0]); n = (string.length != 0); if (njs_regex_is_valid(&pattern->regex[n])) { ret = njs_regexp_match(vm, &pattern->regex[n], string.start, 0, string.size, vm->single_match_data); if (ret >= 0) { c = njs_regex_capture(vm->single_match_data, 0); index = njs_string_index(&string, c); } else if (ret == NJS_ERROR) { return NJS_ERROR; } } } done: njs_set_number(retval, index); return NJS_OK; } static njs_int_t njs_string_prototype_match(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_str_t string; njs_int_t ret; njs_value_t arguments[2]; njs_regexp_pattern_t *pattern; ret = njs_string_object_validate(vm, njs_argument(args, 0)); if (njs_slow_path(ret != NJS_OK)) { return ret; } arguments[1] = args[0]; string.start = NULL; string.length = 0; if (nargs > 1) { if (njs_is_regexp(&args[1])) { pattern = njs_regexp_pattern(&args[1]); if (pattern->global) { return njs_string_match_multiple(vm, args, pattern, retval); } /* * string.match(regexp) is the same as regexp.exec(string) * if the regexp has no global flag. */ arguments[0] = args[1]; goto match; } if (!njs_is_string(&args[1])) { if (!njs_is_undefined(&args[1])) { ret = njs_value_to_string(vm, &args[1], &args[1]); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_string_get(&args[1], &string); } } else { njs_string_get(&args[1], &string); } /* A void value. */ } ret = njs_regexp_create(vm, &arguments[0], string.start, string.length, 0); if (njs_slow_path(ret != NJS_OK)) { return ret; } match: return njs_regexp_prototype_exec(vm, arguments, 2, unused, retval); } static njs_int_t njs_string_match_multiple(njs_vm_t *vm, njs_value_t *args, njs_regexp_pattern_t *pattern, njs_value_t *retval) { size_t c0, c1; int32_t size, length; njs_int_t ret; njs_utf8_t utf8; njs_array_t *array; const u_char *p, *start, *end; njs_regexp_utf8_t type; njs_string_prop_t string; njs_set_number(&args[1].data.u.regexp->last_index, 0); njs_set_null(retval); (void) njs_string_prop(&string, &args[0]); utf8 = NJS_STRING_ASCII; type = NJS_REGEXP_BYTE; if (string.length != 0) { type = NJS_REGEXP_UTF8; if (!njs_is_ascii_string(&string)) { utf8 = NJS_STRING_UTF8; } } if (njs_regex_is_valid(&pattern->regex[type])) { array = njs_array_alloc(vm, 0, 0, NJS_ARRAY_SPARE); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } p = string.start; end = p + string.size; do { ret = njs_regexp_match(vm, &pattern->regex[type], p, 0, string.size, vm->single_match_data); if (ret < 0) { if (njs_fast_path(ret == NJS_DECLINED)) { break; } njs_internal_error(vm, "njs_regexp_match() failed"); return NJS_ERROR; } ret = njs_array_expand(vm, array, 0, 1); if (njs_slow_path(ret != NJS_OK)) { return ret; } c0 = njs_regex_capture(vm->single_match_data, 0); c1 = njs_regex_capture(vm->single_match_data, 1); start = p + c0; if (c1 == 0) { if (start < end) { p = (utf8 == NJS_STRING_UTF8) ? njs_utf8_next(start, end) : start + 1; string.size = end - p; } else { /* To exit the loop. */ p++; } size = 0; length = 0; } else { p += c1; string.size -= c1; size = c1 - c0; length = njs_string_calc_length(utf8, start, size); } ret = njs_string_new(vm, &array->start[array->length], start, size, length); if (njs_slow_path(ret != NJS_OK)) { return ret; } array->length++; } while (p <= end); njs_set_array(retval, array); } return NJS_OK; } static njs_int_t njs_string_prototype_split(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { size_t size; ssize_t len; uint32_t limit; njs_int_t ret; njs_utf8_t utf8; njs_bool_t undefined; njs_value_t *this, *separator, *value; njs_value_t separator_lvalue, limit_lvalue, splitter; njs_array_t *array; const u_char *p, *start, *next, *last, *end; njs_string_prop_t string, split; njs_value_t arguments[3]; static const njs_value_t split_key = njs_wellknown_symbol(NJS_SYMBOL_SPLIT); this = njs_argument(args, 0); if (njs_slow_path(njs_is_null_or_undefined(this))) { njs_type_error(vm, "cannot convert \"%s\"to object", njs_type_string(this->type)); return NJS_ERROR; } separator = njs_lvalue_arg(&separator_lvalue, args, nargs, 1); value = njs_lvalue_arg(&limit_lvalue, args, nargs, 2); if (!njs_is_null_or_undefined(separator)) { ret = njs_value_method(vm, separator, njs_value_arg(&split_key), &splitter); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_is_defined(&splitter)) { arguments[0] = *this; arguments[1] = *value; return njs_function_call(vm, njs_function(&splitter), separator, arguments, 2, retval); } } ret = njs_value_to_string(vm, this, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } array = njs_array_alloc(vm, 0, 0, NJS_ARRAY_SPARE); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } limit = UINT32_MAX; if (njs_is_defined(value)) { ret = njs_value_to_uint32(vm, value, &limit); if (njs_slow_path(ret != NJS_OK)) { return ret; } } undefined = njs_is_undefined(separator); ret = njs_value_to_string(vm, separator, separator); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_slow_path(limit == 0)) { goto done; } if (njs_slow_path(undefined)) { goto single; } (void) njs_string_prop(&string, this); (void) njs_string_prop(&split, separator); if (njs_slow_path(string.size == 0)) { if (split.size != 0) { goto single; } goto done; } utf8 = NJS_STRING_ASCII; if (string.length != 0) { if (!njs_is_ascii_string(&string)) { utf8 = NJS_STRING_UTF8; } } start = string.start; end = string.start + string.size; last = end - split.size; do { for (p = start; p <= last; p++) { if (memcmp(p, split.start, split.size) == 0) { goto found; } } p = end; found: next = p + split.size; /* Empty split string. */ if (p == next) { p = (utf8 == NJS_STRING_UTF8) ? njs_utf8_next(p, end) : p + 1; next = p; } size = p - start; len = njs_string_calc_length(utf8, start, size); ret = njs_array_string_add(vm, array, start, size, len); if (njs_slow_path(ret != NJS_OK)) { return ret; } start = next; limit--; } while (limit != 0 && p < end); goto done; single: value = njs_array_push(vm, array); if (njs_slow_path(value == NULL)) { return NJS_ERROR; } *value = *this; done: njs_set_array(retval, array); return NJS_OK; } njs_int_t njs_string_get_substitution(njs_vm_t *vm, njs_value_t *matched, njs_value_t *string, int64_t pos, njs_value_t *captures, int64_t ncaptures, njs_value_t *groups, njs_value_t *replacement, njs_value_t *retval) { u_char c, c2, *p, *r, *end; size_t length; int64_t tail, n; njs_str_t rep, str, cap; njs_int_t ret; njs_chb_t chain; njs_value_t name, value; njs_string_prop_t s, m; njs_string_get(replacement, &rep); p = rep.start; end = rep.start + rep.length; NJS_CHB_MP_INIT(&chain, vm); while (p < end) { r = njs_strlchr(p, end, '$'); if (r == NULL || r == &end[-1]) { if (njs_fast_path(p == rep.start)) { *retval = *replacement; return NJS_OK; } njs_chb_append(&chain, p, end - p); goto done; } njs_chb_append(&chain, p, r - p); p = r; c = r[1]; switch (c) { case '$': njs_chb_append_literal(&chain, "$"); p += 2; break; case '&': (void) njs_string_prop(&m, matched); njs_chb_append(&chain, m.start, m.size); p += 2; break; case '`': (void) njs_string_prop(&s, string); n = njs_string_offset(&s, pos) - s.start; njs_chb_append(&chain, s.start, n); p += 2; break; case '\'': length = njs_string_prop(&m, matched); (void) njs_string_prop(&s, string); tail = njs_string_offset(&s, pos + length) - s.start; njs_chb_append(&chain, &s.start[tail], njs_max((int64_t) s.size - tail, 0)); p += 2; break; case '<': r = njs_strlchr(p, end, '>'); if (groups == NULL || njs_is_undefined(groups) || r == NULL) { njs_chb_append(&chain, p, 2); p += 2; break; } p += 2; ret = njs_string_create(vm, &name, p, r - p); if (njs_slow_path(ret != NJS_OK)) { goto exception; } p = r + 1; ret = njs_value_property(vm, groups, &name, &value); if (njs_slow_path(ret == NJS_ERROR)) { goto exception; } if (njs_is_defined(&value)) { ret = njs_value_to_string(vm, &value, &value); if (njs_slow_path(ret == NJS_ERROR)) { goto exception; } njs_string_get(&value, &str); njs_chb_append_str(&chain, &str); } break; default: if (c >= '0' && c <= '9') { n = c - '0'; c2 = (&r[2] < end) ? r[2] : 0; if (c2 >= '0' && c2 <= '9' && (n * 10 + (c2 - '0')) <= ncaptures) { n = n * 10 + (c2 - '0'); } else { c2 = 0; } if (n == 0 || n > ncaptures) { njs_chb_append(&chain, p, (c2 != 0) ? 3 : 2); p += (c2 != 0) ? 3 : 2; break; } p += (c2 != 0) ? 3 : 2; if (njs_is_defined(&captures[n])) { njs_string_get(&captures[n], &cap); njs_chb_append_str(&chain, &cap); } break; } njs_chb_append_literal(&chain, "$"); p += 1; break; } } done: ret = njs_string_create_chb(vm, retval, &chain); if (njs_slow_path(ret != NJS_OK)) { ret = NJS_ERROR; goto exception; } ret = NJS_OK; exception: njs_chb_destroy(&chain); return ret; } static njs_int_t njs_string_prototype_replace(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t replaceAll, njs_value_t *retval) { u_char *r; size_t length, size, end_of_last_match; int64_t pos; njs_int_t ret; njs_str_t str; njs_chb_t chain; njs_value_t *this, *search, *replace; njs_value_t search_lvalue, replace_lvalue, replacer, value, arguments[3]; const u_char *start, *end; njs_function_t *func_replace; njs_string_prop_t string, s, ret_string; static const njs_value_t replace_key = njs_wellknown_symbol(NJS_SYMBOL_REPLACE); static const njs_value_t string_flags = njs_string("flags"); this = njs_argument(args, 0); if (njs_slow_path(njs_is_null_or_undefined(this))) { njs_type_error(vm, "cannot convert \"%s\"to object", njs_type_string(this->type)); return NJS_ERROR; } search = njs_lvalue_arg(&search_lvalue, args, nargs, 1); replace = njs_lvalue_arg(&replace_lvalue, args, nargs, 2); if (!njs_is_null_or_undefined(search)) { ret = njs_value_method(vm, search, njs_value_arg(&replace_key), &replacer); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_is_defined(&replacer)) { njs_value_assign(&arguments[0], this); njs_value_assign(&arguments[1], replace); if (replaceAll && njs_function(&replacer)->native && njs_function(&replacer)->u.native == njs_regexp_prototype_symbol_replace) { ret = njs_value_property(vm, search, njs_value_arg(&string_flags), &value); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } ret = njs_value_to_string(vm, &value, &value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_string_get(&value, &str); if (njs_strlchr(str.start, str.start + str.length, 'g') == NULL) { njs_type_error(vm, "String.prototype.replaceAll" " called with a non-global RegExp argument", njs_type_string(this->type)); return NJS_ERROR; } } return njs_function_call(vm, njs_function(&replacer), search, arguments, 2, retval); } } ret = njs_value_to_string(vm, this, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_value_to_string(vm, search, search); if (njs_slow_path(ret != NJS_OK)) { return ret; } func_replace = njs_is_function(replace) ? njs_function(replace) : NULL; if (func_replace == NULL) { ret = njs_value_to_string(vm, replace, replace); if (njs_slow_path(ret != NJS_OK)) { return ret; } } else { njs_value_assign(&arguments[0], search); njs_value_assign(&arguments[2], this); } (void) njs_string_prop(&string, this); (void) njs_string_prop(&s, search); pos = njs_string_index_of(&string, &s, 0); if (pos < 0) { njs_value_assign(retval, this); return NJS_OK; } if (!replaceAll) { if (func_replace == NULL) { ret = njs_string_get_substitution(vm, search, this, pos, NULL, 0, NULL, replace, &value); if (njs_slow_path(ret != NJS_OK)) { return ret; } } else { njs_set_number(&arguments[1], pos); ret = njs_function_call(vm, func_replace, njs_value_arg(&njs_value_undefined), arguments, 3, &value); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_value_to_string(vm, &value, &value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } end = njs_string_offset(&string, pos); (void) njs_string_prop(&ret_string, &value); size = string.size + ret_string.size - s.size; length = string.length + ret_string.length - s.length; r = njs_string_alloc(vm, retval, size, length); if (njs_slow_path(r == NULL)) { return NJS_ERROR; } r = njs_cpymem(r, string.start, end - string.start); r = njs_cpymem(r, ret_string.start, ret_string.size); memcpy(r, end + s.size, string.size - s.size - (end - string.start)); return NJS_OK; } NJS_CHB_MP_INIT(&chain, vm); start = string.start; do { if (func_replace == NULL) { ret = njs_string_get_substitution(vm, search, this, pos, NULL, 0, NULL, replace, &value); if (njs_slow_path(ret != NJS_OK)) { return ret; } } else { njs_set_number(&arguments[1], pos); ret = njs_function_call(vm, func_replace, njs_value_arg(&njs_value_undefined), arguments, 3, &value); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_value_to_string(vm, &value, &value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } end = njs_string_offset(&string, pos); (void) njs_string_prop(&ret_string, &value); njs_chb_append(&chain, start, end - start); njs_chb_append(&chain, ret_string.start, ret_string.size); start = end + s.size; end_of_last_match = pos + s.length; if (njs_slow_path(s.length == 0)) { if (end_of_last_match >= string.length) { pos = -1; } else { pos = end_of_last_match + 1; } } else { pos = njs_string_index_of(&string, &s, end_of_last_match); } } while (pos >= 0); njs_chb_append(&chain, start, string.start + string.size - start); ret = njs_string_create_chb(vm, retval, &chain); if (njs_slow_path(ret != NJS_OK)) { ret = NJS_ERROR; goto exception; } exception: njs_chb_destroy(&chain); return ret; } static njs_int_t njs_string_prototype_iterator_obj(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t kind, njs_value_t *retval) { njs_int_t ret; njs_value_t *this; this = njs_argument(args, 0); ret = njs_string_object_validate(vm, this); if (njs_slow_path(ret != NJS_OK)) { return ret; } return njs_array_iterator_create(vm, this, retval, kind); } double njs_string_to_number(const njs_value_t *value) { double num; njs_bool_t minus; const u_char *p, *start, *end; njs_string_prop_t string; const size_t infinity = njs_length("Infinity"); (void) njs_string_trim(value, &string, NJS_TRIM_START); p = string.start; end = p + string.size; if (p == end) { return 0.0; } minus = 0; if (p + 2 < end && p[0] == '0' && (p[1] == 'x' || p[1] == 'X' || p[1] == 'b' || p[1] == 'B' || p[1] == 'o' || p[1] == 'O')) { p += 2; if (p[-1] == 'x' || p[-1] == 'X') { num = njs_number_hex_parse(&p, end, 0); } else if (p[-1] == 'b' || p[-1] == 'B') { num = njs_number_bin_parse(&p, end, 0); } else { num = njs_number_oct_parse(&p, end, 0); } } else { if (*p == '+') { p++; } else if (*p == '-') { p++; minus = 1; } start = p; num = njs_number_dec_parse(&p, end, 0); if (p == start) { if (p + infinity > end || memcmp(p, "Infinity", infinity) != 0) { return NAN; } num = INFINITY; p += infinity; } } while (p < end) { if (!njs_is_whitespace(*p)) { return NAN; } p++; } return minus ? -num : num; } double njs_string_to_index(const njs_value_t *value) { size_t size, len; double num; njs_bool_t minus; const u_char *p, *start, *end; u_char buf[128]; size = value->short_string.size; if (size != NJS_STRING_LONG) { start = value->short_string.start; } else { size = value->long_string.size; start = value->long_string.data->start; } p = start; end = p + size; minus = 0; if (size > 1) { switch (p[0]) { case '0': if (size != 1) { return NAN; } /* Fall through. */ case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': break; case '-': if (size == 2 && p[1] == '0') { return -0.0; } if (size == njs_length("-Infinity") && memcmp(&p[1], "Infinity", njs_length("Infinity")) == 0) { return -INFINITY; } p++; minus = 1; break; case 'I': if (size == njs_length("Infinity") && memcmp(p, "Infinity", njs_length("Infinity")) == 0) { return INFINITY; } /* Fall through. */ default: return NAN; } } num = njs_strtod(&p, end, 0); if (p != end) { return NAN; } num = minus ? -num : num; len = njs_dtoa(num, (char *) buf); if (size != len || memcmp(start, buf, size) != 0) { return NAN; } return num; } static const njs_object_prop_t njs_string_prototype_properties[] = { NJS_DECLARE_PROP_HANDLER("__proto__", njs_primitive_prototype_get_proto, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_LENGTH(0), NJS_DECLARE_PROP_HANDLER("constructor", njs_object_prototype_create_constructor, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_NATIVE("valueOf", njs_string_prototype_value_of, 0, 0), NJS_DECLARE_PROP_NATIVE("toString", njs_string_prototype_to_string, 0, 0), NJS_DECLARE_PROP_NATIVE("concat", njs_string_prototype_concat, 1, 0), NJS_DECLARE_PROP_NATIVE("slice", njs_string_prototype_slice, 2, 0), NJS_DECLARE_PROP_NATIVE("substring", njs_string_prototype_substring, 2, 0), NJS_DECLARE_PROP_NATIVE("substr", njs_string_prototype_substr, 2, 0), NJS_DECLARE_PROP_NATIVE("charAt", njs_string_prototype_char_at, 1, 0), NJS_DECLARE_PROP_NATIVE("charCodeAt", njs_string_prototype_char_code_at, 1, 0), NJS_DECLARE_PROP_NATIVE("codePointAt", njs_string_prototype_char_code_at, 1, 0), NJS_DECLARE_PROP_NATIVE("indexOf", njs_string_prototype_index_of, 1, 0), NJS_DECLARE_PROP_NATIVE("lastIndexOf", njs_string_prototype_last_index_of, 1, 0), NJS_DECLARE_PROP_NATIVE("includes", njs_string_prototype_includes, 1, 0), NJS_DECLARE_PROP_NATIVE("startsWith", njs_string_prototype_starts_or_ends_with, 1, 1), NJS_DECLARE_PROP_NATIVE("endsWith", njs_string_prototype_starts_or_ends_with, 1, 0), NJS_DECLARE_PROP_NATIVE("toLowerCase", njs_string_prototype_to_lower_case, 0, 0), NJS_DECLARE_PROP_NATIVE("toUpperCase", njs_string_prototype_to_upper_case, 0, 0), NJS_DECLARE_PROP_NATIVE("trim", njs_string_prototype_trim, 0, NJS_TRIM_START | NJS_TRIM_END), NJS_DECLARE_PROP_NATIVE("trimStart", njs_string_prototype_trim, 0, NJS_TRIM_START), NJS_DECLARE_PROP_NATIVE("trimEnd", njs_string_prototype_trim, 0, NJS_TRIM_END), NJS_DECLARE_PROP_NATIVE("repeat", njs_string_prototype_repeat, 1, 0), NJS_DECLARE_PROP_NATIVE("padStart", njs_string_prototype_pad, 1, 1), NJS_DECLARE_PROP_NATIVE("padEnd", njs_string_prototype_pad, 1, 0), NJS_DECLARE_PROP_NATIVE("search", njs_string_prototype_search, 1, 0), NJS_DECLARE_PROP_NATIVE("match", njs_string_prototype_match, 1, 0), NJS_DECLARE_PROP_NATIVE("split", njs_string_prototype_split, 2, 0), NJS_DECLARE_PROP_NATIVE("replace", njs_string_prototype_replace, 2, 0), NJS_DECLARE_PROP_NATIVE("replaceAll", njs_string_prototype_replace, 2, 1), { .type = NJS_PROPERTY, .name = njs_wellknown_symbol(NJS_SYMBOL_ITERATOR), .u.value = njs_native_function2(njs_string_prototype_iterator_obj, 0, NJS_ENUM_VALUES), .writable = 1, .configurable = 1, }, }; const njs_object_init_t njs_string_prototype_init = { njs_string_prototype_properties, njs_nitems(njs_string_prototype_properties), }; const njs_object_prop_t njs_string_instance_properties[] = { NJS_DECLARE_PROP_HANDLER("length", njs_string_instance_length, 0, 0, 0), }; const njs_object_init_t njs_string_instance_init = { njs_string_instance_properties, njs_nitems(njs_string_instance_properties), }; njs_int_t njs_string_encode_uri(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t component, njs_value_t *retval) { u_char byte, *dst; uint64_t size; uint32_t cp, cp_low; njs_int_t ret; njs_value_t *value; const u_char *src, *end; const uint32_t *escape; njs_string_prop_t string; njs_unicode_decode_t ctx; u_char encode[4]; static const uint32_t escape_uri[] = { 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ 0x50000025, /* 0101 0000 0000 0000 0000 0000 0010 0101 */ /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ 0x78000000, /* 0111 1000 0000 0000 0000 0000 0000 0000 */ /* ~}| {zyx wvut srqp onml kjih gfed cba` */ 0xb8000001, /* 1011 1000 0000 0000 0000 0000 0000 0001 */ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ }; static const uint32_t escape_uri_component[] = { 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ 0xfc00987d, /* 1111 1100 0000 0000 1001 1000 0111 1101 */ /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ 0x78000001, /* 0111 1000 0000 0000 0000 0000 0000 0001 */ /* ~}| {zyx wvut srqp onml kjih gfed cba` */ 0xb8000001, /* 1011 1000 0000 0000 0000 0000 0000 0001 */ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ }; if (nargs < 2) { njs_value_assign(retval, &njs_string_undefined); return NJS_OK; } value = njs_argument(args, 1); ret = njs_value_to_string(vm, value, value); if (njs_slow_path(ret != NJS_OK)) { return ret; } escape = (component) ? escape_uri_component : escape_uri; njs_prefetch(escape); (void) njs_string_prop(&string, value); size = 0; src = string.start; end = src + string.size; if (njs_is_ascii_string(&string)) { while (src < end) { byte = *src++; size += njs_need_escape(escape, byte) ? 3 : 1; } } else { /* UTF-8 string. */ njs_utf8_decode_init(&ctx); while (src < end) { cp = njs_utf8_decode(&ctx, &src, end); if (cp < 0x80 && !njs_need_escape(escape, cp)) { size++; continue; } if (njs_slow_path(njs_surrogate_any(cp))) { if (src == end) { goto uri_error; } if (njs_surrogate_leading(cp)) { cp_low = njs_utf8_decode(&ctx, &src, end); if (njs_slow_path(!njs_surrogate_trailing(cp_low))) { goto uri_error; } cp = njs_surrogate_pair(cp, cp_low); size += njs_utf8_size(cp) * 3; continue; } goto uri_error; } size += njs_utf8_size(cp) * 3; } } if (size == 0) { njs_value_assign(retval, value); return NJS_OK; } dst = njs_string_alloc(vm, retval, size, size); if (njs_slow_path(dst == NULL)) { return NJS_ERROR; } src = string.start; if (njs_is_ascii_string(&string)) { (void) njs_string_encode(escape, string.size, src, dst); return NJS_OK; } /* UTF-8 string. */ njs_utf8_decode_init(&ctx); while (src < end) { cp = njs_utf8_decode(&ctx, &src, end); if (njs_slow_path(njs_surrogate_leading(cp))) { cp_low = njs_utf8_decode(&ctx, &src, end); cp = njs_surrogate_pair(cp, cp_low); } njs_utf8_encode(encode, cp); dst = njs_string_encode(escape, njs_utf8_size(cp), encode, dst); } return NJS_OK; uri_error: njs_uri_error(vm, "malformed URI"); return NJS_ERROR; } njs_inline uint32_t njs_string_decode_uri_cp(const int8_t *hex, const u_char **start, const u_char *end, njs_bool_t expect_percent) { int8_t d0, d1; uint32_t cp; const u_char *p; njs_unicode_decode_t ctx; njs_utf8_decode_init(&ctx); cp = njs_utf8_decode(&ctx, start, end); if (njs_fast_path(cp != '%')) { return expect_percent ? NJS_UNICODE_ERROR : cp; } p = *start; if (njs_slow_path((p + 1) >= end)) { return NJS_UNICODE_ERROR; } d0 = hex[*p++]; if (njs_slow_path(d0 < 0)) { return NJS_UNICODE_ERROR; } d1 = hex[*p++]; if (njs_slow_path(d1 < 0)) { return NJS_UNICODE_ERROR; } *start += 2; return (d0 << 4) + d1; } njs_inline njs_bool_t njs_reserved(const uint32_t *reserve, uint32_t byte) { return ((reserve[byte >> 5] & ((uint32_t) 1 << (byte & 0x1f))) != 0); } njs_int_t njs_string_decode_uri(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t component, njs_value_t *retval) { u_char *dst; int64_t size, length; uint32_t cp; njs_int_t ret; njs_chb_t chain; njs_uint_t i, n; njs_bool_t percent; njs_value_t *value; const u_char *src, *p, *end; const uint32_t *reserve; njs_string_prop_t string; njs_unicode_decode_t ctx; u_char encode[4]; static const uint32_t reserve_uri[] = { 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ 0xac009858, /* 1010 1100 0000 0000 1001 1000 0101 1000 */ /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ 0x00000001, /* 0000 0000 0000 0000 0000 0000 0000 0001 */ /* ~}| {zyx wvut srqp onml kjih gfed cba` */ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ }; static const uint32_t reserve_uri_component[] = { 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ /* ~}| {zyx wvut srqp onml kjih gfed cba` */ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ }; static const int8_t hex[256] njs_aligned(32) = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, }; if (nargs < 2) { njs_value_assign(retval, &njs_string_undefined); return NJS_OK; } value = njs_argument(args, 1); ret = njs_value_to_string(vm, value, value); if (njs_slow_path(ret != NJS_OK)) { return ret; } reserve = component ? reserve_uri_component : reserve_uri; njs_prefetch(reserve); njs_prefetch(&hex['0']); (void) njs_string_prop(&string, value); length = 0; src = string.start; end = string.start + string.size; NJS_CHB_MP_INIT(&chain, vm); njs_utf8_decode_init(&ctx); while (src < end) { percent = (src[0] == '%'); cp = njs_string_decode_uri_cp(hex, &src, end, 0); if (njs_slow_path(cp > NJS_UNICODE_MAX_CODEPOINT)) { goto uri_error; } if (!percent) { length += 1; dst = njs_chb_reserve(&chain, 4); if (dst != NULL) { njs_utf8_encode(dst, cp); njs_chb_written(&chain, njs_utf8_size(cp)); } continue; } if (cp < 0x80) { if (njs_reserved(reserve, cp)) { length += 3; njs_chb_append(&chain, &src[-3], 3); } else { length += 1; dst = njs_chb_reserve(&chain, 1); if (dst != NULL) { *dst = cp; njs_chb_written(&chain, 1); } } continue; } n = 1; do { n++; } while (((cp << n) & 0x80)); if (njs_slow_path(n > 4 || src + njs_length("%00") * (n - 1) > end)) { goto uri_error; } encode[0] = cp; for (i = 1; i < n; i++) { cp = njs_string_decode_uri_cp(hex, &src, end, 1); if (njs_slow_path(cp > NJS_UNICODE_MAX_CODEPOINT)) { goto uri_error; } encode[i] = cp; } p = encode; cp = njs_utf8_decode(&ctx, &p, p + n); if (njs_slow_path(cp > NJS_UNICODE_MAX_CODEPOINT)) { goto uri_error; } dst = njs_chb_reserve(&chain, 4); if (dst != NULL) { njs_utf8_encode(dst, cp); njs_chb_written(&chain, njs_utf8_size(cp)); } length += 1; } size = njs_chb_size(&chain); if (njs_slow_path(size < 0)) { njs_memory_error(vm); return NJS_ERROR; } if (size == 0) { njs_value_assign(retval, value); return NJS_OK; } dst = njs_string_alloc(vm, retval, size, length); if (njs_slow_path(dst == NULL)) { return NJS_ERROR; } njs_chb_join_to(&chain, dst); njs_chb_destroy(&chain); return NJS_OK; uri_error: njs_uri_error(vm, "malformed URI"); return NJS_ERROR; } njs_int_t njs_string_btoa(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { u_char *dst; size_t len, length; uint32_t cp0, cp1, cp2; njs_int_t ret; njs_value_t *value, lvalue; const u_char *p, *end; njs_string_prop_t string; njs_unicode_decode_t ctx; value = njs_lvalue_arg(&lvalue, args, nargs, 1); ret = njs_value_to_string(vm, value, value); if (njs_slow_path(ret != NJS_OK)) { return ret; } len = njs_string_prop(&string, value); p = string.start; end = string.start + string.size; njs_utf8_decode_init(&ctx); length = njs_base64_encoded_length(len); dst = njs_string_alloc(vm, retval, length, length); if (njs_slow_path(dst == NULL)) { return NJS_ERROR; } while (len > 2 && p < end) { cp0 = njs_utf8_decode(&ctx, &p, end); cp1 = njs_utf8_decode(&ctx, &p, end); cp2 = njs_utf8_decode(&ctx, &p, end); if (njs_slow_path(cp0 > 0xff || cp1 > 0xff || cp2 > 0xff)) { goto error; } *dst++ = njs_basis64_enc[cp0 >> 2]; *dst++ = njs_basis64_enc[((cp0 & 0x03) << 4) | (cp1 >> 4)]; *dst++ = njs_basis64_enc[((cp1 & 0x0f) << 2) | (cp2 >> 6)]; *dst++ = njs_basis64_enc[cp2 & 0x3f]; len -= 3; } if (len > 0) { cp0 = njs_utf8_decode(&ctx, &p, end); if (njs_slow_path(cp0 > 0xff)) { goto error; } *dst++ = njs_basis64_enc[cp0 >> 2]; if (len == 1) { *dst++ = njs_basis64_enc[(cp0 & 0x03) << 4]; *dst++ = '='; *dst++ = '='; } else { cp1 = njs_utf8_decode(&ctx, &p, end); if (njs_slow_path(cp1 > 0xff)) { goto error; } *dst++ = njs_basis64_enc[((cp0 & 0x03) << 4) | (cp1 >> 4)]; *dst++ = njs_basis64_enc[(cp1 & 0x0f) << 2]; *dst++ = '='; } } return NJS_OK; error: njs_type_error(vm, "invalid character (>= U+00FF)"); return NJS_ERROR; } njs_inline void njs_chb_write_byte_as_utf8(njs_chb_t *chain, u_char byte) { njs_utf8_encode(njs_chb_current(chain), byte); njs_chb_written(chain, njs_utf8_size(byte)); } njs_int_t njs_string_atob(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { size_t i, n, len, pad; u_char *dst, *tmp, *p; ssize_t size; njs_str_t str; njs_int_t ret; njs_chb_t chain; njs_value_t *value, lvalue; const u_char *b64, *s; value = njs_lvalue_arg(&lvalue, args, nargs, 1); ret = njs_value_to_string(vm, value, value); if (njs_slow_path(ret != NJS_OK)) { return ret; } /* Forgiving-base64 decode. */ b64 = njs_basis64; njs_string_get(value, &str); tmp = njs_mp_alloc(vm->mem_pool, str.length); if (tmp == NULL) { njs_memory_error(vm); return NJS_ERROR; } p = tmp; for (i = 0; i < str.length; i++) { if (njs_slow_path(str.start[i] == ' ')) { continue; } *p++ = str.start[i]; } pad = 0; str.start = tmp; str.length = p - tmp; if (str.length % 4 == 0) { if (str.length > 0) { if (str.start[str.length - 1] == '=') { pad += 1; } if (str.start[str.length - 2] == '=') { pad += 1; } } } else if (str.length % 4 == 1) { goto error; } for (i = 0; i < str.length - pad; i++) { if (njs_slow_path(b64[str.start[i]] == 77)) { goto error; } } len = str.length; if (len % 4 != 0) { pad = 4 - (len % 4); len += pad; } len = njs_base64_decoded_length(len, pad); NJS_CHB_MP_INIT(&chain, vm); dst = njs_chb_reserve(&chain, len * 2); if (njs_slow_path(dst == NULL)) { njs_memory_error(vm); return NJS_ERROR; } n = len; s = str.start; while (n >= 3) { njs_chb_write_byte_as_utf8(&chain, b64[s[0]] << 2 | b64[s[1]] >> 4); njs_chb_write_byte_as_utf8(&chain, b64[s[1]] << 4 | b64[s[2]] >> 2); njs_chb_write_byte_as_utf8(&chain, b64[s[2]] << 6 | b64[s[3]]); s += 4; n -= 3; } if (n >= 1) { njs_chb_write_byte_as_utf8(&chain, b64[s[0]] << 2 | b64[s[1]] >> 4); } if (n >= 2) { njs_chb_write_byte_as_utf8(&chain, b64[s[1]] << 4 | b64[s[2]] >> 2); } size = njs_chb_size(&chain); if (njs_slow_path(size < 0)) { njs_memory_error(vm); return NJS_ERROR; } if (size == 0) { njs_value_assign(retval, &njs_string_empty); return NJS_OK; } dst = njs_string_alloc(vm, retval, size, len); if (njs_slow_path(dst == NULL)) { return NJS_ERROR; } njs_chb_join_to(&chain, dst); njs_chb_destroy(&chain); njs_mp_free(vm->mem_pool, tmp); return NJS_OK; error: njs_type_error(vm, "the string to be decoded is not correctly encoded"); return NJS_ERROR; } const njs_object_type_init_t njs_string_type_init = { .constructor = njs_native_ctor(njs_string_constructor, 1, 0), .constructor_props = &njs_string_constructor_init, .prototype_props = &njs_string_prototype_init, .prototype_value = { .object_value = { .value = njs_string(""), .object = { .type = NJS_OBJECT_VALUE } } }, }; njs-0.8.9/src/njs_string.h000066400000000000000000000211661474132077100154560ustar00rootroot00000000000000 /* * 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 less than or equal to 14 (NJS_STRING_SHORT) * 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 /* * NJS_STRING_MAP_STRIDE 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_STRIDE 32 #define njs_string_map_offset(size) njs_align_size((size), sizeof(uint32_t)) #define njs_string_map_start(p) \ ((uint32_t *) njs_align_ptr((p), sizeof(uint32_t))) #define njs_string_map_size(length) \ (((length - 1) / NJS_STRING_MAP_STRIDE) * sizeof(uint32_t)) /* * ECMAScript strings are stored in UTF-16. nJSVM however, allows to store * any byte sequences in strings. A size of string in bytes is stored in the * size field. If 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 use UTF-8 characters * positions and lengths. Otherwise they use 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_STRIDE UTF-8 character except zero position. The * map can be initialized on demand. Unitialized map is marked with zero * value in the first map element. If string comes outside JavaScript as * byte string just to be concatenated or to match regular expressions the * offset map is not required. * * The map is not allocated: * 1) if string length is zero hence string is a byte string; * 2) if string size and length are equal so the string contains only * ASCII characters and map is not required; * 3) if string length is less than NJS_STRING_MAP_STRIDE. * * The current implementation does not support Unicode surrogate pairs. * It can be implemented later if it will be required using the following * algorithm: if offset in map points to surrogate pair then 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. */ }; typedef struct { size_t size; size_t length; u_char *start; } njs_string_prop_t; typedef struct { size_t start; size_t length; size_t string_length; } njs_slice_prop_t; typedef enum { NJS_STRING_ASCII = 0, NJS_STRING_UTF8, } njs_utf8_t; typedef enum { NJS_TRIM_START = 1, NJS_TRIM_END = 2, } njs_trim_t; u_char *njs_string_alloc(njs_vm_t *vm, njs_value_t *value, uint64_t size, uint64_t length); njs_int_t njs_string_new(njs_vm_t *vm, njs_value_t *value, const u_char *start, uint32_t size, uint32_t length); njs_int_t njs_string_create(njs_vm_t *vm, njs_value_t *value, const u_char *src, size_t size); njs_int_t njs_string_create_chb(njs_vm_t *vm, njs_value_t *value, njs_chb_t *chain); uint32_t njs_string_length(njs_value_t *string); size_t njs_string_prop(njs_string_prop_t *string, const njs_value_t *value); void njs_encode_hex(njs_str_t *dst, const njs_str_t *src); size_t njs_encode_hex_length(const njs_str_t *src, size_t *out_size); void njs_encode_base64(njs_str_t *dst, const njs_str_t *src); size_t njs_encode_base64_length(const njs_str_t *src, size_t *out_size); void njs_decode_utf8(njs_str_t *dst, const njs_str_t *src); size_t njs_decode_utf8_length(const njs_str_t *src, size_t *out_size); void njs_decode_hex(njs_str_t *dst, const njs_str_t *src); size_t njs_decode_hex_length(const njs_str_t *src, size_t *out_size); void njs_decode_base64(njs_str_t *dst, const njs_str_t *src); size_t njs_decode_base64_length(const njs_str_t *src, size_t *out_size); void njs_decode_base64url(njs_str_t *dst, const njs_str_t *src); size_t njs_decode_base64url_length(const njs_str_t *src, size_t *out_size); njs_int_t njs_string_hex(njs_vm_t *vm, njs_value_t *value, const njs_str_t *src); njs_int_t njs_string_base64(njs_vm_t *vm, njs_value_t *value, const njs_str_t *src); njs_int_t njs_string_base64url(njs_vm_t *vm, njs_value_t *value, const njs_str_t *src); njs_int_t njs_string_decode_utf8(njs_vm_t *vm, njs_value_t *value, const njs_str_t *src); njs_int_t njs_string_decode_hex(njs_vm_t *vm, njs_value_t *value, const njs_str_t *src); njs_int_t njs_string_decode_base64(njs_vm_t *vm, njs_value_t *value, const njs_str_t *src); njs_int_t njs_string_decode_base64url(njs_vm_t *vm, njs_value_t *value, const njs_str_t *src); void njs_string_truncate(njs_value_t *value, uint32_t size, uint32_t length); uint32_t njs_string_trim(const njs_value_t *value, njs_string_prop_t *string, unsigned mode); void njs_string_copy(njs_value_t *dst, njs_value_t *src); njs_int_t njs_string_cmp(const njs_value_t *val1, const njs_value_t *val2); void njs_string_slice_string_prop(njs_string_prop_t *dst, const njs_string_prop_t *string, const njs_slice_prop_t *slice); njs_int_t njs_string_slice(njs_vm_t *vm, njs_value_t *dst, const njs_string_prop_t *string, const njs_slice_prop_t *slice); const u_char *njs_string_utf8_offset(const u_char *start, const u_char *end, size_t index); uint32_t njs_string_index(njs_string_prop_t *string, uint32_t offset); void njs_string_utf8_offset_map_init(const u_char *start, size_t size); double njs_string_to_index(const njs_value_t *value); njs_int_t njs_string_encode_uri(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t component, njs_value_t *retval); njs_int_t njs_string_decode_uri(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t component, njs_value_t *retval); njs_int_t njs_string_btoa(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); njs_int_t njs_string_atob(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); njs_int_t njs_string_prototype_concat(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); njs_int_t njs_string_get_substitution(njs_vm_t *vm, njs_value_t *matched, njs_value_t *string, int64_t pos, njs_value_t *captures, int64_t ncaptures, njs_value_t *groups, njs_value_t *replacement, njs_value_t *retval); njs_inline njs_bool_t njs_is_ascii_string(njs_string_prop_t *string) { return string->length == string->size; } njs_inline uint32_t njs_string_calc_length(njs_utf8_t utf8, const u_char *start, size_t size) { ssize_t length; switch (utf8) { case NJS_STRING_ASCII: return size; case NJS_STRING_UTF8: default: length = njs_utf8_length(start, size); return length; } } njs_inline njs_bool_t njs_need_escape(const uint32_t *escape, uint32_t byte) { return ((escape[byte >> 5] & ((uint32_t) 1 << (byte & 0x1f))) != 0); } njs_inline u_char * njs_string_encode(const uint32_t *escape, size_t size, const u_char *src, u_char *dst) { uint8_t byte; static const u_char hex[16] = "0123456789ABCDEF"; do { byte = *src++; if (njs_need_escape(escape, byte)) { *dst++ = '%'; *dst++ = hex[byte >> 4]; *dst++ = hex[byte & 0xf]; } else { *dst++ = byte; } size--; } while (size != 0); return dst; } njs_inline const u_char * njs_string_offset(njs_string_prop_t *string, int64_t index) { if (njs_is_ascii_string(string)) { return string->start + index; } /* UTF-8 string. */ if (index == (int64_t) string->length) { return string->start + string->size; } return njs_string_utf8_offset(string->start, string->start + string->size, index); } extern const njs_object_init_t njs_string_instance_init; extern const njs_object_type_init_t njs_string_type_init; #endif /* _NJS_STRING_H_INCLUDED_ */ njs-0.8.9/src/njs_strtod.c000066400000000000000000000246601474132077100154640ustar00rootroot00000000000000/* * An internal strtod() implementation based upon V8 src/strtod.cc * without bignum support. * * Copyright 2012 the V8 project authors. All rights reserved. * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file. */ #include #include /* * Max double: 1.7976931348623157 x 10^308 * Min non-zero double: 4.9406564584124654 x 10^-324 * Any x >= 10^309 is interpreted as +infinity. * Any x <= 10^-324 is interpreted as 0. * Note that 2.5e-324 (despite being smaller than the min double) * will be read as non-zero (equal to the min non-zero double). */ #define NJS_DECIMAL_POWER_MAX 309 #define NJS_DECIMAL_POWER_MIN (-324) #define NJS_UINT64_MAX njs_uint64(0xFFFFFFFF, 0xFFFFFFFF) #define NJS_UINT64_DECIMAL_DIGITS_MAX 19 /* * Reads digits from the buffer and converts them to a uint64. * Reads in as many digits as fit into a uint64. * When the string starts with "1844674407370955161" no further digit is read. * Since 2^64 = 18446744073709551616 it would still be possible read another * digit if it was less or equal than 6, but this would complicate the code. */ njs_inline uint64_t njs_read_uint64(const u_char *start, size_t length, size_t *ndigits) { u_char d; uint64_t value; const u_char *p, *e; value = 0; p = start; e = p + length; while (p < e && value <= (NJS_UINT64_MAX / 10 - 1)) { d = *p++ - '0'; value = 10 * value + d; } *ndigits = p - start; return value; } /* * Reads a njs_diyfp_t from the buffer. * The returned njs_diyfp_t is not necessarily normalized. * If remaining is zero then the returned njs_diyfp_t is accurate. * Otherwise it has been rounded and has error of at most 1/2 ulp. */ static njs_diyfp_t njs_diyfp_read(const u_char *start, size_t length, int *remaining) { size_t read; uint64_t significand; significand = njs_read_uint64(start, length, &read); /* Round the significand. */ if (length != read) { if (start[read] >= '5') { significand++; } } *remaining = length - read; return njs_diyfp(significand, 0); } /* * Returns 10^exp as an exact njs_diyfp_t. * The given exp must be in the range [1; NJS_DECIMAL_EXPONENT_DIST[. */ njs_inline njs_diyfp_t njs_adjust_pow10(int exp) { switch (exp) { case 1: return njs_diyfp(njs_uint64(0xa0000000, 00000000), -60); case 2: return njs_diyfp(njs_uint64(0xc8000000, 00000000), -57); case 3: return njs_diyfp(njs_uint64(0xfa000000, 00000000), -54); case 4: return njs_diyfp(njs_uint64(0x9c400000, 00000000), -50); case 5: return njs_diyfp(njs_uint64(0xc3500000, 00000000), -47); case 6: return njs_diyfp(njs_uint64(0xf4240000, 00000000), -44); case 7: return njs_diyfp(njs_uint64(0x98968000, 00000000), -40); default: njs_unreachable(); return njs_diyfp(0, 0); } } /* * Returns the significand size for a given order of magnitude. * If v = f*2^e with 2^p-1 <= f <= 2^p then p+e is v's order of magnitude. * This function returns the number of significant binary digits v will have * once its encoded into a double. In almost all cases this is equal to * NJS_SIGNIFICAND_SIZE. The only exception are denormals. They start with * leading zeroes and their effective significand-size is hence smaller. */ njs_inline int njs_diyfp_sgnd_size(int order) { if (order >= (NJS_DBL_EXPONENT_DENORMAL + NJS_SIGNIFICAND_SIZE)) { return NJS_SIGNIFICAND_SIZE; } if (order <= NJS_DBL_EXPONENT_DENORMAL) { return 0; } return order - NJS_DBL_EXPONENT_DENORMAL; } #define NJS_DENOM_LOG 3 #define NJS_DENOM (1 << NJS_DENOM_LOG) /* * Returns either the correct double or the double that is just below * the correct double. */ static double njs_diyfp_strtod(const u_char *start, size_t length, int exp) { int magnitude, prec_digits; int remaining, dec_exp, adj_exp, orig_e, shift; int64_t error; uint64_t prec_bits, half_way; njs_diyfp_t value, pow, adj_pow, rounded; value = njs_diyfp_read(start, length, &remaining); exp += remaining; /* * Since some digits may have been dropped the value is not accurate. * If remaining is different than 0 than the error is at most .5 ulp * (unit in the last place). * Using a common denominator to avoid dealing with fractions. */ error = (remaining == 0 ? 0 : NJS_DENOM / 2); orig_e = value.exp; value = njs_diyfp_normalize(value); error <<= orig_e - value.exp; if (exp < NJS_DECIMAL_EXPONENT_MIN) { return 0.0; } pow = njs_cached_power_dec(exp, &dec_exp); if (dec_exp != exp) { adj_exp = exp - dec_exp; adj_pow = njs_adjust_pow10(exp - dec_exp); value = njs_diyfp_mul(value, adj_pow); if (NJS_UINT64_DECIMAL_DIGITS_MAX - (int) length < adj_exp) { /* * The adjustment power is exact. There is hence only * an error of 0.5. */ error += NJS_DENOM / 2; } } value = njs_diyfp_mul(value, pow); /* * The error introduced by a multiplication of a * b equals * error_a + error_b + error_a * error_b / 2^64 + 0.5 * Substituting a with 'value' and b with 'pow': * error_b = 0.5 (all cached powers have an error of less than 0.5 ulp), * error_ab = 0 or 1 / NJS_DENOM > error_a * error_b / 2^64. */ error += NJS_DENOM + (error != 0 ? 1 : 0); orig_e = value.exp; value = njs_diyfp_normalize(value); error <<= orig_e - value.exp; /* * Check whether the double's significand changes when the error is added * or substracted. */ magnitude = NJS_DIYFP_SIGNIFICAND_SIZE + value.exp; prec_digits = NJS_DIYFP_SIGNIFICAND_SIZE - njs_diyfp_sgnd_size(magnitude); if (prec_digits + NJS_DENOM_LOG >= NJS_DIYFP_SIGNIFICAND_SIZE) { /* * This can only happen for very small denormals. In this case the * half-way multiplied by the denominator exceeds the range of uint64. * Simply shift everything to the right. */ shift = prec_digits + NJS_DENOM_LOG - NJS_DIYFP_SIGNIFICAND_SIZE + 1; value = njs_diyfp_shift_right(value, shift); /* * Add 1 for the lost precision of error, and NJS_DENOM * for the lost precision of value.significand. */ error = (error >> shift) + 1 + NJS_DENOM; prec_digits -= shift; } prec_bits = value.significand & (((uint64_t) 1 << prec_digits) - 1); prec_bits *= NJS_DENOM; half_way = (uint64_t) 1 << (prec_digits - 1); half_way *= NJS_DENOM; rounded = njs_diyfp_shift_right(value, prec_digits); if (prec_bits >= half_way + error) { rounded.significand++; } return njs_diyfp2d(rounded); } static double njs_strtod_internal(const u_char *start, size_t length, int exp) { int shift; size_t left, right; const u_char *p, *e, *b; /* Trim leading zeroes. */ p = start; e = p + length; while (p < e) { if (*p != '0') { start = p; break; } p++; } left = e - p; /* Trim trailing zeroes. */ b = start; p = b + left - 1; while (p > b) { if (*p != '0') { break; } p--; } right = p - b + 1; length = right; if (length == 0) { return 0.0; } shift = (int) (left - right); if (exp >= NJS_DECIMAL_POWER_MAX - shift - (int) length + 1) { return INFINITY; } if (exp <= NJS_DECIMAL_POWER_MIN - shift - (int) length) { return 0.0; } return njs_diyfp_strtod(start, length, exp + shift); } double njs_strtod(const u_char **start, const u_char *end, njs_bool_t literal) { int exponent, exp, insignf; u_char c, *pos; njs_bool_t minus; const u_char *e, *p, *last, *_; u_char data[128]; exponent = 0; insignf = 0; pos = data; last = data + sizeof(data); p = *start; _ = p - 2; for (; p < end; p++) { /* Values less than '0' become >= 208. */ c = *p - '0'; if (njs_slow_path(c > 9)) { if (literal) { if ((p - _) == 1) { goto done; } if (*p == '_') { _ = p; continue; } } break; } if (pos < last) { *pos++ = *p; } else { insignf++; } } /* Do not emit a '.', but adjust the exponent instead. */ if (p < end && *p == '.') { _ = p; for (p++; p < end; p++) { /* Values less than '0' become >= 208. */ c = *p - '0'; if (njs_slow_path(c > 9)) { if (literal && *p == '_' && (p - _) > 1) { _ = p; continue; } break; } if (pos < last) { *pos++ = *p; exponent--; } else { /* Ignore insignificant digits in the fractional part. */ } } } if (pos == data) { return NAN; } e = p + 1; if (e < end && (*p == 'e' || *p == 'E')) { minus = 0; if (e + 1 < end) { if (*e == '-') { e++; minus = 1; } else if (*e == '+') { e++; } } /* Values less than '0' become >= 208. */ c = *e - '0'; if (njs_fast_path(c <= 9)) { exp = c; for (p = e + 1; p < end; p++) { /* Values less than '0' become >= 208. */ c = *p - '0'; if (njs_slow_path(c > 9)) { if (literal && *p == '_' && (p - _) > 1) { _ = p; continue; } break; } if (exp < (INT_MAX - 9) / 10) { exp = exp * 10 + c; } } exponent += minus ? -exp : exp; } else if (literal && *e == '_') { p = e; } } done: *start = p; exponent += insignf; return njs_strtod_internal(data, pos - data, exponent); } njs-0.8.9/src/njs_strtod.h000066400000000000000000000004161474132077100154620ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) Nginx, Inc. */ #ifndef _NJS_STRTOD_H_INCLUDED_ #define _NJS_STRTOD_H_INCLUDED_ NJS_EXPORT double njs_strtod(const u_char **start, const u_char *end, njs_bool_t literal); #endif /* _NJS_STRTOD_H_INCLUDED_ */ njs-0.8.9/src/njs_symbol.c000066400000000000000000000263241474132077100154510ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include static const njs_value_t njs_symbol_async_iterator_name = njs_long_string("Symbol.asyncIterator"); static const njs_value_t njs_symbol_has_instance_name = njs_long_string("Symbol.hasInstance"); static const njs_value_t njs_symbol_is_concat_spreadable_name = njs_long_string("Symbol.isConcatSpreadable"); static const njs_value_t njs_symbol_iterator_name = njs_long_string("Symbol.iterator"); static const njs_value_t njs_symbol_match_name = njs_string("Symbol.match"); static const njs_value_t njs_symbol_match_all_name = njs_long_string("Symbol.matchAll"); static const njs_value_t njs_symbol_replace_name = njs_string("Symbol.replace"); static const njs_value_t njs_symbol_search_name = njs_string("Symbol.search"); static const njs_value_t njs_symbol_species_name = njs_string("Symbol.species"); static const njs_value_t njs_symbol_split_name = njs_string("Symbol.split"); static const njs_value_t njs_symbol_to_primitive_name = njs_long_string("Symbol.toPrimitive"); static const njs_value_t njs_symbol_to_string_tag_name = njs_long_string("Symbol.toStringTag"); static const njs_value_t njs_symbol_unscopables_name = njs_long_string("Symbol.unscopables"); static const njs_value_t *njs_symbol_names[NJS_SYMBOL_KNOWN_MAX] = { &njs_string_invalid, &njs_symbol_async_iterator_name, &njs_symbol_has_instance_name, &njs_symbol_is_concat_spreadable_name, &njs_symbol_iterator_name, &njs_symbol_match_name, &njs_symbol_match_all_name, &njs_symbol_replace_name, &njs_symbol_search_name, &njs_symbol_species_name, &njs_symbol_split_name, &njs_symbol_to_primitive_name, &njs_symbol_to_string_tag_name, &njs_symbol_unscopables_name, }; const njs_value_t * njs_symbol_description(const njs_value_t *value) { uint32_t key; key = njs_symbol_key(value); if (key < NJS_SYMBOL_KNOWN_MAX) { return njs_symbol_names[key]; } return value->data.u.value != NULL ? value->data.u.value : &njs_value_undefined; } njs_int_t njs_symbol_descriptive_string(njs_vm_t *vm, njs_value_t *dst, const njs_value_t *value) { u_char *start; const njs_value_t *description; njs_string_prop_t string; description = njs_symbol_description(value); if (njs_is_undefined(description)) { description = &njs_string_empty; } (void) njs_string_prop(&string, description); string.length += njs_length("Symbol()"); start = njs_string_alloc(vm, dst, string.size + 8, string.length); if (njs_slow_path(start == NULL)) { return NJS_ERROR; } start = njs_cpymem(start, "Symbol(", 7); start = njs_cpymem(start, string.start, string.size); *start = ')'; return NJS_OK; } static njs_int_t njs_symbol_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { uint64_t key; njs_int_t ret; njs_value_t *value, *name; if (njs_slow_path(vm->top_frame->ctor)) { njs_type_error(vm, "Symbol is not a constructor"); return NJS_ERROR; } value = njs_arg(args, nargs, 1); if (njs_is_defined(value)) { if (njs_slow_path(!njs_is_string(value))) { ret = njs_value_to_string(vm, value, value); if (njs_slow_path(ret != NJS_OK)) { return ret; } } } key = ++vm->symbol_generator; if (njs_slow_path(key >= UINT32_MAX)) { njs_internal_error(vm, "Symbol generator overflow"); return NJS_ERROR; } name = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t)); if (njs_slow_path(name == NULL)) { njs_memory_error(vm); return NJS_ERROR; } njs_value_assign(name, value); njs_set_symbol(retval, key, name); return NJS_OK; } static njs_int_t njs_symbol_for(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { uint64_t key; njs_int_t ret; njs_value_t *value, lvalue; njs_rbtree_node_t *rb_node; njs_rb_symbol_node_t *node; value = njs_lvalue_arg(&lvalue, args, nargs, 1); if (njs_slow_path(!njs_is_string(value))) { ret = njs_value_to_string(vm, value, value); if (njs_slow_path(ret != NJS_OK)) { return ret; } } rb_node = njs_rbtree_min(&vm->global_symbols); while (njs_rbtree_is_there_successor(&vm->global_symbols, rb_node)) { node = (njs_rb_symbol_node_t *) rb_node; if (njs_is_string(&node->name) && njs_string_cmp(value, &node->name) == 0) { njs_set_symbol(retval, node->key, &node->name); return NJS_OK; } rb_node = njs_rbtree_node_successor(&vm->global_symbols, rb_node); } key = ++vm->symbol_generator; if (njs_slow_path(key >= UINT32_MAX)) { njs_internal_error(vm, "Symbol generator overflow"); return NJS_ERROR; } node = njs_mp_alloc(vm->mem_pool, sizeof(njs_rb_symbol_node_t)); if (njs_slow_path(node == NULL)) { njs_memory_error(vm); return NJS_ERROR; } node->key = key; njs_value_assign(&node->name, value); njs_rbtree_insert(&vm->global_symbols, &node->node); njs_set_symbol(retval, key, &node->name); return NJS_OK; } static njs_int_t njs_symbol_key_for(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_value_t *value; njs_rb_symbol_node_t query, *node; value = njs_arg(args, nargs, 1); if (njs_slow_path(!njs_is_symbol(value))) { njs_type_error(vm, "is not a symbol"); return NJS_ERROR; } query.key = njs_symbol_key(value); node = (njs_rb_symbol_node_t *) njs_rbtree_find(&vm->global_symbols, &query.node); njs_value_assign(retval, node != NULL ? &node->name : &njs_value_undefined); return NJS_OK; } static const njs_object_prop_t njs_symbol_constructor_properties[] = { NJS_DECLARE_PROP_LENGTH(0), NJS_DECLARE_PROP_NAME("Symbol"), NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), NJS_DECLARE_PROP_NATIVE("for", njs_symbol_for, 1, 0), NJS_DECLARE_PROP_NATIVE("keyFor", njs_symbol_key_for, 1, 0), NJS_DECLARE_PROP_VALUE("asyncIterator", njs_wellknown_symbol(NJS_SYMBOL_ASYNC_ITERATOR), 0), NJS_DECLARE_PROP_VALUE("hasInstance", njs_wellknown_symbol(NJS_SYMBOL_HAS_INSTANCE), 0), NJS_DECLARE_PROP_LVALUE("isConcatSpreadable", njs_wellknown_symbol(NJS_SYMBOL_IS_CONCAT_SPREADABLE), 0), NJS_DECLARE_PROP_VALUE("iterator", njs_wellknown_symbol(NJS_SYMBOL_ITERATOR), 0), NJS_DECLARE_PROP_VALUE("match", njs_wellknown_symbol(NJS_SYMBOL_MATCH), 0), NJS_DECLARE_PROP_VALUE("matchAll", njs_wellknown_symbol(NJS_SYMBOL_MATCH_ALL), 0), NJS_DECLARE_PROP_VALUE("replace", njs_wellknown_symbol(NJS_SYMBOL_REPLACE), 0), NJS_DECLARE_PROP_VALUE("search", njs_wellknown_symbol(NJS_SYMBOL_SEARCH), 0), NJS_DECLARE_PROP_VALUE("species", njs_wellknown_symbol(NJS_SYMBOL_SPECIES), 0), NJS_DECLARE_PROP_VALUE("split", njs_wellknown_symbol(NJS_SYMBOL_SPLIT), 0), NJS_DECLARE_PROP_VALUE("toPrimitive", njs_wellknown_symbol(NJS_SYMBOL_TO_PRIMITIVE), 0), NJS_DECLARE_PROP_VALUE("toStringTag", njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG), 0), NJS_DECLARE_PROP_VALUE("unscopables", njs_wellknown_symbol(NJS_SYMBOL_UNSCOPABLES), 0), }; const njs_object_init_t njs_symbol_constructor_init = { njs_symbol_constructor_properties, njs_nitems(njs_symbol_constructor_properties), }; static njs_int_t njs_symbol_prototype_value_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_value_t *value; value = &args[0]; if (value->type != NJS_SYMBOL) { if (njs_is_object_symbol(value)) { value = njs_object_value(value); } else { njs_type_error(vm, "unexpected value type:%s", njs_type_string(value->type)); return NJS_ERROR; } } njs_value_assign(retval, value); return NJS_OK; } static njs_int_t njs_symbol_prototype_to_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; ret = njs_symbol_prototype_value_of(vm, args, nargs, unused, retval); if (njs_slow_path(ret != NJS_OK)) { return ret; } return njs_symbol_descriptive_string(vm, retval, retval); } static njs_int_t njs_symbol_prototype_description(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; ret = njs_symbol_prototype_value_of(vm, args, nargs, unused, retval); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_value_assign(retval, njs_symbol_description(retval)); return NJS_OK; } static const njs_object_prop_t njs_symbol_prototype_properties[] = { { .type = NJS_PROPERTY, .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG), .u.value = njs_string("Symbol"), .configurable = 1, }, NJS_DECLARE_PROP_HANDLER("__proto__", njs_primitive_prototype_get_proto, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_HANDLER("constructor", njs_object_prototype_create_constructor, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_NATIVE("valueOf", njs_symbol_prototype_value_of, 0, 0), NJS_DECLARE_PROP_NATIVE("toString", njs_symbol_prototype_to_string, 0, 0), NJS_DECLARE_PROP_GETTER("description", njs_symbol_prototype_description, 0), }; const njs_object_init_t njs_symbol_prototype_init = { njs_symbol_prototype_properties, njs_nitems(njs_symbol_prototype_properties), }; const njs_object_type_init_t njs_symbol_type_init = { .constructor = njs_native_ctor(njs_symbol_constructor, 0, 0), .constructor_props = &njs_symbol_constructor_init, .prototype_props = &njs_symbol_prototype_init, .prototype_value = { .object = { .type = NJS_OBJECT } }, }; intptr_t njs_symbol_rbtree_cmp(njs_rbtree_node_t *node1, njs_rbtree_node_t *node2) { njs_rb_symbol_node_t *item1, *item2; item1 = (njs_rb_symbol_node_t *) node1; item2 = (njs_rb_symbol_node_t *) node2; if (item1->key < item2->key) { return -1; } if (item1->key == item2->key) { return 0; } return 1; } njs-0.8.9/src/njs_symbol.h000066400000000000000000000011521474132077100154460ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_SYMBOL_H_INCLUDED_ #define _NJS_SYMBOL_H_INCLUDED_ typedef struct { NJS_RBTREE_NODE (node); uint32_t key; njs_value_t name; } njs_rb_symbol_node_t; const njs_value_t *njs_symbol_description(const njs_value_t *value); njs_int_t njs_symbol_descriptive_string(njs_vm_t *vm, njs_value_t *dst, const njs_value_t *value); intptr_t njs_symbol_rbtree_cmp(njs_rbtree_node_t *node1, njs_rbtree_node_t *node2); extern const njs_object_type_init_t njs_symbol_type_init; #endif /* _NJS_SYMBOL_H_INCLUDED_ */ njs-0.8.9/src/njs_trace.c000066400000000000000000000016011474132077100152310ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include static u_char * njs_last_handler(njs_trace_t *trace, njs_trace_data_t *td, u_char *start) { u_char *p; ssize_t size; size = td->end - start; p = njs_vsprintf(start, start + size, td->fmt, td->args); if (p - start < size) { start = p; } return start; } void njs_trace_handler(njs_trace_t *trace, uint32_t level, const char *fmt, ...) { u_char *start; njs_trace_t last; njs_trace_data_t td; td.level = level; td.fmt = fmt; va_start(td.args, fmt); start = alloca(trace->size); td.end = start + trace->size; last.handler = njs_last_handler; trace->next = &last; while (trace->prev != NULL) { trace = trace->prev; } (void) trace->handler(trace, &td, start); va_end(td.args); } njs-0.8.9/src/njs_trace.h000066400000000000000000000044001474132077100152360ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_TRACE_H_INCLUDED_ #define _NJS_TRACE_H_INCLUDED_ typedef enum { NJS_LEVEL_CRIT = 0, NJS_LEVEL_ERROR, NJS_LEVEL_WARN, NJS_LEVEL_INFO, NJS_LEVEL_TRACE, } njs_trace_level_t; typedef struct { uint32_t level; u_char *end; const char *fmt; va_list args; } njs_trace_data_t; typedef struct njs_trace_s njs_trace_t; typedef u_char *(*njs_trace_handler_t)(njs_trace_t *trace, njs_trace_data_t *td, u_char *start); struct njs_trace_s { uint32_t level; uint32_t size; njs_trace_handler_t handler; void *data; njs_trace_t *prev; njs_trace_t *next; }; #define njs_alert(_trace, _level, ...) \ do { \ njs_trace_t *_trace_ = _trace; \ uint32_t _level_ = _level; \ \ if (njs_slow_path(_trace_->level >= _level_)) { \ njs_trace_handler(_trace_, _level_, __VA_ARGS__); \ } \ } while (0) #define njs_trace(_trace, ...) \ do { \ njs_trace_t *_trace_ = _trace; \ \ if (njs_slow_path(_trace_->level == NJS_LEVEL_TRACE)) { \ njs_trace_handler(_trace_, NJS_LEVEL_TRACE, __VA_ARGS__); \ } \ } while (0) #define njs_thread_log_alert(...) #define njs_thread_log_error(...) #define njs_log_error(...) #define njs_thread_log_debug(...) NJS_EXPORT void njs_trace_handler(njs_trace_t *trace, uint32_t level, const char *fmt, ...); #endif /* _NJS_TRACE_H_INCLUDED_ */ njs-0.8.9/src/njs_typed_array.c000066400000000000000000002461231474132077100164700ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #include typedef enum { NJS_ARRAY_EVERY = 0, NJS_ARRAY_FOR_EACH, NJS_ARRAY_SOME, NJS_ARRAY_FIND, NJS_ARRAY_FIND_INDEX, NJS_ARRAY_FILTER, NJS_ARRAY_MAP, } njs_array_iterator_fun_t; static void njs_typed_array_prop_set(njs_vm_t *vm, njs_typed_array_t *array, uint32_t index, double v); njs_typed_array_t * njs_typed_array_alloc(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_bool_t zeroing, njs_object_type_t type) { double num; int64_t i, length; uint32_t element_size; uint64_t size, offset; njs_int_t ret; njs_value_t *value, prop; njs_typed_array_t *array, *src_tarray; njs_array_buffer_t *buffer; size = 0; length = 0; offset = 0; buffer = NULL; src_tarray = NULL; element_size = njs_typed_array_element_size(type); value = njs_arg(args, nargs, 0); if (njs_is_array_buffer(value)) { buffer = njs_array_buffer(value); ret = njs_value_to_index(vm, njs_arg(args, nargs, 1), &offset); if (njs_slow_path(ret != NJS_OK)) { return NULL; } if (njs_slow_path((offset % element_size) != 0)) { njs_range_error(vm, "start offset must be multiple of %uD", element_size); return NULL; } if (njs_is_defined(njs_arg(args, nargs, 2))) { ret = njs_value_to_index(vm, njs_argument(args, 2), &size); if (njs_slow_path(ret != NJS_OK)) { return NULL; } } if (njs_slow_path(njs_is_detached_buffer(buffer))) { njs_type_error(vm, "detached buffer"); return NULL; } if (njs_is_defined(njs_arg(args, nargs, 2))) { ret = njs_value_to_index(vm, njs_argument(args, 2), &size); if (njs_slow_path(ret != NJS_OK)) { return NULL; } size *= element_size; if (njs_slow_path((offset + size) > buffer->size)) { njs_range_error(vm, "Invalid typed array length: %uL", size); return NULL; } } else { if (njs_slow_path((buffer->size % element_size) != 0)) { njs_range_error(vm, "byteLength of buffer must be " "multiple of %uD", element_size); return NULL; } if (offset > buffer->size) { njs_range_error(vm, "byteOffset %uL is outside the bound of " "the buffer", offset); return NULL; } size = buffer->size - offset; } } else if (njs_is_typed_array(value)) { src_tarray = njs_typed_array(value); if (njs_slow_path(njs_is_detached_buffer(src_tarray->buffer))) { njs_type_error(vm, "detached buffer"); return NULL; } size = (uint64_t) njs_typed_array_length(src_tarray) * element_size; } else if (njs_is_object(value)) { ret = njs_object_length(vm, value, &length); if (njs_slow_path(ret == NJS_ERROR)) { return NULL; } size = length * element_size; } else { ret = njs_value_to_index(vm, value, &size); if (njs_slow_path(ret != NJS_OK)) { return NULL; } size *= element_size; } if (buffer == NULL) { buffer = njs_array_buffer_alloc(vm, size, zeroing); if (njs_slow_path(buffer == NULL)) { return NULL; } } array = njs_mp_zalloc(vm->mem_pool, sizeof(njs_typed_array_t)); if (njs_slow_path(array == NULL)) { goto memory_error; } array->buffer = buffer; array->offset = offset / element_size; array->byte_length = size; array->type = type; if (src_tarray != NULL) { if (type != src_tarray->type) { length = njs_typed_array_length(src_tarray); for (i = 0; i < length; i++) { njs_typed_array_prop_set(vm, array, i, njs_typed_array_prop(src_tarray, i)); } } else { memcpy(&buffer->u.u8[0], &src_tarray->buffer->u.u8[0], size); } } else if (!njs_is_array_buffer(value) && njs_is_object(value)) { for (i = 0; i < length; i++) { ret = njs_value_property_i64(vm, value, i, &prop); if (njs_slow_path(ret == NJS_ERROR)) { return NULL; } num = NAN; if (ret == NJS_OK) { ret = njs_value_to_number(vm, &prop, &num); if (njs_slow_path(ret == NJS_ERROR)) { return NULL; } } njs_typed_array_prop_set(vm, array, i, num); } } njs_lvlhsh_init(&array->object.hash); njs_lvlhsh_init(&array->object.shared_hash); array->object.__proto__ = njs_vm_proto(vm, type); array->object.type = NJS_TYPED_ARRAY; array->object.extensible = 1; array->object.fast_array = 1; return array; memory_error: njs_memory_error(vm); return NULL; } static njs_int_t njs_typed_array_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t magic, njs_value_t *retval) { njs_typed_array_t *array; if (!vm->top_frame->ctor) { njs_type_error(vm, "Constructor of TypedArray requires 'new'"); return NJS_ERROR; } array = njs_typed_array_alloc(vm, &args[1], nargs - 1, 1, magic); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } njs_set_typed_array(retval, array); return NJS_OK; } static njs_int_t njs_typed_array_create(njs_vm_t *vm, njs_value_t *constructor, njs_value_t *args, njs_uint_t nargs, njs_value_t *retval) { njs_int_t ret; njs_typed_array_t *array; ret = njs_value_construct(vm, constructor, args, nargs, retval); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } if (njs_slow_path(!njs_is_typed_array(retval))) { njs_type_error(vm, "Derived TypedArray constructor " "returned not a typed array"); return NJS_ERROR; } array = njs_typed_array(retval); if (njs_slow_path(njs_is_detached_buffer(array->buffer))) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } if (njs_slow_path(nargs == 1 && njs_is_number(&args[0]) && njs_typed_array_length(array) < njs_number(&args[0]))) { njs_type_error(vm, "Derived TypedArray constructor " "returned too short array"); return NJS_ERROR; } return NJS_OK; } static njs_int_t njs_typed_array_species_create(njs_vm_t *vm, njs_value_t *exemplar, njs_value_t *args, njs_uint_t nargs, njs_value_t *retval) { njs_int_t ret; njs_value_t constructor; njs_typed_array_t *array; array = njs_typed_array(exemplar); njs_set_function(&constructor, &njs_vm_ctor(vm, array->type)); ret = njs_value_species_constructor(vm, exemplar, &constructor, &constructor); if (njs_slow_path(ret != NJS_OK)) { return ret; } return njs_typed_array_create(vm, &constructor, args, nargs, retval); } static njs_int_t njs_typed_array_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double num; uint32_t length, i; njs_int_t ret; njs_value_t *this; njs_value_t argument; njs_typed_array_t *array; this = njs_argument(args, 0); if (njs_slow_path(!njs_is_constructor(this))) { njs_type_error(vm, "%s is not a constructor", njs_type_string(this->type)); return NJS_ERROR; } length = nargs - 1; njs_set_number(&argument, length); ret = njs_typed_array_create(vm, this, &argument, 1, retval); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } array = njs_typed_array(retval); for (i = 0; i < length; i++) { ret = njs_value_to_number(vm, njs_argument(args, i + 1), &num); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_typed_array_prop_set(vm, array, i, num); } njs_set_typed_array(retval, array); return NJS_OK; } static njs_int_t njs_typed_array_from(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double num; int64_t length, i; njs_int_t ret; njs_value_t *this, *source, *mapfn; njs_value_t arguments[3], value; njs_function_t *function; njs_typed_array_t *array; this = njs_argument(args, 0); if (njs_slow_path(!njs_is_constructor(this))) { njs_type_error(vm, "%s is not a constructor", njs_type_string(this->type)); return NJS_ERROR; } mapfn = njs_arg(args, nargs, 2); if (njs_slow_path(!njs_is_function_or_undefined(mapfn))) { njs_type_error(vm, "\"mapfn\" argument is not callable"); return NJS_ERROR; } function = NULL; if (njs_is_function(mapfn)) { function = njs_function(mapfn); } source = njs_arg(args, nargs, 1); ret = njs_value_to_object(vm, source); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_object_length(vm, source, &length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } njs_set_number(&arguments[0], length); ret = njs_typed_array_create(vm, this, arguments, 1, retval); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } array = njs_typed_array(retval); arguments[0] = *njs_arg(args, nargs, 3); for (i = 0; i < length; i++) { ret = njs_value_property_i64(vm, source, i, &value); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } if (function != NULL) { njs_value_assign(&arguments[1], &value); njs_set_number(&arguments[2], i); ret = njs_function_apply(vm, function, arguments, 3, &value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } ret = njs_value_to_number(vm, &value, &num); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_typed_array_prop_set(vm, array, i, num); } njs_set_typed_array(retval, array); return NJS_OK; } static njs_int_t njs_typed_array_get_this(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_value_assign(retval, njs_argument(args, 0)); return NJS_OK; } njs_array_buffer_t * njs_typed_array_writable(njs_vm_t *vm, njs_typed_array_t *array) { njs_int_t ret; njs_array_buffer_t *buffer; buffer = array->buffer; if (njs_slow_path(njs_is_detached_buffer(buffer))) { njs_type_error(vm, "detached buffer"); return NULL; } ret = njs_array_buffer_writable(vm, buffer); if (njs_slow_path(ret != NJS_OK)) { return NULL; } return buffer; } static const njs_value_t njs_typed_array_uint8_tag = njs_string("Uint8Array"); static const njs_value_t njs_typed_array_uint8_clamped_tag = njs_long_string("Uint8ClampedArray"); static const njs_value_t njs_typed_array_int8_tag = njs_string("Int8Array"); static const njs_value_t njs_typed_array_uint16_tag = njs_string("Uint16Array"); static const njs_value_t njs_typed_array_int16_tag = njs_string("Int16Array"); static const njs_value_t njs_typed_array_uint32_tag = njs_string("Uint32Array"); static const njs_value_t njs_typed_array_int32_tag = njs_string("Int32Array"); static const njs_value_t njs_typed_array_float32_tag = njs_string("Float32Array"); static const njs_value_t njs_typed_array_float64_tag = njs_string("Float64Array"); static njs_int_t njs_typed_array_get_string_tag(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_value_t *this; static const njs_value_t *tags[NJS_OBJ_TYPE_TYPED_ARRAY_SIZE] = { &njs_typed_array_uint8_tag, &njs_typed_array_uint8_clamped_tag, &njs_typed_array_int8_tag, &njs_typed_array_uint16_tag, &njs_typed_array_int16_tag, &njs_typed_array_uint32_tag, &njs_typed_array_int32_tag, &njs_typed_array_float32_tag, &njs_typed_array_float64_tag, }; this = njs_argument(args, 0); if (!njs_is_typed_array(this)) { njs_set_undefined(retval); return NJS_OK; } njs_value_assign(retval, tags[njs_typed_array_index(njs_typed_array(this)->type)]); return NJS_OK; } static njs_int_t njs_typed_array_prototype_length(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { uint32_t length; njs_value_t *this; njs_typed_array_t *array; this = njs_argument(args, 0); if (!njs_is_typed_array(this)) { njs_type_error(vm, "Method TypedArray.prototype.length called " "on incompatible receiver"); return NJS_ERROR; } array = njs_typed_array(this); length = njs_typed_array_length(array); if (njs_slow_path(njs_is_detached_buffer(array->buffer))) { length = 0; } njs_set_number(retval, length); return NJS_OK; } static njs_int_t njs_typed_array_prototype_buffer(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_value_t *this; njs_typed_array_t *array; this = njs_argument(args, 0); if (!njs_is_typed_array(this) && !njs_is_data_view(this)) { njs_type_error(vm, "Method TypedArray.prototype.buffer called " "on incompatible receiver"); return NJS_ERROR; } array = njs_typed_array(this); njs_set_array_buffer(retval, njs_typed_array_buffer(array)); return NJS_OK; } static njs_int_t njs_typed_array_prototype_byte_length(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { size_t byte_length; njs_value_t *this; njs_typed_array_t *array; this = njs_argument(args, 0); if (!njs_is_typed_array(this) && !njs_is_data_view(this)) { njs_type_error(vm, "Method TypedArray.prototype.byteLength called " "on incompatible receiver"); return NJS_ERROR; } array = njs_typed_array(this); byte_length = array->byte_length; if (njs_slow_path(njs_is_detached_buffer(array->buffer))) { if (njs_is_data_view(this)) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } byte_length = 0; } njs_set_number(retval, byte_length); return NJS_OK; } static njs_int_t njs_typed_array_prototype_byte_offset(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { size_t byte_offset; njs_value_t *this; njs_typed_array_t *array; this = njs_argument(args, 0); if (!njs_is_typed_array(this) && !njs_is_data_view(this)) { njs_type_error(vm, "Method TypedArray.prototype.byteOffset called " "on incompatible receiver"); return NJS_ERROR; } array = njs_typed_array(this); byte_offset = njs_typed_array_offset(array); if (njs_slow_path(njs_is_detached_buffer(array->buffer))) { if (njs_is_data_view(this)) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } byte_offset = 0; } njs_set_number(retval, byte_offset); return NJS_OK; } static void njs_typed_array_prop_set(njs_vm_t *vm, njs_typed_array_t *array, uint32_t index, double v) { int8_t i8; int16_t i16; int32_t i32; njs_array_buffer_t *buffer; buffer = array->buffer; index += array->offset; njs_assert(!buffer->object.shared); switch (array->type) { case NJS_OBJ_TYPE_UINT8_CLAMPED_ARRAY: if (isnan(v) || v < 0) { v = 0; } else if (v > 255) { v = 255; } buffer->u.u8[index] = lrint(v); break; case NJS_OBJ_TYPE_UINT8_ARRAY: case NJS_OBJ_TYPE_INT8_ARRAY: i8 = njs_number_to_int32(v); buffer->u.u8[index] = i8; break; case NJS_OBJ_TYPE_UINT16_ARRAY: case NJS_OBJ_TYPE_INT16_ARRAY: i16 = njs_number_to_int32(v); buffer->u.u16[index] = i16; break; case NJS_OBJ_TYPE_UINT32_ARRAY: case NJS_OBJ_TYPE_INT32_ARRAY: i32 = njs_number_to_int32(v); buffer->u.u32[index] = i32; break; case NJS_OBJ_TYPE_FLOAT32_ARRAY: buffer->u.f32[index] = v; break; default: /* NJS_OBJ_TYPE_FLOAT64_ARRAY. */ buffer->u.f64[index] = v; } } njs_int_t njs_typed_array_set_value(njs_vm_t *vm, njs_typed_array_t *array, uint32_t index, njs_value_t *setval) { double num; njs_int_t ret; njs_array_buffer_t *buffer; ret = njs_value_to_number(vm, setval, &num); if (njs_slow_path(ret != NJS_OK)) { return ret; } buffer = njs_typed_array_writable(vm, array); if (njs_slow_path(buffer == NULL)) { return NJS_ERROR; } njs_typed_array_prop_set(vm, array, index, num); return NJS_OK; } static njs_int_t njs_typed_array_prototype_set(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double num; int64_t i, length, src_length, offset; njs_int_t ret; njs_value_t *this, *src, *value, prop; njs_typed_array_t *self, *src_tarray; njs_array_buffer_t *buffer; this = njs_argument(args, 0); if (njs_slow_path(!njs_is_typed_array(this))) { njs_type_error(vm, "this is not a typed array"); return NJS_ERROR; } self = njs_typed_array(this); src = njs_arg(args, nargs, 1); value = njs_arg(args, nargs, 2); ret = njs_value_to_integer(vm, value, &offset); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } if (njs_slow_path(offset < 0)) { njs_range_error(vm, "offset is out of bounds"); return NJS_ERROR; } buffer = njs_typed_array_writable(vm, self); if (njs_slow_path(buffer == NULL)) { return NJS_ERROR; } length = njs_typed_array_length(self); if (njs_is_typed_array(src)) { src_tarray = njs_typed_array(src); if (njs_slow_path(njs_is_detached_buffer(src_tarray->buffer))) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } src_length = njs_typed_array_length(src_tarray); if (njs_slow_path((src_length > length) || (offset > length - src_length))) { njs_range_error(vm, "source is too large"); return NJS_ERROR; } length = njs_min(njs_typed_array_length(src_tarray), length - offset); for (i = 0; i < length; i++) { njs_typed_array_prop_set(vm, self, offset + i, njs_typed_array_prop(src_tarray, i)); } } else { ret = njs_value_to_object(vm, src); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_object_length(vm, src, &src_length); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (njs_slow_path((src_length > length) || (offset > length - src_length))) { njs_range_error(vm, "source is too large"); return NJS_ERROR; } length = njs_min(src_length, length - offset); for (i = 0; i < length; i++) { ret = njs_value_property_i64(vm, src, i, &prop); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } num = NAN; if (ret == NJS_OK) { ret = njs_value_to_number(vm, &prop, &num); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } } if (njs_slow_path(njs_is_detached_buffer(buffer))) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } njs_typed_array_prop_set(vm, self, offset + i, num); } } njs_set_undefined(retval); return NJS_OK; } static njs_int_t njs_typed_array_prototype_fill(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { float f32; int8_t i8; double num; int16_t i16; int32_t i32; uint8_t u8; int64_t start, end, offset; uint32_t i, length; njs_int_t ret; njs_value_t *this, *setval, lvalue; njs_typed_array_t *array; njs_array_buffer_t *buffer; this = njs_argument(args, 0); if (njs_slow_path(!njs_is_typed_array(this))) { njs_type_error(vm, "this is not a typed array"); return NJS_ERROR; } array = njs_typed_array(this); if (njs_slow_path(njs_is_detached_buffer(array->buffer))) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } length = njs_typed_array_length(array); setval = njs_lvalue_arg(&lvalue, args, nargs, 1); ret = njs_value_to_number(vm, setval, &num); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_value_to_integer(vm, njs_arg(args, nargs, 2), &start); if (njs_slow_path(ret != NJS_OK)) { return ret; } start = (start < 0) ? njs_max(length + start, 0) : njs_min(start, length); if (njs_is_undefined(njs_arg(args, nargs, 3))) { end = length; } else { ret = njs_value_to_integer(vm, njs_arg(args, nargs, 3), &end); if (njs_slow_path(ret != NJS_OK)) { return ret; } } end = (end < 0) ? njs_max(length + end, 0) : njs_min(end, length); buffer = njs_typed_array_writable(vm, array); if (njs_slow_path(buffer == NULL)) { return NJS_ERROR; } njs_set_typed_array(retval, array); offset = array->offset; switch (array->type) { case NJS_OBJ_TYPE_UINT8_CLAMPED_ARRAY: if (isnan(num) || num < 0) { u8 = 0; } else if (num > 255) { u8 = 255; } else { u8 = lrint(num); } if (start < end) { memset(&buffer->u.u8[start + offset], u8, end - start); } break; case NJS_OBJ_TYPE_UINT8_ARRAY: case NJS_OBJ_TYPE_INT8_ARRAY: i8 = njs_number_to_int32(num); if (start < end) { memset(&buffer->u.u8[start + offset], i8, end - start); } break; case NJS_OBJ_TYPE_UINT16_ARRAY: case NJS_OBJ_TYPE_INT16_ARRAY: i16 = njs_number_to_int32(num); for (i = start; i < end; i++) { buffer->u.u16[i + offset] = i16; } break; case NJS_OBJ_TYPE_UINT32_ARRAY: case NJS_OBJ_TYPE_INT32_ARRAY: i32 = njs_number_to_int32(num); for (i = start; i < end; i++) { buffer->u.u32[i + offset] = i32; } break; case NJS_OBJ_TYPE_FLOAT32_ARRAY: f32 = num; for (i = start; i < end; i++) { buffer->u.f32[i + offset] = f32; } break; default: /* NJS_OBJ_TYPE_FLOAT64_ARRAY. */ for (i = start; i < end; i++) { buffer->u.f64[i + offset] = num; } } return NJS_OK; } njs_int_t njs_typed_array_prototype_slice(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t copy, njs_value_t *retval) { int64_t start, end, count, offset; uint32_t i, element_size, length; njs_int_t ret; njs_value_t arguments[3], *this, *value; njs_typed_array_t *array, *new_array; njs_array_buffer_t *buffer, *new_buffer; this = njs_argument(args, 0); if (njs_slow_path(!njs_is_typed_array(this))) { njs_type_error(vm, "this is not a typed array"); return NJS_ERROR; } array = njs_typed_array(this); length = njs_typed_array_length(array); buffer = njs_typed_array_buffer(array); if (njs_slow_path(copy && njs_is_detached_buffer(buffer))) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } ret = njs_value_to_integer(vm, njs_arg(args, nargs, 1), &start); if (njs_slow_path(ret != NJS_OK)) { njs_range_error(vm, "invalid start"); return NJS_ERROR; } start = (start < 0) ? njs_max(length + start, 0) : njs_min(start, length); value = njs_arg(args, nargs, 2); if (njs_is_undefined(value)) { end = length; } else { ret = njs_value_to_integer(vm, value, &end); if (njs_slow_path(ret != NJS_OK)) { njs_range_error(vm, "invalid end"); return NJS_ERROR; } } end = (end < 0) ? njs_max(length + end, 0) : njs_min(end, length); element_size = njs_typed_array_element_size(array->type); if (copy) { count = njs_max(end - start, 0); njs_set_number(&arguments[0], count); ret = njs_typed_array_species_create(vm, this, njs_value_arg(arguments), 1, retval); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (count == 0) { return NJS_OK; } if (njs_slow_path(njs_is_detached_buffer(buffer))) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } new_array = njs_typed_array(retval); new_buffer = njs_typed_array_buffer(new_array); element_size = njs_typed_array_element_size(array->type); if (njs_fast_path(array->type == new_array->type)) { start = start * element_size; count = count * element_size; memcpy(&new_buffer->u.u8[0], &buffer->u.u8[start], count); } else { for (i = 0; i < count; i++) { njs_typed_array_prop_set(vm, new_array, i, njs_typed_array_prop(array, i + start)); } } return NJS_OK; } offset = array->offset * element_size; offset += start * element_size; njs_set_array_buffer(&arguments[0], buffer); njs_set_number(&arguments[1], offset); njs_set_number(&arguments[2], njs_max(end - start, 0)); return njs_typed_array_species_create(vm, this, njs_value_arg(arguments), 3, retval); } static njs_int_t njs_typed_array_prototype_copy_within(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t length, to, from, final, count; uint32_t element_size; njs_int_t ret; njs_value_t *this, *value; njs_typed_array_t *array; njs_array_buffer_t *buffer; this = njs_argument(args, 0); if (njs_slow_path(!njs_is_typed_array(this))) { njs_type_error(vm, "this is not a typed array"); return NJS_ERROR; } array = njs_typed_array(this); if (njs_slow_path(njs_is_detached_buffer(array->buffer))) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } length = njs_typed_array_length(array); value = njs_arg(args, nargs, 1); ret = njs_value_to_integer(vm, value, &to); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } to = to < 0 ? njs_max(to + length, 0) : njs_min(to, length); value = njs_arg(args, nargs, 2); ret = njs_value_to_integer(vm, value, &from); if (njs_slow_path(ret != NJS_OK)) { return ret; } from = from < 0 ? njs_max(from + length, 0) : njs_min(from, length); value = njs_arg(args, nargs, 3); final = length; if (njs_is_defined(value)) { ret = njs_value_to_integer(vm, value, &final); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } final = (final < 0) ? njs_max(final + length, 0) : njs_min(final, length); njs_set_typed_array(retval, array); count = njs_min(final - from, length - to); if (count <= 0) { return NJS_OK; } buffer = njs_typed_array_writable(vm, array); if (njs_slow_path(buffer == NULL)) { return NJS_ERROR; } element_size = njs_typed_array_element_size(array->type); to = (to + array->offset) * element_size; from = (from + array->offset) * element_size; count = count * element_size; memmove(&buffer->u.u8[to], &buffer->u.u8[from], count); return NJS_OK; } static njs_int_t njs_typed_array_prototype_iterator(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t type, njs_value_t *retval) { double val; int64_t i, length; njs_int_t ret; njs_arr_t results; njs_value_t *this, *this_arg, *r; njs_value_t arguments[4], value; njs_function_t *function; njs_typed_array_t *array, *dst; njs_array_buffer_t *buffer; this = njs_argument(args, 0); if (njs_slow_path(!njs_is_typed_array(this))) { njs_type_error(vm, "this is not a typed array"); return NJS_ERROR; } dst = NULL; array = njs_typed_array(this); length = njs_typed_array_length(array); if (njs_slow_path(!njs_is_function(njs_arg(args, nargs, 1)))) { njs_type_error(vm, "callback argument is not callable"); return NJS_ERROR; } function = njs_function(njs_argument(args, 1)); this_arg = njs_arg(args, nargs, 2); buffer = array->buffer; results.separate = 0; results.pointer = 0; switch (type) { case NJS_ARRAY_MAP: njs_set_number(&arguments[0], length); ret = njs_typed_array_species_create(vm, this, njs_value_arg(arguments), 1, &value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } dst = njs_typed_array(&value); break; case NJS_ARRAY_FILTER: default: r = njs_arr_init(vm->mem_pool, &results, NULL, 4, sizeof(njs_value_t)); if (njs_slow_path(r == NULL)) { return NJS_ERROR; } } for (i = 0; i < length; i++) { if (njs_slow_path(njs_is_detached_buffer(buffer))) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } val = njs_typed_array_prop(array, i); arguments[0] = *this_arg; njs_set_number(&arguments[1], val); njs_set_number(&arguments[2], i); njs_set_typed_array(&arguments[3], array); ret = njs_function_apply(vm, function, arguments, 4, retval); if (njs_slow_path(ret != NJS_OK)) { goto exception; } switch (type) { case NJS_ARRAY_EVERY: if (!njs_is_true(retval)) { njs_set_boolean(retval, 0); goto done; } break; case NJS_ARRAY_FOR_EACH: break; case NJS_ARRAY_SOME: case NJS_ARRAY_FIND: case NJS_ARRAY_FIND_INDEX: if (!njs_is_true(retval)) { continue; } switch (type) { case NJS_ARRAY_SOME: njs_set_boolean(retval, 1); break; case NJS_ARRAY_FIND: njs_set_number(retval, val); break; default: njs_set_number(retval, i); break; } goto done; case NJS_ARRAY_MAP: ret = njs_typed_array_set_value(vm, dst, i, retval); if (njs_slow_path(ret != NJS_OK)) { goto exception; } break; default: /* NJS_ARRAY_FILTER. */ if (!njs_is_true(retval)) { continue; } r = njs_arr_add(&results); if (njs_slow_path(r == NULL)) { goto exception; } njs_set_number(r, val); } } /* Default values. */ switch (type) { case NJS_ARRAY_EVERY: njs_set_boolean(retval, 1); break; case NJS_ARRAY_SOME: njs_set_boolean(retval, 0); break; case NJS_ARRAY_FOR_EACH: case NJS_ARRAY_FIND: njs_set_undefined(retval); break; case NJS_ARRAY_FIND_INDEX: njs_set_number(retval, -1); break; case NJS_ARRAY_MAP: case NJS_ARRAY_FILTER: default: if (type == NJS_ARRAY_FILTER) { njs_set_number(&arguments[0], results.items); ret = njs_typed_array_species_create(vm, this, njs_value_arg(arguments), 1, &value); if (njs_slow_path(ret != NJS_OK)) { goto exception; } dst = njs_typed_array(&value); i = 0; while (i < results.items) { r = njs_arr_item(&results, i); ret = njs_typed_array_set_value(vm, dst, i++, r); if (njs_slow_path(ret != NJS_OK)) { goto exception; } } } njs_set_typed_array(retval, dst); break; } done: ret = NJS_OK; exception: njs_arr_destroy(&results); return ret; } static njs_int_t njs_typed_array_prototype_index_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t type, njs_value_t *retval) { double v; int64_t i, i64, from, to, index, increment, offset, length; njs_int_t ret, integer; njs_value_t *this; const float *f32; const double *f64; const uint8_t *u8; const uint16_t *u16; const uint32_t *u32; njs_typed_array_t *array; njs_array_buffer_t *buffer; this = njs_argument(args, 0); if (njs_slow_path(!njs_is_typed_array(this))) { njs_type_error(vm, "this is not a typed array"); return NJS_ERROR; } index = -1; array = njs_typed_array(this); length = njs_typed_array_length(array); if (!njs_is_number(njs_arg(args, nargs, 1)) || length == 0) { goto done; } if (type & 2) { /* lastIndexOf(). */ if (nargs > 2) { ret = njs_value_to_integer(vm, njs_arg(args, nargs, 2), &from); if (njs_slow_path(ret != NJS_OK)) { return ret; } } else { from = length - 1; } if (from >= 0) { from = njs_min(from, length - 1); } else if (from < 0) { from += length; } to = -1; increment = -1; if (from <= to) { goto done; } } else { /* indexOf(), includes(). */ ret = njs_value_to_integer(vm, njs_arg(args, nargs, 2), &from); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (from < 0) { from += length; if (from < 0) { from = 0; } } to = length; increment = 1; if (from >= to) { goto done; } } if (njs_slow_path(njs_is_detached_buffer(array->buffer))) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } v = njs_number(njs_argument(args, 1)); i64 = njs_unsafe_cast_double_to_int64(v); integer = (v == i64); buffer = array->buffer; offset = array->offset; switch (array->type) { case NJS_OBJ_TYPE_INT8_ARRAY: if (integer && ((int8_t) i64 == i64)) { goto search8; } break; case NJS_OBJ_TYPE_UINT8_CLAMPED_ARRAY: case NJS_OBJ_TYPE_UINT8_ARRAY: if (integer && ((uint8_t) i64 == i64)) { search8: u8 = &buffer->u.u8[0]; for (i = from; i != to; i += increment) { if (u8[offset + i] == (uint8_t) i64) { index = i; break; } } } break; case NJS_OBJ_TYPE_INT16_ARRAY: if (integer && ((int16_t) i64 == i64)) { goto search16; } break; case NJS_OBJ_TYPE_UINT16_ARRAY: if (integer && ((uint16_t) i64 == i64)) { search16: u16 = &buffer->u.u16[0]; for (i = from; i != to; i += increment) { if (u16[offset + i] == (uint16_t) i64) { index = i; break; } } } break; case NJS_OBJ_TYPE_INT32_ARRAY: if (integer && ((int32_t) i64 == i64)) { goto search32; } break; case NJS_OBJ_TYPE_UINT32_ARRAY: if (integer && ((uint32_t) i64 == i64)) { search32: u32 = &buffer->u.u32[0]; for (i = from; i != to; i += increment) { if (u32[offset + i] == (uint32_t) i64) { index = i; break; } } } break; case NJS_OBJ_TYPE_FLOAT32_ARRAY: f32 = &buffer->u.f32[0]; if (((float) v == v)) { for (i = from; i != to; i += increment) { if (f32[offset + i] == (float) v) { index = i; break; } } } else if ((type & 1) && isnan(v)) { /* includes() handles NaN. */ for (i = from; i != to; i += increment) { if (isnan(f32[offset + i])) { index = i; break; } } } break; default: /* NJS_OBJ_TYPE_FLOAT64_ARRAY. */ f64 = &buffer->u.f64[0]; if ((type & 1) && isnan(v)) { /* includes() handles NaN. */ for (i = from; i != to; i += increment) { if (isnan(f64[offset + i])) { index = i; break; } } } else { for (i = from; i != to; i += increment) { if (f64[offset + i] == v) { index = i; break; } } } } done: /* Default values. */ if (type & 1) { njs_set_boolean(retval, index != -1); } else { njs_set_number(retval, index); } return NJS_OK; } static njs_int_t njs_typed_array_prototype_reduce(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t right, njs_value_t *retval) { int64_t i, from, to, increment, length; njs_int_t ret; njs_value_t *this, accumulator; njs_value_t arguments[5]; njs_function_t *function; njs_typed_array_t *array; njs_array_buffer_t *buffer; this = njs_argument(args, 0); if (njs_slow_path(!njs_is_typed_array(this))) { njs_type_error(vm, "this is not a typed array"); return NJS_ERROR; } array = njs_typed_array(this); length = njs_typed_array_length(array); if (njs_slow_path(!njs_is_function(njs_arg(args, nargs, 1)))) { njs_type_error(vm, "callback argument is not callable"); return NJS_ERROR; } function = njs_function(njs_argument(args, 1)); if (length == 0 && nargs <= 2) { njs_type_error(vm, "Reduce of empty object with no initial value"); return NJS_ERROR; } if (right) { from = length - 1; to = -1; increment = -1; } else { from = 0; to = length; increment = 1; } buffer = array->buffer; if (nargs > 2) { accumulator = *njs_argument(args, 2); } else { if (njs_slow_path(njs_is_detached_buffer(buffer))) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } njs_set_number(&accumulator, njs_typed_array_prop(array, from)); from += increment; } for (i = from; i != to; i += increment) { if (njs_slow_path(njs_is_detached_buffer(buffer))) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } njs_set_undefined(&arguments[0]); arguments[1] = accumulator; njs_set_number(&arguments[2], njs_typed_array_prop(array, i)); njs_set_number(&arguments[3], i); njs_set_typed_array(&arguments[4], array); ret = njs_function_apply(vm, function, arguments, 5, &accumulator); if (njs_slow_path(ret != NJS_OK)) { return ret; } } njs_value_assign(retval, &accumulator); return NJS_OK; } static njs_int_t njs_typed_array_prototype_reverse(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t to_reversed, njs_value_t *retval) { double *f64; uint8_t *u8; int64_t i, length; uint16_t *u16; uint32_t *u32; njs_value_t *this, arguments[1]; njs_typed_array_t *array, *self; njs_array_buffer_t *buffer; this = njs_argument(args, 0); if (njs_slow_path(!njs_is_typed_array(this))) { njs_type_error(vm, "this is not a typed array"); return NJS_ERROR; } array = njs_typed_array(this); if (njs_slow_path(njs_is_detached_buffer(array->buffer))) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } if (to_reversed) { self = array; njs_set_number(&arguments[0], njs_typed_array_length(self)); array = njs_typed_array_alloc(vm, arguments, 1, 0, self->type); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } memcpy(&array->buffer->u.u8[0], &self->buffer->u.u8[0], self->byte_length); } length = njs_typed_array_length(array); buffer = njs_typed_array_writable(vm, array); if (njs_slow_path(buffer == NULL)) { return NJS_ERROR; } switch (array->type) { case NJS_OBJ_TYPE_UINT8_ARRAY: case NJS_OBJ_TYPE_UINT8_CLAMPED_ARRAY: case NJS_OBJ_TYPE_INT8_ARRAY: u8 = &buffer->u.u8[array->offset]; for (i = 0; i < length / 2; i++) { njs_swap_u8(&u8[i], &u8[length - i - 1], 0); } break; case NJS_OBJ_TYPE_INT16_ARRAY: case NJS_OBJ_TYPE_UINT16_ARRAY: u16 = &buffer->u.u16[array->offset]; for (i = 0; i < length / 2; i++) { njs_swap_u16(&u16[i], &u16[length - i - 1], 0); } break; case NJS_OBJ_TYPE_INT32_ARRAY: case NJS_OBJ_TYPE_UINT32_ARRAY: case NJS_OBJ_TYPE_FLOAT32_ARRAY: u32 = &buffer->u.u32[array->offset]; for (i = 0; i < length / 2; i++) { njs_swap_u32(&u32[i], &u32[length - i - 1], 0); } break; default: /* NJS_OBJ_TYPE_FLOAT64_ARRAY. */ f64 = &buffer->u.f64[array->offset]; for (i = 0; i < length / 2; i++) { njs_swap_u64(&f64[i], &f64[length - i - 1], 0); } } njs_set_typed_array(retval, array); return NJS_OK; } static int njs_typed_array_compare_i8(const void *a, const void *b, void *c) { return *((const int8_t *) a) - *((const int8_t *) b); } static int njs_typed_array_compare_u8(const void *a, const void *b, void *c) { return *((const uint8_t *) a) - *((const uint8_t *) b); } static int njs_typed_array_compare_i16(const void *a, const void *b, void *c) { return *((const int16_t *) a) - *((const int16_t *) b); } static int njs_typed_array_compare_u16(const void *a, const void *b, void *c) { return *((const uint16_t *) a) - *((const uint16_t *) b); } static int njs_typed_array_compare_i32(const void *a, const void *b, void *c) { int32_t ai, bi; ai = *(const int32_t *) a; bi = *(const int32_t *) b; return (ai > bi) - (ai < bi); } static int njs_typed_array_compare_u32(const void *a, const void *b, void *c) { uint32_t au, bu; au = *(const uint32_t *) a; bu = *(const uint32_t *) b; return (au > bu) - (au < bu); } njs_inline int njs_typed_array_compare(double a, double b) { if (njs_slow_path(isnan(a))) { if (isnan(b)) { return 0; } return 1; } if (njs_slow_path(isnan(b))) { return -1; } if (a < b) { return -1; } if (a > b) { return 1; } return signbit(b) - signbit(a); } static int njs_typed_array_compare_f32(const void *a, const void *b, void *c) { return njs_typed_array_compare(*(const float *) a, *(const float *) b); } static int njs_typed_array_compare_f64(const void *a, const void *b, void *c) { return njs_typed_array_compare(*(const double *) a, *(const double *) b); } static double njs_typed_array_get_u8(const void *a) { return *(const uint8_t *) a; } static double njs_typed_array_get_i8(const void *a) { return *(const int8_t *) a; } static double njs_typed_array_get_u16(const void *a) { return *(const uint16_t *) a; } static double njs_typed_array_get_i16(const void *a) { return *(const int16_t *) a; } static double njs_typed_array_get_u32(const void *a) { return *(const uint32_t *) a; } static double njs_typed_array_get_i32(const void *a) { return *(const int32_t *) a; } static double njs_typed_array_get_f32(const void *a) { return *(const float *) a; } static double njs_typed_array_get_f64(const void *a) { return *(const double *) a; } typedef struct { njs_vm_t *vm; njs_array_buffer_t *buffer; njs_function_t *function; njs_bool_t exception; double (*get)(const void *v); } njs_typed_array_sort_ctx_t; typedef int (*njs_typed_array_cmp_t)(const void *, const void *, void *ctx); static int njs_typed_array_generic_compare(const void *a, const void *b, void *c) { double num; njs_int_t ret; njs_value_t arguments[3], retval; njs_typed_array_sort_ctx_t *ctx; ctx = c; if (njs_slow_path(ctx->exception)) { return 0; } njs_set_undefined(&arguments[0]); njs_set_number(&arguments[1], ctx->get(a)); njs_set_number(&arguments[2], ctx->get(b)); ret = njs_function_apply(ctx->vm, ctx->function, arguments, 3, &retval); if (njs_slow_path(ret != NJS_OK)) { goto exception; } ret = njs_value_to_number(ctx->vm, &retval, &num); if (njs_slow_path(ret != NJS_OK)) { goto exception; } if (njs_slow_path(njs_is_detached_buffer(ctx->buffer))) { njs_type_error(ctx->vm, "detached buffer"); goto exception; } if (njs_slow_path(isnan(num))) { return 0; } if (num != 0) { return (num > 0) - (num < 0); } return 0; exception: ctx->exception = 1; return 0; } static njs_int_t njs_typed_array_prototype_sort(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t to_sorted, njs_value_t *retval) { u_char *base, *orig; int64_t length; uint32_t element_size; njs_value_t *this, *comparefn, arguments[1]; njs_typed_array_t *array, *self; njs_array_buffer_t *buffer; njs_typed_array_cmp_t cmp; njs_typed_array_sort_ctx_t ctx; this = njs_argument(args, 0); if (njs_slow_path(!njs_is_typed_array(this))) { njs_type_error(vm, "this is not a typed array"); return NJS_ERROR; } array = njs_typed_array(this); if (njs_slow_path(njs_is_detached_buffer(array->buffer))) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } if (to_sorted) { self = array; njs_set_number(&arguments[0], njs_typed_array_length(self)); array = njs_typed_array_alloc(vm, arguments, 1, 0, self->type); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } memcpy(&array->buffer->u.u8[0], &self->buffer->u.u8[0], self->byte_length); } ctx.vm = vm; ctx.buffer = array->buffer; ctx.exception = 0; comparefn = njs_arg(args, nargs, 1); if (njs_is_defined(comparefn)) { if (njs_slow_path(!njs_is_function(comparefn))) { njs_type_error(vm, "comparefn must be callable or undefined"); return NJS_ERROR; } ctx.function = njs_function(comparefn); } else { ctx.function = NULL; } switch (array->type) { case NJS_OBJ_TYPE_UINT8_ARRAY: case NJS_OBJ_TYPE_UINT8_CLAMPED_ARRAY: cmp = njs_typed_array_compare_u8; ctx.get = njs_typed_array_get_u8; break; case NJS_OBJ_TYPE_INT8_ARRAY: cmp = njs_typed_array_compare_i8; ctx.get = njs_typed_array_get_i8; break; case NJS_OBJ_TYPE_UINT16_ARRAY: cmp = njs_typed_array_compare_u16; ctx.get = njs_typed_array_get_u16; break; case NJS_OBJ_TYPE_INT16_ARRAY: cmp = njs_typed_array_compare_i16; ctx.get = njs_typed_array_get_i16; break; case NJS_OBJ_TYPE_UINT32_ARRAY: cmp = njs_typed_array_compare_u32; ctx.get = njs_typed_array_get_u32; break; case NJS_OBJ_TYPE_INT32_ARRAY: cmp = njs_typed_array_compare_i32; ctx.get = njs_typed_array_get_i32; break; case NJS_OBJ_TYPE_FLOAT32_ARRAY: cmp = njs_typed_array_compare_f32; ctx.get = njs_typed_array_get_f32; break; default: /* NJS_OBJ_TYPE_FLOAT64_ARRAY. */ cmp = njs_typed_array_compare_f64; ctx.get = njs_typed_array_get_f64; break; } buffer = njs_typed_array_writable(vm, array); if (njs_slow_path(buffer == NULL)) { return NJS_ERROR; } length = njs_typed_array_length(array); element_size = njs_typed_array_element_size(array->type); base = &buffer->u.u8[array->offset * element_size]; orig = base; if (ctx.function != NULL) { cmp = njs_typed_array_generic_compare; base = njs_mp_alloc(vm->mem_pool, length * element_size); if (njs_slow_path(base == NULL)) { njs_memory_error(vm); return NJS_ERROR; } memcpy(base, &buffer->u.u8[array->offset * element_size], length * element_size); } njs_qsort(base, length, element_size, cmp, &ctx); if (njs_slow_path(ctx.exception)) { return NJS_ERROR; } if (ctx.function != NULL) { if (&buffer->u.u8[array->offset * element_size] == orig) { memcpy(orig, base, length * element_size); } njs_mp_free(vm->mem_pool, base); } njs_set_typed_array(retval, array); return NJS_OK; } void njs_typed_array_to_chain(njs_vm_t *vm, njs_chb_t *chain, njs_typed_array_t *array, njs_value_t *sep) { size_t length; uint32_t i; njs_string_prop_t separator; if (sep == NULL) { sep = njs_value_arg(&njs_string_comma); } (void) njs_string_prop(&separator, sep); length = njs_typed_array_length(array); if (length == 0) { return; } for (i = 0; i < length; i++) { njs_number_to_chain(vm, chain, njs_typed_array_prop(array, i)); njs_chb_append(chain, separator.start, separator.size); } njs_chb_drop(chain, separator.size); } static njs_int_t njs_typed_array_prototype_join(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { size_t length; njs_int_t ret; njs_chb_t chain; njs_value_t *this, *separator; njs_typed_array_t *array; this = njs_argument(args, 0); if (njs_slow_path(!njs_is_typed_array(this))) { njs_type_error(vm, "this is not a typed array"); return NJS_ERROR; } array = njs_typed_array(this); if (njs_slow_path(njs_is_detached_buffer(array->buffer))) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } length = njs_typed_array_length(array); separator = njs_arg(args, nargs, 1); if (njs_slow_path(!njs_is_string(separator))) { if (njs_is_undefined(separator)) { separator = njs_value_arg(&njs_string_comma); } else { ret = njs_value_to_string(vm, separator, separator); if (njs_slow_path(ret != NJS_OK)) { return ret; } } } if (length == 0) { njs_value_assign(retval, &njs_string_empty); return NJS_OK; } if (njs_slow_path(njs_is_detached_buffer(array->buffer))) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } NJS_CHB_MP_INIT(&chain, vm); njs_typed_array_to_chain(vm, &chain, array, separator); ret = njs_string_create_chb(vm, retval, &chain); njs_chb_destroy(&chain); return ret; } static njs_int_t njs_typed_array_prototype_iterator_obj(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t kind, njs_value_t *retval) { njs_value_t *this; njs_typed_array_t *array; this = njs_argument(args, 0); if (njs_slow_path(!njs_is_typed_array(this))) { njs_type_error(vm, "this is not a typed array"); return NJS_ERROR; } array = njs_typed_array(this); if (njs_slow_path(njs_is_detached_buffer(array->buffer))) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } return njs_array_iterator_create(vm, this, retval, kind); } static njs_int_t njs_typed_array_constructor_intrinsic(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_type_error(vm, "Abstract class TypedArray not directly constructable"); return NJS_ERROR; } static const njs_object_prop_t njs_typed_array_constructor_props[] = { NJS_DECLARE_PROP_LENGTH(0), NJS_DECLARE_PROP_NAME("TypedArray"), NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), { .type = NJS_ACCESSOR, .name = njs_wellknown_symbol(NJS_SYMBOL_SPECIES), .u.accessor = njs_getter(njs_typed_array_get_this, 0), .writable = NJS_ATTRIBUTE_UNSET, .configurable = 1, .enumerable = 0, }, NJS_DECLARE_PROP_NATIVE("of", njs_typed_array_of, 0, 0), NJS_DECLARE_PROP_NATIVE("from", njs_typed_array_from, 1, 0), }; static const njs_object_init_t njs_typed_array_constructor_init = { njs_typed_array_constructor_props, njs_nitems(njs_typed_array_constructor_props), }; static const njs_object_prop_t njs_typed_array_prototype_properties[] = { { .type = NJS_ACCESSOR, .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG), .u.accessor = njs_getter(njs_typed_array_get_string_tag, 0), .writable = NJS_ATTRIBUTE_UNSET, .configurable = 1, .enumerable = 0, }, NJS_DECLARE_PROP_HANDLER("constructor", njs_object_prototype_create_constructor, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_GETTER("buffer", njs_typed_array_prototype_buffer, 0), NJS_DECLARE_PROP_GETTER("byteLength", njs_typed_array_prototype_byte_length, 0), NJS_DECLARE_PROP_GETTER("byteOffset", njs_typed_array_prototype_byte_offset, 0), NJS_DECLARE_PROP_GETTER("length", njs_typed_array_prototype_length, 0), NJS_DECLARE_PROP_NATIVE("copyWithin", njs_typed_array_prototype_copy_within, 2, 0), NJS_DECLARE_PROP_NATIVE("entries", njs_typed_array_prototype_iterator_obj, 0, NJS_ENUM_BOTH), NJS_DECLARE_PROP_NATIVE("every", njs_typed_array_prototype_iterator, 1, NJS_ARRAY_EVERY), NJS_DECLARE_PROP_NATIVE("filter", njs_typed_array_prototype_iterator, 1, NJS_ARRAY_FILTER), NJS_DECLARE_PROP_NATIVE("find", njs_typed_array_prototype_iterator, 1, NJS_ARRAY_FIND), NJS_DECLARE_PROP_NATIVE("findIndex", njs_typed_array_prototype_iterator, 1, NJS_ARRAY_FIND_INDEX), NJS_DECLARE_PROP_NATIVE("forEach", njs_typed_array_prototype_iterator, 1, NJS_ARRAY_FOR_EACH), NJS_DECLARE_PROP_NATIVE("includes", njs_typed_array_prototype_index_of, 1, 1), NJS_DECLARE_PROP_NATIVE("indexOf", njs_typed_array_prototype_index_of, 1, 0), NJS_DECLARE_PROP_NATIVE("join", njs_typed_array_prototype_join, 1, 0), NJS_DECLARE_PROP_NATIVE("fill", njs_typed_array_prototype_fill, 1, 0), NJS_DECLARE_PROP_NATIVE("keys", njs_typed_array_prototype_iterator_obj, 0, NJS_ENUM_KEYS), NJS_DECLARE_PROP_NATIVE("lastIndexOf", njs_typed_array_prototype_index_of, 1, 2), NJS_DECLARE_PROP_NATIVE("map", njs_typed_array_prototype_iterator, 1, NJS_ARRAY_MAP), NJS_DECLARE_PROP_NATIVE("reduce", njs_typed_array_prototype_reduce, 1, 0), NJS_DECLARE_PROP_NATIVE("reduceRight", njs_typed_array_prototype_reduce, 1, 1), NJS_DECLARE_PROP_NATIVE("reverse", njs_typed_array_prototype_reverse, 0, 0), NJS_DECLARE_PROP_NATIVE("set", njs_typed_array_prototype_set, 2, 0), NJS_DECLARE_PROP_NATIVE("slice", njs_typed_array_prototype_slice, 2, 1), NJS_DECLARE_PROP_NATIVE("some", njs_typed_array_prototype_iterator, 1, NJS_ARRAY_SOME), NJS_DECLARE_PROP_NATIVE("sort", njs_typed_array_prototype_sort, 1, 0), NJS_DECLARE_PROP_NATIVE("subarray", njs_typed_array_prototype_slice, 2, 0), NJS_DECLARE_PROP_NATIVE("toReversed", njs_typed_array_prototype_reverse, 0, 1), NJS_DECLARE_PROP_NATIVE("toSorted", njs_typed_array_prototype_sort, 1, 1), NJS_DECLARE_PROP_NATIVE("toString", njs_array_prototype_to_string, 0, 0), NJS_DECLARE_PROP_NATIVE("values", njs_typed_array_prototype_iterator_obj, 0, NJS_ENUM_VALUES), { .type = NJS_PROPERTY, .name = njs_wellknown_symbol(NJS_SYMBOL_ITERATOR), .u.value = njs_native_function2(njs_typed_array_prototype_iterator_obj, 0, NJS_ENUM_VALUES), .writable = 1, .configurable = 1, }, }; static const njs_object_init_t njs_typed_array_prototype_init = { njs_typed_array_prototype_properties, njs_nitems(njs_typed_array_prototype_properties), }; const njs_object_type_init_t njs_typed_array_type_init = { .constructor = njs_native_ctor(njs_typed_array_constructor_intrinsic, 0, 0), .prototype_props = &njs_typed_array_prototype_init, .constructor_props = &njs_typed_array_constructor_init, .prototype_value = { .object = { .type = NJS_OBJECT } }, }; static njs_int_t njs_data_view_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { uint64_t size, offset; njs_int_t ret; njs_data_view_t *view; njs_array_buffer_t *buffer; if (!vm->top_frame->ctor) { njs_type_error(vm, "Constructor of DataView requires 'new'"); return NJS_ERROR; } if (!njs_is_array_buffer(njs_arg(args, nargs, 1))) { njs_type_error(vm, "buffer is not an ArrayBuffer"); return NJS_ERROR; } size = 0; offset = 0; ret = njs_value_to_index(vm, njs_arg(args, nargs, 2), &offset); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } buffer = njs_array_buffer(njs_argument(args, 1)); if (njs_slow_path(njs_is_detached_buffer(buffer))) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } if (!njs_is_undefined(njs_arg(args, nargs, 3))) { ret = njs_value_to_index(vm, njs_argument(args, 3), &size); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } if (njs_slow_path((offset + size) > buffer->size)) { njs_range_error(vm, "Invalid DataView length: %uL", size); return NJS_ERROR; } } else { if (offset > buffer->size) { njs_range_error(vm, "byteOffset %uL is outside the bound of " "the buffer", offset); return NJS_ERROR; } size = buffer->size - offset; } view = njs_mp_zalloc(vm->mem_pool, sizeof(njs_data_view_t)); if (njs_slow_path(view == NULL)) { goto memory_error; } view->buffer = buffer; view->offset = offset; view->byte_length = size; view->type = NJS_OBJ_TYPE_DATA_VIEW; njs_lvlhsh_init(&view->object.hash); njs_lvlhsh_init(&view->object.shared_hash); view->object.__proto__ = njs_vm_proto(vm, view->type); view->object.type = NJS_DATA_VIEW; view->object.extensible = 1; njs_set_data_view(retval, view); return NJS_OK; memory_error: njs_memory_error(vm); return NJS_ERROR; } static const njs_object_prop_t njs_data_view_constructor_props[] = { NJS_DECLARE_PROP_LENGTH(1), NJS_DECLARE_PROP_NAME("DataView"), NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), }; static const njs_object_init_t njs_data_view_constructor_init = { njs_data_view_constructor_props, njs_nitems(njs_data_view_constructor_props), }; static njs_int_t njs_data_view_prototype_get(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t type, njs_value_t *retval) { double v; uint32_t u32; uint64_t index; njs_int_t ret; njs_bool_t swap; njs_value_t *this; const uint8_t *u8; njs_conv_f32_t conv_f32; njs_conv_f64_t conv_f64; njs_data_view_t *view; njs_array_buffer_t *buffer; this = njs_argument(args, 0); if (njs_slow_path(!njs_is_data_view(this))) { njs_type_error(vm, "this is not a DataView"); return NJS_ERROR; } ret = njs_value_to_index(vm, njs_arg(args, nargs, 1), &index); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } swap = njs_bool(njs_arg(args, nargs, 2)); #if NJS_HAVE_LITTLE_ENDIAN swap = !swap; #endif view = njs_data_view(this); if (njs_slow_path(njs_is_detached_buffer(view->buffer))) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } if (njs_typed_array_element_size(type) + index > view->byte_length) { njs_range_error(vm, "index %uL is outside the bound of the buffer", index); return NJS_ERROR; } buffer = view->buffer; u8 = &buffer->u.u8[index + view->offset]; switch (type) { case NJS_OBJ_TYPE_UINT8_ARRAY: v = *u8; break; case NJS_OBJ_TYPE_INT8_ARRAY: v = (int8_t) *u8; break; case NJS_OBJ_TYPE_UINT16_ARRAY: u32 = njs_get_u16(u8); if (swap) { u32 = njs_bswap_u16(u32); } v = u32; break; case NJS_OBJ_TYPE_INT16_ARRAY: u32 = njs_get_u16(u8); if (swap) { u32 = njs_bswap_u16(u32); } v = (int16_t) u32; break; case NJS_OBJ_TYPE_UINT32_ARRAY: case NJS_OBJ_TYPE_INT32_ARRAY: case NJS_OBJ_TYPE_FLOAT32_ARRAY: u32 = njs_get_u32(u8); if (swap) { u32 = njs_bswap_u32(u32); } switch (type) { case NJS_OBJ_TYPE_UINT32_ARRAY: v = u32; break; case NJS_OBJ_TYPE_INT32_ARRAY: v = (int32_t) u32; break; default: conv_f32.u = u32; v = conv_f32.f; } break; default: /* NJS_OBJ_TYPE_FLOAT64_ARRAY. */ conv_f64.u = njs_get_u64(u8); if (swap) { conv_f64.u = njs_bswap_u64(conv_f64.u); } v = conv_f64.f; } njs_set_number(retval, v); return NJS_OK; } static njs_int_t njs_data_view_prototype_set(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t type, njs_value_t *retval) { double v; uint8_t *u8; uint32_t u32; uint64_t index; njs_int_t ret; njs_bool_t swap; njs_value_t *this; njs_conv_f32_t conv_f32; njs_conv_f64_t conv_f64; njs_data_view_t *view; njs_array_buffer_t *buffer; this = njs_argument(args, 0); if (njs_slow_path(!njs_is_data_view(this))) { njs_type_error(vm, "this is not a DataView"); return NJS_ERROR; } ret = njs_value_to_index(vm, njs_arg(args, nargs, 1), &index); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_value_to_number(vm, njs_arg(args, nargs, 2), &v); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } swap = njs_bool(njs_arg(args, nargs, 3)); #if NJS_HAVE_LITTLE_ENDIAN swap = !swap; #endif view = njs_data_view(this); if (njs_slow_path(njs_is_detached_buffer(view->buffer))) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } if (njs_typed_array_element_size(type) + index > view->byte_length) { njs_range_error(vm, "index %uL is outside the bound of the buffer", index); return NJS_ERROR; } buffer = view->buffer; u8 = &buffer->u.u8[index + view->offset]; switch (type) { case NJS_OBJ_TYPE_UINT8_ARRAY: case NJS_OBJ_TYPE_INT8_ARRAY: *u8 = njs_number_to_int32(v); break; case NJS_OBJ_TYPE_UINT16_ARRAY: case NJS_OBJ_TYPE_INT16_ARRAY: u32 = (uint16_t) njs_number_to_int32(v); if (swap) { u32 = njs_bswap_u16(u32); } njs_set_u16(u8, u32); break; case NJS_OBJ_TYPE_UINT32_ARRAY: case NJS_OBJ_TYPE_INT32_ARRAY: u32 = njs_number_to_int32(v); if (swap) { u32 = njs_bswap_u32(u32); } njs_set_u32(u8, u32); break; case NJS_OBJ_TYPE_FLOAT32_ARRAY: conv_f32.f = (float) v; if (swap) { conv_f32.u = njs_bswap_u32(conv_f32.u); } njs_set_u32(u8, conv_f32.u); break; default: /* NJS_OBJ_TYPE_FLOAT64_ARRAY. */ conv_f64.f = v; if (swap) { conv_f64.u = njs_bswap_u64(conv_f64.u); } njs_set_u64(u8, conv_f64.u); } njs_set_undefined(retval); return NJS_OK; } static const njs_object_prop_t njs_data_view_prototype_properties[] = { { .type = NJS_PROPERTY, .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG), .u.value = njs_string("DataView"), .configurable = 1, }, NJS_DECLARE_PROP_HANDLER("constructor", njs_object_prototype_create_constructor, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_GETTER("buffer", njs_typed_array_prototype_buffer, 0), NJS_DECLARE_PROP_GETTER("byteLength", njs_typed_array_prototype_byte_length, 0), NJS_DECLARE_PROP_GETTER("byteOffset", njs_typed_array_prototype_byte_offset, 0), NJS_DECLARE_PROP_NATIVE("getUint8", njs_data_view_prototype_get, 1, NJS_OBJ_TYPE_UINT8_ARRAY), NJS_DECLARE_PROP_NATIVE("getInt8", njs_data_view_prototype_get, 1, NJS_OBJ_TYPE_INT8_ARRAY), NJS_DECLARE_PROP_NATIVE("getUint16", njs_data_view_prototype_get, 1, NJS_OBJ_TYPE_UINT16_ARRAY), NJS_DECLARE_PROP_NATIVE("getInt16", njs_data_view_prototype_get, 1, NJS_OBJ_TYPE_INT16_ARRAY), NJS_DECLARE_PROP_NATIVE("getUint32", njs_data_view_prototype_get, 1, NJS_OBJ_TYPE_UINT32_ARRAY), NJS_DECLARE_PROP_NATIVE("getInt32", njs_data_view_prototype_get, 1, NJS_OBJ_TYPE_INT32_ARRAY), NJS_DECLARE_PROP_NATIVE("getFloat32", njs_data_view_prototype_get, 1, NJS_OBJ_TYPE_FLOAT32_ARRAY), NJS_DECLARE_PROP_NATIVE("getFloat64", njs_data_view_prototype_get, 1, NJS_OBJ_TYPE_FLOAT64_ARRAY), NJS_DECLARE_PROP_NATIVE("setUint8", njs_data_view_prototype_set, 2, NJS_OBJ_TYPE_UINT8_ARRAY), NJS_DECLARE_PROP_NATIVE("setInt8", njs_data_view_prototype_set, 2, NJS_OBJ_TYPE_INT8_ARRAY), NJS_DECLARE_PROP_NATIVE("setUint16", njs_data_view_prototype_set, 2, NJS_OBJ_TYPE_UINT16_ARRAY), NJS_DECLARE_PROP_NATIVE("setInt16", njs_data_view_prototype_set, 2, NJS_OBJ_TYPE_INT16_ARRAY), NJS_DECLARE_PROP_NATIVE("setUint32", njs_data_view_prototype_set, 2, NJS_OBJ_TYPE_UINT32_ARRAY), NJS_DECLARE_PROP_NATIVE("setInt32", njs_data_view_prototype_set, 2, NJS_OBJ_TYPE_INT32_ARRAY), NJS_DECLARE_PROP_NATIVE("setFloat32", njs_data_view_prototype_set, 2, NJS_OBJ_TYPE_FLOAT32_ARRAY), NJS_DECLARE_PROP_NATIVE("setFloat64", njs_data_view_prototype_set, 2, NJS_OBJ_TYPE_FLOAT64_ARRAY), }; static const njs_object_init_t njs_data_view_prototype_init = { njs_data_view_prototype_properties, njs_nitems(njs_data_view_prototype_properties), }; const njs_object_type_init_t njs_data_view_type_init = { .constructor = njs_native_ctor(njs_data_view_constructor, 1, 0), .prototype_props = &njs_data_view_prototype_init, .constructor_props = &njs_data_view_constructor_init, .prototype_value = { .object = { .type = NJS_OBJECT } }, }; static const njs_object_prop_t njs_typed_array_u8_constructor_props[] = { NJS_DECLARE_PROP_LENGTH(3), NJS_DECLARE_PROP_NAME("Uint8Array"), NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), NJS_DECLARE_PROP_LVALUE("BYTES_PER_ELEMENT", njs_value(NJS_NUMBER, 1, 1), 0), }; static const njs_object_init_t njs_typed_array_u8_constructor_init = { njs_typed_array_u8_constructor_props, njs_nitems(njs_typed_array_u8_constructor_props), }; static const njs_object_prop_t njs_typed_array_u8_prototype_properties[] = { NJS_DECLARE_PROP_HANDLER("constructor", njs_object_prototype_create_constructor, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_LVALUE("BYTES_PER_ELEMENT", njs_value(NJS_NUMBER, 1, 1), 0), }; static const njs_object_init_t njs_typed_array_u8_prototype_init = { njs_typed_array_u8_prototype_properties, njs_nitems(njs_typed_array_u8_prototype_properties), }; const njs_object_type_init_t njs_typed_array_u8_type_init = { .constructor = njs_native_ctor(njs_typed_array_constructor, 3, NJS_OBJ_TYPE_UINT8_ARRAY), .prototype_props = &njs_typed_array_u8_prototype_init, .constructor_props = &njs_typed_array_u8_constructor_init, .prototype_value = { .object = { .type = NJS_OBJECT } }, }; static const njs_object_prop_t njs_typed_array_u8c_constructor_props[] = { { .type = NJS_PROPERTY, .name = njs_string("name"), .u.value = njs_long_string("Uint8ClampedArray"), .configurable = 1, }, NJS_DECLARE_PROP_LENGTH(3), NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), NJS_DECLARE_PROP_LVALUE("BYTES_PER_ELEMENT", njs_value(NJS_NUMBER, 1, 1), 0), }; static const njs_object_init_t njs_typed_array_u8c_constructor_init = { njs_typed_array_u8c_constructor_props, njs_nitems(njs_typed_array_u8c_constructor_props), }; static const njs_object_prop_t njs_typed_array_u8c_prototype_properties[] = { NJS_DECLARE_PROP_HANDLER("constructor", njs_object_prototype_create_constructor, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_LVALUE("BYTES_PER_ELEMENT", njs_value(NJS_NUMBER, 1, 1), 0), }; static const njs_object_init_t njs_typed_array_u8c_prototype_init = { njs_typed_array_u8c_prototype_properties, njs_nitems(njs_typed_array_u8c_prototype_properties), }; const njs_object_type_init_t njs_typed_array_u8clamped_type_init = { .constructor = njs_native_ctor(njs_typed_array_constructor, 3, NJS_OBJ_TYPE_UINT8_CLAMPED_ARRAY), .prototype_props = &njs_typed_array_u8c_prototype_init, .constructor_props = &njs_typed_array_u8c_constructor_init, .prototype_value = { .object = { .type = NJS_OBJECT } }, }; static const njs_object_prop_t njs_typed_array_i8_constructor_props[] = { NJS_DECLARE_PROP_LENGTH(3), NJS_DECLARE_PROP_NAME("Int8Array"), NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), NJS_DECLARE_PROP_LVALUE("BYTES_PER_ELEMENT", njs_value(NJS_NUMBER, 1, 1), 0), }; static const njs_object_init_t njs_typed_array_i8_constructor_init = { njs_typed_array_i8_constructor_props, njs_nitems(njs_typed_array_i8_constructor_props), }; static const njs_object_prop_t njs_typed_array_i8_prototype_properties[] = { NJS_DECLARE_PROP_HANDLER("constructor", njs_object_prototype_create_constructor, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_LVALUE("BYTES_PER_ELEMENT", njs_value(NJS_NUMBER, 1, 1), 0), }; static const njs_object_init_t njs_typed_array_i8_prototype_init = { njs_typed_array_i8_prototype_properties, njs_nitems(njs_typed_array_i8_prototype_properties), }; const njs_object_type_init_t njs_typed_array_i8_type_init = { .constructor = njs_native_ctor(njs_typed_array_constructor, 3, NJS_OBJ_TYPE_INT8_ARRAY), .prototype_props = &njs_typed_array_i8_prototype_init, .constructor_props = &njs_typed_array_i8_constructor_init, .prototype_value = { .object = { .type = NJS_OBJECT } }, }; static const njs_object_prop_t njs_typed_array_u16_constructor_props[] = { NJS_DECLARE_PROP_LENGTH(3), NJS_DECLARE_PROP_NAME("Uint16Array"), NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), NJS_DECLARE_PROP_LVALUE("BYTES_PER_ELEMENT", njs_value(NJS_NUMBER, 1, 2), 0), }; static const njs_object_init_t njs_typed_array_u16_constructor_init = { njs_typed_array_u16_constructor_props, njs_nitems(njs_typed_array_u16_constructor_props), }; static const njs_object_prop_t njs_typed_array_u16_prototype_properties[] = { NJS_DECLARE_PROP_HANDLER("constructor", njs_object_prototype_create_constructor, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_LVALUE("BYTES_PER_ELEMENT", njs_value(NJS_NUMBER, 1, 2), 0), }; static const njs_object_init_t njs_typed_array_u16_prototype_init = { njs_typed_array_u16_prototype_properties, njs_nitems(njs_typed_array_u16_prototype_properties), }; const njs_object_type_init_t njs_typed_array_u16_type_init = { .constructor = njs_native_ctor(njs_typed_array_constructor, 3, NJS_OBJ_TYPE_UINT16_ARRAY), .prototype_props = &njs_typed_array_u16_prototype_init, .constructor_props = &njs_typed_array_u16_constructor_init, .prototype_value = { .object = { .type = NJS_OBJECT } }, }; static const njs_object_prop_t njs_typed_array_i16_constructor_props[] = { NJS_DECLARE_PROP_LENGTH(3), NJS_DECLARE_PROP_NAME("Int16Array"), NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), NJS_DECLARE_PROP_LVALUE("BYTES_PER_ELEMENT", njs_value(NJS_NUMBER, 1, 2), 0), }; static const njs_object_init_t njs_typed_array_i16_constructor_init = { njs_typed_array_i16_constructor_props, njs_nitems(njs_typed_array_i16_constructor_props), }; static const njs_object_prop_t njs_typed_array_i16_prototype_properties[] = { NJS_DECLARE_PROP_HANDLER("constructor", njs_object_prototype_create_constructor, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_LVALUE("BYTES_PER_ELEMENT", njs_value(NJS_NUMBER, 1, 2), 0), }; static const njs_object_init_t njs_typed_array_i16_prototype_init = { njs_typed_array_i16_prototype_properties, njs_nitems(njs_typed_array_i16_prototype_properties), }; const njs_object_type_init_t njs_typed_array_i16_type_init = { .constructor = njs_native_ctor(njs_typed_array_constructor, 3, NJS_OBJ_TYPE_INT16_ARRAY), .prototype_props = &njs_typed_array_i16_prototype_init, .constructor_props = &njs_typed_array_i16_constructor_init, .prototype_value = { .object = { .type = NJS_OBJECT } }, }; static const njs_object_prop_t njs_typed_array_u32_constructor_props[] = { NJS_DECLARE_PROP_LENGTH(3), NJS_DECLARE_PROP_NAME("Uint32Array"), NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), NJS_DECLARE_PROP_LVALUE("BYTES_PER_ELEMENT", njs_value(NJS_NUMBER, 1, 4), 0), }; static const njs_object_init_t njs_typed_array_u32_constructor_init = { njs_typed_array_u32_constructor_props, njs_nitems(njs_typed_array_u32_constructor_props), }; static const njs_object_prop_t njs_typed_array_u32_prototype_properties[] = { NJS_DECLARE_PROP_HANDLER("constructor", njs_object_prototype_create_constructor, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_LVALUE("BYTES_PER_ELEMENT", njs_value(NJS_NUMBER, 1, 4), 0), }; static const njs_object_init_t njs_typed_array_u32_prototype_init = { njs_typed_array_u32_prototype_properties, njs_nitems(njs_typed_array_u32_prototype_properties), }; const njs_object_type_init_t njs_typed_array_u32_type_init = { .constructor = njs_native_ctor(njs_typed_array_constructor, 3, NJS_OBJ_TYPE_UINT32_ARRAY), .prototype_props = &njs_typed_array_u32_prototype_init, .constructor_props = &njs_typed_array_u32_constructor_init, .prototype_value = { .object = { .type = NJS_OBJECT } }, }; static const njs_object_prop_t njs_typed_array_i32_constructor_props[] = { NJS_DECLARE_PROP_LENGTH(3), NJS_DECLARE_PROP_NAME("Int32Array"), NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), NJS_DECLARE_PROP_LVALUE("BYTES_PER_ELEMENT", njs_value(NJS_NUMBER, 1, 4), 0), }; static const njs_object_init_t njs_typed_array_i32_constructor_init = { njs_typed_array_i32_constructor_props, njs_nitems(njs_typed_array_i32_constructor_props), }; static const njs_object_prop_t njs_typed_array_i32_prototype_properties[] = { NJS_DECLARE_PROP_HANDLER("constructor", njs_object_prototype_create_constructor, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_LVALUE("BYTES_PER_ELEMENT", njs_value(NJS_NUMBER, 1, 4), 0), }; static const njs_object_init_t njs_typed_array_i32_prototype_init = { njs_typed_array_i32_prototype_properties, njs_nitems(njs_typed_array_i32_prototype_properties), }; const njs_object_type_init_t njs_typed_array_i32_type_init = { .constructor = njs_native_ctor(njs_typed_array_constructor, 3, NJS_OBJ_TYPE_INT32_ARRAY), .prototype_props = &njs_typed_array_i32_prototype_init, .constructor_props = &njs_typed_array_i32_constructor_init, .prototype_value = { .object = { .type = NJS_OBJECT } }, }; static const njs_object_prop_t njs_typed_array_f32_constructor_props[] = { NJS_DECLARE_PROP_LENGTH(3), NJS_DECLARE_PROP_NAME("Float32Array"), NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), NJS_DECLARE_PROP_LVALUE("BYTES_PER_ELEMENT", njs_value(NJS_NUMBER, 1, 4), 0), }; static const njs_object_init_t njs_typed_array_f32_constructor_init = { njs_typed_array_f32_constructor_props, njs_nitems(njs_typed_array_f32_constructor_props), }; static const njs_object_prop_t njs_typed_array_f32_prototype_properties[] = { NJS_DECLARE_PROP_HANDLER("constructor", njs_object_prototype_create_constructor, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_LVALUE("BYTES_PER_ELEMENT", njs_value(NJS_NUMBER, 1, 4), 0), }; static const njs_object_init_t njs_typed_array_f32_prototype_init = { njs_typed_array_f32_prototype_properties, njs_nitems(njs_typed_array_f32_prototype_properties), }; const njs_object_type_init_t njs_typed_array_f32_type_init = { .constructor = njs_native_ctor(njs_typed_array_constructor, 3, NJS_OBJ_TYPE_FLOAT32_ARRAY), .prototype_props = &njs_typed_array_f32_prototype_init, .constructor_props = &njs_typed_array_f32_constructor_init, .prototype_value = { .object = { .type = NJS_OBJECT } }, }; static const njs_object_prop_t njs_typed_array_f64_constructor_props[] = { NJS_DECLARE_PROP_LENGTH(3), NJS_DECLARE_PROP_NAME("Float64Array"), NJS_DECLARE_PROP_HANDLER("prototype", njs_object_prototype_create, 0, 0, 0), NJS_DECLARE_PROP_LVALUE("BYTES_PER_ELEMENT", njs_value(NJS_NUMBER, 1, 8), 0), }; static const njs_object_init_t njs_typed_array_f64_constructor_init = { njs_typed_array_f64_constructor_props, njs_nitems(njs_typed_array_f64_constructor_props), }; static const njs_object_prop_t njs_typed_array_f64_prototype_properties[] = { NJS_DECLARE_PROP_HANDLER("constructor", njs_object_prototype_create_constructor, 0, 0, NJS_OBJECT_PROP_VALUE_CW), NJS_DECLARE_PROP_LVALUE("BYTES_PER_ELEMENT", njs_value(NJS_NUMBER, 1, 8), 0), }; static const njs_object_init_t njs_typed_array_f64_prototype_init = { njs_typed_array_f64_prototype_properties, njs_nitems(njs_typed_array_f64_prototype_properties), }; const njs_object_type_init_t njs_typed_array_f64_type_init = { .constructor = njs_native_ctor(njs_typed_array_constructor, 3, NJS_OBJ_TYPE_FLOAT64_ARRAY), .prototype_props = &njs_typed_array_f64_prototype_init, .constructor_props = &njs_typed_array_f64_constructor_init, .prototype_value = { .object = { .type = NJS_OBJECT } }, }; njs-0.8.9/src/njs_typed_array.h000066400000000000000000000065161474132077100164750ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_TYPED_ARRAY_H_INCLUDED_ #define _NJS_TYPED_ARRAY_H_INCLUDED_ njs_typed_array_t *njs_typed_array_alloc(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_bool_t zeroing, njs_object_type_t type); njs_array_buffer_t *njs_typed_array_writable(njs_vm_t *vm, njs_typed_array_t *array); njs_int_t njs_typed_array_set_value(njs_vm_t *vm, njs_typed_array_t *array, uint32_t index, njs_value_t *setval); void njs_typed_array_to_chain(njs_vm_t *vm, njs_chb_t *chain, njs_typed_array_t *array, njs_value_t *sep); njs_int_t njs_typed_array_prototype_slice(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t copy, njs_value_t *retval); njs_inline unsigned njs_typed_array_element_size(njs_object_type_t type) { switch (type) { case NJS_OBJ_TYPE_DATA_VIEW: case NJS_OBJ_TYPE_UINT8_ARRAY: case NJS_OBJ_TYPE_UINT8_CLAMPED_ARRAY: case NJS_OBJ_TYPE_INT8_ARRAY: return 1; case NJS_OBJ_TYPE_UINT16_ARRAY: case NJS_OBJ_TYPE_INT16_ARRAY: return 2; case NJS_OBJ_TYPE_UINT32_ARRAY: case NJS_OBJ_TYPE_INT32_ARRAY: case NJS_OBJ_TYPE_FLOAT32_ARRAY: return 4; default: /* NJS_OBJ_TYPE_FLOAT64_ARRAY. */ return 8; } } njs_inline uint32_t njs_typed_array_length(const njs_typed_array_t *array) { return array->byte_length / njs_typed_array_element_size(array->type); } njs_inline uint32_t njs_typed_array_offset(const njs_typed_array_t *array) { return array->offset * njs_typed_array_element_size(array->type); } njs_inline u_char * njs_typed_array_start(const njs_typed_array_t *array) { return &array->buffer->u.u8[njs_typed_array_offset(array)]; } njs_inline double njs_typed_array_prop(const njs_typed_array_t *array, uint32_t index) { njs_array_buffer_t *buffer; index += array->offset; buffer = array->buffer; switch (array->type) { case NJS_OBJ_TYPE_UINT8_ARRAY: case NJS_OBJ_TYPE_UINT8_CLAMPED_ARRAY: return buffer->u.u8[index]; case NJS_OBJ_TYPE_INT8_ARRAY: return buffer->u.i8[index]; case NJS_OBJ_TYPE_UINT16_ARRAY: return buffer->u.u16[index]; case NJS_OBJ_TYPE_INT16_ARRAY: return buffer->u.i16[index]; case NJS_OBJ_TYPE_UINT32_ARRAY: return buffer->u.u32[index]; case NJS_OBJ_TYPE_INT32_ARRAY: return buffer->u.i32[index]; case NJS_OBJ_TYPE_FLOAT32_ARRAY: return buffer->u.f32[index]; default: /* NJS_OBJ_TYPE_FLOAT64_ARRAY. */ return buffer->u.f64[index]; } } extern const njs_object_type_init_t njs_typed_array_type_init; extern const njs_object_type_init_t njs_data_view_type_init; extern const njs_object_type_init_t njs_typed_array_u8_type_init; extern const njs_object_type_init_t njs_typed_array_u8clamped_type_init; extern const njs_object_type_init_t njs_typed_array_i8_type_init; extern const njs_object_type_init_t njs_typed_array_u16_type_init; extern const njs_object_type_init_t njs_typed_array_i16_type_init; extern const njs_object_type_init_t njs_typed_array_u32_type_init; extern const njs_object_type_init_t njs_typed_array_i32_type_init; extern const njs_object_type_init_t njs_typed_array_f32_type_init; extern const njs_object_type_init_t njs_typed_array_f64_type_init; #endif /* _NJS_TYPED_ARRAY_H_INCLUDED_ */ njs-0.8.9/src/njs_types.h000066400000000000000000000060021474132077100153040ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_TYPES_H_INCLUDED_ #define _NJS_TYPES_H_INCLUDED_ #define NJS_OK 0 #define NJS_ERROR (-1) #define NJS_AGAIN (-2) #define NJS_DECLINED (-3) #define NJS_DONE (-4) /* * off_t is 32 bit on Linux, Solaris and HP-UX by default. * Must be before . */ #define _FILE_OFFSET_BITS 64 /* u_char, u_int, int8_t, int32_t, int64_t, size_t, off_t. */ #include #include #if (__LP64__) #define NJS_64BIT 1 #define NJS_PTR_SIZE 8 #else #define NJS_64BIT 0 #define NJS_PTR_SIZE 4 #endif /* * njs_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. njs_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 NJS_INT_T_SIZE 4 typedef int njs_int_t; typedef u_int njs_uint_t; #else #define NJS_INT_T_SIZE NJS_PTR_SIZE typedef intptr_t njs_int_t; typedef uintptr_t njs_uint_t; #endif #if (NJS_HAVE_UNSIGNED_INT128) typedef unsigned __int128 njs_uint128_t; #endif #if (NJS_INT_T_SIZE == 8) #define NJS_INT_T_LEN NJS_INT64_T_LEN #define NJS_INT_T_HEXLEN NJS_INT64_T_HEXLEN #define NJS_INT_T_MAX NJS_INT64_T_MAX #else #define NJS_INT_T_LEN NJS_INT32_T_LEN #define NJS_INT_T_HEXLEN NJS_INT32_T_HEXLEN #define NJS_INT_T_MAX NJS_INT32_T_MAX #endif typedef njs_uint_t njs_bool_t; typedef int njs_err_t; /* * njs_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 (NJS_WINDOWS) /* Windows defines off_t as a 32-bit "long". */ typedef __int64 njs_off_t; #else typedef off_t njs_off_t; #endif /* * njs_time_t corresponds to OS's time_t, time in seconds. njs_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 (NJS_QNX) /* Y2038 fix: "typedef int64_t njs_time_t". */ typedef int32_t njs_time_t; #else /* Y2038, if time_t is 32-bit integer. */ typedef time_t njs_time_t; #endif typedef pid_t njs_pid_t; #define NJS_INT32_T_LEN njs_length("-2147483648") #define NJS_INT64_T_LEN njs_length("-9223372036854775808") #define NJS_DOUBLE_LEN (1 + DBL_MAX_10_EXP) #define NJS_MAX_ERROR_STR 2048 #endif /* _NJS_TYPES_H_INCLUDED_ */ njs-0.8.9/src/njs_unicode.h000066400000000000000000000020531474132077100155700ustar00rootroot00000000000000 /* * Copyright (C) Alexander Borisov * Copyright (C) NGINX, Inc. */ #ifndef _NJS_UNICODE_H_INCLUDED_ #define _NJS_UNICODE_H_INCLUDED_ enum { NJS_UNICODE_BOM = 0xFEFF, NJS_UNICODE_REPLACEMENT = 0xFFFD, NJS_UNICODE_MAX_CODEPOINT = 0x10FFFF, NJS_UNICODE_ERROR = 0x1FFFFF, NJS_UNICODE_CONTINUE = 0x2FFFFF }; typedef struct { uint32_t codepoint; unsigned need; u_char lower; u_char upper; } njs_unicode_decode_t; #define njs_surrogate_leading(cp) \ (((unsigned) (cp) - 0xd800) <= 0xdbff - 0xd800) #define njs_surrogate_trailing(cp) \ (((unsigned) (cp) - 0xdc00) <= 0xdfff - 0xdc00) #define njs_surrogate_any(cp) \ (((unsigned) (cp) - 0xd800) <= 0xdfff - 0xd800) #define njs_surrogate_pair(high, low) \ (0x10000 + (((high) - 0xd800) << 10) + ((low) - 0xdc00)) #endif /* _NJS_UNICODE_H_INCLUDED_ */ njs-0.8.9/src/njs_unicode_lower_case.h000066400000000000000000001551031474132077100200000ustar00rootroot00000000000000 /* * 35 128-bytes blocks, 979 pointers. * 21354 bytes on 32-bit platforms, 25266 bytes on 64-bit platforms. */ #define NJS_UNICODE_MAX_LOWER_CASE 0x1e921 #define NJS_UNICODE_BLOCK_SIZE 128 static const uint32_t njs_unicode_lower_case_block_000[128] njs_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 njs_unicode_lower_case_block_001[128] njs_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, 0x000b5, 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 njs_unicode_lower_case_block_002[128] njs_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, 0x00069, 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, 0x0017f, }; static const uint32_t njs_unicode_lower_case_block_003[128] njs_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 njs_unicode_lower_case_block_004[128] njs_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 njs_unicode_lower_case_block_006[128] njs_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, 0x00345, 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, 0x003f3, }; static const uint32_t njs_unicode_lower_case_block_007[128] njs_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, 0x003c2, 0x003c3, 0x003c4, 0x003c5, 0x003c6, 0x003c7, 0x003c8, 0x003c9, 0x003ca, 0x003cb, 0x003cc, 0x003cd, 0x003ce, 0x003d7, 0x003d0, 0x003d1, 0x003d2, 0x003d3, 0x003d4, 0x003d5, 0x003d6, 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, 0x003f0, 0x003f1, 0x003f2, 0x003f3, 0x003b8, 0x003f5, 0x003f6, 0x003f8, 0x003f8, 0x003f2, 0x003fb, 0x003fb, 0x003fc, 0x0037b, 0x0037c, 0x0037d, }; static const uint32_t njs_unicode_lower_case_block_008[128] njs_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 njs_unicode_lower_case_block_009[128] njs_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 njs_unicode_lower_case_block_00a[128] njs_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, 0x00529, 0x00529, 0x0052b, 0x0052b, 0x0052d, 0x0052d, 0x0052f, 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 njs_unicode_lower_case_block_021[128] njs_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 njs_unicode_lower_case_block_027[128] njs_aligned(64) = { 0x01380, 0x01381, 0x01382, 0x01383, 0x01384, 0x01385, 0x01386, 0x01387, 0x01388, 0x01389, 0x0138a, 0x0138b, 0x0138c, 0x0138d, 0x0138e, 0x0138f, 0x01390, 0x01391, 0x01392, 0x01393, 0x01394, 0x01395, 0x01396, 0x01397, 0x01398, 0x01399, 0x0139a, 0x0139b, 0x0139c, 0x0139d, 0x0139e, 0x0139f, 0x0ab70, 0x0ab71, 0x0ab72, 0x0ab73, 0x0ab74, 0x0ab75, 0x0ab76, 0x0ab77, 0x0ab78, 0x0ab79, 0x0ab7a, 0x0ab7b, 0x0ab7c, 0x0ab7d, 0x0ab7e, 0x0ab7f, 0x0ab80, 0x0ab81, 0x0ab82, 0x0ab83, 0x0ab84, 0x0ab85, 0x0ab86, 0x0ab87, 0x0ab88, 0x0ab89, 0x0ab8a, 0x0ab8b, 0x0ab8c, 0x0ab8d, 0x0ab8e, 0x0ab8f, 0x0ab90, 0x0ab91, 0x0ab92, 0x0ab93, 0x0ab94, 0x0ab95, 0x0ab96, 0x0ab97, 0x0ab98, 0x0ab99, 0x0ab9a, 0x0ab9b, 0x0ab9c, 0x0ab9d, 0x0ab9e, 0x0ab9f, 0x0aba0, 0x0aba1, 0x0aba2, 0x0aba3, 0x0aba4, 0x0aba5, 0x0aba6, 0x0aba7, 0x0aba8, 0x0aba9, 0x0abaa, 0x0abab, 0x0abac, 0x0abad, 0x0abae, 0x0abaf, 0x0abb0, 0x0abb1, 0x0abb2, 0x0abb3, 0x0abb4, 0x0abb5, 0x0abb6, 0x0abb7, 0x0abb8, 0x0abb9, 0x0abba, 0x0abbb, 0x0abbc, 0x0abbd, 0x0abbe, 0x0abbf, 0x013f8, 0x013f9, 0x013fa, 0x013fb, 0x013fc, 0x013fd, 0x013f6, 0x013f7, 0x013f8, 0x013f9, 0x013fa, 0x013fb, 0x013fc, 0x013fd, 0x013fe, 0x013ff, }; static const uint32_t njs_unicode_lower_case_block_039[128] njs_aligned(64) = { 0x01c80, 0x01c81, 0x01c82, 0x01c83, 0x01c84, 0x01c85, 0x01c86, 0x01c87, 0x01c88, 0x01c89, 0x01c8a, 0x01c8b, 0x01c8c, 0x01c8d, 0x01c8e, 0x01c8f, 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, 0x01cbb, 0x01cbc, 0x010fd, 0x010fe, 0x010ff, 0x01cc0, 0x01cc1, 0x01cc2, 0x01cc3, 0x01cc4, 0x01cc5, 0x01cc6, 0x01cc7, 0x01cc8, 0x01cc9, 0x01cca, 0x01ccb, 0x01ccc, 0x01ccd, 0x01cce, 0x01ccf, 0x01cd0, 0x01cd1, 0x01cd2, 0x01cd3, 0x01cd4, 0x01cd5, 0x01cd6, 0x01cd7, 0x01cd8, 0x01cd9, 0x01cda, 0x01cdb, 0x01cdc, 0x01cdd, 0x01cde, 0x01cdf, 0x01ce0, 0x01ce1, 0x01ce2, 0x01ce3, 0x01ce4, 0x01ce5, 0x01ce6, 0x01ce7, 0x01ce8, 0x01ce9, 0x01cea, 0x01ceb, 0x01cec, 0x01ced, 0x01cee, 0x01cef, 0x01cf0, 0x01cf1, 0x01cf2, 0x01cf3, 0x01cf4, 0x01cf5, 0x01cf6, 0x01cf7, 0x01cf8, 0x01cf9, 0x01cfa, 0x01cfb, 0x01cfc, 0x01cfd, 0x01cfe, 0x01cff, }; static const uint32_t njs_unicode_lower_case_block_03c[128] njs_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 njs_unicode_lower_case_block_03d[128] njs_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, 0x01e9b, 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 njs_unicode_lower_case_block_03e[128] njs_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 njs_unicode_lower_case_block_03f[128] njs_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, 0x01fbe, 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 njs_unicode_lower_case_block_042[128] njs_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 njs_unicode_lower_case_block_043[128] njs_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 njs_unicode_lower_case_block_049[128] njs_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 njs_unicode_lower_case_block_058[128] njs_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, 0x02c5f, 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 njs_unicode_lower_case_block_059[128] njs_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 njs_unicode_lower_case_block_14c[128] njs_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 njs_unicode_lower_case_block_14d[128] njs_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, 0x0a699, 0x0a699, 0x0a69b, 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 njs_unicode_lower_case_block_14e[128] njs_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 njs_unicode_lower_case_block_14f[128] njs_aligned(64) = { 0x0a781, 0x0a781, 0x0a783, 0x0a783, 0x0a785, 0x0a785, 0x0a787, 0x0a787, 0x0a788, 0x0a789, 0x0a78a, 0x0a78c, 0x0a78c, 0x00265, 0x0a78e, 0x0a78f, 0x0a791, 0x0a791, 0x0a793, 0x0a793, 0x0a794, 0x0a795, 0x0a797, 0x0a797, 0x0a799, 0x0a799, 0x0a79b, 0x0a79b, 0x0a79d, 0x0a79d, 0x0a79f, 0x0a79f, 0x0a7a1, 0x0a7a1, 0x0a7a3, 0x0a7a3, 0x0a7a5, 0x0a7a5, 0x0a7a7, 0x0a7a7, 0x0a7a9, 0x0a7a9, 0x00266, 0x0025c, 0x00261, 0x0026c, 0x0026a, 0x0a7af, 0x0029e, 0x00287, 0x0029d, 0x0ab53, 0x0a7b5, 0x0a7b5, 0x0a7b7, 0x0a7b7, 0x0a7b9, 0x0a7b9, 0x0a7bb, 0x0a7bb, 0x0a7bd, 0x0a7bd, 0x0a7bf, 0x0a7bf, 0x0a7c1, 0x0a7c1, 0x0a7c3, 0x0a7c3, 0x0a794, 0x00282, 0x01d8e, 0x0a7c8, 0x0a7c8, 0x0a7ca, 0x0a7ca, 0x0a7cb, 0x0a7cc, 0x0a7cd, 0x0a7ce, 0x0a7cf, 0x0a7d1, 0x0a7d1, 0x0a7d2, 0x0a7d3, 0x0a7d4, 0x0a7d5, 0x0a7d7, 0x0a7d7, 0x0a7d9, 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, 0x0a7f6, 0x0a7f6, 0x0a7f7, 0x0a7f8, 0x0a7f9, 0x0a7fa, 0x0a7fb, 0x0a7fc, 0x0a7fd, 0x0a7fe, 0x0a7ff, }; static const uint32_t njs_unicode_lower_case_block_1fe[128] njs_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 njs_unicode_lower_case_block_208[128] njs_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, 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, 0x10450, 0x10451, 0x10452, 0x10453, 0x10454, 0x10455, 0x10456, 0x10457, 0x10458, 0x10459, 0x1045a, 0x1045b, 0x1045c, 0x1045d, 0x1045e, 0x1045f, 0x10460, 0x10461, 0x10462, 0x10463, 0x10464, 0x10465, 0x10466, 0x10467, 0x10468, 0x10469, 0x1046a, 0x1046b, 0x1046c, 0x1046d, 0x1046e, 0x1046f, 0x10470, 0x10471, 0x10472, 0x10473, 0x10474, 0x10475, 0x10476, 0x10477, 0x10478, 0x10479, 0x1047a, 0x1047b, 0x1047c, 0x1047d, 0x1047e, 0x1047f, }; static const uint32_t njs_unicode_lower_case_block_209[128] njs_aligned(64) = { 0x10480, 0x10481, 0x10482, 0x10483, 0x10484, 0x10485, 0x10486, 0x10487, 0x10488, 0x10489, 0x1048a, 0x1048b, 0x1048c, 0x1048d, 0x1048e, 0x1048f, 0x10490, 0x10491, 0x10492, 0x10493, 0x10494, 0x10495, 0x10496, 0x10497, 0x10498, 0x10499, 0x1049a, 0x1049b, 0x1049c, 0x1049d, 0x1049e, 0x1049f, 0x104a0, 0x104a1, 0x104a2, 0x104a3, 0x104a4, 0x104a5, 0x104a6, 0x104a7, 0x104a8, 0x104a9, 0x104aa, 0x104ab, 0x104ac, 0x104ad, 0x104ae, 0x104af, 0x104d8, 0x104d9, 0x104da, 0x104db, 0x104dc, 0x104dd, 0x104de, 0x104df, 0x104e0, 0x104e1, 0x104e2, 0x104e3, 0x104e4, 0x104e5, 0x104e6, 0x104e7, 0x104e8, 0x104e9, 0x104ea, 0x104eb, 0x104ec, 0x104ed, 0x104ee, 0x104ef, 0x104f0, 0x104f1, 0x104f2, 0x104f3, 0x104f4, 0x104f5, 0x104f6, 0x104f7, 0x104f8, 0x104f9, 0x104fa, 0x104fb, 0x104d4, 0x104d5, 0x104d6, 0x104d7, 0x104d8, 0x104d9, 0x104da, 0x104db, 0x104dc, 0x104dd, 0x104de, 0x104df, 0x104e0, 0x104e1, 0x104e2, 0x104e3, 0x104e4, 0x104e5, 0x104e6, 0x104e7, 0x104e8, 0x104e9, 0x104ea, 0x104eb, 0x104ec, 0x104ed, 0x104ee, 0x104ef, 0x104f0, 0x104f1, 0x104f2, 0x104f3, 0x104f4, 0x104f5, 0x104f6, 0x104f7, 0x104f8, 0x104f9, 0x104fa, 0x104fb, 0x104fc, 0x104fd, 0x104fe, 0x104ff, }; static const uint32_t njs_unicode_lower_case_block_20a[128] njs_aligned(64) = { 0x10500, 0x10501, 0x10502, 0x10503, 0x10504, 0x10505, 0x10506, 0x10507, 0x10508, 0x10509, 0x1050a, 0x1050b, 0x1050c, 0x1050d, 0x1050e, 0x1050f, 0x10510, 0x10511, 0x10512, 0x10513, 0x10514, 0x10515, 0x10516, 0x10517, 0x10518, 0x10519, 0x1051a, 0x1051b, 0x1051c, 0x1051d, 0x1051e, 0x1051f, 0x10520, 0x10521, 0x10522, 0x10523, 0x10524, 0x10525, 0x10526, 0x10527, 0x10528, 0x10529, 0x1052a, 0x1052b, 0x1052c, 0x1052d, 0x1052e, 0x1052f, 0x10530, 0x10531, 0x10532, 0x10533, 0x10534, 0x10535, 0x10536, 0x10537, 0x10538, 0x10539, 0x1053a, 0x1053b, 0x1053c, 0x1053d, 0x1053e, 0x1053f, 0x10540, 0x10541, 0x10542, 0x10543, 0x10544, 0x10545, 0x10546, 0x10547, 0x10548, 0x10549, 0x1054a, 0x1054b, 0x1054c, 0x1054d, 0x1054e, 0x1054f, 0x10550, 0x10551, 0x10552, 0x10553, 0x10554, 0x10555, 0x10556, 0x10557, 0x10558, 0x10559, 0x1055a, 0x1055b, 0x1055c, 0x1055d, 0x1055e, 0x1055f, 0x10560, 0x10561, 0x10562, 0x10563, 0x10564, 0x10565, 0x10566, 0x10567, 0x10568, 0x10569, 0x1056a, 0x1056b, 0x1056c, 0x1056d, 0x1056e, 0x1056f, 0x10597, 0x10598, 0x10599, 0x1059a, 0x1059b, 0x1059c, 0x1059d, 0x1059e, 0x1059f, 0x105a0, 0x105a1, 0x1057b, 0x105a3, 0x105a4, 0x105a5, 0x105a6, }; static const uint32_t njs_unicode_lower_case_block_20b[128] njs_aligned(64) = { 0x105a7, 0x105a8, 0x105a9, 0x105aa, 0x105ab, 0x105ac, 0x105ad, 0x105ae, 0x105af, 0x105b0, 0x105b1, 0x1058b, 0x105b3, 0x105b4, 0x105b5, 0x105b6, 0x105b7, 0x105b8, 0x105b9, 0x10593, 0x105bb, 0x105bc, 0x10596, 0x10597, 0x10598, 0x10599, 0x1059a, 0x1059b, 0x1059c, 0x1059d, 0x1059e, 0x1059f, 0x105a0, 0x105a1, 0x105a2, 0x105a3, 0x105a4, 0x105a5, 0x105a6, 0x105a7, 0x105a8, 0x105a9, 0x105aa, 0x105ab, 0x105ac, 0x105ad, 0x105ae, 0x105af, 0x105b0, 0x105b1, 0x105b2, 0x105b3, 0x105b4, 0x105b5, 0x105b6, 0x105b7, 0x105b8, 0x105b9, 0x105ba, 0x105bb, 0x105bc, 0x105bd, 0x105be, 0x105bf, 0x105c0, 0x105c1, 0x105c2, 0x105c3, 0x105c4, 0x105c5, 0x105c6, 0x105c7, 0x105c8, 0x105c9, 0x105ca, 0x105cb, 0x105cc, 0x105cd, 0x105ce, 0x105cf, 0x105d0, 0x105d1, 0x105d2, 0x105d3, 0x105d4, 0x105d5, 0x105d6, 0x105d7, 0x105d8, 0x105d9, 0x105da, 0x105db, 0x105dc, 0x105dd, 0x105de, 0x105df, 0x105e0, 0x105e1, 0x105e2, 0x105e3, 0x105e4, 0x105e5, 0x105e6, 0x105e7, 0x105e8, 0x105e9, 0x105ea, 0x105eb, 0x105ec, 0x105ed, 0x105ee, 0x105ef, 0x105f0, 0x105f1, 0x105f2, 0x105f3, 0x105f4, 0x105f5, 0x105f6, 0x105f7, 0x105f8, 0x105f9, 0x105fa, 0x105fb, 0x105fc, 0x105fd, 0x105fe, 0x105ff, }; static const uint32_t njs_unicode_lower_case_block_219[128] njs_aligned(64) = { 0x10cc0, 0x10cc1, 0x10cc2, 0x10cc3, 0x10cc4, 0x10cc5, 0x10cc6, 0x10cc7, 0x10cc8, 0x10cc9, 0x10cca, 0x10ccb, 0x10ccc, 0x10ccd, 0x10cce, 0x10ccf, 0x10cd0, 0x10cd1, 0x10cd2, 0x10cd3, 0x10cd4, 0x10cd5, 0x10cd6, 0x10cd7, 0x10cd8, 0x10cd9, 0x10cda, 0x10cdb, 0x10cdc, 0x10cdd, 0x10cde, 0x10cdf, 0x10ce0, 0x10ce1, 0x10ce2, 0x10ce3, 0x10ce4, 0x10ce5, 0x10ce6, 0x10ce7, 0x10ce8, 0x10ce9, 0x10cea, 0x10ceb, 0x10cec, 0x10ced, 0x10cee, 0x10cef, 0x10cf0, 0x10cf1, 0x10cf2, 0x10cb3, 0x10cb4, 0x10cb5, 0x10cb6, 0x10cb7, 0x10cb8, 0x10cb9, 0x10cba, 0x10cbb, 0x10cbc, 0x10cbd, 0x10cbe, 0x10cbf, 0x10cc0, 0x10cc1, 0x10cc2, 0x10cc3, 0x10cc4, 0x10cc5, 0x10cc6, 0x10cc7, 0x10cc8, 0x10cc9, 0x10cca, 0x10ccb, 0x10ccc, 0x10ccd, 0x10cce, 0x10ccf, 0x10cd0, 0x10cd1, 0x10cd2, 0x10cd3, 0x10cd4, 0x10cd5, 0x10cd6, 0x10cd7, 0x10cd8, 0x10cd9, 0x10cda, 0x10cdb, 0x10cdc, 0x10cdd, 0x10cde, 0x10cdf, 0x10ce0, 0x10ce1, 0x10ce2, 0x10ce3, 0x10ce4, 0x10ce5, 0x10ce6, 0x10ce7, 0x10ce8, 0x10ce9, 0x10cea, 0x10ceb, 0x10cec, 0x10ced, 0x10cee, 0x10cef, 0x10cf0, 0x10cf1, 0x10cf2, 0x10cf3, 0x10cf4, 0x10cf5, 0x10cf6, 0x10cf7, 0x10cf8, 0x10cf9, 0x10cfa, 0x10cfb, 0x10cfc, 0x10cfd, 0x10cfe, 0x10cff, }; static const uint32_t njs_unicode_lower_case_block_231[128] njs_aligned(64) = { 0x11880, 0x11881, 0x11882, 0x11883, 0x11884, 0x11885, 0x11886, 0x11887, 0x11888, 0x11889, 0x1188a, 0x1188b, 0x1188c, 0x1188d, 0x1188e, 0x1188f, 0x11890, 0x11891, 0x11892, 0x11893, 0x11894, 0x11895, 0x11896, 0x11897, 0x11898, 0x11899, 0x1189a, 0x1189b, 0x1189c, 0x1189d, 0x1189e, 0x1189f, 0x118c0, 0x118c1, 0x118c2, 0x118c3, 0x118c4, 0x118c5, 0x118c6, 0x118c7, 0x118c8, 0x118c9, 0x118ca, 0x118cb, 0x118cc, 0x118cd, 0x118ce, 0x118cf, 0x118d0, 0x118d1, 0x118d2, 0x118d3, 0x118d4, 0x118d5, 0x118d6, 0x118d7, 0x118d8, 0x118d9, 0x118da, 0x118db, 0x118dc, 0x118dd, 0x118de, 0x118df, 0x118c0, 0x118c1, 0x118c2, 0x118c3, 0x118c4, 0x118c5, 0x118c6, 0x118c7, 0x118c8, 0x118c9, 0x118ca, 0x118cb, 0x118cc, 0x118cd, 0x118ce, 0x118cf, 0x118d0, 0x118d1, 0x118d2, 0x118d3, 0x118d4, 0x118d5, 0x118d6, 0x118d7, 0x118d8, 0x118d9, 0x118da, 0x118db, 0x118dc, 0x118dd, 0x118de, 0x118df, 0x118e0, 0x118e1, 0x118e2, 0x118e3, 0x118e4, 0x118e5, 0x118e6, 0x118e7, 0x118e8, 0x118e9, 0x118ea, 0x118eb, 0x118ec, 0x118ed, 0x118ee, 0x118ef, 0x118f0, 0x118f1, 0x118f2, 0x118f3, 0x118f4, 0x118f5, 0x118f6, 0x118f7, 0x118f8, 0x118f9, 0x118fa, 0x118fb, 0x118fc, 0x118fd, 0x118fe, 0x118ff, }; static const uint32_t njs_unicode_lower_case_block_2dc[128] njs_aligned(64) = { 0x16e00, 0x16e01, 0x16e02, 0x16e03, 0x16e04, 0x16e05, 0x16e06, 0x16e07, 0x16e08, 0x16e09, 0x16e0a, 0x16e0b, 0x16e0c, 0x16e0d, 0x16e0e, 0x16e0f, 0x16e10, 0x16e11, 0x16e12, 0x16e13, 0x16e14, 0x16e15, 0x16e16, 0x16e17, 0x16e18, 0x16e19, 0x16e1a, 0x16e1b, 0x16e1c, 0x16e1d, 0x16e1e, 0x16e1f, 0x16e20, 0x16e21, 0x16e22, 0x16e23, 0x16e24, 0x16e25, 0x16e26, 0x16e27, 0x16e28, 0x16e29, 0x16e2a, 0x16e2b, 0x16e2c, 0x16e2d, 0x16e2e, 0x16e2f, 0x16e30, 0x16e31, 0x16e32, 0x16e33, 0x16e34, 0x16e35, 0x16e36, 0x16e37, 0x16e38, 0x16e39, 0x16e3a, 0x16e3b, 0x16e3c, 0x16e3d, 0x16e3e, 0x16e3f, 0x16e60, 0x16e61, 0x16e62, 0x16e63, 0x16e64, 0x16e65, 0x16e66, 0x16e67, 0x16e68, 0x16e69, 0x16e6a, 0x16e6b, 0x16e6c, 0x16e6d, 0x16e6e, 0x16e6f, 0x16e70, 0x16e71, 0x16e72, 0x16e73, 0x16e74, 0x16e75, 0x16e76, 0x16e77, 0x16e78, 0x16e79, 0x16e7a, 0x16e7b, 0x16e7c, 0x16e7d, 0x16e7e, 0x16e7f, 0x16e60, 0x16e61, 0x16e62, 0x16e63, 0x16e64, 0x16e65, 0x16e66, 0x16e67, 0x16e68, 0x16e69, 0x16e6a, 0x16e6b, 0x16e6c, 0x16e6d, 0x16e6e, 0x16e6f, 0x16e70, 0x16e71, 0x16e72, 0x16e73, 0x16e74, 0x16e75, 0x16e76, 0x16e77, 0x16e78, 0x16e79, 0x16e7a, 0x16e7b, 0x16e7c, 0x16e7d, 0x16e7e, 0x16e7f, }; static const uint32_t njs_unicode_lower_case_block_3d2[34] njs_aligned(64) = { 0x1e922, 0x1e923, 0x1e924, 0x1e925, 0x1e926, 0x1e927, 0x1e928, 0x1e929, 0x1e92a, 0x1e92b, 0x1e92c, 0x1e92d, 0x1e92e, 0x1e92f, 0x1e930, 0x1e931, 0x1e932, 0x1e933, 0x1e934, 0x1e935, 0x1e936, 0x1e937, 0x1e938, 0x1e939, 0x1e93a, 0x1e93b, 0x1e93c, 0x1e93d, 0x1e93e, 0x1e93f, 0x1e940, 0x1e941, 0x1e942, 0x1e943, }; static const uint32_t *njs_unicode_lower_case_blocks[] njs_aligned(64) = { njs_unicode_lower_case_block_000, njs_unicode_lower_case_block_001, njs_unicode_lower_case_block_002, njs_unicode_lower_case_block_003, njs_unicode_lower_case_block_004, NULL, njs_unicode_lower_case_block_006, njs_unicode_lower_case_block_007, njs_unicode_lower_case_block_008, njs_unicode_lower_case_block_009, njs_unicode_lower_case_block_00a, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, njs_unicode_lower_case_block_021, NULL, NULL, NULL, NULL, NULL, njs_unicode_lower_case_block_027, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, njs_unicode_lower_case_block_039, NULL, NULL, njs_unicode_lower_case_block_03c, njs_unicode_lower_case_block_03d, njs_unicode_lower_case_block_03e, njs_unicode_lower_case_block_03f, NULL, NULL, njs_unicode_lower_case_block_042, njs_unicode_lower_case_block_043, NULL, NULL, NULL, NULL, NULL, njs_unicode_lower_case_block_049, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, njs_unicode_lower_case_block_058, njs_unicode_lower_case_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, njs_unicode_lower_case_block_14c, njs_unicode_lower_case_block_14d, njs_unicode_lower_case_block_14e, njs_unicode_lower_case_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, njs_unicode_lower_case_block_1fe, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, njs_unicode_lower_case_block_208, njs_unicode_lower_case_block_209, njs_unicode_lower_case_block_20a, njs_unicode_lower_case_block_20b, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, njs_unicode_lower_case_block_219, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, njs_unicode_lower_case_block_231, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, njs_unicode_lower_case_block_2dc, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, njs_unicode_lower_case_block_3d2, }; njs-0.8.9/src/njs_unicode_lower_case.pl000077500000000000000000000043571474132077100201730ustar00rootroot00000000000000#!/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 %lower_case; my %blocks; my $max_block = 0; my $max_lower_case = 0; while (<>) { my @line = split(";", $_); if ($line[13]) { my ($symbol, $folding) = (hex $line[0], hex $line[13]); $lower_case{$symbol} = $folding; $blocks{int($symbol / BLOCK_SIZE)} = 1; if ($max_lower_case < $symbol) { $max_lower_case = $symbol; } } } my $last_block_size = $max_lower_case % 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 NJS_UNICODE_MAX_LOWER_CASE 0x%05x\n\n", $max_lower_case); printf("#define NJS_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("njs_unicode_lower_case_block_%03x[%d]\n" . " njs_aligned(64) =\n" . "{", $block, $block_size); for my $c (0 .. $block_size - 1) { printf "\n " if $c % 8 == 0; my $n = $block * BLOCK_SIZE + $c; if (exists $lower_case{$n}) { printf(" 0x%05x,", $lower_case{$n}); } else { #print " .......,"; printf(" 0x%05x,", $n); } } print "\n};\n\n\n"; } print "static const uint32_t *njs_unicode_lower_case_blocks[]\n" . " njs_aligned(64) =\n" . "{\n"; for my $block (0 .. $max_block) { if (exists($blocks{$block})) { printf(" njs_unicode_lower_case_block_%03x,\n", $block); } else { print " NULL,\n"; } } print "};\n"; njs-0.8.9/src/njs_unicode_upper_case.h000066400000000000000000001753111474132077100200060ustar00rootroot00000000000000 /* * 41 128-bytes blocks, 979 pointers. * 24460 bytes on 32-bit platforms, 28372 bytes on 64-bit platforms. */ #define NJS_UNICODE_MAX_UPPER_CASE 0x1e943 #define NJS_UNICODE_BLOCK_SIZE 128 static const uint32_t njs_unicode_upper_case_block_000[128] njs_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, 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047, 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f, 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, 0x00058, 0x00059, 0x0005a, 0x0005b, 0x0005c, 0x0005d, 0x0005e, 0x0005f, 0x00060, 0x00041, 0x00042, 0x00043, 0x00044, 0x00045, 0x00046, 0x00047, 0x00048, 0x00049, 0x0004a, 0x0004b, 0x0004c, 0x0004d, 0x0004e, 0x0004f, 0x00050, 0x00051, 0x00052, 0x00053, 0x00054, 0x00055, 0x00056, 0x00057, 0x00058, 0x00059, 0x0005a, 0x0007b, 0x0007c, 0x0007d, 0x0007e, 0x0007f, }; static const uint32_t njs_unicode_upper_case_block_001[128] njs_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, 0x0039c, 0x000b6, 0x000b7, 0x000b8, 0x000b9, 0x000ba, 0x000bb, 0x000bc, 0x000bd, 0x000be, 0x000bf, 0x000c0, 0x000c1, 0x000c2, 0x000c3, 0x000c4, 0x000c5, 0x000c6, 0x000c7, 0x000c8, 0x000c9, 0x000ca, 0x000cb, 0x000cc, 0x000cd, 0x000ce, 0x000cf, 0x000d0, 0x000d1, 0x000d2, 0x000d3, 0x000d4, 0x000d5, 0x000d6, 0x000d7, 0x000d8, 0x000d9, 0x000da, 0x000db, 0x000dc, 0x000dd, 0x000de, 0x000df, 0x000c0, 0x000c1, 0x000c2, 0x000c3, 0x000c4, 0x000c5, 0x000c6, 0x000c7, 0x000c8, 0x000c9, 0x000ca, 0x000cb, 0x000cc, 0x000cd, 0x000ce, 0x000cf, 0x000d0, 0x000d1, 0x000d2, 0x000d3, 0x000d4, 0x000d5, 0x000d6, 0x000f7, 0x000d8, 0x000d9, 0x000da, 0x000db, 0x000dc, 0x000dd, 0x000de, 0x00178, }; static const uint32_t njs_unicode_upper_case_block_002[128] njs_aligned(64) = { 0x00100, 0x00100, 0x00102, 0x00102, 0x00104, 0x00104, 0x00106, 0x00106, 0x00108, 0x00108, 0x0010a, 0x0010a, 0x0010c, 0x0010c, 0x0010e, 0x0010e, 0x00110, 0x00110, 0x00112, 0x00112, 0x00114, 0x00114, 0x00116, 0x00116, 0x00118, 0x00118, 0x0011a, 0x0011a, 0x0011c, 0x0011c, 0x0011e, 0x0011e, 0x00120, 0x00120, 0x00122, 0x00122, 0x00124, 0x00124, 0x00126, 0x00126, 0x00128, 0x00128, 0x0012a, 0x0012a, 0x0012c, 0x0012c, 0x0012e, 0x0012e, 0x00130, 0x00049, 0x00132, 0x00132, 0x00134, 0x00134, 0x00136, 0x00136, 0x00138, 0x00139, 0x00139, 0x0013b, 0x0013b, 0x0013d, 0x0013d, 0x0013f, 0x0013f, 0x00141, 0x00141, 0x00143, 0x00143, 0x00145, 0x00145, 0x00147, 0x00147, 0x00149, 0x0014a, 0x0014a, 0x0014c, 0x0014c, 0x0014e, 0x0014e, 0x00150, 0x00150, 0x00152, 0x00152, 0x00154, 0x00154, 0x00156, 0x00156, 0x00158, 0x00158, 0x0015a, 0x0015a, 0x0015c, 0x0015c, 0x0015e, 0x0015e, 0x00160, 0x00160, 0x00162, 0x00162, 0x00164, 0x00164, 0x00166, 0x00166, 0x00168, 0x00168, 0x0016a, 0x0016a, 0x0016c, 0x0016c, 0x0016e, 0x0016e, 0x00170, 0x00170, 0x00172, 0x00172, 0x00174, 0x00174, 0x00176, 0x00176, 0x00178, 0x00179, 0x00179, 0x0017b, 0x0017b, 0x0017d, 0x0017d, 0x00053, }; static const uint32_t njs_unicode_upper_case_block_003[128] njs_aligned(64) = { 0x00243, 0x00181, 0x00182, 0x00182, 0x00184, 0x00184, 0x00186, 0x00187, 0x00187, 0x00189, 0x0018a, 0x0018b, 0x0018b, 0x0018d, 0x0018e, 0x0018f, 0x00190, 0x00191, 0x00191, 0x00193, 0x00194, 0x001f6, 0x00196, 0x00197, 0x00198, 0x00198, 0x0023d, 0x0019b, 0x0019c, 0x0019d, 0x00220, 0x0019f, 0x001a0, 0x001a0, 0x001a2, 0x001a2, 0x001a4, 0x001a4, 0x001a6, 0x001a7, 0x001a7, 0x001a9, 0x001aa, 0x001ab, 0x001ac, 0x001ac, 0x001ae, 0x001af, 0x001af, 0x001b1, 0x001b2, 0x001b3, 0x001b3, 0x001b5, 0x001b5, 0x001b7, 0x001b8, 0x001b8, 0x001ba, 0x001bb, 0x001bc, 0x001bc, 0x001be, 0x001f7, 0x001c0, 0x001c1, 0x001c2, 0x001c3, 0x001c4, 0x001c4, 0x001c4, 0x001c7, 0x001c7, 0x001c7, 0x001ca, 0x001ca, 0x001ca, 0x001cd, 0x001cd, 0x001cf, 0x001cf, 0x001d1, 0x001d1, 0x001d3, 0x001d3, 0x001d5, 0x001d5, 0x001d7, 0x001d7, 0x001d9, 0x001d9, 0x001db, 0x001db, 0x0018e, 0x001de, 0x001de, 0x001e0, 0x001e0, 0x001e2, 0x001e2, 0x001e4, 0x001e4, 0x001e6, 0x001e6, 0x001e8, 0x001e8, 0x001ea, 0x001ea, 0x001ec, 0x001ec, 0x001ee, 0x001ee, 0x001f0, 0x001f1, 0x001f1, 0x001f1, 0x001f4, 0x001f4, 0x001f6, 0x001f7, 0x001f8, 0x001f8, 0x001fa, 0x001fa, 0x001fc, 0x001fc, 0x001fe, 0x001fe, }; static const uint32_t njs_unicode_upper_case_block_004[128] njs_aligned(64) = { 0x00200, 0x00200, 0x00202, 0x00202, 0x00204, 0x00204, 0x00206, 0x00206, 0x00208, 0x00208, 0x0020a, 0x0020a, 0x0020c, 0x0020c, 0x0020e, 0x0020e, 0x00210, 0x00210, 0x00212, 0x00212, 0x00214, 0x00214, 0x00216, 0x00216, 0x00218, 0x00218, 0x0021a, 0x0021a, 0x0021c, 0x0021c, 0x0021e, 0x0021e, 0x00220, 0x00221, 0x00222, 0x00222, 0x00224, 0x00224, 0x00226, 0x00226, 0x00228, 0x00228, 0x0022a, 0x0022a, 0x0022c, 0x0022c, 0x0022e, 0x0022e, 0x00230, 0x00230, 0x00232, 0x00232, 0x00234, 0x00235, 0x00236, 0x00237, 0x00238, 0x00239, 0x0023a, 0x0023b, 0x0023b, 0x0023d, 0x0023e, 0x02c7e, 0x02c7f, 0x00241, 0x00241, 0x00243, 0x00244, 0x00245, 0x00246, 0x00246, 0x00248, 0x00248, 0x0024a, 0x0024a, 0x0024c, 0x0024c, 0x0024e, 0x0024e, 0x02c6f, 0x02c6d, 0x02c70, 0x00181, 0x00186, 0x00255, 0x00189, 0x0018a, 0x00258, 0x0018f, 0x0025a, 0x00190, 0x0a7ab, 0x0025d, 0x0025e, 0x0025f, 0x00193, 0x0a7ac, 0x00262, 0x00194, 0x00264, 0x0a78d, 0x0a7aa, 0x00267, 0x00197, 0x00196, 0x0a7ae, 0x02c62, 0x0a7ad, 0x0026d, 0x0026e, 0x0019c, 0x00270, 0x02c6e, 0x0019d, 0x00273, 0x00274, 0x0019f, 0x00276, 0x00277, 0x00278, 0x00279, 0x0027a, 0x0027b, 0x0027c, 0x02c64, 0x0027e, 0x0027f, }; static const uint32_t njs_unicode_upper_case_block_005[128] njs_aligned(64) = { 0x001a6, 0x00281, 0x0a7c5, 0x001a9, 0x00284, 0x00285, 0x00286, 0x0a7b1, 0x001ae, 0x00244, 0x001b1, 0x001b2, 0x00245, 0x0028d, 0x0028e, 0x0028f, 0x00290, 0x00291, 0x001b7, 0x00293, 0x00294, 0x00295, 0x00296, 0x00297, 0x00298, 0x00299, 0x0029a, 0x0029b, 0x0029c, 0x0a7b2, 0x0a7b0, 0x0029f, 0x002a0, 0x002a1, 0x002a2, 0x002a3, 0x002a4, 0x002a5, 0x002a6, 0x002a7, 0x002a8, 0x002a9, 0x002aa, 0x002ab, 0x002ac, 0x002ad, 0x002ae, 0x002af, 0x002b0, 0x002b1, 0x002b2, 0x002b3, 0x002b4, 0x002b5, 0x002b6, 0x002b7, 0x002b8, 0x002b9, 0x002ba, 0x002bb, 0x002bc, 0x002bd, 0x002be, 0x002bf, 0x002c0, 0x002c1, 0x002c2, 0x002c3, 0x002c4, 0x002c5, 0x002c6, 0x002c7, 0x002c8, 0x002c9, 0x002ca, 0x002cb, 0x002cc, 0x002cd, 0x002ce, 0x002cf, 0x002d0, 0x002d1, 0x002d2, 0x002d3, 0x002d4, 0x002d5, 0x002d6, 0x002d7, 0x002d8, 0x002d9, 0x002da, 0x002db, 0x002dc, 0x002dd, 0x002de, 0x002df, 0x002e0, 0x002e1, 0x002e2, 0x002e3, 0x002e4, 0x002e5, 0x002e6, 0x002e7, 0x002e8, 0x002e9, 0x002ea, 0x002eb, 0x002ec, 0x002ed, 0x002ee, 0x002ef, 0x002f0, 0x002f1, 0x002f2, 0x002f3, 0x002f4, 0x002f5, 0x002f6, 0x002f7, 0x002f8, 0x002f9, 0x002fa, 0x002fb, 0x002fc, 0x002fd, 0x002fe, 0x002ff, }; static const uint32_t njs_unicode_upper_case_block_006[128] njs_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, 0x00399, 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, 0x00370, 0x00370, 0x00372, 0x00372, 0x00374, 0x00375, 0x00376, 0x00376, 0x00378, 0x00379, 0x0037a, 0x003fd, 0x003fe, 0x003ff, 0x0037e, 0x0037f, }; static const uint32_t njs_unicode_upper_case_block_007[128] njs_aligned(64) = { 0x00380, 0x00381, 0x00382, 0x00383, 0x00384, 0x00385, 0x00386, 0x00387, 0x00388, 0x00389, 0x0038a, 0x0038b, 0x0038c, 0x0038d, 0x0038e, 0x0038f, 0x00390, 0x00391, 0x00392, 0x00393, 0x00394, 0x00395, 0x00396, 0x00397, 0x00398, 0x00399, 0x0039a, 0x0039b, 0x0039c, 0x0039d, 0x0039e, 0x0039f, 0x003a0, 0x003a1, 0x003a2, 0x003a3, 0x003a4, 0x003a5, 0x003a6, 0x003a7, 0x003a8, 0x003a9, 0x003aa, 0x003ab, 0x00386, 0x00388, 0x00389, 0x0038a, 0x003b0, 0x00391, 0x00392, 0x00393, 0x00394, 0x00395, 0x00396, 0x00397, 0x00398, 0x00399, 0x0039a, 0x0039b, 0x0039c, 0x0039d, 0x0039e, 0x0039f, 0x003a0, 0x003a1, 0x003a3, 0x003a3, 0x003a4, 0x003a5, 0x003a6, 0x003a7, 0x003a8, 0x003a9, 0x003aa, 0x003ab, 0x0038c, 0x0038e, 0x0038f, 0x003cf, 0x00392, 0x00398, 0x003d2, 0x003d3, 0x003d4, 0x003a6, 0x003a0, 0x003cf, 0x003d8, 0x003d8, 0x003da, 0x003da, 0x003dc, 0x003dc, 0x003de, 0x003de, 0x003e0, 0x003e0, 0x003e2, 0x003e2, 0x003e4, 0x003e4, 0x003e6, 0x003e6, 0x003e8, 0x003e8, 0x003ea, 0x003ea, 0x003ec, 0x003ec, 0x003ee, 0x003ee, 0x0039a, 0x003a1, 0x003f9, 0x0037f, 0x003f4, 0x00395, 0x003f6, 0x003f7, 0x003f7, 0x003f9, 0x003fa, 0x003fa, 0x003fc, 0x003fd, 0x003fe, 0x003ff, }; static const uint32_t njs_unicode_upper_case_block_008[128] njs_aligned(64) = { 0x00400, 0x00401, 0x00402, 0x00403, 0x00404, 0x00405, 0x00406, 0x00407, 0x00408, 0x00409, 0x0040a, 0x0040b, 0x0040c, 0x0040d, 0x0040e, 0x0040f, 0x00410, 0x00411, 0x00412, 0x00413, 0x00414, 0x00415, 0x00416, 0x00417, 0x00418, 0x00419, 0x0041a, 0x0041b, 0x0041c, 0x0041d, 0x0041e, 0x0041f, 0x00420, 0x00421, 0x00422, 0x00423, 0x00424, 0x00425, 0x00426, 0x00427, 0x00428, 0x00429, 0x0042a, 0x0042b, 0x0042c, 0x0042d, 0x0042e, 0x0042f, 0x00410, 0x00411, 0x00412, 0x00413, 0x00414, 0x00415, 0x00416, 0x00417, 0x00418, 0x00419, 0x0041a, 0x0041b, 0x0041c, 0x0041d, 0x0041e, 0x0041f, 0x00420, 0x00421, 0x00422, 0x00423, 0x00424, 0x00425, 0x00426, 0x00427, 0x00428, 0x00429, 0x0042a, 0x0042b, 0x0042c, 0x0042d, 0x0042e, 0x0042f, 0x00400, 0x00401, 0x00402, 0x00403, 0x00404, 0x00405, 0x00406, 0x00407, 0x00408, 0x00409, 0x0040a, 0x0040b, 0x0040c, 0x0040d, 0x0040e, 0x0040f, 0x00460, 0x00460, 0x00462, 0x00462, 0x00464, 0x00464, 0x00466, 0x00466, 0x00468, 0x00468, 0x0046a, 0x0046a, 0x0046c, 0x0046c, 0x0046e, 0x0046e, 0x00470, 0x00470, 0x00472, 0x00472, 0x00474, 0x00474, 0x00476, 0x00476, 0x00478, 0x00478, 0x0047a, 0x0047a, 0x0047c, 0x0047c, 0x0047e, 0x0047e, }; static const uint32_t njs_unicode_upper_case_block_009[128] njs_aligned(64) = { 0x00480, 0x00480, 0x00482, 0x00483, 0x00484, 0x00485, 0x00486, 0x00487, 0x00488, 0x00489, 0x0048a, 0x0048a, 0x0048c, 0x0048c, 0x0048e, 0x0048e, 0x00490, 0x00490, 0x00492, 0x00492, 0x00494, 0x00494, 0x00496, 0x00496, 0x00498, 0x00498, 0x0049a, 0x0049a, 0x0049c, 0x0049c, 0x0049e, 0x0049e, 0x004a0, 0x004a0, 0x004a2, 0x004a2, 0x004a4, 0x004a4, 0x004a6, 0x004a6, 0x004a8, 0x004a8, 0x004aa, 0x004aa, 0x004ac, 0x004ac, 0x004ae, 0x004ae, 0x004b0, 0x004b0, 0x004b2, 0x004b2, 0x004b4, 0x004b4, 0x004b6, 0x004b6, 0x004b8, 0x004b8, 0x004ba, 0x004ba, 0x004bc, 0x004bc, 0x004be, 0x004be, 0x004c0, 0x004c1, 0x004c1, 0x004c3, 0x004c3, 0x004c5, 0x004c5, 0x004c7, 0x004c7, 0x004c9, 0x004c9, 0x004cb, 0x004cb, 0x004cd, 0x004cd, 0x004c0, 0x004d0, 0x004d0, 0x004d2, 0x004d2, 0x004d4, 0x004d4, 0x004d6, 0x004d6, 0x004d8, 0x004d8, 0x004da, 0x004da, 0x004dc, 0x004dc, 0x004de, 0x004de, 0x004e0, 0x004e0, 0x004e2, 0x004e2, 0x004e4, 0x004e4, 0x004e6, 0x004e6, 0x004e8, 0x004e8, 0x004ea, 0x004ea, 0x004ec, 0x004ec, 0x004ee, 0x004ee, 0x004f0, 0x004f0, 0x004f2, 0x004f2, 0x004f4, 0x004f4, 0x004f6, 0x004f6, 0x004f8, 0x004f8, 0x004fa, 0x004fa, 0x004fc, 0x004fc, 0x004fe, 0x004fe, }; static const uint32_t njs_unicode_upper_case_block_00a[128] njs_aligned(64) = { 0x00500, 0x00500, 0x00502, 0x00502, 0x00504, 0x00504, 0x00506, 0x00506, 0x00508, 0x00508, 0x0050a, 0x0050a, 0x0050c, 0x0050c, 0x0050e, 0x0050e, 0x00510, 0x00510, 0x00512, 0x00512, 0x00514, 0x00514, 0x00516, 0x00516, 0x00518, 0x00518, 0x0051a, 0x0051a, 0x0051c, 0x0051c, 0x0051e, 0x0051e, 0x00520, 0x00520, 0x00522, 0x00522, 0x00524, 0x00524, 0x00526, 0x00526, 0x00528, 0x00528, 0x0052a, 0x0052a, 0x0052c, 0x0052c, 0x0052e, 0x0052e, 0x00530, 0x00531, 0x00532, 0x00533, 0x00534, 0x00535, 0x00536, 0x00537, 0x00538, 0x00539, 0x0053a, 0x0053b, 0x0053c, 0x0053d, 0x0053e, 0x0053f, 0x00540, 0x00541, 0x00542, 0x00543, 0x00544, 0x00545, 0x00546, 0x00547, 0x00548, 0x00549, 0x0054a, 0x0054b, 0x0054c, 0x0054d, 0x0054e, 0x0054f, 0x00550, 0x00551, 0x00552, 0x00553, 0x00554, 0x00555, 0x00556, 0x00557, 0x00558, 0x00559, 0x0055a, 0x0055b, 0x0055c, 0x0055d, 0x0055e, 0x0055f, 0x00560, 0x00531, 0x00532, 0x00533, 0x00534, 0x00535, 0x00536, 0x00537, 0x00538, 0x00539, 0x0053a, 0x0053b, 0x0053c, 0x0053d, 0x0053e, 0x0053f, 0x00540, 0x00541, 0x00542, 0x00543, 0x00544, 0x00545, 0x00546, 0x00547, 0x00548, 0x00549, 0x0054a, 0x0054b, 0x0054c, 0x0054d, 0x0054e, 0x0054f, }; static const uint32_t njs_unicode_upper_case_block_00b[128] njs_aligned(64) = { 0x00550, 0x00551, 0x00552, 0x00553, 0x00554, 0x00555, 0x00556, 0x00587, 0x00588, 0x00589, 0x0058a, 0x0058b, 0x0058c, 0x0058d, 0x0058e, 0x0058f, 0x00590, 0x00591, 0x00592, 0x00593, 0x00594, 0x00595, 0x00596, 0x00597, 0x00598, 0x00599, 0x0059a, 0x0059b, 0x0059c, 0x0059d, 0x0059e, 0x0059f, 0x005a0, 0x005a1, 0x005a2, 0x005a3, 0x005a4, 0x005a5, 0x005a6, 0x005a7, 0x005a8, 0x005a9, 0x005aa, 0x005ab, 0x005ac, 0x005ad, 0x005ae, 0x005af, 0x005b0, 0x005b1, 0x005b2, 0x005b3, 0x005b4, 0x005b5, 0x005b6, 0x005b7, 0x005b8, 0x005b9, 0x005ba, 0x005bb, 0x005bc, 0x005bd, 0x005be, 0x005bf, 0x005c0, 0x005c1, 0x005c2, 0x005c3, 0x005c4, 0x005c5, 0x005c6, 0x005c7, 0x005c8, 0x005c9, 0x005ca, 0x005cb, 0x005cc, 0x005cd, 0x005ce, 0x005cf, 0x005d0, 0x005d1, 0x005d2, 0x005d3, 0x005d4, 0x005d5, 0x005d6, 0x005d7, 0x005d8, 0x005d9, 0x005da, 0x005db, 0x005dc, 0x005dd, 0x005de, 0x005df, 0x005e0, 0x005e1, 0x005e2, 0x005e3, 0x005e4, 0x005e5, 0x005e6, 0x005e7, 0x005e8, 0x005e9, 0x005ea, 0x005eb, 0x005ec, 0x005ed, 0x005ee, 0x005ef, 0x005f0, 0x005f1, 0x005f2, 0x005f3, 0x005f4, 0x005f5, 0x005f6, 0x005f7, 0x005f8, 0x005f9, 0x005fa, 0x005fb, 0x005fc, 0x005fd, 0x005fe, 0x005ff, }; static const uint32_t njs_unicode_upper_case_block_021[128] njs_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, 0x010a0, 0x010a1, 0x010a2, 0x010a3, 0x010a4, 0x010a5, 0x010a6, 0x010a7, 0x010a8, 0x010a9, 0x010aa, 0x010ab, 0x010ac, 0x010ad, 0x010ae, 0x010af, 0x010b0, 0x010b1, 0x010b2, 0x010b3, 0x010b4, 0x010b5, 0x010b6, 0x010b7, 0x010b8, 0x010b9, 0x010ba, 0x010bb, 0x010bc, 0x010bd, 0x010be, 0x010bf, 0x010c0, 0x010c1, 0x010c2, 0x010c3, 0x010c4, 0x010c5, 0x010c6, 0x010c7, 0x010c8, 0x010c9, 0x010ca, 0x010cb, 0x010cc, 0x010cd, 0x010ce, 0x010cf, 0x01c90, 0x01c91, 0x01c92, 0x01c93, 0x01c94, 0x01c95, 0x01c96, 0x01c97, 0x01c98, 0x01c99, 0x01c9a, 0x01c9b, 0x01c9c, 0x01c9d, 0x01c9e, 0x01c9f, 0x01ca0, 0x01ca1, 0x01ca2, 0x01ca3, 0x01ca4, 0x01ca5, 0x01ca6, 0x01ca7, 0x01ca8, 0x01ca9, 0x01caa, 0x01cab, 0x01cac, 0x01cad, 0x01cae, 0x01caf, 0x01cb0, 0x01cb1, 0x01cb2, 0x01cb3, 0x01cb4, 0x01cb5, 0x01cb6, 0x01cb7, 0x01cb8, 0x01cb9, 0x01cba, 0x010fb, 0x010fc, 0x01cbd, 0x01cbe, 0x01cbf, }; static const uint32_t njs_unicode_upper_case_block_027[128] njs_aligned(64) = { 0x01380, 0x01381, 0x01382, 0x01383, 0x01384, 0x01385, 0x01386, 0x01387, 0x01388, 0x01389, 0x0138a, 0x0138b, 0x0138c, 0x0138d, 0x0138e, 0x0138f, 0x01390, 0x01391, 0x01392, 0x01393, 0x01394, 0x01395, 0x01396, 0x01397, 0x01398, 0x01399, 0x0139a, 0x0139b, 0x0139c, 0x0139d, 0x0139e, 0x0139f, 0x013a0, 0x013a1, 0x013a2, 0x013a3, 0x013a4, 0x013a5, 0x013a6, 0x013a7, 0x013a8, 0x013a9, 0x013aa, 0x013ab, 0x013ac, 0x013ad, 0x013ae, 0x013af, 0x013b0, 0x013b1, 0x013b2, 0x013b3, 0x013b4, 0x013b5, 0x013b6, 0x013b7, 0x013b8, 0x013b9, 0x013ba, 0x013bb, 0x013bc, 0x013bd, 0x013be, 0x013bf, 0x013c0, 0x013c1, 0x013c2, 0x013c3, 0x013c4, 0x013c5, 0x013c6, 0x013c7, 0x013c8, 0x013c9, 0x013ca, 0x013cb, 0x013cc, 0x013cd, 0x013ce, 0x013cf, 0x013d0, 0x013d1, 0x013d2, 0x013d3, 0x013d4, 0x013d5, 0x013d6, 0x013d7, 0x013d8, 0x013d9, 0x013da, 0x013db, 0x013dc, 0x013dd, 0x013de, 0x013df, 0x013e0, 0x013e1, 0x013e2, 0x013e3, 0x013e4, 0x013e5, 0x013e6, 0x013e7, 0x013e8, 0x013e9, 0x013ea, 0x013eb, 0x013ec, 0x013ed, 0x013ee, 0x013ef, 0x013f0, 0x013f1, 0x013f2, 0x013f3, 0x013f4, 0x013f5, 0x013f6, 0x013f7, 0x013f0, 0x013f1, 0x013f2, 0x013f3, 0x013f4, 0x013f5, 0x013fe, 0x013ff, }; static const uint32_t njs_unicode_upper_case_block_039[128] njs_aligned(64) = { 0x00412, 0x00414, 0x0041e, 0x00421, 0x00422, 0x00422, 0x0042a, 0x00462, 0x0a64a, 0x01c89, 0x01c8a, 0x01c8b, 0x01c8c, 0x01c8d, 0x01c8e, 0x01c8f, 0x01c90, 0x01c91, 0x01c92, 0x01c93, 0x01c94, 0x01c95, 0x01c96, 0x01c97, 0x01c98, 0x01c99, 0x01c9a, 0x01c9b, 0x01c9c, 0x01c9d, 0x01c9e, 0x01c9f, 0x01ca0, 0x01ca1, 0x01ca2, 0x01ca3, 0x01ca4, 0x01ca5, 0x01ca6, 0x01ca7, 0x01ca8, 0x01ca9, 0x01caa, 0x01cab, 0x01cac, 0x01cad, 0x01cae, 0x01caf, 0x01cb0, 0x01cb1, 0x01cb2, 0x01cb3, 0x01cb4, 0x01cb5, 0x01cb6, 0x01cb7, 0x01cb8, 0x01cb9, 0x01cba, 0x01cbb, 0x01cbc, 0x01cbd, 0x01cbe, 0x01cbf, 0x01cc0, 0x01cc1, 0x01cc2, 0x01cc3, 0x01cc4, 0x01cc5, 0x01cc6, 0x01cc7, 0x01cc8, 0x01cc9, 0x01cca, 0x01ccb, 0x01ccc, 0x01ccd, 0x01cce, 0x01ccf, 0x01cd0, 0x01cd1, 0x01cd2, 0x01cd3, 0x01cd4, 0x01cd5, 0x01cd6, 0x01cd7, 0x01cd8, 0x01cd9, 0x01cda, 0x01cdb, 0x01cdc, 0x01cdd, 0x01cde, 0x01cdf, 0x01ce0, 0x01ce1, 0x01ce2, 0x01ce3, 0x01ce4, 0x01ce5, 0x01ce6, 0x01ce7, 0x01ce8, 0x01ce9, 0x01cea, 0x01ceb, 0x01cec, 0x01ced, 0x01cee, 0x01cef, 0x01cf0, 0x01cf1, 0x01cf2, 0x01cf3, 0x01cf4, 0x01cf5, 0x01cf6, 0x01cf7, 0x01cf8, 0x01cf9, 0x01cfa, 0x01cfb, 0x01cfc, 0x01cfd, 0x01cfe, 0x01cff, }; static const uint32_t njs_unicode_upper_case_block_03a[128] njs_aligned(64) = { 0x01d00, 0x01d01, 0x01d02, 0x01d03, 0x01d04, 0x01d05, 0x01d06, 0x01d07, 0x01d08, 0x01d09, 0x01d0a, 0x01d0b, 0x01d0c, 0x01d0d, 0x01d0e, 0x01d0f, 0x01d10, 0x01d11, 0x01d12, 0x01d13, 0x01d14, 0x01d15, 0x01d16, 0x01d17, 0x01d18, 0x01d19, 0x01d1a, 0x01d1b, 0x01d1c, 0x01d1d, 0x01d1e, 0x01d1f, 0x01d20, 0x01d21, 0x01d22, 0x01d23, 0x01d24, 0x01d25, 0x01d26, 0x01d27, 0x01d28, 0x01d29, 0x01d2a, 0x01d2b, 0x01d2c, 0x01d2d, 0x01d2e, 0x01d2f, 0x01d30, 0x01d31, 0x01d32, 0x01d33, 0x01d34, 0x01d35, 0x01d36, 0x01d37, 0x01d38, 0x01d39, 0x01d3a, 0x01d3b, 0x01d3c, 0x01d3d, 0x01d3e, 0x01d3f, 0x01d40, 0x01d41, 0x01d42, 0x01d43, 0x01d44, 0x01d45, 0x01d46, 0x01d47, 0x01d48, 0x01d49, 0x01d4a, 0x01d4b, 0x01d4c, 0x01d4d, 0x01d4e, 0x01d4f, 0x01d50, 0x01d51, 0x01d52, 0x01d53, 0x01d54, 0x01d55, 0x01d56, 0x01d57, 0x01d58, 0x01d59, 0x01d5a, 0x01d5b, 0x01d5c, 0x01d5d, 0x01d5e, 0x01d5f, 0x01d60, 0x01d61, 0x01d62, 0x01d63, 0x01d64, 0x01d65, 0x01d66, 0x01d67, 0x01d68, 0x01d69, 0x01d6a, 0x01d6b, 0x01d6c, 0x01d6d, 0x01d6e, 0x01d6f, 0x01d70, 0x01d71, 0x01d72, 0x01d73, 0x01d74, 0x01d75, 0x01d76, 0x01d77, 0x01d78, 0x0a77d, 0x01d7a, 0x01d7b, 0x01d7c, 0x02c63, 0x01d7e, 0x01d7f, }; static const uint32_t njs_unicode_upper_case_block_03b[128] njs_aligned(64) = { 0x01d80, 0x01d81, 0x01d82, 0x01d83, 0x01d84, 0x01d85, 0x01d86, 0x01d87, 0x01d88, 0x01d89, 0x01d8a, 0x01d8b, 0x01d8c, 0x01d8d, 0x0a7c6, 0x01d8f, 0x01d90, 0x01d91, 0x01d92, 0x01d93, 0x01d94, 0x01d95, 0x01d96, 0x01d97, 0x01d98, 0x01d99, 0x01d9a, 0x01d9b, 0x01d9c, 0x01d9d, 0x01d9e, 0x01d9f, 0x01da0, 0x01da1, 0x01da2, 0x01da3, 0x01da4, 0x01da5, 0x01da6, 0x01da7, 0x01da8, 0x01da9, 0x01daa, 0x01dab, 0x01dac, 0x01dad, 0x01dae, 0x01daf, 0x01db0, 0x01db1, 0x01db2, 0x01db3, 0x01db4, 0x01db5, 0x01db6, 0x01db7, 0x01db8, 0x01db9, 0x01dba, 0x01dbb, 0x01dbc, 0x01dbd, 0x01dbe, 0x01dbf, 0x01dc0, 0x01dc1, 0x01dc2, 0x01dc3, 0x01dc4, 0x01dc5, 0x01dc6, 0x01dc7, 0x01dc8, 0x01dc9, 0x01dca, 0x01dcb, 0x01dcc, 0x01dcd, 0x01dce, 0x01dcf, 0x01dd0, 0x01dd1, 0x01dd2, 0x01dd3, 0x01dd4, 0x01dd5, 0x01dd6, 0x01dd7, 0x01dd8, 0x01dd9, 0x01dda, 0x01ddb, 0x01ddc, 0x01ddd, 0x01dde, 0x01ddf, 0x01de0, 0x01de1, 0x01de2, 0x01de3, 0x01de4, 0x01de5, 0x01de6, 0x01de7, 0x01de8, 0x01de9, 0x01dea, 0x01deb, 0x01dec, 0x01ded, 0x01dee, 0x01def, 0x01df0, 0x01df1, 0x01df2, 0x01df3, 0x01df4, 0x01df5, 0x01df6, 0x01df7, 0x01df8, 0x01df9, 0x01dfa, 0x01dfb, 0x01dfc, 0x01dfd, 0x01dfe, 0x01dff, }; static const uint32_t njs_unicode_upper_case_block_03c[128] njs_aligned(64) = { 0x01e00, 0x01e00, 0x01e02, 0x01e02, 0x01e04, 0x01e04, 0x01e06, 0x01e06, 0x01e08, 0x01e08, 0x01e0a, 0x01e0a, 0x01e0c, 0x01e0c, 0x01e0e, 0x01e0e, 0x01e10, 0x01e10, 0x01e12, 0x01e12, 0x01e14, 0x01e14, 0x01e16, 0x01e16, 0x01e18, 0x01e18, 0x01e1a, 0x01e1a, 0x01e1c, 0x01e1c, 0x01e1e, 0x01e1e, 0x01e20, 0x01e20, 0x01e22, 0x01e22, 0x01e24, 0x01e24, 0x01e26, 0x01e26, 0x01e28, 0x01e28, 0x01e2a, 0x01e2a, 0x01e2c, 0x01e2c, 0x01e2e, 0x01e2e, 0x01e30, 0x01e30, 0x01e32, 0x01e32, 0x01e34, 0x01e34, 0x01e36, 0x01e36, 0x01e38, 0x01e38, 0x01e3a, 0x01e3a, 0x01e3c, 0x01e3c, 0x01e3e, 0x01e3e, 0x01e40, 0x01e40, 0x01e42, 0x01e42, 0x01e44, 0x01e44, 0x01e46, 0x01e46, 0x01e48, 0x01e48, 0x01e4a, 0x01e4a, 0x01e4c, 0x01e4c, 0x01e4e, 0x01e4e, 0x01e50, 0x01e50, 0x01e52, 0x01e52, 0x01e54, 0x01e54, 0x01e56, 0x01e56, 0x01e58, 0x01e58, 0x01e5a, 0x01e5a, 0x01e5c, 0x01e5c, 0x01e5e, 0x01e5e, 0x01e60, 0x01e60, 0x01e62, 0x01e62, 0x01e64, 0x01e64, 0x01e66, 0x01e66, 0x01e68, 0x01e68, 0x01e6a, 0x01e6a, 0x01e6c, 0x01e6c, 0x01e6e, 0x01e6e, 0x01e70, 0x01e70, 0x01e72, 0x01e72, 0x01e74, 0x01e74, 0x01e76, 0x01e76, 0x01e78, 0x01e78, 0x01e7a, 0x01e7a, 0x01e7c, 0x01e7c, 0x01e7e, 0x01e7e, }; static const uint32_t njs_unicode_upper_case_block_03d[128] njs_aligned(64) = { 0x01e80, 0x01e80, 0x01e82, 0x01e82, 0x01e84, 0x01e84, 0x01e86, 0x01e86, 0x01e88, 0x01e88, 0x01e8a, 0x01e8a, 0x01e8c, 0x01e8c, 0x01e8e, 0x01e8e, 0x01e90, 0x01e90, 0x01e92, 0x01e92, 0x01e94, 0x01e94, 0x01e96, 0x01e97, 0x01e98, 0x01e99, 0x01e9a, 0x01e60, 0x01e9c, 0x01e9d, 0x01e9e, 0x01e9f, 0x01ea0, 0x01ea0, 0x01ea2, 0x01ea2, 0x01ea4, 0x01ea4, 0x01ea6, 0x01ea6, 0x01ea8, 0x01ea8, 0x01eaa, 0x01eaa, 0x01eac, 0x01eac, 0x01eae, 0x01eae, 0x01eb0, 0x01eb0, 0x01eb2, 0x01eb2, 0x01eb4, 0x01eb4, 0x01eb6, 0x01eb6, 0x01eb8, 0x01eb8, 0x01eba, 0x01eba, 0x01ebc, 0x01ebc, 0x01ebe, 0x01ebe, 0x01ec0, 0x01ec0, 0x01ec2, 0x01ec2, 0x01ec4, 0x01ec4, 0x01ec6, 0x01ec6, 0x01ec8, 0x01ec8, 0x01eca, 0x01eca, 0x01ecc, 0x01ecc, 0x01ece, 0x01ece, 0x01ed0, 0x01ed0, 0x01ed2, 0x01ed2, 0x01ed4, 0x01ed4, 0x01ed6, 0x01ed6, 0x01ed8, 0x01ed8, 0x01eda, 0x01eda, 0x01edc, 0x01edc, 0x01ede, 0x01ede, 0x01ee0, 0x01ee0, 0x01ee2, 0x01ee2, 0x01ee4, 0x01ee4, 0x01ee6, 0x01ee6, 0x01ee8, 0x01ee8, 0x01eea, 0x01eea, 0x01eec, 0x01eec, 0x01eee, 0x01eee, 0x01ef0, 0x01ef0, 0x01ef2, 0x01ef2, 0x01ef4, 0x01ef4, 0x01ef6, 0x01ef6, 0x01ef8, 0x01ef8, 0x01efa, 0x01efa, 0x01efc, 0x01efc, 0x01efe, 0x01efe, }; static const uint32_t njs_unicode_upper_case_block_03e[128] njs_aligned(64) = { 0x01f08, 0x01f09, 0x01f0a, 0x01f0b, 0x01f0c, 0x01f0d, 0x01f0e, 0x01f0f, 0x01f08, 0x01f09, 0x01f0a, 0x01f0b, 0x01f0c, 0x01f0d, 0x01f0e, 0x01f0f, 0x01f18, 0x01f19, 0x01f1a, 0x01f1b, 0x01f1c, 0x01f1d, 0x01f16, 0x01f17, 0x01f18, 0x01f19, 0x01f1a, 0x01f1b, 0x01f1c, 0x01f1d, 0x01f1e, 0x01f1f, 0x01f28, 0x01f29, 0x01f2a, 0x01f2b, 0x01f2c, 0x01f2d, 0x01f2e, 0x01f2f, 0x01f28, 0x01f29, 0x01f2a, 0x01f2b, 0x01f2c, 0x01f2d, 0x01f2e, 0x01f2f, 0x01f38, 0x01f39, 0x01f3a, 0x01f3b, 0x01f3c, 0x01f3d, 0x01f3e, 0x01f3f, 0x01f38, 0x01f39, 0x01f3a, 0x01f3b, 0x01f3c, 0x01f3d, 0x01f3e, 0x01f3f, 0x01f48, 0x01f49, 0x01f4a, 0x01f4b, 0x01f4c, 0x01f4d, 0x01f46, 0x01f47, 0x01f48, 0x01f49, 0x01f4a, 0x01f4b, 0x01f4c, 0x01f4d, 0x01f4e, 0x01f4f, 0x01f50, 0x01f59, 0x01f52, 0x01f5b, 0x01f54, 0x01f5d, 0x01f56, 0x01f5f, 0x01f58, 0x01f59, 0x01f5a, 0x01f5b, 0x01f5c, 0x01f5d, 0x01f5e, 0x01f5f, 0x01f68, 0x01f69, 0x01f6a, 0x01f6b, 0x01f6c, 0x01f6d, 0x01f6e, 0x01f6f, 0x01f68, 0x01f69, 0x01f6a, 0x01f6b, 0x01f6c, 0x01f6d, 0x01f6e, 0x01f6f, 0x01fba, 0x01fbb, 0x01fc8, 0x01fc9, 0x01fca, 0x01fcb, 0x01fda, 0x01fdb, 0x01ff8, 0x01ff9, 0x01fea, 0x01feb, 0x01ffa, 0x01ffb, 0x01f7e, 0x01f7f, }; static const uint32_t njs_unicode_upper_case_block_03f[128] njs_aligned(64) = { 0x01f88, 0x01f89, 0x01f8a, 0x01f8b, 0x01f8c, 0x01f8d, 0x01f8e, 0x01f8f, 0x01f88, 0x01f89, 0x01f8a, 0x01f8b, 0x01f8c, 0x01f8d, 0x01f8e, 0x01f8f, 0x01f98, 0x01f99, 0x01f9a, 0x01f9b, 0x01f9c, 0x01f9d, 0x01f9e, 0x01f9f, 0x01f98, 0x01f99, 0x01f9a, 0x01f9b, 0x01f9c, 0x01f9d, 0x01f9e, 0x01f9f, 0x01fa8, 0x01fa9, 0x01faa, 0x01fab, 0x01fac, 0x01fad, 0x01fae, 0x01faf, 0x01fa8, 0x01fa9, 0x01faa, 0x01fab, 0x01fac, 0x01fad, 0x01fae, 0x01faf, 0x01fb8, 0x01fb9, 0x01fb2, 0x01fbc, 0x01fb4, 0x01fb5, 0x01fb6, 0x01fb7, 0x01fb8, 0x01fb9, 0x01fba, 0x01fbb, 0x01fbc, 0x01fbd, 0x00399, 0x01fbf, 0x01fc0, 0x01fc1, 0x01fc2, 0x01fcc, 0x01fc4, 0x01fc5, 0x01fc6, 0x01fc7, 0x01fc8, 0x01fc9, 0x01fca, 0x01fcb, 0x01fcc, 0x01fcd, 0x01fce, 0x01fcf, 0x01fd8, 0x01fd9, 0x01fd2, 0x01fd3, 0x01fd4, 0x01fd5, 0x01fd6, 0x01fd7, 0x01fd8, 0x01fd9, 0x01fda, 0x01fdb, 0x01fdc, 0x01fdd, 0x01fde, 0x01fdf, 0x01fe8, 0x01fe9, 0x01fe2, 0x01fe3, 0x01fe4, 0x01fec, 0x01fe6, 0x01fe7, 0x01fe8, 0x01fe9, 0x01fea, 0x01feb, 0x01fec, 0x01fed, 0x01fee, 0x01fef, 0x01ff0, 0x01ff1, 0x01ff2, 0x01ffc, 0x01ff4, 0x01ff5, 0x01ff6, 0x01ff7, 0x01ff8, 0x01ff9, 0x01ffa, 0x01ffb, 0x01ffc, 0x01ffd, 0x01ffe, 0x01fff, }; static const uint32_t njs_unicode_upper_case_block_042[128] njs_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, 0x02126, 0x02127, 0x02128, 0x02129, 0x0212a, 0x0212b, 0x0212c, 0x0212d, 0x0212e, 0x0212f, 0x02130, 0x02131, 0x02132, 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, 0x02132, 0x0214f, 0x02150, 0x02151, 0x02152, 0x02153, 0x02154, 0x02155, 0x02156, 0x02157, 0x02158, 0x02159, 0x0215a, 0x0215b, 0x0215c, 0x0215d, 0x0215e, 0x0215f, 0x02160, 0x02161, 0x02162, 0x02163, 0x02164, 0x02165, 0x02166, 0x02167, 0x02168, 0x02169, 0x0216a, 0x0216b, 0x0216c, 0x0216d, 0x0216e, 0x0216f, 0x02160, 0x02161, 0x02162, 0x02163, 0x02164, 0x02165, 0x02166, 0x02167, 0x02168, 0x02169, 0x0216a, 0x0216b, 0x0216c, 0x0216d, 0x0216e, 0x0216f, }; static const uint32_t njs_unicode_upper_case_block_043[128] njs_aligned(64) = { 0x02180, 0x02181, 0x02182, 0x02183, 0x02183, 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 njs_unicode_upper_case_block_049[128] njs_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, 0x024b6, 0x024b7, 0x024b8, 0x024b9, 0x024ba, 0x024bb, 0x024bc, 0x024bd, 0x024be, 0x024bf, 0x024c0, 0x024c1, 0x024c2, 0x024c3, 0x024c4, 0x024c5, 0x024c6, 0x024c7, 0x024c8, 0x024c9, 0x024ca, 0x024cb, 0x024cc, 0x024cd, 0x024ce, 0x024cf, 0x024b6, 0x024b7, 0x024b8, 0x024b9, 0x024ba, 0x024bb, 0x024bc, 0x024bd, 0x024be, 0x024bf, 0x024c0, 0x024c1, 0x024c2, 0x024c3, 0x024c4, 0x024c5, 0x024c6, 0x024c7, 0x024c8, 0x024c9, 0x024ca, 0x024cb, 0x024cc, 0x024cd, 0x024ce, 0x024cf, 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 njs_unicode_upper_case_block_058[128] njs_aligned(64) = { 0x02c00, 0x02c01, 0x02c02, 0x02c03, 0x02c04, 0x02c05, 0x02c06, 0x02c07, 0x02c08, 0x02c09, 0x02c0a, 0x02c0b, 0x02c0c, 0x02c0d, 0x02c0e, 0x02c0f, 0x02c10, 0x02c11, 0x02c12, 0x02c13, 0x02c14, 0x02c15, 0x02c16, 0x02c17, 0x02c18, 0x02c19, 0x02c1a, 0x02c1b, 0x02c1c, 0x02c1d, 0x02c1e, 0x02c1f, 0x02c20, 0x02c21, 0x02c22, 0x02c23, 0x02c24, 0x02c25, 0x02c26, 0x02c27, 0x02c28, 0x02c29, 0x02c2a, 0x02c2b, 0x02c2c, 0x02c2d, 0x02c2e, 0x02c2f, 0x02c00, 0x02c01, 0x02c02, 0x02c03, 0x02c04, 0x02c05, 0x02c06, 0x02c07, 0x02c08, 0x02c09, 0x02c0a, 0x02c0b, 0x02c0c, 0x02c0d, 0x02c0e, 0x02c0f, 0x02c10, 0x02c11, 0x02c12, 0x02c13, 0x02c14, 0x02c15, 0x02c16, 0x02c17, 0x02c18, 0x02c19, 0x02c1a, 0x02c1b, 0x02c1c, 0x02c1d, 0x02c1e, 0x02c1f, 0x02c20, 0x02c21, 0x02c22, 0x02c23, 0x02c24, 0x02c25, 0x02c26, 0x02c27, 0x02c28, 0x02c29, 0x02c2a, 0x02c2b, 0x02c2c, 0x02c2d, 0x02c2e, 0x02c2f, 0x02c60, 0x02c60, 0x02c62, 0x02c63, 0x02c64, 0x0023a, 0x0023e, 0x02c67, 0x02c67, 0x02c69, 0x02c69, 0x02c6b, 0x02c6b, 0x02c6d, 0x02c6e, 0x02c6f, 0x02c70, 0x02c71, 0x02c72, 0x02c72, 0x02c74, 0x02c75, 0x02c75, 0x02c77, 0x02c78, 0x02c79, 0x02c7a, 0x02c7b, 0x02c7c, 0x02c7d, 0x02c7e, 0x02c7f, }; static const uint32_t njs_unicode_upper_case_block_059[128] njs_aligned(64) = { 0x02c80, 0x02c80, 0x02c82, 0x02c82, 0x02c84, 0x02c84, 0x02c86, 0x02c86, 0x02c88, 0x02c88, 0x02c8a, 0x02c8a, 0x02c8c, 0x02c8c, 0x02c8e, 0x02c8e, 0x02c90, 0x02c90, 0x02c92, 0x02c92, 0x02c94, 0x02c94, 0x02c96, 0x02c96, 0x02c98, 0x02c98, 0x02c9a, 0x02c9a, 0x02c9c, 0x02c9c, 0x02c9e, 0x02c9e, 0x02ca0, 0x02ca0, 0x02ca2, 0x02ca2, 0x02ca4, 0x02ca4, 0x02ca6, 0x02ca6, 0x02ca8, 0x02ca8, 0x02caa, 0x02caa, 0x02cac, 0x02cac, 0x02cae, 0x02cae, 0x02cb0, 0x02cb0, 0x02cb2, 0x02cb2, 0x02cb4, 0x02cb4, 0x02cb6, 0x02cb6, 0x02cb8, 0x02cb8, 0x02cba, 0x02cba, 0x02cbc, 0x02cbc, 0x02cbe, 0x02cbe, 0x02cc0, 0x02cc0, 0x02cc2, 0x02cc2, 0x02cc4, 0x02cc4, 0x02cc6, 0x02cc6, 0x02cc8, 0x02cc8, 0x02cca, 0x02cca, 0x02ccc, 0x02ccc, 0x02cce, 0x02cce, 0x02cd0, 0x02cd0, 0x02cd2, 0x02cd2, 0x02cd4, 0x02cd4, 0x02cd6, 0x02cd6, 0x02cd8, 0x02cd8, 0x02cda, 0x02cda, 0x02cdc, 0x02cdc, 0x02cde, 0x02cde, 0x02ce0, 0x02ce0, 0x02ce2, 0x02ce2, 0x02ce4, 0x02ce5, 0x02ce6, 0x02ce7, 0x02ce8, 0x02ce9, 0x02cea, 0x02ceb, 0x02ceb, 0x02ced, 0x02ced, 0x02cef, 0x02cf0, 0x02cf1, 0x02cf2, 0x02cf2, 0x02cf4, 0x02cf5, 0x02cf6, 0x02cf7, 0x02cf8, 0x02cf9, 0x02cfa, 0x02cfb, 0x02cfc, 0x02cfd, 0x02cfe, 0x02cff, }; static const uint32_t njs_unicode_upper_case_block_05a[128] njs_aligned(64) = { 0x010a0, 0x010a1, 0x010a2, 0x010a3, 0x010a4, 0x010a5, 0x010a6, 0x010a7, 0x010a8, 0x010a9, 0x010aa, 0x010ab, 0x010ac, 0x010ad, 0x010ae, 0x010af, 0x010b0, 0x010b1, 0x010b2, 0x010b3, 0x010b4, 0x010b5, 0x010b6, 0x010b7, 0x010b8, 0x010b9, 0x010ba, 0x010bb, 0x010bc, 0x010bd, 0x010be, 0x010bf, 0x010c0, 0x010c1, 0x010c2, 0x010c3, 0x010c4, 0x010c5, 0x02d26, 0x010c7, 0x02d28, 0x02d29, 0x02d2a, 0x02d2b, 0x02d2c, 0x010cd, 0x02d2e, 0x02d2f, 0x02d30, 0x02d31, 0x02d32, 0x02d33, 0x02d34, 0x02d35, 0x02d36, 0x02d37, 0x02d38, 0x02d39, 0x02d3a, 0x02d3b, 0x02d3c, 0x02d3d, 0x02d3e, 0x02d3f, 0x02d40, 0x02d41, 0x02d42, 0x02d43, 0x02d44, 0x02d45, 0x02d46, 0x02d47, 0x02d48, 0x02d49, 0x02d4a, 0x02d4b, 0x02d4c, 0x02d4d, 0x02d4e, 0x02d4f, 0x02d50, 0x02d51, 0x02d52, 0x02d53, 0x02d54, 0x02d55, 0x02d56, 0x02d57, 0x02d58, 0x02d59, 0x02d5a, 0x02d5b, 0x02d5c, 0x02d5d, 0x02d5e, 0x02d5f, 0x02d60, 0x02d61, 0x02d62, 0x02d63, 0x02d64, 0x02d65, 0x02d66, 0x02d67, 0x02d68, 0x02d69, 0x02d6a, 0x02d6b, 0x02d6c, 0x02d6d, 0x02d6e, 0x02d6f, 0x02d70, 0x02d71, 0x02d72, 0x02d73, 0x02d74, 0x02d75, 0x02d76, 0x02d77, 0x02d78, 0x02d79, 0x02d7a, 0x02d7b, 0x02d7c, 0x02d7d, 0x02d7e, 0x02d7f, }; static const uint32_t njs_unicode_upper_case_block_14c[128] njs_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, 0x0a640, 0x0a640, 0x0a642, 0x0a642, 0x0a644, 0x0a644, 0x0a646, 0x0a646, 0x0a648, 0x0a648, 0x0a64a, 0x0a64a, 0x0a64c, 0x0a64c, 0x0a64e, 0x0a64e, 0x0a650, 0x0a650, 0x0a652, 0x0a652, 0x0a654, 0x0a654, 0x0a656, 0x0a656, 0x0a658, 0x0a658, 0x0a65a, 0x0a65a, 0x0a65c, 0x0a65c, 0x0a65e, 0x0a65e, 0x0a660, 0x0a660, 0x0a662, 0x0a662, 0x0a664, 0x0a664, 0x0a666, 0x0a666, 0x0a668, 0x0a668, 0x0a66a, 0x0a66a, 0x0a66c, 0x0a66c, 0x0a66e, 0x0a66f, 0x0a670, 0x0a671, 0x0a672, 0x0a673, 0x0a674, 0x0a675, 0x0a676, 0x0a677, 0x0a678, 0x0a679, 0x0a67a, 0x0a67b, 0x0a67c, 0x0a67d, 0x0a67e, 0x0a67f, }; static const uint32_t njs_unicode_upper_case_block_14d[128] njs_aligned(64) = { 0x0a680, 0x0a680, 0x0a682, 0x0a682, 0x0a684, 0x0a684, 0x0a686, 0x0a686, 0x0a688, 0x0a688, 0x0a68a, 0x0a68a, 0x0a68c, 0x0a68c, 0x0a68e, 0x0a68e, 0x0a690, 0x0a690, 0x0a692, 0x0a692, 0x0a694, 0x0a694, 0x0a696, 0x0a696, 0x0a698, 0x0a698, 0x0a69a, 0x0a69a, 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 njs_unicode_upper_case_block_14e[128] njs_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, 0x0a722, 0x0a722, 0x0a724, 0x0a724, 0x0a726, 0x0a726, 0x0a728, 0x0a728, 0x0a72a, 0x0a72a, 0x0a72c, 0x0a72c, 0x0a72e, 0x0a72e, 0x0a730, 0x0a731, 0x0a732, 0x0a732, 0x0a734, 0x0a734, 0x0a736, 0x0a736, 0x0a738, 0x0a738, 0x0a73a, 0x0a73a, 0x0a73c, 0x0a73c, 0x0a73e, 0x0a73e, 0x0a740, 0x0a740, 0x0a742, 0x0a742, 0x0a744, 0x0a744, 0x0a746, 0x0a746, 0x0a748, 0x0a748, 0x0a74a, 0x0a74a, 0x0a74c, 0x0a74c, 0x0a74e, 0x0a74e, 0x0a750, 0x0a750, 0x0a752, 0x0a752, 0x0a754, 0x0a754, 0x0a756, 0x0a756, 0x0a758, 0x0a758, 0x0a75a, 0x0a75a, 0x0a75c, 0x0a75c, 0x0a75e, 0x0a75e, 0x0a760, 0x0a760, 0x0a762, 0x0a762, 0x0a764, 0x0a764, 0x0a766, 0x0a766, 0x0a768, 0x0a768, 0x0a76a, 0x0a76a, 0x0a76c, 0x0a76c, 0x0a76e, 0x0a76e, 0x0a770, 0x0a771, 0x0a772, 0x0a773, 0x0a774, 0x0a775, 0x0a776, 0x0a777, 0x0a778, 0x0a779, 0x0a779, 0x0a77b, 0x0a77b, 0x0a77d, 0x0a77e, 0x0a77e, }; static const uint32_t njs_unicode_upper_case_block_14f[128] njs_aligned(64) = { 0x0a780, 0x0a780, 0x0a782, 0x0a782, 0x0a784, 0x0a784, 0x0a786, 0x0a786, 0x0a788, 0x0a789, 0x0a78a, 0x0a78b, 0x0a78b, 0x0a78d, 0x0a78e, 0x0a78f, 0x0a790, 0x0a790, 0x0a792, 0x0a792, 0x0a7c4, 0x0a795, 0x0a796, 0x0a796, 0x0a798, 0x0a798, 0x0a79a, 0x0a79a, 0x0a79c, 0x0a79c, 0x0a79e, 0x0a79e, 0x0a7a0, 0x0a7a0, 0x0a7a2, 0x0a7a2, 0x0a7a4, 0x0a7a4, 0x0a7a6, 0x0a7a6, 0x0a7a8, 0x0a7a8, 0x0a7aa, 0x0a7ab, 0x0a7ac, 0x0a7ad, 0x0a7ae, 0x0a7af, 0x0a7b0, 0x0a7b1, 0x0a7b2, 0x0a7b3, 0x0a7b4, 0x0a7b4, 0x0a7b6, 0x0a7b6, 0x0a7b8, 0x0a7b8, 0x0a7ba, 0x0a7ba, 0x0a7bc, 0x0a7bc, 0x0a7be, 0x0a7be, 0x0a7c0, 0x0a7c0, 0x0a7c2, 0x0a7c2, 0x0a7c4, 0x0a7c5, 0x0a7c6, 0x0a7c7, 0x0a7c7, 0x0a7c9, 0x0a7c9, 0x0a7cb, 0x0a7cc, 0x0a7cd, 0x0a7ce, 0x0a7cf, 0x0a7d0, 0x0a7d0, 0x0a7d2, 0x0a7d3, 0x0a7d4, 0x0a7d5, 0x0a7d6, 0x0a7d6, 0x0a7d8, 0x0a7d8, 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, 0x0a7f5, 0x0a7f7, 0x0a7f8, 0x0a7f9, 0x0a7fa, 0x0a7fb, 0x0a7fc, 0x0a7fd, 0x0a7fe, 0x0a7ff, }; static const uint32_t njs_unicode_upper_case_block_156[128] njs_aligned(64) = { 0x0ab00, 0x0ab01, 0x0ab02, 0x0ab03, 0x0ab04, 0x0ab05, 0x0ab06, 0x0ab07, 0x0ab08, 0x0ab09, 0x0ab0a, 0x0ab0b, 0x0ab0c, 0x0ab0d, 0x0ab0e, 0x0ab0f, 0x0ab10, 0x0ab11, 0x0ab12, 0x0ab13, 0x0ab14, 0x0ab15, 0x0ab16, 0x0ab17, 0x0ab18, 0x0ab19, 0x0ab1a, 0x0ab1b, 0x0ab1c, 0x0ab1d, 0x0ab1e, 0x0ab1f, 0x0ab20, 0x0ab21, 0x0ab22, 0x0ab23, 0x0ab24, 0x0ab25, 0x0ab26, 0x0ab27, 0x0ab28, 0x0ab29, 0x0ab2a, 0x0ab2b, 0x0ab2c, 0x0ab2d, 0x0ab2e, 0x0ab2f, 0x0ab30, 0x0ab31, 0x0ab32, 0x0ab33, 0x0ab34, 0x0ab35, 0x0ab36, 0x0ab37, 0x0ab38, 0x0ab39, 0x0ab3a, 0x0ab3b, 0x0ab3c, 0x0ab3d, 0x0ab3e, 0x0ab3f, 0x0ab40, 0x0ab41, 0x0ab42, 0x0ab43, 0x0ab44, 0x0ab45, 0x0ab46, 0x0ab47, 0x0ab48, 0x0ab49, 0x0ab4a, 0x0ab4b, 0x0ab4c, 0x0ab4d, 0x0ab4e, 0x0ab4f, 0x0ab50, 0x0ab51, 0x0ab52, 0x0a7b3, 0x0ab54, 0x0ab55, 0x0ab56, 0x0ab57, 0x0ab58, 0x0ab59, 0x0ab5a, 0x0ab5b, 0x0ab5c, 0x0ab5d, 0x0ab5e, 0x0ab5f, 0x0ab60, 0x0ab61, 0x0ab62, 0x0ab63, 0x0ab64, 0x0ab65, 0x0ab66, 0x0ab67, 0x0ab68, 0x0ab69, 0x0ab6a, 0x0ab6b, 0x0ab6c, 0x0ab6d, 0x0ab6e, 0x0ab6f, 0x013a0, 0x013a1, 0x013a2, 0x013a3, 0x013a4, 0x013a5, 0x013a6, 0x013a7, 0x013a8, 0x013a9, 0x013aa, 0x013ab, 0x013ac, 0x013ad, 0x013ae, 0x013af, }; static const uint32_t njs_unicode_upper_case_block_157[128] njs_aligned(64) = { 0x013b0, 0x013b1, 0x013b2, 0x013b3, 0x013b4, 0x013b5, 0x013b6, 0x013b7, 0x013b8, 0x013b9, 0x013ba, 0x013bb, 0x013bc, 0x013bd, 0x013be, 0x013bf, 0x013c0, 0x013c1, 0x013c2, 0x013c3, 0x013c4, 0x013c5, 0x013c6, 0x013c7, 0x013c8, 0x013c9, 0x013ca, 0x013cb, 0x013cc, 0x013cd, 0x013ce, 0x013cf, 0x013d0, 0x013d1, 0x013d2, 0x013d3, 0x013d4, 0x013d5, 0x013d6, 0x013d7, 0x013d8, 0x013d9, 0x013da, 0x013db, 0x013dc, 0x013dd, 0x013de, 0x013df, 0x013e0, 0x013e1, 0x013e2, 0x013e3, 0x013e4, 0x013e5, 0x013e6, 0x013e7, 0x013e8, 0x013e9, 0x013ea, 0x013eb, 0x013ec, 0x013ed, 0x013ee, 0x013ef, 0x0abc0, 0x0abc1, 0x0abc2, 0x0abc3, 0x0abc4, 0x0abc5, 0x0abc6, 0x0abc7, 0x0abc8, 0x0abc9, 0x0abca, 0x0abcb, 0x0abcc, 0x0abcd, 0x0abce, 0x0abcf, 0x0abd0, 0x0abd1, 0x0abd2, 0x0abd3, 0x0abd4, 0x0abd5, 0x0abd6, 0x0abd7, 0x0abd8, 0x0abd9, 0x0abda, 0x0abdb, 0x0abdc, 0x0abdd, 0x0abde, 0x0abdf, 0x0abe0, 0x0abe1, 0x0abe2, 0x0abe3, 0x0abe4, 0x0abe5, 0x0abe6, 0x0abe7, 0x0abe8, 0x0abe9, 0x0abea, 0x0abeb, 0x0abec, 0x0abed, 0x0abee, 0x0abef, 0x0abf0, 0x0abf1, 0x0abf2, 0x0abf3, 0x0abf4, 0x0abf5, 0x0abf6, 0x0abf7, 0x0abf8, 0x0abf9, 0x0abfa, 0x0abfb, 0x0abfc, 0x0abfd, 0x0abfe, 0x0abff, }; static const uint32_t njs_unicode_upper_case_block_1fe[128] njs_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, 0x0ff21, 0x0ff22, 0x0ff23, 0x0ff24, 0x0ff25, 0x0ff26, 0x0ff27, 0x0ff28, 0x0ff29, 0x0ff2a, 0x0ff2b, 0x0ff2c, 0x0ff2d, 0x0ff2e, 0x0ff2f, 0x0ff30, 0x0ff31, 0x0ff32, 0x0ff33, 0x0ff34, 0x0ff35, 0x0ff36, 0x0ff37, 0x0ff38, 0x0ff39, 0x0ff3a, 0x0ff3b, 0x0ff3c, 0x0ff3d, 0x0ff3e, 0x0ff3f, 0x0ff40, 0x0ff21, 0x0ff22, 0x0ff23, 0x0ff24, 0x0ff25, 0x0ff26, 0x0ff27, 0x0ff28, 0x0ff29, 0x0ff2a, 0x0ff2b, 0x0ff2c, 0x0ff2d, 0x0ff2e, 0x0ff2f, 0x0ff30, 0x0ff31, 0x0ff32, 0x0ff33, 0x0ff34, 0x0ff35, 0x0ff36, 0x0ff37, 0x0ff38, 0x0ff39, 0x0ff3a, 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 njs_unicode_upper_case_block_208[128] njs_aligned(64) = { 0x10400, 0x10401, 0x10402, 0x10403, 0x10404, 0x10405, 0x10406, 0x10407, 0x10408, 0x10409, 0x1040a, 0x1040b, 0x1040c, 0x1040d, 0x1040e, 0x1040f, 0x10410, 0x10411, 0x10412, 0x10413, 0x10414, 0x10415, 0x10416, 0x10417, 0x10418, 0x10419, 0x1041a, 0x1041b, 0x1041c, 0x1041d, 0x1041e, 0x1041f, 0x10420, 0x10421, 0x10422, 0x10423, 0x10424, 0x10425, 0x10426, 0x10427, 0x10400, 0x10401, 0x10402, 0x10403, 0x10404, 0x10405, 0x10406, 0x10407, 0x10408, 0x10409, 0x1040a, 0x1040b, 0x1040c, 0x1040d, 0x1040e, 0x1040f, 0x10410, 0x10411, 0x10412, 0x10413, 0x10414, 0x10415, 0x10416, 0x10417, 0x10418, 0x10419, 0x1041a, 0x1041b, 0x1041c, 0x1041d, 0x1041e, 0x1041f, 0x10420, 0x10421, 0x10422, 0x10423, 0x10424, 0x10425, 0x10426, 0x10427, 0x10450, 0x10451, 0x10452, 0x10453, 0x10454, 0x10455, 0x10456, 0x10457, 0x10458, 0x10459, 0x1045a, 0x1045b, 0x1045c, 0x1045d, 0x1045e, 0x1045f, 0x10460, 0x10461, 0x10462, 0x10463, 0x10464, 0x10465, 0x10466, 0x10467, 0x10468, 0x10469, 0x1046a, 0x1046b, 0x1046c, 0x1046d, 0x1046e, 0x1046f, 0x10470, 0x10471, 0x10472, 0x10473, 0x10474, 0x10475, 0x10476, 0x10477, 0x10478, 0x10479, 0x1047a, 0x1047b, 0x1047c, 0x1047d, 0x1047e, 0x1047f, }; static const uint32_t njs_unicode_upper_case_block_209[128] njs_aligned(64) = { 0x10480, 0x10481, 0x10482, 0x10483, 0x10484, 0x10485, 0x10486, 0x10487, 0x10488, 0x10489, 0x1048a, 0x1048b, 0x1048c, 0x1048d, 0x1048e, 0x1048f, 0x10490, 0x10491, 0x10492, 0x10493, 0x10494, 0x10495, 0x10496, 0x10497, 0x10498, 0x10499, 0x1049a, 0x1049b, 0x1049c, 0x1049d, 0x1049e, 0x1049f, 0x104a0, 0x104a1, 0x104a2, 0x104a3, 0x104a4, 0x104a5, 0x104a6, 0x104a7, 0x104a8, 0x104a9, 0x104aa, 0x104ab, 0x104ac, 0x104ad, 0x104ae, 0x104af, 0x104b0, 0x104b1, 0x104b2, 0x104b3, 0x104b4, 0x104b5, 0x104b6, 0x104b7, 0x104b8, 0x104b9, 0x104ba, 0x104bb, 0x104bc, 0x104bd, 0x104be, 0x104bf, 0x104c0, 0x104c1, 0x104c2, 0x104c3, 0x104c4, 0x104c5, 0x104c6, 0x104c7, 0x104c8, 0x104c9, 0x104ca, 0x104cb, 0x104cc, 0x104cd, 0x104ce, 0x104cf, 0x104d0, 0x104d1, 0x104d2, 0x104d3, 0x104d4, 0x104d5, 0x104d6, 0x104d7, 0x104b0, 0x104b1, 0x104b2, 0x104b3, 0x104b4, 0x104b5, 0x104b6, 0x104b7, 0x104b8, 0x104b9, 0x104ba, 0x104bb, 0x104bc, 0x104bd, 0x104be, 0x104bf, 0x104c0, 0x104c1, 0x104c2, 0x104c3, 0x104c4, 0x104c5, 0x104c6, 0x104c7, 0x104c8, 0x104c9, 0x104ca, 0x104cb, 0x104cc, 0x104cd, 0x104ce, 0x104cf, 0x104d0, 0x104d1, 0x104d2, 0x104d3, 0x104fc, 0x104fd, 0x104fe, 0x104ff, }; static const uint32_t njs_unicode_upper_case_block_20b[128] njs_aligned(64) = { 0x10580, 0x10581, 0x10582, 0x10583, 0x10584, 0x10585, 0x10586, 0x10587, 0x10588, 0x10589, 0x1058a, 0x1058b, 0x1058c, 0x1058d, 0x1058e, 0x1058f, 0x10590, 0x10591, 0x10592, 0x10593, 0x10594, 0x10595, 0x10596, 0x10570, 0x10571, 0x10572, 0x10573, 0x10574, 0x10575, 0x10576, 0x10577, 0x10578, 0x10579, 0x1057a, 0x105a2, 0x1057c, 0x1057d, 0x1057e, 0x1057f, 0x10580, 0x10581, 0x10582, 0x10583, 0x10584, 0x10585, 0x10586, 0x10587, 0x10588, 0x10589, 0x1058a, 0x105b2, 0x1058c, 0x1058d, 0x1058e, 0x1058f, 0x10590, 0x10591, 0x10592, 0x105ba, 0x10594, 0x10595, 0x105bd, 0x105be, 0x105bf, 0x105c0, 0x105c1, 0x105c2, 0x105c3, 0x105c4, 0x105c5, 0x105c6, 0x105c7, 0x105c8, 0x105c9, 0x105ca, 0x105cb, 0x105cc, 0x105cd, 0x105ce, 0x105cf, 0x105d0, 0x105d1, 0x105d2, 0x105d3, 0x105d4, 0x105d5, 0x105d6, 0x105d7, 0x105d8, 0x105d9, 0x105da, 0x105db, 0x105dc, 0x105dd, 0x105de, 0x105df, 0x105e0, 0x105e1, 0x105e2, 0x105e3, 0x105e4, 0x105e5, 0x105e6, 0x105e7, 0x105e8, 0x105e9, 0x105ea, 0x105eb, 0x105ec, 0x105ed, 0x105ee, 0x105ef, 0x105f0, 0x105f1, 0x105f2, 0x105f3, 0x105f4, 0x105f5, 0x105f6, 0x105f7, 0x105f8, 0x105f9, 0x105fa, 0x105fb, 0x105fc, 0x105fd, 0x105fe, 0x105ff, }; static const uint32_t njs_unicode_upper_case_block_219[128] njs_aligned(64) = { 0x10c80, 0x10c81, 0x10c82, 0x10c83, 0x10c84, 0x10c85, 0x10c86, 0x10c87, 0x10c88, 0x10c89, 0x10c8a, 0x10c8b, 0x10c8c, 0x10c8d, 0x10c8e, 0x10c8f, 0x10c90, 0x10c91, 0x10c92, 0x10c93, 0x10c94, 0x10c95, 0x10c96, 0x10c97, 0x10c98, 0x10c99, 0x10c9a, 0x10c9b, 0x10c9c, 0x10c9d, 0x10c9e, 0x10c9f, 0x10ca0, 0x10ca1, 0x10ca2, 0x10ca3, 0x10ca4, 0x10ca5, 0x10ca6, 0x10ca7, 0x10ca8, 0x10ca9, 0x10caa, 0x10cab, 0x10cac, 0x10cad, 0x10cae, 0x10caf, 0x10cb0, 0x10cb1, 0x10cb2, 0x10cb3, 0x10cb4, 0x10cb5, 0x10cb6, 0x10cb7, 0x10cb8, 0x10cb9, 0x10cba, 0x10cbb, 0x10cbc, 0x10cbd, 0x10cbe, 0x10cbf, 0x10c80, 0x10c81, 0x10c82, 0x10c83, 0x10c84, 0x10c85, 0x10c86, 0x10c87, 0x10c88, 0x10c89, 0x10c8a, 0x10c8b, 0x10c8c, 0x10c8d, 0x10c8e, 0x10c8f, 0x10c90, 0x10c91, 0x10c92, 0x10c93, 0x10c94, 0x10c95, 0x10c96, 0x10c97, 0x10c98, 0x10c99, 0x10c9a, 0x10c9b, 0x10c9c, 0x10c9d, 0x10c9e, 0x10c9f, 0x10ca0, 0x10ca1, 0x10ca2, 0x10ca3, 0x10ca4, 0x10ca5, 0x10ca6, 0x10ca7, 0x10ca8, 0x10ca9, 0x10caa, 0x10cab, 0x10cac, 0x10cad, 0x10cae, 0x10caf, 0x10cb0, 0x10cb1, 0x10cb2, 0x10cf3, 0x10cf4, 0x10cf5, 0x10cf6, 0x10cf7, 0x10cf8, 0x10cf9, 0x10cfa, 0x10cfb, 0x10cfc, 0x10cfd, 0x10cfe, 0x10cff, }; static const uint32_t njs_unicode_upper_case_block_231[128] njs_aligned(64) = { 0x11880, 0x11881, 0x11882, 0x11883, 0x11884, 0x11885, 0x11886, 0x11887, 0x11888, 0x11889, 0x1188a, 0x1188b, 0x1188c, 0x1188d, 0x1188e, 0x1188f, 0x11890, 0x11891, 0x11892, 0x11893, 0x11894, 0x11895, 0x11896, 0x11897, 0x11898, 0x11899, 0x1189a, 0x1189b, 0x1189c, 0x1189d, 0x1189e, 0x1189f, 0x118a0, 0x118a1, 0x118a2, 0x118a3, 0x118a4, 0x118a5, 0x118a6, 0x118a7, 0x118a8, 0x118a9, 0x118aa, 0x118ab, 0x118ac, 0x118ad, 0x118ae, 0x118af, 0x118b0, 0x118b1, 0x118b2, 0x118b3, 0x118b4, 0x118b5, 0x118b6, 0x118b7, 0x118b8, 0x118b9, 0x118ba, 0x118bb, 0x118bc, 0x118bd, 0x118be, 0x118bf, 0x118a0, 0x118a1, 0x118a2, 0x118a3, 0x118a4, 0x118a5, 0x118a6, 0x118a7, 0x118a8, 0x118a9, 0x118aa, 0x118ab, 0x118ac, 0x118ad, 0x118ae, 0x118af, 0x118b0, 0x118b1, 0x118b2, 0x118b3, 0x118b4, 0x118b5, 0x118b6, 0x118b7, 0x118b8, 0x118b9, 0x118ba, 0x118bb, 0x118bc, 0x118bd, 0x118be, 0x118bf, 0x118e0, 0x118e1, 0x118e2, 0x118e3, 0x118e4, 0x118e5, 0x118e6, 0x118e7, 0x118e8, 0x118e9, 0x118ea, 0x118eb, 0x118ec, 0x118ed, 0x118ee, 0x118ef, 0x118f0, 0x118f1, 0x118f2, 0x118f3, 0x118f4, 0x118f5, 0x118f6, 0x118f7, 0x118f8, 0x118f9, 0x118fa, 0x118fb, 0x118fc, 0x118fd, 0x118fe, 0x118ff, }; static const uint32_t njs_unicode_upper_case_block_2dc[128] njs_aligned(64) = { 0x16e00, 0x16e01, 0x16e02, 0x16e03, 0x16e04, 0x16e05, 0x16e06, 0x16e07, 0x16e08, 0x16e09, 0x16e0a, 0x16e0b, 0x16e0c, 0x16e0d, 0x16e0e, 0x16e0f, 0x16e10, 0x16e11, 0x16e12, 0x16e13, 0x16e14, 0x16e15, 0x16e16, 0x16e17, 0x16e18, 0x16e19, 0x16e1a, 0x16e1b, 0x16e1c, 0x16e1d, 0x16e1e, 0x16e1f, 0x16e20, 0x16e21, 0x16e22, 0x16e23, 0x16e24, 0x16e25, 0x16e26, 0x16e27, 0x16e28, 0x16e29, 0x16e2a, 0x16e2b, 0x16e2c, 0x16e2d, 0x16e2e, 0x16e2f, 0x16e30, 0x16e31, 0x16e32, 0x16e33, 0x16e34, 0x16e35, 0x16e36, 0x16e37, 0x16e38, 0x16e39, 0x16e3a, 0x16e3b, 0x16e3c, 0x16e3d, 0x16e3e, 0x16e3f, 0x16e40, 0x16e41, 0x16e42, 0x16e43, 0x16e44, 0x16e45, 0x16e46, 0x16e47, 0x16e48, 0x16e49, 0x16e4a, 0x16e4b, 0x16e4c, 0x16e4d, 0x16e4e, 0x16e4f, 0x16e50, 0x16e51, 0x16e52, 0x16e53, 0x16e54, 0x16e55, 0x16e56, 0x16e57, 0x16e58, 0x16e59, 0x16e5a, 0x16e5b, 0x16e5c, 0x16e5d, 0x16e5e, 0x16e5f, 0x16e40, 0x16e41, 0x16e42, 0x16e43, 0x16e44, 0x16e45, 0x16e46, 0x16e47, 0x16e48, 0x16e49, 0x16e4a, 0x16e4b, 0x16e4c, 0x16e4d, 0x16e4e, 0x16e4f, 0x16e50, 0x16e51, 0x16e52, 0x16e53, 0x16e54, 0x16e55, 0x16e56, 0x16e57, 0x16e58, 0x16e59, 0x16e5a, 0x16e5b, 0x16e5c, 0x16e5d, 0x16e5e, 0x16e5f, }; static const uint32_t njs_unicode_upper_case_block_3d2[68] njs_aligned(64) = { 0x1e900, 0x1e901, 0x1e902, 0x1e903, 0x1e904, 0x1e905, 0x1e906, 0x1e907, 0x1e908, 0x1e909, 0x1e90a, 0x1e90b, 0x1e90c, 0x1e90d, 0x1e90e, 0x1e90f, 0x1e910, 0x1e911, 0x1e912, 0x1e913, 0x1e914, 0x1e915, 0x1e916, 0x1e917, 0x1e918, 0x1e919, 0x1e91a, 0x1e91b, 0x1e91c, 0x1e91d, 0x1e91e, 0x1e91f, 0x1e920, 0x1e921, 0x1e900, 0x1e901, 0x1e902, 0x1e903, 0x1e904, 0x1e905, 0x1e906, 0x1e907, 0x1e908, 0x1e909, 0x1e90a, 0x1e90b, 0x1e90c, 0x1e90d, 0x1e90e, 0x1e90f, 0x1e910, 0x1e911, 0x1e912, 0x1e913, 0x1e914, 0x1e915, 0x1e916, 0x1e917, 0x1e918, 0x1e919, 0x1e91a, 0x1e91b, 0x1e91c, 0x1e91d, 0x1e91e, 0x1e91f, 0x1e920, 0x1e921, }; static const uint32_t *njs_unicode_upper_case_blocks[] njs_aligned(64) = { njs_unicode_upper_case_block_000, njs_unicode_upper_case_block_001, njs_unicode_upper_case_block_002, njs_unicode_upper_case_block_003, njs_unicode_upper_case_block_004, njs_unicode_upper_case_block_005, njs_unicode_upper_case_block_006, njs_unicode_upper_case_block_007, njs_unicode_upper_case_block_008, njs_unicode_upper_case_block_009, njs_unicode_upper_case_block_00a, njs_unicode_upper_case_block_00b, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, njs_unicode_upper_case_block_021, NULL, NULL, NULL, NULL, NULL, njs_unicode_upper_case_block_027, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, njs_unicode_upper_case_block_039, njs_unicode_upper_case_block_03a, njs_unicode_upper_case_block_03b, njs_unicode_upper_case_block_03c, njs_unicode_upper_case_block_03d, njs_unicode_upper_case_block_03e, njs_unicode_upper_case_block_03f, NULL, NULL, njs_unicode_upper_case_block_042, njs_unicode_upper_case_block_043, NULL, NULL, NULL, NULL, NULL, njs_unicode_upper_case_block_049, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, njs_unicode_upper_case_block_058, njs_unicode_upper_case_block_059, njs_unicode_upper_case_block_05a, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, njs_unicode_upper_case_block_14c, njs_unicode_upper_case_block_14d, njs_unicode_upper_case_block_14e, njs_unicode_upper_case_block_14f, NULL, NULL, NULL, NULL, NULL, NULL, njs_unicode_upper_case_block_156, njs_unicode_upper_case_block_157, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, njs_unicode_upper_case_block_1fe, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, njs_unicode_upper_case_block_208, njs_unicode_upper_case_block_209, NULL, njs_unicode_upper_case_block_20b, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, njs_unicode_upper_case_block_219, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, njs_unicode_upper_case_block_231, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, njs_unicode_upper_case_block_2dc, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, njs_unicode_upper_case_block_3d2, }; njs-0.8.9/src/njs_unicode_upper_case.pl000077500000000000000000000043571474132077100201760ustar00rootroot00000000000000#!/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 %upper_case; my %blocks; my $max_block = 0; my $max_upper_case = 0; while (<>) { my @line = split(";", $_); if ($line[12]) { my ($symbol, $folding) = (hex $line[0], hex $line[12]); $upper_case{$symbol} = $folding; $blocks{int($symbol / BLOCK_SIZE)} = 1; if ($max_upper_case < $symbol) { $max_upper_case = $symbol; } } } my $last_block_size = $max_upper_case % 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 NJS_UNICODE_MAX_UPPER_CASE 0x%05x\n\n", $max_upper_case); printf("#define NJS_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("njs_unicode_upper_case_block_%03x[%d]\n" . " njs_aligned(64) =\n" . "{", $block, $block_size); for my $c (0 .. $block_size - 1) { printf "\n " if $c % 8 == 0; my $n = $block * BLOCK_SIZE + $c; if (exists $upper_case{$n}) { printf(" 0x%05x,", $upper_case{$n}); } else { #print " .......,"; printf(" 0x%05x,", $n); } } print "\n};\n\n\n"; } print "static const uint32_t *njs_unicode_upper_case_blocks[]\n" . " njs_aligned(64) =\n" . "{\n"; for my $block (0 .. $max_block) { if (exists($blocks{$block})) { printf(" njs_unicode_upper_case_block_%03x,\n", $block); } else { print " NULL,\n"; } } print "};\n"; njs-0.8.9/src/njs_unix.h000066400000000000000000000016661474132077100151360ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_UNIX_H_INCLUDED_ #define _NJS_UNIX_H_INCLUDED_ #define njs_pagesize() getpagesize() #if (NJS_LINUX) #ifdef _FORTIFY_SOURCE /* * _FORTIFY_SOURCE * does not allow to use "(void) write()"; */ #undef _FORTIFY_SOURCE #endif #endif /* NJS_LINUX */ #include #include #include #include #include #include #include #include #include /* * alloca() is defined in stdlib.h in Linux, FreeBSD and MacOSX * and in alloca.h in Linux, Solaris and MacOSX. */ #if (NJS_SOLARIS) #include #endif #include #include #include #include extern char **environ; #if defined(PATH_MAX) #define NJS_MAX_PATH PATH_MAX #else #define NJS_MAX_PATH 4096 #endif #endif /* _NJS_UNIX_H_INCLUDED_ */ njs-0.8.9/src/njs_utf16.c000066400000000000000000000036661474132077100151150ustar00rootroot00000000000000 /* * Copyright (C) Alexander Borisov * Copyright (C) NGINX, Inc. */ #include njs_inline void njs_utf16_encode_write(uint32_t cp, u_char **start) { #ifdef NJS_HAVE_BIG_ENDIAN *(*start)++ = cp >> 8; *(*start)++ = cp & 0x00FF; #else *(*start)++ = cp & 0x00FF; *(*start)++ = cp >> 8; #endif } ssize_t njs_utf16_encode(uint32_t cp, u_char **start, const u_char *end) { if ((*start + 2) > end) { return NJS_ERROR; } if (cp < 0x10000) { njs_utf16_encode_write(cp, start); return 2; } if ((*start + 4) > end) { return NJS_ERROR; } cp -= 0x10000; njs_utf16_encode_write((0xD800 | (cp >> 0x0A)), start); njs_utf16_encode_write((0xDC00 | (cp & 0x03FF)), start); return 4; } uint32_t njs_utf16_decode(njs_unicode_decode_t *ctx, const u_char **start, const u_char *end) { uint32_t unit; unsigned lead; if (ctx->upper != 0x00) { lead = ctx->upper - 0x01; ctx->upper = 0x00; goto lead_state; } pair_state: lead = *(*start)++; if (*start >= end) { ctx->upper = lead + 0x01; return NJS_UNICODE_CONTINUE; } lead_state: #ifdef NJS_HAVE_BIG_ENDIAN unit = (lead << 8) + *(*start)++; #else unit = (*(*start)++ << 8) + lead; #endif if (ctx->codepoint != 0x00) { if (njs_surrogate_trailing(unit)) { unit = njs_surrogate_pair(ctx->codepoint, unit); ctx->codepoint = 0x00; return unit; } (*start)--; ctx->upper = lead + 0x01; ctx->codepoint = 0x00; return NJS_UNICODE_ERROR; } if (njs_surrogate_any(unit)) { if (njs_surrogate_trailing(unit)) { return NJS_UNICODE_ERROR; } ctx->codepoint = unit; if (*start >= end) { return NJS_UNICODE_CONTINUE; } goto pair_state; } return unit; } njs-0.8.9/src/njs_utf16.h000066400000000000000000000007571474132077100151200ustar00rootroot00000000000000 /* * Copyright (C) Alexander Borisov * Copyright (C) NGINX, Inc. */ #ifndef _NJS_UTF16_H_INCLUDED_ #define _NJS_UTF16_H_INCLUDED_ NJS_EXPORT ssize_t njs_utf16_encode(uint32_t cp, u_char **start, const u_char *end); NJS_EXPORT uint32_t njs_utf16_decode(njs_unicode_decode_t *ctx, const u_char **start, const u_char *end); njs_inline void njs_utf16_decode_init(njs_unicode_decode_t *ctx) { ctx->upper = 0x00; ctx->codepoint = 0x00; } #endif /* _NJS_UTF16_H_INCLUDED_ */ njs-0.8.9/src/njs_utf8.c000066400000000000000000000205741474132077100150330ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include /* * The njs_unicode_lower_case.h and njs_unicode_upper_case.h files are * auto-generated from the UnicodeData.txt file version 14.0.0 (May 2021) * provided by Unicode, Inc.: * * ./njs_unicode_lower_case.pl UnicodeData.txt * ./njs_unicode_upper_case.pl UnicodeData.txt * * Only common and simple case foldings are supported. Full case foldings * are not supported. Combined characters are also not supported. */ #include #include u_char * njs_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; } njs_inline njs_int_t njs_utf8_boundary(njs_unicode_decode_t *ctx, const u_char **data, unsigned *need, u_char lower, u_char upper) { u_char ch; ch = **data; if (ch < lower || ch > upper) { return NJS_ERROR; } (*data)++; (*need)--; ctx->codepoint = (ctx->codepoint << 6) | (ch & 0x3F); return NJS_OK; } njs_inline void njs_utf8_boundary_set(njs_unicode_decode_t *ctx, const u_char ch, u_char first, u_char second, u_char lower, u_char upper) { if (ch == first) { ctx->lower = lower; ctx->upper = 0xBF; } else if (ch == second) { ctx->lower = 0x80; ctx->upper = upper; } } uint32_t njs_utf8_decode(njs_unicode_decode_t *ctx, const u_char **start, const u_char *end) { u_char c; unsigned need; njs_int_t ret; const u_char *p; if (ctx->need != 0) { need = ctx->need; ctx->need = 0; if (ctx->lower != 0x00) { ret = njs_utf8_boundary(ctx, start, &need, ctx->lower, ctx->upper); if (njs_slow_path(ret != NJS_OK)) { goto failed; } ctx->lower = 0x00; } goto decode; } c = *(*start)++; if (c < 0x80) { return c; } else if (c <= 0xDF) { if (c < 0xC2) { return NJS_UNICODE_ERROR; } need = 1; ctx->codepoint = c & 0x1F; } else if (c < 0xF0) { need = 2; ctx->codepoint = c & 0x0F; if (*start == end) { njs_utf8_boundary_set(ctx, c, 0xE0, 0xED, 0xA0, 0x9F); goto next; } ret = NJS_OK; if (c == 0xE0) { ret = njs_utf8_boundary(ctx, start, &need, 0xA0, 0xBF); } else if (c == 0xED) { ret = njs_utf8_boundary(ctx, start, &need, 0x80, 0x9F); } if (njs_slow_path(ret != NJS_OK)) { goto failed; } } else if (c < 0xF5) { need = 3; ctx->codepoint = c & 0x07; if (*start == end) { njs_utf8_boundary_set(ctx, c, 0xF0, 0xF4, 0x90, 0x8F); goto next; } ret = NJS_OK; if (c == 0xF0) { ret = njs_utf8_boundary(ctx, start, &need, 0x90, 0xBF); } else if (c == 0xF4) { ret = njs_utf8_boundary(ctx, start, &need, 0x80, 0x8F); } if (njs_slow_path(ret != NJS_OK)) { goto failed; } } else { return NJS_UNICODE_ERROR; } decode: for (p = *start; p < end; p++) { c = *p; if (c < 0x80 || c > 0xBF) { *start = p; goto failed; } ctx->codepoint = (ctx->codepoint << 6) | (c & 0x3F); if (--need == 0) { *start = p + 1; return ctx->codepoint; } } *start = p; next: ctx->need = need; return NJS_UNICODE_CONTINUE; failed: ctx->lower = 0x00; ctx->need = 0; return NJS_UNICODE_ERROR; } u_char * njs_utf8_stream_encode(njs_unicode_decode_t *ctx, const u_char *start, const u_char *end, u_char *dst, njs_bool_t last, njs_bool_t fatal) { uint32_t cp; while (start < end) { cp = njs_utf8_decode(ctx, &start, end); if (cp > NJS_UNICODE_MAX_CODEPOINT) { if (cp == NJS_UNICODE_CONTINUE) { break; } if (fatal) { return NULL; } cp = NJS_UNICODE_REPLACEMENT; } dst = njs_utf8_encode(dst, cp); } if (last && ctx->need != 0x00) { if (fatal) { return NULL; } dst = njs_utf8_encode(dst, NJS_UNICODE_REPLACEMENT); } return dst; } /* * njs_utf8_casecmp() tests only up to the minimum of given lengths, but * requires lengths of both strings because otherwise njs_utf8_decode() * may fail due to incomplete sequence. */ njs_int_t njs_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 = njs_utf8_lower_case(&start1, end1); u2 = njs_utf8_lower_case(&start2, end2); if (njs_slow_path((u1 | u2) == 0xFFFFFFFF)) { return NJS_UNICODE_ERROR; } n = u1 - u2; if (n != 0) { return (njs_int_t) n; } } return 0; } uint32_t njs_utf8_lower_case(const u_char **start, const u_char *end) { uint32_t u; const uint32_t *block; njs_unicode_decode_t ctx; u = (uint32_t) **start; if (njs_fast_path(u < 0x80)) { (*start)++; return njs_unicode_lower_case_block_000[u]; } njs_utf8_decode_init(&ctx); u = njs_utf8_decode(&ctx, start, end); if (u <= NJS_UNICODE_MAX_LOWER_CASE) { block = njs_unicode_lower_case_blocks[u / NJS_UNICODE_BLOCK_SIZE]; if (block != NULL) { return block[u % NJS_UNICODE_BLOCK_SIZE]; } } return u; } uint32_t njs_utf8_upper_case(const u_char **start, const u_char *end) { uint32_t u; const uint32_t *block; njs_unicode_decode_t ctx; u = (uint32_t) **start; if (njs_fast_path(u < 0x80)) { (*start)++; return njs_unicode_upper_case_block_000[u]; } njs_utf8_decode_init(&ctx); u = njs_utf8_decode(&ctx, start, end); if (u <= NJS_UNICODE_MAX_UPPER_CASE) { block = njs_unicode_upper_case_blocks[u / NJS_UNICODE_BLOCK_SIZE]; if (block != NULL) { return block[u % NJS_UNICODE_BLOCK_SIZE]; } } return u; } ssize_t njs_utf8_stream_length(njs_unicode_decode_t *ctx, const u_char *p, size_t len, njs_bool_t last, njs_bool_t fatal, size_t *out_size) { size_t size, length; uint32_t codepoint; const u_char *end; size = 0; length = 0; if (p != NULL) { end = p + len; while (p < end) { codepoint = njs_utf8_decode(ctx, &p, end); if (codepoint > NJS_UNICODE_MAX_CODEPOINT) { if (codepoint == NJS_UNICODE_CONTINUE) { break; } if (fatal) { return -1; } codepoint = NJS_UNICODE_REPLACEMENT; } size += njs_utf8_size(codepoint); length++; } } if (last && ctx->need != 0x00) { if (fatal) { return -1; } size += njs_utf8_size(NJS_UNICODE_REPLACEMENT); length++; } if (out_size != NULL) { *out_size = size; } return length; } njs_bool_t njs_utf8_is_valid(const u_char *p, size_t len) { const u_char *end; njs_unicode_decode_t ctx; end = p + len; njs_utf8_decode_init(&ctx); while (p < end) { if (njs_slow_path(njs_utf8_decode(&ctx, &p, end) > NJS_UNICODE_MAX_CODEPOINT)) { return 0; } } return 1; } njs-0.8.9/src/njs_utf8.h000066400000000000000000000075501474132077100150370ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_UTF8_H_INCLUDED_ #define _NJS_UTF8_H_INCLUDED_ NJS_EXPORT uint32_t njs_utf8_decode(njs_unicode_decode_t *ctx, const u_char **data, const u_char *end); NJS_EXPORT u_char *njs_utf8_encode(u_char *p, uint32_t u); NJS_EXPORT u_char *njs_utf8_stream_encode(njs_unicode_decode_t *ctx, const u_char *start, const u_char *end, u_char *dst, njs_bool_t last, njs_bool_t fatal); NJS_EXPORT njs_int_t njs_utf8_casecmp(const u_char *start1, const u_char *start2, size_t len1, size_t len2); NJS_EXPORT uint32_t njs_utf8_lower_case(const u_char **start, const u_char *end); NJS_EXPORT uint32_t njs_utf8_upper_case(const u_char **start, const u_char *end); NJS_EXPORT ssize_t njs_utf8_stream_length(njs_unicode_decode_t *ctx, const u_char *p, size_t len, njs_bool_t last, njs_bool_t fatal, size_t *out_size); NJS_EXPORT njs_bool_t njs_utf8_is_valid(const u_char *p, size_t len); njs_inline uint32_t njs_utf8_consume(njs_unicode_decode_t *ctx, u_char byte) { const u_char *p; p = &byte; return njs_utf8_decode(ctx, &p, p + 1); } /* * njs_utf8_next() and njs_utf8_prev() expect a valid UTF-8 string. * * The leading UTF-8 byte is either 0xxxxxxx or 11xxxxxx. * The continuation UTF-8 bytes are 10xxxxxx. */ njs_inline const u_char * njs_utf8_next(const u_char *p, const u_char *end) { u_char c; c = *p++; if ((c & 0x80) != 0) { if (njs_slow_path(p >= end)) { return p; } do { c = *p; if ((c & 0xC0) != 0x80) { return p; } p++; } while (p < end); } return p; } njs_inline const u_char * njs_utf8_prev(const u_char *p, const u_char *start) { u_char c; do { p--; if (njs_slow_path(p < start)) { break; } c = *p; } while ((c & 0xC0) == 0x80); return p; } njs_inline u_char * njs_utf8_copy(u_char *dst, const u_char **src, const u_char *end) { u_char c; const u_char *p; p = *src; c = *p++; *dst++ = c; if ((c & 0x80) != 0) { do { c = *p; if ((c & 0xC0) != 0x80) { break; } *dst++ = c; p++; } while (p < end); } *src = p; return dst; } njs_inline void njs_utf8_decode_init(njs_unicode_decode_t *ctx) { ctx->need = 0x00; ctx->lower = 0x00; ctx->codepoint = 0; } njs_inline ssize_t njs_utf8_length(const u_char *p, size_t len) { njs_unicode_decode_t ctx; njs_utf8_decode_init(&ctx); return njs_utf8_stream_length(&ctx, p, len, 1, 1, NULL); } njs_inline size_t njs_utf8_bom(const u_char *start, const u_char *end) { if (start + 3 > end) { return 0; } if (start[0] == 0xEF && start[1] == 0xBB && start[2] == 0xBF) { return 3; } return 0; } njs_inline size_t njs_utf8_size(uint32_t cp) { return (cp < 0x80) ? 1 : ((cp < 0x0800) ? 2 : ((cp < 0x10000) ? 3 : 4)); } njs_inline size_t njs_utf8_size_uint16(uint32_t cp) { return ((cp < 0x80) ? 1 : ((cp < 0x0800) ? 2 : 3)); } njs_inline njs_bool_t njs_utf8_is_whitespace(uint32_t c) { switch (c) { case 0x0009: /* */ case 0x000A: /* */ case 0x000B: /* */ case 0x000C: /* */ case 0x000D: /* */ case 0x0020: /* */ case 0x00A0: /* */ case 0x1680: case 0x2000: case 0x2001: case 0x2002: case 0x2003: case 0x2004: case 0x2005: case 0x2006: case 0x2007: case 0x2008: case 0x2009: case 0x200A: case 0x2028: /* */ case 0x2029: /* */ case 0x202F: case 0x205F: case 0x3000: case 0xFEFF: /* */ return 1; default: return 0; } } #endif /* _NJS_UTF8_H_INCLUDED_ */ njs-0.8.9/src/njs_utils.c000066400000000000000000000226261474132077100153050ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #include typedef void (*njs_swap_t) (void *a, void *b, size_t size); njs_inline void njs_swap_u128(void *a, void *b, size_t size) { uint64_t u, v, *au, *bu; au = (uint64_t *) a; bu = (uint64_t *) b; u = au[0]; v = au[1]; au[0] = bu[0]; au[1] = bu[1]; bu[0] = u; bu[1] = v; } njs_inline void njs_swap_u128x(void *a, void *b, size_t size) { uint64_t u, v, *au, *bu; au = (uint64_t *) a; bu = (uint64_t *) b; do { u = au[0]; v = au[1]; au[0] = bu[0]; au[1] = bu[1]; bu[0] = u; bu[1] = v; size -= sizeof(uint64_t) * 2; au += 2; bu += 2; } while (size != 0); } njs_inline void njs_swap_bytes(void *a, void *b, size_t size) { uint8_t u, *au, *bu; au = (uint8_t *) a; bu = (uint8_t *) b; while (size-- != 0) { u = *au; *au++ = *bu; *bu++ = u; } } njs_inline njs_swap_t njs_choose_swap(size_t size) { switch (size) { case 2: return njs_swap_u16; case 4: return njs_swap_u32; case 8: return njs_swap_u64; case 16: return njs_swap_u128; default: if ((size % 16) == 0) { return njs_swap_u128x; } if (size == 1) { return njs_swap_u8; } } return njs_swap_bytes; } njs_inline void njs_sift_down(u_char *base, njs_sort_cmp_t cmp, njs_swap_t swap, size_t n, size_t esize, void *ctx, njs_uint_t i) { njs_uint_t c, m; m = i; while (1) { c = 2 * i + esize; if (c < n && cmp(base + m, base + c, ctx) < 0) { m = c; } c += esize; if (c < n && cmp(base + m, base + c, ctx) < 0) { m = c; } if (m == i) { break; } swap(base + i, base + m, esize); i = m; } } static void njs_heapsort(u_char *base, size_t n, size_t esize, njs_swap_t swap, njs_sort_cmp_t cmp, void *ctx) { njs_uint_t i; i = (n / 2) * esize; n = n * esize; for ( ;; ) { njs_sift_down(base, cmp, swap, n, esize, ctx, i); if (i == 0) { break; } i -= esize; } while (n > esize) { swap(base, base + n - esize, esize); n -= esize; njs_sift_down(base, cmp, swap, n, esize, ctx, 0); } } njs_inline void * njs_pivot(void *a, void *b, void *c, njs_sort_cmp_t cmp, void *ctx) { if (cmp(a, c, ctx) < 0) { if (cmp(b, c, ctx) < 0) { return (cmp(a, b, ctx) < 0) ? b : a; } return c; } if (cmp(b, a, ctx) < 0) { return (cmp(b, c, ctx) < 0) ? c : b; } return a; } typedef struct { u_char *base; njs_uint_t elems; } njs_qsort_state_t; #define NJS_MAX_DEPTH 16 void njs_qsort(void *arr, size_t n, size_t esize, njs_sort_cmp_t cmp, void *ctx) { int r; u_char *base, *lt, *gt, *c, *p, *end; njs_uint_t m4; njs_swap_t swap; njs_qsort_state_t stack[NJS_MAX_DEPTH], *sp; if (n < 2) { return; } swap = njs_choose_swap(esize); sp = stack; sp->base = arr; sp->elems = n; sp++; while (sp-- > stack) { base = sp->base; n = sp->elems; end = base + n * esize; while (n > 6) { if (njs_slow_path(sp == &stack[NJS_MAX_DEPTH - 1])) { njs_heapsort(base, n, esize, swap, cmp, ctx); end = base; break; } m4 = (n / 4) * esize; p = njs_pivot(base + m4, base + 2 * m4, base + 3 * m4, cmp, ctx); swap(base, p, esize); /** * Partition * < mid | == mid | unprocessed | mid > * lt p gt */ lt = base; gt = end; p = lt + esize; while (p < gt) { r = cmp(p, lt, ctx); if (r <= 0) { if (r < 0) { swap(lt, p, esize); lt += esize; } p += esize; continue; } swap(gt - esize, p, esize); gt -= esize; } if (lt - base > end - gt) { sp->base = base; sp->elems = (lt - base) / esize; base = gt; n = (end - gt) / esize; } else { sp->base = gt; sp->elems = (end - gt) / esize; n = (lt - base) / esize; } end = base + n * esize; sp++; } /* Insertion sort. */ for (p = base + esize; p < end; p += esize) { for (c = p; c > base && cmp(c, c - esize, ctx) < 0; c -= esize) { swap(c, c - esize, esize); } } } } #define njs_errno_case(e) \ case e: \ return #e; const char* njs_errno_string(int errnum) { switch (errnum) { #ifdef EACCES njs_errno_case(EACCES); #endif #ifdef EADDRINUSE njs_errno_case(EADDRINUSE); #endif #ifdef EADDRNOTAVAIL njs_errno_case(EADDRNOTAVAIL); #endif #ifdef EAFNOSUPPORT njs_errno_case(EAFNOSUPPORT); #endif #ifdef EAGAIN njs_errno_case(EAGAIN); #endif #ifdef EWOULDBLOCK #if EAGAIN != EWOULDBLOCK njs_errno_case(EWOULDBLOCK); #endif #endif #ifdef EALREADY njs_errno_case(EALREADY); #endif #ifdef EBADF njs_errno_case(EBADF); #endif #ifdef EBADMSG njs_errno_case(EBADMSG); #endif #ifdef EBUSY njs_errno_case(EBUSY); #endif #ifdef ECANCELED njs_errno_case(ECANCELED); #endif #ifdef ECHILD njs_errno_case(ECHILD); #endif #ifdef ECONNABORTED njs_errno_case(ECONNABORTED); #endif #ifdef ECONNREFUSED njs_errno_case(ECONNREFUSED); #endif #ifdef ECONNRESET njs_errno_case(ECONNRESET); #endif #ifdef EDEADLK njs_errno_case(EDEADLK); #endif #ifdef EDESTADDRREQ njs_errno_case(EDESTADDRREQ); #endif #ifdef EDOM njs_errno_case(EDOM); #endif #ifdef EDQUOT njs_errno_case(EDQUOT); #endif #ifdef EEXIST njs_errno_case(EEXIST); #endif #ifdef EFAULT njs_errno_case(EFAULT); #endif #ifdef EFBIG njs_errno_case(EFBIG); #endif #ifdef EHOSTUNREACH njs_errno_case(EHOSTUNREACH); #endif #ifdef EIDRM njs_errno_case(EIDRM); #endif #ifdef EILSEQ njs_errno_case(EILSEQ); #endif #ifdef EINPROGRESS njs_errno_case(EINPROGRESS); #endif #ifdef EINTR njs_errno_case(EINTR); #endif #ifdef EINVAL njs_errno_case(EINVAL); #endif #ifdef EIO njs_errno_case(EIO); #endif #ifdef EISCONN njs_errno_case(EISCONN); #endif #ifdef EISDIR njs_errno_case(EISDIR); #endif #ifdef ELOOP njs_errno_case(ELOOP); #endif #ifdef EMFILE njs_errno_case(EMFILE); #endif #ifdef EMLINK njs_errno_case(EMLINK); #endif #ifdef EMSGSIZE njs_errno_case(EMSGSIZE); #endif #ifdef EMULTIHOP njs_errno_case(EMULTIHOP); #endif #ifdef ENAMETOOLONG njs_errno_case(ENAMETOOLONG); #endif #ifdef ENETDOWN njs_errno_case(ENETDOWN); #endif #ifdef ENETRESET njs_errno_case(ENETRESET); #endif #ifdef ENETUNREACH njs_errno_case(ENETUNREACH); #endif #ifdef ENFILE njs_errno_case(ENFILE); #endif #ifdef ENOBUFS njs_errno_case(ENOBUFS); #endif #ifdef ENODATA njs_errno_case(ENODATA); #endif #ifdef ENODEV njs_errno_case(ENODEV); #endif #ifdef ENOENT njs_errno_case(ENOENT); #endif #ifdef ENOEXEC njs_errno_case(ENOEXEC); #endif #ifdef ENOLINK njs_errno_case(ENOLINK); #endif #ifdef ENOLCK #if ENOLINK != ENOLCK njs_errno_case(ENOLCK); #endif #endif #ifdef ENOMEM njs_errno_case(ENOMEM); #endif #ifdef ENOMSG njs_errno_case(ENOMSG); #endif #ifdef ENOPROTOOPT njs_errno_case(ENOPROTOOPT); #endif #ifdef ENOSPC njs_errno_case(ENOSPC); #endif #ifdef ENOSR njs_errno_case(ENOSR); #endif #ifdef ENOSTR njs_errno_case(ENOSTR); #endif #ifdef ENOSYS njs_errno_case(ENOSYS); #endif #ifdef ENOTCONN njs_errno_case(ENOTCONN); #endif #ifdef ENOTDIR njs_errno_case(ENOTDIR); #endif #ifdef ENOTEMPTY #if ENOTEMPTY != EEXIST njs_errno_case(ENOTEMPTY); #endif #endif #ifdef ENOTSOCK njs_errno_case(ENOTSOCK); #endif #ifdef ENOTSUP njs_errno_case(ENOTSUP); #else #ifdef EOPNOTSUPP njs_errno_case(EOPNOTSUPP); #endif #endif #ifdef ENOTTY njs_errno_case(ENOTTY); #endif #ifdef ENXIO njs_errno_case(ENXIO); #endif #ifdef EOVERFLOW njs_errno_case(EOVERFLOW); #endif #ifdef EPERM njs_errno_case(EPERM); #endif #ifdef EPIPE njs_errno_case(EPIPE); #endif #ifdef EPROTO njs_errno_case(EPROTO); #endif #ifdef EPROTONOSUPPORT njs_errno_case(EPROTONOSUPPORT); #endif #ifdef EPROTOTYPE njs_errno_case(EPROTOTYPE); #endif #ifdef ERANGE njs_errno_case(ERANGE); #endif #ifdef EROFS njs_errno_case(EROFS); #endif #ifdef ESPIPE njs_errno_case(ESPIPE); #endif #ifdef ESRCH njs_errno_case(ESRCH); #endif #ifdef ESTALE njs_errno_case(ESTALE); #endif #ifdef ETIME njs_errno_case(ETIME); #endif #ifdef ETIMEDOUT njs_errno_case(ETIMEDOUT); #endif #ifdef ETXTBSY njs_errno_case(ETXTBSY); #endif #ifdef EXDEV njs_errno_case(EXDEV); #endif default: break; } return "UNKNOWN CODE"; } njs-0.8.9/src/njs_utils.h000066400000000000000000000054741474132077100153140ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_UTILS_H_INCLUDED_ #define _NJS_UTILS_H_INCLUDED_ typedef union { float f; uint32_t u; } njs_conv_f32_t; typedef union { double f; uint64_t u; } njs_conv_f64_t; struct NJS_PACKED njs_packed_u16_t { uint16_t v; }; struct NJS_PACKED njs_packed_u32_t { uint32_t v; }; struct NJS_PACKED njs_packed_u64_t { uint64_t v; }; typedef int (*njs_sort_cmp_t)(const void *, const void *, void *ctx); void njs_qsort(void *base, size_t n, size_t size, njs_sort_cmp_t cmp, void *ctx); const char *njs_errno_string(int errnum); njs_inline void njs_swap_u8(void *a, void *b, size_t size) { uint8_t u, *au, *bu; au = (uint8_t *) a; bu = (uint8_t *) b; u = au[0]; au[0] = bu[0]; bu[0] = u; } njs_inline void njs_swap_u16(void *a, void *b, size_t size) { uint16_t u, *au, *bu; au = (uint16_t *) a; bu = (uint16_t *) b; u = au[0]; au[0] = bu[0]; bu[0] = u; } njs_inline void njs_swap_u32(void *a, void *b, size_t size) { uint32_t u, *au, *bu; au = (uint32_t *) a; bu = (uint32_t *) b; u = au[0]; au[0] = bu[0]; bu[0] = u; } njs_inline void njs_swap_u64(void *a, void *b, size_t size) { uint64_t u, *au, *bu; au = (uint64_t *) a; bu = (uint64_t *) b; u = au[0]; au[0] = bu[0]; bu[0] = u; } njs_inline uint16_t njs_bswap_u16(uint16_t u16) { return (u16 >> 8) | (u16 << 8); } njs_inline uint32_t njs_bswap_u32(uint32_t u32) { return ((u32 & 0xff000000) >> 24) | ((u32 & 0x00ff0000) >> 8) | ((u32 & 0x0000ff00) << 8) | ((u32 & 0x000000ff) << 24); } njs_inline uint64_t njs_bswap_u64(uint64_t u64) { return ((u64 & 0xff00000000000000ULL) >> 56) | ((u64 & 0x00ff000000000000ULL) >> 40) | ((u64 & 0x0000ff0000000000ULL) >> 24) | ((u64 & 0x000000ff00000000ULL) >> 8) | ((u64 & 0x00000000ff000000ULL) << 8) | ((u64 & 0x0000000000ff0000ULL) << 24) | ((u64 & 0x000000000000ff00ULL) << 40) | ((u64 & 0x00000000000000ffULL) << 56); } njs_inline uint16_t njs_get_u16(const uint8_t *p) { return ((const struct njs_packed_u16_t *) p)->v; } njs_inline uint32_t njs_get_u32(const uint8_t *p) { return ((const struct njs_packed_u32_t *) p)->v; } njs_inline uint64_t njs_get_u64(const uint8_t *p) { return ((const struct njs_packed_u64_t *) p)->v; } njs_inline void njs_set_u16(uint8_t *p, uint16_t val) { ((struct njs_packed_u16_t *) p)->v = val; } njs_inline void njs_set_u32(uint8_t *p, uint32_t val) { ((struct njs_packed_u32_t *) p)->v = val; } njs_inline void njs_set_u64(uint8_t *p, uint64_t val) { ((struct njs_packed_u64_t *) p)->v = val; } #endif /* _NJS_UTILS_H_INCLUDED_ */ njs-0.8.9/src/njs_value.c000066400000000000000000001251101474132077100152510ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include static njs_int_t njs_object_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_object_t *object, const njs_value_t *key); static njs_int_t njs_array_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_array_t *array, uint32_t index); static njs_int_t njs_typed_array_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_typed_array_t *array, uint32_t index); static njs_int_t njs_string_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *object, uint32_t index); static njs_int_t njs_external_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *value); const njs_value_t njs_value_null = njs_value(NJS_NULL, 0, 0.0); const njs_value_t njs_value_undefined = njs_value(NJS_UNDEFINED, 0, 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, NAN); const njs_value_t njs_value_invalid = njs_value(NJS_INVALID, 0, 0.0); const njs_value_t njs_string_empty = njs_string(""); const njs_value_t njs_string_empty_regexp = njs_string("(?:)"); const njs_value_t njs_string_comma = njs_string(","); const njs_value_t njs_string_null = njs_string("null"); const njs_value_t njs_string_undefined = njs_string("undefined"); 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_zero = njs_string("-0"); 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_symbol = njs_string("symbol"); const njs_value_t njs_string_string = njs_string("string"); const njs_value_t njs_string_name = njs_string("name"); const njs_value_t njs_string_data = njs_string("data"); const njs_value_t njs_string_type = njs_string("type"); const njs_value_t njs_string_external = njs_string("external"); const njs_value_t njs_string_invalid = njs_string("invalid"); 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_anonymous = njs_string("anonymous"); const njs_value_t njs_string_memory_error = njs_string("MemoryError"); /* * 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. */ njs_int_t njs_value_to_primitive(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value, njs_uint_t hint) { njs_int_t ret; njs_uint_t tries; njs_value_t method, retval; njs_lvlhsh_query_t lhq; static const uint32_t hashes[] = { NJS_VALUE_OF_HASH, NJS_TO_STRING_HASH, }; static const njs_str_t names[] = { njs_str("valueOf"), njs_str("toString"), }; if (njs_is_primitive(value)) { *dst = *value; return NJS_OK; } tries = 0; lhq.proto = &njs_object_hash_proto; for ( ;; ) { ret = NJS_ERROR; if (njs_is_object(value) && tries < 2) { hint ^= tries++; lhq.key_hash = hashes[hint]; lhq.key = names[hint]; ret = njs_object_property(vm, njs_object(value), &lhq, &method); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (njs_is_function(&method)) { ret = njs_function_apply(vm, njs_function(&method), value, 1, &retval); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (njs_is_primitive(&retval)) { break; } } /* Try the second method. */ continue; } njs_type_error(vm, "Cannot convert object to primitive value"); return ret; } *dst = retval; return NJS_OK; } njs_array_t * njs_value_enumerate(njs_vm_t *vm, njs_value_t *value, uint32_t flags) { njs_int_t ret; njs_value_t keys; njs_object_value_t obj_val; njs_exotic_slots_t *slots; if (njs_is_object(value)) { if ((flags & NJS_ENUM_KEYS) && (flags & NJS_ENUM_STRING)) { slots = njs_object_slots(value); if (slots != NULL && slots->keys != NULL) { ret = slots->keys(vm, value, &keys); if (njs_slow_path(ret != NJS_OK)) { return NULL; } return njs_array(&keys); } } return njs_object_enumerate(vm, njs_object(value), flags); } if (value->type != NJS_STRING) { return njs_array_alloc(vm, 1, 0, NJS_ARRAY_SPARE); } obj_val.object = vm->string_object; obj_val.value = *value; return njs_object_enumerate(vm, (njs_object_t *) &obj_val, flags); } njs_array_t * njs_value_own_enumerate(njs_vm_t *vm, njs_value_t *value, uint32_t flags) { njs_int_t ret, len; njs_array_t *values, *entry; njs_value_t keys, *k, *dst, *end; njs_object_value_t obj_val; njs_exotic_slots_t *slots; if (njs_is_object(value)) { slots = njs_object_slots(value); if (slots == NULL || slots->keys == NULL) { return njs_object_own_enumerate(vm, njs_object(value), flags); } ret = slots->keys(vm, value, &keys); if (njs_slow_path(ret != NJS_OK)) { return NULL; } switch (njs_object_enum_kind(flags)) { case NJS_ENUM_KEYS: return njs_array(&keys); case NJS_ENUM_VALUES: len = njs_array_len(&keys); k = njs_array(&keys)->start; end = k + len; values = njs_array_alloc(vm, 1, len, 0); if (njs_slow_path(values == NULL)) { return NULL; } dst = values->start; while (k < end) { ret = njs_value_property(vm, value, k++, dst++); if (njs_slow_path(ret != NJS_OK)) { return NULL; } } return values; case NJS_ENUM_BOTH: default: len = njs_array_len(&keys); k = njs_array(&keys)->start; end = k + len; values = njs_array_alloc(vm, 1, len, 0); if (njs_slow_path(values == NULL)) { return NULL; } dst = values->start; while (k < end) { entry = njs_array_alloc(vm, 1, 2, 0); if (njs_slow_path(entry == NULL)) { return NULL; } ret = njs_value_property(vm, value, k, &entry->start[1]); if (njs_slow_path(ret != NJS_OK)) { return NULL; } njs_value_assign(&entry->start[0], k++); njs_set_array(dst++, entry); } return values; } } if (value->type != NJS_STRING) { return njs_array_alloc(vm, 1, 0, NJS_ARRAY_SPARE); } obj_val.object = vm->string_object; obj_val.value = *value; return njs_object_own_enumerate(vm, (njs_object_t *) &obj_val, flags); } njs_int_t njs_value_of(njs_vm_t *vm, njs_value_t *value, njs_value_t *retval) { njs_int_t ret; static const njs_value_t value_of = njs_string("valueOf"); if (njs_slow_path(!njs_is_object(value))) { return NJS_DECLINED; } ret = njs_value_property(vm, value, njs_value_arg(&value_of), retval); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (!njs_is_function(retval)) { njs_type_error(vm, "object.valueOf is not a function"); return NJS_ERROR; } return njs_function_apply(vm, njs_function(retval), value, 1, retval); } njs_int_t njs_value_length(njs_vm_t *vm, njs_value_t *value, int64_t *length) { njs_string_prop_t string_prop; if (njs_is_string(value)) { *length = njs_string_prop(&string_prop, value); } else if (njs_is_primitive(value)) { *length = 0; } else if (njs_is_fast_array(value)) { *length = njs_array_len(value); } else { return njs_object_length(vm, value, length); } return NJS_OK; } const char * njs_type_string(njs_value_type_t type) { switch (type) { case NJS_NULL: return "null"; case NJS_UNDEFINED: return "undefined"; case NJS_BOOLEAN: return "boolean"; case NJS_NUMBER: return "number"; case NJS_SYMBOL: return "symbol"; case NJS_STRING: return "string"; case NJS_INVALID: return "invalid"; case NJS_OBJECT: case NJS_OBJECT_VALUE: return "object"; case NJS_ARRAY: return "array"; case NJS_ARRAY_BUFFER: return "array buffer"; case NJS_TYPED_ARRAY: return "typed array"; case NJS_FUNCTION: return "function"; case NJS_REGEXP: return "regexp"; case NJS_DATE: return "date"; case NJS_PROMISE: return "promise"; default: return NULL; } } void njs_value_undefined_set(njs_value_t *value) { njs_set_undefined(value); } void njs_value_null_set(njs_value_t *value) { njs_set_null(value); } void njs_value_invalid_set(njs_value_t *value) { njs_set_invalid(value); } void njs_value_boolean_set(njs_value_t *value, int yn) { njs_set_boolean(value, yn); } void njs_value_number_set(njs_value_t *value, double num) { njs_set_number(value, num); } void njs_value_function_set(njs_value_t *value, njs_function_t *function) { njs_set_function(value, function); } void njs_value_external_set(njs_value_t *value, njs_external_ptr_t external) { njs_assert(njs_value_is_external(value, NJS_PROTO_ID_ANY)); njs_object_data(value) = external; } uint8_t njs_value_bool(const njs_value_t *value) { return njs_bool(value); } double njs_value_number(const njs_value_t *value) { return njs_number(value); } njs_function_t * njs_value_function(const njs_value_t *value) { return njs_function(value); } njs_function_native_t njs_value_native_function(const njs_value_t *value) { njs_function_t *function; if (njs_is_function(value)) { function = njs_function(value); if (function->native) { return function->u.native; } } return NULL; } void * njs_value_ptr(const njs_value_t *value) { return njs_data(value); } njs_external_ptr_t njs_value_external(const njs_value_t *value) { njs_assert(njs_value_is_external(value, NJS_PROTO_ID_ANY)); return njs_object_data(value); } njs_int_t njs_value_is_null(const njs_value_t *value) { return njs_is_null(value); } njs_int_t njs_value_is_undefined(const njs_value_t *value) { return njs_is_undefined(value); } njs_int_t njs_value_is_null_or_undefined(const njs_value_t *value) { return njs_is_null_or_undefined(value); } njs_int_t njs_value_is_valid(const njs_value_t *value) { return njs_is_valid(value); } njs_int_t njs_value_is_boolean(const njs_value_t *value) { return njs_is_boolean(value); } njs_int_t njs_value_is_number(const njs_value_t *value) { return njs_is_number(value); } njs_int_t njs_value_is_valid_number(const njs_value_t *value) { return njs_is_number(value) && !isnan(njs_number(value)) && !isinf(njs_number(value)); } njs_int_t njs_value_is_string(const njs_value_t *value) { return njs_is_string(value); } njs_int_t njs_value_is_object(const njs_value_t *value) { return njs_is_object(value); } njs_int_t njs_value_is_error(const njs_value_t *value) { return njs_is_error(value); } njs_int_t njs_value_is_external(const njs_value_t *value, njs_int_t proto_id) { return njs_is_object_data(value, njs_make_tag(proto_id)); } njs_int_t njs_value_is_array(const njs_value_t *value) { return njs_is_array(value); } njs_int_t njs_value_is_function(const njs_value_t *value) { return njs_is_function(value); } njs_int_t njs_value_is_buffer(const njs_value_t *value) { return njs_is_typed_array(value); } njs_int_t njs_value_is_data_view(const njs_value_t *value) { return njs_is_data_view(value); } /* * ES5.1, 8.12.1: [[GetOwnProperty]], [[GetProperty]]. * The njs_property_query() returns values * NJS_OK property has been found in object, * retval of type njs_object_prop_t * is in pq->lhq.value. * in NJS_PROPERTY_QUERY_GET * prop->type is NJS_PROPERTY or NJS_PROPERTY_HANDLER. * in NJS_PROPERTY_QUERY_SET, NJS_PROPERTY_QUERY_DELETE * prop->type is NJS_PROPERTY, NJS_PROPERTY_REF, NJS_PROPERTY_PLACE_REF, * NJS_PROPERTY_TYPED_ARRAY_REF or * NJS_PROPERTY_HANDLER. * NJS_DECLINED property was not found in object, * if pq->lhq.value != NULL it contains retval of type * njs_object_prop_t * where prop->type is NJS_WHITEOUT * NJS_ERROR exception has been thrown. */ njs_int_t njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *value, njs_value_t *key) { double num; uint32_t index; njs_int_t ret; njs_object_t *obj; njs_function_t *function; njs_assert(njs_is_index_or_key(key)); switch (value->type) { case NJS_BOOLEAN: case NJS_NUMBER: case NJS_SYMBOL: index = njs_primitive_prototype_index(value->type); obj = njs_vm_proto(vm, index); break; case NJS_STRING: if (njs_fast_path(!njs_is_null_or_undefined_or_boolean(key))) { num = njs_key_to_index(key); if (njs_fast_path(njs_key_is_integer_index(num, key))) { return njs_string_property_query(vm, pq, value, num); } } obj = &vm->string_object; break; case NJS_OBJECT: case NJS_ARRAY: case NJS_ARRAY_BUFFER: case NJS_DATA_VIEW: case NJS_TYPED_ARRAY: case NJS_REGEXP: case NJS_DATE: case NJS_PROMISE: case NJS_OBJECT_VALUE: obj = njs_object(value); break; case NJS_FUNCTION: function = njs_function_value_copy(vm, value); if (njs_slow_path(function == NULL)) { return NJS_ERROR; } obj = &function->object; break; case NJS_UNDEFINED: case NJS_NULL: default: ret = njs_primitive_value_to_string(vm, &pq->key, key); if (njs_fast_path(ret == NJS_OK)) { njs_string_get(&pq->key, &pq->lhq.key); njs_type_error(vm, "cannot get property \"%V\" of %s", &pq->lhq.key, njs_is_null(value) ? "null" : "undefined"); return NJS_ERROR; } njs_type_error(vm, "cannot get property \"unknown\" of %s", njs_is_null(value) ? "null" : "undefined"); return NJS_ERROR; } ret = njs_primitive_value_to_key(vm, &pq->key, key); if (njs_fast_path(ret == NJS_OK)) { if (njs_is_symbol(key)) { pq->lhq.key_hash = njs_symbol_key(key); pq->lhq.key.start = NULL; } else { njs_string_get(&pq->key, &pq->lhq.key); if (pq->lhq.key_hash == 0) { pq->lhq.key_hash = njs_djb_hash(pq->lhq.key.start, pq->lhq.key.length); } } ret = njs_object_property_query(vm, pq, obj, key); if (njs_slow_path(ret == NJS_DECLINED && obj->slots != NULL)) { return njs_external_property_query(vm, pq, value); } } return ret; } static njs_int_t njs_object_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_object_t *object, const njs_value_t *key) { double num; njs_int_t ret; njs_bool_t own; njs_array_t *array; njs_object_t *proto; njs_object_prop_t *prop; njs_typed_array_t *tarray; njs_object_value_t *ov; pq->lhq.proto = &njs_object_hash_proto; own = pq->own; pq->own = 1; proto = object; do { switch (proto->type) { case NJS_ARRAY: array = (njs_array_t *) proto; num = njs_key_to_index(key); if (njs_fast_path(njs_key_is_integer_index(num, key))) { ret = njs_array_property_query(vm, pq, array, num); if (njs_fast_path(ret != NJS_DECLINED)) { return (ret == NJS_DONE) ? NJS_DECLINED : ret; } } break; case NJS_TYPED_ARRAY: num = njs_key_to_index(key); if (njs_fast_path(njs_key_is_integer_index(num, key))) { tarray = (njs_typed_array_t *) proto; return njs_typed_array_property_query(vm, pq, tarray, num); } if (!isnan(num)) { return NJS_DECLINED; } break; case NJS_OBJECT_VALUE: ov = (njs_object_value_t *) proto; if (!njs_is_string(&ov->value)) { break; } num = njs_key_to_index(key); if (njs_fast_path(njs_key_is_integer_index(num, key))) { ov = (njs_object_value_t *) proto; ret = njs_string_property_query(vm, pq, &ov->value, num); if (njs_fast_path(ret != NJS_DECLINED)) { return ret; } } break; default: break; } ret = njs_lvlhsh_find(&proto->hash, &pq->lhq); if (ret == NJS_OK) { prop = pq->lhq.value; if (prop->type != NJS_WHITEOUT) { return ret; } if (pq->own) { pq->own_whiteout = &proto->hash; } } else { ret = njs_lvlhsh_find(&proto->shared_hash, &pq->lhq); if (ret == NJS_OK) { return njs_prop_private_copy(vm, pq, proto); } } if (own) { return NJS_DECLINED; } pq->own = 0; proto = proto->__proto__; } while (proto != NULL); return NJS_DECLINED; } static njs_int_t njs_array_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_array_t *array, uint32_t index) { int64_t length; uint64_t size; njs_int_t ret; njs_bool_t resized; njs_value_t *setval, value; njs_object_prop_t *prop; resized = 0; if (pq->query == NJS_PROPERTY_QUERY_SET) { if (!array->object.extensible) { return NJS_DECLINED; } if (njs_fast_path(array->object.fast_array)) { if (njs_fast_path(index < NJS_ARRAY_LARGE_OBJECT_LENGTH)) { if (index >= array->length) { size = index - array->length + 1; ret = njs_array_expand(vm, array, 0, size); if (njs_slow_path(ret != NJS_OK)) { return ret; } setval = &array->start[array->length]; while (size != 0) { njs_set_invalid(setval); setval++; size--; } array->length = index + 1; resized = 1; } goto prop; } ret = njs_array_convert_to_slow_array(vm, array); if (njs_slow_path(ret != NJS_OK)) { return ret; } } njs_set_array(&value, array); ret = njs_object_length(vm, &value, &length); if (njs_slow_path(ret != NJS_OK)) { return ret; } if ((index + 1) > length) { ret = njs_array_length_redefine(vm, &value, index + 1, 1); if (njs_slow_path(ret != NJS_OK)) { return ret; } } ret = njs_lvlhsh_find(&array->object.hash, &pq->lhq); if (ret == NJS_OK) { prop = pq->lhq.value; if (prop->type != NJS_WHITEOUT) { return NJS_OK; } if (pq->own) { pq->own_whiteout = &array->object.hash; } return NJS_DECLINED; } return NJS_DONE; } if (njs_slow_path(!array->object.fast_array)) { return NJS_DECLINED; } if (index >= array->length) { return NJS_DECLINED; } prop: prop = &pq->scratch; if (pq->query == NJS_PROPERTY_QUERY_GET) { if (!njs_is_valid(&array->start[index])) { return NJS_DECLINED; } njs_value_assign(njs_prop_value(prop), &array->start[index]); prop->type = NJS_PROPERTY; } else { njs_prop_ref(prop) = &array->start[index]; prop->type = resized ? NJS_PROPERTY_PLACE_REF : NJS_PROPERTY_REF; } njs_set_number(&prop->name, index); prop->writable = 1; prop->enumerable = 1; prop->configurable = 1; pq->lhq.value = prop; return NJS_OK; } static njs_int_t njs_typed_array_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_typed_array_t *array, uint32_t index) { njs_object_prop_t *prop; if (njs_slow_path(njs_is_detached_buffer(array->buffer))) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } if (index >= njs_typed_array_length(array)) { return NJS_DECLINED; } prop = &pq->scratch; if (pq->query == NJS_PROPERTY_QUERY_GET) { njs_set_number(njs_prop_value(prop), njs_typed_array_prop(array, index)); prop->type = NJS_PROPERTY; } else { njs_prop_typed_ref(prop) = array; njs_prop_magic32(prop) = index; prop->type = NJS_PROPERTY_TYPED_ARRAY_REF; } prop->writable = 1; prop->enumerable = 1; prop->configurable = 0; pq->lhq.value = prop; return NJS_OK; } static njs_int_t njs_string_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *object, uint32_t index) { njs_slice_prop_t slice; njs_object_prop_t *prop; njs_string_prop_t string; prop = &pq->scratch; slice.start = index; slice.length = 1; slice.string_length = njs_string_prop(&string, object); if (slice.start < slice.string_length) { /* * A single codepoint string fits in retval * so the function cannot fail. */ (void) njs_string_slice(vm, njs_prop_value(prop), &string, &slice); prop->type = NJS_PROPERTY; prop->writable = 0; prop->enumerable = 1; prop->configurable = 0; pq->lhq.value = prop; if (pq->query != NJS_PROPERTY_QUERY_GET) { /* pq->lhq.key is used by NJS_VMCODE_PROPERTY_SET for TypeError */ njs_uint32_to_string(&pq->key, index); njs_string_get(&pq->key, &pq->lhq.key); } return NJS_OK; } return NJS_DECLINED; } static njs_int_t njs_external_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *value) { njs_object_prop_t *prop; njs_exotic_slots_t *slots; slots = njs_object_slots(value); if (njs_slow_path(slots->prop_handler == NULL)) { return NJS_DECLINED; } pq->temp = 1; prop = &pq->scratch; njs_memzero(prop, sizeof(njs_object_prop_t)); /* * njs_memzero() does also: * prop->type = NJS_PROPERTY; * prop->writable = 0; * prop->configurable = 0; */ njs_prop_magic32(prop) = slots->magic32; prop->name = pq->key; pq->lhq.value = prop; prop->writable = slots->writable; prop->configurable = slots->configurable; prop->enumerable = slots->enumerable; switch (pq->query) { case NJS_PROPERTY_QUERY_GET: return slots->prop_handler(vm, prop, value, NULL, njs_prop_value(prop)); case NJS_PROPERTY_QUERY_SET: if (slots->writable == 0) { return NJS_OK; } break; case NJS_PROPERTY_QUERY_DELETE: if (slots->configurable == 0) { return NJS_OK; } break; } prop->type = NJS_PROPERTY_HANDLER; njs_prop_handler(prop) = slots->prop_handler; return NJS_OK; } njs_int_t njs_value_property(njs_vm_t *vm, njs_value_t *value, njs_value_t *key, njs_value_t *retval) { double num; uint32_t index; njs_int_t ret; njs_array_t *array; njs_object_prop_t *prop; njs_typed_array_t *tarray; njs_property_query_t pq; njs_assert(njs_is_index_or_key(key)); if (njs_fast_path(njs_is_number(key))) { num = njs_number(key); if (njs_slow_path(!njs_number_is_integer_index(num))) { goto slow_path; } index = (uint32_t) num; if (njs_is_typed_array(value)) { tarray = njs_typed_array(value); if (njs_slow_path(njs_is_detached_buffer(tarray->buffer))) { njs_type_error(vm, "detached buffer"); return NJS_ERROR; } if (njs_slow_path(index >= njs_typed_array_length(tarray))) { goto slow_path; } njs_set_number(retval, njs_typed_array_prop(tarray, index)); return NJS_OK; } if (njs_slow_path(!(njs_is_object(value) && njs_object(value)->fast_array))) { goto slow_path; } /* njs_is_fast_array() */ array = njs_array(value); if (njs_slow_path(index >= array->length || !njs_is_valid(&array->start[index]))) { goto slow_path; } njs_value_assign(retval, &array->start[index]); return NJS_OK; } slow_path: njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0, 0); ret = njs_property_query(vm, &pq, value, key); switch (ret) { case NJS_OK: prop = pq.lhq.value; switch (prop->type) { case NJS_PROPERTY: case NJS_ACCESSOR: if (njs_is_data_descriptor(prop)) { njs_value_assign(retval, njs_prop_value(prop)); break; } if (njs_prop_getter(prop) == NULL) { njs_set_undefined(retval); break; } return njs_function_apply(vm, njs_prop_getter(prop), value, 1, retval); case NJS_PROPERTY_HANDLER: pq.scratch = *prop; prop = &pq.scratch; ret = njs_prop_handler(prop)(vm, prop, value, NULL, njs_prop_value(prop)); if (njs_slow_path(ret != NJS_OK)) { if (ret == NJS_ERROR) { return ret; } njs_set_undefined(njs_prop_value(prop)); } njs_value_assign(retval, njs_prop_value(prop)); break; default: njs_internal_error(vm, "unexpected property type \"%s\" " "while getting", njs_prop_type_string(prop->type)); return NJS_ERROR; } break; case NJS_DECLINED: njs_set_undefined(retval); return NJS_DECLINED; case NJS_ERROR: default: return NJS_ERROR; } return NJS_OK; } njs_int_t njs_value_property_set(njs_vm_t *vm, njs_value_t *value, njs_value_t *key, njs_value_t *setval) { double num; uint32_t index; njs_int_t ret; njs_array_t *array; njs_value_t retval; njs_flathsh_elt_t *elt; njs_object_prop_t *prop; njs_typed_array_t *tarray; njs_flathsh_descr_t *h; njs_property_query_t pq; static const njs_str_t length_key = njs_str("length"); njs_assert(njs_is_index_or_key(key)); if (njs_fast_path(njs_is_number(key))) { num = njs_number(key); if (njs_slow_path(!njs_number_is_integer_index(num))) { goto slow_path; } index = (uint32_t) num; if (njs_is_typed_array(value)) { tarray = njs_typed_array(value); if (njs_fast_path(index < njs_typed_array_length(tarray))) { return njs_typed_array_set_value(vm, tarray, index, setval); } return NJS_OK; } if (njs_slow_path(!(njs_is_object(value) && njs_object(value)->fast_array))) { goto slow_path; } /* NJS_ARRAY */ array = njs_array(value); if (njs_slow_path(index >= array->length)) { goto slow_path; } njs_value_assign(&array->start[index], setval); return NJS_OK; } slow_path: if (njs_is_primitive(value)) { njs_type_error(vm, "property set on primitive %s type", njs_type_string(value->type)); return NJS_ERROR; } njs_property_query_init(&pq, NJS_PROPERTY_QUERY_SET, 0, 0); ret = njs_property_query(vm, &pq, value, key); switch (ret) { case NJS_OK: prop = pq.lhq.value; if (njs_is_data_descriptor(prop)) { if (!prop->writable) { njs_key_string_get(vm, &pq.key, &pq.lhq.key); njs_type_error(vm, "Cannot assign to read-only property \"%V\" of %s", &pq.lhq.key, njs_type_string(value->type)); return NJS_ERROR; } } else { if (njs_prop_setter(prop) != NULL) { return njs_function_call(vm, njs_prop_setter(prop), value, setval, 1, &retval); } njs_key_string_get(vm, &pq.key, &pq.lhq.key); njs_type_error(vm, "Cannot set property \"%V\" of %s which has only a getter", &pq.lhq.key, njs_type_string(value->type)); return NJS_ERROR; } if (prop->type == NJS_PROPERTY_HANDLER) { ret = njs_prop_handler(prop)(vm, prop, value, setval, &retval); if (njs_slow_path(ret != NJS_DECLINED)) { return ret; } } if (pq.own) { switch (prop->type) { case NJS_PROPERTY: if (njs_is_array(value)) { if (njs_slow_path(pq.lhq.key_hash == NJS_LENGTH_HASH)) { if (njs_strstr_eq(&pq.lhq.key, &length_key)) { return njs_array_length_set(vm, value, prop, setval); } } } goto found; case NJS_PROPERTY_REF: case NJS_PROPERTY_PLACE_REF: njs_value_assign(njs_prop_ref(prop), setval); return NJS_OK; case NJS_PROPERTY_TYPED_ARRAY_REF: return njs_typed_array_set_value(vm, njs_typed_array(njs_prop_value(prop)), njs_prop_magic32(prop), setval); default: njs_internal_error(vm, "unexpected property type \"%s\" " "while setting", njs_prop_type_string(prop->type)); return NJS_ERROR; } break; } /* Fall through. */ case NJS_DECLINED: if (njs_slow_path(pq.own_whiteout != NULL)) { /* * Previously deleted property. * * delete it, and then * insert it again as new one to preserve insertion order. */ if (!njs_object(value)->extensible) { goto fail; } pq.lhq.pool = vm->mem_pool; int rc = njs_lvlhsh_delete(pq.own_whiteout, &pq.lhq); if (rc != NJS_OK) { return NJS_ERROR; } h = pq.own_whiteout->slot; if (h == NULL) { h = njs_flathsh_new(&pq.lhq); if (njs_slow_path(h == NULL)) { return NJS_ERROR; } pq.own_whiteout->slot = h; } elt = njs_flathsh_add_elt(pq.own_whiteout, &pq.lhq); if (njs_slow_path(elt == NULL)) { return NJS_ERROR; } elt->value = (&pq.lhq)->value; prop = (njs_object_prop_t *) elt->value; prop->type = NJS_PROPERTY; prop->enumerable = 1; prop->configurable = 1; prop->writable = 1; goto found; } if (njs_slow_path(pq.own && njs_is_typed_array(value) && njs_is_string(key))) { /* Integer-Indexed Exotic Objects [[DefineOwnProperty]]. */ if (!isnan(njs_string_to_index(key))) { return NJS_OK; } } break; case NJS_ERROR: default: return NJS_ERROR; } if (njs_slow_path(!njs_object(value)->extensible)) { goto fail; } prop = njs_object_prop_alloc(vm, &pq.key, &njs_value_undefined, 1); if (njs_slow_path(prop == NULL)) { return NJS_ERROR; } pq.lhq.replace = 0; pq.lhq.value = prop; pq.lhq.pool = vm->mem_pool; ret = njs_lvlhsh_insert(njs_object_hash(value), &pq.lhq); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "lvlhsh insert failed"); return NJS_ERROR; } found: njs_value_assign(njs_prop_value(prop), setval); return NJS_OK; fail: njs_key_string_get(vm, &pq.key, &pq.lhq.key); njs_type_error(vm, "Cannot add property \"%V\", object is not extensible", &pq.lhq.key); return NJS_ERROR; } njs_int_t njs_value_property_delete(njs_vm_t *vm, njs_value_t *value, njs_value_t *key, njs_value_t *removed, njs_bool_t thrw) { double num; uint32_t index; njs_int_t ret; njs_array_t *array; njs_object_prop_t *prop; njs_property_query_t pq; njs_assert(njs_is_index_or_key(key)); if (njs_fast_path(njs_is_number(key))) { if (njs_slow_path(!(njs_is_fast_array(value)))) { goto slow_path; } num = njs_number(key); if (njs_slow_path(!njs_number_is_integer_index(num))) { goto slow_path; } index = (uint32_t) num; array = njs_array(value); if (njs_slow_path(index >= array->length)) { goto slow_path; } njs_value_assign(&array->start[index], &njs_value_invalid); return NJS_OK; } slow_path: njs_property_query_init(&pq, NJS_PROPERTY_QUERY_DELETE, 0, 1); ret = njs_property_query(vm, &pq, value, key); if (njs_slow_path(ret != NJS_OK)) { return ret; } prop = pq.lhq.value; if (njs_slow_path(!prop->configurable)) { if (thrw) { njs_key_string_get(vm, &pq.key, &pq.lhq.key); njs_type_error(vm, "Cannot delete property \"%V\" of %s", &pq.lhq.key, njs_type_string(value->type)); return NJS_ERROR; } return NJS_OK; } switch (prop->type) { case NJS_PROPERTY_HANDLER: if (njs_is_object(value) && njs_object_slots(value) != NULL) { ret = njs_prop_handler(prop)(vm, prop, value, NULL, NULL); if (njs_slow_path(ret != NJS_DECLINED)) { return ret; } } /* Fall through. */ case NJS_PROPERTY: break; case NJS_ACCESSOR: if (removed == NULL) { break; } if (njs_prop_getter(prop) == NULL) { njs_set_undefined(removed); break; } return njs_function_apply(vm, njs_prop_getter(prop), value, 1, removed); case NJS_PROPERTY_REF: case NJS_PROPERTY_PLACE_REF: if (removed != NULL) { njs_value_assign(removed, njs_prop_ref(prop)); } njs_set_invalid(njs_prop_ref(prop)); return NJS_OK; default: njs_internal_error(vm, "unexpected property type \"%s\" " "while deleting", njs_prop_type_string(prop->type)); return NJS_ERROR; } if (removed != NULL) { if (njs_is_valid(njs_prop_value(prop))) { njs_value_assign(removed, njs_prop_value(prop)); } else { njs_set_undefined(removed); } } prop->type = NJS_WHITEOUT; prop->enum_in_object_hash = 1; return NJS_OK; } njs_int_t njs_primitive_value_to_string(njs_vm_t *vm, njs_value_t *dst, const njs_value_t *src) { const njs_value_t *value; switch (src->type) { case NJS_NULL: value = &njs_string_null; break; case NJS_UNDEFINED: value = &njs_string_undefined; 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_SYMBOL: njs_symbol_conversion_failed(vm, 1); return NJS_ERROR; case NJS_STRING: value = src; break; default: return NJS_ERROR; } *dst = *value; return NJS_OK; } njs_int_t njs_primitive_value_to_chain(njs_vm_t *vm, njs_chb_t *chain, const njs_value_t *src) { njs_string_prop_t string; switch (src->type) { case NJS_NULL: njs_chb_append_literal(chain, "null"); return njs_length("null"); case NJS_UNDEFINED: njs_chb_append_literal(chain, "undefined"); return njs_length("undefined"); case NJS_BOOLEAN: if (njs_is_true(src)) { njs_chb_append_literal(chain, "true"); return njs_length("true"); } else { njs_chb_append_literal(chain, "false"); return njs_length("false"); } case NJS_NUMBER: return njs_number_to_chain(vm, chain, njs_number(src)); case NJS_SYMBOL: njs_symbol_conversion_failed(vm, 1); return NJS_ERROR; case NJS_STRING: (void) njs_string_prop(&string, src); njs_chb_append(chain, string.start, string.size); return string.length; default: return NJS_ERROR; } } njs_int_t njs_value_to_integer(njs_vm_t *vm, njs_value_t *value, int64_t *dst) { double num; njs_int_t ret; ret = njs_value_to_number(vm, value, &num); if (njs_slow_path(ret != NJS_OK)) { return ret; } *dst = njs_number_to_integer(num); return NJS_OK; } njs_int_t njs_value_to_object(njs_vm_t *vm, njs_value_t *value) { njs_uint_t index; njs_object_value_t *object; if (njs_slow_path(njs_is_null_or_undefined(value))) { njs_type_error(vm, "cannot convert null or undefined to object"); return NJS_ERROR; } if (njs_fast_path(njs_is_object(value))) { return NJS_OK; } if (njs_is_primitive(value)) { index = njs_primitive_prototype_index(value->type); object = njs_object_value_alloc(vm, index, 0, value); if (njs_slow_path(object == NULL)) { return NJS_ERROR; } njs_set_object_value(value, object); return NJS_OK; } njs_type_error(vm, "cannot convert %s to object", njs_type_string(value->type)); return NJS_ERROR; } void njs_symbol_conversion_failed(njs_vm_t *vm, njs_bool_t to_string) { njs_type_error(vm, to_string ? "Cannot convert a Symbol value to a string" : "Cannot convert a Symbol value to a number"); } njs_int_t njs_value_construct(njs_vm_t *vm, njs_value_t *constructor, njs_value_t *args, njs_uint_t nargs, njs_value_t *retval) { njs_value_t this; njs_object_t *object; object = njs_function_new_object(vm, constructor); if (njs_slow_path(object == NULL)) { return NJS_ERROR; } njs_set_object(&this, object); return njs_function_call2(vm, njs_function(constructor), &this, args, nargs, retval, 1); } njs_int_t njs_value_species_constructor(njs_vm_t *vm, njs_value_t *object, njs_value_t *default_constructor, njs_value_t *dst) { njs_int_t ret; njs_value_t constructor, retval; static const njs_value_t string_constructor = njs_string("constructor"); static const njs_value_t string_species = njs_wellknown_symbol(NJS_SYMBOL_SPECIES); ret = njs_value_property(vm, object, njs_value_arg(&string_constructor), &constructor); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } if (njs_is_undefined(&constructor)) { goto default_constructor; } if (njs_slow_path(!njs_is_object(&constructor))) { njs_type_error(vm, "constructor is not object"); return NJS_ERROR; } ret = njs_value_property(vm, &constructor, njs_value_arg(&string_species), &retval); if (njs_slow_path(ret == NJS_ERROR)) { return NJS_ERROR; } if (njs_value_is_null_or_undefined(&retval)) { goto default_constructor; } if (!njs_is_function(&retval)) { njs_type_error(vm, "object does not contain a constructor"); return NJS_ERROR; } *dst = retval; return NJS_OK; default_constructor: *dst = *default_constructor; return NJS_OK; } njs_int_t njs_value_method(njs_vm_t *vm, njs_value_t *value, njs_value_t *key, njs_value_t *retval) { njs_int_t ret; ret = njs_value_to_object(vm, value); if (njs_slow_path(ret != NJS_OK)) { return ret; } ret = njs_value_property(vm, value, key, retval); if (njs_slow_path(ret != NJS_OK)) { return (ret == NJS_DECLINED) ? NJS_OK : ret; } if (njs_slow_path(!njs_is_function(retval))) { njs_type_error(vm, "method is not callable"); return NJS_ERROR; } return NJS_OK; } njs-0.8.9/src/njs_value.h000066400000000000000000001132731474132077100152650ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_VALUE_H_INCLUDED_ #define _NJS_VALUE_H_INCLUDED_ /* * The order of the enum is used in njs_vmcode_typeof() * and njs_object_prototype_to_string(). */ typedef enum { NJS_NULL, NJS_UNDEFINED, /* The order of the above type is used in njs_is_null_or_undefined(). */ NJS_BOOLEAN, /* * The order of the above type is used in * njs_is_null_or_undefined_or_boolean(). */ NJS_NUMBER, /* * 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_SYMBOL, NJS_STRING, /* The order of the above type is used in njs_is_primitive(). */ NJS_DATA, /* * The invalid value type is used: * for uninitialized array members, * to detect non-declared explicitly or implicitly variables, * for native property getters. */ NJS_INVALID, NJS_OBJECT = 0x10, NJS_ARRAY, #define NJS_OBJECT_SPECIAL_MIN (NJS_FUNCTION) NJS_FUNCTION, NJS_REGEXP, NJS_DATE, NJS_TYPED_ARRAY, #define NJS_OBJECT_SPECIAL_MAX (NJS_TYPED_ARRAY + 1) NJS_PROMISE, NJS_OBJECT_VALUE, NJS_ARRAY_BUFFER, NJS_DATA_VIEW, NJS_VALUE_TYPE_MAX } njs_value_type_t; typedef enum { NJS_DATA_TAG_ANY = 0, NJS_DATA_TAG_EXTERNAL, NJS_DATA_TAG_TEXT_ENCODER, NJS_DATA_TAG_TEXT_DECODER, NJS_DATA_TAG_ARRAY_ITERATOR, NJS_DATA_TAG_FOREACH_NEXT, NJS_DATA_TAG_MAX } njs_data_tag_t; typedef struct njs_string_s njs_string_t; typedef struct njs_object_s njs_object_t; typedef struct njs_object_value_s njs_object_value_t; typedef struct njs_function_lambda_s njs_function_lambda_t; typedef struct njs_regexp_pattern_s njs_regexp_pattern_t; typedef struct njs_array_s njs_array_t; typedef struct njs_array_buffer_s njs_array_buffer_t; typedef struct njs_typed_array_s njs_typed_array_t; typedef struct njs_typed_array_s njs_data_view_t; typedef struct njs_regexp_s njs_regexp_t; typedef struct njs_date_s njs_date_t; typedef struct njs_object_value_s njs_promise_t; typedef struct njs_property_next_s njs_property_next_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 long_string.size field * and the long_string.data 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 is also 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; /* 6 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; uint16_t magic16; uint32_t magic32; union { double number; njs_object_t *object; njs_array_t *array; njs_array_buffer_t *array_buffer; njs_typed_array_t *typed_array; njs_data_view_t *data_view; njs_object_value_t *object_value; njs_function_t *function; njs_function_lambda_t *lambda; njs_regexp_t *regexp; njs_date_t *date; njs_promise_t *promise; njs_prop_handler_t prop_handler; njs_value_t *value; void *data; } u; } data; struct { njs_value_type_t type:8; /* 6 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; struct { njs_value_type_t type:8; /* 6 bits */ uint8_t truth; /* 0xff if data is external string. */ uint8_t external; uint8_t _spare; uint32_t size; njs_string_t *data; } long_string; njs_value_type_t type:8; /* 6 bits */ }; typedef struct { /* Get, also Set if writable, also Delete if configurable. */ njs_prop_handler_t prop_handler; uint32_t magic32; unsigned writable:1; unsigned configurable:1; unsigned enumerable:1; njs_exotic_keys_t keys; /* A shared hash of njs_object_prop_t for externals. */ njs_lvlhsh_t external_shared_hash; } njs_exotic_slots_t; struct njs_object_s { /* A private hash of njs_object_prop_t. */ njs_lvlhsh_t hash; /* A shared hash of njs_object_prop_t. */ njs_lvlhsh_t shared_hash; njs_object_t *__proto__; njs_exotic_slots_t *slots; /* The type is used in constructor prototypes. */ njs_value_type_t type:8; uint8_t shared; /* 1 bit */ uint8_t extensible:1; uint8_t error_data:1; uint8_t stack_attached:1; uint8_t fast_array:1; }; struct njs_object_value_s { njs_object_t object; /* The value can be unaligned since it never used in nJSVM operations. */ njs_value_t value; }; struct njs_array_s { njs_object_t object; uint32_t size; uint32_t length; njs_value_t *start; njs_value_t *data; }; struct njs_array_buffer_s { njs_object_t object; size_t size; union { uint8_t *u8; uint16_t *u16; uint32_t *u32; uint64_t *u64; int8_t *i8; int16_t *i16; int32_t *i32; int64_t *i64; float *f32; double *f64; void *data; } u; }; struct njs_typed_array_s { njs_object_t object; njs_array_buffer_t *buffer; size_t offset; // byte_offset / element_size size_t byte_length; uint8_t type; }; struct njs_function_s { njs_object_t object; /* Number of bound args excluding 'this'. */ uint8_t bound_args; uint8_t args_count:4; uint8_t closure_copied:1; uint8_t native:1; uint8_t ctor:1; uint8_t global_this:1; uint8_t global:1; uint8_t magic8; union { njs_function_lambda_t *lambda; njs_function_native_t native; } u; void *context; /* Bound args including 'this'. */ njs_value_t *bound; }; struct njs_regexp_s { njs_object_t object; njs_value_t last_index; njs_regexp_pattern_t *pattern; /* * This string value can be unaligned since * it never used in nJSVM operations. */ njs_value_t string; }; struct njs_date_s { njs_object_t object; double time; }; typedef union { njs_object_t object; njs_object_value_t object_value; njs_array_t array; njs_function_t function; njs_regexp_t regexp; njs_date_t date; njs_promise_t promise; } njs_object_prototype_t; struct njs_object_type_init_s { njs_function_t constructor; const njs_object_init_t *constructor_props; const njs_object_init_t *prototype_props; njs_object_prototype_t prototype_value; }; typedef enum { NJS_PROPERTY = 0, NJS_ACCESSOR, NJS_PROPERTY_REF, NJS_PROPERTY_PLACE_REF, NJS_PROPERTY_TYPED_ARRAY_REF, NJS_PROPERTY_HANDLER, NJS_WHITEOUT, } njs_object_prop_type_t; typedef enum { NJS_PROPERTY_QUERY_GET = 0, NJS_PROPERTY_QUERY_SET, NJS_PROPERTY_QUERY_DELETE, } njs_prop_query_t; /* * Attributes are generally used as Boolean values. * The UNSET value is can be seen: * for newly created property descriptors in njs_define_property(), * for writable attribute of accessor descriptors (desc->writable * cannot be used as a boolean value). */ typedef enum { NJS_ATTRIBUTE_FALSE = 0, NJS_ATTRIBUTE_TRUE = 1, NJS_ATTRIBUTE_UNSET, } njs_object_attribute_t; struct njs_object_prop_s { njs_value_t name; union { njs_value_t value; struct { njs_function_t *getter; njs_function_t *setter; } accessor; } u; #define njs_prop_value(_p) (&(_p)->u.value) #define njs_prop_handler(_p) (_p)->u.value.data.u.prop_handler #define njs_prop_ref(_p) (_p)->u.value.data.u.value #define njs_prop_typed_ref(_p) (_p)->u.value.data.u.typed_array #define njs_prop_magic16(_p) (_p)->u.value.data.magic16 #define njs_prop_magic32(_p) (_p)->u.value.data.magic32 #define NJS_PROP_PTR_UNSET ((void *) (uintptr_t) -1) #define njs_prop_getter(_p) (_p)->u.accessor.getter #define njs_prop_setter(_p) (_p)->u.accessor.setter njs_object_prop_type_t type:8; /* 3 bits */ njs_object_prop_type_t enum_in_object_hash:8; /* 3 bits */ njs_object_attribute_t writable:8; /* 2 bits */ njs_object_attribute_t enumerable:8; /* 2 bits */ njs_object_attribute_t configurable:8; /* 2 bits */ }; typedef struct { njs_lvlhsh_query_t lhq; uint8_t query; /* scratch is used to get the value of an NJS_PROPERTY_HANDLER property. */ njs_object_prop_t scratch; njs_value_t key; njs_lvlhsh_t *own_whiteout; uint8_t temp; uint8_t own; } njs_property_query_t; #define njs_value(_type, _truth, _number) { \ .data = { \ .type = _type, \ .truth = _truth, \ .u.number = _number, \ } \ } #define njs_wellknown_symbol(key) { \ .data = { \ .type = NJS_SYMBOL, \ .truth = 1, \ .magic32 = key, \ .u = { .value = NULL } \ } \ } #define njs_string(s) { \ .short_string = { \ .type = NJS_STRING, \ .size = njs_length(s), \ .length = njs_length(s), \ .start = s, \ } \ } /* NJS_STRING_LONG is set for both big and little endian platforms. */ #define njs_long_string(s) { \ .long_string = { \ .type = NJS_STRING, \ .truth = (NJS_STRING_LONG << 4) | NJS_STRING_LONG, \ .size = njs_length(s), \ .data = & (njs_string_t) { \ .start = (u_char *) s, \ .length = njs_length(s), \ } \ } \ } #define _njs_function(_function, _args_count, _ctor, _magic) { \ .native = 1, \ .magic8 = _magic, \ .args_count = _args_count, \ .ctor = _ctor, \ .u.native = _function, \ .object = { .type = NJS_FUNCTION, \ .shared = 1, \ .extensible = 1 }, \ } #define _njs_native_function(_func, _args, _ctor, _magic) { \ .data = { \ .type = NJS_FUNCTION, \ .truth = 1, \ .u.function = & (njs_function_t) _njs_function(_func, _args, \ _ctor, _magic) \ } \ } #define njs_native_function(_function, _args_count) \ _njs_native_function(_function, _args_count, 0, 0) #define njs_native_function2(_function, _args_count, _magic) \ _njs_native_function(_function, _args_count, 0, _magic) #define njs_getter(_function, _magic) \ { \ .getter = & (njs_function_t) _njs_function(_function, 0, 0, _magic), \ .setter = NULL, \ } #define njs_accessor(_getter, _m1, _setter, _m2) \ { \ .getter = & (njs_function_t) _njs_function(_getter, 0, 0, _m1), \ .setter = & (njs_function_t) _njs_function(_setter, 0, 0, _m2), \ } #define njs_native_ctor(_function, _args_count, _magic) \ _njs_function(_function, _args_count, 1, _magic) #define njs_prop_handler2(_handler, _magic16, _magic32) { \ .data = { \ .type = NJS_INVALID, \ .truth = 1, \ .magic16 = _magic16, \ .magic32 = _magic32, \ .u = { .prop_handler = _handler } \ } \ } #define njs_is_null(value) \ ((value)->type == NJS_NULL) #define njs_is_undefined(value) \ ((value)->type == NJS_UNDEFINED) #define njs_is_defined(value) \ ((value)->type != NJS_UNDEFINED) #define njs_is_null_or_undefined(value) \ ((value)->type <= NJS_UNDEFINED) #define njs_is_boolean(value) \ ((value)->type == NJS_BOOLEAN) #define njs_is_null_or_undefined_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) \ (!isnan(num) && num != 0) #define njs_is_numeric(value) \ ((value)->type <= NJS_NUMBER) #define njs_is_symbol(value) \ ((value)->type == NJS_SYMBOL) #define njs_is_string(value) \ ((value)->type == NJS_STRING) #define njs_is_key(value) \ (njs_is_string(value) || njs_is_symbol(value)) #define njs_is_index_or_key(value) \ (njs_is_number(value) || njs_is_key(value)) /* * 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_string_get(value, str) \ do { \ if ((value)->short_string.size != NJS_STRING_LONG) { \ (str)->length = (value)->short_string.size; \ (str)->start = (u_char *) (value)->short_string.start; \ \ } else { \ (str)->length = (value)->long_string.size; \ (str)->start = (u_char *) (value)->long_string.data->start; \ } \ } while (0) #define njs_string_short_start(value) \ (value)->short_string.start #define njs_string_short_set(value, _size, _length) \ do { \ (value)->type = NJS_STRING; \ njs_string_truth(value, _size); \ (value)->short_string.size = _size; \ (value)->short_string.length = _length; \ } while (0) #define njs_string_length_set(value, _length) \ do { \ if ((value)->short_string.size != NJS_STRING_LONG) { \ (value)->short_string.length = length; \ \ } else { \ (value)->long_string.data->length = length; \ } \ } while (0) #define njs_is_primitive(value) \ ((value)->type <= NJS_STRING) #define njs_make_tag(proto_id) \ (((njs_uint_t) proto_id << 8) | NJS_DATA_TAG_EXTERNAL) #define njs_is_data(value, tag) \ ((value)->type == NJS_DATA \ && ((tag) == njs_make_tag(NJS_PROTO_ID_ANY) \ || value->data.magic32 == (tag))) #define njs_is_object(value) \ ((value)->type >= NJS_OBJECT) #define njs_has_prototype(vm, value, proto_id) \ (njs_object(value)->__proto__ == njs_vm_proto(vm, proto_id)) #define njs_is_object_value(value) \ ((value)->type == NJS_OBJECT_VALUE) #define njs_is_object_boolean(_value) \ (((_value)->type == NJS_OBJECT_VALUE) \ && njs_is_boolean(njs_object_value(_value))) #define njs_is_object_number(_value) \ (((_value)->type == NJS_OBJECT_VALUE) \ && njs_is_number(njs_object_value(_value))) #define njs_is_object_symbol(_value) \ (((_value)->type == NJS_OBJECT_VALUE) \ && njs_is_symbol(njs_object_value(_value))) #define njs_is_object_string(_value) \ (((_value)->type == NJS_OBJECT_VALUE) \ && njs_is_string(njs_object_value(_value))) #define njs_is_object_primitive(_value) \ (((_value)->type == NJS_OBJECT_VALUE) \ && njs_is_primitive(njs_object_value(_value))) #define njs_is_object_data(_value, tag) \ (((_value)->type == NJS_OBJECT_VALUE) \ && njs_is_data(njs_object_value(_value), tag)) #define njs_is_array(value) \ ((value)->type == NJS_ARRAY) #define njs_is_fast_array(value) \ (njs_is_array(value) && njs_array(value)->object.fast_array) #define njs_is_array_buffer(value) \ ((value)->type == NJS_ARRAY_BUFFER) #define njs_is_typed_array(value) \ ((value)->type == NJS_TYPED_ARRAY) #define njs_is_detached_buffer(buffer) \ ((buffer)->u.data == NULL) #define njs_is_data_view(value) \ ((value)->type == NJS_DATA_VIEW) #define njs_is_typed_array_uint8(value) \ (njs_is_typed_array(value) \ && njs_typed_array(value)->type == NJS_OBJ_TYPE_UINT8_ARRAY) #define njs_is_function(value) \ ((value)->type == NJS_FUNCTION) #define njs_is_function_or_undefined(value) \ ((value)->type == NJS_FUNCTION || (value)->type == NJS_UNDEFINED) #define njs_is_constructor(value) \ (njs_is_function(value) && njs_function(value)->ctor) #define njs_is_regexp(value) \ ((value)->type == NJS_REGEXP) #define njs_is_date(value) \ ((value)->type == NJS_DATE) #define njs_is_promise(value) \ ((value)->type == NJS_PROMISE) #define njs_is_error(value) \ ((value)->type == NJS_OBJECT && njs_object(value)->error_data) #define njs_is_valid(value) \ ((value)->type != NJS_INVALID) #define njs_bool(value) \ ((value)->data.truth) #define njs_number(value) \ ((value)->data.u.number) #define njs_data(value) \ ((value)->data.u.data) #define njs_function(value) \ ((value)->data.u.function) #define njs_function_lambda(value) \ ((value)->data.u.function->u.lambda) #define njs_object(value) \ ((value)->data.u.object) #define njs_object_hash(value) \ (&(value)->data.u.object->hash) #define njs_object_slots(value) \ ((value)->data.u.object->slots) #define njs_array(value) \ ((value)->data.u.array) #define njs_array_len(value) \ ((value)->data.u.array->length) #define njs_array_buffer(value) \ ((value)->data.u.array_buffer) #define njs_data_view(value) \ ((value)->data.u.data_view) #define njs_typed_array(value) \ ((value)->data.u.typed_array) #define njs_typed_array_buffer(value) \ ((value)->buffer) #define njs_array_start(value) \ ((value)->data.u.array->start) #define njs_date(value) \ ((value)->data.u.date) #define njs_promise(value) \ ((value)->data.u.promise) #define njs_regexp(value) \ ((value)->data.u.regexp) #define njs_regexp_pattern(value) \ ((value)->data.u.regexp->pattern) #define njs_object_value(_value) \ (&(_value)->data.u.object_value->value) #define njs_object_data(_value) \ njs_data(njs_object_value(_value)) #define njs_set_undefined(value) \ *(value) = njs_value_undefined #define njs_set_null(value) \ *(value) = njs_value_null #define njs_set_true(value) \ *(value) = njs_value_true #define njs_set_false(value) \ *(value) = njs_value_false #define njs_symbol_key(value) \ ((value)->data.magic32) #define njs_symbol_eq(value1, value2) \ (njs_symbol_key(value1) == njs_symbol_key(value2)) extern const njs_value_t njs_value_null; extern const njs_value_t njs_value_undefined; 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_value_invalid; extern const njs_value_t njs_string_empty; extern const njs_value_t njs_string_empty_regexp; extern const njs_value_t njs_string_comma; extern const njs_value_t njs_string_null; extern const njs_value_t njs_string_undefined; extern const njs_value_t njs_string_boolean; extern const njs_value_t njs_string_false; extern const njs_value_t njs_string_true; extern const njs_value_t njs_string_number; extern const njs_value_t njs_string_minus_zero; 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_symbol; extern const njs_value_t njs_string_string; extern const njs_value_t njs_string_data; extern const njs_value_t njs_string_type; extern const njs_value_t njs_string_name; extern const njs_value_t njs_string_external; extern const njs_value_t njs_string_invalid; extern const njs_value_t njs_string_object; extern const njs_value_t njs_string_function; extern const njs_value_t njs_string_anonymous; extern const njs_value_t njs_string_memory_error; njs_inline void njs_set_boolean(njs_value_t *value, unsigned yn) { const njs_value_t *retval; /* Using const retval generates a better code at least on i386/amd64. */ retval = (yn) ? &njs_value_true : &njs_value_false; *value = *retval; } njs_inline void njs_set_number(njs_value_t *value, double num) { value->data.u.number = num; value->type = NJS_NUMBER; value->data.truth = njs_is_number_true(num); } njs_inline void njs_set_int32(njs_value_t *value, int32_t num) { value->data.u.number = num; value->type = NJS_NUMBER; value->data.truth = (num != 0); } njs_inline void njs_set_uint32(njs_value_t *value, uint32_t num) { value->data.u.number = num; value->type = NJS_NUMBER; value->data.truth = (num != 0); } njs_inline void njs_set_symbol(njs_value_t *value, uint32_t symbol, njs_value_t *name) { value->data.magic32 = symbol; value->type = NJS_SYMBOL; value->data.truth = 1; value->data.u.value = name; } njs_inline void njs_set_data(njs_value_t *value, void *data, njs_data_tag_t tag) { value->data.magic32 = tag; value->data.u.data = data; value->type = NJS_DATA; value->data.truth = 1; } njs_inline void njs_set_object(njs_value_t *value, njs_object_t *object) { value->data.u.object = object; value->type = NJS_OBJECT; value->data.truth = 1; } njs_inline void njs_set_type_object(njs_value_t *value, njs_object_t *object, njs_uint_t type) { value->data.u.object = object; value->type = type; value->data.truth = 1; } njs_inline void njs_set_array(njs_value_t *value, njs_array_t *array) { value->data.u.array = array; value->type = NJS_ARRAY; value->data.truth = 1; } njs_inline void njs_set_array_buffer(njs_value_t *value, njs_array_buffer_t *array) { value->data.u.array_buffer = array; value->type = NJS_ARRAY_BUFFER; value->data.truth = 1; } njs_inline void njs_set_typed_array(njs_value_t *value, njs_typed_array_t *array) { value->data.u.typed_array = array; value->type = NJS_TYPED_ARRAY; value->data.truth = 1; } njs_inline void njs_set_data_view(njs_value_t *value, njs_data_view_t *array) { value->data.u.data_view = array; value->type = NJS_DATA_VIEW; value->data.truth = 1; } njs_inline void njs_set_function(njs_value_t *value, njs_function_t *function) { value->data.u.function = function; value->type = NJS_FUNCTION; value->data.truth = 1; } njs_inline void njs_set_date(njs_value_t *value, njs_date_t *date) { value->data.u.date = date; value->type = NJS_DATE; value->data.truth = 1; } njs_inline void njs_set_promise(njs_value_t *value, njs_promise_t *promise) { value->data.u.promise = promise; value->type = NJS_PROMISE; value->data.truth = 1; } njs_inline void njs_set_regexp(njs_value_t *value, njs_regexp_t *regexp) { value->data.u.regexp = regexp; value->type = NJS_REGEXP; value->data.truth = 1; } njs_inline void njs_set_object_value(njs_value_t *value, njs_object_value_t *object_value) { value->data.u.object_value = object_value; value->type = NJS_OBJECT_VALUE; value->data.truth = 1; } #define njs_set_invalid(value) \ (value)->type = NJS_INVALID njs_int_t njs_value_to_primitive(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value, njs_uint_t hint); njs_array_t *njs_value_enumerate(njs_vm_t *vm, njs_value_t *value, uint32_t flags); njs_array_t *njs_value_own_enumerate(njs_vm_t *vm, njs_value_t *value, uint32_t flags); njs_int_t njs_value_of(njs_vm_t *vm, njs_value_t *value, njs_value_t *retval); njs_int_t njs_value_length(njs_vm_t *vm, njs_value_t *value, int64_t *dst); const char *njs_type_string(njs_value_type_t type); njs_int_t njs_primitive_value_to_string(njs_vm_t *vm, njs_value_t *dst, const njs_value_t *src); njs_int_t njs_primitive_value_to_chain(njs_vm_t *vm, njs_chb_t *chain, const njs_value_t *src); double njs_string_to_number(const njs_value_t *value); njs_int_t njs_int64_to_string(njs_vm_t *vm, njs_value_t *value, int64_t i64); njs_bool_t njs_string_eq(const njs_value_t *v1, const njs_value_t *v2); njs_int_t njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *value, njs_value_t *key); njs_int_t njs_value_property(njs_vm_t *vm, njs_value_t *value, njs_value_t *key, njs_value_t *retval); njs_int_t njs_value_property_set(njs_vm_t *vm, njs_value_t *value, njs_value_t *key, njs_value_t *setval); njs_int_t njs_value_property_delete(njs_vm_t *vm, njs_value_t *value, njs_value_t *key, njs_value_t *removed, njs_bool_t thrw); njs_int_t njs_value_to_object(njs_vm_t *vm, njs_value_t *value); void njs_symbol_conversion_failed(njs_vm_t *vm, njs_bool_t to_string); njs_int_t njs_value_construct(njs_vm_t *vm, njs_value_t *constructor, njs_value_t *args, njs_uint_t nargs, njs_value_t *retval); njs_int_t njs_value_species_constructor(njs_vm_t *vm, njs_value_t *object, njs_value_t *default_constructor, njs_value_t *dst); njs_int_t njs_value_method(njs_vm_t *vm, njs_value_t *value, njs_value_t *key, njs_value_t *retval); njs_inline void njs_property_query_init(njs_property_query_t *pq, njs_prop_query_t query, uint32_t hash, uint8_t own) { pq->query = query; pq->lhq.key_hash = hash; pq->own = own; if (query == NJS_PROPERTY_QUERY_SET) { pq->lhq.value = NULL; pq->own_whiteout = NULL; pq->temp = 0; } } njs_inline njs_int_t njs_value_property_i64(njs_vm_t *vm, njs_value_t *value, int64_t index, njs_value_t *retval) { njs_value_t key; njs_set_number(&key, index); return njs_value_property(vm, value, &key, retval); } njs_inline njs_int_t njs_value_property_i64_set(njs_vm_t *vm, njs_value_t *value, int64_t index, njs_value_t *setval) { njs_value_t key; njs_set_number(&key, index); return njs_value_property_set(vm, value, &key, setval); } njs_inline njs_int_t njs_value_property_i64_delete(njs_vm_t *vm, njs_value_t *value, int64_t index, njs_value_t *removed) { njs_value_t key; njs_set_number(&key, index); return njs_value_property_delete(vm, value, &key, removed, 1); } njs_inline njs_bool_t njs_values_same_non_numeric(const njs_value_t *val1, const njs_value_t *val2) { if (njs_is_string(val1)) { return njs_string_eq(val1, val2); } if (njs_is_symbol(val1)) { return njs_symbol_eq(val1, val2); } return (njs_object(val1) == njs_object(val2)); } njs_inline njs_bool_t njs_values_strict_equal(const njs_value_t *val1, const njs_value_t *val2) { if (val1->type != val2->type) { return 0; } if (njs_is_numeric(val1)) { if (njs_is_undefined(val1)) { return 1; } /* Infinities are handled correctly by comparision. */ return (njs_number(val1) == njs_number(val2)); } return njs_values_same_non_numeric(val1, val2); } njs_inline njs_bool_t njs_values_same(const njs_value_t *val1, const njs_value_t *val2) { double num1, num2; if (val1->type != val2->type) { return 0; } if (njs_is_numeric(val1)) { if (njs_is_undefined(val1)) { return 1; } num1 = njs_number(val1); num2 = njs_number(val2); if (njs_slow_path(isnan(num1) && isnan(num2))) { return 1; } if (njs_slow_path(num1 == 0 && num2 == 0 && (signbit(num1) ^ signbit(num2)))) { return 0; } /* Infinities are handled correctly by comparision. */ return num1 == num2; } return njs_values_same_non_numeric(val1, val2); } njs_inline njs_bool_t njs_values_same_zero(const njs_value_t *val1, const njs_value_t *val2) { double num1, num2; if (val1->type != val2->type) { return 0; } if (njs_is_numeric(val1)) { if (njs_is_undefined(val1)) { return 1; } num1 = njs_number(val1); num2 = njs_number(val2); if (njs_slow_path(isnan(num1) && isnan(num2))) { return 1; } /* Infinities are handled correctly by comparision. */ return num1 == num2; } return njs_values_same_non_numeric(val1, val2); } #endif /* _NJS_VALUE_H_INCLUDED_ */ njs-0.8.9/src/njs_value_conversion.h000066400000000000000000000073071474132077100175320ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_VALUE_CONVERSION_H_INCLUDED_ #define _NJS_VALUE_CONVERSION_H_INCLUDED_ njs_inline njs_int_t njs_value_to_number(njs_vm_t *vm, njs_value_t *value, double *dst) { njs_int_t ret; njs_value_t primitive; if (njs_slow_path(!njs_is_primitive(value))) { ret = njs_value_to_primitive(vm, &primitive, value, 0); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } value = &primitive; } if (njs_slow_path(!njs_is_numeric(value))) { if (njs_slow_path(njs_is_symbol(value))) { njs_symbol_conversion_failed(vm, 0); return NJS_ERROR; } *dst = NAN; if (njs_is_string(value)) { *dst = njs_string_to_number(value); } return NJS_OK; } *dst = njs_number(value); return NJS_OK; } njs_inline njs_int_t njs_value_to_numeric(njs_vm_t *vm, njs_value_t *value, njs_value_t *dst) { double num; njs_int_t ret; ret = njs_value_to_number(vm, value, &num); if (njs_slow_path(ret != NJS_OK)) { return ret; } njs_set_number(dst, num); return NJS_OK; } njs_inline njs_int_t njs_value_to_length(njs_vm_t *vm, njs_value_t *value, int64_t *dst) { double num; njs_int_t ret; ret = njs_value_to_number(vm, value, &num); if (njs_slow_path(ret != NJS_OK)) { return ret; } *dst = njs_number_to_length(num); return NJS_OK; } njs_inline njs_int_t njs_value_to_index(njs_vm_t *vm, njs_value_t *value, uint64_t *dst) { int64_t integer_index; njs_int_t ret; if (njs_slow_path(njs_is_undefined(value))) { *dst = 0; } else { ret = njs_value_to_integer(vm, value, &integer_index); if (njs_slow_path(ret != NJS_OK)) { return ret; } if (integer_index < 0 || integer_index > UINT32_MAX) { njs_range_error(vm, "invalid index"); return NJS_ERROR; } *dst = integer_index; } return NJS_OK; } njs_inline njs_int_t njs_value_to_int32(njs_vm_t *vm, njs_value_t *value, int32_t *dst) { double num; njs_int_t ret; ret = njs_value_to_number(vm, value, &num); if (njs_slow_path(ret != NJS_OK)) { return ret; } *dst = njs_number_to_int32(num); return NJS_OK; } njs_inline njs_int_t njs_value_to_uint32(njs_vm_t *vm, njs_value_t *value, uint32_t *dst) { double num; njs_int_t ret; ret = njs_value_to_number(vm, value, &num); if (njs_slow_path(ret != NJS_OK)) { return ret; } *dst = njs_number_to_uint32(num); return NJS_OK; } njs_inline njs_int_t njs_value_to_uint16(njs_vm_t *vm, njs_value_t *value, uint16_t *dst) { double num; njs_int_t ret; ret = njs_value_to_number(vm, value, &num); if (njs_slow_path(ret != NJS_OK)) { return ret; } *dst = njs_number_to_uint16(num); return NJS_OK; } /* * retval >= 0 is length (UTF8 characters) value of appended string. */ njs_inline njs_int_t njs_value_to_chain(njs_vm_t *vm, njs_chb_t *chain, njs_value_t *value) { njs_int_t ret; njs_value_t primitive; if (njs_slow_path(!njs_is_primitive(value))) { if (njs_slow_path(njs_is_object_symbol(value))) { /* should fail */ value = njs_object_value(value); } else { ret = njs_value_to_primitive(vm, &primitive, value, 1); if (njs_slow_path(ret != NJS_OK)) { return ret; } value = &primitive; } } return njs_primitive_value_to_chain(vm, chain, value); } #endif /* _NJS_VALUE_CONVERSION_H_INCLUDED_ */ njs-0.8.9/src/njs_variable.c000066400000000000000000000344141474132077100157300ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #include static njs_declaration_t *njs_variable_scope_function_add(njs_parser_t *parser, njs_parser_scope_t *scope); static njs_parser_scope_t *njs_variable_scope_find(njs_parser_t *parser, njs_parser_scope_t *scope, uintptr_t unique_id, njs_variable_type_t type); static njs_variable_t *njs_variable_alloc(njs_vm_t *vm, uintptr_t unique_id, njs_variable_type_t type); njs_variable_t * njs_variable_add(njs_parser_t *parser, njs_parser_scope_t *scope, uintptr_t unique_id, njs_variable_type_t type) { njs_parser_scope_t *root; root = njs_variable_scope_find(parser, scope, unique_id, type); if (njs_slow_path(root == NULL)) { njs_parser_ref_error(parser, "scope not found"); return NULL; } return njs_variable_scope_add(parser, root, scope, unique_id, type, NJS_INDEX_NONE); } njs_variable_t * njs_variable_function_add(njs_parser_t *parser, njs_parser_scope_t *scope, uintptr_t unique_id, njs_variable_type_t type) { njs_bool_t ctor; njs_variable_t *var; njs_declaration_t *declr; njs_parser_scope_t *root; njs_function_lambda_t *lambda; root = njs_variable_scope_find(parser, scope, unique_id, type); if (njs_slow_path(root == NULL)) { njs_parser_ref_error(parser, "scope not found"); return NULL; } var = njs_variable_scope_add(parser, root, scope, unique_id, type, NJS_INDEX_ERROR); if (njs_slow_path(var == NULL)) { return NULL; } root = njs_function_scope(scope); if (njs_slow_path(scope == NULL)) { return NULL; } ctor = parser->node->token_type != NJS_TOKEN_ASYNC_FUNCTION_DECLARATION; lambda = njs_function_lambda_alloc(parser->vm, ctor); if (lambda == NULL) { return NULL; } njs_set_invalid(&var->value); var->value.data.u.lambda = lambda; declr = njs_variable_scope_function_add(parser, root); if (njs_slow_path(declr == NULL)) { return NULL; } var->index = njs_scope_index(root->type, root->items, NJS_LEVEL_LOCAL, type); declr->value = &var->value; declr->index = var->index; root->items++; var->type = NJS_VARIABLE_FUNCTION; var->function = 1; return var; } static njs_declaration_t * njs_variable_scope_function_add(njs_parser_t *parser, njs_parser_scope_t *scope) { if (scope->declarations == NULL) { scope->declarations = njs_arr_create(parser->vm->mem_pool, 1, sizeof(njs_declaration_t)); if (njs_slow_path(scope->declarations == NULL)) { return NULL; } } return njs_arr_add(scope->declarations); } static njs_parser_scope_t * njs_variable_scope(njs_parser_scope_t *scope, uintptr_t unique_id, njs_variable_t **retvar, njs_variable_type_t type) { njs_variable_t *var; njs_rbtree_node_t *node; njs_variable_node_t var_node; *retvar = NULL; var_node.key = unique_id; do { node = njs_rbtree_find(&scope->variables, &var_node.node); if (node != NULL) { var = ((njs_variable_node_t *) node)->variable; if (var->type != NJS_VARIABLE_CATCH || type != NJS_VARIABLE_VAR) { *retvar = var; return scope; } } if (scope->type == NJS_SCOPE_GLOBAL || scope->type == NJS_SCOPE_FUNCTION) { return scope; } scope = scope->parent; } while (scope != NULL); return NULL; } static njs_parser_scope_t * njs_variable_scope_find(njs_parser_t *parser, njs_parser_scope_t *scope, uintptr_t unique_id, njs_variable_type_t type) { njs_bool_t module; njs_variable_t *var; njs_parser_scope_t *root; const njs_lexer_entry_t *entry; root = njs_variable_scope(scope, unique_id, &var, type); if (njs_slow_path(root == NULL)) { return NULL; } switch (type) { case NJS_VARIABLE_CONST: case NJS_VARIABLE_LET: if (scope->type == NJS_SCOPE_GLOBAL && parser->undefined_id == unique_id) { goto failed; } if (root != scope) { return scope; } if (var != NULL && var->scope == root) { if (var->self) { var->function = 0; return scope; } goto failed; } return scope; case NJS_VARIABLE_VAR: case NJS_VARIABLE_FUNCTION: break; default: return scope; } if (type == NJS_VARIABLE_FUNCTION) { root = scope; } if (var == NULL) { return root; } if (var->type == NJS_VARIABLE_LET || var->type == NJS_VARIABLE_CONST) { goto failed; } if (var->original->type == NJS_SCOPE_BLOCK) { if (type == NJS_VARIABLE_FUNCTION || var->type == NJS_VARIABLE_FUNCTION) { if (var->original == root) { goto failed; } } } if (type != NJS_VARIABLE_FUNCTION && var->type != NJS_VARIABLE_FUNCTION) { return var->scope; } if (root != scope) { return root; } if (scope->parent == NULL) { module = parser->vm->options.module || parser->module; if (module) { if (type == NJS_VARIABLE_FUNCTION || var->type == NJS_VARIABLE_FUNCTION) { goto failed; } } } return root; failed: entry = njs_lexer_entry(unique_id); njs_parser_syntax_error(parser, "\"%V\" has already been declared", &entry->name); return NULL; } njs_variable_t * njs_variable_scope_add(njs_parser_t *parser, njs_parser_scope_t *scope, njs_parser_scope_t *original, uintptr_t unique_id, njs_variable_type_t type, njs_index_t index) { njs_variable_t *var; njs_rbtree_node_t *node; njs_parser_scope_t *root; njs_variable_node_t var_node, *var_node_new; var_node.key = unique_id; node = njs_rbtree_find(&scope->variables, &var_node.node); if (node != NULL) { return ((njs_variable_node_t *) node)->variable; } var = njs_variable_alloc(parser->vm, unique_id, type); if (njs_slow_path(var == NULL)) { goto memory_error; } var->scope = scope; var->index = index; var->original = original; if (index == NJS_INDEX_NONE) { root = njs_function_scope(scope); if (njs_slow_path(scope == NULL)) { return NULL; } var->index = njs_scope_index(root->type, root->items, NJS_LEVEL_LOCAL, type); root->items++; } var_node_new = njs_variable_node_alloc(parser->vm, var, unique_id); if (njs_slow_path(var_node_new == NULL)) { goto memory_error; } njs_rbtree_insert(&scope->variables, &var_node_new->node); return var; memory_error: njs_memory_error(parser->vm); return NULL; } njs_variable_t * njs_label_add(njs_vm_t *vm, njs_parser_scope_t *scope, uintptr_t unique_id) { njs_variable_t *label; njs_rbtree_node_t *node; njs_variable_node_t var_node, *var_node_new; var_node.key = unique_id; node = njs_rbtree_find(&scope->labels, &var_node.node); if (node != NULL) { return ((njs_variable_node_t *) node)->variable; } label = njs_variable_alloc(vm, unique_id, NJS_VARIABLE_CONST); if (njs_slow_path(label == NULL)) { goto memory_error; } var_node_new = njs_variable_node_alloc(vm, label, unique_id); if (njs_slow_path(var_node_new == NULL)) { goto memory_error; } njs_rbtree_insert(&scope->labels, &var_node_new->node); return label; memory_error: njs_memory_error(vm); return NULL; } njs_int_t njs_label_remove(njs_vm_t *vm, njs_parser_scope_t *scope, uintptr_t unique_id) { njs_rbtree_node_t *node; njs_variable_node_t var_node; var_node.key = unique_id; node = njs_rbtree_find(&scope->labels, &var_node.node); if (njs_slow_path(node == NULL)) { njs_internal_error(vm, "failed to find label while removing"); return NJS_ERROR; } njs_rbtree_delete(&scope->labels, (njs_rbtree_part_t *) node); njs_variable_node_free(vm, (njs_variable_node_t *) node); return NJS_OK; } njs_bool_t njs_variable_closure_test(njs_parser_scope_t *root, njs_parser_scope_t *scope) { if (root == scope) { return 0; } do { if (root->type == NJS_SCOPE_FUNCTION) { return 1; } root = root->parent; } while (root != scope); return 0; } njs_variable_t * njs_variable_resolve(njs_vm_t *vm, njs_parser_node_t *node) { njs_rbtree_node_t *rb_node; njs_parser_scope_t *scope; njs_variable_node_t var_node; njs_variable_reference_t *ref; ref = &node->u.reference; scope = node->scope; var_node.key = ref->unique_id; do { rb_node = njs_rbtree_find(&scope->variables, &var_node.node); if (rb_node != NULL) { return ((njs_variable_node_t *) rb_node)->variable; } scope = scope->parent; } while (scope != NULL); return NULL; } static njs_index_t njs_variable_closure(njs_vm_t *vm, njs_variable_t *var, njs_parser_scope_t *scope) { njs_index_t index, prev_index, *idx; njs_level_type_t type; njs_rbtree_node_t *rb_node; njs_parser_scope_t **p; njs_parser_rbtree_node_t *parse_node, ref_node; #define NJS_VAR_MAX_DEPTH 32 njs_parser_scope_t *list[NJS_VAR_MAX_DEPTH]; ref_node.key = var->unique_id; p = list; do { if (njs_slow_path(p == &list[NJS_VAR_MAX_DEPTH - 1])) { njs_error(vm, "maximum depth of nested functions is reached"); return NJS_INDEX_ERROR; } if (scope->type == NJS_SCOPE_FUNCTION) { *p++ = scope; } scope = scope->parent; } while (scope != var->scope && scope->type != NJS_SCOPE_GLOBAL); prev_index = var->index; while (p != list) { p--; scope = *p; rb_node = njs_rbtree_find(&scope->references, &ref_node.node); parse_node = ((njs_parser_rbtree_node_t *) rb_node); type = NJS_LEVEL_LOCAL; if (parse_node != NULL) { type = njs_scope_index_type(parse_node->index); if (p != list && parse_node->index != 0) { prev_index = parse_node->index; continue; } } if (type != NJS_LEVEL_CLOSURE) { /* Create new closure for scope. */ index = njs_scope_index(scope->type, scope->closures->items, NJS_LEVEL_CLOSURE, var->type); if (njs_slow_path(index == NJS_INDEX_ERROR)) { return NJS_INDEX_ERROR; } idx = njs_arr_add(scope->closures); if (njs_slow_path(idx == NULL)) { return NJS_INDEX_ERROR; } *idx = prev_index; if (parse_node == NULL) { /* Create new reference for closure. */ parse_node = njs_mp_alloc(vm->mem_pool, sizeof(njs_parser_rbtree_node_t)); if (njs_slow_path(parse_node == NULL)) { return NJS_INDEX_ERROR; } parse_node->key = var->unique_id; njs_rbtree_insert(&scope->references, &parse_node->node); } parse_node->index = index; } prev_index = parse_node->index; } return prev_index; } njs_variable_t * njs_variable_reference(njs_vm_t *vm, njs_parser_node_t *node) { njs_bool_t closure; njs_rbtree_node_t *rb_node; njs_parser_scope_t *scope; njs_parser_rbtree_node_t *parse_node, ref_node; njs_variable_reference_t *ref; ref = &node->u.reference; scope = node->scope; if (ref->variable == NULL) { ref->variable = njs_variable_resolve(vm, node); if (njs_slow_path(ref->variable == NULL)) { ref->not_defined = 1; return NULL; } } closure = njs_variable_closure_test(node->scope, ref->variable->scope); ref->scope = node->scope; ref_node.key = ref->unique_id; rb_node = njs_rbtree_find(&scope->references, &ref_node.node); if (njs_slow_path(rb_node == NULL)) { return NULL; } parse_node = ((njs_parser_rbtree_node_t *) rb_node); if (parse_node->index != NJS_INDEX_NONE) { node->index = parse_node->index; return ref->variable; } if (!closure) { node->index = ref->variable->index; return ref->variable; } ref->variable->closure = closure; node->index = njs_variable_closure(vm, ref->variable, scope); if (njs_slow_path(node->index == NJS_INDEX_ERROR)) { return NULL; } return ref->variable; } njs_variable_t * njs_label_find(njs_vm_t *vm, njs_parser_scope_t *scope, uintptr_t unique_id) { njs_rbtree_node_t *node; njs_variable_node_t var_node; var_node.key = unique_id; do { node = njs_rbtree_find(&scope->labels, &var_node.node); if (node != NULL) { return ((njs_variable_node_t *) node)->variable; } scope = scope->parent; } while (scope != NULL); return NULL; } static njs_variable_t * njs_variable_alloc(njs_vm_t *vm, uintptr_t unique_id, njs_variable_type_t type) { njs_variable_t *var; var = njs_mp_zalloc(vm->mem_pool, sizeof(njs_variable_t)); if (njs_slow_path(var == NULL)) { njs_memory_error(vm); return NULL; } var->unique_id = unique_id; var->type = type; return var; } njs_int_t njs_name_copy(njs_vm_t *vm, njs_str_t *dst, const njs_str_t *src) { dst->length = src->length; dst->start = njs_mp_alloc(vm->mem_pool, src->length); if (njs_fast_path(dst->start != NULL)) { (void) memcpy(dst->start, src->start, src->length); return NJS_OK; } njs_memory_error(vm); return NJS_ERROR; } njs-0.8.9/src/njs_variable.h000066400000000000000000000056201474132077100157320ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_VARIABLE_H_INCLUDED_ #define _NJS_VARIABLE_H_INCLUDED_ typedef enum { NJS_VARIABLE_CONST = 0, NJS_VARIABLE_LET, NJS_VARIABLE_CATCH, NJS_VARIABLE_VAR, NJS_VARIABLE_FUNCTION, } njs_variable_type_t; typedef struct { uintptr_t unique_id; njs_variable_type_t type:8; /* 3 bits */ njs_bool_t argument; njs_bool_t arguments_object; njs_bool_t self; njs_bool_t init; njs_bool_t closure; njs_bool_t function; njs_parser_scope_t *scope; njs_parser_scope_t *original; njs_index_t index; njs_value_t value; } njs_variable_t; typedef enum { NJS_DECLARATION = 0, NJS_REFERENCE, NJS_TYPEOF, } njs_reference_type_t; typedef struct { njs_reference_type_t type; uintptr_t unique_id; njs_variable_t *variable; njs_parser_scope_t *scope; njs_bool_t not_defined; } njs_variable_reference_t; typedef struct { NJS_RBTREE_NODE (node); uintptr_t key; njs_variable_t *variable; } njs_variable_node_t; njs_variable_t *njs_variable_add(njs_parser_t *parser, njs_parser_scope_t *scope, uintptr_t unique_id, njs_variable_type_t type); njs_variable_t *njs_variable_function_add(njs_parser_t *parser, njs_parser_scope_t *scope, uintptr_t unique_id, njs_variable_type_t type); njs_variable_t * njs_label_add(njs_vm_t *vm, njs_parser_scope_t *scope, uintptr_t unique_id); njs_variable_t *njs_label_find(njs_vm_t *vm, njs_parser_scope_t *scope, uintptr_t unique_id); njs_int_t njs_label_remove(njs_vm_t *vm, njs_parser_scope_t *scope, uintptr_t unique_id); njs_variable_t *njs_variable_reference(njs_vm_t *vm, njs_parser_node_t *node); njs_variable_t *njs_variable_scope_add(njs_parser_t *parser, njs_parser_scope_t *scope, njs_parser_scope_t *original, uintptr_t unique_id, njs_variable_type_t type, njs_index_t index); njs_int_t njs_name_copy(njs_vm_t *vm, njs_str_t *dst, const njs_str_t *src); njs_inline njs_variable_node_t * njs_variable_node_alloc(njs_vm_t *vm, njs_variable_t *var, uintptr_t key) { njs_variable_node_t *node; node = njs_mp_zalloc(vm->mem_pool, sizeof(njs_variable_node_t)); if (njs_fast_path(node != NULL)) { node->key = key; node->variable = var; } return node; } njs_inline njs_function_lambda_t * njs_variable_lambda(njs_variable_t * var) { if (njs_is_function(&var->value)) { /* may be set by generator in njs_generate_function_declaration(). */ return njs_function(&var->value)->u.lambda; } return var->value.data.u.lambda; } njs_inline void njs_variable_node_free(njs_vm_t *vm, njs_variable_node_t *node) { njs_mp_free(vm->mem_pool, node); } #endif /* _NJS_VARIABLE_H_INCLUDED_ */ njs-0.8.9/src/njs_vm.c000066400000000000000000001132541474132077100145650ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include static njs_int_t njs_vm_protos_init(njs_vm_t *vm, njs_value_t *global); const njs_str_t njs_entry_empty = njs_str(""); const njs_str_t njs_entry_main = njs_str("main"); const njs_str_t njs_entry_module = njs_str("module"); const njs_str_t njs_entry_native = njs_str("native"); const njs_str_t njs_entry_unknown = njs_str("unknown"); const njs_str_t njs_entry_anonymous = njs_str("anonymous"); void njs_vm_opt_init(njs_vm_opt_t *options) { njs_memzero(options, sizeof(njs_vm_opt_t)); options->max_stack_size = NJS_MAX_STACK_SIZE; } njs_vm_t * njs_vm_create(njs_vm_opt_t *options) { njs_mp_t *mp; njs_vm_t *vm; njs_int_t ret; njs_uint_t i; njs_module_t **addons; mp = njs_mp_fast_create(2 * njs_pagesize(), 128, 512, 16); if (njs_slow_path(mp == NULL)) { return NULL; } vm = njs_mp_zalign(mp, sizeof(njs_value_t), sizeof(njs_vm_t)); if (njs_slow_path(vm == NULL)) { return NULL; } vm->mem_pool = mp; ret = njs_regexp_init(vm); if (njs_slow_path(ret != NJS_OK)) { return NULL; } njs_lvlhsh_init(&vm->values_hash); vm->options = *options; if (options->shared != NULL) { vm->shared = options->shared; } else { ret = njs_builtin_objects_create(vm); if (njs_slow_path(ret != NJS_OK)) { return NULL; } } vm->external = options->external; vm->spare_stack_size = options->max_stack_size; vm->trace.level = NJS_LEVEL_TRACE; vm->trace.size = 2048; vm->trace.data = vm; if (options->init) { ret = njs_vm_runtime_init(vm); if (njs_slow_path(ret != NJS_OK)) { return NULL; } } for (i = 0; njs_modules[i] != NULL; i++) { if (njs_modules[i]->preinit == NULL) { continue; } ret = njs_modules[i]->preinit(vm); if (njs_slow_path(ret != NJS_OK)) { return NULL; } } if (options->addons != NULL) { addons = options->addons; for (i = 0; addons[i] != NULL; i++) { if (addons[i]->preinit == NULL) { continue; } ret = addons[i]->preinit(vm); if (njs_slow_path(ret != NJS_OK)) { return NULL; } } } ret = njs_vm_protos_init(vm, &vm->global_value); if (njs_slow_path(ret != NJS_OK)) { return NULL; } for (i = 0; njs_modules[i] != NULL; i++) { if (njs_modules[i]->init == NULL) { continue; } ret = njs_modules[i]->init(vm); if (njs_slow_path(ret != NJS_OK)) { return NULL; } } if (options->addons != NULL) { addons = options->addons; for (i = 0; addons[i] != NULL; i++) { if (addons[i]->init == NULL) { continue; } ret = addons[i]->init(vm); if (njs_slow_path(ret != NJS_OK)) { return NULL; } } } vm->symbol_generator = NJS_SYMBOL_KNOWN_MAX; if (njs_scope_undefined_index(vm, 0) == NJS_INDEX_ERROR) { return NULL; } return vm; } njs_int_t njs_vm_ctor_push(njs_vm_t *vm) { njs_function_t *ctor; njs_vm_shared_t *shared; njs_object_prototype_t *prototype; shared = vm->shared; if (shared->constructors == NULL) { shared->constructors = njs_arr_create(vm->mem_pool, NJS_OBJ_TYPE_MAX + 8, sizeof(njs_function_t)); if (njs_slow_path(shared->constructors == NULL)) { njs_memory_error(vm); return -1; } shared->prototypes = njs_arr_create(vm->mem_pool, NJS_OBJ_TYPE_MAX + 8, sizeof(njs_object_prototype_t)); if (njs_slow_path(shared->prototypes == NULL)) { njs_memory_error(vm); return -1; } } ctor = njs_arr_add(shared->constructors); if (njs_slow_path(ctor == NULL)) { njs_memory_error(vm); return -1; } prototype = njs_arr_add(shared->prototypes); if (njs_slow_path(prototype == NULL)) { njs_memory_error(vm); return -1; } njs_assert(shared->constructors->items == shared->prototypes->items); return shared->constructors->items - 1; } void njs_vm_destroy(njs_vm_t *vm) { if (vm->hooks[NJS_HOOK_EXIT] != NULL) { (void) njs_vm_call(vm, vm->hooks[NJS_HOOK_EXIT], NULL, 0); } njs_mp_destroy(vm->mem_pool); } njs_int_t njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end) { size_t global_items; njs_int_t ret; njs_str_t ast; njs_chb_t chain; njs_value_t **global, **new; njs_parser_t parser; njs_vm_code_t *code; njs_generator_t generator; njs_parser_scope_t *scope; vm->codes = NULL; global_items = (vm->global_scope != NULL) ? vm->global_scope->items : 0; ret = njs_parser_init(vm, &parser, vm->global_scope, &vm->options.file, *start, end, 0); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_parser(vm, &parser); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } if (njs_slow_path(vm->options.ast)) { NJS_CHB_MP_INIT(&chain, vm); ret = njs_parser_serialize_ast(parser.node, &chain); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (njs_slow_path(njs_chb_join(&chain, &ast) != NJS_OK)) { return NJS_ERROR; } njs_print(ast.start, ast.length); njs_chb_destroy(&chain); njs_mp_free(vm->mem_pool, ast.start); } *start = parser.lexer->start; scope = parser.scope; ret = njs_generator_init(&generator, &vm->options.file, 0, 0); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "njs_generator_init() failed"); return NJS_ERROR; } code = njs_generate_scope(vm, &generator, scope, &njs_entry_main); if (njs_slow_path(code == NULL)) { if (!njs_is_error(&vm->exception)) { njs_internal_error(vm, "njs_generate_scope() failed"); } return NJS_ERROR; } if (scope->items > global_items) { global = vm->levels[NJS_LEVEL_GLOBAL]; new = njs_scope_make(vm, scope->items); if (njs_slow_path(new == NULL)) { return ret; } vm->levels[NJS_LEVEL_GLOBAL] = new; if (global != NULL) { while (global_items != 0) { global_items--; *new++ = *global++; } } } /* globalThis and this */ njs_scope_value_set(vm, njs_scope_global_this_index(), &vm->global_value); vm->start = generator.code_start; vm->global_scope = scope; if (vm->options.disassemble) { njs_disassembler(vm); } return NJS_OK; } njs_mod_t * njs_vm_add_module(njs_vm_t *vm, njs_str_t *name, njs_value_t *value) { return njs_module_add(vm, name, value); } njs_mod_t * njs_vm_compile_module(njs_vm_t *vm, njs_str_t *name, u_char **start, u_char *end) { njs_int_t ret; njs_arr_t *arr; njs_mod_t *module; njs_parser_t parser; njs_vm_code_t *code; njs_generator_t generator; njs_parser_scope_t *scope; njs_function_lambda_t *lambda; module = njs_module_find(vm, name, 1); if (module != NULL) { return module; } module = njs_module_add(vm, name, NULL); if (njs_slow_path(module == NULL)) { return NULL; } ret = njs_parser_init(vm, &parser, NULL, name, *start, end, 1); if (njs_slow_path(ret != NJS_OK)) { return NULL; } parser.module = 1; ret = njs_parser(vm, &parser); if (njs_slow_path(ret != NJS_OK)) { return NULL; } *start = parser.lexer->start; ret = njs_generator_init(&generator, &module->name, 0, 0); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "njs_generator_init() failed"); return NULL; } code = njs_generate_scope(vm, &generator, parser.scope, &njs_entry_module); if (njs_slow_path(code == NULL)) { njs_internal_error(vm, "njs_generate_scope() failed"); return NULL; } lambda = njs_mp_zalloc(vm->mem_pool, sizeof(njs_function_lambda_t)); if (njs_fast_path(lambda == NULL)) { njs_memory_error(vm); return NULL; } scope = parser.scope; lambda->start = generator.code_start; lambda->nlocal = scope->items; arr = scope->declarations; lambda->declarations = (arr != NULL) ? arr->start : NULL; lambda->ndeclarations = (arr != NULL) ? arr->items : 0; module->function.u.lambda = lambda; return module; } njs_int_t njs_vm_reuse(njs_vm_t *vm) { vm->active_frame = NULL; vm->top_frame = NULL; vm->modules = NULL; return njs_object_make_shared(vm, njs_object(&vm->global_value)); } njs_vm_t * njs_vm_clone(njs_vm_t *vm, njs_external_ptr_t external) { njs_mp_t *nmp; njs_vm_t *nvm; njs_int_t ret; njs_value_t **global, **value; njs_thread_log_debug("CLONE:"); if (vm->options.interactive) { return NULL; } nmp = njs_mp_fast_create(2 * njs_pagesize(), 128, 512, 16); if (njs_slow_path(nmp == NULL)) { return NULL; } nvm = njs_mp_align(nmp, sizeof(njs_value_t), sizeof(njs_vm_t)); if (njs_slow_path(nvm == NULL)) { goto fail; } *nvm = *vm; nvm->mem_pool = nmp; nvm->trace.data = nvm; nvm->external = external; ret = njs_vm_runtime_init(nvm); if (njs_slow_path(ret != NJS_OK)) { goto fail; } ret = njs_vm_protos_init(nvm, &nvm->global_value); if (njs_slow_path(ret != NJS_OK)) { goto fail; } global = njs_scope_make(nvm, nvm->global_scope->items); if (njs_slow_path(global == NULL)) { goto fail; } if (nvm->options.unsafe) { nvm->scope_absolute = njs_arr_create(nvm->mem_pool, vm->scope_absolute->items, sizeof(njs_value_t *)); if (njs_slow_path(nvm->scope_absolute == NULL)) { goto fail; } value = njs_arr_add_multiple(nvm->scope_absolute, vm->scope_absolute->items); if (njs_slow_path(value == NULL)) { goto fail; } memcpy(value, vm->scope_absolute->start, vm->scope_absolute->items * sizeof(njs_value_t *)); } nvm->levels[NJS_LEVEL_GLOBAL] = global; /* globalThis and this */ njs_scope_value_set(nvm, njs_scope_global_this_index(), &nvm->global_value); nvm->levels[NJS_LEVEL_LOCAL] = NULL; return nvm; fail: njs_mp_destroy(nmp); return NULL; } njs_int_t njs_vm_runtime_init(njs_vm_t *vm) { njs_int_t ret; njs_frame_t *frame; if (vm->active_frame == NULL) { frame = (njs_frame_t *) njs_function_frame_alloc(vm, NJS_FRAME_SIZE); if (njs_slow_path(frame == NULL)) { njs_memory_error(vm); return NJS_ERROR; } frame->exception.catch = NULL; frame->exception.next = NULL; frame->previous_active_frame = NULL; vm->active_frame = frame; } ret = njs_regexp_init(vm); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_lvlhsh_init(&vm->values_hash); njs_lvlhsh_init(&vm->keywords_hash); njs_lvlhsh_init(&vm->modules_hash); njs_rbtree_init(&vm->global_symbols, njs_symbol_rbtree_cmp); njs_queue_init(&vm->jobs); return NJS_OK; } void njs_vm_constructors_init(njs_vm_t *vm) { njs_uint_t i; njs_object_t *object_prototype, *function_prototype, *typed_array_prototype, *error_prototype, *async_prototype, *typed_array_ctor, *error_ctor; object_prototype = njs_vm_proto(vm, NJS_OBJ_TYPE_OBJECT); for (i = NJS_OBJ_TYPE_ARRAY; i < NJS_OBJ_TYPE_NORMAL_MAX; i++) { vm->prototypes[i].object.__proto__ = object_prototype; } typed_array_prototype = njs_vm_proto(vm, NJS_OBJ_TYPE_TYPED_ARRAY); for (i = NJS_OBJ_TYPE_TYPED_ARRAY_MIN; i < NJS_OBJ_TYPE_TYPED_ARRAY_MAX; i++) { vm->prototypes[i].object.__proto__ = typed_array_prototype; } vm->prototypes[NJS_OBJ_TYPE_ARRAY_ITERATOR].object.__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_ITERATOR); vm->prototypes[NJS_OBJ_TYPE_BUFFER].object.__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_UINT8_ARRAY); error_prototype = njs_vm_proto(vm, NJS_OBJ_TYPE_ERROR); error_prototype->__proto__ = object_prototype; for (i = NJS_OBJ_TYPE_EVAL_ERROR; i < vm->constructors_size; i++) { vm->prototypes[i].object.__proto__ = error_prototype; } function_prototype = njs_vm_proto(vm, NJS_OBJ_TYPE_FUNCTION); async_prototype = njs_vm_proto(vm, NJS_OBJ_TYPE_ASYNC_FUNCTION); async_prototype->__proto__ = function_prototype; for (i = NJS_OBJ_TYPE_OBJECT; i < NJS_OBJ_TYPE_NORMAL_MAX; i++) { vm->constructors[i].object.__proto__ = function_prototype; } typed_array_ctor = &njs_vm_ctor(vm, NJS_OBJ_TYPE_TYPED_ARRAY).object; for (i = NJS_OBJ_TYPE_TYPED_ARRAY_MIN; i < NJS_OBJ_TYPE_TYPED_ARRAY_MAX; i++) { vm->constructors[i].object.__proto__ = typed_array_ctor; } error_ctor = &njs_vm_ctor(vm, NJS_OBJ_TYPE_ERROR).object; error_ctor->__proto__ = function_prototype; for (i = NJS_OBJ_TYPE_EVAL_ERROR; i < vm->constructors_size; i++) { vm->constructors[i].object.__proto__ = error_ctor; } } static njs_int_t njs_vm_protos_init(njs_vm_t *vm, njs_value_t *global) { size_t ctor_size, proto_size; vm->constructors_size = vm->shared->constructors->items; ctor_size = vm->constructors_size * sizeof(njs_function_t); proto_size = vm->constructors_size * sizeof(njs_object_prototype_t); vm->constructors = njs_mp_alloc(vm->mem_pool, ctor_size + proto_size); if (njs_slow_path(vm->constructors == NULL)) { njs_memory_error(vm); return NJS_ERROR; } vm->prototypes = (njs_object_prototype_t *) ((u_char *) vm->constructors + ctor_size); memcpy(vm->constructors, vm->shared->constructors->start, ctor_size); memcpy(vm->prototypes, vm->shared->prototypes->start, proto_size); njs_vm_constructors_init(vm); vm->global_object.__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_OBJECT); njs_set_undefined(global); njs_set_object(global, &vm->global_object); vm->string_object = vm->shared->string_object; vm->string_object.__proto__ = njs_vm_proto(vm, NJS_OBJ_TYPE_STRING); return NJS_OK; } njs_int_t njs_vm_call(njs_vm_t *vm, njs_function_t *function, const njs_value_t *args, njs_uint_t nargs) { njs_value_t unused; return njs_vm_invoke(vm, function, args, nargs, &unused); } njs_int_t njs_vm_invoke(njs_vm_t *vm, njs_function_t *function, const njs_value_t *args, njs_uint_t nargs, njs_value_t *retval) { njs_int_t ret; ret = njs_function_frame(vm, function, &njs_value_undefined, args, nargs, 0); if (njs_slow_path(ret != NJS_OK)) { return ret; } return njs_function_frame_invoke(vm, retval); } void njs_vm_scopes_restore(njs_vm_t *vm, njs_native_frame_t *native) { njs_frame_t *frame; vm->top_frame = native->previous; if (native->function->native) { return; } frame = (njs_frame_t *) native; frame = frame->previous_active_frame; vm->active_frame = frame; } njs_int_t njs_vm_pending(njs_vm_t *vm) { return !njs_queue_is_empty(&(vm)->jobs); } njs_int_t njs_vm_enqueue_job(njs_vm_t *vm, njs_function_t *function, const njs_value_t *args, njs_uint_t nargs) { njs_event_t *event; event = njs_mp_zalloc(vm->mem_pool, sizeof(njs_event_t)); if (njs_slow_path(event == NULL)) { njs_memory_error(vm); return NJS_ERROR; } event->function = function; if (nargs != 0) { event->args = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t) * nargs); if (njs_slow_path(event->args == NULL)) { njs_memory_error(vm); return NJS_ERROR; } memcpy(event->args, args, sizeof(njs_value_t) * nargs); event->nargs = nargs; } njs_queue_insert_tail(&vm->jobs, &event->link); return NJS_OK; } njs_int_t njs_vm_start(njs_vm_t *vm, njs_value_t *retval) { njs_int_t ret; ret = njs_vmcode_interpreter(vm, vm->start, retval, NULL, NULL); return (ret == NJS_ERROR) ? NJS_ERROR : NJS_OK; } njs_int_t njs_vm_execute_pending_job(njs_vm_t *vm) { njs_int_t ret; njs_event_t *ev; njs_queue_t *jobs; njs_queue_link_t *link; jobs = &vm->jobs; link = njs_queue_first(jobs); if (link == njs_queue_tail(jobs)) { return NJS_OK; } ev = njs_queue_link_data(link, njs_event_t, link); njs_queue_remove(&ev->link); ret = njs_vm_call(vm, ev->function, ev->args, ev->nargs); if (ret == NJS_ERROR) { return ret; } return 1; } void njs_vm_set_module_loader(njs_vm_t *vm, njs_module_loader_t module_loader, void *opaque) { vm->module_loader = module_loader; vm->module_loader_opaque = opaque; } void njs_vm_set_rejection_tracker(njs_vm_t *vm, njs_rejection_tracker_t rejection_tracker, void *opaque) { vm->rejection_tracker = rejection_tracker; vm->rejection_tracker_opaque = opaque; } njs_value_t njs_vm_exception(njs_vm_t *vm) { njs_value_t value; value = vm->exception; njs_set_invalid(&vm->exception); return value; } void njs_vm_exception_get(njs_vm_t *vm, njs_value_t *retval) { *retval = njs_vm_exception(vm); } njs_mp_t * njs_vm_memory_pool(njs_vm_t *vm) { return vm->mem_pool; } njs_external_ptr_t njs_vm_external_ptr(njs_vm_t *vm) { return vm->external; } njs_bool_t njs_vm_constructor(njs_vm_t *vm) { return vm->top_frame->ctor; } uintptr_t njs_vm_meta(njs_vm_t *vm, njs_uint_t index) { njs_vm_meta_t *metas; metas = vm->options.metas; if (njs_slow_path(metas == NULL || metas->size <= index)) { return -1; } return metas->values[index]; } njs_vm_opt_t * njs_vm_options(njs_vm_t *vm) { return &vm->options; } void njs_vm_throw(njs_vm_t *vm, const njs_value_t *value) { vm->exception = *value; } void njs_vm_error2(njs_vm_t *vm, unsigned error_type, const char *fmt, ...) { va_list args; if (error_type > (NJS_OBJ_TYPE_ERROR_MAX - NJS_OBJ_TYPE_ERROR)) { return; } va_start(args, fmt); error_type += NJS_OBJ_TYPE_ERROR; njs_throw_error_va(vm, njs_vm_proto(vm, error_type), fmt, args); va_end(args); } void njs_vm_error3(njs_vm_t *vm, unsigned type, const char *fmt, ...) { va_list args; if (type > vm->constructors_size) { return; } va_start(args, fmt); njs_throw_error_va(vm, njs_vm_proto(vm, type), fmt, args); va_end(args); } njs_int_t njs_vm_global(njs_vm_t *vm, njs_value_t *retval) { njs_value_assign(retval, &vm->global_value); return NJS_OK; } njs_int_t njs_vm_value(njs_vm_t *vm, const njs_str_t *path, njs_value_t *retval) { u_char *start, *p, *end; size_t size; njs_int_t ret; njs_value_t value, key; start = path->start; end = start + path->length; njs_value_assign(&value, &vm->global_value); for ( ;; ) { p = njs_strlchr(start, end, '.'); size = ((p != NULL) ? p : end) - start; if (njs_slow_path(size == 0)) { njs_type_error(vm, "empty path element"); return NJS_ERROR; } ret = njs_string_create(vm, &key, start, size); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_value_property(vm, &value, &key, njs_value_arg(retval)); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (p == NULL) { break; } start = p + 1; value = *retval; } return NJS_OK; } static njs_int_t njs_vm_bind2(njs_vm_t *vm, const njs_str_t *var_name, njs_object_prop_t *prop, njs_bool_t shared) { njs_int_t ret; njs_object_t *global; njs_lvlhsh_t *hash; njs_lvlhsh_query_t lhq; ret = njs_string_create(vm, &prop->name, var_name->start, var_name->length); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } lhq.value = prop; lhq.key = *var_name; lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length); lhq.replace = 1; lhq.pool = vm->mem_pool; lhq.proto = &njs_object_hash_proto; global = &vm->global_object; hash = shared ? &global->shared_hash : &global->hash; ret = njs_lvlhsh_insert(hash, &lhq); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "lvlhsh insert failed"); return ret; } return NJS_OK; } njs_int_t njs_vm_bind(njs_vm_t *vm, const njs_str_t *var_name, const njs_value_t *value, njs_bool_t shared) { njs_object_prop_t *prop; prop = njs_object_prop_alloc(vm, &njs_value_undefined, value, 1); if (njs_slow_path(prop == NULL)) { return NJS_ERROR; } return njs_vm_bind2(vm, var_name, prop, shared); } njs_int_t njs_vm_bind_handler(njs_vm_t *vm, const njs_str_t *var_name, njs_prop_handler_t handler, uint16_t magic16, uint32_t magic32, njs_bool_t shared) { njs_object_prop_t *prop; prop = njs_object_prop_alloc(vm, &njs_string_empty, &njs_value_invalid, 1); if (njs_slow_path(prop == NULL)) { return NJS_ERROR; } prop->type = NJS_PROPERTY_HANDLER; prop->u.value.type = NJS_INVALID; prop->u.value.data.truth = 1; njs_prop_magic16(prop) = magic16; njs_prop_magic32(prop) = magic32; njs_prop_handler(prop) = handler; return njs_vm_bind2(vm, var_name, prop, shared); } void njs_value_string_get(njs_value_t *value, njs_str_t *dst) { njs_string_get(value, dst); } njs_int_t njs_vm_value_array_buffer_set(njs_vm_t *vm, njs_value_t *value, const u_char *start, uint32_t size) { njs_array_buffer_t *array; array = njs_array_buffer_alloc(vm, 0, 0); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } array->u.data = (u_char *) start; array->size = size; njs_set_array_buffer(value, array); return NJS_OK; } njs_int_t njs_vm_value_buffer_set(njs_vm_t *vm, njs_value_t *value, const u_char *start, uint32_t size) { return njs_buffer_set(vm, value, start, size); } njs_int_t njs_vm_value_string_create(njs_vm_t *vm, njs_value_t *value, const u_char *start, uint32_t size) { return njs_string_create(vm, value, start, size); } njs_int_t njs_vm_value_string_create_chb(njs_vm_t *vm, njs_value_t *value, njs_chb_t *chain) { return njs_string_create_chb(vm, value, chain); } njs_function_t * njs_vm_function(njs_vm_t *vm, const njs_str_t *path) { njs_int_t ret; njs_value_t retval; ret = njs_vm_value(vm, path, &retval); if (njs_slow_path(ret != NJS_OK || !njs_is_function(&retval))) { return NULL; } return njs_function(&retval); } uint16_t njs_vm_prop_magic16(njs_object_prop_t *prop) { return njs_prop_magic16(prop); } uint32_t njs_vm_prop_magic32(njs_object_prop_t *prop) { return njs_prop_magic32(prop); } njs_int_t njs_vm_prop_name(njs_vm_t *vm, njs_object_prop_t *prop, njs_str_t *dst) { if (njs_slow_path(!njs_is_string(&prop->name))) { return NJS_ERROR; } njs_string_get(&prop->name, dst); return NJS_OK; } njs_noinline void njs_vm_memory_error(njs_vm_t *vm) { njs_memory_error_set(vm, &vm->exception); } njs_int_t njs_vm_value_string(njs_vm_t *vm, njs_str_t *dst, njs_value_t *src) { njs_int_t ret; njs_uint_t exception; njs_value_t value; if (njs_slow_path(vm->top_frame == NULL)) { /* An exception was thrown during compilation. */ njs_vm_runtime_init(vm); } if (njs_is_valid(&vm->exception)) { value = njs_vm_exception(vm); src = &value; } if (njs_slow_path(src->type == NJS_NUMBER && njs_number(src) == 0 && signbit(njs_number(src)))) { njs_string_get(&njs_string_minus_zero, dst); return NJS_OK; } exception = 0; again: ret = njs_vm_value_to_string(vm, dst, src); if (njs_fast_path(ret == NJS_OK)) { return NJS_OK; } if (!exception) { exception = 1; /* value evaluation threw an exception. */ *src = njs_vm_exception(vm); goto again; } dst->length = 0; dst->start = NULL; return NJS_ERROR; } njs_int_t njs_vm_exception_string(njs_vm_t *vm, njs_str_t *dst) { njs_value_t exception; exception = njs_vm_exception(vm); return njs_vm_value_string(vm, dst, &exception); } njs_value_t * njs_vm_value_enumerate(njs_vm_t *vm, njs_value_t *value, uint32_t flags, njs_value_t *retval) { njs_int_t ret; njs_value_t *val; njs_array_t *keys; njs_rbtree_t *variables; njs_rbtree_node_t *rb_node; njs_variable_node_t *node; const njs_lexer_entry_t *lex_entry; static const njs_str_t njs_this_str = njs_str("this"); keys = njs_value_enumerate(vm, value, flags); if (njs_slow_path(keys == NULL)) { return NULL; } if (!njs_values_same(value, &vm->global_value) || vm->global_scope == NULL) { goto done; } /* TODO: workaround for values in global object. */ variables = &vm->global_scope->variables; rb_node = njs_rbtree_min(variables); while (njs_rbtree_is_there_successor(variables, rb_node)) { node = (njs_variable_node_t *) rb_node; lex_entry = njs_lexer_entry(node->variable->unique_id); if (njs_slow_path(lex_entry == NULL)) { return NULL; } if (njs_strstr_eq(&lex_entry->name, &njs_this_str)) { rb_node = njs_rbtree_node_successor(variables, rb_node); continue; } val = njs_array_push(vm, keys); if (njs_slow_path(value == NULL)) { return NULL; } ret = njs_string_create(vm, val, lex_entry->name.start, lex_entry->name.length); if (njs_slow_path(ret != NJS_OK)) { return NULL; } rb_node = njs_rbtree_node_successor(variables, rb_node); } done: njs_set_array(retval, keys); return retval; } njs_value_t * njs_vm_value_own_enumerate(njs_vm_t *vm, njs_value_t *value, uint32_t flags, njs_value_t *retval) { njs_array_t *keys; keys = njs_value_own_enumerate(vm, value, flags); if (njs_slow_path(keys == NULL)) { return NULL; } njs_set_array(retval, keys); return retval; } njs_int_t njs_vm_object_alloc(njs_vm_t *vm, njs_value_t *retval, ...) { va_list args; njs_int_t ret; njs_value_t *name, *value; njs_object_t *object; njs_object_prop_t *prop; njs_lvlhsh_query_t lhq; object = njs_object_alloc(vm); if (njs_slow_path(object == NULL)) { return NJS_ERROR; } ret = NJS_ERROR; va_start(args, retval); for ( ;; ) { name = va_arg(args, njs_value_t *); if (name == NULL) { break; } value = va_arg(args, njs_value_t *); if (value == NULL) { njs_type_error(vm, "missed value for a key"); goto done; } if (njs_slow_path(!njs_is_string(name))) { njs_type_error(vm, "prop name is not a string"); goto done; } lhq.replace = 0; lhq.pool = vm->mem_pool; lhq.proto = &njs_object_hash_proto; njs_string_get(name, &lhq.key); lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length); prop = njs_object_prop_alloc(vm, name, value, 1); if (njs_slow_path(prop == NULL)) { goto done; } lhq.value = prop; ret = njs_lvlhsh_insert(&object->hash, &lhq); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, NULL); goto done; } } ret = NJS_OK; njs_set_object(retval, object); done: va_end(args); return ret; } njs_value_t * njs_vm_object_keys(njs_vm_t *vm, njs_value_t *value, njs_value_t *retval) { njs_array_t *keys; keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS | NJS_ENUM_STRING | NJS_ENUM_ENUMERABLE_ONLY); if (njs_slow_path(keys == NULL)) { return NULL; } njs_set_array(retval, keys); return retval; } njs_int_t njs_vm_prototype(njs_vm_t *vm, njs_value_t *value, njs_value_t *retval) { njs_value_t arguments[2]; njs_set_undefined(&arguments[0]); njs_value_assign(&arguments[1], value); return njs_object_get_prototype_of(vm, njs_value_arg(&arguments), 2, 0, retval); } njs_int_t njs_vm_array_alloc(njs_vm_t *vm, njs_value_t *retval, uint32_t spare) { njs_array_t *array; array = njs_array_alloc(vm, 1, 0, spare); if (njs_slow_path(array == NULL)) { return NJS_ERROR; } njs_set_array(retval, array); return NJS_OK; } njs_value_t * njs_vm_array_push(njs_vm_t *vm, njs_value_t *value) { if (njs_slow_path(!njs_is_array(value))) { njs_type_error(vm, "njs_vm_array_push() argument is not array"); return NULL; } return njs_array_push(vm, njs_array(value)); } njs_value_t * njs_vm_object_prop(njs_vm_t *vm, njs_value_t *value, const njs_str_t *prop, njs_opaque_value_t *retval) { njs_int_t ret; njs_value_t key; if (njs_slow_path(!njs_is_object(value))) { njs_type_error(vm, "njs_vm_object_prop() argument is not object"); return NULL; } ret = njs_vm_value_string_create(vm, &key, prop->start, prop->length); if (njs_slow_path(ret != NJS_OK)) { return NULL; } ret = njs_value_property(vm, value, &key, njs_value_arg(retval)); if (njs_slow_path(ret != NJS_OK)) { return NULL; } return njs_value_arg(retval); } njs_int_t njs_vm_object_prop_set(njs_vm_t *vm, njs_value_t *value, const njs_str_t *prop, njs_opaque_value_t *setval) { njs_int_t ret; njs_value_t key; if (njs_slow_path(!njs_is_object(value))) { njs_type_error(vm, "njs_vm_object_prop_set() argument is not object"); return NJS_ERROR; } ret = njs_vm_value_string_create(vm, &key, prop->start, prop->length); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_value_property_set(vm, value, &key, njs_value_arg(setval)); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } return NJS_OK; } njs_value_t * njs_vm_array_prop(njs_vm_t *vm, njs_value_t *value, int64_t index, njs_opaque_value_t *retval) { njs_int_t ret; njs_array_t *array; if (njs_slow_path(!njs_is_object(value))) { njs_type_error(vm, "njs_vm_array_prop() argument is not object"); return NULL; } if (njs_fast_path(njs_is_fast_array(value))) { array = njs_array(value); if (index >= 0 && index < array->length) { return &array->start[index]; } return NULL; } ret = njs_value_property_i64(vm, value, index, njs_value_arg(retval)); if (njs_slow_path(ret != NJS_OK)) { return NULL; } return njs_value_arg(retval); } njs_int_t njs_vm_object_iterate(njs_vm_t *vm, njs_iterator_args_t *args, njs_iterator_handler_t handler, njs_value_t *retval) { return njs_object_iterate(vm, args, handler, retval); } njs_value_t * njs_vm_array_start(njs_vm_t *vm, njs_value_t *value) { if (njs_slow_path(!njs_is_fast_array(value))) { njs_type_error(vm, "njs_vm_array_start() argument is not a fast array"); return NULL; } return njs_array(value)->start; } njs_int_t njs_vm_array_length(njs_vm_t *vm, njs_value_t *value, int64_t *length) { if (njs_fast_path(njs_is_array(value))) { *length = njs_array(value)->length; } return njs_object_length(vm, value, length); } njs_int_t njs_vm_date_alloc(njs_vm_t *vm, njs_value_t *retval, double time) { njs_date_t *date; date = njs_date_alloc(vm, time); if (njs_slow_path(date == NULL)) { return NJS_ERROR; } njs_set_date(retval, date); return NJS_OK; } njs_int_t njs_vm_value_to_string(njs_vm_t *vm, njs_str_t *dst, njs_value_t *src) { u_char *start; size_t size; njs_int_t ret; njs_value_t value, stack; if (njs_slow_path(src == NULL)) { return NJS_ERROR; } if (njs_is_error(src)) { if (njs_is_memory_error(vm, src)) { njs_string_get(&njs_string_memory_error, dst); return NJS_OK; } ret = njs_error_stack(vm, src, &stack); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (ret == NJS_OK) { src = &stack; } } value = *src; ret = njs_value_to_string(vm, &value, &value); if (njs_fast_path(ret == NJS_OK)) { size = value.short_string.size; if (size != NJS_STRING_LONG) { start = njs_mp_alloc(vm->mem_pool, size); if (njs_slow_path(start == NULL)) { njs_memory_error(vm); return NJS_ERROR; } memcpy(start, value.short_string.start, size); } else { size = value.long_string.size; start = value.long_string.data->start; } dst->length = size; dst->start = start; } return ret; } /* * If string value is null-terminated the corresponding C string * is returned as is, otherwise the new copy is allocated with * the terminating zero byte. */ const char * njs_vm_value_to_c_string(njs_vm_t *vm, njs_value_t *value) { u_char *p, *data, *start; size_t size; njs_assert(njs_is_string(value)); if (value->short_string.size != NJS_STRING_LONG) { start = value->short_string.start; size = value->short_string.size; if (size < NJS_STRING_SHORT) { start[size] = '\0'; return (const char *) start; } } else { start = value->long_string.data->start; size = value->long_string.size; } data = njs_mp_alloc(vm->mem_pool, size + njs_length("\0")); if (njs_slow_path(data == NULL)) { njs_memory_error(vm); return NULL; } p = njs_cpymem(data, start, size); *p++ = '\0'; return (const char *) data; } njs_int_t njs_value_to_string(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value) { njs_int_t ret; njs_value_t primitive; if (njs_slow_path(!njs_is_primitive(value))) { if (njs_slow_path(njs_is_object_symbol(value))) { /* should fail */ value = njs_object_value(value); } else { ret = njs_value_to_primitive(vm, &primitive, value, 1); if (njs_slow_path(ret != NJS_OK)) { return ret; } value = &primitive; } } return njs_primitive_value_to_string(vm, dst, value); } njs_int_t njs_vm_value_to_bytes(njs_vm_t *vm, njs_str_t *dst, njs_value_t *src) { u_char *start; size_t size, length, offset; njs_int_t ret; njs_value_t value; njs_typed_array_t *array; njs_array_buffer_t *buffer; if (njs_slow_path(src == NULL)) { return NJS_ERROR; } ret = NJS_OK; value = *src; switch (value.type) { case NJS_TYPED_ARRAY: case NJS_DATA_VIEW: case NJS_ARRAY_BUFFER: if (value.type != NJS_ARRAY_BUFFER) { array = njs_typed_array(&value); buffer = njs_typed_array_buffer(array); offset = array->offset; length = array->byte_length; } else { buffer = njs_array_buffer(&value); offset = 0; length = buffer->size; } if (njs_slow_path(njs_is_detached_buffer(buffer))) { if (length == 0) { dst->length = 0; dst->start = NULL; return NJS_OK; } njs_type_error(vm, "detached buffer"); return NJS_ERROR; } dst->start = &buffer->u.u8[offset]; dst->length = length; break; default: ret = njs_value_to_string(vm, &value, &value); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } size = value.short_string.size; if (size != NJS_STRING_LONG) { start = njs_mp_alloc(vm->mem_pool, size); if (njs_slow_path(start == NULL)) { njs_memory_error(vm); return NJS_ERROR; } memcpy(start, value.short_string.start, size); } else { size = value.long_string.size; start = value.long_string.data->start; } dst->length = size; dst->start = start; } return ret; } njs_int_t njs_vm_string_compare(const njs_value_t *v1, const njs_value_t *v2) { return njs_string_cmp(v1, v2); } void * njs_lvlhsh_alloc(void *data, size_t size) { return njs_mp_align(data, NJS_MAX_ALIGNMENT, size); } void njs_lvlhsh_free(void *data, void *p, size_t size) { njs_mp_free(data, p); } njs-0.8.9/src/njs_vm.h000066400000000000000000000167471474132077100146030ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_VM_H_INCLUDED_ #define _NJS_VM_H_INCLUDED_ #define NJS_MAX_STACK_SIZE (64 * 1024) typedef struct njs_frame_s njs_frame_t; typedef struct njs_native_frame_s njs_native_frame_t; typedef struct njs_parser_s njs_parser_t; typedef struct njs_parser_scope_s njs_parser_scope_t; typedef struct njs_parser_node_s njs_parser_node_t; typedef struct njs_generator_s njs_generator_t; typedef enum { NJS_SCOPE_GLOBAL = 0, NJS_SCOPE_FUNCTION, NJS_SCOPE_BLOCK } njs_scope_t; typedef enum { NJS_OBJ_TYPE_OBJECT = 0, NJS_OBJ_TYPE_ARRAY, NJS_OBJ_TYPE_BOOLEAN, NJS_OBJ_TYPE_NUMBER, NJS_OBJ_TYPE_SYMBOL, NJS_OBJ_TYPE_STRING, NJS_OBJ_TYPE_FUNCTION, NJS_OBJ_TYPE_ASYNC_FUNCTION, NJS_OBJ_TYPE_REGEXP, NJS_OBJ_TYPE_DATE, NJS_OBJ_TYPE_PROMISE, NJS_OBJ_TYPE_ARRAY_BUFFER, NJS_OBJ_TYPE_DATA_VIEW, NJS_OBJ_TYPE_TEXT_DECODER, NJS_OBJ_TYPE_TEXT_ENCODER, NJS_OBJ_TYPE_BUFFER, #define NJS_OBJ_TYPE_HIDDEN_MIN (NJS_OBJ_TYPE_ITERATOR) NJS_OBJ_TYPE_ITERATOR, NJS_OBJ_TYPE_ARRAY_ITERATOR, NJS_OBJ_TYPE_TYPED_ARRAY, #define NJS_OBJ_TYPE_HIDDEN_MAX (NJS_OBJ_TYPE_TYPED_ARRAY + 1) #define NJS_OBJ_TYPE_NORMAL_MAX (NJS_OBJ_TYPE_HIDDEN_MAX) #define NJS_OBJ_TYPE_TYPED_ARRAY_MIN (NJS_OBJ_TYPE_UINT8_ARRAY) #define njs_typed_array_index(type) (type - NJS_OBJ_TYPE_TYPED_ARRAY_MIN) NJS_OBJ_TYPE_UINT8_ARRAY, NJS_OBJ_TYPE_UINT8_CLAMPED_ARRAY, NJS_OBJ_TYPE_INT8_ARRAY, NJS_OBJ_TYPE_UINT16_ARRAY, NJS_OBJ_TYPE_INT16_ARRAY, NJS_OBJ_TYPE_UINT32_ARRAY, NJS_OBJ_TYPE_INT32_ARRAY, NJS_OBJ_TYPE_FLOAT32_ARRAY, NJS_OBJ_TYPE_FLOAT64_ARRAY, #define NJS_OBJ_TYPE_TYPED_ARRAY_MAX (NJS_OBJ_TYPE_FLOAT64_ARRAY + 1) #define NJS_OBJ_TYPE_TYPED_ARRAY_SIZE (NJS_OBJ_TYPE_TYPED_ARRAY_MAX \ - NJS_OBJ_TYPE_TYPED_ARRAY_MIN) NJS_OBJ_TYPE_ERROR, NJS_OBJ_TYPE_EVAL_ERROR, NJS_OBJ_TYPE_INTERNAL_ERROR, NJS_OBJ_TYPE_RANGE_ERROR, NJS_OBJ_TYPE_REF_ERROR, NJS_OBJ_TYPE_SYNTAX_ERROR, NJS_OBJ_TYPE_TYPE_ERROR, NJS_OBJ_TYPE_URI_ERROR, NJS_OBJ_TYPE_MEMORY_ERROR, NJS_OBJ_TYPE_AGGREGATE_ERROR, #define NJS_OBJ_TYPE_ERROR_MAX (NJS_OBJ_TYPE_AGGREGATE_ERROR) NJS_OBJ_TYPE_MAX, } njs_object_type_t; #define njs_primitive_prototype_index(type) \ (NJS_OBJ_TYPE_BOOLEAN + ((type) - NJS_BOOLEAN)) #define njs_prototype_type(index) \ (index + NJS_OBJECT) enum njs_object_e { NJS_OBJECT_THIS = 0, NJS_OBJECT_NJS, NJS_OBJECT_PROCESS, NJS_OBJECT_MATH, NJS_OBJECT_JSON, NJS_OBJECT_MAX }; enum njs_hook_e { NJS_HOOK_EXIT = 0, NJS_HOOK_MAX }; typedef enum { NJS_LEVEL_LOCAL = 0, NJS_LEVEL_CLOSURE, NJS_LEVEL_GLOBAL, NJS_LEVEL_STATIC, NJS_LEVEL_MAX } njs_level_type_t; struct njs_vm_s { njs_value_t exception; njs_arr_t *protos; njs_arr_t *scope_absolute; njs_value_t **levels[NJS_LEVEL_MAX]; njs_external_ptr_t external; njs_native_frame_t *top_frame; njs_frame_t *active_frame; njs_lvlhsh_t keywords_hash; njs_lvlhsh_t values_hash; njs_arr_t *modules; njs_lvlhsh_t modules_hash; uint32_t event_id; njs_queue_t jobs; njs_vm_opt_t options; #define njs_vm_proto(vm, index) (&(vm)->prototypes[index].object) #define njs_vm_ctor(vm, index) ((vm)->constructors[index]) njs_object_prototype_t *prototypes; njs_function_t *constructors; size_t constructors_size; njs_function_t *hooks[NJS_HOOK_MAX]; njs_mp_t *mem_pool; u_char *start; size_t spare_stack_size; njs_vm_shared_t *shared; njs_regex_generic_ctx_t *regex_generic_ctx; njs_regex_compile_ctx_t *regex_compile_ctx; njs_regex_match_data_t *single_match_data; njs_parser_scope_t *global_scope; /* * MemoryError is statically allocated immutable Error object * with the InternalError prototype. */ njs_object_value_t memory_error_object; njs_object_t string_object; njs_object_t global_object; njs_value_t global_value; njs_arr_t *codes; /* of njs_vm_code_t */ njs_arr_t *functions_name_cache; njs_trace_t trace; njs_random_t random; njs_rbtree_t global_symbols; uint64_t symbol_generator; njs_module_loader_t module_loader; void *module_loader_opaque; njs_rejection_tracker_t rejection_tracker; void *rejection_tracker_opaque; }; typedef struct { uint32_t offset; uint32_t line; } njs_vm_line_num_t; typedef struct { u_char *start; u_char *end; njs_str_t file; njs_str_t name; njs_arr_t *lines; /* of njs_vm_line_num_t */ } njs_vm_code_t; struct njs_vm_shared_s { njs_lvlhsh_t keywords_hash; njs_lvlhsh_t values_hash; njs_lvlhsh_t array_instance_hash; njs_lvlhsh_t string_instance_hash; njs_lvlhsh_t function_instance_hash; njs_lvlhsh_t async_function_instance_hash; njs_lvlhsh_t arrow_instance_hash; njs_lvlhsh_t arguments_object_instance_hash; njs_lvlhsh_t regexp_instance_hash; size_t module_items; njs_lvlhsh_t modules_hash; njs_lvlhsh_t env_hash; njs_object_t string_object; njs_object_t objects[NJS_OBJECT_MAX]; #define njs_shared_ctor(shared, index) \ ((njs_function_t *) njs_arr_item((shared)->constructors, index)) #define njs_shared_prototype(shared, index) \ ((njs_object_prototype_t *) njs_arr_item((shared)->prototypes, index)) njs_arr_t *constructors; /* of njs_function_t */ njs_arr_t *prototypes; /* of njs_object_prototype_t */ njs_exotic_slots_t global_slots; njs_regexp_pattern_t *empty_regexp_pattern; }; njs_int_t njs_vm_runtime_init(njs_vm_t *vm); njs_int_t njs_vm_ctor_push(njs_vm_t *vm); void njs_vm_constructors_init(njs_vm_t *vm); njs_value_t njs_vm_exception(njs_vm_t *vm); void njs_vm_scopes_restore(njs_vm_t *vm, njs_native_frame_t *frame); njs_int_t njs_builtin_objects_create(njs_vm_t *vm); njs_int_t njs_builtin_match_native_function(njs_vm_t *vm, njs_function_t *function, njs_str_t *name); void njs_disassemble(u_char *start, u_char *end, njs_int_t count, njs_arr_t *lines); void *njs_lvlhsh_alloc(void *data, size_t size); void njs_lvlhsh_free(void *data, void *p, size_t size); extern const njs_str_t njs_entry_empty; extern const njs_str_t njs_entry_main; extern const njs_str_t njs_entry_module; extern const njs_str_t njs_entry_native; extern const njs_str_t njs_entry_unknown; extern const njs_str_t njs_entry_anonymous; extern const njs_lvlhsh_proto_t njs_object_hash_proto; #endif /* _NJS_VM_H_INCLUDED_ */ njs-0.8.9/src/njs_vmcode.c000066400000000000000000002377551474132077100154350ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include struct njs_property_next_s { uint32_t index; njs_array_t *array; }; static njs_jump_off_t njs_vmcode_object(njs_vm_t *vm, njs_value_t *retval); static njs_jump_off_t njs_vmcode_array(njs_vm_t *vm, u_char *pc, njs_value_t *retval); static njs_jump_off_t njs_vmcode_function(njs_vm_t *vm, u_char *pc, njs_value_t *retval); static njs_jump_off_t njs_vmcode_arguments(njs_vm_t *vm, u_char *pc); static njs_jump_off_t njs_vmcode_regexp(njs_vm_t *vm, u_char *pc, njs_value_t *retval); static njs_jump_off_t njs_vmcode_template_literal(njs_vm_t *vm, njs_value_t *retval); static njs_jump_off_t njs_vmcode_function_copy(njs_vm_t *vm, njs_value_t *value, njs_index_t retval); static njs_jump_off_t njs_vmcode_property_init(njs_vm_t *vm, njs_value_t *value, njs_value_t *key, njs_value_t *retval); static njs_jump_off_t njs_vmcode_proto_init(njs_vm_t *vm, njs_value_t *value, njs_value_t *key, njs_value_t *retval); static njs_jump_off_t njs_vmcode_property_in(njs_vm_t *vm, njs_value_t *value, njs_value_t *key, njs_value_t *retval); static njs_jump_off_t njs_vmcode_property_foreach(njs_vm_t *vm, njs_value_t *object, u_char *pc, njs_value_t *retval); static njs_jump_off_t njs_vmcode_instance_of(njs_vm_t *vm, njs_value_t *object, njs_value_t *constructor, njs_value_t *retval); static njs_jump_off_t njs_vmcode_typeof(njs_vm_t *vm, njs_value_t *value, njs_value_t *retval); static njs_jump_off_t njs_vmcode_debugger(njs_vm_t *vm, njs_value_t *retval); static void njs_vmcode_return(njs_vm_t *vm, njs_value_t *dst, njs_value_t *retval); static njs_jump_off_t njs_vmcode_import(njs_vm_t *vm, njs_mod_t *module, njs_value_t *retval); static njs_int_t njs_vmcode_await(njs_vm_t *vm, njs_vmcode_await_t *await, njs_value_t *dst, njs_promise_capability_t *pcap, njs_async_ctx_t *actx); static njs_jump_off_t njs_vmcode_try_start(njs_vm_t *vm, njs_value_t *value, njs_value_t *offset, u_char *pc); static njs_jump_off_t njs_vmcode_try_break(njs_vm_t *vm, njs_value_t *value, njs_value_t *offset); static njs_jump_off_t njs_vmcode_try_continue(njs_vm_t *vm, njs_value_t *value, njs_value_t *offset); static njs_jump_off_t njs_vmcode_try_end(njs_vm_t *vm, njs_value_t *invld, njs_value_t *offset); static njs_jump_off_t njs_vmcode_finally(njs_vm_t *vm, njs_value_t *dst, njs_value_t *retval, u_char *pc); static void njs_vmcode_error(njs_vm_t *vm, u_char *pc); static njs_int_t njs_throw_cannot_property(njs_vm_t *vm, njs_value_t *object, njs_value_t *key, const char *what); static njs_jump_off_t njs_string_concat(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2, njs_value_t *retval); static njs_jump_off_t njs_values_equal(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2); static njs_jump_off_t njs_primitive_values_compare(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2); static njs_jump_off_t njs_function_frame_create(njs_vm_t *vm, njs_value_t *value, const njs_value_t *this, uintptr_t nargs, njs_bool_t ctor); #define njs_vmcode_operand(vm, index, _retval) \ do { \ _retval = njs_scope_valid_value(vm, index); \ if (njs_slow_path(_retval == NULL)) { \ goto error; \ } \ } while (0) njs_int_t njs_vmcode_interpreter(njs_vm_t *vm, u_char *pc, njs_value_t *rval, void *promise_cap, void *async_ctx) { u_char *catch; double num, exponent; int32_t i32; uint32_t u32; njs_str_t string; njs_uint_t hint; njs_bool_t valid, lambda_call; njs_value_t *retval, *value1, *value2; njs_value_t *src, *s1, *s2, dst; njs_value_t *function, name; njs_value_t numeric1, numeric2, primitive1, primitive2; njs_frame_t *frame; njs_jump_off_t ret; njs_vmcode_1addr_t *put_arg; njs_vmcode_await_t *await; njs_native_frame_t *previous, *native; njs_property_next_t *next; njs_vmcode_import_t *import; njs_vmcode_generic_t *vmcode; njs_vmcode_variable_t *var; njs_vmcode_prop_get_t *get; njs_vmcode_prop_set_t *set; njs_vmcode_prop_next_t *pnext; njs_vmcode_test_jump_t *test_jump; njs_vmcode_equal_jump_t *equal; njs_vmcode_try_return_t *try_return; njs_vmcode_method_frame_t *method_frame; njs_vmcode_function_copy_t *fcopy; njs_vmcode_prop_accessor_t *accessor; njs_vmcode_try_trampoline_t *try_trampoline; njs_vmcode_function_frame_t *function_frame; njs_vmcode_debug(vm, pc, "ENTER"); #if !defined(NJS_HAVE_COMPUTED_GOTO) #define SWITCH(op) switch (op) #define CASE(op) case op #define BREAK pc += ret; NEXT #define NEXT vmcode = (njs_vmcode_generic_t *) pc; \ goto next #define NEXT_LBL next: #define FALLTHROUGH NJS_FALLTHROUGH #else #define SWITCH(op) goto *switch_tbl[(uint8_t) op]; #define CASE(op) case_ ## op #define BREAK pc += ret; NEXT #define NEXT vmcode = (njs_vmcode_generic_t *) pc; \ SWITCH (vmcode->code) #define NEXT_LBL #define FALLTHROUGH #define NJS_GOTO_ROW(name) [ (uint8_t) name ] = &&case_ ## name static const void * const switch_tbl[NJS_VMCODES] = { NJS_GOTO_ROW(NJS_VMCODE_PUT_ARG), NJS_GOTO_ROW(NJS_VMCODE_STOP), NJS_GOTO_ROW(NJS_VMCODE_JUMP), NJS_GOTO_ROW(NJS_VMCODE_PROPERTY_SET), NJS_GOTO_ROW(NJS_VMCODE_PROPERTY_ACCESSOR), NJS_GOTO_ROW(NJS_VMCODE_IF_TRUE_JUMP), NJS_GOTO_ROW(NJS_VMCODE_IF_FALSE_JUMP), NJS_GOTO_ROW(NJS_VMCODE_IF_EQUAL_JUMP), NJS_GOTO_ROW(NJS_VMCODE_PROPERTY_INIT), NJS_GOTO_ROW(NJS_VMCODE_RETURN), NJS_GOTO_ROW(NJS_VMCODE_FUNCTION_COPY), NJS_GOTO_ROW(NJS_VMCODE_FUNCTION_FRAME), NJS_GOTO_ROW(NJS_VMCODE_METHOD_FRAME), NJS_GOTO_ROW(NJS_VMCODE_FUNCTION_CALL), NJS_GOTO_ROW(NJS_VMCODE_PROPERTY_NEXT), NJS_GOTO_ROW(NJS_VMCODE_ARGUMENTS), NJS_GOTO_ROW(NJS_VMCODE_PROTO_INIT), NJS_GOTO_ROW(NJS_VMCODE_TO_PROPERTY_KEY), NJS_GOTO_ROW(NJS_VMCODE_TO_PROPERTY_KEY_CHK), NJS_GOTO_ROW(NJS_VMCODE_SET_FUNCTION_NAME), NJS_GOTO_ROW(NJS_VMCODE_IMPORT), NJS_GOTO_ROW(NJS_VMCODE_AWAIT), NJS_GOTO_ROW(NJS_VMCODE_TRY_START), NJS_GOTO_ROW(NJS_VMCODE_THROW), NJS_GOTO_ROW(NJS_VMCODE_TRY_BREAK), NJS_GOTO_ROW(NJS_VMCODE_TRY_CONTINUE), NJS_GOTO_ROW(NJS_VMCODE_TRY_END), NJS_GOTO_ROW(NJS_VMCODE_CATCH), NJS_GOTO_ROW(NJS_VMCODE_FINALLY), NJS_GOTO_ROW(NJS_VMCODE_LET), NJS_GOTO_ROW(NJS_VMCODE_LET_UPDATE), NJS_GOTO_ROW(NJS_VMCODE_INITIALIZATION_TEST), NJS_GOTO_ROW(NJS_VMCODE_NOT_INITIALIZED), NJS_GOTO_ROW(NJS_VMCODE_ASSIGNMENT_ERROR), NJS_GOTO_ROW(NJS_VMCODE_ERROR), NJS_GOTO_ROW(NJS_VMCODE_MOVE), NJS_GOTO_ROW(NJS_VMCODE_PROPERTY_GET), NJS_GOTO_ROW(NJS_VMCODE_INCREMENT), NJS_GOTO_ROW(NJS_VMCODE_POST_INCREMENT), NJS_GOTO_ROW(NJS_VMCODE_DECREMENT), NJS_GOTO_ROW(NJS_VMCODE_POST_DECREMENT), NJS_GOTO_ROW(NJS_VMCODE_TRY_RETURN), NJS_GOTO_ROW(NJS_VMCODE_GLOBAL_GET), NJS_GOTO_ROW(NJS_VMCODE_LESS), NJS_GOTO_ROW(NJS_VMCODE_GREATER), NJS_GOTO_ROW(NJS_VMCODE_LESS_OR_EQUAL), NJS_GOTO_ROW(NJS_VMCODE_GREATER_OR_EQUAL), NJS_GOTO_ROW(NJS_VMCODE_ADDITION), NJS_GOTO_ROW(NJS_VMCODE_EQUAL), NJS_GOTO_ROW(NJS_VMCODE_NOT_EQUAL), NJS_GOTO_ROW(NJS_VMCODE_SUBTRACTION), NJS_GOTO_ROW(NJS_VMCODE_MULTIPLICATION), NJS_GOTO_ROW(NJS_VMCODE_EXPONENTIATION), NJS_GOTO_ROW(NJS_VMCODE_DIVISION), NJS_GOTO_ROW(NJS_VMCODE_REMAINDER), NJS_GOTO_ROW(NJS_VMCODE_BITWISE_AND), NJS_GOTO_ROW(NJS_VMCODE_BITWISE_OR), NJS_GOTO_ROW(NJS_VMCODE_BITWISE_XOR), NJS_GOTO_ROW(NJS_VMCODE_LEFT_SHIFT), NJS_GOTO_ROW(NJS_VMCODE_RIGHT_SHIFT), NJS_GOTO_ROW(NJS_VMCODE_UNSIGNED_RIGHT_SHIFT), NJS_GOTO_ROW(NJS_VMCODE_TEMPLATE_LITERAL), NJS_GOTO_ROW(NJS_VMCODE_PROPERTY_IN), NJS_GOTO_ROW(NJS_VMCODE_PROPERTY_DELETE), NJS_GOTO_ROW(NJS_VMCODE_PROPERTY_FOREACH), NJS_GOTO_ROW(NJS_VMCODE_STRICT_EQUAL), NJS_GOTO_ROW(NJS_VMCODE_STRICT_NOT_EQUAL), NJS_GOTO_ROW(NJS_VMCODE_TEST_IF_TRUE), NJS_GOTO_ROW(NJS_VMCODE_TEST_IF_FALSE), NJS_GOTO_ROW(NJS_VMCODE_COALESCE), NJS_GOTO_ROW(NJS_VMCODE_UNARY_PLUS), NJS_GOTO_ROW(NJS_VMCODE_UNARY_NEGATION), NJS_GOTO_ROW(NJS_VMCODE_BITWISE_NOT), NJS_GOTO_ROW(NJS_VMCODE_LOGICAL_NOT), NJS_GOTO_ROW(NJS_VMCODE_OBJECT), NJS_GOTO_ROW(NJS_VMCODE_ARRAY), NJS_GOTO_ROW(NJS_VMCODE_FUNCTION), NJS_GOTO_ROW(NJS_VMCODE_REGEXP), NJS_GOTO_ROW(NJS_VMCODE_INSTANCE_OF), NJS_GOTO_ROW(NJS_VMCODE_TYPEOF), NJS_GOTO_ROW(NJS_VMCODE_VOID), NJS_GOTO_ROW(NJS_VMCODE_DELETE), NJS_GOTO_ROW(NJS_VMCODE_DEBUGGER), }; #endif vmcode = (njs_vmcode_generic_t *) pc; NEXT_LBL; SWITCH (vmcode->code) { CASE (NJS_VMCODE_MOVE): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand2, value1); njs_vmcode_operand(vm, vmcode->operand1, retval); *retval = *value1; pc += sizeof(njs_vmcode_move_t); NEXT; CASE (NJS_VMCODE_PROPERTY_GET): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); get = (njs_vmcode_prop_get_t *) pc; njs_vmcode_operand(vm, get->value, retval); if (njs_slow_path(!njs_is_index_or_key(value2))) { if (njs_slow_path(njs_is_null_or_undefined(value1))) { (void) njs_throw_cannot_property(vm, value1, value2, "get"); goto error; } ret = njs_value_to_key(vm, &primitive1, value2); if (njs_slow_path(ret != NJS_OK)) { goto error; } value2 = &primitive1; } ret = njs_value_property(vm, value1, value2, retval); if (njs_slow_path(ret == NJS_ERROR)) { goto error; } pc += sizeof(njs_vmcode_prop_get_t); NEXT; CASE (NJS_VMCODE_INCREMENT): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); if (njs_slow_path(!njs_is_numeric(value2))) { ret = njs_value_to_numeric(vm, value2, &numeric1); if (njs_slow_path(ret != NJS_OK)) { goto error; } num = njs_number(&numeric1); } else { num = njs_number(value2); } njs_set_number(value1, num + 1); njs_vmcode_operand(vm, vmcode->operand1, retval); *retval = *value1; pc += sizeof(njs_vmcode_3addr_t); NEXT; CASE (NJS_VMCODE_POST_INCREMENT): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); if (njs_slow_path(!njs_is_numeric(value2))) { ret = njs_value_to_numeric(vm, value2, &numeric1); if (njs_slow_path(ret != NJS_OK)) { goto error; } num = njs_number(&numeric1); } else { num = njs_number(value2); } njs_set_number(value1, num + 1); njs_vmcode_operand(vm, vmcode->operand1, retval); njs_set_number(retval, num); pc += sizeof(njs_vmcode_3addr_t); NEXT; CASE (NJS_VMCODE_DECREMENT): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); if (njs_slow_path(!njs_is_numeric(value2))) { ret = njs_value_to_numeric(vm, value2, &numeric1); if (njs_slow_path(ret != NJS_OK)) { goto error; } num = njs_number(&numeric1); } else { num = njs_number(value2); } njs_set_number(value1, num - 1); njs_vmcode_operand(vm, vmcode->operand1, retval); *retval = *value1; pc += sizeof(njs_vmcode_3addr_t); NEXT; CASE (NJS_VMCODE_POST_DECREMENT): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); if (njs_slow_path(!njs_is_numeric(value2))) { ret = njs_value_to_numeric(vm, value2, &numeric1); if (njs_slow_path(ret != NJS_OK)) { goto error; } num = njs_number(&numeric1); } else { num = njs_number(value2); } njs_set_number(value1, num - 1); njs_vmcode_operand(vm, vmcode->operand1, retval); njs_set_number(retval, num); pc += sizeof(njs_vmcode_3addr_t); NEXT; CASE (NJS_VMCODE_GLOBAL_GET): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); get = (njs_vmcode_prop_get_t *) pc; njs_vmcode_operand(vm, get->value, retval); ret = njs_value_property(vm, value1, value2, retval); if (njs_slow_path(ret == NJS_ERROR)) { goto error; } pc += sizeof(njs_vmcode_prop_get_t); if (ret == NJS_OK) { pc += sizeof(njs_vmcode_error_t); } NEXT; /* * njs_vmcode_try_return() saves a return value to use it later by * njs_vmcode_finally(), and jumps to the nearest try_break block. */ CASE (NJS_VMCODE_TRY_RETURN): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand2, value1); njs_vmcode_operand(vm, vmcode->operand1, retval); *retval = *value1; try_return = (njs_vmcode_try_return_t *) pc; pc += try_return->offset; NEXT; CASE (NJS_VMCODE_LESS): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); if (njs_slow_path(!njs_is_primitive(value1))) { ret = njs_value_to_primitive(vm, &primitive1, value1, 0); if (ret != NJS_OK) { goto error; } value1 = &primitive1; } if (njs_slow_path(!njs_is_primitive(value2))) { ret = njs_value_to_primitive(vm, &primitive2, value2, 0); if (ret != NJS_OK) { goto error; } value2 = &primitive2; } if (njs_slow_path(njs_is_symbol(value1) || njs_is_symbol(value2))) { njs_symbol_conversion_failed(vm, 0); goto error; } njs_vmcode_operand(vm, vmcode->operand1, retval); njs_set_boolean(retval, njs_primitive_values_compare(vm, value1, value2) > 0); pc += sizeof(njs_vmcode_3addr_t); NEXT; CASE (NJS_VMCODE_GREATER): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); if (njs_slow_path(!njs_is_primitive(value1))) { ret = njs_value_to_primitive(vm, &primitive1, value1, 0); if (ret != NJS_OK) { goto error; } value1 = &primitive1; } if (njs_slow_path(!njs_is_primitive(value2))) { ret = njs_value_to_primitive(vm, &primitive2, value2, 0); if (ret != NJS_OK) { goto error; } value2 = &primitive2; } if (njs_slow_path(njs_is_symbol(value1) || njs_is_symbol(value2))) { njs_symbol_conversion_failed(vm, 0); goto error; } njs_vmcode_operand(vm, vmcode->operand1, retval); njs_set_boolean(retval, njs_primitive_values_compare(vm, value2, value1) > 0); pc += sizeof(njs_vmcode_3addr_t); NEXT; CASE (NJS_VMCODE_LESS_OR_EQUAL): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); if (njs_slow_path(!njs_is_primitive(value1))) { ret = njs_value_to_primitive(vm, &primitive1, value1, 0); if (ret != NJS_OK) { goto error; } value1 = &primitive1; } if (njs_slow_path(!njs_is_primitive(value2))) { ret = njs_value_to_primitive(vm, &primitive2, value2, 0); if (ret != NJS_OK) { goto error; } value2 = &primitive2; } if (njs_slow_path(njs_is_symbol(value1) || njs_is_symbol(value2))) { njs_symbol_conversion_failed(vm, 0); goto error; } njs_vmcode_operand(vm, vmcode->operand1, retval); njs_set_boolean(retval, njs_primitive_values_compare(vm, value2, value1) == 0); pc += sizeof(njs_vmcode_3addr_t); NEXT; CASE (NJS_VMCODE_GREATER_OR_EQUAL): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); if (njs_slow_path(!njs_is_primitive(value1))) { ret = njs_value_to_primitive(vm, &primitive1, value1, 0); if (ret != NJS_OK) { goto error; } value1 = &primitive1; } if (njs_slow_path(!njs_is_primitive(value2))) { ret = njs_value_to_primitive(vm, &primitive2, value2, 0); if (ret != NJS_OK) { goto error; } value2 = &primitive2; } if (njs_slow_path(njs_is_symbol(value1) || njs_is_symbol(value2))) { njs_symbol_conversion_failed(vm, 0); goto error; } njs_vmcode_operand(vm, vmcode->operand1, retval); njs_set_boolean(retval, njs_primitive_values_compare(vm, value1, value2) == 0); pc += sizeof(njs_vmcode_3addr_t); NEXT; CASE (NJS_VMCODE_ADDITION): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); if (njs_slow_path(!njs_is_primitive(value1))) { hint = njs_is_date(value1); ret = njs_value_to_primitive(vm, &primitive1, value1, hint); if (ret != NJS_OK) { goto error; } value1 = &primitive1; } if (njs_slow_path(!njs_is_primitive(value2))) { hint = njs_is_date(value2); ret = njs_value_to_primitive(vm, &primitive2, value2, hint); if (ret != NJS_OK) { goto error; } value2 = &primitive2; } if (njs_slow_path(njs_is_symbol(value1) || njs_is_symbol(value2))) { njs_symbol_conversion_failed(vm, (njs_is_string(value1) || njs_is_string(value2))); goto error; } njs_vmcode_operand(vm, vmcode->operand1, retval); if (njs_fast_path(njs_is_numeric(value1) && njs_is_numeric(value2))) { njs_set_number(retval, njs_number(value1) + njs_number(value2)); pc += sizeof(njs_vmcode_3addr_t); NEXT; } if (njs_is_string(value1)) { s1 = value1; s2 = &dst; src = value2; } else { s1 = &dst; s2 = value2; src = value1; } ret = njs_primitive_value_to_string(vm, &dst, src); if (njs_slow_path(ret != NJS_OK)) { goto error; } ret = njs_string_concat(vm, s1, s2, &name); if (njs_slow_path(ret == NJS_ERROR)) { goto error; } njs_value_assign(retval, &name); BREAK; CASE (NJS_VMCODE_EQUAL): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); ret = njs_values_equal(vm, value1, value2); if (njs_slow_path(ret < 0)) { goto error; } njs_vmcode_operand(vm, vmcode->operand1, retval); njs_set_boolean(retval, ret); pc += sizeof(njs_vmcode_3addr_t); NEXT; CASE (NJS_VMCODE_NOT_EQUAL): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); ret = njs_values_equal(vm, value1, value2); if (njs_slow_path(ret < 0)) { goto error; } njs_vmcode_operand(vm, vmcode->operand1, retval); njs_set_boolean(retval, !ret); pc += sizeof(njs_vmcode_3addr_t); NEXT; #define NJS_PRE_NUMERIC \ \ if (njs_slow_path(!njs_is_numeric(value1))) { \ ret = njs_value_to_numeric(vm, value1, &numeric1); \ if (njs_slow_path(ret != NJS_OK)) { \ goto error; \ } \ \ value1 = &numeric1; \ } \ \ if (njs_slow_path(!njs_is_numeric(value2))) { \ ret = njs_value_to_numeric(vm, value2, &numeric2); \ if (njs_slow_path(ret != NJS_OK)) { \ goto error; \ } \ \ value2 = &numeric2; \ } \ \ num = njs_number(value1); \ \ njs_vmcode_operand(vm, vmcode->operand1, retval); \ pc += sizeof(njs_vmcode_3addr_t) CASE (NJS_VMCODE_SUBTRACTION): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); NJS_PRE_NUMERIC; num -= njs_number(value2); njs_set_number(retval, num); NEXT; CASE (NJS_VMCODE_MULTIPLICATION): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); NJS_PRE_NUMERIC; num *= njs_number(value2); njs_set_number(retval, num); NEXT; CASE (NJS_VMCODE_EXPONENTIATION): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); NJS_PRE_NUMERIC; exponent = njs_number(value2); /* * According to ES7: * 1. If exponent is NaN, the result should be NaN; * 2. The result of +/-1 ** +/-Infinity should be NaN. */ valid = njs_expect(1, fabs(num) != 1 || (!isnan(exponent) && !isinf(exponent))); num = valid ? pow(num, exponent) : NAN; njs_set_number(retval, num); NEXT; CASE (NJS_VMCODE_DIVISION): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); NJS_PRE_NUMERIC; num /= njs_number(value2); njs_set_number(retval, num); NEXT; CASE (NJS_VMCODE_REMAINDER): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); NJS_PRE_NUMERIC; num = fmod(num, njs_number(value2)); njs_set_number(retval, num); NEXT; CASE (NJS_VMCODE_BITWISE_AND): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); NJS_PRE_NUMERIC; i32 = njs_number_to_int32(njs_number(value2)); i32 &= njs_number_to_int32(num); njs_set_int32(retval, i32); NEXT; CASE (NJS_VMCODE_BITWISE_OR): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); NJS_PRE_NUMERIC; i32 = njs_number_to_int32(njs_number(value2)); i32 |= njs_number_to_int32(num); njs_set_int32(retval, i32); NEXT; CASE (NJS_VMCODE_BITWISE_XOR): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); NJS_PRE_NUMERIC; i32 = njs_number_to_int32(njs_number(value2)); i32 ^= njs_number_to_int32(num); njs_set_int32(retval, i32); NEXT; CASE (NJS_VMCODE_LEFT_SHIFT): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); NJS_PRE_NUMERIC; u32 = njs_number_to_uint32(njs_number(value2)) & 0x1f; i32 = njs_number_to_int32(num); /* Shifting of negative numbers is undefined. */ i32 = (uint32_t) i32 << u32; njs_set_int32(retval, i32); NEXT; CASE (NJS_VMCODE_RIGHT_SHIFT): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); NJS_PRE_NUMERIC; u32 = njs_number_to_uint32(njs_number(value2)) & 0x1f; i32 = njs_number_to_int32(num); i32 >>= u32; njs_set_int32(retval, i32); NEXT; CASE (NJS_VMCODE_UNSIGNED_RIGHT_SHIFT): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); NJS_PRE_NUMERIC; u32 = njs_number_to_uint32(njs_number(value2)) & 0x1f; njs_set_uint32(retval, njs_number_to_uint32(num) >> u32); NEXT; CASE (NJS_VMCODE_TEMPLATE_LITERAL): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand1, retval); ret = njs_vmcode_template_literal(vm, retval); if (njs_slow_path(ret < 0 && ret >= NJS_PREEMPT)) { goto error; } BREAK; CASE (NJS_VMCODE_PROPERTY_IN): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); njs_vmcode_operand(vm, vmcode->operand1, retval); ret = njs_vmcode_property_in(vm, value1, value2, retval); if (njs_slow_path(ret < 0 && ret >= NJS_PREEMPT)) { goto error; } BREAK; CASE (NJS_VMCODE_PROPERTY_DELETE): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); if (njs_slow_path(!njs_is_index_or_key(value2))) { if (njs_slow_path(njs_is_null_or_undefined(value1))) { (void) njs_throw_cannot_property(vm, value1, value2, "delete"); goto error; } ret = njs_value_to_key(vm, &primitive1, value2); if (njs_slow_path(ret != NJS_OK)) { goto error; } value2 = &primitive1; } ret = njs_value_property_delete(vm, value1, value2, NULL, 1); if (njs_slow_path(ret == NJS_ERROR)) { goto error; } njs_vmcode_operand(vm, vmcode->operand1, retval); njs_value_assign(retval, &njs_value_true); ret = sizeof(njs_vmcode_3addr_t); BREAK; CASE (NJS_VMCODE_PROPERTY_FOREACH): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand2, value1); njs_vmcode_operand(vm, vmcode->operand1, retval); ret = njs_vmcode_property_foreach(vm, value1, pc, retval); if (njs_slow_path(ret < 0 && ret >= NJS_PREEMPT)) { goto error; } BREAK; CASE (NJS_VMCODE_STRICT_EQUAL): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); ret = njs_values_strict_equal(value1, value2); njs_vmcode_operand(vm, vmcode->operand1, retval); njs_set_boolean(retval, ret); pc += sizeof(njs_vmcode_3addr_t); NEXT; CASE (NJS_VMCODE_STRICT_NOT_EQUAL): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); ret = njs_values_strict_equal(value1, value2); njs_vmcode_operand(vm, vmcode->operand1, retval); njs_set_boolean(retval, !ret); pc += sizeof(njs_vmcode_3addr_t); NEXT; CASE (NJS_VMCODE_TEST_IF_TRUE): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand2, value1); ret = njs_is_true(value1); if (ret) { test_jump = (njs_vmcode_test_jump_t *) pc; ret = test_jump->offset; } else { ret = sizeof(njs_vmcode_3addr_t); } njs_vmcode_operand(vm, vmcode->operand1, retval); *retval = *value1; BREAK; CASE (NJS_VMCODE_TEST_IF_FALSE): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand2, value1); ret = !njs_is_true(value1); if (ret) { test_jump = (njs_vmcode_test_jump_t *) pc; ret = test_jump->offset; } else { ret = sizeof(njs_vmcode_3addr_t); } njs_vmcode_operand(vm, vmcode->operand1, retval); *retval = *value1; BREAK; CASE (NJS_VMCODE_COALESCE): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand2, value1); ret = !njs_is_null_or_undefined(value1); if (ret) { test_jump = (njs_vmcode_test_jump_t *) pc; ret = test_jump->offset; } else { ret = sizeof(njs_vmcode_3addr_t); } njs_vmcode_operand(vm, vmcode->operand1, retval); *retval = *value1; BREAK; #define NJS_PRE_UNARY \ if (njs_slow_path(!njs_is_numeric(value1))) { \ ret = njs_value_to_numeric(vm, value1, &numeric1); \ if (njs_slow_path(ret != NJS_OK)) { \ goto error; \ } \ \ value1 = &numeric1; \ } \ \ num = njs_number(value1); \ njs_vmcode_operand(vm, vmcode->operand1, retval); CASE (NJS_VMCODE_UNARY_NEGATION): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand2, value1); NJS_PRE_UNARY; num = -num; njs_set_number(retval, num); pc += sizeof(njs_vmcode_2addr_t); NEXT; CASE (NJS_VMCODE_UNARY_PLUS): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand2, value1); NJS_PRE_UNARY; njs_set_number(retval, num); pc += sizeof(njs_vmcode_2addr_t); NEXT; CASE (NJS_VMCODE_BITWISE_NOT): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand2, value1); NJS_PRE_UNARY; njs_set_int32(retval, ~njs_number_to_uint32(num)); pc += sizeof(njs_vmcode_2addr_t); NEXT; CASE (NJS_VMCODE_LOGICAL_NOT): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand2, value1); njs_vmcode_operand(vm, vmcode->operand1, retval); njs_set_boolean(retval, !njs_is_true(value1)); pc += sizeof(njs_vmcode_2addr_t); NEXT; CASE (NJS_VMCODE_OBJECT): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand1, retval); ret = njs_vmcode_object(vm, retval); if (njs_slow_path(ret < 0 && ret >= NJS_PREEMPT)) { goto error; } BREAK; CASE (NJS_VMCODE_ARRAY): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand1, retval); ret = njs_vmcode_array(vm, pc, retval); if (njs_slow_path(ret < 0 && ret >= NJS_PREEMPT)) { goto error; } BREAK; CASE (NJS_VMCODE_FUNCTION): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand1, retval); ret = njs_vmcode_function(vm, pc, retval); if (njs_slow_path(ret < 0 && ret >= NJS_PREEMPT)) { goto error; } BREAK; CASE (NJS_VMCODE_REGEXP): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand1, retval); ret = njs_vmcode_regexp(vm, pc, retval); if (njs_slow_path(ret < 0 && ret >= NJS_PREEMPT)) { goto error; } BREAK; CASE (NJS_VMCODE_INSTANCE_OF): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); njs_vmcode_operand(vm, vmcode->operand1, retval); ret = njs_vmcode_instance_of(vm, value1, value2, retval); if (njs_slow_path(ret < 0 && ret >= NJS_PREEMPT)) { goto error; } BREAK; CASE (NJS_VMCODE_TYPEOF): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand2, value1); njs_vmcode_operand(vm, vmcode->operand1, retval); ret = njs_vmcode_typeof(vm, value1, retval); if (njs_slow_path(ret < 0 && ret >= NJS_PREEMPT)) { goto error; } BREAK; CASE (NJS_VMCODE_VOID): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand1, retval); njs_set_undefined(retval); ret = sizeof(njs_vmcode_2addr_t); BREAK; CASE (NJS_VMCODE_DELETE): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand2, value1); njs_vmcode_operand(vm, vmcode->operand1, retval); njs_set_boolean(retval, 1); ret = sizeof(njs_vmcode_2addr_t); BREAK; CASE (NJS_VMCODE_DEBUGGER): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand1, retval); ret = njs_vmcode_debugger(vm, retval); BREAK; CASE (NJS_VMCODE_PUT_ARG): njs_vmcode_debug_opcode(); put_arg = (njs_vmcode_1addr_t *) pc; native = vm->top_frame; value1 = &native->arguments[native->put_args++]; njs_vmcode_operand(vm, put_arg->index, value2); njs_value_assign(value1, value2); ret = sizeof(njs_vmcode_1addr_t); BREAK; CASE (NJS_VMCODE_STOP): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand1, value2); *rval = *value2; njs_vmcode_debug(vm, pc, "EXIT STOP"); return NJS_OK; CASE (NJS_VMCODE_JUMP): njs_vmcode_debug_opcode(); ret = (njs_jump_off_t) vmcode->operand1; BREAK; CASE (NJS_VMCODE_PROPERTY_SET): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); njs_vmcode_operand(vm, vmcode->operand1, retval); if (njs_slow_path(!njs_is_index_or_key(value2))) { if (njs_slow_path(njs_is_null_or_undefined(value1))) { (void) njs_throw_cannot_property(vm, value1, value2, "set"); goto error; } njs_value_assign(&primitive1, value1); ret = njs_value_to_key(vm, &primitive2, value2); if (njs_slow_path(ret != NJS_OK)) { goto error; } value1 = &primitive1; value2 = &primitive2; } ret = njs_value_property_set(vm, value1, value2, retval); if (njs_slow_path(ret == NJS_ERROR)) { goto error; } ret = sizeof(njs_vmcode_prop_set_t); BREAK; CASE (NJS_VMCODE_PROPERTY_ACCESSOR): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); accessor = (njs_vmcode_prop_accessor_t *) pc; njs_vmcode_operand(vm, accessor->value, function); ret = njs_value_to_key(vm, &name, value2); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "failed conversion of type \"%s\" " "to string while property define", njs_type_string(value2->type)); goto error; } ret = njs_object_prop_define(vm, value1, &name, function, accessor->type, 0); if (njs_slow_path(ret != NJS_OK)) { goto error; } ret = sizeof(njs_vmcode_prop_accessor_t); BREAK; CASE (NJS_VMCODE_IF_TRUE_JUMP): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand2, value1); value2 = (njs_value_t *) vmcode->operand1; ret = njs_is_true(value1); ret = ret ? (njs_jump_off_t) value2 : (njs_jump_off_t) sizeof(njs_vmcode_cond_jump_t); BREAK; CASE (NJS_VMCODE_IF_FALSE_JUMP): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand2, value1); value2 = (njs_value_t *) vmcode->operand1; ret = njs_is_true(value1); ret = !ret ? (njs_jump_off_t) value2 : (njs_jump_off_t) sizeof(njs_vmcode_cond_jump_t); BREAK; CASE (NJS_VMCODE_IF_EQUAL_JUMP): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); if (njs_values_strict_equal(value1, value2)) { equal = (njs_vmcode_equal_jump_t *) pc; ret = equal->offset; } else { ret = sizeof(njs_vmcode_3addr_t); } BREAK; CASE (NJS_VMCODE_PROPERTY_INIT): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); set = (njs_vmcode_prop_set_t *) pc; njs_vmcode_operand(vm, set->value, retval); ret = njs_vmcode_property_init(vm, value1, value2, retval); if (njs_slow_path(ret == NJS_ERROR)) { goto error; } BREAK; CASE (NJS_VMCODE_RETURN): njs_vmcode_debug_opcode(); value2 = (njs_value_t *) vmcode->operand1; njs_vmcode_operand(vm, (njs_index_t) value2, value2); njs_vmcode_debug(vm, pc, "EXIT RETURN"); njs_vmcode_return(vm, rval, value2); return NJS_OK; CASE (NJS_VMCODE_FUNCTION_COPY): njs_vmcode_debug_opcode(); fcopy = (njs_vmcode_function_copy_t *) pc; ret = njs_vmcode_function_copy(vm, fcopy->function, fcopy->retval); if (njs_slow_path(ret == NJS_ERROR)) { goto error; } BREAK; CASE (NJS_VMCODE_FUNCTION_FRAME): njs_vmcode_debug_opcode(); value2 = (njs_value_t *) vmcode->operand1; njs_vmcode_operand(vm, vmcode->operand2, value1); function_frame = (njs_vmcode_function_frame_t *) pc; ret = njs_function_frame_create(vm, value1, &njs_value_undefined, (uintptr_t) value2, function_frame->ctor); if (njs_slow_path(ret != NJS_OK)) { goto error; } ret = sizeof(njs_vmcode_function_frame_t); BREAK; CASE (NJS_VMCODE_METHOD_FRAME): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); method_frame = (njs_vmcode_method_frame_t *) pc; if (njs_slow_path(!njs_is_key(value2))) { if (njs_slow_path(njs_is_null_or_undefined(value1))) { (void) njs_throw_cannot_property(vm, value1, value2, "get"); goto error; } ret = njs_value_to_key(vm, &primitive1, value2); if (njs_slow_path(ret != NJS_OK)) { goto error; } value2 = &primitive1; } ret = njs_value_property(vm, value1, value2, &dst); if (njs_slow_path(ret == NJS_ERROR)) { goto error; } if (njs_slow_path(!njs_is_function(&dst))) { ret = njs_value_to_key(vm, &dst, value2); if (njs_slow_path(ret != NJS_OK)) { goto error; } njs_key_string_get(vm, &dst, &string); njs_type_error(vm, "(intermediate value)[\"%V\"] is not a function", &string); goto error; } ret = njs_function_frame_create(vm, &dst, value1, method_frame->nargs, method_frame->ctor); if (njs_slow_path(ret != NJS_OK)) { goto error; } ret = sizeof(njs_vmcode_method_frame_t); BREAK; CASE (NJS_VMCODE_FUNCTION_CALL): njs_vmcode_debug_opcode(); value2 = (njs_value_t *) vmcode->operand1; vm->active_frame->native.pc = pc; njs_vmcode_operand(vm, (njs_index_t) value2, value2); ret = njs_function_frame_invoke(vm, value2); if (njs_slow_path(ret == NJS_ERROR)) { goto error; } njs_vmcode_debug(vm, pc, "RESUME"); ret = sizeof(njs_vmcode_function_call_t); BREAK; CASE (NJS_VMCODE_PROPERTY_NEXT): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); pnext = (njs_vmcode_prop_next_t *) pc; retval = njs_scope_value(vm, pnext->retval); njs_assert(njs_is_data(value2, NJS_DATA_TAG_FOREACH_NEXT)); next = njs_data(value2); if (next->index < next->array->length) { *retval = next->array->start[next->index++]; ret = pnext->offset; BREAK; } njs_mp_free(vm->mem_pool, next); ret = sizeof(njs_vmcode_prop_next_t); BREAK; CASE (NJS_VMCODE_ARGUMENTS): njs_vmcode_debug_opcode(); ret = njs_vmcode_arguments(vm, pc); if (njs_slow_path(ret == NJS_ERROR)) { goto error; } BREAK; CASE (NJS_VMCODE_TO_PROPERTY_KEY): njs_vmcode_debug_opcode(); value2 = (njs_value_t *) vmcode->operand1; njs_vmcode_operand(vm, vmcode->operand2, value1); njs_vmcode_operand(vm, (njs_index_t) value2, retval); ret = njs_value_to_key(vm, retval, value1); if (njs_fast_path(ret == NJS_ERROR)) { goto error; } ret = sizeof(njs_vmcode_2addr_t); BREAK; CASE (NJS_VMCODE_TO_PROPERTY_KEY_CHK): njs_vmcode_debug_opcode(); value2 = (njs_value_t *) vmcode->operand1; njs_vmcode_operand(vm, vmcode->operand2, value1); njs_vmcode_operand(vm, (njs_index_t) value2, retval); njs_vmcode_operand(vm, vmcode->operand3, value2); if (njs_slow_path(njs_is_null_or_undefined(value2))) { (void) njs_throw_cannot_property(vm, value2, value1, "get"); goto error; } ret = njs_value_to_key(vm, retval, value1); if (njs_fast_path(ret == NJS_ERROR)) { goto error; } ret = sizeof(njs_vmcode_3addr_t); BREAK; CASE (NJS_VMCODE_SET_FUNCTION_NAME): njs_vmcode_debug_opcode(); value2 = (njs_value_t *) vmcode->operand1; njs_vmcode_operand(vm, vmcode->operand2, value1); njs_vmcode_operand(vm, (njs_index_t) value2, value2); njs_assert(njs_is_function(value2)); ret = njs_function_name_set(vm, njs_function(value2), value1, NULL); if (njs_slow_path(ret == NJS_ERROR)) { goto error; } ret = sizeof(njs_vmcode_2addr_t); BREAK; CASE (NJS_VMCODE_PROTO_INIT): njs_vmcode_debug_opcode(); njs_vmcode_operand(vm, vmcode->operand3, value2); njs_vmcode_operand(vm, vmcode->operand2, value1); set = (njs_vmcode_prop_set_t *) pc; njs_vmcode_operand(vm, set->value, retval); ret = njs_vmcode_proto_init(vm, value1, value2, retval); if (njs_slow_path(ret == NJS_ERROR)) { goto error; } BREAK; CASE (NJS_VMCODE_IMPORT): njs_vmcode_debug_opcode(); import = (njs_vmcode_import_t *) pc; retval = njs_scope_value(vm, import->retval); ret = njs_vmcode_import(vm, import->module, retval); if (njs_slow_path(ret == NJS_ERROR)) { goto error; } BREAK; CASE (NJS_VMCODE_AWAIT): njs_vmcode_debug_opcode(); await = (njs_vmcode_await_t *) pc; ret = njs_vmcode_await(vm, await, rval, promise_cap, async_ctx); njs_vmcode_debug(vm, pc, "EXIT AWAIT"); if (njs_slow_path(ret == NJS_ERROR)) { goto error; } return ret; CASE (NJS_VMCODE_TRY_START): njs_vmcode_debug_opcode(); value2 = (njs_value_t *) vmcode->operand1; njs_vmcode_operand(vm, vmcode->operand2, value1); ret = njs_vmcode_try_start(vm, value1, value2, pc); if (njs_slow_path(ret == NJS_ERROR)) { goto error; } BREAK; CASE (NJS_VMCODE_THROW): njs_vmcode_debug_opcode(); value2 = (njs_value_t *) vmcode->operand1; njs_vmcode_operand(vm, (njs_index_t) value2, value2); njs_vm_throw(vm, value2); goto error; CASE (NJS_VMCODE_TRY_BREAK): njs_vmcode_debug_opcode(); value2 = (njs_value_t *) vmcode->operand1; try_trampoline = (njs_vmcode_try_trampoline_t *) pc; value1 = njs_scope_value(vm, try_trampoline->exit_value); ret = njs_vmcode_try_break(vm, value1, value2); BREAK; CASE (NJS_VMCODE_TRY_CONTINUE): njs_vmcode_debug_opcode(); value2 = (njs_value_t *) vmcode->operand1; try_trampoline = (njs_vmcode_try_trampoline_t *) pc; value1 = njs_scope_value(vm, try_trampoline->exit_value); ret = njs_vmcode_try_continue(vm, value1, value2); BREAK; CASE (NJS_VMCODE_TRY_END): njs_vmcode_debug_opcode(); ret = njs_vmcode_try_end(vm, NULL, (njs_value_t *) vmcode->operand1); BREAK; /* * 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. */ CASE (NJS_VMCODE_CATCH): njs_vmcode_debug_opcode(); value2 = (njs_value_t *) vmcode->operand1; njs_vmcode_operand(vm, vmcode->operand2, value1); *value1 = njs_vm_exception(vm); if ((njs_jump_off_t) value2 == sizeof(njs_vmcode_catch_t)) { ret = njs_vmcode_try_end(vm, value1, value2); } else { frame = (njs_frame_t *) vm->top_frame; frame->exception.catch = pc + (njs_jump_off_t) value2; ret = sizeof(njs_vmcode_catch_t); } BREAK; CASE (NJS_VMCODE_FINALLY): njs_vmcode_debug_opcode(); value2 = (njs_value_t *) vmcode->operand1; ret = njs_vmcode_finally(vm, rval, value2, pc); switch (ret) { case NJS_OK: njs_vmcode_debug(vm, pc, "EXIT FINALLY"); return NJS_OK; case NJS_ERROR: goto error; } BREAK; CASE (NJS_VMCODE_LET): njs_vmcode_debug_opcode(); var = (njs_vmcode_variable_t *) pc; value1 = njs_scope_value(vm, var->dst); if (njs_is_valid(value1)) { value1 = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t)); if (njs_slow_path(value1 == NULL)) { njs_memory_error(vm); goto error; } njs_scope_value_set(vm, var->dst, value1); } njs_set_undefined(value1); ret = sizeof(njs_vmcode_variable_t); BREAK; CASE (NJS_VMCODE_LET_UPDATE): njs_vmcode_debug_opcode(); var = (njs_vmcode_variable_t *) pc; value2 = njs_scope_value(vm, var->dst); value1 = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t)); if (njs_slow_path(value1 == NULL)) { njs_memory_error(vm); goto error; } *value1 = *value2; njs_scope_value_set(vm, var->dst, value1); ret = sizeof(njs_vmcode_variable_t); BREAK; CASE (NJS_VMCODE_INITIALIZATION_TEST): njs_vmcode_debug_opcode(); var = (njs_vmcode_variable_t *) pc; value1 = njs_scope_value(vm, var->dst); if (njs_is_valid(value1)) { ret = sizeof(njs_vmcode_variable_t); BREAK; } FALLTHROUGH; CASE (NJS_VMCODE_NOT_INITIALIZED): njs_vmcode_debug_opcode(); njs_reference_error(vm, "cannot access variable before initialization"); goto error; CASE (NJS_VMCODE_ERROR): njs_vmcode_debug_opcode(); njs_vmcode_error(vm, pc); goto error; CASE (NJS_VMCODE_ASSIGNMENT_ERROR): njs_vmcode_debug_opcode(); njs_type_error(vm, "assignment to constant variable"); goto error; } error: if (njs_is_error(&vm->exception)) { vm->active_frame->native.pc = pc; (void) njs_error_stack_attach(vm, vm->exception); } for ( ;; ) { native = vm->top_frame; if (!native->native) { frame = (njs_frame_t *) native; catch = frame->exception.catch; if (catch != NULL) { pc = catch; NEXT; } } previous = native->previous; if (previous == NULL) { break; } lambda_call = (native == &vm->active_frame->native); njs_vm_scopes_restore(vm, native); if (native->size != 0) { vm->spare_stack_size += native->size; njs_mp_free(vm->mem_pool, native); } if (lambda_call) { break; } } njs_vmcode_debug(vm, pc, "EXIT ERROR"); return NJS_ERROR; } static njs_jump_off_t njs_vmcode_object(njs_vm_t *vm, njs_value_t *retval) { njs_object_t *object; object = njs_object_alloc(vm); if (njs_fast_path(object != NULL)) { njs_set_object(retval, object); return sizeof(njs_vmcode_object_t); } return NJS_ERROR; } static njs_jump_off_t njs_vmcode_array(njs_vm_t *vm, u_char *pc, njs_value_t *retval) { uint32_t length; njs_array_t *array; njs_value_t *value; njs_vmcode_array_t *code; code = (njs_vmcode_array_t *) pc; array = njs_array_alloc(vm, 0, code->length, NJS_ARRAY_SPARE); if (njs_fast_path(array != NULL)) { if (code->ctor) { /* Array of the form [,,,], [1,,]. */ if (array->object.fast_array) { value = array->start; length = array->length; do { njs_set_invalid(value); value++; length--; } while (length != 0); } } else { /* Array of the form [], [,,1], [1,2,3]. */ array->length = 0; } njs_set_array(retval, array); return sizeof(njs_vmcode_array_t); } return NJS_ERROR; } static njs_jump_off_t njs_vmcode_function(njs_vm_t *vm, u_char *pc, njs_value_t *retval) { njs_function_t *function; njs_vmcode_function_t *code; njs_function_lambda_t *lambda; code = (njs_vmcode_function_t *) pc; lambda = code->lambda; function = njs_function_alloc(vm, lambda, code->async); if (njs_slow_path(function == NULL)) { return NJS_ERROR; } if (njs_function_capture_closure(vm, function, lambda) != NJS_OK) { return NJS_ERROR; } function->args_count = lambda->nargs - lambda->rest_parameters; njs_set_function(retval, function); return sizeof(njs_vmcode_function_t); } static njs_jump_off_t njs_vmcode_arguments(njs_vm_t *vm, u_char *pc) { njs_frame_t *frame; njs_value_t *value; njs_jump_off_t ret; njs_vmcode_arguments_t *code; frame = (njs_frame_t *) vm->active_frame; if (frame->native.arguments_object == NULL) { ret = njs_function_arguments_object_init(vm, &frame->native); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } code = (njs_vmcode_arguments_t *) pc; value = njs_scope_valid_value(vm, code->dst); if (njs_slow_path(value == NULL)) { return NJS_ERROR; } njs_set_object(value, frame->native.arguments_object); return sizeof(njs_vmcode_arguments_t); } static njs_jump_off_t njs_vmcode_regexp(njs_vm_t *vm, u_char *pc, njs_value_t *retval) { njs_regexp_t *regexp; njs_vmcode_regexp_t *code; code = (njs_vmcode_regexp_t *) pc; regexp = njs_regexp_alloc(vm, code->pattern); if (njs_fast_path(regexp != NULL)) { njs_set_regexp(retval, regexp); return sizeof(njs_vmcode_regexp_t); } return NJS_ERROR; } static njs_jump_off_t njs_vmcode_template_literal(njs_vm_t *vm, njs_value_t *retval) { njs_array_t *array; njs_jump_off_t ret; static const njs_function_t concat = { .native = 1, .u.native = njs_string_prototype_concat }; njs_assert(njs_is_array(retval)); array = njs_array(retval); ret = njs_function_call(vm, (njs_function_t *) &concat, &njs_string_empty, array->start, array->length, retval); if (njs_slow_path(ret != NJS_OK)) { return ret; } return sizeof(njs_vmcode_template_literal_t); } static njs_jump_off_t njs_vmcode_function_copy(njs_vm_t *vm, njs_value_t *value, njs_index_t retidx) { njs_value_t *retval; njs_function_t *function; retval = njs_scope_value(vm, retidx); if (!njs_is_valid(retval)) { *retval = *value; function = njs_function_value_copy(vm, retval); if (njs_slow_path(function == NULL)) { return NJS_ERROR; } } return sizeof(njs_vmcode_function_copy_t); } static njs_jump_off_t njs_vmcode_property_init(njs_vm_t *vm, njs_value_t *value, njs_value_t *key, njs_value_t *init) { double num; uint32_t index, size; njs_int_t ret; njs_array_t *array; njs_value_t *val, name; njs_object_prop_t *prop; njs_lvlhsh_query_t lhq; switch (value->type) { case NJS_ARRAY: num = njs_key_to_index(key); if (njs_slow_path(!njs_key_is_integer_index(num, key))) { njs_internal_error(vm, "invalid index while property initialization"); return NJS_ERROR; } index = (uint32_t) num; array = value->data.u.array; if (index >= array->length) { size = index - array->length; ret = njs_array_expand(vm, array, 0, size + 1); if (njs_slow_path(ret != NJS_OK)) { return ret; } val = &array->start[array->length]; while (size != 0) { njs_set_invalid(val); val++; size--; } array->length = index + 1; } array->start[index] = *init; break; case NJS_OBJECT: ret = njs_value_to_key(vm, &name, key); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_object_property_key_set(&lhq, &name, 0); lhq.proto = &njs_object_hash_proto; lhq.pool = vm->mem_pool; prop = njs_object_prop_alloc(vm, &name, init, 1); if (njs_slow_path(prop == NULL)) { return NJS_ERROR; } lhq.value = prop; lhq.replace = 1; ret = njs_lvlhsh_insert(njs_object_hash(value), &lhq); if (njs_slow_path(ret != NJS_OK)) { njs_internal_error(vm, "lvlhsh insert/replace failed"); return NJS_ERROR; } break; default: njs_internal_error(vm, "unexpected object type \"%s\" " "while property initialization", njs_type_string(value->type)); return NJS_ERROR; } return sizeof(njs_vmcode_prop_set_t); } static njs_jump_off_t njs_vmcode_proto_init(njs_vm_t *vm, njs_value_t *value, njs_value_t *unused, njs_value_t *init) { njs_object_t *obj; njs_value_t retval; njs_jump_off_t ret; njs_object_prop_t *prop; njs_lvlhsh_query_t lhq; lhq.key = njs_str_value("__proto__"); lhq.key_hash = NJS___PROTO___HASH; lhq.proto = &njs_object_hash_proto; lhq.pool = vm->mem_pool; obj = njs_object(value); ret = njs_lvlhsh_find(&obj->__proto__->shared_hash, &lhq); if (njs_slow_path(ret != NJS_OK)) { goto fail; } prop = lhq.value; if (prop->type != NJS_PROPERTY_HANDLER) { goto fail; } ret = njs_prop_handler(prop)(vm, prop, value, init, &retval); if (njs_slow_path(ret != NJS_OK)) { goto fail; } return sizeof(njs_vmcode_prop_set_t); fail: njs_internal_error(vm, "\"__proto__\" init failed"); return NJS_ERROR; } static njs_jump_off_t njs_vmcode_property_in(njs_vm_t *vm, njs_value_t *value, njs_value_t *key, njs_value_t *retval) { njs_int_t ret; njs_value_t primitive; njs_property_query_t pq; if (njs_slow_path(njs_is_primitive(value))) { njs_type_error(vm, "property \"in\" on primitive %s type", njs_type_string(value->type)); return NJS_ERROR; } if (njs_slow_path(!njs_is_index_or_key(key))) { ret = njs_value_to_key(vm, &primitive, key); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } key = &primitive; } njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0, 0); ret = njs_property_query(vm, &pq, value, key); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } njs_set_boolean(retval, ret == NJS_OK); return sizeof(njs_vmcode_3addr_t); } static njs_jump_off_t njs_vmcode_property_foreach(njs_vm_t *vm, njs_value_t *object, u_char *pc, njs_value_t *retval) { njs_property_next_t *next; njs_vmcode_prop_foreach_t *code; next = njs_mp_alloc(vm->mem_pool, sizeof(njs_property_next_t)); if (njs_slow_path(next == NULL)) { njs_memory_error(vm); return NJS_ERROR; } next->index = 0; next->array = njs_value_enumerate(vm, object, NJS_ENUM_KEYS | NJS_ENUM_STRING | NJS_ENUM_ENUMERABLE_ONLY); if (njs_slow_path(next->array == NULL)) { njs_memory_error(vm); return NJS_ERROR; } njs_set_data(retval, next, NJS_DATA_TAG_FOREACH_NEXT); code = (njs_vmcode_prop_foreach_t *) pc; return code->offset; } static njs_jump_off_t njs_vmcode_instance_of(njs_vm_t *vm, njs_value_t *object, njs_value_t *constructor, njs_value_t *retval) { njs_value_t value, bound; njs_object_t *prototype, *proto; njs_function_t *function; njs_jump_off_t ret; static const njs_value_t prototype_string = njs_string("prototype"); if (!njs_is_function(constructor)) { njs_type_error(vm, "right argument is not callable"); return NJS_ERROR; } function = njs_function(constructor); if (function->bound != NULL) { function = function->context; njs_set_function(&bound, function); constructor = &bound; } if (njs_is_object(object)) { ret = njs_value_property(vm, constructor, njs_value_arg(&prototype_string), &value); if (njs_slow_path(ret == NJS_ERROR)) { return ret; } if (njs_fast_path(ret == NJS_OK)) { if (njs_slow_path(!njs_is_object(&value))) { njs_type_error(vm, "Function has non-object prototype " "in instanceof"); return NJS_ERROR; } prototype = njs_object(&value); proto = njs_object(object); do { proto = proto->__proto__; if (proto == prototype) { njs_value_assign(retval, &njs_value_true); return sizeof(njs_vmcode_instance_of_t); } } while (proto != NULL); } } njs_value_assign(retval, &njs_value_false); return sizeof(njs_vmcode_instance_of_t); } static njs_jump_off_t njs_vmcode_typeof(njs_vm_t *vm, njs_value_t *value, njs_value_t *retval) { /* ECMAScript 5.1: null, array and regexp are objects. */ static const njs_value_t *types[NJS_VALUE_TYPE_MAX] = { &njs_string_object, &njs_string_undefined, &njs_string_boolean, &njs_string_number, &njs_string_symbol, &njs_string_string, &njs_string_data, &njs_string_external, &njs_string_invalid, &njs_string_undefined, &njs_string_undefined, &njs_string_undefined, &njs_string_undefined, &njs_string_undefined, &njs_string_undefined, &njs_string_undefined, &njs_string_object, &njs_string_object, &njs_string_function, &njs_string_object, &njs_string_object, &njs_string_object, &njs_string_object, &njs_string_object, &njs_string_object, &njs_string_object, }; njs_value_assign(retval, types[value->type]); return sizeof(njs_vmcode_2addr_t); } static njs_jump_off_t njs_vmcode_debugger(njs_vm_t *vm, njs_value_t *retval) { /* * HOW TO DEBUG JS CODE: * 1) put debugger instruction when certain condition is met * in the JS code: * function test() { * if (<>) debugger; * } * 2) set a breakpoint to njs_vmcode_debugger() in gdb. * * To see the values of certain indices: * 1) use njs -d test.js to dump the byte code * 2) find an appropriate index around DEBUGGER instruction * 3) in gdb: p *njs_scope_value_get(vm, ) **/ njs_set_undefined(retval); return sizeof(njs_vmcode_debugger_t); } static njs_jump_off_t njs_string_concat(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2, njs_value_t *retval) { u_char *start; size_t size, length; njs_string_prop_t string1, string2; (void) njs_string_prop(&string1, val1); (void) njs_string_prop(&string2, val2); length = string1.length + string2.length; size = string1.size + string2.size; start = njs_string_alloc(vm, retval, size, length); if (njs_slow_path(start == NULL)) { return NJS_ERROR; } (void) memcpy(start, string1.start, string1.size); (void) memcpy(start + string1.size, string2.start, string2.size); return sizeof(njs_vmcode_3addr_t); } static njs_jump_off_t njs_values_equal(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2) { njs_int_t ret; njs_bool_t nv1, nv2; njs_value_t primitive; njs_value_t *hv, *lv; again: nv1 = njs_is_null_or_undefined(val1); nv2 = njs_is_null_or_undefined(val2); /* Void and null are equal and not comparable with anything else. */ if (nv1 || nv2) { return (nv1 && nv2); } if (njs_is_numeric(val1) && njs_is_numeric(val2)) { /* NaNs and Infinities are handled correctly by comparision. */ return (njs_number(val1) == njs_number(val2)); } if (val1->type == val2->type) { if (njs_is_string(val1)) { return njs_string_eq(val1, val2); } if (njs_is_symbol(val1)) { return njs_symbol_eq(val1, val2); } return (njs_object(val1) == njs_object(val2)); } /* Sort values as: numeric < symbol < string < objects. */ if (val1->type > val2->type) { hv = val1; lv = val2; } else { hv = val2; lv = val1; } /* If "lv" is an object then "hv" can only be another object. */ if (njs_is_object(lv)) { return 0; } /* If "hv" is a symbol then "lv" can only be a numeric. */ if (njs_is_symbol(hv)) { return 0; } /* If "hv" is a string then "lv" can be a numeric or symbol. */ if (njs_is_string(hv)) { return !njs_is_symbol(lv) && (njs_number(lv) == njs_string_to_number(hv)); } /* "hv" is an object and "lv" is either a string or a symbol or a numeric. */ ret = njs_value_to_primitive(vm, &primitive, hv, 0); if (ret != NJS_OK) { return ret; } val1 = &primitive; val2 = lv; goto again; } /* * ECMAScript 5.1: 11.8.5 * njs_primitive_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. */ static njs_jump_off_t njs_primitive_values_compare(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2) { double num1, num2; if (njs_fast_path(njs_is_numeric(val1))) { num1 = njs_number(val1); if (njs_fast_path(njs_is_numeric(val2))) { num2 = njs_number(val2); } else { num2 = njs_string_to_number(val2); } } else if (njs_is_numeric(val2)) { num1 = njs_string_to_number(val1); num2 = njs_number(val2); } else { return (njs_string_cmp(val1, val2) < 0) ? 1 : 0; } /* NaN and void values are not comparable with anything. */ if (isnan(num1) || isnan(num2)) { return -1; } /* Infinities are handled correctly by comparision. */ return (num1 < num2); } static njs_jump_off_t njs_function_frame_create(njs_vm_t *vm, njs_value_t *value, const njs_value_t *this, uintptr_t nargs, njs_bool_t ctor) { njs_int_t ret; njs_value_t new_target, *args; njs_object_t *object; njs_function_t *function, *target; if (njs_fast_path(njs_is_function(value))) { function = njs_function(value); target = function; args = NULL; if (ctor) { if (function->bound != NULL) { target = function->context; nargs += function->bound_args; args = njs_mp_alloc(vm->mem_pool, nargs * sizeof(njs_value_t)); if (njs_slow_path(args == NULL)) { njs_memory_error(vm); return NJS_ERROR; } memcpy(args, &function->bound[1], function->bound_args * sizeof(njs_value_t)); } if (!target->ctor) { njs_type_error(vm, "%s is not a constructor", njs_type_string(value->type)); return NJS_ERROR; } if (!target->native) { object = njs_function_new_object(vm, value); if (njs_slow_path(object == NULL)) { return NJS_ERROR; } njs_set_object(&new_target, object); this = &new_target; } } ret = njs_function_frame(vm, target, this, args, nargs, ctor); if (args != NULL) { vm->top_frame->put_args = function->bound_args; njs_mp_free(vm->mem_pool, args); } return ret; } njs_type_error(vm, "%s is not a function", njs_type_string(value->type)); return NJS_ERROR; } inline njs_object_t * njs_function_new_object(njs_vm_t *vm, njs_value_t *constructor) { njs_value_t proto, bound; njs_object_t *object; njs_function_t *function; njs_jump_off_t ret; const njs_value_t prototype_string = njs_string("prototype"); object = njs_object_alloc(vm); if (njs_slow_path(object == NULL)) { return NULL; } njs_assert(njs_is_function(constructor)); function = njs_function(constructor); if (function->bound != NULL) { function = function->context; njs_set_function(&bound, function); constructor = &bound; } ret = njs_value_property(vm, constructor, njs_value_arg(&prototype_string), &proto); if (njs_slow_path(ret == NJS_ERROR)) { return NULL; } if (njs_fast_path(njs_is_object(&proto))) { object->__proto__ = njs_object(&proto); } return object; } static void njs_vmcode_return(njs_vm_t *vm, njs_value_t *dst, njs_value_t *retval) { njs_frame_t *frame; frame = (njs_frame_t *) vm->top_frame; if (frame->native.ctor) { if (!njs_is_object(retval)) { retval = frame->native.local[0]; } } njs_vm_scopes_restore(vm, &frame->native); *dst = *retval; njs_function_frame_free(vm, &frame->native); } static njs_jump_off_t njs_vmcode_import(njs_vm_t *vm, njs_mod_t *module, njs_value_t *retval) { njs_int_t ret; njs_arr_t *m; njs_value_t *value; njs_object_t *object; if (vm->modules == NULL) { vm->modules = njs_arr_create(vm->mem_pool, 4, sizeof(njs_value_t)); if (njs_slow_path(vm->modules == NULL)) { njs_memory_error(vm); return NJS_ERROR; } m = vm->modules; value = njs_arr_add_multiple(m, vm->shared->module_items); if (njs_slow_path(value == NULL)) { njs_memory_error(vm); return NJS_ERROR; } njs_memzero(m->start, m->items * sizeof(njs_value_t)); } njs_assert(module->index < vm->modules->items); value = njs_arr_item(vm->modules, module->index); if (!njs_is_null(value)) { njs_value_assign(retval, value); return sizeof(njs_vmcode_import_t); } if (module->function.native) { njs_value_assign(value, &module->value); object = njs_object_value_copy(vm, value); if (njs_slow_path(object == NULL)) { return NJS_ERROR; } } else { njs_set_invalid(value); ret = njs_vm_invoke(vm, &module->function, NULL, 0, value); if (ret == NJS_ERROR) { return ret; } } njs_value_assign(retval, value); return sizeof(njs_vmcode_import_t); } static njs_int_t njs_vmcode_await(njs_vm_t *vm, njs_vmcode_await_t *await, njs_value_t *dst, njs_promise_capability_t *pcap, njs_async_ctx_t *ctx) { size_t size; njs_int_t ret; njs_frame_t *frame; njs_value_t ctor, val, on_fulfilled, on_rejected, *value, retval; njs_function_t *fulfilled, *rejected; njs_native_frame_t *active; active = &vm->active_frame->native; value = njs_scope_valid_value(vm, await->retval); if (njs_slow_path(value == NULL)) { njs_internal_error(vm, "await->retval is invalid"); return NJS_ERROR; } njs_set_function(&ctor, &njs_vm_ctor(vm, NJS_OBJ_TYPE_PROMISE)); ret = njs_promise_resolve(vm, &ctor, value, &val); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } if (ctx == NULL) { ctx = njs_mp_alloc(vm->mem_pool, sizeof(njs_async_ctx_t)); if (njs_slow_path(ctx == NULL)) { njs_memory_error(vm); return NJS_ERROR; } size = njs_function_frame_size(active); fulfilled = njs_promise_create_function(vm, size); if (njs_slow_path(fulfilled == NULL)) { return NJS_ERROR; } ctx->await = fulfilled->context; ctx->capability = pcap; ret = njs_function_frame_save(vm, ctx->await, NULL); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } } else { fulfilled = njs_promise_create_function(vm, 0); if (njs_slow_path(fulfilled == NULL)) { return NJS_ERROR; } } ctx->pc = (u_char *) await + sizeof(njs_vmcode_await_t); ctx->index = await->retval; frame = (njs_frame_t *) active; if (frame->exception.catch != NULL) { ctx->await->native.pc = frame->exception.catch; } else { ctx->await->native.pc = ctx->pc; } fulfilled->context = ctx; fulfilled->args_count = 1; fulfilled->u.native = njs_await_fulfilled; rejected = njs_promise_create_function(vm, 0); if (njs_slow_path(rejected == NULL)) { return NJS_ERROR; } rejected->context = ctx; rejected->args_count = 1; rejected->u.native = njs_await_rejected; njs_set_function(&on_fulfilled, fulfilled); njs_set_function(&on_rejected, rejected); ret = njs_promise_perform_then(vm, &val, &on_fulfilled, &on_rejected, NULL, &retval); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_vmcode_return(vm, dst, &retval); return NJS_AGAIN; } /* * 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. */ static njs_jump_off_t njs_vmcode_try_start(njs_vm_t *vm, njs_value_t *exception_value, njs_value_t *offset, u_char *pc) { njs_value_t *exit_value; njs_frame_t *frame; njs_exception_t *e; njs_vmcode_try_start_t *try_start; frame = (njs_frame_t *) vm->top_frame; if (frame->exception.catch != NULL) { e = njs_mp_alloc(vm->mem_pool, sizeof(njs_exception_t)); if (njs_slow_path(e == NULL)) { njs_memory_error(vm); return NJS_ERROR; } *e = frame->exception; frame->exception.next = e; } frame->exception.catch = pc + (njs_jump_off_t) offset; njs_set_invalid(exception_value); try_start = (njs_vmcode_try_start_t *) pc; exit_value = njs_scope_value(vm, try_start->exit_value); njs_set_invalid(exit_value); njs_number(exit_value) = 0; return sizeof(njs_vmcode_try_start_t); } /* * njs_vmcode_try_break() sets exit_value to INVALID 1, and jumps to * the nearest try_end block. The exit_value is checked by njs_vmcode_finally(). */ static njs_jump_off_t njs_vmcode_try_break(njs_vm_t *vm, njs_value_t *exit_value, njs_value_t *offset) { /* exit_value can contain valid value set by vmcode_try_return. */ if (!njs_is_valid(exit_value)) { njs_number(exit_value) = 1; } return (njs_jump_off_t) offset; } /* * njs_vmcode_try_continue() sets exit_value to INVALID -1, and jumps to * the nearest try_end block. The exit_value is checked by njs_vmcode_finally(). */ static njs_jump_off_t njs_vmcode_try_continue(njs_vm_t *vm, njs_value_t *exit_value, njs_value_t *offset) { njs_number(exit_value) = -1; return (njs_jump_off_t) offset; } /* * 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. */ static njs_jump_off_t njs_vmcode_try_end(njs_vm_t *vm, njs_value_t *invld, njs_value_t *offset) { njs_frame_t *frame; njs_exception_t *e; frame = (njs_frame_t *) vm->top_frame; e = frame->exception.next; if (e == NULL) { frame->exception.catch = NULL; } else { frame->exception = *e; njs_mp_free(vm->mem_pool, e); } return (njs_jump_off_t) offset; } /* * njs_vmcode_finally() is set on the end of a "finally" or a "catch" block. * 1) to throw uncaught exception. * 2) to make a jump to an enslosing loop exit if "continue" or "break" * statement was used inside try block. * 3) to finalize "return" instruction from "try" block. */ static njs_jump_off_t njs_vmcode_finally(njs_vm_t *vm, njs_value_t *dst, njs_value_t *retval, u_char *pc) { njs_value_t *exception_value, *exit_value; njs_jump_off_t offset; njs_vmcode_finally_t *finally; exception_value = njs_scope_value(vm, (njs_index_t) retval); if (njs_is_valid(exception_value)) { njs_vm_throw(vm, exception_value); return NJS_ERROR; } finally = (njs_vmcode_finally_t *) pc; exit_value = njs_scope_value(vm, finally->exit_value); /* * exit_value is set by: * vmcode_try_start to INVALID 0 * vmcode_try_break to INVALID 1 * vmcode_try_continue to INVALID -1 * vmcode_try_return to a valid return value */ if (njs_is_valid(exit_value)) { njs_vmcode_return(vm, dst, exit_value); return NJS_OK; } else if (njs_number(exit_value) != 0) { offset = (njs_number(exit_value) > 0) ? finally->break_offset : finally->continue_offset; if (njs_slow_path(offset < (njs_jump_off_t) sizeof(njs_vmcode_finally_t))) { njs_internal_error(vm, "unset %s offset for FINALLY block", (njs_number(exit_value) > 0) ? "exit" : "continuaion"); return NJS_ERROR; } return offset; } return sizeof(njs_vmcode_finally_t); } static void njs_vmcode_error(njs_vm_t *vm, u_char *pc) { njs_vmcode_error_t *err; err = (njs_vmcode_error_t *) pc; if (err->type == NJS_OBJ_TYPE_REF_ERROR) { njs_reference_error(vm, "\"%V\" is not defined", &err->u.name); } else { njs_throw_error(vm, err->type, "%V", &err->u.message); } } static njs_int_t njs_throw_cannot_property(njs_vm_t *vm, njs_value_t *object, njs_value_t *key, const char *what) { njs_int_t ret; njs_str_t string; njs_value_t dst; ret = njs_value_to_key2(vm, &dst, key, 0); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } njs_key_string_get(vm, &dst, &string); njs_type_error(vm, "cannot %s property \"%V\" of %s", what, &string, njs_is_null(object) ? "null" : "undefined"); return NJS_OK; } njs-0.8.9/src/njs_vmcode.h000066400000000000000000000260451474132077100154260ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_VMCODE_H_INCLUDED_ #define _NJS_VMCODE_H_INCLUDED_ /* * Negative return values handled by nJSVM interpreter as special events. * The values must be in range from -1 to -11, because -12 is minimal jump * offset on 32-bit platforms. * 0 (NJS_OK) : njs_vmcode_stop() has stopped execution, * execution successfully finished * -1 (NJS_ERROR): error or exception; * -2 .. -11: not used. */ /* The last return value which preempts execution. */ #define NJS_PREEMPT (-11) typedef intptr_t njs_jump_off_t; typedef uint8_t njs_vmcode_t; enum { NJS_VMCODE_PUT_ARG = 0, NJS_VMCODE_STOP, NJS_VMCODE_JUMP, NJS_VMCODE_PROPERTY_SET, NJS_VMCODE_PROPERTY_ACCESSOR, NJS_VMCODE_IF_TRUE_JUMP, NJS_VMCODE_IF_FALSE_JUMP, NJS_VMCODE_IF_EQUAL_JUMP, NJS_VMCODE_PROPERTY_INIT, NJS_VMCODE_RETURN, NJS_VMCODE_FUNCTION_COPY, NJS_VMCODE_FUNCTION_FRAME, NJS_VMCODE_METHOD_FRAME, NJS_VMCODE_FUNCTION_CALL, NJS_VMCODE_PROPERTY_NEXT, NJS_VMCODE_ARGUMENTS, NJS_VMCODE_PROTO_INIT, NJS_VMCODE_TO_PROPERTY_KEY, NJS_VMCODE_TO_PROPERTY_KEY_CHK, NJS_VMCODE_SET_FUNCTION_NAME, NJS_VMCODE_IMPORT, NJS_VMCODE_AWAIT, NJS_VMCODE_TRY_START, NJS_VMCODE_THROW, NJS_VMCODE_TRY_BREAK, NJS_VMCODE_TRY_CONTINUE, NJS_VMCODE_TRY_END, NJS_VMCODE_CATCH, NJS_VMCODE_FINALLY, NJS_VMCODE_LET, NJS_VMCODE_LET_UPDATE, NJS_VMCODE_INITIALIZATION_TEST, NJS_VMCODE_NOT_INITIALIZED, NJS_VMCODE_ASSIGNMENT_ERROR, NJS_VMCODE_ERROR, NJS_VMCODE_MOVE, NJS_VMCODE_PROPERTY_GET, NJS_VMCODE_INCREMENT, NJS_VMCODE_POST_INCREMENT, NJS_VMCODE_DECREMENT, NJS_VMCODE_POST_DECREMENT, NJS_VMCODE_TRY_RETURN, NJS_VMCODE_GLOBAL_GET, NJS_VMCODE_LESS, NJS_VMCODE_GREATER, NJS_VMCODE_LESS_OR_EQUAL, NJS_VMCODE_GREATER_OR_EQUAL, NJS_VMCODE_ADDITION, NJS_VMCODE_EQUAL, NJS_VMCODE_NOT_EQUAL, NJS_VMCODE_SUBTRACTION, NJS_VMCODE_MULTIPLICATION, NJS_VMCODE_EXPONENTIATION, NJS_VMCODE_DIVISION, NJS_VMCODE_REMAINDER, NJS_VMCODE_BITWISE_AND, NJS_VMCODE_BITWISE_OR, NJS_VMCODE_BITWISE_XOR, NJS_VMCODE_LEFT_SHIFT, NJS_VMCODE_RIGHT_SHIFT, NJS_VMCODE_UNSIGNED_RIGHT_SHIFT, NJS_VMCODE_TEMPLATE_LITERAL, NJS_VMCODE_PROPERTY_IN, NJS_VMCODE_PROPERTY_DELETE, NJS_VMCODE_PROPERTY_FOREACH, NJS_VMCODE_STRICT_EQUAL, NJS_VMCODE_STRICT_NOT_EQUAL, NJS_VMCODE_TEST_IF_TRUE, NJS_VMCODE_TEST_IF_FALSE, NJS_VMCODE_COALESCE, NJS_VMCODE_UNARY_PLUS, NJS_VMCODE_UNARY_NEGATION, NJS_VMCODE_BITWISE_NOT, NJS_VMCODE_LOGICAL_NOT, NJS_VMCODE_OBJECT, NJS_VMCODE_ARRAY, NJS_VMCODE_FUNCTION, NJS_VMCODE_REGEXP, NJS_VMCODE_INSTANCE_OF, NJS_VMCODE_TYPEOF, NJS_VMCODE_VOID, NJS_VMCODE_DELETE, NJS_VMCODE_DEBUGGER, NJS_VMCODES }; 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 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 dst; } njs_vmcode_this_t; typedef struct { njs_vmcode_t code; njs_index_t dst; } njs_vmcode_arguments_t; typedef struct { njs_vmcode_t code; njs_index_t retval; uintptr_t length; uint8_t ctor; /* 1 bit */ } njs_vmcode_array_t; typedef struct { njs_vmcode_t code; njs_index_t retval; } njs_vmcode_template_literal_t; typedef struct { njs_vmcode_t code; njs_index_t retval; njs_function_lambda_t *lambda; njs_bool_t async; } njs_vmcode_function_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_index_t retval; njs_index_t object; } njs_vmcode_object_copy_t; typedef struct { njs_vmcode_t code; njs_jump_off_t offset; } njs_vmcode_jump_t; typedef struct { njs_vmcode_t code; njs_jump_off_t offset; njs_index_t cond; } njs_vmcode_cond_jump_t; typedef struct { njs_vmcode_t code; njs_jump_off_t offset; njs_index_t value1; njs_index_t value2; } njs_vmcode_equal_jump_t; typedef struct { njs_vmcode_t code; njs_index_t retval; njs_index_t value; njs_jump_off_t offset; } njs_vmcode_test_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 value; njs_index_t object; njs_index_t property; uint8_t type; } njs_vmcode_prop_accessor_t; typedef struct { njs_vmcode_t code; njs_index_t next; njs_index_t object; njs_jump_off_t offset; } njs_vmcode_prop_foreach_t; typedef struct { njs_vmcode_t code; njs_index_t retval; njs_index_t object; njs_index_t next; njs_jump_off_t offset; } njs_vmcode_prop_next_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 nargs; njs_index_t name; uint8_t ctor; /* 1 bit */ } njs_vmcode_function_frame_t; typedef struct { njs_vmcode_t code; njs_index_t nargs; njs_index_t object; njs_index_t method; uint8_t ctor; /* 1 bit */ } njs_vmcode_method_frame_t; typedef struct { njs_vmcode_t code; njs_index_t retval; } njs_vmcode_function_call_t; typedef struct { njs_vmcode_t code; njs_index_t retval; } njs_vmcode_return_t; typedef struct { njs_vmcode_t code; njs_index_t retval; } njs_vmcode_stop_t; typedef struct { njs_vmcode_t code; njs_jump_off_t offset; njs_index_t exception_value; njs_index_t exit_value; } njs_vmcode_try_start_t; typedef struct { njs_vmcode_t code; njs_jump_off_t offset; njs_index_t exit_value; } njs_vmcode_try_trampoline_t; typedef struct { njs_vmcode_t code; njs_jump_off_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_jump_off_t offset; } njs_vmcode_try_end_t; typedef struct { njs_vmcode_t code; njs_index_t save; njs_index_t retval; njs_jump_off_t offset; } njs_vmcode_try_return_t; typedef struct { njs_vmcode_t code; njs_index_t retval; njs_index_t exit_value; njs_jump_off_t continue_offset; njs_jump_off_t break_offset; } njs_vmcode_finally_t; typedef struct { njs_vmcode_t code; njs_object_type_t type; union { njs_str_t name; njs_str_t message; } u; } njs_vmcode_error_t; typedef struct { njs_vmcode_t code; njs_value_t *function; njs_index_t retval; } njs_vmcode_function_copy_t; typedef struct { njs_vmcode_t code; njs_index_t retval; njs_mod_t *module; } njs_vmcode_import_t; typedef struct { njs_vmcode_t code; njs_index_t dst; } njs_vmcode_variable_t; typedef struct { njs_vmcode_t code; njs_index_t retval; } njs_vmcode_debugger_t; typedef struct { njs_vmcode_t code; njs_index_t retval; } njs_vmcode_await_t; njs_int_t njs_vmcode_interpreter(njs_vm_t *vm, u_char *pc, njs_value_t *retval, void *promise_cap, void *async_ctx); njs_object_t *njs_function_new_object(njs_vm_t *vm, njs_value_t *constructor); #ifdef NJS_DEBUG_OPCODE #define njs_vmcode_debug(vm, pc, prefix) { \ if (vm->options.opcode_debug) do { \ njs_vm_code_t *code; \ \ code = njs_lookup_code(vm, pc); \ \ njs_printf("%s %V\n", prefix, \ (code != NULL) ? &code->name : &njs_entry_unknown); \ } while (0); \ } #define njs_vmcode_debug_opcode() \ if (vm->options.opcode_debug) { \ njs_disassemble(pc, NULL, 1, NULL); \ } #else #define njs_vmcode_debug(vm, pc, prefix) #define njs_vmcode_debug_opcode() #endif #endif /* _NJS_VMCODE_H_INCLUDED_ */ njs-0.8.9/src/qjs.c000066400000000000000000000271341474132077100140670ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) F5, Inc. */ #include #include /* NJS_VERSION */ #include #include #include #include typedef struct { njs_str_t name; int value; } qjs_signal_entry_t; extern char **environ; static JSValue qjs_njs_getter(JSContext *ctx, JSValueConst this_val); static JSValue qjs_njs_to_string_tag(JSContext *ctx, JSValueConst this_val); static JSValue qjs_process_to_string_tag(JSContext *ctx, JSValueConst this_val); static JSValue qjs_process_argv(JSContext *ctx, JSValueConst this_val); static JSValue qjs_process_env(JSContext *ctx, JSValueConst this_val); static JSValue qjs_process_kill(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue qjs_process_pid(JSContext *ctx, JSValueConst this_val); static JSValue qjs_process_ppid(JSContext *ctx, JSValueConst this_val); /* P1990 signals from `man 7 signal` are supported */ static qjs_signal_entry_t qjs_signals_table[] = { { njs_str("ABRT"), SIGABRT }, { njs_str("ALRM"), SIGALRM }, { njs_str("CHLD"), SIGCHLD }, { njs_str("CONT"), SIGCONT }, { njs_str("FPE"), SIGFPE }, { njs_str("HUP"), SIGHUP }, { njs_str("ILL"), SIGILL }, { njs_str("INT"), SIGINT }, { njs_str("KILL"), SIGKILL }, { njs_str("PIPE"), SIGPIPE }, { njs_str("QUIT"), SIGQUIT }, { njs_str("SEGV"), SIGSEGV }, { njs_str("STOP"), SIGSTOP }, { njs_str("TSTP"), SIGTSTP }, { njs_str("TERM"), SIGTERM }, { njs_str("TTIN"), SIGTTIN }, { njs_str("TTOU"), SIGTTOU }, { njs_str("USR1"), SIGUSR1 }, { njs_str("USR2"), SIGUSR2 }, { njs_null_str, 0 } }; static const JSCFunctionListEntry qjs_global_proto[] = { JS_CGETSET_DEF("njs", qjs_njs_getter, NULL), }; static const JSCFunctionListEntry qjs_njs_proto[] = { JS_CGETSET_DEF("[Symbol.toStringTag]", qjs_njs_to_string_tag, NULL), JS_PROP_STRING_DEF("version", NJS_VERSION, JS_PROP_C_W_E), JS_PROP_INT32_DEF("version_number", NJS_VERSION_NUMBER, JS_PROP_C_W_E), JS_PROP_STRING_DEF("engine", "QuickJS", JS_PROP_C_W_E), }; static const JSCFunctionListEntry qjs_process_proto[] = { JS_CGETSET_DEF("[Symbol.toStringTag]", qjs_process_to_string_tag, NULL), JS_CGETSET_DEF("argv", qjs_process_argv, NULL), JS_CGETSET_DEF("env", qjs_process_env, NULL), JS_CFUNC_DEF("kill", 2, qjs_process_kill), JS_CGETSET_DEF("pid", qjs_process_pid, NULL), JS_CGETSET_DEF("ppid", qjs_process_ppid, NULL), }; JSContext * qjs_new_context(JSRuntime *rt, qjs_module_t **addons) { int ret; JSAtom prop; JSValue global_obj; JSContext *ctx; qjs_module_t **module; ctx = JS_NewContextRaw(rt); if (ctx == NULL) { return NULL; } JS_AddIntrinsicBaseObjects(ctx); JS_AddIntrinsicDate(ctx); JS_AddIntrinsicRegExp(ctx); JS_AddIntrinsicJSON(ctx); JS_AddIntrinsicProxy(ctx); JS_AddIntrinsicMapSet(ctx); JS_AddIntrinsicTypedArrays(ctx); JS_AddIntrinsicPromise(ctx); JS_AddIntrinsicBigInt(ctx); JS_AddIntrinsicEval(ctx); for (module = qjs_modules; *module != NULL; module++) { if ((*module)->init(ctx, (*module)->name) == NULL) { return NULL; } } if (addons != NULL) { for (module = addons; *module != NULL; module++) { if ((*module)->init(ctx, (*module)->name) == NULL) { return NULL; } } } global_obj = JS_GetGlobalObject(ctx); JS_SetPropertyFunctionList(ctx, global_obj, qjs_global_proto, njs_nitems(qjs_global_proto)); prop = JS_NewAtom(ctx, "eval"); if (prop == JS_ATOM_NULL) { return NULL; } ret = JS_DeleteProperty(ctx, global_obj, prop, 0); JS_FreeAtom(ctx, prop); if (ret < 0) { return NULL; } prop = JS_NewAtom(ctx, "Function"); if (prop == JS_ATOM_NULL) { return NULL; } ret = JS_DeleteProperty(ctx, global_obj, prop, 0); JS_FreeAtom(ctx, prop); if (ret < 0) { return NULL; } JS_FreeValue(ctx, global_obj); return ctx; } static JSValue qjs_njs_getter(JSContext *ctx, JSValueConst this_val) { JSValue obj; obj = JS_NewObject(ctx); if (JS_IsException(obj)) { return JS_EXCEPTION; } JS_SetPropertyFunctionList(ctx, obj, qjs_njs_proto, njs_nitems(qjs_njs_proto)); return obj; } static JSValue qjs_njs_to_string_tag(JSContext *ctx, JSValueConst this_val) { return JS_NewString(ctx, "njs"); } static JSValue qjs_process_to_string_tag(JSContext *ctx, JSValueConst this_val) { return JS_NewString(ctx, "process"); } static JSValue qjs_process_argv(JSContext *ctx, JSValueConst this_val) { int i, ret, argc; JSValue val, str; const char **argv; val = JS_GetPropertyStr(ctx, this_val, "argc"); if (JS_IsException(val)) { return JS_EXCEPTION; } if (JS_ToInt32(ctx, &argc, val) < 0) { return JS_EXCEPTION; } argv = JS_GetOpaque(this_val, JS_GetClassID(this_val)); if (argv == NULL) { return JS_NewArray(ctx); } val = JS_NewArray(ctx); if (JS_IsException(val)) { return JS_EXCEPTION; } for (i = 0; i < argc; i++) { str = JS_NewStringLen(ctx, argv[i], njs_strlen(argv[i])); if (JS_IsException(str)) { JS_FreeValue(ctx, val); return JS_EXCEPTION; } ret = JS_DefinePropertyValueUint32(ctx, val, i, str, JS_PROP_C_W_E); if (ret < 0) { JS_FreeValue(ctx, str); JS_FreeValue(ctx, val); return JS_EXCEPTION; } } return val; } static JSValue qjs_process_env(JSContext *ctx, JSValueConst this_val) { int ret; char **ep; JSValue obj; JSAtom atom; JSValue str, name; const char *entry, *value; obj = JS_NewObject(ctx); if (JS_IsException(obj)) { return JS_EXCEPTION; } ep = environ; while (*ep != NULL) { entry = *ep++; value = (const char *) njs_strchr(entry, '='); if (value == NULL) { continue; } str = JS_UNDEFINED; name = JS_NewStringLen(ctx, entry, value - entry); if (JS_IsException(name)) { goto error; } value++; str = JS_NewStringLen(ctx, value, njs_strlen(value)); if (JS_IsException(str)) { goto error; } atom = JS_ValueToAtom(ctx, name); if (atom == JS_ATOM_NULL) { goto error; } ret = JS_DefinePropertyValue(ctx, obj, atom, str, JS_PROP_C_W_E); JS_FreeAtom(ctx, atom); if (ret < 0) { error: JS_FreeValue(ctx, name); JS_FreeValue(ctx, str); return JS_EXCEPTION; } JS_FreeValue(ctx, name); } return obj; } static JSValue qjs_process_kill(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { int signo, pid; JSValue val; njs_str_t name; const char *signal; qjs_signal_entry_t *entry; if (JS_ToInt32(ctx, &pid, argv[0]) < 0) { return JS_EXCEPTION; } signo = SIGTERM; if (JS_IsNumber(argv[1])) { if (JS_ToInt32(ctx, &signo, argv[1]) < 0) { return JS_EXCEPTION; } if (signo < 0 || signo >= NSIG) { return JS_ThrowTypeError(ctx, "unknown signal: %d", signo); } } else { val = JS_ToString(ctx, argv[1]); if (JS_IsException(val)) { return JS_EXCEPTION; } signal = JS_ToCString(ctx, val); if (signal == NULL) { JS_FreeValue(ctx, val); return JS_EXCEPTION; } if (njs_strlen(signal) < 3 || memcmp(signal, "SIG", 3) != 0) { JS_FreeCString(ctx, signal); return JS_ThrowTypeError(ctx, "unknown signal: %s", signal); } name.start = (u_char *) signal + 3; name.length = njs_strlen(signal) - 3; for (entry = qjs_signals_table; entry->name.length != 0; entry++) { if (njs_strstr_eq(&entry->name, &name)) { signo = entry->value; break; } } JS_FreeCString(ctx, signal); if (entry->name.length == 0) { return JS_ThrowTypeError(ctx, "unknown signal: %s", signal); } } if (kill(pid, signo) < 0) { return JS_ThrowTypeError(ctx, "kill failed with (%d:%s)", errno, strerror(errno)); } return JS_TRUE; } static JSValue qjs_process_pid(JSContext *ctx, JSValueConst this_val) { return JS_NewInt32(ctx, getpid()); } static JSValue qjs_process_ppid(JSContext *ctx, JSValueConst this_val) { return JS_NewInt32(ctx, getppid()); } JSValue qjs_process_object(JSContext *ctx, int argc, const char **argv) { JSValue obj; obj = JS_NewObject(ctx); if (JS_IsException(obj)) { return JS_EXCEPTION; } JS_SetPropertyFunctionList(ctx, obj, qjs_process_proto, njs_nitems(qjs_process_proto)); JS_SetOpaque(obj, argv); if (JS_SetPropertyStr(ctx, obj, "argc", JS_NewInt32(ctx, argc)) < 0) { JS_FreeValue(ctx, obj); return JS_EXCEPTION; } return obj; } int qjs_to_bytes(JSContext *ctx, qjs_bytes_t *bytes, JSValueConst value) { size_t byte_offset, byte_length; JSValue val; if (JS_IsString(value)) { goto string; } val = JS_GetTypedArrayBuffer(ctx, value, &byte_offset, &byte_length, NULL); if (!JS_IsException(val)) { bytes->start = JS_GetArrayBuffer(ctx, &bytes->length, val); JS_FreeValue(ctx, val); if (bytes->start != NULL) { bytes->tag = JS_TAG_OBJECT; bytes->start += byte_offset; bytes->length = byte_length; return 0; } } bytes->start = JS_GetArrayBuffer(ctx, &bytes->length, value); if (bytes->start != NULL) { bytes->tag = JS_TAG_OBJECT; return 0; } if (!JS_IsString(value)) { val = JS_ToString(ctx, value); bytes->start = (u_char *) JS_ToCStringLen(ctx, &bytes->length, val); JS_FreeValue(ctx, val); if (bytes->start == NULL) { return -1; } } string: bytes->tag = JS_TAG_STRING; bytes->start = (u_char *) JS_ToCStringLen(ctx, &bytes->length, value); return (bytes->start != NULL) ? 0 : -1; } void qjs_bytes_free(JSContext *ctx, qjs_bytes_t *bytes) { if (bytes->tag == JS_TAG_STRING) { JS_FreeCString(ctx, (char *) bytes->start); } } JSValue qjs_typed_array_data(JSContext *ctx, JSValueConst value, njs_str_t *data) { size_t byte_offset, byte_length; value = JS_GetTypedArrayBuffer(ctx, value, &byte_offset, &byte_length, NULL); if (JS_IsException(value)) { return value; } data->start = JS_GetArrayBuffer(ctx, &data->length, value); JS_FreeValue(ctx, value); if (data->start == NULL) { return JS_EXCEPTION; } data->start += byte_offset; data->length = byte_length; return JS_UNDEFINED; } JSValue qjs_string_create_chb(JSContext *cx, njs_chb_t *chain) { JSValue val; njs_int_t ret; njs_str_t str; ret = njs_chb_join(chain, &str); if (ret != NJS_OK) { return JS_ThrowInternalError(cx, "failed to create string"); } val = JS_NewStringLen(cx, (const char *) str.start, str.length); chain->free(cx, str.start); return val; } njs-0.8.9/src/qjs.h000066400000000000000000000056461474132077100141000ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) F5, Inc. */ #ifndef _QJS_H_INCLUDED_ #define _QJS_H_INCLUDED_ #include #include #include #include #include #include #include #include #include #include #if defined(__GNUC__) && (__GNUC__ >= 8) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-function-type" #endif #include #if defined(__GNUC__) && (__GNUC__ >= 8) #pragma GCC diagnostic pop #endif #define NJS_QUICKJS_VERSION "Unknown version" #include #define QJS_CORE_CLASS_ID_OFFSET 64 #define QJS_CORE_CLASS_ID_BUFFER (QJS_CORE_CLASS_ID_OFFSET) #define QJS_CORE_CLASS_ID_UINT8_ARRAY_CTOR (QJS_CORE_CLASS_ID_OFFSET + 1) #define QJS_CORE_CLASS_ID_FS_STATS (QJS_CORE_CLASS_ID_OFFSET + 2) #define QJS_CORE_CLASS_ID_FS_DIRENT (QJS_CORE_CLASS_ID_OFFSET + 3) #define QJS_CORE_CLASS_ID_FS_FILEHANDLE (QJS_CORE_CLASS_ID_OFFSET + 4) #define QJS_CORE_CLASS_ID_LAST (QJS_CORE_CLASS_ID_OFFSET + 5) typedef JSModuleDef *(*qjs_addon_init_pt)(JSContext *ctx, const char *name); typedef struct { const char *name; qjs_addon_init_pt init; } qjs_module_t; JSContext *qjs_new_context(JSRuntime *rt, qjs_module_t **addons); JSValue qjs_buffer_alloc(JSContext *ctx, size_t size); JSValue qjs_buffer_create(JSContext *ctx, u_char *start, size_t size); JSValue qjs_buffer_chb_alloc(JSContext *ctx, njs_chb_t *chain); typedef int (*qjs_buffer_encode_t)(JSContext *ctx, const njs_str_t *src, njs_str_t *dst); typedef size_t (*qjs_buffer_encode_length_t)(JSContext *ctx, const njs_str_t *src); typedef struct { njs_str_t name; qjs_buffer_encode_t encode; qjs_buffer_encode_length_t encode_length; qjs_buffer_encode_t decode; qjs_buffer_encode_length_t decode_length; } qjs_buffer_encoding_t; const qjs_buffer_encoding_t *qjs_buffer_encoding(JSContext *ctx, JSValueConst value, JS_BOOL thrw); JSValue qjs_process_object(JSContext *ctx, int argc, const char **argv); typedef struct { int tag; size_t length; u_char *start; } qjs_bytes_t; int qjs_to_bytes(JSContext *ctx, qjs_bytes_t *data, JSValueConst value); void qjs_bytes_free(JSContext *ctx, qjs_bytes_t *data); JSValue qjs_typed_array_data(JSContext *ctx, JSValueConst value, njs_str_t *data); #define qjs_string_create(ctx, data, len) \ JS_NewStringLen(ctx, (const char *) (data), len) JSValue qjs_string_create_chb(JSContext *cx, njs_chb_t *chain); static inline JS_BOOL JS_IsNullOrUndefined(JSValueConst v) { return JS_VALUE_GET_TAG(v) == JS_TAG_NULL || JS_VALUE_GET_TAG(v) == JS_TAG_UNDEFINED; } extern qjs_module_t *qjs_modules[]; #endif /* _QJS_H_INCLUDED_ */ njs-0.8.9/src/qjs_buffer.c000066400000000000000000002062231474132077100154160ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) F5, Inc. */ #include #define INT24_MAX 0x7FFFFF #define INT24_MIN (-0x800000) #define INT40_MAX 0x7FFFFFFFFFLL #define INT40_MIN (-0x8000000000LL) #define INT48_MAX 0x7FFFFFFFFFFFLL #define INT48_MIN (-0x800000000000LL) #define UINT24_MAX 0xFFFFFFLL #define UINT40_MAX 0xFFFFFFFFFFLL #define UINT48_MAX 0xFFFFFFFFFFFFLL #define qjs_buffer_magic(size, sign, little) \ ((size << 2) | (sign << 1) | little) static JSValue qjs_buffer(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue qjs_buffer_ctor(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue qjs_bufferobj_alloc(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int ignored); static JSValue qjs_buffer_byte_length(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue qjs_buffer_compare(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue qjs_buffer_concat(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue qjs_buffer_fill(JSContext *ctx, JSValueConst buffer, JSValueConst fill, JSValueConst encode, uint64_t offset, uint64_t end); static JSValue qjs_buffer_from(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue qjs_buffer_is_buffer(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue qjs_buffer_is_encoding(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue qjs_buffer_prototype_compare(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue qjs_buffer_prototype_copy(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue qjs_buffer_prototype_equals(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue qjs_buffer_prototype_fill(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue qjs_buffer_prototype_includes(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue qjs_buffer_prototype_index_of(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int last); static JSValue qjs_buffer_prototype_read_float(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic); static JSValue qjs_buffer_prototype_read_int(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic); static JSValue qjs_buffer_prototype_swap(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int size); static JSValue qjs_buffer_prototype_to_json(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue qjs_buffer_prototype_to_string(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue qjs_buffer_prototype_write(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); static JSValue qjs_buffer_prototype_write_int(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic); static JSValue qjs_buffer_prototype_write_float(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic); static JSValue qjs_buffer_from_string(JSContext *ctx, JSValueConst str, JSValueConst encoding); static JSValue qjs_buffer_from_typed_array(JSContext *ctx, JSValueConst obj, size_t offset, size_t size, size_t bytes, int float32); static JSValue qjs_buffer_from_object(JSContext *ctx, JSValueConst obj); static JSValue qjs_buffer_compare_array(JSContext *ctx, JSValue val1, JSValue val2, JSValueConst target_start, JSValueConst target_end, JSValueConst source_start, JSValueConst source_end); static int qjs_base64_encode(JSContext *ctx, const njs_str_t *src, njs_str_t *dst); static size_t qjs_base64_encode_length(JSContext *ctx, const njs_str_t *src); static int qjs_base64_decode(JSContext *ctx, const njs_str_t *src, njs_str_t *dst); static size_t qjs_base64_decode_length(JSContext *ctx, const njs_str_t *src); static int qjs_base64url_encode(JSContext *ctx, const njs_str_t *src, njs_str_t *dst); static int qjs_base64url_decode(JSContext *ctx, const njs_str_t *src, njs_str_t *dst); static size_t qjs_base64url_decode_length(JSContext *ctx, const njs_str_t *src); static int qjs_hex_encode(JSContext *ctx, const njs_str_t *src, njs_str_t *dst); static size_t qjs_hex_encode_length(JSContext *ctx, const njs_str_t *src); static int qjs_hex_decode(JSContext *ctx, const njs_str_t *src, njs_str_t *dst); static size_t qjs_hex_decode_length(JSContext *ctx, const njs_str_t *src); static JSValue qjs_new_uint8_array(JSContext *ctx, int argc, JSValueConst *argv); static JSModuleDef *qjs_buffer_init(JSContext *ctx, const char *name); static qjs_buffer_encoding_t qjs_buffer_encodings[] = { { njs_str("utf-8"), NULL, NULL, NULL, NULL, }, { njs_str("utf8"), NULL, NULL, NULL, NULL, }, { njs_str("base64"), qjs_base64_encode, qjs_base64_encode_length, qjs_base64_decode, qjs_base64_decode_length, }, { njs_str("base64url"), qjs_base64url_encode, qjs_base64_encode_length, qjs_base64url_decode, qjs_base64url_decode_length, }, { njs_str("hex"), qjs_hex_encode, qjs_hex_encode_length, qjs_hex_decode, qjs_hex_decode_length, }, { njs_null_str, 0, 0, 0, 0 } }; static const JSCFunctionListEntry qjs_buffer_constants[] = { JS_PROP_INT32_DEF("MAX_LENGTH", INT32_MAX, JS_PROP_ENUMERABLE), JS_PROP_INT32_DEF("MAX_STRING_LENGTH", 0x3fffffff, JS_PROP_ENUMERABLE), }; static const JSCFunctionListEntry qjs_buffer_export[] = { JS_OBJECT_DEF("constants", qjs_buffer_constants, njs_nitems(qjs_buffer_constants), JS_PROP_CONFIGURABLE), }; static const JSCFunctionListEntry qjs_buffer_props[] = { JS_CFUNC_MAGIC_DEF("alloc", 3, qjs_bufferobj_alloc, 0), JS_CFUNC_MAGIC_DEF("allocUnsafe", 3, qjs_bufferobj_alloc, 1), JS_CFUNC_DEF("byteLength", 2, qjs_buffer_byte_length), JS_CFUNC_DEF("compare", 6, qjs_buffer_compare), JS_CFUNC_DEF("concat", 1, qjs_buffer_concat), JS_CFUNC_DEF("from", 3, qjs_buffer_from), JS_CFUNC_DEF("isBuffer", 1, qjs_buffer_is_buffer), JS_CFUNC_DEF("isEncoding", 1, qjs_buffer_is_encoding), }; static const JSCFunctionListEntry qjs_buffer_proto[] = { JS_CFUNC_DEF("compare", 5, qjs_buffer_prototype_compare), JS_CFUNC_DEF("copy", 5, qjs_buffer_prototype_copy), JS_CFUNC_DEF("equals", 1, qjs_buffer_prototype_equals), JS_CFUNC_DEF("fill", 4, qjs_buffer_prototype_fill), JS_CFUNC_DEF("includes", 3, qjs_buffer_prototype_includes), JS_CFUNC_MAGIC_DEF("indexOf", 3, qjs_buffer_prototype_index_of, 0), JS_CFUNC_MAGIC_DEF("lastIndexOf", 3, qjs_buffer_prototype_index_of, 1), JS_CFUNC_MAGIC_DEF("readFloatLE", 1, qjs_buffer_prototype_read_float, qjs_buffer_magic(4, 1, 1)), JS_CFUNC_MAGIC_DEF("readFloatBE", 1, qjs_buffer_prototype_read_float, qjs_buffer_magic(4, 1, 0)), JS_CFUNC_MAGIC_DEF("readDoubleLE", 1, qjs_buffer_prototype_read_float, qjs_buffer_magic(8, 1, 1)), JS_CFUNC_MAGIC_DEF("readDoubleBE", 1, qjs_buffer_prototype_read_float, qjs_buffer_magic(8, 1, 0)), JS_CFUNC_MAGIC_DEF("readInt8", 1, qjs_buffer_prototype_read_int, qjs_buffer_magic(1, 1, 1)), JS_CFUNC_MAGIC_DEF("readUInt8", 1, qjs_buffer_prototype_read_int, qjs_buffer_magic(1, 0, 1)), JS_CFUNC_MAGIC_DEF("readInt16LE", 1, qjs_buffer_prototype_read_int, qjs_buffer_magic(2, 1, 1)), JS_CFUNC_MAGIC_DEF("readUInt16LE", 1, qjs_buffer_prototype_read_int, qjs_buffer_magic(2, 0, 1)), JS_CFUNC_MAGIC_DEF("readInt16BE", 1, qjs_buffer_prototype_read_int, qjs_buffer_magic(2, 1, 0)), JS_CFUNC_MAGIC_DEF("readUInt16BE", 1, qjs_buffer_prototype_read_int, qjs_buffer_magic(2, 0, 0)), JS_CFUNC_MAGIC_DEF("readInt32LE", 1, qjs_buffer_prototype_read_int, qjs_buffer_magic(4, 1, 1)), JS_CFUNC_MAGIC_DEF("readUInt32LE", 1, qjs_buffer_prototype_read_int, qjs_buffer_magic(4, 0, 1)), JS_CFUNC_MAGIC_DEF("readInt32BE", 1, qjs_buffer_prototype_read_int, qjs_buffer_magic(4, 1, 0)), JS_CFUNC_MAGIC_DEF("readUInt32BE", 1, qjs_buffer_prototype_read_int, qjs_buffer_magic(4, 0, 0)), JS_CFUNC_MAGIC_DEF("readIntLE", 2, qjs_buffer_prototype_read_int, qjs_buffer_magic(0, 1, 1)), JS_CFUNC_MAGIC_DEF("readUIntLE", 2, qjs_buffer_prototype_read_int, qjs_buffer_magic(0, 0, 1)), JS_CFUNC_MAGIC_DEF("readIntBE", 2, qjs_buffer_prototype_read_int, qjs_buffer_magic(0, 1, 0)), JS_CFUNC_MAGIC_DEF("readUIntBE", 2, qjs_buffer_prototype_read_int, qjs_buffer_magic(0, 0, 0)), JS_CFUNC_MAGIC_DEF("swap16", 0, qjs_buffer_prototype_swap, 2), JS_CFUNC_MAGIC_DEF("swap32", 0, qjs_buffer_prototype_swap, 4), JS_CFUNC_MAGIC_DEF("swap64", 0, qjs_buffer_prototype_swap, 8), JS_CFUNC_DEF("toJSON", 0, qjs_buffer_prototype_to_json), JS_CFUNC_DEF("toString", 1, qjs_buffer_prototype_to_string), JS_CFUNC_DEF("write", 4, qjs_buffer_prototype_write), JS_CFUNC_MAGIC_DEF("writeInt8", 1, qjs_buffer_prototype_write_int, qjs_buffer_magic(1, 1, 1)), JS_CFUNC_MAGIC_DEF("writeUInt8", 1, qjs_buffer_prototype_write_int, qjs_buffer_magic(1, 0, 1)), JS_CFUNC_MAGIC_DEF("writeInt16LE", 1, qjs_buffer_prototype_write_int, qjs_buffer_magic(2, 1, 1)), JS_CFUNC_MAGIC_DEF("writeUInt16LE", 1, qjs_buffer_prototype_write_int, qjs_buffer_magic(2, 0, 1)), JS_CFUNC_MAGIC_DEF("writeInt16BE", 1, qjs_buffer_prototype_write_int, qjs_buffer_magic(2, 1, 0)), JS_CFUNC_MAGIC_DEF("writeUInt16BE", 1, qjs_buffer_prototype_write_int, qjs_buffer_magic(2, 0, 0)), JS_CFUNC_MAGIC_DEF("writeInt32LE", 1, qjs_buffer_prototype_write_int, qjs_buffer_magic(4, 1, 1)), JS_CFUNC_MAGIC_DEF("writeUInt32LE", 1, qjs_buffer_prototype_write_int, qjs_buffer_magic(4, 0, 1)), JS_CFUNC_MAGIC_DEF("writeInt32BE", 1, qjs_buffer_prototype_write_int, qjs_buffer_magic(4, 1, 0)), JS_CFUNC_MAGIC_DEF("writeUInt32BE", 1, qjs_buffer_prototype_write_int, qjs_buffer_magic(4, 0, 0)), JS_CFUNC_MAGIC_DEF("writeIntLE", 2, qjs_buffer_prototype_write_int, qjs_buffer_magic(0, 1, 1)), JS_CFUNC_MAGIC_DEF("writeUIntLE", 2, qjs_buffer_prototype_write_int, qjs_buffer_magic(0, 0, 1)), JS_CFUNC_MAGIC_DEF("writeIntBE", 2, qjs_buffer_prototype_write_int, qjs_buffer_magic(0, 1, 0)), JS_CFUNC_MAGIC_DEF("writeUIntBE", 2, qjs_buffer_prototype_write_int, qjs_buffer_magic(0, 0, 0)), JS_CFUNC_MAGIC_DEF("writeFloatLE", 2, qjs_buffer_prototype_write_float, qjs_buffer_magic(4, 1, 1)), JS_CFUNC_MAGIC_DEF("writeFloatBE", 2, qjs_buffer_prototype_write_float, qjs_buffer_magic(4, 1, 0)), JS_CFUNC_MAGIC_DEF("writeDoubleLE", 2, qjs_buffer_prototype_write_float, qjs_buffer_magic(8, 1, 1)), JS_CFUNC_MAGIC_DEF("writeDoubleBE", 2, qjs_buffer_prototype_write_float, qjs_buffer_magic(8, 1, 0)), }; static JSClassDef qjs_buffer_class = { "Buffer", .finalizer = NULL, }; #ifndef NJS_HAVE_QUICKJS_NEW_TYPED_ARRAY static JSClassDef qjs_uint8_array_ctor_class = { "Uint8ArrayConstructor", .finalizer = NULL, }; #endif qjs_module_t qjs_buffer_module = { .name = "buffer", .init = qjs_buffer_init, }; static u_char qjs_basis64[] = { 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 62, 77, 77, 77, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 77, 77, 77, 77, 77, 77, 77, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 77, 77, 77, 77, 77, 77, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77 }; static u_char qjs_basis64url[] = { 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 62, 77, 77, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 77, 77, 77, 77, 77, 77, 77, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 77, 77, 77, 77, 63, 77, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77 }; static u_char qjs_basis64_enc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static u_char qjs_basis64url_enc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; #define qjs_base64_encoded_length(len) (((len + 2) / 3) * 4) #define qjs_base64_decoded_length(len, pad) (((len / 4) * 3) - pad) static JSValue qjs_buffer(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { JS_ThrowTypeError(ctx, "Buffer() is deprecated. Use the Buffer.alloc() " "or Buffer.from() methods instead."); return JS_EXCEPTION; } static JSValue qjs_buffer_ctor(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { JSValue ret, proto; ret = qjs_new_uint8_array(ctx, argc, argv); if (JS_IsException(ret)) { return ret; } proto = JS_GetClassProto(ctx, QJS_CORE_CLASS_ID_BUFFER); JS_SetPrototype(ctx, ret, proto); JS_FreeValue(ctx, proto); return ret; } static JSValue qjs_bufferobj_alloc(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int ignored) { JSValue buffer, ret; uint32_t size; if (!JS_IsNumber(argv[0])) { return JS_ThrowTypeError(ctx, "The \"size\" argument must be of type" " number"); } if (JS_ToUint32(ctx, &size, argv[0])) { return JS_EXCEPTION; } buffer = qjs_buffer_alloc(ctx, size); if (JS_IsException(buffer)) { return buffer; } if (!JS_IsUndefined(argv[1])) { ret = qjs_buffer_fill(ctx, buffer, argv[1], argv[2], 0, size); if (JS_IsException(ret)) { JS_FreeValue(ctx, buffer); return ret; } } return buffer; } static JSValue qjs_buffer_byte_length(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { size_t size; JSValue ret; njs_str_t src; const qjs_buffer_encoding_t *encoding; if (JS_GetArrayBuffer(ctx, &size, argv[0]) != NULL) { return JS_NewInt32(ctx, size); } ret = JS_GetTypedArrayBuffer(ctx, argv[0], NULL, &size, NULL); if (!JS_IsException(ret)) { JS_FreeValue(ctx, ret); return JS_NewInt32(ctx, size); } if (!JS_IsString(argv[0])) { return JS_ThrowTypeError(ctx, "first argument is not a string " "or Buffer-like object"); } encoding = qjs_buffer_encoding(ctx, argv[1], 1); if (encoding == NULL) { return JS_EXCEPTION; } src.start = (u_char *) JS_ToCStringLen(ctx, &src.length, argv[0]); if (encoding->decode_length != NULL) { size = encoding->decode_length(ctx, &src); } else { size = src.length; } JS_FreeCString(ctx, (char *) src.start); return JS_NewInt32(ctx, size); } static JSValue qjs_buffer_compare(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { return qjs_buffer_compare_array(ctx, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); } static JSValue qjs_buffer_concat(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { u_char *p; size_t n; JSValue list, length, val, ret, buffer; uint32_t i, len, list_len; njs_str_t buf, dst; list = argv[0]; if (!JS_IsArray(ctx, list)) { return JS_ThrowTypeError(ctx, "\"list\" argument must be an instance of Array"); } length = JS_GetPropertyStr(ctx, list, "length"); if (JS_IsException(length)) { return JS_EXCEPTION; } len = 0; if (JS_ToUint32(ctx, &list_len, length)) { JS_FreeValue(ctx, length); return JS_EXCEPTION; } JS_FreeValue(ctx, length); if (JS_IsUndefined(argv[1])) { for (i = 0; i < list_len; i++) { val = JS_GetPropertyUint32(ctx, list, i); if (JS_IsException(val)) { return JS_EXCEPTION; } ret = qjs_typed_array_data(ctx, val, &buf); JS_FreeValue(ctx, val); if (JS_IsException(ret)) { return JS_ThrowTypeError(ctx, "\"list[%d]\" argument must be an" " instance of Buffer or Uint8Array", i); } if ((SIZE_MAX - len) < buf.length) { return JS_ThrowTypeError(ctx, "Total size of buffers is too large"); } len += buf.length; } } else { if (JS_ToUint32(ctx, &len, argv[1])) { return JS_EXCEPTION; } } buffer = qjs_buffer_alloc(ctx, len); if (JS_IsException(buffer)) { return JS_EXCEPTION; } ret = qjs_typed_array_data(ctx, buffer, &dst); if (JS_IsException(ret)) { JS_FreeValue(ctx, buffer); return JS_EXCEPTION; } p = dst.start; for (i = 0; len != 0 && i < list_len; i++) { val = JS_GetPropertyUint32(ctx, list, i); if (JS_IsException(val)) { JS_FreeValue(ctx, buffer); return JS_EXCEPTION; } ret = qjs_typed_array_data(ctx, val, &buf); if (JS_IsException(ret)) { JS_FreeValue(ctx, buffer); JS_FreeValue(ctx, val); return JS_EXCEPTION; } JS_FreeValue(ctx, val); n = njs_min((size_t) len, buf.length); p = njs_cpymem(p, buf.start, n); len -= n; } if (len != 0) { njs_memzero(p, len); } return buffer; } static JSValue qjs_buffer_fill(JSContext *ctx, JSValueConst buffer, JSValueConst fill, JSValueConst encode, uint64_t offset, uint64_t end) { JSValue ret, fill_buf; uint32_t n; njs_str_t dst, src; ret = qjs_typed_array_data(ctx, buffer, &dst); if (JS_IsException(ret)) { return ret; } if (end > dst.length) { return JS_ThrowRangeError(ctx, "\"end\" is out of range"); } if (offset >= end) { return buffer; } if (JS_IsNumber(fill)) { if (JS_ToUint32(ctx, &n, fill)) { return JS_EXCEPTION; } memset(dst.start + offset, n & 0xff, end - offset); return buffer; } fill_buf = JS_UNDEFINED; if (JS_IsString(fill)) { fill_buf = qjs_buffer_from_string(ctx, fill, encode); if (JS_IsException(fill_buf)) { return fill_buf; } fill = fill_buf; } ret = qjs_typed_array_data(ctx, fill, &src); if (JS_IsException(ret)) { JS_FreeValue(ctx, fill_buf); return ret; } if (src.length == 0) { memset(dst.start + offset, 0, end - offset); JS_FreeValue(ctx, fill_buf); return buffer; } if (src.start >= (dst.start + dst.length) || dst.start >= (dst.start + dst.length)) { while (offset < end) { n = njs_min(src.length, end - offset); memcpy(dst.start + offset, src.start, n); offset += n; } } else { while (offset < end) { n = njs_min(src.length, end - offset); memmove(dst.start + offset, src.start, n); offset += n; } } JS_FreeValue(ctx, fill_buf); return buffer; } static JSValue qjs_buffer_from(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { int float32; size_t off, size, bytes; u_char *buf; JSValue ret, ctor, name, obj, valueOf; const char *str; if (JS_IsString(argv[0])) { return qjs_buffer_from_string(ctx, argv[0], argv[1]); } else if (ret = JS_GetTypedArrayBuffer(ctx, argv[0], &off, &size, &bytes), !JS_IsException(ret)) { float32 = 0; if (bytes == 4) { /* * API workaround for JS_GetTypedArrayBuffer() * that does not distinguish between Float32Array and Uint32Array. */ ctor = JS_GetPropertyStr(ctx, argv[0], "constructor"); if (JS_IsException(ctor)) { JS_FreeValue(ctx, ret); return ctor; } name = JS_GetPropertyStr(ctx, ctor, "name"); if (JS_IsException(name)) { JS_FreeValue(ctx, ret); return name; } JS_FreeValue(ctx, ctor); str = JS_ToCString(ctx, name); if (strncmp(str, "Float32Array", 12) == 0) { float32 = 1; } JS_FreeCString(ctx, str); JS_FreeValue(ctx, name); } return qjs_buffer_from_typed_array(ctx, ret, off, size, bytes, float32); } else if ((buf = JS_GetArrayBuffer(ctx, &size, argv[0])) != NULL) { return qjs_buffer_ctor(ctx, JS_UNDEFINED, argc, argv); } else if (JS_IsObject(argv[0])) { obj = argv[0]; valueOf = JS_GetPropertyStr(ctx, obj, "valueOf"); if (JS_IsException(valueOf)) { return valueOf; } if (JS_IsFunction(ctx, valueOf)) { ret = JS_Call(ctx, valueOf, obj, 0, NULL); JS_FreeValue(ctx, valueOf); if (JS_IsException(ret)) { return ret; } if (JS_IsString(ret)) { obj = ret; ret = qjs_buffer_from_string(ctx, obj, argv[1]); JS_FreeValue(ctx, obj); return ret; } if (JS_IsObject(ret) && JS_VALUE_GET_PTR(ret) != JS_VALUE_GET_PTR(obj)) { obj = ret; ret = qjs_buffer_from_object(ctx, obj); JS_FreeValue(ctx, obj); return ret; } JS_FreeValue(ctx, ret); } return qjs_buffer_from_object(ctx, obj); } JS_ThrowTypeError(ctx, "first argument is not a string " "or Buffer-like object"); return JS_EXCEPTION; } static JSValue qjs_buffer_is_buffer(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { JSValue proto, buffer_proto, ret; proto = JS_GetPrototype(ctx, argv[0]); buffer_proto = JS_GetClassProto(ctx, QJS_CORE_CLASS_ID_BUFFER); ret = JS_NewBool(ctx, JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT && JS_VALUE_GET_OBJ(buffer_proto) == JS_VALUE_GET_OBJ(proto)); JS_FreeValue(ctx, buffer_proto); JS_FreeValue(ctx, proto); return ret; } static JSValue qjs_buffer_is_encoding(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { return JS_NewBool(ctx, qjs_buffer_encoding(ctx, argv[0], 0) != NULL); } static JSValue qjs_buffer_array_range(JSContext *ctx, njs_str_t *array, JSValueConst start, JSValueConst end, const char *name) { int64_t num_start, num_end; num_start = 0; if (!JS_IsUndefined(start)) { if (JS_ToInt64(ctx, &num_start, start)) { return JS_EXCEPTION; } } if (num_start < 0 || (size_t) num_start > array->length) { return JS_ThrowRangeError(ctx, "\"%sStart\" is out of range: %" PRId64, name, num_start); } num_end = array->length; if (!JS_IsUndefined(end)) { if (JS_ToInt64(ctx, &num_end, end)) { return JS_EXCEPTION; } } if (num_end < 0 || (size_t) num_end > array->length) { return JS_ThrowRangeError(ctx, "\"%sEnd\" is out of range: %" PRId64, name, num_end); } if (num_start > num_end) { num_end = num_start; } array->start += num_start; array->length = num_end - num_start; return JS_UNDEFINED; } static JSValue qjs_buffer_compare_array(JSContext *ctx, JSValue val1, JSValue val2, JSValueConst target_start, JSValueConst target_end, JSValueConst source_start, JSValueConst source_end) { int rc; size_t size; JSValue ret; njs_str_t src, target; ret = qjs_typed_array_data(ctx, val1, &src); if (JS_IsException(ret)) { return ret; } ret = qjs_typed_array_data(ctx, val2, &target); if (JS_IsException(ret)) { return ret; } ret = qjs_buffer_array_range(ctx, &src, source_start, source_end, "source"); if (JS_IsException(ret)) { return ret; } ret = qjs_buffer_array_range(ctx, &target, target_start, target_end, "target"); if (JS_IsException(ret)) { return ret; } size = njs_min(src.length, target.length); rc = memcmp(src.start, target.start, size); if (rc != 0) { return JS_NewInt32(ctx, (rc < 0) ? -1 : 1); } if (target.length > src.length) { rc = -1; } else if (target.length < src.length) { rc = 1; } return JS_NewInt32(ctx, rc); } static JSValue qjs_buffer_prototype_compare(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { return qjs_buffer_compare_array(ctx, this_val, argv[0], argv[1], argv[2], argv[3], argv[4]); } static JSValue qjs_buffer_prototype_copy(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { size_t size; JSValue ret; njs_str_t src, target; ret = qjs_typed_array_data(ctx, this_val, &src); if (JS_IsException(ret)) { return ret; } ret = qjs_typed_array_data(ctx, argv[0], &target); if (JS_IsException(ret)) { return ret; } ret = qjs_buffer_array_range(ctx, &target, argv[1], JS_UNDEFINED, "target"); if (JS_IsException(ret)) { return ret; } ret = qjs_buffer_array_range(ctx, &src, argv[2], argv[3], "source"); if (JS_IsException(ret)) { return ret; } size = njs_min(src.length, target.length); if (src.start >= (target.start + size) || target.start >= (src.start + size)) { memcpy(target.start, src.start, size); } else { memmove(target.start, src.start, size); } return JS_NewInt32(ctx, size); } static JSValue qjs_buffer_prototype_equals(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { JSValue ret; ret = qjs_buffer_compare_array(ctx, this_val, argv[0], JS_UNDEFINED, JS_UNDEFINED, JS_UNDEFINED, JS_UNDEFINED); if (JS_IsException(ret)) { return ret; } return JS_NewBool(ctx, JS_VALUE_GET_INT(ret) == 0); } static JSValue qjs_buffer_prototype_fill(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { JSValue ret, encode; uint64_t offset, end; njs_str_t dst; offset = 0; encode = argv[3]; ret = qjs_typed_array_data(ctx, this_val, &dst); if (JS_IsException(ret)) { return ret; } end = dst.length; if (!JS_IsUndefined(argv[1])) { if (JS_IsString(argv[0]) && JS_IsString(argv[1])) { encode = argv[1]; goto fill; } if (JS_ToIndex(ctx, &offset, argv[1])) { return JS_EXCEPTION; } } if (!JS_IsUndefined(argv[2])) { if (JS_IsString(argv[0]) && JS_IsString(argv[2])) { encode = argv[2]; goto fill; } if (JS_ToIndex(ctx, &end, argv[2])) { return JS_EXCEPTION; } } fill: ret = qjs_buffer_fill(ctx, this_val, argv[0], encode, offset, end); if (JS_IsException(ret)) { return ret; } JS_DupValue(ctx, ret); return ret; } static JSValue qjs_buffer_prototype_includes(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { JSValue ret; ret = qjs_buffer_prototype_index_of(ctx, this_val, argc, argv, 0); if (JS_IsException(ret)) { return ret; } return JS_NewBool(ctx, JS_VALUE_GET_INT(ret) != -1); } static JSValue qjs_buffer_prototype_index_of(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int last) { JSValue ret, buffer, encode, value; int64_t from, to, increment, length, i; uint32_t byte; njs_str_t self, str; const qjs_buffer_encoding_t *encoding; ret = qjs_typed_array_data(ctx, this_val, &self); if (JS_IsException(ret)) { return ret; } length = self.length; if (last) { from = length - 1; to = -1; increment = -1; } else { from = 0; to = length; increment = 1; } encode = argv[2]; if (!JS_IsUndefined(argv[1])) { if (JS_IsString(argv[0]) && JS_IsString(argv[1])) { encode = argv[1]; goto encoding; } if (JS_ToInt64(ctx, &from, argv[1])) { return JS_EXCEPTION; } if (from >= 0) { from = njs_min(from, length); } else { from = njs_max(0, length + from); } } if (JS_IsNumber(argv[0])) { if (JS_ToUint32(ctx, &byte, argv[0])) { return JS_EXCEPTION; } if (last) { from = njs_min(from, length - 1); } for (i = from; i != to; i += increment) { if (self.start[i] == (uint8_t) byte) { return JS_NewInt32(ctx, i); } } return JS_NewInt32(ctx, -1); } encoding: buffer = JS_UNDEFINED; value = argv[0]; if (JS_IsString(value)) { encoding = qjs_buffer_encoding(ctx, encode, 1); if (encoding == NULL) { return JS_EXCEPTION; } buffer = qjs_buffer_from_string(ctx, value, encode); if (JS_IsException(buffer)) { return buffer; } value = buffer; } ret = qjs_typed_array_data(ctx, value, &str); if (JS_IsException(ret)) { JS_FreeValue(ctx, buffer); return JS_ThrowTypeError(ctx, "\"value\" argument is not a string " "or Buffer-like object"); } if (last) { from = njs_min(from, length - (int64_t) str.length); if (to > from) { goto done; } } else { to -= (int64_t) str.length - 1; if (from > to) { goto done; } } if (from == to && str.length == 0) { JS_FreeValue(ctx, buffer); return JS_NewInt32(ctx, 0); } for (i = from; i != to; i += increment) { if (memcmp(&self.start[i], str.start, str.length) == 0) { JS_FreeValue(ctx, buffer); return JS_NewInt32(ctx, i); } } done: JS_FreeValue(ctx, buffer); return JS_NewInt32(ctx, -1); } static JSValue qjs_buffer_prototype_read_float(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) { double v; JSValue ret; uint32_t u32; uint64_t u64, index, size; njs_str_t self; njs_bool_t little, swap; njs_conv_f32_t conv_f32; njs_conv_f64_t conv_f64; ret = qjs_typed_array_data(ctx, this_val, &self); if (JS_IsException(ret)) { return ret; } if (JS_ToIndex(ctx, &index, argv[0])) { return JS_EXCEPTION; } size = magic >> 2; if (size + index > self.length) { return JS_ThrowRangeError(ctx, "index %" PRIu64 " is outside the bound" " of the buffer", index); } little = magic & 1; swap = little; #if NJS_HAVE_LITTLE_ENDIAN swap = !swap; #endif switch (size) { case 4: u32 = *((uint32_t *) &self.start[index]); if (swap) { u32 = njs_bswap_u32(u32); } conv_f32.u = u32; v = conv_f32.f; break; case 8: default: u64 = *((uint64_t *) &self.start[index]); if (swap) { u64 = njs_bswap_u64(u64); } conv_f64.u = u64; v = conv_f64.f; break; } return JS_NewFloat64(ctx, v); } static JSValue qjs_buffer_prototype_read_int(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) { JSValue ret; uint32_t u32; uint64_t u64, index, size; njs_str_t self; njs_bool_t little, swap, sign; ret = qjs_typed_array_data(ctx, this_val, &self); if (JS_IsException(ret)) { return ret; } if (JS_ToIndex(ctx, &index, argv[0])) { return JS_EXCEPTION; } size = magic >> 2; if (!size) { if (!JS_IsNumber(argv[1])) { return JS_ThrowTypeError(ctx, "\"byteLength\" is not a number"); } if (JS_ToIndex(ctx, &size, argv[1])) { return JS_EXCEPTION; } if (size > 6) { return JS_ThrowRangeError(ctx, "\"byteLength\" must be <= 6"); } } if (size + index > self.length) { return JS_ThrowRangeError(ctx, "index %" PRIu64 " is outside the bound" " of the buffer", index); } sign = (magic >> 1) & 1; little = magic & 1; swap = little; #if NJS_HAVE_LITTLE_ENDIAN swap = !swap; #endif switch (size) { case 1: if (sign) { return JS_NewInt32(ctx, (int8_t) self.start[index]); } return JS_NewUint32(ctx, self.start[index]); case 2: u32 = njs_get_u16(&self.start[index]); if (swap) { u32 = njs_bswap_u16(u32); } if (sign) { /* Sign extension. */ u32 |= (u32 & (INT16_MAX + 1ULL)) * UINT32_MAX; return JS_NewInt32(ctx, (int16_t) u32); } return JS_NewUint32(ctx, u32); case 3: if (little) { u32 = (self.start[index + 2] << 16) | (self.start[index + 1] << 8) | self.start[index]; } else { u32 = (self.start[index] << 16) | (self.start[index + 1] << 8) | self.start[index + 2]; } if (sign) { /* Sign extension. */ u32 |= (u32 & (INT24_MAX + 1ULL)) * UINT32_MAX; return JS_NewInt32(ctx, (int32_t) u32); } return JS_NewUint32(ctx, u32); case 4: u32 = njs_get_u32(&self.start[index]); if (swap) { u32 = njs_bswap_u32(u32); } if (sign) { /* Sign extension. */ u32 |= (u32 & (INT32_MAX + 1ULL)) * UINT32_MAX; return JS_NewInt32(ctx, (int32_t) u32); } return JS_NewUint32(ctx, u32); case 5: if (little) { u64 = ((uint64_t) self.start[index + 4] << 32) | ((uint64_t) self.start[index + 3] << 24) | (self.start[index + 2] << 16) | (self.start[index + 1] << 8) | self.start[index]; } else { u64 = ((uint64_t) self.start[index] << 32) | ((uint64_t) self.start[index + 1] << 24) | (self.start[index + 2] << 16) | (self.start[index + 3] << 8) | self.start[index + 4]; } if (sign) { /* Sign extension. */ u64 |= (u64 & (INT40_MAX + 1ULL)) * UINT64_MAX; return JS_NewFloat64(ctx, (int64_t) u64); } return JS_NewFloat64(ctx, u64); case 6: default: if (little) { u64 = ((uint64_t) self.start[index + 5] << 40) | ((uint64_t) self.start[index + 4] << 32) | ((uint64_t) self.start[index + 3] << 24) | (self.start[index + 2] << 16) | (self.start[index + 1] << 8) | self.start[index]; } else { u64 = ((uint64_t) self.start[index] << 40) | ((uint64_t) self.start[index + 1] << 32) | ((uint64_t) self.start[index + 2] << 24) | (self.start[index + 3] << 16) | (self.start[index + 4] << 8) | self.start[index + 5]; } if (sign) { /* Sign extension. */ u64 |= (u64 & (INT48_MAX + 1ULL)) * UINT64_MAX; return JS_NewFloat64(ctx, (int64_t) u64); } return JS_NewFloat64(ctx, u64); } } static JSValue qjs_buffer_prototype_to_json(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { int rc; JSValue obj, data, ret; njs_str_t src; njs_uint_t i; ret = qjs_typed_array_data(ctx, this_val, &src); if (JS_IsException(ret)) { return ret; } obj = JS_NewObject(ctx); if (JS_IsException(obj)) { return obj; } data = JS_NewArray(ctx); if (JS_IsException(data)) { JS_FreeValue(ctx, obj); return data; } rc = JS_DefinePropertyValueStr(ctx, obj, "type", JS_NewString(ctx, "Buffer"), JS_PROP_ENUMERABLE); if (rc == -1) { JS_FreeValue(ctx, obj); JS_FreeValue(ctx, data); return ret; } rc = JS_DefinePropertyValueStr(ctx, obj, "data", data, JS_PROP_ENUMERABLE); if (rc == -1) { JS_FreeValue(ctx, obj); JS_FreeValue(ctx, data); return ret; } for (i = 0; i < src.length; i++) { rc = JS_SetPropertyUint32(ctx, data, i, JS_NewInt32(ctx, src.start[i])); if (rc == -1) { JS_FreeValue(ctx, obj); JS_FreeValue(ctx, data); return ret; } } return obj; } static JSValue qjs_buffer_prototype_swap(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int size) { uint8_t *p, *end; JSValue ret; njs_str_t self; ret = qjs_typed_array_data(ctx, this_val, &self); if (JS_IsException(ret)) { return ret; } if ((self.length % size) != 0) { return JS_ThrowRangeError(ctx, "Buffer size must be a multiple " "of %d-bits", (int) (size << 3)); } p = self.start; end = p + self.length; switch (size) { case 2: for (; p < end; p += 2) { njs_set_u16(p, njs_bswap_u16(njs_get_u16(p))); } break; case 4: for (; p < end; p += 4) { njs_set_u32(p, njs_bswap_u32(njs_get_u32(p))); } break; case 8: default: for (; p < end; p += 8) { njs_set_u64(p, njs_bswap_u64(njs_get_u64(p))); } } JS_DupValue(ctx, this_val); return this_val; } static JSValue qjs_buffer_prototype_to_string(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { JSValue ret; njs_str_t src, data; const qjs_buffer_encoding_t *encoding; ret = qjs_typed_array_data(ctx, this_val, &src); if (JS_IsException(ret)) { return JS_ThrowTypeError(ctx, "method toString() called on incompatible" " object"); } if (JS_IsUndefined(argv[0]) || src.length == 0) { return JS_NewStringLen(ctx, (char *) src.start, src.length); } encoding = qjs_buffer_encoding(ctx, argv[0], 1); if (njs_slow_path(encoding == NULL)) { return JS_EXCEPTION; } if (encoding->encode_length == NULL) { return JS_NewStringLen(ctx, (char *) src.start, src.length); } data.length = encoding->encode_length(ctx, &src); data.start = js_malloc(ctx, data.length); if (njs_slow_path(data.start == NULL)) { JS_ThrowOutOfMemory(ctx); return JS_EXCEPTION; } if (encoding->encode(ctx, &src, &data) != 0) { js_free(ctx, data.start); JS_ThrowTypeError(ctx, "failed to encode buffer"); return JS_EXCEPTION; } ret = JS_NewStringLen(ctx, (char *) data.start, data.length); js_free(ctx, data.start); return ret; } static JSValue qjs_buffer_prototype_write(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { JSValue ret, buffer, encode; uint64_t offset, max_length; njs_str_t self, src; const uint8_t *p, *end, *prev; const qjs_buffer_encoding_t *encoding; ret = qjs_typed_array_data(ctx, this_val, &self); if (JS_IsException(ret)) { return ret; } offset = 0; max_length = self.length; encode = argv[3]; if (!JS_IsUndefined(argv[1])) { if (JS_IsString(argv[0]) && JS_IsString(argv[1])) { encode = argv[1]; goto write; } if (JS_ToIndex(ctx, &offset, argv[1])) { return JS_EXCEPTION; } max_length = self.length - offset; } if (!JS_IsUndefined(argv[2])) { if (JS_IsString(argv[0]) && JS_IsString(argv[2])) { encode = argv[2]; goto write; } if (JS_ToIndex(ctx, &max_length, argv[2])) { return JS_EXCEPTION; } } write: encoding = qjs_buffer_encoding(ctx, encode, 1); if (encoding == NULL) { return JS_EXCEPTION; } buffer = qjs_buffer_from_string(ctx, argv[0], encode); if (JS_IsException(buffer)) { return buffer; } (void) qjs_typed_array_data(ctx, buffer, &src); if (offset > self.length) { JS_FreeValue(ctx, buffer); return JS_ThrowRangeError(ctx, "\"offset\" is out of range"); } if (src.length == 0) { JS_FreeValue(ctx, buffer); return JS_NewInt32(ctx, 0); } if (max_length > self.length - offset) { JS_FreeValue(ctx, buffer); return JS_ThrowRangeError(ctx, "\"length\" is out of range"); } max_length = njs_min(max_length, src.length); if (encoding->decode == NULL) { /* Avoid writing incomplete UTF-8 characters. */ p = prev = src.start; end = p + max_length; while (p < end) { p = njs_utf8_next(p, src.start + src.length); if (p <= end) { prev = p; } } max_length = prev - src.start; } memcpy(&self.start[offset], src.start, max_length); JS_FreeValue(ctx, buffer); return JS_NewInt32(ctx, max_length); } static JSValue qjs_buffer_prototype_write_int(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) { JSValue ret; int64_t i64; uint32_t u32; uint64_t index, size; njs_str_t self; njs_bool_t little, swap, sign; ret = qjs_typed_array_data(ctx, this_val, &self); if (JS_IsException(ret)) { return ret; } if (JS_ToIndex(ctx, &index, argv[1])) { return JS_EXCEPTION; } size = magic >> 2; if (!size) { if (JS_ToIndex(ctx, &size, argv[2])) { return JS_EXCEPTION; } if (size > 6) { return JS_ThrowRangeError(ctx, "\"byteLength\" must be <= 6"); } } if (size + index > self.length) { return JS_ThrowRangeError(ctx, "index %" PRIu64 " is outside the bound" " of the buffer", index); } little = magic & 1; sign = (magic >> 1) & 1; swap = little; #if NJS_HAVE_LITTLE_ENDIAN swap = !swap; #endif if (JS_ToInt64(ctx, &i64, argv[0])) { return JS_EXCEPTION; } switch (size) { case 1: if (sign) { if (i64 < INT8_MIN || i64 > INT8_MAX) { return JS_ThrowRangeError(ctx, "value is outside the range of" " representable values"); } } else { if (i64 < 0 || i64 > UINT8_MAX) { return JS_ThrowRangeError(ctx, "value is outside the range of" " representable values"); } } self.start[index] = (uint8_t) i64; break; case 2: u32 = (uint16_t) i64; if (sign) { if (i64 < INT16_MIN || i64 > INT16_MAX) { return JS_ThrowRangeError(ctx, "value is outside the range of" " representable values"); } } else { if (i64 < 0 || i64 > UINT16_MAX) { return JS_ThrowRangeError(ctx, "value is outside the range of" " representable values"); } } if (swap) { u32 = njs_bswap_u16(u32); } njs_set_u16(&self.start[index], u32); break; case 3: if (sign) { if (i64 < INT24_MIN || i64 > INT24_MAX) { return JS_ThrowRangeError(ctx, "value is outside the range of" " representable values"); } } else { if (i64 < 0 || i64 > UINT24_MAX) { return JS_ThrowRangeError(ctx, "value is outside the range of" " representable values"); } } if (little) { self.start[index] = i64; i64 >>= 8; self.start[index + 1] = i64; i64 >>= 8; self.start[index + 2] = i64; } else { self.start[index + 2] = i64; i64 >>= 8; self.start[index + 1] = i64; i64 >>= 8; self.start[index] = i64; } break; case 4: u32 = i64; if (sign) { if (i64 < INT32_MIN || i64 > INT32_MAX) { return JS_ThrowRangeError(ctx, "value is outside the range of" " representable values"); } } else { if (i64 < 0 || i64 > UINT32_MAX) { return JS_ThrowRangeError(ctx, "value is outside the range of" " representable values"); } } if (swap) { u32 = njs_bswap_u32(u32); } njs_set_u32(&self.start[index], u32); break; case 5: if (sign) { if (i64 < INT40_MIN || i64 > INT40_MAX) { return JS_ThrowRangeError(ctx, "value is outside the range of" " representable values"); } } else { if (i64 < 0 || i64 > UINT40_MAX) { return JS_ThrowRangeError(ctx, "value is outside the range of" " representable values"); } } if (little) { self.start[index] = i64; i64 >>= 8; self.start[index + 1] = i64; i64 >>= 8; self.start[index + 2] = i64; i64 >>= 8; self.start[index + 3] = i64; i64 >>= 8; self.start[index + 4] = i64; } else { self.start[index + 4] = i64; i64 >>= 8; self.start[index + 3] = i64; i64 >>= 8; self.start[index + 2] = i64; i64 >>= 8; self.start[index + 1] = i64; i64 >>= 8; self.start[index] = i64; } break; case 6: default: if (sign) { if (i64 < INT48_MIN || i64 > INT48_MAX) { return JS_ThrowRangeError(ctx, "value is outside the range of" " representable values"); } } else { if (i64 < 0 || i64 > UINT48_MAX) { return JS_ThrowRangeError(ctx, "value is outside the range of" " representable values"); } } if (little) { self.start[index] = i64; i64 >>= 8; self.start[index + 1] = i64; i64 >>= 8; self.start[index + 2] = i64; i64 >>= 8; self.start[index + 3] = i64; i64 >>= 8; self.start[index + 4] = i64; i64 >>= 8; self.start[index + 5] = i64; } else { self.start[index + 5] = i64; i64 >>= 8; self.start[index + 4] = i64; i64 >>= 8; self.start[index + 3] = i64; i64 >>= 8; self.start[index + 2] = i64; i64 >>= 8; self.start[index + 1] = i64; i64 >>= 8; self.start[index] = i64; } break; } return JS_NewInt32(ctx, size + index); } static JSValue qjs_buffer_prototype_write_float(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) { double v; JSValue ret; uint32_t u32; uint64_t u64, index, size; njs_str_t self; njs_bool_t little, swap; njs_conv_f32_t conv_f32; njs_conv_f64_t conv_f64; ret = qjs_typed_array_data(ctx, this_val, &self); if (JS_IsException(ret)) { return ret; } if (JS_ToFloat64(ctx, &v, argv[0])) { return JS_EXCEPTION; } if (JS_ToIndex(ctx, &index, argv[1])) { return JS_EXCEPTION; } size = magic >> 2; if (size + index > self.length) { return JS_ThrowRangeError(ctx, "index %" PRIu64 " is outside the bound" " of the buffer", index); } little = magic & 1; swap = little; #if NJS_HAVE_LITTLE_ENDIAN swap = !swap; #endif switch (size) { case 4: conv_f32.f = (float) v; if (swap) { conv_f32.u = njs_bswap_u32(conv_f32.u); } u32 = conv_f32.u; memcpy(&self.start[index], &u32, size); break; case 8: default: conv_f64.f = v; if (swap) { conv_f64.u = njs_bswap_u64(conv_f64.u); } u64 = conv_f64.u; memcpy(&self.start[index], &u64, size); break; } return JS_NewInt32(ctx, size + index); } static JSValue qjs_buffer_from_string(JSContext *ctx, JSValueConst str, JSValueConst enc) { size_t size; JSValue buffer, ret; njs_str_t src, dst; const qjs_buffer_encoding_t *encoding; if (!JS_IsString(str)) { JS_ThrowTypeError(ctx, "first argument is not a string"); return JS_EXCEPTION; } encoding = qjs_buffer_encoding(ctx, enc, 1); if (njs_slow_path(encoding == NULL)) { return JS_EXCEPTION; } src.start = (u_char *) JS_ToCStringLen(ctx, &src.length, str); if (encoding->decode_length != NULL) { size = encoding->decode_length(ctx, &src); } else { size = src.length; } buffer = qjs_buffer_alloc(ctx, size); if (JS_IsException(buffer)) { JS_FreeCString(ctx, (char *) src.start); return buffer; } ret = qjs_typed_array_data(ctx, buffer, &dst); if (JS_IsException(ret)) { JS_FreeCString(ctx, (char *) src.start); return ret; } if (encoding->decode != NULL) { if (encoding->decode(ctx, &src, &dst) != 0) { JS_FreeCString(ctx, (char *) src.start); JS_ThrowTypeError(ctx, "failed to decode string"); return JS_EXCEPTION; } } else { memcpy(dst.start, src.start, src.length); } JS_FreeCString(ctx, (char *) src.start); return buffer; } static JSValue qjs_buffer_from_typed_array(JSContext *ctx, JSValueConst arr_buf, size_t offset, size_t size, size_t bytes, int float32) { float *f32; u_char *p, *u8; size_t i; double *f64; JSValue buffer, ret; uint16_t *u16; uint32_t *u32; njs_str_t src, dst; size = size / bytes; buffer = qjs_buffer_alloc(ctx, size); if (JS_IsException(buffer)) { JS_FreeValue(ctx, arr_buf); return buffer; } ret = qjs_typed_array_data(ctx, buffer, &dst); if (JS_IsException(ret)) { JS_FreeValue(ctx, arr_buf); JS_FreeValue(ctx, buffer); return ret; } src.start = JS_GetArrayBuffer(ctx, &src.length, arr_buf); if (src.start == NULL) { JS_FreeValue(ctx, arr_buf); JS_FreeValue(ctx, buffer); return JS_EXCEPTION; } p = dst.start; switch (bytes) { case 1: u8 = src.start; memcpy(p, u8 + offset, size); break; case 2: u16 = (uint16_t *) src.start; for (i = 0; i < size; i++) { *p++ = u16[offset + i]; } break; case 4: if (float32) { f32 = (float *) src.start; for (i = 0; i < size; i++) { *p++ = f32[offset + i]; } break; } u32 = (uint32_t *) src.start; for (i = 0; i < size; i++) { *p++ = u32[offset + i]; } break; case 8: f64 = (double *) src.start; for (i = 0; i < size; i++) { *p++ = f64[offset + i]; } break; } JS_FreeValue(ctx, arr_buf); return buffer; } static JSValue qjs_buffer_from_object(JSContext *ctx, JSValueConst obj) { int v; u_char *p; int64_t i, len; JSValue buffer, ret; njs_str_t dst; const char *str; ret = JS_GetPropertyStr(ctx, obj, "length"); if (JS_IsException(ret)) { return ret; } if (JS_IsUndefined(ret)) { ret = JS_GetPropertyStr(ctx, obj, "type"); if (JS_IsException(ret)) { return ret; } if (JS_IsString(ret)) { str = JS_ToCString(ctx, ret); JS_FreeValue(ctx, ret); if (str != NULL) { if (strcmp(str, "Buffer") != 0) { JS_FreeCString(ctx, str); goto reject; } JS_FreeCString(ctx, str); ret = JS_GetPropertyStr(ctx, obj, "data"); if (JS_IsException(ret)) { return ret; } if (JS_IsObject(ret)) { obj = ret; ret = qjs_buffer_from_object(ctx, obj); JS_FreeValue(ctx, obj); return ret; } } } } if (!JS_IsNumber(ret)) { JS_FreeValue(ctx, ret); reject: JS_ThrowTypeError(ctx, "first argument is not a string " "or Buffer-like object"); return JS_EXCEPTION; } len = JS_VALUE_GET_INT(ret); buffer = qjs_buffer_alloc(ctx, len); if (JS_IsException(buffer)) { return buffer; } ret = qjs_typed_array_data(ctx, buffer, &dst); if (JS_IsException(ret)) { return ret; } p = dst.start; for (i = 0; i < len; i++) { ret = JS_GetPropertyUint32(ctx, obj, i); if (njs_slow_path(JS_IsException(ret))) { return ret; } if (njs_slow_path(JS_ToInt32(ctx, &v, ret))) { return JS_EXCEPTION; } JS_FreeValue(ctx, ret); *p++ = v; } return buffer; } const qjs_buffer_encoding_t * qjs_buffer_encoding(JSContext *ctx, JSValueConst value, JS_BOOL thrw) { njs_str_t name; qjs_buffer_encoding_t *encoding; if (!JS_IsString(value)){ if (!JS_IsUndefined(value)) { JS_ThrowTypeError(ctx, "encoding must be a string"); return NULL; } return &qjs_buffer_encodings[0]; } name.start = (u_char *) JS_ToCStringLen(ctx, &name.length, value); for (encoding = &qjs_buffer_encodings[0]; encoding->name.length != 0; encoding++) { if (njs_strstr_eq(&name, &encoding->name)) { JS_FreeCString(ctx, (char *) name.start); return encoding; } } JS_FreeCString(ctx, (char *) name.start); if (thrw) { JS_ThrowTypeError(ctx, "\"%*s\" encoding is not supported", (int) name.length, name.start); } return NULL; } static void qjs_base64_encode_core(njs_str_t *dst, const njs_str_t *src, const u_char *basis, njs_bool_t padding) { u_char *d, *s, c0, c1, c2; size_t len; len = src->length; s = src->start; d = dst->start; while (len > 2) { c0 = s[0]; c1 = s[1]; c2 = s[2]; *d++ = basis[c0 >> 2]; *d++ = basis[((c0 & 0x03) << 4) | (c1 >> 4)]; *d++ = basis[((c1 & 0x0f) << 2) | (c2 >> 6)]; *d++ = basis[c2 & 0x3f]; s += 3; len -= 3; } if (len > 0) { c0 = s[0]; *d++ = basis[c0 >> 2]; if (len == 1) { *d++ = basis[(c0 & 0x03) << 4]; if (padding) { *d++ = '='; *d++ = '='; } } else { c1 = s[1]; *d++ = basis[((c0 & 0x03) << 4) | (c1 >> 4)]; *d++ = basis[(c1 & 0x0f) << 2]; if (padding) { *d++ = '='; } } } dst->length = d - dst->start; } static int qjs_base64_encode(JSContext *ctx, const njs_str_t *src, njs_str_t *dst) { qjs_base64_encode_core(dst, src, qjs_basis64_enc, 1); return 0; } static size_t qjs_base64_encode_length(JSContext *ctx, const njs_str_t *src) { return qjs_base64_encoded_length(src->length); } static void qjs_base64_decode_core(njs_str_t *dst, const njs_str_t *src, const u_char *basis) { size_t len; u_char *d, *s; s = src->start; d = dst->start; len = dst->length; while (len >= 3) { *d++ = (u_char) (basis[s[0]] << 2 | basis[s[1]] >> 4); *d++ = (u_char) (basis[s[1]] << 4 | basis[s[2]] >> 2); *d++ = (u_char) (basis[s[2]] << 6 | basis[s[3]]); s += 4; len -= 3; } if (len >= 1) { *d++ = (u_char) (basis[s[0]] << 2 | basis[s[1]] >> 4); } if (len >= 2) { *d++ = (u_char) (basis[s[1]] << 4 | basis[s[2]] >> 2); } } static size_t qjs_base64_decode_length_core(const njs_str_t *src, const u_char *basis) { uint pad; size_t len; for (len = 0; len < src->length; len++) { if (basis[src->start[len]] == 77) { break; } } pad = 0; if (len % 4 != 0) { pad = 4 - (len % 4); len += pad; } return qjs_base64_decoded_length(len, pad); } static int qjs_base64_decode(JSContext *ctx, const njs_str_t *src, njs_str_t *dst) { qjs_base64_decode_core(dst, src, qjs_basis64); return 0; } static size_t qjs_base64_decode_length(JSContext *ctx, const njs_str_t *src) { return qjs_base64_decode_length_core(src, qjs_basis64); } static int qjs_base64url_encode(JSContext *ctx, const njs_str_t *src, njs_str_t *dst) { qjs_base64_encode_core(dst, src, qjs_basis64url_enc, 0); return 0; } static int qjs_base64url_decode(JSContext *ctx, const njs_str_t *src, njs_str_t *dst) { qjs_base64_decode_core(dst, src, qjs_basis64url); return 0; } static size_t qjs_base64url_decode_length(JSContext *ctx, const njs_str_t *src) { return qjs_base64_decode_length_core(src, qjs_basis64url); } njs_inline njs_int_t qjs_char_to_hex(u_char c) { c |= 0x20; /* Values less than '0' become >= 208. */ c = c - '0'; if (c > 9) { /* Values less than 'a' become >= 159. */ c = c - ('a' - '0'); if (njs_slow_path(c > 5)) { return -1; } c += 10; } return c; } static int qjs_hex_decode(JSContext *ctx, const njs_str_t *src, njs_str_t *dst) { u_char *p; size_t len; njs_int_t c; njs_uint_t i, n; const u_char *start; n = 0; p = dst->start; start = src->start; len = src->length; for (i = 0; i < len; i++) { c = qjs_char_to_hex(start[i]); if (njs_slow_path(c < 0)) { break; } n = n * 16 + c; if ((i & 1) != 0) { *p++ = (u_char) n; n = 0; } } dst->length -= (dst->start + dst->length) - p; return 0; } static size_t qjs_hex_decode_length(JSContext *ctx, const njs_str_t *src) { const u_char *p, *end; p = src->start; end = p + src->length; for (; p < end; p++) { if (njs_slow_path(qjs_char_to_hex(*p) < 0)) { break; } } return (p - src->start) / 2; } static int qjs_hex_encode(JSContext *ctx, const njs_str_t *src, njs_str_t *dst) { u_char *p, c; size_t i, len; const u_char *start; static const u_char hex[16] = "0123456789abcdef"; len = src->length; start = src->start; p = dst->start; for (i = 0; i < len; i++) { c = start[i]; *p++ = hex[c >> 4]; *p++ = hex[c & 0x0f]; } return 0; } static size_t qjs_hex_encode_length(JSContext *ctx, const njs_str_t *src) { return src->length * 2; } JSValue qjs_buffer_alloc(JSContext *ctx, size_t size) { JSValue ret, proto, value; value = JS_NewInt64(ctx, size); ret = qjs_new_uint8_array(ctx, 1, &value); if (JS_IsException(ret)) { return ret; } proto = JS_GetClassProto(ctx, QJS_CORE_CLASS_ID_BUFFER); JS_SetPrototype(ctx, ret, proto); JS_FreeValue(ctx, proto); return ret; } JSValue qjs_buffer_create(JSContext *ctx, u_char *start, size_t size) { JSValue buffer, ret; njs_str_t dst; buffer = qjs_buffer_alloc(ctx, size); if (JS_IsException(buffer)) { return buffer; } ret = qjs_typed_array_data(ctx, buffer, &dst); if (JS_IsException(ret)) { return ret; } memcpy(dst.start, start, size); return buffer; } JSValue qjs_buffer_chb_alloc(JSContext *ctx, njs_chb_t *chain) { ssize_t size; JSValue ret; qjs_bytes_t bytes; size = njs_chb_size(chain); if (njs_slow_path(size < 0)) { JS_ThrowOutOfMemory(ctx); return JS_EXCEPTION; } ret = qjs_buffer_alloc(ctx, size); if (JS_IsException(ret)) { return ret; } (void) qjs_to_bytes(ctx, &bytes, ret); njs_chb_join_to(chain, bytes.start); qjs_bytes_free(ctx, &bytes); return ret; } static JSValue qjs_new_uint8_array(JSContext *ctx, int argc, JSValueConst *argv) { JSValue ret; #ifdef NJS_HAVE_QUICKJS_NEW_TYPED_ARRAY ret = JS_NewTypedArray(ctx, argc, argv, JS_TYPED_ARRAY_UINT8); #else JSValue ctor; ctor = JS_GetClassProto(ctx, QJS_CORE_CLASS_ID_UINT8_ARRAY_CTOR); ret = JS_CallConstructor(ctx, ctor, argc, argv); JS_FreeValue(ctx, ctor); #endif return ret; } static int qjs_buffer_builtin_init(JSContext *ctx) { int rc; JSAtom species_atom; JSValue global_obj, buffer, proto, ctor, ta, ta_proto, symbol, species; JSClassID u8_ta_class_id; JS_NewClass(JS_GetRuntime(ctx), QJS_CORE_CLASS_ID_BUFFER, &qjs_buffer_class); global_obj = JS_GetGlobalObject(ctx); proto = JS_NewObject(ctx); JS_SetPropertyFunctionList(ctx, proto, qjs_buffer_proto, njs_nitems(qjs_buffer_proto)); ctor = JS_GetPropertyStr(ctx, global_obj, "Uint8Array"); #ifndef NJS_HAVE_QUICKJS_NEW_TYPED_ARRAY /* * Workaround for absence of JS_NewTypedArray() in QuickJS. * We use JS_SetClassProto()/JS_GetClassProto() as a key-value store * for fast value query by class ID without querying the global object. */ JS_NewClass(JS_GetRuntime(ctx), QJS_CORE_CLASS_ID_UINT8_ARRAY_CTOR, &qjs_uint8_array_ctor_class); JS_SetClassProto(ctx, QJS_CORE_CLASS_ID_UINT8_ARRAY_CTOR, JS_DupValue(ctx, ctor)); #endif ta = JS_CallConstructor(ctx, ctor, 0, NULL); u8_ta_class_id = JS_GetClassID(ta); JS_FreeValue(ctx, ta); JS_FreeValue(ctx, ctor); ta_proto = JS_GetClassProto(ctx, u8_ta_class_id); JS_SetPrototype(ctx, proto, ta_proto); JS_FreeValue(ctx, ta_proto); JS_SetClassProto(ctx, QJS_CORE_CLASS_ID_BUFFER, proto); buffer = JS_NewCFunction2(ctx, qjs_buffer, "Buffer", 3, JS_CFUNC_constructor, 0); if (JS_IsException(buffer)) { return -1; } JS_SetConstructor(ctx, buffer, proto); JS_SetPropertyFunctionList(ctx, buffer, qjs_buffer_props, njs_nitems(qjs_buffer_props)); symbol = JS_GetPropertyStr(ctx, global_obj, "Symbol"); species = JS_GetPropertyStr(ctx, symbol, "species"); JS_FreeValue(ctx, symbol); species_atom = JS_ValueToAtom(ctx, species); JS_FreeValue(ctx, species); ctor = JS_NewCFunction2(ctx, qjs_buffer_ctor, "Buffer species ctor", 3, JS_CFUNC_constructor, 0); JS_SetProperty(ctx, buffer, species_atom, ctor); JS_FreeAtom(ctx, species_atom); rc = JS_SetPropertyStr(ctx, global_obj, "Buffer", buffer); if (rc == -1) { return -1; } JS_FreeValue(ctx, global_obj); return 0; } static int qjs_buffer_module_init(JSContext *ctx, JSModuleDef *m) { int rc; JSValue proto; proto = JS_NewObject(ctx); JS_SetPropertyFunctionList(ctx, proto, qjs_buffer_export, njs_nitems(qjs_buffer_export)); rc = JS_SetModuleExport(ctx, m, "default", proto); if (rc != 0) { return -1; } return JS_SetModuleExportList(ctx, m, qjs_buffer_export, njs_nitems(qjs_buffer_export)); } static JSModuleDef * qjs_buffer_init(JSContext *ctx, const char *name) { int rc; JSModuleDef *m; qjs_buffer_builtin_init(ctx); m = JS_NewCModule(ctx, name, qjs_buffer_module_init); if (m == NULL) { return NULL; } JS_AddModuleExport(ctx, m, "default"); rc = JS_AddModuleExportList(ctx, m, qjs_buffer_export, njs_nitems(qjs_buffer_export)); if (rc != 0) { return NULL; } return m; } njs-0.8.9/src/test/000077500000000000000000000000001474132077100140765ustar00rootroot00000000000000njs-0.8.9/src/test/lvlhsh_unit_test.c000066400000000000000000000104721474132077100176440ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include static njs_int_t lvlhsh_unit_test_key_test(njs_lvlhsh_query_t *lhq, void *data) { if (*(uintptr_t *) lhq->key.start == (uintptr_t) data) { return NJS_OK; } return NJS_DECLINED; } static void * lvlhsh_unit_test_pool_alloc(void *pool, size_t size) { return njs_mp_align(pool, NJS_MAX_ALIGNMENT, size); } static void lvlhsh_unit_test_pool_free(void *pool, void *p, size_t size) { njs_mp_free(pool, p); } static const njs_lvlhsh_proto_t lvlhsh_proto njs_aligned(64) = { NJS_LVLHSH_LARGE_SLAB, lvlhsh_unit_test_key_test, lvlhsh_unit_test_pool_alloc, lvlhsh_unit_test_pool_free, }; static njs_int_t lvlhsh_unit_test_add(njs_lvlhsh_t *lh, const njs_lvlhsh_proto_t *proto, void *pool, uintptr_t key) { njs_lvlhsh_query_t lhq; lhq.key_hash = key; lhq.replace = 0; lhq.key.length = sizeof(uintptr_t); lhq.key.start = (u_char *) &key; lhq.value = (void *) key; lhq.proto = proto; lhq.pool = pool; switch (njs_lvlhsh_insert(lh, &lhq)) { case NJS_OK: return NJS_OK; case NJS_DECLINED: njs_printf("lvlhsh unit test failed: key %08Xl is already in hash\n", (long) key); /* Fall through. */ default: return NJS_ERROR; } } static njs_int_t lvlhsh_unit_test_get(njs_lvlhsh_t *lh, const njs_lvlhsh_proto_t *proto, uintptr_t key) { njs_lvlhsh_query_t lhq; lhq.key_hash = key; lhq.key.length = sizeof(uintptr_t); lhq.key.start = (u_char *) &key; lhq.proto = proto; if (njs_lvlhsh_find(lh, &lhq) == NJS_OK) { if (key == (uintptr_t) lhq.value) { return NJS_OK; } } njs_printf("lvlhsh unit test failed: key %08Xl not found in hash\n", (long) key); return NJS_ERROR; } static njs_int_t lvlhsh_unit_test_delete(njs_lvlhsh_t *lh, const njs_lvlhsh_proto_t *proto, void *pool, uintptr_t key) { njs_int_t ret; njs_lvlhsh_query_t lhq; lhq.key_hash = key; lhq.key.length = sizeof(uintptr_t); lhq.key.start = (u_char *) &key; lhq.proto = proto; lhq.pool = pool; ret = njs_lvlhsh_delete(lh, &lhq); if (ret != NJS_OK) { njs_printf("lvlhsh unit test failed: key %08lX not found in hash\n", (long) key); } return ret; } static njs_int_t lvlhsh_unit_test(njs_uint_t n) { njs_mp_t *pool; uint32_t key; njs_uint_t i; njs_lvlhsh_t lh; njs_lvlhsh_each_t lhe; 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 = njs_mp_create(cluster_size, page_alignment, page_size, min_chunk_size); if (pool == NULL) { return NJS_ERROR; } njs_printf("lvlhsh unit test started: %l items\n", (long) n); njs_memzero(&lh, sizeof(njs_lvlhsh_t)); key = 0; for (i = 0; i < n; i++) { key = njs_murmur_hash2(&key, sizeof(uint32_t)); if (lvlhsh_unit_test_add(&lh, &lvlhsh_proto, pool, key) != NJS_OK) { njs_printf("lvlhsh add unit test failed at %l\n", (long) i); return NJS_ERROR; } } key = 0; for (i = 0; i < n; i++) { key = njs_murmur_hash2(&key, sizeof(uint32_t)); if (lvlhsh_unit_test_get(&lh, &lvlhsh_proto, key) != NJS_OK) { return NJS_ERROR; } } njs_lvlhsh_each_init(&lhe, &lvlhsh_proto); for (i = 0; i < n + 1; i++) { if (njs_lvlhsh_each(&lh, &lhe) == NULL) { break; } } if (i != n) { njs_printf("lvlhsh each unit test failed at %l of %l\n", (long) i, (long) n); return NJS_ERROR; } key = 0; for (i = 0; i < n; i++) { key = njs_murmur_hash2(&key, sizeof(uint32_t)); if (lvlhsh_unit_test_delete(&lh, &lvlhsh_proto, pool, key) != NJS_OK) { return NJS_ERROR; } } if (!njs_mp_is_empty(pool)) { njs_printf("mem cache pool is not empty\n"); return NJS_ERROR; } njs_mp_destroy(pool); njs_printf("lvlhsh unit test passed\n"); return NJS_OK; } int main(void) { return lvlhsh_unit_test(1000 * 1000); } njs-0.8.9/src/test/njs_benchmark.c000066400000000000000000000467161474132077100170640ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include #include #include "njs_externals_test.h" #include #include #include #include typedef struct { const char *name; njs_str_t script; njs_str_t result; njs_uint_t repeat; } njs_benchmark_test_t; typedef struct { uint8_t dump_report; const char *prefix; const char *previous; } njs_opts_t; static njs_int_t njs_benchmark_preinit(njs_vm_t *vm); static njs_int_t njs_benchmark_init(njs_vm_t *vm); static njs_int_t njs_benchmark_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_external_t njs_benchmark_external[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "Benchmark", } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("string"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_benchmark_string, } }, }; njs_module_t njs_benchmark_module = { .name = njs_str("benchmark"), .preinit = njs_benchmark_preinit, .init = njs_benchmark_init, }; njs_module_t *njs_benchmark_addon_external_modules[] = { &njs_unit_test_external_module, &njs_benchmark_module, NULL }; static njs_int_t njs_benchmark_proto_id; static uint64_t njs_time(void) { #if (NJS_HAVE_CLOCK_MONOTONIC) struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (uint64_t) ts.tv_sec * 1000000000 + ts.tv_nsec; #else struct timeval tv; gettimeofday(&tv, NULL); return (uint64_t) tv.tv_sec * 1000000000 + tv.tv_usec * 1000; #endif } static njs_int_t njs_benchmark_test(njs_vm_t *parent, njs_opts_t *opts, njs_value_t *report, njs_benchmark_test_t *test) { u_char *start; njs_vm_t *vm, *nvm; uint64_t ns; njs_int_t ret; njs_str_t s, *expected; njs_uint_t i, n; njs_bool_t success; njs_value_t *result; njs_vm_opt_t options; njs_opaque_value_t retval, name, usec, times; static const njs_str_t name_key = njs_str("name"); static const njs_str_t usec_key = njs_str("usec"); static const njs_str_t times_key = njs_str("times"); njs_vm_opt_init(&options); options.backtrace = 1; options.addons = njs_benchmark_addon_external_modules; vm = NULL; nvm = NULL; ret = NJS_ERROR; vm = njs_vm_create(&options); if (vm == NULL) { njs_printf("njs_vm_create() failed\n"); goto done; } start = test->script.start; ret = njs_vm_compile(vm, &start, start + test->script.length); if (ret != NJS_OK) { njs_printf("njs_vm_compile() failed\n"); goto done; } n = test->repeat; expected = &test->result; ret = NJS_ERROR; ns = njs_time(); for (i = 0; i < n; i++) { nvm = njs_vm_clone(vm, NULL); if (nvm == NULL) { njs_printf("njs_vm_clone() failed\n"); goto done; } (void) njs_vm_start(nvm, njs_value_arg(&retval)); if (njs_vm_value_string(nvm, &s, njs_value_arg(&retval)) != NJS_OK) { njs_printf("njs_vm_value_string() failed\n"); goto done; } success = njs_strstr_eq(expected, &s); if (!success) { njs_printf("%s failed: \"%V\" vs \"%V\"\n", test->name, expected, &s); goto done; } njs_vm_destroy(nvm); nvm = NULL; } ns = njs_time() - ns; if (!opts->dump_report) { if (n == 1) { njs_printf("%s%s: %.3fs\n", opts->previous ? " " : "", test->name, (double) ns / 1000000000); } else { njs_printf("%s%s: %.3fµs, %d times/s\n", opts->previous ? " " : "", test->name, (double) ns / n / 1000, (int) ((uint64_t) n * 1000000000 / ns)); } } result = njs_vm_array_push(parent, report); if (result == NULL) { njs_printf("njs_vm_array_push() failed\n"); goto done; } ret = njs_vm_value_string_create(parent, njs_value_arg(&name), (u_char *) test->name, njs_strlen(test->name)); if (ret != NJS_OK) { njs_printf("njs_vm_value_string_create() failed\n"); goto done; } njs_value_number_set(njs_value_arg(&usec), 1000 * ns); njs_value_number_set(njs_value_arg(×), n); ret = njs_vm_object_alloc(parent, result, NULL); if (ret != NJS_OK) { njs_printf("njs_vm_object_alloc() failed\n"); goto done; } ret = njs_vm_object_prop_set(parent, result, &name_key, &name); if (ret != NJS_OK) { njs_printf("njs_vm_object_prop_set() failed\n"); goto done; } ret = njs_vm_object_prop_set(parent, result, &usec_key, &usec); if (ret != NJS_OK) { njs_printf("njs_vm_object_prop_set() failed\n"); goto done; } ret = njs_vm_object_prop_set(parent, result, ×_key, ×); if (ret != NJS_OK) { njs_printf("njs_vm_object_prop_set() failed\n"); goto done; } ret = NJS_OK; done: if (nvm != NULL) { njs_vm_destroy(nvm); } if (vm != NULL) { njs_vm_destroy(vm); } return ret; } static njs_benchmark_test_t njs_test[] = { { "nJSVM clone/destroy", njs_str("null"), njs_str("null"), 1000000 }, { "func call", njs_str("function test(a) { return 1 }" "" "test(1);" "test(1);" "test(1);" "test(1);"), njs_str("1"), 100000 }, { "func call (3 local functions)", njs_str("function test(a) { " " function g(x) {}" " function h(x) {}" " function f(x) {}" " return 1;" "}" "" "test(1);" "test(1);" "test(1);" "test(1);"), njs_str("1"), 100000 }, { "closure var global", njs_str("function test(a) { sum++ }" "" "var sum = 0;" "" "test(1);" "test(1);" "test(1);" "test(1);" "sum"), njs_str("4"), 100000 }, { "string create 'abcdefABCDEF'", njs_str("benchmark.string('create', 'abcdef', 1000000)"), njs_str("undefined"), 1 }, { "string create 'АБВГДЕ'", njs_str("benchmark.string('create', 'АБВГДЕ', 1000000)"), njs_str("undefined"), 1 }, { "string create 'x'.repeat(24)", njs_str("benchmark.string('create', 'x'.repeat(32), 1000000)"), njs_str("undefined"), 1 }, { "string create 'Д'.repeat(12)", njs_str("benchmark.string('create', 'А'.repeat(16), 1000000)"), njs_str("undefined"), 1 }, { "string create chb 'x'.repeat(256)", njs_str("benchmark.string('chb', 'x'.repeat(256), 10000)"), njs_str("undefined"), 1 }, { "string create chb 'Д'.repeat(128)", njs_str("benchmark.string('chb', 'Д'.repeat(128), 10000)"), njs_str("undefined"), 1 }, { "string create chb 'x'.repeat(128) + 'Д'.repeat(64)", njs_str("benchmark.string('chb', 'x'.repeat(128) + 'Д'.repeat(64), 10000)"), njs_str("undefined"), 1 }, { "JSON.parse", njs_str("JSON.parse('{\"a\":123, \"XXX\":[3,4,null]}').a"), njs_str("123"), 1000000 }, { "JSON.parse large", njs_str("JSON.parse(JSON.stringify([Array(2**16)]))[0].length"), njs_str("65536"), 10 }, { "JSON.parse reviver large", njs_str("JSON.parse(JSON.stringify([Array(2**16)]), v=>v)"), njs_str(""), 10 }, { "for loop 100M", njs_str("var i; for (i = 0; i < 100000000; i++); i"), njs_str("100000000"), 1 }, { "for let loop 100M", njs_str("let i; for (i = 0; i < 100000000; i++); i"), njs_str("100000000"), 1 }, { "for let closures 1M", njs_str("let a = []; for (let i = 0; i < 1000000; i++) { a.push(() => i); }" "a[5]()"), njs_str("5"), 1 }, { "while loop 100M", njs_str("var i = 0; while (i < 100000000) { i++ }; i"), njs_str("100000000"), 1 }, { "fibobench numbers", njs_str("function fibo(n) {" " if (n > 1)" " return fibo(n - 1) + fibo(n - 2);" " return 1" "}" "fibo(32)"), njs_str("3524578"), 1 }, { "fibobench ascii strings", njs_str("function fibo(n) {" " if (n > 1)" " return fibo(n - 1) + fibo(n - 2);" " return '.'" "}" "fibo(32).length"), njs_str("3524578"), 1 }, { "fibobench utf8 strings", njs_str("function fibo(n) {" " if (n > 1)" " return fibo(n - 1) + fibo(n - 2);" " return 'α'" "}" "fibo(32).length"), njs_str("3524578"), 1 }, { "array 64k keys", njs_str("var arr = new Array(2**16);" "arr.fill(1);" "Object.keys(arr)[0]"), njs_str("0"), 10 }, { "array 64k values", njs_str("var arr = new Array(2**16);" "arr.fill(1);" "Object.values(arr)[0]"), njs_str("1"), 10 }, { "array 64k entries", njs_str("var arr = new Array(2**16);" "arr.fill(1);" "Object.entries(arr)[0][0]"), njs_str("0"), 10 }, { "array 1M", njs_str("var arr = new Array(1000000);" "var count = 0, length = arr.length;" "arr.fill(2);" "for (var i = 0; i < length; i++) { count += arr[i]; }" "count"), njs_str("2000000"), 1 }, { "typed array 10M", njs_str("var arr = new Uint8Array(10**7);" "var count = 0, length = arr.length;" "arr.fill(2);" "for (var i = 0; i < length; i++) { count += arr[i]; }" "count"), njs_str("20000000"), 1 }, { "typed array 10M set", njs_str("var arr = new Uint32Array(10**7);" "var length = arr.length;" "for (var i = 0; i < length; i++) { arr[i] = i; }"), njs_str("undefined"), 1 }, { "regexp split", njs_str("var s = Array(26).fill(0).map((v,i)=> {" " var u = String.fromCodePoint(65+i), l = u.toLowerCase(); return u+l+l;}).join('');" "s.split(/(?=[A-Z])/).length"), njs_str("26"), 100 }, { "regexp 10K split", njs_str("'a '.repeat(10000).split(/ /).length"), njs_str("10001"), 1 }, { "simple 100K split", njs_str("'a '.repeat(100000).split(' ').length"), njs_str("100001"), 1 }, { "external property ($shared.uri)", njs_str("$shared.uri"), njs_str("shared"), 1000 }, { "external object property ($shared.props.a)", njs_str("$shared.props.a"), njs_str("11"), 1000 }, { "external dump (JSON.stringify($shared.header))", njs_str("JSON.stringify($shared.header)"), njs_str("{\"01\":\"01|АБВ\",\"02\":\"02|АБВ\",\"03\":\"03|АБВ\"}"), 1000 }, { "external method ($shared.method('YES'))", njs_str("$shared.method('YES')"), njs_str("shared"), 1000 }, { "exception", njs_str("function f() { try { throw new Error('test') } catch (e) { return e.message } } [f].map(v=>v())[0]"), njs_str("test"), 10000 }, { "exception.stack", njs_str("function f() { try { throw new Error('test') } catch (e) { return e.stack } } [f].map(v=>v())[0]"), njs_str("Error: test\n at f (:1)\n at anonymous (:1)\n at Array.prototype.map (native)\n at main (:1)\n"), 100 }, }; static njs_str_t code = njs_str( "import fs from 'fs';" "" "function compare(prev_fn, current) {" " var prev_report = JSON.parse(fs.readFileSync(prev_fn));" " var test, prev, diff, result = [`Diff with ${prev_fn}:`];" " for (var t in current) {" " test = current[t];" " prev = find(prev_report, test.name);" " diff = (test.usec - prev.usec) / prev.usec * 100;" " result.push(` ${test.name}: ${diff.toFixed(2)}%`);" " }" " return result.join('\\n') + '\\n';" "}" "" "function find(report, name) {" " for (var t in report) {" " if (report[t].name == name) { return report[t];}" " }" "}"); int njs_cdecl main(int argc, char **argv) { char *p; u_char *start; njs_vm_t *vm; njs_int_t ret, k; njs_str_t out; njs_uint_t i; njs_opts_t opts; njs_vm_opt_t options; njs_opaque_value_t args[2], report, retval; njs_benchmark_test_t *test; static const char help[] = "njs benchmark.\n" "\n" "njs_benchmark [OPTIONS]" "\n" "Options:\n" " -b specify the benchmarks to execute.\n" " -d dump report as a JSON file.\n" " -c compare with previous report.\n" " -h this help.\n"; static const njs_str_t compare = njs_str("compare"); njs_memzero(&opts, sizeof(njs_opts_t)); opts.prefix = ""; for (k = 1; k < argc; k++) { p = argv[k]; if (p[0] != '-') { goto invalid_options; } p++; switch (*p) { case '?': case 'h': njs_print(help, njs_length(help)); return EXIT_SUCCESS; case 'b': if (++k < argc) { opts.prefix = argv[k]; break; } njs_stderror("option \"-b\" requires argument\n"); return EXIT_FAILURE; case 'c': if (++k < argc) { opts.previous = argv[k]; break; } njs_stderror("option \"-c\" requires argument\n"); return EXIT_FAILURE; case 'd': opts.dump_report = 1; break; default: goto invalid_options; } } njs_vm_opt_init(&options); options.init = 1; options.argv = argv; options.argc = argc; vm = njs_vm_create(&options); if (vm == NULL) { njs_printf("njs_vm_create() failed\n"); return EXIT_FAILURE; } start = code.start; ret = njs_vm_compile(vm, &start, start + code.length); if (ret != NJS_OK) { njs_printf("njs_vm_compile() failed\n"); goto done; } njs_vm_start(vm, njs_value_arg(&retval)); ret = njs_vm_array_alloc(vm, njs_value_arg(&report), 8); if (ret != NJS_OK) { njs_printf("njs_vm_array_alloc() failed\n"); goto done; } if (opts.previous) { njs_printf("Current:\n"); } for (i = 0; i < njs_nitems(njs_test); i++) { test = &njs_test[i]; if (strncmp(test->name, opts.prefix, njs_min(strlen(test->name), strlen(opts.prefix))) == 0) { ret = njs_benchmark_test(vm, &opts, njs_value_arg(&report), test); if (ret != NJS_OK) { goto done; } } } if (opts.previous) { ret = njs_vm_value_string_create(vm, njs_value_arg(&args[0]), (u_char *) opts.previous, njs_strlen(opts.previous)); if (ret != NJS_OK) { njs_printf("njs_vm_value_string_create() failed\n"); goto done; } njs_value_assign(&args[1], &report); njs_vm_invoke(vm, njs_vm_function(vm, &compare), njs_value_arg(&args), 2, njs_value_arg(&retval)); ret = njs_vm_value_dump(vm, &out, njs_value_arg(&retval), 1, 1); if (ret != NJS_OK) { njs_printf("njs_vm_value_dump() failed\n"); goto done; } njs_print(out.start, out.length); return EXIT_SUCCESS; } if (opts.dump_report) { ret = njs_vm_json_stringify(vm, njs_value_arg(&report), 1, njs_value_arg(&retval)); if (ret != NJS_OK) { njs_printf("njs_vm_json_stringify() failed\n"); goto done; } ret = njs_vm_value_dump(vm, &out, njs_value_arg(&retval), 1, 1); if (ret != NJS_OK) { njs_printf("njs_vm_value_dump() failed\n"); goto done; } njs_print(out.start, out.length); } ret = EXIT_SUCCESS; done: njs_vm_destroy(vm); return ret; invalid_options: njs_stderror("Unknown argument: \"%s\" " "try \"%s -h\" for available options\n", argv[k], argv[0]); return EXIT_FAILURE; } static njs_int_t njs_benchmark_preinit(njs_vm_t *vm) { njs_benchmark_proto_id = njs_vm_external_prototype(vm, njs_benchmark_external, njs_nitems(njs_benchmark_external)); if (njs_slow_path(njs_benchmark_proto_id < 0)) { njs_printf("njs_vm_external_prototype() failed\n"); return NJS_ERROR; } return NJS_OK; } static njs_int_t njs_benchmark_init(njs_vm_t *vm) { njs_int_t ret; njs_opaque_value_t value; static const njs_str_t benchmark = njs_str("benchmark"); ret = njs_vm_external_create(vm, njs_value_arg(&value), njs_benchmark_proto_id, NULL, 1); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_vm_bind(vm, &benchmark, njs_value_arg(&value), 1); if (njs_slow_path(ret != NJS_OK)) { njs_printf("njs_vm_bind() failed\n"); return NJS_ERROR; } return NJS_OK; } static njs_int_t njs_benchmark_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { int64_t i, n; njs_chb_t chain; njs_str_t s, mode; njs_opaque_value_t value; njs_value_string_get(njs_arg(args, nargs, 1), &mode); njs_value_string_get(njs_arg(args, nargs, 2), &s); if (njs_value_to_integer(vm, njs_arg(args, nargs, 3), &n) != NJS_OK) { return NJS_ERROR; } if (memcmp(mode.start, "create", 6) == 0) { for (i = 0; i < n; i++) { njs_vm_value_string_create(vm, njs_value_arg(&value), s.start, s.length); } } else if (memcmp(mode.start, "chb", 3) == 0) { NJS_CHB_MP_INIT(&chain, vm); njs_chb_append_literal(&chain, "abc"); njs_chb_append(&chain, s.start, s.length); njs_chb_append_literal(&chain, "abc"); njs_chb_append(&chain, s.start, s.length); njs_chb_append_literal(&chain, "abc"); njs_chb_append(&chain, s.start, s.length); for (i = 0; i < n; i++) { njs_vm_value_string_create_chb(vm, njs_value_arg(&value), &chain); } njs_chb_destroy(&chain); } else { njs_vm_type_error(vm, "unknown mode \"%V\"", &mode); return NJS_ERROR; } njs_value_undefined_set(retval); return NJS_OK; } njs-0.8.9/src/test/njs_externals_test.c000066400000000000000000000775321474132077100201760ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #include #include #include #include #include "njs_externals_test.h" typedef struct { njs_lvlhsh_t hash; uint32_t a; uint32_t d; njs_str_t uri; njs_opaque_value_t value; } njs_unit_test_req_t; typedef struct { njs_str_t name; njs_opaque_value_t value; } njs_unit_test_prop_t; static njs_int_t njs_externals_262_init(njs_vm_t *vm); static njs_int_t njs_externals_shared_preinit(njs_vm_t *vm); static njs_int_t njs_externals_shared_init(njs_vm_t *vm); njs_int_t njs_array_buffer_detach(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_external_r_proto_id; static njs_int_t njs_external_null_proto_id; static njs_int_t njs_external_error_ctor_id; njs_module_t njs_unit_test_262_module = { .name = njs_str("$262"), .preinit = NULL, .init = njs_externals_262_init, }; njs_module_t njs_unit_test_external_module = { .name = njs_str("external"), .preinit = njs_externals_shared_preinit, .init = njs_externals_shared_init, }; static njs_int_t lvlhsh_unit_test_key_test(njs_lvlhsh_query_t *lhq, void *data) { njs_str_t name; njs_unit_test_prop_t *prop; prop = data; name = prop->name; if (name.length != lhq->key.length) { return NJS_DECLINED; } if (memcmp(name.start, lhq->key.start, lhq->key.length) == 0) { return NJS_OK; } return NJS_DECLINED; } static void * lvlhsh_unit_test_pool_alloc(void *pool, size_t size) { return njs_mp_align(pool, NJS_MAX_ALIGNMENT, size); } static void lvlhsh_unit_test_pool_free(void *pool, void *p, size_t size) { njs_mp_free(pool, p); } static const njs_lvlhsh_proto_t lvlhsh_proto njs_aligned(64) = { NJS_LVLHSH_LARGE_SLAB, lvlhsh_unit_test_key_test, lvlhsh_unit_test_pool_alloc, lvlhsh_unit_test_pool_free, }; static njs_unit_test_prop_t * lvlhsh_unit_test_alloc(njs_mp_t *pool, const njs_str_t *name, const njs_value_t *value) { njs_unit_test_prop_t *prop; prop = njs_mp_alloc(pool, sizeof(njs_unit_test_prop_t) + name->length); if (prop == NULL) { return NULL; } prop->name.length = name->length; prop->name.start = (u_char *) prop + sizeof(njs_unit_test_prop_t); memcpy(prop->name.start, name->start, name->length); njs_value_assign(&prop->value, value); return prop; } static njs_int_t lvlhsh_unit_test_add(njs_mp_t *pool, njs_unit_test_req_t *r, njs_unit_test_prop_t *prop) { njs_lvlhsh_query_t lhq; lhq.key = prop->name; lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length); lhq.replace = 1; lhq.value = (void *) prop; lhq.proto = &lvlhsh_proto; lhq.pool = pool; switch (njs_lvlhsh_insert(&r->hash, &lhq)) { case NJS_OK: return NJS_OK; case NJS_DECLINED: default: return NJS_ERROR; } } static njs_int_t njs_unit_test_r_uri(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { char *p; njs_str_t *field; p = njs_vm_external(vm, njs_external_r_proto_id, value); if (p == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } field = (njs_str_t *) (p + njs_vm_prop_magic32(prop)); if (setval != NULL) { return njs_vm_value_to_bytes(vm, field, setval); } return njs_vm_value_string_create(vm, retval, field->start, field->length); } static njs_int_t njs_unit_test_r_a(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *unused, njs_value_t *retval) { u_char *p; njs_unit_test_req_t *r; u_char buf[16]; r = njs_vm_external(vm, njs_external_r_proto_id, value); if (r == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } p = njs_sprintf(buf, buf + njs_length(buf), "%uD", r->a); return njs_vm_value_string_create(vm, retval, buf, p - buf); } static njs_int_t njs_unit_test_r_b(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *unused, njs_value_t *retval) { njs_value_number_set(retval, njs_vm_prop_magic32(prop)); return NJS_OK; } static njs_int_t njs_unit_test_r_d(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *unused, njs_value_t *retval) { njs_unit_test_req_t *r; r = njs_vm_external(vm, njs_external_r_proto_id, value); if (r == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } njs_value_number_set(retval, r->d); return NJS_OK; } static njs_int_t njs_unit_test_r_host(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { return njs_vm_value_string_create(vm, retval, (u_char *) "АБВГДЕЁЖЗИЙ", 22); } static njs_int_t njs_unit_test_r_buffer(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { return njs_vm_value_buffer_set(vm, retval, (u_char *) "АБВГДЕЁЖЗИЙ", 22); } static njs_int_t njs_unit_test_r_vars(njs_vm_t *vm, njs_object_prop_t *self, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { njs_int_t ret; njs_lvlhsh_query_t lhq; njs_unit_test_req_t *r; njs_unit_test_prop_t *prop; r = njs_vm_external(vm, njs_external_r_proto_id, value); if (r == NULL) { njs_value_undefined_set(retval); return NJS_DECLINED; } ret = njs_vm_prop_name(vm, self, &lhq.key); if (ret != NJS_OK) { if (setval == NULL && retval != NULL) { /* Get. */ njs_value_undefined_set(retval); return NJS_DECLINED; } return NJS_ERROR; } if (setval != NULL || retval == NULL) { /* Set or Delete. */ if (lhq.key.length == 5 && memcmp(lhq.key.start, "error", 5) == 0) { njs_vm_error(vm, "cannot %s \"error\" prop", retval != NULL ? "set" : "delete"); return NJS_ERROR; } } if (setval != NULL) { /* Set. */ prop = lvlhsh_unit_test_alloc(njs_vm_memory_pool(vm), &lhq.key, setval); if (prop == NULL) { njs_vm_memory_error(vm); return NJS_ERROR; } ret = lvlhsh_unit_test_add(njs_vm_memory_pool(vm), r, prop); if (ret != NJS_OK) { njs_vm_error(vm, "lvlhsh_unit_test_add() failed"); return NJS_ERROR; } return NJS_OK; } /* Get or Delete. */ lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length); lhq.proto = &lvlhsh_proto; ret = njs_lvlhsh_find(&r->hash, &lhq); prop = lhq.value; if (ret == NJS_OK) { if (retval == NULL) { njs_value_invalid_set(njs_value_arg(&prop->value)); return NJS_OK; } if (njs_value_is_valid(njs_value_arg(&prop->value))) { njs_value_assign(retval, njs_value_arg(&prop->value)); return NJS_OK; } } if (retval != NULL) { njs_value_undefined_set(retval); } return NJS_DECLINED; } static njs_int_t njs_unit_test_r_header(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *unused, njs_value_t *retval) { njs_int_t ret; njs_str_t h; njs_chb_t chain; ret = njs_vm_prop_name(vm, prop, &h); if (ret == NJS_OK) { NJS_CHB_MP_INIT(&chain, vm); njs_chb_append(&chain, h.start, h.length); njs_chb_append(&chain, (u_char *) "|АБВ", njs_length("|АБВ")); ret = njs_vm_value_string_create_chb(vm, retval, &chain); njs_chb_destroy(&chain); return ret; } njs_value_undefined_set(retval); return NJS_DECLINED; } static njs_int_t njs_unit_test_r_header_keys(njs_vm_t *vm, njs_value_t *value, njs_value_t *keys) { njs_int_t ret, i; njs_value_t *push; u_char k[2]; ret = njs_vm_array_alloc(vm, keys, 4); if (ret != NJS_OK) { return NJS_ERROR; } k[0] = '0'; k[1] = '1'; for (i = 0; i < 3; i++) { push = njs_vm_array_push(vm, keys); if (push == NULL) { return NJS_ERROR; } (void) njs_vm_value_string_create(vm, push, k, 2); k[1]++; } return NJS_OK; } static njs_int_t njs_unit_test_r_method(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_str_t s; njs_unit_test_req_t *r; r = njs_vm_external(vm, njs_external_r_proto_id, njs_argument(args, 0)); if (r == NULL) { njs_vm_type_error(vm, "\"this\" is not an external"); return NJS_ERROR; } ret = njs_vm_value_to_bytes(vm, &s, njs_arg(args, nargs, 1)); if (ret == NJS_OK && s.length == 3 && memcmp(s.start, "YES", 3) == 0) { return njs_vm_value_string_create(vm, retval, r->uri.start, r->uri.length); } njs_value_undefined_set(retval); return NJS_OK; } static njs_int_t njs_unit_test_promise_trampoline(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_function_t *callback; callback = njs_value_function(njs_argument(args, 1)); if (callback != NULL) { return njs_vm_invoke(vm, callback, njs_argument(args, 2), 1, retval); } return NJS_OK; } static njs_int_t njs_unit_test_r_subrequest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_value_t *argument, *select; njs_function_t *callback; njs_external_ev_t *ev; njs_external_env_t *env; njs_opaque_value_t value; njs_unit_test_req_t *r; r = njs_vm_external(vm, njs_external_r_proto_id, njs_argument(args, 0)); if (r == NULL) { njs_vm_type_error(vm, "\"this\" is not an external"); return NJS_ERROR; } ev = njs_mp_alloc(njs_vm_memory_pool(vm), sizeof(njs_external_ev_t)); if (ev == NULL) { njs_vm_memory_error(vm); return NJS_ERROR; } ret = njs_vm_promise_create(vm, njs_value_arg(&value), njs_value_arg(&ev->callbacks[0])); if (ret != NJS_OK) { return NJS_ERROR; } callback = njs_vm_function_alloc(vm, njs_unit_test_promise_trampoline, 0, 0); if (callback == NULL) { return NJS_ERROR; } argument = njs_arg(args, nargs, 1); select = njs_arg(args, nargs, 2); ev->function = callback; ev->data = r; ev->nargs = 2; njs_value_assign(&ev->args[0], &ev->callbacks[!!njs_value_bool(select)]); njs_value_assign(&ev->args[1], argument); env = njs_vm_external_ptr(vm); njs_queue_insert_tail(&env->events, &ev->link); njs_value_assign(retval, &value); return NJS_OK; } static njs_int_t njs_unit_test_r_retval(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_external_env_t *env; env = njs_vm_external_ptr(vm); njs_value_assign(&env->retval, njs_arg(args, nargs, 1)); njs_value_undefined_set(retval); return NJS_OK; } static njs_int_t njs_unit_test_r_custom_exception(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_vm_error3(vm, njs_external_error_ctor_id, "Oops", NULL); return NJS_ERROR; } static njs_int_t njs_unit_test_r_create(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_int_t ret; njs_unit_test_req_t *r, *sr; r = njs_vm_external(vm, njs_external_r_proto_id, njs_argument(args, 0)); if (r == NULL) { njs_vm_type_error(vm, "\"this\" is not an external"); return NJS_ERROR; } sr = njs_mp_zalloc(njs_vm_memory_pool(vm), sizeof(njs_unit_test_req_t)); if (sr == NULL) { goto memory_error; } if (njs_vm_value_to_bytes(vm, &sr->uri, njs_arg(args, nargs, 1)) != NJS_OK) { return NJS_ERROR; } ret = njs_vm_external_create(vm, retval, njs_external_r_proto_id, sr, 0); if (ret != NJS_OK) { return NJS_ERROR; } return NJS_OK; memory_error: njs_vm_memory_error(vm); return NJS_ERROR; } static njs_int_t njs_unit_test_r_bind(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_str_t name; njs_unit_test_req_t *r; r = njs_vm_external(vm, njs_external_r_proto_id, njs_argument(args, 0)); if (r == NULL) { njs_vm_type_error(vm, "\"this\" is not an external"); return NJS_ERROR; } if (njs_vm_value_to_bytes(vm, &name, njs_arg(args, nargs, 1)) != NJS_OK) { return NJS_ERROR; } return njs_vm_bind(vm, &name, njs_arg(args, nargs, 2), 0); } static njs_int_t njs_unit_test_null_get(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double *d; njs_value_t *this; this = njs_argument(args, 0); if (!njs_value_is_external(this, njs_external_null_proto_id)) { njs_vm_type_error(vm, "\"this\" is not a null external"); return NJS_ERROR; } d = njs_value_external(this); if (d == NULL) { njs_value_undefined_set(retval); } else { njs_value_number_set(retval, *d); } return NJS_OK; } static njs_int_t njs_unit_test_null_set(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { double *d; njs_value_t *this; this = njs_argument(args, 0); if (!njs_value_is_external(this, njs_external_null_proto_id)) { njs_vm_type_error(vm, "\"this\" is not a null external"); return NJS_ERROR; } d = njs_value_external(this); if (d == NULL) { d = njs_mp_alloc(njs_vm_memory_pool(vm), sizeof(double)); if (d == NULL) { njs_vm_memory_error(vm); return NJS_ERROR; } } *d = njs_value_number(njs_arg(args, nargs, 1)); njs_value_external_set(this, d); njs_value_undefined_set(retval); return NJS_OK; } static njs_int_t njs_unit_test_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { njs_unit_test_req_t *sr; sr = njs_mp_zalloc(njs_vm_memory_pool(vm), sizeof(njs_unit_test_req_t)); if (sr == NULL) { njs_vm_memory_error(vm); return NJS_ERROR; } if (njs_vm_value_to_bytes(vm, &sr->uri, njs_arg(args, nargs, 1)) != NJS_OK) { return NJS_ERROR; } return njs_vm_external_create(vm, retval, njs_external_r_proto_id, sr, 0); } static njs_int_t njs_unit_test_error_name(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { return njs_vm_value_string_create(vm, retval, (u_char *) "ExternalError", 13); } static njs_int_t njs_unit_test_error_message(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval) { return njs_vm_value_string_create(vm, retval, (u_char *) "", 0); } static njs_external_t njs_unit_test_262_external[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "$262", } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("detachArrayBuffer"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_array_buffer_detach, } }, }; static njs_external_t njs_unit_test_r_c[] = { { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("d"), .enumerable = 1, .u.property = { .handler = njs_unit_test_r_d, } }, }; static njs_external_t njs_unit_test_r_props[] = { { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("a"), .enumerable = 1, .u.property = { .handler = njs_unit_test_r_a, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("b"), .enumerable = 1, .u.property = { .handler = njs_unit_test_r_b, .magic32 = 42, } }, { .flags = NJS_EXTERN_OBJECT, .name.string = njs_str("c"), .enumerable = 1, .u.object = { .properties = njs_unit_test_r_c, .nproperties = njs_nitems(njs_unit_test_r_c), } }, }; static njs_external_t njs_unit_test_r_header_props[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "Header", } }, }; static njs_external_t njs_unit_test_r_header_props2[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "Header2", } }, { .flags = NJS_EXTERN_SELF, .u.object = { .enumerable = 1, .prop_handler = njs_unit_test_r_header, .keys = njs_unit_test_r_header_keys, } }, }; static njs_external_t njs_unit_test_r_external[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "External", } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("bind"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_unit_test_r_bind, } }, { .flags = NJS_EXTERN_OBJECT, .name.string = njs_str("consts"), .writable = 1, .configurable = 1, .enumerable = 1, .u.object = { .enumerable = 1, .prop_handler = njs_unit_test_r_vars, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("create"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_unit_test_r_create, } }, { .flags = NJS_EXTERN_OBJECT, .name.string = njs_str("header"), .writable = 1, .configurable = 1, .enumerable = 1, .u.object = { .properties = njs_unit_test_r_header_props, .nproperties = njs_nitems(njs_unit_test_r_header_props), .enumerable = 1, .prop_handler = njs_unit_test_r_header, .keys = njs_unit_test_r_header_keys, } }, { .flags = NJS_EXTERN_OBJECT, .name.string = njs_str("header2"), .writable = 1, .configurable = 1, .u.object = { .properties = njs_unit_test_r_header_props2, .nproperties = njs_nitems(njs_unit_test_r_header_props2), } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("host"), .enumerable = 1, .u.property = { .handler = njs_unit_test_r_host, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("buffer"), .enumerable = 1, .u.property = { .handler = njs_unit_test_r_buffer, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("method"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_unit_test_r_method, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("subrequest"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_unit_test_r_subrequest, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("retval"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_unit_test_r_retval, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("customException"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_unit_test_r_custom_exception, } }, { .flags = NJS_EXTERN_OBJECT, .name.string = njs_str("props"), .enumerable = 1, .writable = 1, .u.object = { .enumerable = 1, .properties = njs_unit_test_r_props, .nproperties = njs_nitems(njs_unit_test_r_props), } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("uri"), .writable = 1, .enumerable = 1, .u.property = { .handler = njs_unit_test_r_uri, .magic32 = offsetof(njs_unit_test_req_t, uri), } }, { .flags = NJS_EXTERN_OBJECT, .name.string = njs_str("vars"), .enumerable = 1, .u.object = { .writable = 1, .configurable = 1, .enumerable = 1, .prop_handler = njs_unit_test_r_vars, } }, }; static njs_external_t njs_unit_test_null_external[] = { { .flags = NJS_EXTERN_PROPERTY | NJS_EXTERN_SYMBOL, .name.symbol = NJS_SYMBOL_TO_STRING_TAG, .u.property = { .value = "Null", } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("get"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_unit_test_null_get, } }, { .flags = NJS_EXTERN_METHOD, .name.string = njs_str("set"), .writable = 1, .configurable = 1, .enumerable = 1, .u.method = { .native = njs_unit_test_null_set, } }, }; static njs_external_t njs_unit_test_ctor_props[] = { { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("name"), .enumerable = 1, .u.property = { .handler = njs_unit_test_error_name, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("prototype"), .enumerable = 1, .u.property = { .handler = njs_object_prototype_create, } }, }; static njs_external_t njs_unit_test_proto_props[] = { { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("name"), .enumerable = 1, .u.property = { .handler = njs_unit_test_error_name, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("message"), .enumerable = 1, .u.property = { .handler = njs_unit_test_error_message, } }, { .flags = NJS_EXTERN_PROPERTY, .name.string = njs_str("constructor"), .enumerable = 1, .u.property = { .handler = njs_object_prototype_create_constructor, } }, }; typedef struct { njs_str_t name; njs_str_t value; } njs_unit_test_prop_init_t; typedef struct { njs_str_t name; njs_unit_test_req_t request; njs_unit_test_prop_init_t props[2]; } njs_unit_test_req_init_t; static njs_unit_test_req_init_t njs_test_requests[] = { { njs_str("$shared"), { .uri = njs_str("shared"), .a = 11, .d = 13, }, { { njs_str("r"), njs_str("rval") }, { njs_str("r2"), njs_str("r2val") }, } }, { njs_str("$r"), { .uri = njs_str("АБВ"), .a = 1, .d = 1024, }, { { njs_str("p"), njs_str("pval") }, { njs_str("p2"), njs_str("p2val") }, } }, { njs_str("$r2"), { .uri = njs_str("αβγ"), .a = 2, .d = 1025, }, { { njs_str("q"), njs_str("qval") }, { njs_str("q2"), njs_str("q2val") }, } }, { njs_str("$r3"), { .uri = njs_str("abc"), .a = 3, .d = 1026, }, { { njs_str("k"), njs_str("kval") }, { njs_str("k2"), njs_str("k2val") }, } }, }; static njs_int_t njs_externals_init_internal(njs_vm_t *vm, njs_unit_test_req_init_t *init, njs_uint_t n, njs_bool_t shared) { njs_int_t ret; njs_uint_t i, j; njs_opaque_value_t value; njs_unit_test_req_t *requests; njs_unit_test_prop_t *prop; requests = njs_mp_zalloc(njs_vm_memory_pool(vm), n * sizeof(njs_unit_test_req_t)); if (njs_slow_path(requests == NULL)) { return NJS_ERROR; } for (i = 0; i < n; i++) { requests[i] = init[i].request; ret = njs_vm_external_create(vm, njs_value_arg(&requests[i].value), njs_external_r_proto_id, &requests[i], shared); if (njs_slow_path(ret != NJS_OK)) { njs_printf("njs_vm_external_create() failed\n"); return NJS_ERROR; } ret = njs_vm_bind(vm, &init[i].name, njs_value_arg(&requests[i].value), shared); if (njs_slow_path(ret != NJS_OK)) { njs_printf("njs_vm_bind() failed\n"); return NJS_ERROR; } for (j = 0; j < njs_nitems(init[i].props); j++) { ret = njs_vm_value_string_create(vm, njs_value_arg(&value), init[i].props[j].value.start, init[i].props[j].value.length); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } prop = lvlhsh_unit_test_alloc(njs_vm_memory_pool(vm), &init[i].props[j].name, njs_value_arg(&value)); if (njs_slow_path(prop == NULL)) { njs_printf("lvlhsh_unit_test_alloc() failed\n"); return NJS_ERROR; } ret = lvlhsh_unit_test_add(njs_vm_memory_pool(vm), &requests[i], prop); if (njs_slow_path(ret != NJS_OK)) { njs_printf("lvlhsh_unit_test_add() failed\n"); return NJS_ERROR; } } } return NJS_OK; } static njs_int_t njs_externals_262_init(njs_vm_t *vm) { njs_int_t ret, proto_id; njs_opaque_value_t value; static const njs_str_t dollar_262 = njs_str("$262"); proto_id = njs_vm_external_prototype(vm, njs_unit_test_262_external, njs_nitems(njs_unit_test_262_external)); if (njs_slow_path(proto_id < 0)) { njs_printf("njs_vm_external_prototype() failed\n"); return NJS_ERROR; } ret = njs_vm_external_create(vm, njs_value_arg(&value), proto_id, NULL, 1); if (njs_slow_path(ret != NJS_OK)) { njs_printf("njs_vm_external_create() failed\n"); return NJS_ERROR; } ret = njs_vm_bind(vm, &dollar_262, njs_value_arg(&value), 1); if (njs_slow_path(ret != NJS_OK)) { njs_printf("njs_vm_bind() failed\n"); return NJS_ERROR; } return NJS_OK; } static njs_int_t njs_externals_shared_preinit(njs_vm_t *vm) { static const njs_str_t external_error = njs_str("ExternalError"); njs_external_r_proto_id = njs_vm_external_prototype(vm, njs_unit_test_r_external, njs_nitems(njs_unit_test_r_external)); if (njs_slow_path(njs_external_r_proto_id < 0)) { njs_printf("njs_vm_external_prototype() failed\n"); return NJS_ERROR; } njs_external_null_proto_id = njs_vm_external_prototype(vm, njs_unit_test_null_external, njs_nitems(njs_unit_test_null_external)); if (njs_slow_path(njs_external_null_proto_id < 0)) { njs_printf("njs_vm_external_prototype() failed\n"); return NJS_ERROR; } njs_external_error_ctor_id = njs_vm_external_constructor(vm, &external_error, njs_error_constructor, njs_unit_test_ctor_props, njs_nitems(njs_unit_test_ctor_props), njs_unit_test_proto_props, njs_nitems(njs_unit_test_proto_props)); if (njs_slow_path(njs_external_error_ctor_id < 0)) { njs_printf("njs_vm_external_constructor() failed\n"); return NJS_ERROR; } return NJS_OK; } static njs_int_t njs_externals_shared_init(njs_vm_t *vm) { njs_int_t ret; njs_function_t *f; njs_opaque_value_t value; static const njs_str_t external_ctor = njs_str("ExternalConstructor"); static const njs_str_t external_null = njs_str("ExternalNull"); f = njs_vm_function_alloc(vm, njs_unit_test_constructor, 1, 1); if (f == NULL) { njs_printf("njs_vm_function_alloc() failed\n"); return NJS_ERROR; } njs_value_function_set(njs_value_arg(&value), f); ret = njs_vm_bind(vm, &external_ctor, njs_value_arg(&value), 1); if (njs_slow_path(ret != NJS_OK)) { njs_printf("njs_vm_bind() failed\n"); return NJS_ERROR; } ret = njs_vm_external_create(vm, njs_value_arg(&value), njs_external_null_proto_id, NULL, 1); if (njs_slow_path(ret != NJS_OK)) { return NJS_ERROR; } ret = njs_vm_bind(vm, &external_null, njs_value_arg(&value), 1); if (njs_slow_path(ret != NJS_OK)) { njs_printf("njs_vm_bind() failed\n"); return NJS_ERROR; } return njs_externals_init_internal(vm, &njs_test_requests[0], 1, 1); } njs_int_t njs_externals_init(njs_vm_t *vm) { return njs_externals_init_internal(vm, &njs_test_requests[1], 3, 0); } njs_int_t njs_external_env_init(njs_external_env_t *env) { if (env != NULL) { njs_value_invalid_set(njs_value_arg(&env->retval)); njs_queue_init(&env->events); } return NJS_OK; } njs_int_t njs_external_process_events(njs_vm_t *vm, njs_external_env_t *env) { njs_int_t ret; njs_queue_t *events; njs_queue_link_t *link; njs_external_ev_t *ev; if (env == NULL) { return NJS_OK; } events = &env->events; for ( ;; ) { link = njs_queue_first(events); if (link == njs_queue_tail(events)) { break; } ev = njs_queue_link_data(link, njs_external_ev_t, link); njs_queue_remove(&ev->link); ev->link.prev = NULL; ev->link.next = NULL; ret = njs_vm_call(vm, ev->function, njs_value_arg(ev->args), ev->nargs); if (ret == NJS_ERROR) { return NJS_ERROR; } } return njs_vm_pending(vm) ? NJS_AGAIN: NJS_OK; } njs_int_t njs_external_call(njs_vm_t *vm, const njs_str_t *fname, njs_value_t *args, njs_uint_t nargs) { njs_int_t ret; njs_function_t *func; func = njs_vm_function(vm, fname); if (func == NULL) { njs_stderror("njs_external_call(): function \"%V\" not found\n", fname); return NJS_ERROR; } ret = njs_vm_call(vm, func, args, nargs); if (ret == NJS_ERROR) { return NJS_ERROR; } for ( ;; ) { ret = njs_vm_execute_pending_job(vm); if (ret <= NJS_OK) { if (ret == NJS_ERROR) { return NJS_ERROR; } break; } } return NJS_OK; } njs-0.8.9/src/test/njs_externals_test.h000066400000000000000000000017411474132077100201700ustar00rootroot00000000000000 /* * Copyright (C) Dmitry Volyntsev * Copyright (C) NGINX, Inc. */ #ifndef _NJS_EXTERNALS_TEST_H_INCLUDED_ #define _NJS_EXTERNALS_TEST_H_INCLUDED_ typedef struct { njs_opaque_value_t retval; njs_queue_t events; /* of njs_external_ev_t */ } njs_external_env_t; typedef struct { njs_function_t *function; void *data; njs_uint_t nargs; njs_opaque_value_t args[3]; njs_opaque_value_t callbacks[2]; njs_queue_link_t link; } njs_external_ev_t; njs_int_t njs_externals_init(njs_vm_t *vm); njs_int_t njs_external_env_init(njs_external_env_t *env); njs_int_t njs_external_call(njs_vm_t *vm, const njs_str_t *fname, njs_value_t *args, njs_uint_t nargs); njs_int_t njs_external_process_events(njs_vm_t *vm, njs_external_env_t *env); extern njs_module_t njs_unit_test_262_module; extern njs_module_t njs_unit_test_external_module; #endif /* _NJS_EXTERNALS_TEST_H_INCLUDED_ */ njs-0.8.9/src/test/njs_unit_test.c000066400000000000000000027271101474132077100171440ustar00rootroot00000000000000/* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include #include #include #include #include #include #ifndef NJS_HAVE_PCRE2 #include #endif #include "njs_externals_test.h" #define NJS_HAVE_LARGE_STACK (!NJS_HAVE_ADDRESS_SANITIZER && !NJS_HAVE_MEMORY_SANITIZER) #ifdef NJS_HAVE_LITTLE_ENDIAN #define njs_evar(little, big) little #else #define njs_evar(little, big) big #endif #ifdef NJS_HAVE_PCRE2 #define njs_pcre_var(pcre2, pcre) pcre2 #else #define njs_pcre_var(pcre2, pcre) pcre #endif #define njs_declare_sparse_array(nm, sz) \ "var " nm " = Array(" njs_stringify(sz) "); " \ "Object.defineProperty(" nm ", '0'," \ "{writable:true, enumerable:false, configurable:true});" \ "delete " nm "[0];" #define njs_buffer_byte_map(func, sign, divisor) \ "var buf = Buffer.alloc(6);" \ "[1,2,3,4,5,6].map(byte => {" \ " buf." func "(" sign "(2 ** (byte * 7)) / " \ njs_stringify(divisor) ", 0, byte);" \ " return njs.dump(buf);" \ "})" typedef struct { njs_str_t script; njs_str_t ret; } njs_unit_test_t; static njs_unit_test_t njs_test[] = { { njs_str("@"), njs_str("SyntaxError: Unexpected token \"@\" in 1") }, { njs_str("}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("1}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("/***/1/*\n**/"), njs_str("1") }, { njs_str("/***/1// "), njs_str("1") }, { njs_str(">"), njs_str("SyntaxError: Unexpected token \">\" in 1") }, { njs_str(">>"), njs_str("SyntaxError: Unexpected token \">>\" in 1") }, { njs_str(">>>"), njs_str("SyntaxError: Unexpected token \">>>\" in 1") }, { njs_str("=="), njs_str("SyntaxError: Unexpected token \"==\" in 1") }, { njs_str("?"), njs_str("SyntaxError: Unexpected token \"?\" in 1") }, /* Variable declarations. */ { njs_str("var x"), njs_str("undefined") }, { njs_str("var x;"), njs_str("undefined") }, { njs_str("var x;;"), njs_str("undefined") }, { njs_str("var x = 0"), njs_str("undefined") }, { njs_str("var x = 0;"), njs_str("undefined") }, { njs_str("var x = 0;;"), njs_str("undefined") }, { njs_str("var; a"), njs_str("SyntaxError: Unexpected token \";\" in 1") }, { njs_str("var + a"), njs_str("SyntaxError: Unexpected token \"+\" in 1") }, { njs_str("//\r\n; var + a"), njs_str("SyntaxError: Unexpected token \"+\" in 2") }, { njs_str("/*\n*/; var + a"), njs_str("SyntaxError: Unexpected token \"+\" in 2") }, { njs_str("var \n a \n = 1; a"), njs_str("1") }, { njs_str("var \n a, \n b; b"), njs_str("undefined") }, { njs_str("var from = 2; from + 2"), njs_str("4") }, { njs_str("var a / ="), njs_str("SyntaxError: Unexpected token \"/\" in 1") }, { njs_str("var a = 1; var b; a"), njs_str("1") }, { njs_str("a = 1;for(;a;a--)var a; a"), njs_str("0") }, { njs_str("if(1)if(0){0?0:0}else\nvar a\nelse\nvar b"), njs_str("undefined") }, { njs_str("var a = 1; var a; a"), njs_str("1") }, { njs_str("(function (x) {if (x) { var a = 3; return a} else { var a = 4; return a}})(1)"), njs_str("3") }, { njs_str("(function (x) {if (x) { var a = 3; return a} else { var a = 4; return a}})(0)"), njs_str("4") }, { njs_str("function f(){return 2}; var f; f()"), njs_str("2") }, { njs_str("function f(){return 2}; var f = 1; f()"), njs_str("TypeError: number is not a function") }, { njs_str("function f(){return 1}; function f(){return 2}; f()"), njs_str("2") }, { njs_str("var f = 1; function f() {}; f"), njs_str("1") }, { njs_str("var f = 1; function f() {}; f"), njs_str("1") }, { njs_str("function f(a) {return function (x) {return a(x)}} f(1)(0)"), njs_str("TypeError: number is not a function") }, { njs_str("var x = 0;" "" "function f1() {" " function f2() {" " return x;" " };" "" " return f2();" "" " var x = 1;" "}" "" "f1() === undefined"), njs_str("true") }, { njs_str("var fn = function fn() {return fn.test}; fn.test = 'test'; fn()"), njs_str("test") }, { njs_str("var body;" "var n = 'outside';" "var before = function() {return n};" "" "var func = function n() {" " var n;" " body = function() {return n};" "};" "" "func();" "" "[before(), body()]"), njs_str("outside,") }, { njs_str("var func = function x(x) {return x}; func()"), njs_str("undefined") }, { njs_str("var func = function f() {f = null; return f;}; func()"), njs_str("TypeError: assignment to constant variable") }, { njs_str("var func = function f() {let f = null; return f;}; func()"), njs_str("null") }, { njs_str("var a; Object.getOwnPropertyDescriptor(this, 'a').value"), njs_str("undefined") }, { njs_str("f() = 1"), njs_str("ReferenceError: Invalid left-hand side in assignment in 1") }, { njs_str("f.a() = 1"), njs_str("ReferenceError: Invalid left-hand side in assignment in 1") }, { njs_str("++f()"), njs_str("ReferenceError: Invalid left-hand side in prefix operation in 1") }, { njs_str("f()++"), njs_str("ReferenceError: Invalid left-hand side in postfix operation in 1") }, /* Numbers. */ { njs_str("0"), njs_str("0") }, { njs_str("-0"), njs_str("-0") }, { njs_str(".0"), njs_str("0") }, { njs_str("0.1"), njs_str("0.1") }, { njs_str(".9"), njs_str("0.9") }, { njs_str("-.0_1"), njs_str("-0.01") }, { njs_str("0.000_001"), njs_str("0.000001") }, { njs_str("0.00000_123456"), njs_str("0.00000123456") }, { njs_str("0.0000001"), njs_str("1e-7") }, { njs_str("1.1000000"), njs_str("1.1") }, { njs_str("1_0"), njs_str("10") }, { njs_str("99_999_999_999_999_999_999"), njs_str("100000000000000000000") }, { njs_str("9999999999999999999_9.1_1_1"), njs_str("100000000000000000000") }, { njs_str("999999999999999999999"), njs_str("1e+21") }, { njs_str("9223372036854775808"), njs_str("9223372036854776000") }, { njs_str("18446744073709551616"), njs_str("18446744073709552000") }, { njs_str("1.79769313_48623157E+3_0_8"), njs_str("1.7976931348623157e+308") }, { njs_str("+1"), njs_str("1") }, { njs_str("+1\n"), njs_str("1") }, { njs_str("."), njs_str("SyntaxError: Unexpected token \".\" in 1") }, { njs_str("0_1"), njs_str("SyntaxError: Unexpected token \"0_\" in 1") }, { njs_str("1_"), njs_str("SyntaxError: Unexpected token \"_\" in 1") }, { njs_str("1__0"), njs_str("SyntaxError: Unexpected token \"__0\" in 1") }, { njs_str("._1"), njs_str("SyntaxError: Unexpected token \".\" in 1") }, { njs_str(".1_"), njs_str("SyntaxError: Unexpected token \"_\" in 1") }, { njs_str("1_.1"), njs_str("SyntaxError: Unexpected token \"_\" in 1") }, { njs_str(".0__1"), njs_str("SyntaxError: Unexpected token \"__1\" in 1") }, { njs_str("1e_1"), njs_str("SyntaxError: Unexpected token \"_1\" in 1") }, { njs_str("1e-_1"), njs_str("SyntaxError: Unexpected token \"_1\" in 1") }, { njs_str("1E1__0"), njs_str("SyntaxError: Unexpected token \"__0\" in 1") }, { njs_str("1_e1"), njs_str("SyntaxError: Unexpected token \"_e1\" in 1") }, { njs_str("1e1_"), njs_str("SyntaxError: Unexpected token \"_\" in 1") }, { njs_str("-_1"), njs_str("ReferenceError: \"_1\" is not defined") }, { njs_str("_1"), njs_str("ReferenceError: \"_1\" is not defined") }, /* Octal Numbers. */ { njs_str("0o0"), njs_str("0") }, { njs_str("0O10"), njs_str("8") }, { njs_str("0o011"), njs_str("9") }, { njs_str("-0O7_7_7"), njs_str("-511") }, { njs_str("0o7777777777777777777777777777777777700000000000000000000000000000000"), njs_str("3.2138760885179806e+60") }, { njs_str("0o"), njs_str("SyntaxError: Unexpected token \"0o\" in 1") }, { njs_str("0O778"), njs_str("SyntaxError: Unexpected token \"0O778\" in 1") }, { njs_str("0O_7"), njs_str("SyntaxError: Unexpected token \"0O\" in 1") }, { njs_str("0O + 1"), njs_str("SyntaxError: Unexpected token \"0O\" in 1") }, { njs_str("0o7_"), njs_str("SyntaxError: Unexpected token \"_\" in 1") }, { njs_str("0o7__7"), njs_str("SyntaxError: Unexpected token \"__7\" in 1") }, /* Legacy Octal Numbers are deprecated. */ { njs_str("00"), njs_str("SyntaxError: Unexpected token \"00\" in 1") }, { njs_str("08"), njs_str("SyntaxError: Unexpected token \"08\" in 1") }, { njs_str("09"), njs_str("SyntaxError: Unexpected token \"09\" in 1") }, { njs_str("0011"), njs_str("SyntaxError: Unexpected token \"00\" in 1") }, { njs_str("0_"), njs_str("SyntaxError: Unexpected token \"0_\" in 1") }, { njs_str("0_1"), njs_str("SyntaxError: Unexpected token \"0_\" in 1") }, { njs_str("00_1"), njs_str("SyntaxError: Unexpected token \"00\" in 1") }, /* Binary Numbers. */ { njs_str("0b0"), njs_str("0") }, { njs_str("0B10"), njs_str("2") }, { njs_str("0b0_1_0_1"), njs_str("5") }, { njs_str("-0B1111_1111"), njs_str("-255") }, { njs_str("0b111111111111111111111111111111111111111111111111111111111111111111111111111110000000000000"), njs_str("1.2379400392853803e+27") }, { njs_str("0b"), njs_str("SyntaxError: Unexpected token \"0b\" in 1") }, { njs_str("0B12"), njs_str("SyntaxError: Unexpected token \"0B12\" in 1") }, { njs_str("0b_11"), njs_str("SyntaxError: Unexpected token \"0b\" in 1") }, { njs_str("0b + 1"), njs_str("SyntaxError: Unexpected token \"0b\" in 1") }, { njs_str("0B1__1"), njs_str("SyntaxError: Unexpected token \"__1\" in 1") }, { njs_str("0b11_"), njs_str("SyntaxError: Unexpected token \"_\" in 1") }, /* Hex Numbers. */ { njs_str("0x0"), njs_str("0") }, { njs_str("-0x1"), njs_str("-1") }, { njs_str("0xffFF"), njs_str("65535") }, { njs_str("0X00_00_BE_EF"), njs_str("48879") }, { njs_str("0x21bc2b266d3a3600000000000000000000000000000000000000000000000000000"), njs_str("6.25e+79") }, { njs_str("0x21bc2b266d3a36000000000000000000000000000000000000000000000000000000"), njs_str("1e+81") }, { njs_str("0x"), njs_str("SyntaxError: Unexpected token \"0x\" in 1") }, { njs_str("0xffff."), njs_str("SyntaxError: Unexpected end of input in 1") }, { njs_str("0x12g"), njs_str("SyntaxError: Unexpected token \"g\" in 1") }, { njs_str("0X_ff"), njs_str("SyntaxError: Unexpected token \"0X\" in 1") }, { njs_str("0X + 1"), njs_str("SyntaxError: Unexpected token \"0X\" in 1") }, { njs_str("0xff_"), njs_str("SyntaxError: Unexpected token \"_\" in 1") }, { njs_str("0Xf__f"), njs_str("SyntaxError: Unexpected token \"__f\" in 1") }, { njs_str(""), njs_str("undefined") }, { njs_str("\n"), njs_str("undefined") }, { njs_str(";"), njs_str("undefined") }, { njs_str("\n +1"), njs_str("1") }, /* Scientific notation. */ { njs_str("0e0"), njs_str("0") }, { njs_str("0.0e0"), njs_str("0") }, { njs_str("1e0"), njs_str("1") }, { njs_str("1e1"), njs_str("10") }, { njs_str("1.e01"), njs_str("10") }, { njs_str("5.7e1"), njs_str("57") }, { njs_str("5.7e-1"), njs_str("0.57") }, { njs_str("-5.7e-1"), njs_str("-0.57") }, { njs_str("1.1e-01"), njs_str("0.11") }, { njs_str("5.7e-2"), njs_str("0.057") }, { njs_str("1.1e+01"), njs_str("11") }, { njs_str("-.01e-01"), njs_str("-0.001") }, { njs_str("1e9"), njs_str("1000000000") }, { njs_str("1.0e308"), njs_str("1e+308") }, { njs_str("0e309"), njs_str("0") }, { njs_str("0e-309"), njs_str("0") }, { njs_str("1e309"), njs_str("Infinity") }, { njs_str("-1e309"), njs_str("-Infinity") }, { njs_str("1e"), njs_str("SyntaxError: Unexpected token \"e\" in 1") }, { njs_str("1.e"), njs_str("SyntaxError: Unexpected token \"e\" in 1") }, { njs_str("1e+"), njs_str("SyntaxError: Unexpected token \"e\" in 1") }, { njs_str("1.e-"), njs_str("SyntaxError: Unexpected token \"e\" in 1") }, { njs_str("1eZ"), njs_str("SyntaxError: Unexpected token \"eZ\" in 1") }, { njs_str(".e1"), njs_str("SyntaxError: Unexpected token \".\" in 1") }, { njs_str("Number.prototype.X = function(){return 123;};" "(1).X()"), njs_str("123") }, /* Indices. */ { njs_str("var a = []; a[-1] = 2; a[-1] == a['-1']"), njs_str("true") }, { njs_str("var a = []; a[Infinity] = 2; a[Infinity] == a['Infinity']"), njs_str("true") }, { njs_str("var a = []; a[NaN] = 2; a[NaN] == a['NaN']"), njs_str("true") }, #define NJS_NOT_CANONICAL_INDICES "['+0', '-0', '1.', '0.', '0.0', '4294967295', " \ " '4294967296', '-1', '1.1', '9223372036854775808']" { njs_str("var a = [1,2]; " NJS_NOT_CANONICAL_INDICES ".every(v=>(a[v] === undefined))"), njs_str("true") }, { njs_str("var a = [1,2]; " NJS_NOT_CANONICAL_INDICES ".every(v=>{a[v] = 'a'; return a[v] === 'a'})"), njs_str("true") }, /* Number.toString(radix) method. */ { njs_str("0..toString(2)"), njs_str("0") }, { njs_str("(1234.567).toString(3)"), njs_str("1200201.120022100021001021021002202") }, { njs_str("(1234.567).toString(5)"), njs_str("14414.240414141414141414") }, { njs_str("(1234.567).toString(17)"), njs_str("44a.9aeb6faa0da") }, { njs_str("(1234.567).toString(36)"), njs_str("ya.kety9sifl") }, { njs_str("Number(-1.1).toString(36)"), njs_str("-1.3llllllllm") }, { njs_str("Math.pow(-2, 1023).toString(2).length"), njs_str("1025") }, { njs_str("8.0625.toString(2)"), njs_str("1000.0001") }, { njs_str("(1/3).toString(2)"), njs_str("0.010101010101010101010101010101010101010101010101010101") }, { njs_str("9999..toString(3)"), njs_str("111201100") }, { njs_str("-9999..toString(3)"), njs_str("-111201100") }, { njs_str("81985529216486895..toString(16)"), njs_str("123456789abcdf0") }, { njs_str("0xffff.toString(16)"), njs_str("ffff") }, { njs_str("30520..toString(36)"), njs_str("njs") }, { njs_str("Infinity.toString()"), njs_str("Infinity") }, { njs_str("Infinity.toString(2)"), njs_str("Infinity") }, { njs_str("Infinity.toString(10)"), njs_str("Infinity") }, { njs_str("Infinity.toString(NaN)"), njs_str("RangeError: radix argument must be between 2 and 36") }, { njs_str("Infinity.toString({})"), njs_str("RangeError: radix argument must be between 2 and 36") }, { njs_str("Infinity.toString(Infinity)"), njs_str("RangeError: radix argument must be between 2 and 36") }, { njs_str("NaN.toString()"), njs_str("NaN") }, { njs_str("NaN.toString(2)"), njs_str("NaN") }, { njs_str("NaN.toString(10)"), njs_str("NaN") }, { njs_str("NaN.toString(Infinity)"), njs_str("RangeError: radix argument must be between 2 and 36") }, { njs_str("NaN.toString({})"), njs_str("RangeError: radix argument must be between 2 and 36") }, { njs_str("NaN.toString(NaN)"), njs_str("RangeError: radix argument must be between 2 and 36") }, { njs_str("1.2312313132.toString(14)"), njs_str("1.3346da6d5d455c") }, { njs_str("7.799999999999999.toString(14)"), njs_str("7.b2b2b2b2b2b2a5") }, #ifndef NJS_SUNC { njs_str("1e20.toString(14)"), njs_str("33cb3bb449c2a92000") }, { njs_str("1.7976931348623157E+308.toString(36) == ('1a1e4vngaiqo' + '0'.repeat(187))"), njs_str("true") }, /* Largest positive double (prev_double(INFINITY)). */ { njs_str("1.7976931348623157E+308.toString(2) == ('1'.repeat(53) + '0'.repeat(971))"), njs_str("true") }, { njs_str("Array(5).fill().map((n, i) => i + 10).map((v)=>(1.2312313132).toString(v))"), njs_str("1.2312313132,1.25a850416057383,1.293699002749414,1.3010274cab0288,1.3346da6d5d455c") }, { njs_str("Array(5).fill().map((n, i) => 36 - i).map((v)=>(1e23).toString(v))"), njs_str("ga894a06abs0000,o5hlsorok4y0000,128fpsprqld20000,1m1s0ajv6cmo0000,2kmg5hv19br00000") }, #endif /* Number.prototype.toFixed(frac) method. */ { njs_str("(900.1).toFixed(1)"), njs_str("900.1") }, { njs_str("(0).toFixed(0)"), njs_str("0") }, { njs_str("(0).toFixed(3)"), njs_str("0.000") }, { njs_str("(7).toFixed()"), njs_str("7") }, { njs_str("(7).toFixed(0)"), njs_str("7") }, { njs_str("(7).toFixed(2)"), njs_str("7.00") }, { njs_str("(-900.1).toFixed(3.3)"), njs_str("-900.100") }, { njs_str("(900.123).toFixed(5)"), njs_str("900.12300") }, { njs_str("(1/3).toFixed(5)"), njs_str("0.33333") }, { njs_str("(new Number(1/3)).toFixed(5)"), njs_str("0.33333") }, { njs_str("(new Number(1/3)).toFixed(5)"), njs_str("0.33333") }, { njs_str("(1/3).toFixed({toString(){return '5'}})"), njs_str("0.33333") }, { njs_str("(1/3).toFixed(100)"), njs_str("0.3333333333333333148296162562473909929394721984863281250000000000000000000000000000000000000000000000") }, { njs_str("(1.23e+20).toFixed(2)"), njs_str("123000000000000000000.00") }, { njs_str("(1.23e-10).toFixed(2)"), njs_str("0.00") }, { njs_str("(1.23e-10).toFixed(15)"), njs_str("0.000000000123000") }, { njs_str("(1.23e-10).toFixed(100)"), njs_str("0.0000000001229999999999999888422768137255427361997917046210204716771841049194335937500000000000000000") }, { njs_str("NaN.toFixed(1)"), njs_str("NaN") }, #if 0 /* FIXME: bignum support is requred to support frac >= 20 */ { njs_str("(2 ** -100).toFixed(100)"), njs_str("0.0000000000000000000000000000007888609052210118054117285652827862296732064351090230047702789306640625") }, #endif /* Number.prototype.toPrecision(prec) method. */ { njs_str("Array(4).fill().map((n, i) => i+1).map((v)=>(1/7).toPrecision(v))"), njs_str("0.1,0.14,0.143,0.1429") }, { njs_str("Array(4).fill().map((n, i) => i+1).map((v)=>(0).toPrecision(v))"), njs_str("0,0.0,0.00,0.000") }, { njs_str("Array(4).fill().map((n, i) => i+1).map((v)=>(1/2).toPrecision(v))"), njs_str("0.5,0.50,0.500,0.5000") }, { njs_str("Array(6).fill().map((n, i) => i+2).map((v)=>(1/v).toPrecision(5))"), njs_str("0.50000,0.33333,0.25000,0.20000,0.16667,0.14286") }, { njs_str("Array(6).fill().map((n, i) => i+2).map((v)=>(1/(v*100)).toPrecision(5))"), njs_str("0.0050000,0.0033333,0.0025000,0.0020000,0.0016667,0.0014286") }, { njs_str("Array(6).fill().map((n, i) => i+1).map((v)=>(10*v/7).toPrecision(5))"), njs_str("1.4286,2.8571,4.2857,5.7143,7.1429,8.5714") }, { njs_str("Array(6).fill().map((n, i) => i+1).map((v)=>(v/3).toPrecision(5))"), njs_str("0.33333,0.66667,1.0000,1.3333,1.6667,2.0000") }, { njs_str("Array(6).fill().map((n, i) => i+1).map((v)=>((Math.pow(-1,v))*(2*v)/3).toPrecision(5))"), njs_str("-0.66667,1.3333,-2.0000,2.6667,-3.3333,4.0000") }, { njs_str("Array(12).fill().map((n, i) => i-3).map((v)=>(2**v).toPrecision(6))"), njs_str("0.125000,0.250000,0.500000,1.00000,2.00000,4.00000,8.00000,16.0000,32.0000,64.0000,128.000,256.000") }, { njs_str("Array(5).fill().map((n, i) => i+16).map((v)=>(4.1).toPrecision(v))"), njs_str("4.100000000000000,4.0999999999999996,4.09999999999999964,4.099999999999999644,4.0999999999999996447") }, { njs_str("Array(3).fill().map((n, i) => i + 19).map((v)=>(2**(-v)).toPrecision(20))"), njs_str("0.0000019073486328125000000,9.5367431640625000000e-7,4.7683715820312500000e-7") }, { njs_str("Array(3).fill().map((n, i) => i + 32).map((v)=>(2**(v)+0.1).toPrecision(10))"), njs_str("4294967296,8589934592,1.717986918e+10") }, #if 0 /* FIXME: bignum support is requred to support prec >= 20 */ { njs_str("(1/7).toPrecision(100)"), njs_str("0.1428571428571428492126926812488818541169166564941406250000000000000000000000000000000000000000000000") }, { njs_str("(2**128).toPrecision(40)"), njs_str("340282366920938463463374607431768211456.0") }, #endif { njs_str("(2**128).toPrecision(1)"), njs_str("3e+38") }, { njs_str("(2**128).toPrecision(2)"), njs_str("3.4e+38") }, { njs_str("(2**128).toPrecision(40)"), njs_str("340282366920938463490000000000000000000.0") }, { njs_str("(123).toPrecision(0)"), njs_str("RangeError: precision argument must be between 1 and 100") }, { njs_str("(123).toPrecision(2.4)"), njs_str("1.2e+2") }, { njs_str("(123).toPrecision(101)"), njs_str("RangeError: precision argument must be between 1 and 100") }, { njs_str("(2**10000).toPrecision()"), njs_str("Infinity") }, { njs_str("(-(2**10000)).toPrecision()"), njs_str("-Infinity") }, { njs_str("var v = parseFloat('9'.repeat(98));" "[98,100].map(p=>v.toPrecision(p).length)"), njs_str("98,101") }, { njs_str("(-0).toPrecision(2)"), njs_str("0.0") }, { njs_str("NaN.toPrecision()"), njs_str("NaN") }, { njs_str("NaN.toPrecision(0)"), njs_str("NaN") }, { njs_str("(10**22).toPrecision()"), njs_str("1e+22") }, { njs_str("Number.prototype.toPrecision.call('12')"), njs_str("TypeError: unexpected value type:string") }, { njs_str("(1000000000000000128).toString()"), njs_str("1000000000000000100") }, { njs_str("(1000000000000000128).toFixed(0)"), njs_str("1000000000000000128") }, { njs_str("(1e21).toFixed(1)"), njs_str("1e+21") }, { njs_str("Number.prototype.toFixed.call({})"), njs_str("TypeError: unexpected value type:object") }, { njs_str("(0).toFixed(-1)"), njs_str("RangeError: digits argument must be between 0 and 100") }, { njs_str("(0).toFixed(101)"), njs_str("RangeError: digits argument must be between 0 and 100") }, /* Number.prototype.toExponential(frac) method. */ { njs_str("Array(3).fill().map((n, i) => i + 1).map((v)=>(0).toExponential(v))"), njs_str("0.0e+0,0.00e+0,0.000e+0") }, { njs_str("Array(6).fill().map((n, i) => i + 1).map((v)=>((Math.pow(-1,v))*(2*v)/3).toExponential(5))"), njs_str("-6.66667e-1,1.33333e+0,-2.00000e+0,2.66667e+0,-3.33333e+0,4.00000e+0") }, { njs_str("Array(5).fill().map((n, i) => i + 1).map((v)=>((Math.pow(-1,v))*(2*v)/3).toExponential())"), njs_str("-6.666666666666666e-1,1.3333333333333333e+0,-2e+0,2.6666666666666667e+0,-3.3333333333333337e+0") }, { njs_str("1.7976931348623157e+308.toExponential()"), njs_str("1.7976931348623157e+308") }, #if 0 /* FIXME: bignum support is requred to support prec >= 20 */ { njs_str("(1/7).toExponential(100)"), njs_str("1.4285714285714284921269268124888185411691665649414062500000000000000000000000000000000000000000000000e-1") }, #endif { njs_str("var v = 1.7976931348623157e+308; Number(v.toExponential()) == v"), njs_str("true") }, { njs_str("(123).toExponential(-1)"), njs_str("RangeError: digits argument must be between 0 and 100") }, { njs_str("(123).toExponential(2.4)"), njs_str("1.23e+2") }, { njs_str("(123).toExponential(101)"), njs_str("RangeError: digits argument must be between 0 and 100") }, { njs_str("[2**10000,-(2**10000),NaN].map((v)=>v.toExponential())"), njs_str("Infinity,-Infinity,NaN") }, { njs_str("[2**10000,-(2**10000),NaN].map((v)=>v.toExponential(1000))"), njs_str("Infinity,-Infinity,NaN") }, { njs_str("Number.prototype.toExponential.call('12')"), njs_str("TypeError: unexpected value type:string") }, /* An object "valueOf/toString" methods. */ { njs_str("var a = { valueOf: function() { return 1 } }; +a"), njs_str("1") }, { njs_str("var a = { valueOf: function() { return '1' } }; +a"), njs_str("1") }, { njs_str("var a = { valueOf: 2," " toString: function() { return '1' } }; +a"), njs_str("1") }, { njs_str("var a = { valueOf: function() { return 1 } }; ''+a"), njs_str("1") }, { njs_str("var a = { valueOf: function() { return [] }," " toString: function() { return '1' } }; +a"), njs_str("1") }, { njs_str("var a = { toString: function() { return 'a' } };" "var b = { toString: function() { return a+'b' } }; '0'+b"), njs_str("0ab") }, { njs_str("({valueOf:()=>'4'}) / ({valueOf:()=>2})"), njs_str("2") }, { njs_str("({valueOf:()=>{throw 'x'}}) / ({valueOf:()=>{throw 'y'}});" "var e; try { x/y } catch(ex) {e = ex}; ex"), njs_str("x") }, { njs_str("({valueOf:()=>{ try {throw 'x'} catch (ex) {return 6} } }) / 2"), njs_str("3") }, { njs_str("({valueOf:()=>2}) / ({valueOf:()=>{throw 'y'}});" "var e; try { x/y } catch(ex) {e = ex}; ex"), njs_str("y") }, { njs_str("({valueOf:()=>'4'}) % ({valueOf:()=>3})"), njs_str("1") }, { njs_str("({valueOf:()=>9}) >>> ({valueOf:()=>2})"), njs_str("2") }, { njs_str("({valueOf:()=>0x1f}) & ({valueOf:()=>0xf})"), njs_str("15") }, { njs_str("({valueOf:()=>0x1f}) ^ ({valueOf:()=>0xf})"), njs_str("16") }, { njs_str("({valueOf:()=>0xf}) == ({valueOf:()=>0xf})"), njs_str("false") }, { njs_str("var e; try {({valueOf: String.prototype.valueOf}) == 1} " "catch (ex) { e = ex}; e"), njs_str("TypeError: unexpected value type:object") }, { njs_str("({valueOf:()=>0xf}) == 0xf"), njs_str("true") }, { njs_str("0xf == ({valueOf:()=>0xf})"), njs_str("true") }, { njs_str("({valueOf:()=>'0xf'}) == 0xf"), njs_str("true") }, { njs_str("0xf == ({valueOf:()=>'0xf'})"), njs_str("true") }, { njs_str("({valueOf:()=>0xf}) == '0xf'"), njs_str("true") }, { njs_str("'0xf' == ({valueOf:()=>0xf})"), njs_str("true") }, /**/ { njs_str("1 + undefined"), njs_str("NaN") }, { njs_str("1 + ''"), njs_str("1") }, { njs_str("0xA + ''"), njs_str("10") }, { njs_str("undefined + undefined"), njs_str("NaN") }, { njs_str("var undefined"), njs_str("undefined") }, { njs_str("1.2 + 5.7"), njs_str("6.9") }, { njs_str("0xf + 1"), njs_str("16") }, { njs_str("1 + 1 + '2' + 1 + 1"), njs_str("2211") }, { njs_str("'gg' + -0"), njs_str("gg0") }, { njs_str("1.2 - '5.7'"), njs_str("-4.5") }, { njs_str("1.2 + -'5.7'"), njs_str("-4.5") }, { njs_str("1.2 - '-5.7'"), njs_str("6.9") }, { njs_str("5 - ' \t 12 \t'"), njs_str("-7") }, { njs_str("5 - '12zz'"), njs_str("NaN") }, { njs_str("5 - '0x2'"), njs_str("3") }, { njs_str("5 - '-0x2'"), njs_str("NaN") }, { njs_str("5 - '\t 0x2 \t'"), njs_str("3") }, { njs_str("5 - '\t\\u000c0x2 \t'"), njs_str("3") }, { njs_str("5 - '0x2 z'"), njs_str("NaN") }, { njs_str("12 - '5.7e1'"), njs_str("-45") }, { njs_str("12 - '5.e1'"), njs_str("-38") }, { njs_str("12 - '5.7e+01'"), njs_str("-45") }, { njs_str("12 - '5.7e-01'"), njs_str("11.43") }, { njs_str("12 - ' 5.7e1 '"), njs_str("-45") }, { njs_str("12 - '5.7e'"), njs_str("NaN") }, { njs_str("12 - '5.7e+'"), njs_str("NaN") }, { njs_str("12 - '5.7e-'"), njs_str("NaN") }, { njs_str("12 - ' 5.7e1 z'"), njs_str("NaN") }, { njs_str("5 - '0x'"), njs_str("NaN") }, { njs_str("1 + +'3'"), njs_str("4") }, { njs_str("1 - undefined"), njs_str("NaN") }, { njs_str("1 - ''"), njs_str("1") }, { njs_str("undefined - undefined"), njs_str("NaN") }, { njs_str("'A'.toString()"), njs_str("A") }, /* Assignment. */ { njs_str("var a, b = (a = [2]) * (3 * 4); a +' '+ b"), njs_str("2 24") }, { njs_str("var a = 1; var b = a += 1; b"), njs_str("2") }, { njs_str("var a = 1; var b = a -= 1; b"), njs_str("0") }, { njs_str("var a = 1; var b = a <<= 1; b"), njs_str("2") }, /* 3 address operation and side effect. */ { njs_str("var a = 1; function f(x) { a = x; return 2 }; a+f(5)+' '+a"), njs_str("3 5") }, { njs_str("var a = 1; function f(x) { a = x; return 2 }; a += f(5)"), njs_str("3") }, { njs_str("var x; x in (x = 1, [1, 2, 3])"), njs_str("false") }, /* ToInt32(). */ { njs_str("-1.0 | 0"), njs_str("-1") }, { njs_str("0.0 | 0"), njs_str("0") }, { njs_str("0.001 | 0"), njs_str("0") }, { njs_str("1.0 | 0"), njs_str("1") }, { njs_str("2147483647.0 | 0"), njs_str("2147483647") }, { njs_str("2147483648.0 | 0"), njs_str("-2147483648") }, { njs_str("2147483649.0 | 0"), njs_str("-2147483647") }, { njs_str("-1844674406941458432.0 | 0"), njs_str("-2147483648") }, { njs_str("4.835703278458518e+24 /* 2**(53+29) + 2**30 */ | 0"), njs_str("1073741824") }, { njs_str("9.671406556917036e+24 /* 2**(53+30) + 2**31 */ | 0"), njs_str("-2147483648") }, /* Exponentiation. */ { njs_str("2 ** 3 ** 2"), njs_str("512") }, { njs_str("2 ** (3 ** 2)"), njs_str("512") }, { njs_str("(2 ** 3) ** 2"), njs_str("64") }, { njs_str("3 ** 2 - 9"), njs_str("0") }, { njs_str("-9 + 3 ** 2"), njs_str("0") }, { njs_str("-3 ** 2"), njs_str("SyntaxError: Either left-hand side or entire exponentiation " "must be parenthesized in 1") }, { njs_str("-(3) ** 2"), njs_str("SyntaxError: Either left-hand side or entire exponentiation " "must be parenthesized in 1") }, { njs_str("-(3 ** 2)"), njs_str("-9") }, { njs_str("(-3) ** 2"), njs_str("9") }, { njs_str("1 ** NaN"), njs_str("NaN") }, { njs_str("'a' ** -0"), njs_str("1") }, { njs_str("1.1 ** Infinity"), njs_str("Infinity") }, { njs_str("(-1.1) ** -Infinity"), njs_str("0") }, { njs_str("(-1) ** Infinity"), njs_str("NaN") }, { njs_str("1 ** -Infinity"), njs_str("NaN") }, { njs_str("(-0.9) ** Infinity"), njs_str("0") }, { njs_str("0.9 ** -Infinity"), njs_str("Infinity") }, { njs_str("'Infinity' ** 0.1"), njs_str("Infinity") }, { njs_str("Infinity ** '-0.1'"), njs_str("0") }, { njs_str("(-Infinity) ** 3"), njs_str("-Infinity") }, { njs_str("'-Infinity' ** '3.1'"), njs_str("Infinity") }, { njs_str("(-Infinity) ** '-3'"), njs_str("-0") }, { njs_str("'-Infinity' ** -2"), njs_str("0") }, { njs_str("'0' ** 0.1"), njs_str("0") }, #ifndef __NetBSD__ /* NetBSD 7: pow(0, negative) == -Infinity. */ { njs_str("0 ** '-0.1'"), njs_str("Infinity") }, #endif { njs_str("(-0) ** 3"), njs_str("-0") }, { njs_str("'-0' ** '3.1'"), njs_str("0") }, { njs_str("(-0) ** '-3'"), njs_str("-Infinity") }, #ifndef __NetBSD__ /* NetBSD 7: pow(0, negative) == -Infinity. */ { njs_str("'-0' ** -2"), njs_str("Infinity") }, #endif { njs_str("(-3) ** 0.1"), njs_str("NaN") }, { njs_str("var a = 0.1; a **= -2"), njs_str("99.99999999999999") }, { njs_str("var a = 1; a **= NaN"), njs_str("NaN") }, { njs_str("var a = 'a'; a **= -0"), njs_str("1") }, { njs_str("var a = 1.1; a **= Infinity"), njs_str("Infinity") }, { njs_str("var a = -1.1; a **= -Infinity"), njs_str("0") }, { njs_str("var a = -1; a **= Infinity"), njs_str("NaN") }, { njs_str("var a = 1; a **= -Infinity"), njs_str("NaN") }, { njs_str("var a = -0.9; a **= Infinity"), njs_str("0") }, { njs_str("var a = 0.9; a **= -Infinity"), njs_str("Infinity") }, { njs_str("var a = 'Infinity'; a **= 0.1"), njs_str("Infinity") }, { njs_str("var a = Infinity; a **= '-0.1'"), njs_str("0") }, { njs_str("var a = -Infinity; a **= 3"), njs_str("-Infinity") }, { njs_str("var a = '-Infinity'; a **= '3.1'"), njs_str("Infinity") }, { njs_str("var a = -Infinity; a **= '-3'"), njs_str("-0") }, { njs_str("var a = '-Infinity'; a **= -2"), njs_str("0") }, { njs_str("var a = '0'; a **= 0.1"), njs_str("0") }, #ifndef __NetBSD__ /* NetBSD 7: pow(0, negative) == -Infinity. */ { njs_str("var a = 0; a **= '-0.1'"), njs_str("Infinity") }, #endif { njs_str("var a = -0; a **= 3"), njs_str("-0") }, { njs_str("var a = '-0'; a **= '3.1'"), njs_str("0") }, { njs_str("var a = -0; a **= '-3'"), njs_str("-Infinity") }, #ifndef __NetBSD__ /* NetBSD 7: pow(0, negative) == -Infinity. */ { njs_str("var a = '-0'; a **= -2"), njs_str("Infinity") }, #endif { njs_str("var a = -3; a **= 0.1"), njs_str("NaN") }, /**/ { njs_str("12 | 6"), njs_str("14") }, { njs_str("12 | 'abc'"), njs_str("12") }, { njs_str("-1 | 0"), njs_str("-1") }, { njs_str("-2147483648 | 0"), njs_str("-2147483648") }, { njs_str("1024.9 | 0"), njs_str("1024") }, { njs_str("-1024.9 | 0"), njs_str("-1024") }, { njs_str("9007199254740991 | 0"), njs_str("-1") }, { njs_str("9007199254740992 | 0"), njs_str("0") }, { njs_str("9007199254740993 | 0"), njs_str("0") }, #if 0 { njs_str("9223372036854775808 | 0"), njs_str("0") }, #endif { njs_str("9223372036854777856 | 0"), njs_str("2048") }, { njs_str("-9223372036854777856 | 0"), njs_str("-2048") }, { njs_str("NaN | 0"), njs_str("0") }, { njs_str("-NaN | 0"), njs_str("0") }, { njs_str("Infinity | 0"), njs_str("0") }, { njs_str("-Infinity | 0"), njs_str("0") }, { njs_str("+0 | 0"), njs_str("0") }, { njs_str("-0 | 0"), njs_str("0") }, { njs_str("32.5 << 2.4"), njs_str("128") }, { njs_str("32.5 << 'abc'"), njs_str("32") }, { njs_str("'abc' << 2"), njs_str("0") }, { njs_str("-1 << 0"), njs_str("-1") }, { njs_str("-1 << -1"), njs_str("-2147483648") }, { njs_str("-2147483648 << 0"), njs_str("-2147483648") }, #if 0 { njs_str("9223372036854775808 << 0"), njs_str("0") }, #endif { njs_str("9223372036854777856 << 0"), njs_str("2048") }, { njs_str("-9223372036854777856 << 0"), njs_str("-2048") }, { njs_str("NaN << 0"), njs_str("0") }, { njs_str("32.5 >> 2.4"), njs_str("8") }, { njs_str("-1 >> 30"), njs_str("-1") }, { njs_str("'abc' >> 2"), njs_str("0") }, { njs_str("-1 >> 0"), njs_str("-1") }, { njs_str("-1 >> -1"), njs_str("-1") }, { njs_str("-2147483648 >> 0"), njs_str("-2147483648") }, { njs_str("-2147483648 >> -1"), njs_str("-1") }, #if 0 { njs_str("9223372036854775808 >> 0"), njs_str("0") }, #endif { njs_str("9223372036854777856 >> 0"), njs_str("2048") }, { njs_str("-9223372036854777856 >> 0"), njs_str("-2048") }, { njs_str("NaN >> 0"), njs_str("0") }, { njs_str("-1 >>> 30"), njs_str("3") }, { njs_str("NaN >>> 1"), njs_str("0") }, #if 0 { njs_str("9223372036854775808 >>> 1"), njs_str("0") }, #endif { njs_str("-1 >>> 0"), njs_str("4294967295") }, { njs_str("-1 >>> -1"), njs_str("1") }, { njs_str("-2147483648 >>> 0"), njs_str("2147483648") }, { njs_str("-2147483648 >>> -1"), njs_str("1") }, #if 0 { njs_str("9223372036854775808 >>> 0"), njs_str("0") }, #endif { njs_str("9223372036854777856 >>> 0"), njs_str("2048") }, { njs_str("-9223372036854777856 >>> 0"), njs_str("4294965248") }, { njs_str("NaN >>> 0"), njs_str("0") }, { njs_str("!2"), njs_str("false") }, /**/ { njs_str("var a = { valueOf: function() { return 1 } }; ~a"), njs_str("-2") }, { njs_str("var a = { valueOf: function() { return '1' } }; ~a"), njs_str("-2") }, /**/ { njs_str("1 || 2"), njs_str("1") }, { njs_str("var a = 1; 1 || (a = 2); a"), njs_str("1") }, { njs_str("var x; x = 0 || x; x"), njs_str("undefined") }, { njs_str("var x; x = 1 && x; x"), njs_str("undefined") }, { njs_str("1 || 2 || 3"), njs_str("1") }, { njs_str("1 || (2 + 2) || 3"), njs_str("1") }, { njs_str("1 && 2"), njs_str("2") }, { njs_str("1 && 2 && 3"), njs_str("3") }, { njs_str("var a = 1; 0 && (a = 2); a"), njs_str("1") }, { njs_str("false && true || true"), njs_str("true") }, { njs_str("false && (true || true)"), njs_str("false") }, { njs_str("true && (null ?? true)"), njs_str("true") }, { njs_str("(null || undefined) ?? (true && true)"), njs_str("true") }, { njs_str("undefined ?? null ?? false ?? true"), njs_str("false") }, { njs_str("1 && 1 ?? true"), njs_str("SyntaxError: Unexpected token \"??\" in 1") }, { njs_str("null ?? 0 || 1"), njs_str("SyntaxError: Unexpected token \"||\" in 1") }, { njs_str("var a = true; a = -~!a"), njs_str("1") }, { njs_str("12 & 6"), njs_str("4") }, { njs_str("-1 & 65536"), njs_str("65536") }, { njs_str("-2147483648 & 65536"), njs_str("0") }, #if 0 { njs_str("9223372036854775808 & 65536"), njs_str("0") }, #endif { njs_str("NaN & 65536"), njs_str("0") }, { njs_str("12 ^ 6"), njs_str("10") }, { njs_str("-1 ^ 65536"), njs_str("-65537") }, { njs_str("-2147483648 ^ 65536"), njs_str("-2147418112") }, #if 0 { njs_str("9223372036854775808 ^ 65536"), njs_str("65536") }, #endif { njs_str("NaN ^ 65536"), njs_str("65536") }, { njs_str("var x = '1'; +x + 2"), njs_str("3") }, /* Weird things. */ { njs_str("'3' -+-+-+ '1' + '1' / '3' * '6' + '2'"), njs_str("42") }, { njs_str("((+!![])+(+!![])+(+!![])+(+!![])+[])+((+!![])+(+!![])+[])"), njs_str("42") }, { njs_str("1+[[]+[]]-[]+[[]-[]]-1"), njs_str("9") }, { njs_str("[[]+[]]-[]+[[]-[]]"), njs_str("00") }, { njs_str("!--[][1]"), njs_str("true") }, { njs_str("[].concat[1,2,3]"), njs_str("undefined") }, /**/ { njs_str("'true' == true"), njs_str("false") }, { njs_str("null == false"), njs_str("false") }, { njs_str("0 == null"), njs_str("false") }, { njs_str("!null"), njs_str("true") }, { njs_str("0 === -0"), njs_str("true") }, { njs_str("1/-0"), njs_str("-Infinity") }, { njs_str("1/0 === 1/-0"), njs_str("false") }, { njs_str("1 == true"), njs_str("true") }, { njs_str("NaN === NaN"), njs_str("false") }, { njs_str("NaN !== NaN"), njs_str("true") }, { njs_str("NaN == NaN"), njs_str("false") }, { njs_str("NaN != NaN"), njs_str("true") }, { njs_str("NaN == false"), njs_str("false") }, { njs_str("Infinity == Infinity"), njs_str("true") }, { njs_str("-Infinity == -Infinity"), njs_str("true") }, { njs_str("-Infinity < Infinity"), njs_str("true") }, { njs_str("Infinity - Infinity"), njs_str("NaN") }, { njs_str("Infinity - -Infinity"), njs_str("Infinity") }, { njs_str("undefined == 0"), njs_str("false") }, { njs_str("undefined == null"), njs_str("true") }, { njs_str("'1' == 1"), njs_str("true") }, { njs_str("'1a' == '1'"), njs_str("false") }, { njs_str("'abc' == 'abc'"), njs_str("true") }, { njs_str("'abc' < 'abcde'"), njs_str("true") }, { njs_str("0 == ''"), njs_str("true") }, { njs_str("0 == ' '"), njs_str("true") }, { njs_str("0 == ' '"), njs_str("true") }, { njs_str("0 == '0'"), njs_str("true") }, { njs_str("0 == ' 0 '"), njs_str("true") }, { njs_str("0 == '000'"), njs_str("true") }, { njs_str("'0' == ''"), njs_str("false") }, { njs_str("1 < 2"), njs_str("true") }, { njs_str("NaN < NaN"), njs_str("false") }, { njs_str("NaN > NaN"), njs_str("false") }, { njs_str("undefined < 1"), njs_str("false") }, { njs_str("[] == false"), njs_str("true") }, { njs_str("[0] == false"), njs_str("true") }, { njs_str("[0,0] == false"), njs_str("false") }, { njs_str("({}) == false"), njs_str("false") }, { njs_str("new Number(1) == new String('1')"), njs_str("false") }, { njs_str("var a = Object; a == Object"), njs_str("true") }, { njs_str("'1' == new Number(1)"), njs_str("true") }, { njs_str("new Number(null) + ''"), njs_str("0") }, { njs_str("new String('abc') == 'abc'"), njs_str("true") }, { njs_str("false == new String('0')"), njs_str("true") }, { njs_str("var a = { valueOf: function() { return 5 } }; a == 5"), njs_str("true") }, { njs_str("var a = { valueOf: function() { return '5' } }; a == 5"), njs_str("true") }, { njs_str("var a = { valueOf: function() { return '5' } }; a == '5'"), njs_str("true") }, { njs_str("var a = { valueOf: function() { return 5 } }; a == '5'"), njs_str("true") }, { njs_str("var a = { toString: function() { return true } }; '1' == a"), njs_str("true") }, { njs_str("var a = { valueOf: function() { return 'b' }," " toString: function() { return 'a' } }; a == 'a'"), njs_str("false") }, /* Comparisions. */ { njs_str("1 < 2"), njs_str("true") }, { njs_str("1 < 1"), njs_str("false") }, { njs_str("1 <= 1"), njs_str("true") }, { njs_str("1 <= 2"), njs_str("true") }, { njs_str("2 > 1"), njs_str("true") }, { njs_str("1 > 2"), njs_str("false") }, { njs_str("1 > 1"), njs_str("false") }, { njs_str("1 >= 1"), njs_str("true") }, { njs_str("2 >= 1"), njs_str("true") }, { njs_str("1 >= 2"), njs_str("false") }, /**/ { njs_str("null === null"), njs_str("true") }, { njs_str("null !== null"), njs_str("false") }, { njs_str("null == null"), njs_str("true") }, { njs_str("null != null"), njs_str("false") }, { njs_str("null < null"), njs_str("false") }, { njs_str("null > null"), njs_str("false") }, { njs_str("null <= null"), njs_str("true") }, { njs_str("null >= null"), njs_str("true") }, /**/ { njs_str("null === undefined"), njs_str("false") }, { njs_str("null !== undefined"), njs_str("true") }, { njs_str("null == undefined"), njs_str("true") }, { njs_str("null != undefined"), njs_str("false") }, { njs_str("null < undefined"), njs_str("false") }, { njs_str("null > undefined"), njs_str("false") }, { njs_str("null <= undefined"), njs_str("false") }, { njs_str("null >= undefined"), njs_str("false") }, /**/ { njs_str("null === false"), njs_str("false") }, { njs_str("null !== false"), njs_str("true") }, { njs_str("null == false"), njs_str("false") }, { njs_str("null != false"), njs_str("true") }, { njs_str("null < false"), njs_str("false") }, { njs_str("null > false"), njs_str("false") }, { njs_str("null <= false"), njs_str("true") }, { njs_str("null >= false"), njs_str("true") }, /**/ { njs_str("null === true"), njs_str("false") }, { njs_str("null !== true"), njs_str("true") }, { njs_str("null == true"), njs_str("false") }, { njs_str("null != true"), njs_str("true") }, { njs_str("null < true"), njs_str("true") }, { njs_str("null > true"), njs_str("false") }, { njs_str("null <= true"), njs_str("true") }, { njs_str("null >= true"), njs_str("false") }, /**/ { njs_str("Infinity === Infinity"), njs_str("true") }, { njs_str("Infinity !== Infinity"), njs_str("false") }, { njs_str("Infinity == Infinity"), njs_str("true") }, { njs_str("Infinity != Infinity"), njs_str("false") }, { njs_str("Infinity < Infinity"), njs_str("false") }, { njs_str("Infinity > Infinity"), njs_str("false") }, { njs_str("Infinity <= Infinity"), njs_str("true") }, { njs_str("Infinity >= Infinity"), njs_str("true") }, /**/ { njs_str("-Infinity === Infinity"), njs_str("false") }, { njs_str("-Infinity !== Infinity"), njs_str("true") }, { njs_str("-Infinity == Infinity"), njs_str("false") }, { njs_str("-Infinity != Infinity"), njs_str("true") }, { njs_str("-Infinity < Infinity"), njs_str("true") }, { njs_str("-Infinity > Infinity"), njs_str("false") }, { njs_str("-Infinity <= Infinity"), njs_str("true") }, { njs_str("-Infinity >= Infinity"), njs_str("false") }, { njs_str("Boolean(Infinity)"), njs_str("true") }, { njs_str("!Infinity === false"), njs_str("true") }, { njs_str("var Infinity"), njs_str("undefined") }, { njs_str("Infinity = 1"), njs_str("TypeError: Cannot assign to read-only property \"Infinity\" of object") }, /**/ { njs_str("NaN === NaN"), njs_str("false") }, { njs_str("NaN !== NaN"), njs_str("true") }, { njs_str("NaN == NaN"), njs_str("false") }, { njs_str("NaN != NaN"), njs_str("true") }, { njs_str("NaN < NaN"), njs_str("false") }, { njs_str("NaN > NaN"), njs_str("false") }, { njs_str("NaN >= NaN"), njs_str("false") }, { njs_str("NaN <= NaN"), njs_str("false") }, { njs_str("var NaN"), njs_str("undefined") }, { njs_str("NaN = 1"), njs_str("TypeError: Cannot assign to read-only property \"NaN\" of object") }, /**/ { njs_str("null < 0"), njs_str("false") }, { njs_str("null < 1"), njs_str("true") }, { njs_str("null < NaN"), njs_str("false") }, { njs_str("null < -Infinity"), njs_str("false") }, { njs_str("null < Infinity"), njs_str("true") }, { njs_str("null < 'null'"), njs_str("false") }, { njs_str("null < '1'"), njs_str("true") }, { njs_str("null < [1]"), njs_str("true") }, { njs_str("null < ({})"), njs_str("false") }, { njs_str("var a = { valueOf: function() { return 1 } }; null < a"), njs_str("true") }, { njs_str("var a = { valueOf: function() { return 'null' } };null < a"), njs_str("false") }, { njs_str("var a = { valueOf: function() { return '1' } }; null < a"), njs_str("true") }, { njs_str("1 < {valueOf: ()=>{throw 'x'}}"), njs_str("x") }, { njs_str("({valueOf: ()=>{throw 'x'}}) <= ({valueOf:()=>{throw 'y'}})"), njs_str("x") }, { njs_str("({valueOf:()=>{throw 'x'}}) > ({valueOf:()=>{throw 'y'}})"), njs_str("x") }, /**/ { njs_str("undefined == undefined"), njs_str("true") }, { njs_str("undefined != undefined"), njs_str("false") }, { njs_str("undefined === undefined"), njs_str("true") }, { njs_str("undefined !== undefined"), njs_str("false") }, { njs_str("undefined < undefined"), njs_str("false") }, { njs_str("undefined < null"), njs_str("false") }, { njs_str("undefined < false"), njs_str("false") }, { njs_str("undefined < true"), njs_str("false") }, { njs_str("undefined < 0"), njs_str("false") }, { njs_str("undefined < 1"), njs_str("false") }, { njs_str("undefined < NaN"), njs_str("false") }, { njs_str("undefined < -Infinity"), njs_str("false") }, { njs_str("undefined < Infinity"), njs_str("false") }, { njs_str("undefined < 'undefined'"), njs_str("false") }, { njs_str("undefined < '1'"), njs_str("false") }, { njs_str("undefined < [1]"), njs_str("false") }, { njs_str("undefined < ({})"), njs_str("false") }, { njs_str("var a = { valueOf: function() { return 1 } }; undefined < a"), njs_str("false") }, { njs_str("var a = { valueOf: function() { return 'undefined' } };" "undefined < a"), njs_str("false") }, { njs_str("var a = { valueOf: function() { return '1' } };" "undefined < a"), njs_str("false") }, /**/ { njs_str("false < 1"), njs_str("true") }, { njs_str("true < 1"), njs_str("false") }, { njs_str("-1 < 1"), njs_str("true") }, { njs_str("-1 < '1'"), njs_str("true") }, { njs_str("NaN < NaN"), njs_str("false") }, { njs_str("-Infinity < Infinity"), njs_str("true") }, { njs_str("Infinity < -Infinity"), njs_str("false") }, { njs_str("1 < 'abc'"), njs_str("false") }, /**/ { njs_str("[] === []"), njs_str("false") }, { njs_str("[] !== []"), njs_str("true") }, { njs_str("[] == []"), njs_str("false") }, { njs_str("[] != []"), njs_str("true") }, { njs_str("[] < []"), njs_str("false") }, { njs_str("[] > []"), njs_str("false") }, { njs_str("[] >= []"), njs_str("true") }, { njs_str("[] <= []"), njs_str("true") }, /**/ { njs_str("({}) === ({})"), njs_str("false") }, { njs_str("({}) !== ({})"), njs_str("true") }, { njs_str("({}) == ({})"), njs_str("false") }, { njs_str("({}) != ({})"), njs_str("true") }, { njs_str("({}) > ({})"), njs_str("false") }, { njs_str("({}) <= ({})"), njs_str("true") }, { njs_str("({}) >= ({})"), njs_str("true") }, /**/ { njs_str("[0] == ({})"), njs_str("false") }, { njs_str("[0] != ({})"), njs_str("true") }, { njs_str("[0] <= ({})"), njs_str("true") }, { njs_str("[0] >= ({})"), njs_str("false") }, /**/ { njs_str("new String('1') > new Number(1)"), njs_str("false") }, { njs_str("new Boolean(true) > '1'"), njs_str("false") }, { njs_str("'0' >= new Number(1)"), njs_str("false") }, { njs_str("'1' >= new Number(1)"), njs_str("true") }, { njs_str("new String('1') < new Number(1)"), njs_str("false") }, { njs_str("new Boolean(true) < '1'"), njs_str("false") }, { njs_str("new String('1') <= new Number(1)"), njs_str("true") }, { njs_str("new Boolean(true) <= '1'"), njs_str("true") }, { njs_str("'-1' < {valueOf: function() {return -2}}"), njs_str("false") }, { njs_str("new 0[isNaN]"), njs_str("TypeError: (intermediate value)[\"[object Function]\"] is not a function") }, { njs_str("new 0[undefined]"), njs_str("TypeError: (intermediate value)[\"undefined\"] is not a function") }, /**/ { njs_str("var a; a = 1 ? 2 : 3"), njs_str("2") }, { njs_str("var a; a = 1 ? 2 : 3 ? 4 : 5"), njs_str("2") }, { njs_str("var a; a = 0 ? 2 : 3 ? 4 : 5"), njs_str("4") }, { njs_str("0 ? 2 ? 3 : 4 : 5"), njs_str("5") }, { njs_str("1 ? 2 ? 3 : 4 : 5"), njs_str("3") }, { njs_str("1 ? 0 ? 3 : 4 : 5"), njs_str("4") }, { njs_str("(1 ? 0 : 3) ? 4 : 5"), njs_str("5") }, { njs_str("var a; a = (1 + 2) ? 2 ? 3 + 4 : 5 : 6"), njs_str("7") }, { njs_str("var a; a = (1 ? 2 : 3) + 4"), njs_str("6") }, { njs_str("var a, b; a = 1 ? b = 2 + 4 : b = 3"), njs_str("6") }, { njs_str("var a; a = 1 ? [1,2] : []"), njs_str("1,2") }, /**/ { njs_str("var a = { valueOf: function() { return 1 } }; +a"), njs_str("1") }, { njs_str("var a = { valueOf: function() { return '1' } }; +a"), njs_str("1") }, { njs_str("var a = { valueOf: function() { return 1 } }; -a"), njs_str("-1") }, { njs_str("var a = { valueOf: function() { return '1' } }; -a"), njs_str("-1") }, /* Increment. */ { njs_str("var a = 1; ++a"), njs_str("2") }, { njs_str("var a = '1'; ++a"), njs_str("2") }, { njs_str("var a = [1]; ++a"), njs_str("2") }, { njs_str("var a = {}; ++a"), njs_str("NaN") }, { njs_str("var a = [1,2,3]; var b = 1; b = ++a[b]; b + ' '+ a"), njs_str("3 1,3,3") }, { njs_str("var a = { valueOf: function() { return 1 } };" "++a +' '+ a +' '+ typeof a"), njs_str("2 2 number") }, { njs_str("var a = { valueOf: function() { return '1' } };" "++a +' '+ a +' '+ typeof a"), njs_str("2 2 number") }, { njs_str("var a = { valueOf: function() { return [1] } };" "++a +' '+ a +' '+ typeof a"), njs_str("NaN NaN number") }, { njs_str("var a = { valueOf: function() { return {} } };" "++a +' '+ a +' '+ typeof a"), njs_str("NaN NaN number") }, /**/ { njs_str("var a = 1; a = ++a"), njs_str("2") }, { njs_str("var a = '1'; a = ++a"), njs_str("2") }, { njs_str("var a = [1]; a = ++a"), njs_str("2") }, { njs_str("var a = {}; a = ++a"), njs_str("NaN") }, { njs_str("var a = { valueOf: function() { return 1 } }; a = ++a"), njs_str("2") }, { njs_str("var a = { valueOf: function() { return '1' } }; a = ++a"), njs_str("2") }, { njs_str("var a = { valueOf: function() { return [1] } }; a = ++a"), njs_str("NaN") }, { njs_str("var a = { valueOf: function() { return {} } }; a = ++a"), njs_str("NaN") }, /**/ { njs_str("var a = 1; var b = ++a; a +' '+ b"), njs_str("2 2") }, { njs_str("var a = '1'; var b = ++a; a +' '+ b"), njs_str("2 2") }, { njs_str("var a = [1]; var b = ++a; a +' '+ b"), njs_str("2 2") }, { njs_str("var a = {}; var b = ++a; a +' '+ b"), njs_str("NaN NaN") }, { njs_str("var a = { valueOf: function() { return 1 } };" "var b = ++a; a +' '+ b"), njs_str("2 2") }, { njs_str("var a = { valueOf: function() { return '1' } };" "var b = ++a; a +' '+ b"), njs_str("2 2") }, { njs_str("var a = { valueOf: function() { return [1] } };" "var b = ++a; a +' '+ b"), njs_str("NaN NaN") }, { njs_str("var a = { valueOf: function() { return {} } };" "var b = ++a; a +' '+ b"), njs_str("NaN NaN") }, { njs_str("var a = 0; a = a + ++a; a"), njs_str("1") }, { njs_str("var a = 0; a += a + ++a; a"), njs_str("1") }, { njs_str("var i = 0, arr = ['a', 'b'];" "arr[i] = arr[i] + arr[++i]; arr"), njs_str("ab,b") }, { njs_str("var i = 0, arr = ['a', 'b'];" "arr[i] += arr[i] + arr[++i]; arr"), njs_str("aab,b") }, /* Post increment. */ { njs_str("var a = 1; a++"), njs_str("1") }, { njs_str("var a = '1'; a++"), njs_str("1") }, { njs_str("var a = [1]; a++"), njs_str("1") }, { njs_str("var a = {}; a++"), njs_str("NaN") }, { njs_str("var a = { valueOf: function() { return 1 } }; a++"), njs_str("1") }, { njs_str("var a = { valueOf: function() { return '1' } }; a++"), njs_str("1") }, { njs_str("var a = { valueOf: function() { return [1] } }; a++"), njs_str("NaN") }, { njs_str("var a = { valueOf: function() { return {} } }; a++"), njs_str("NaN") }, /**/ { njs_str("var a = 1; a = a++"), njs_str("1") }, { njs_str("var a = '1'; a = a++"), njs_str("1") }, { njs_str("var a = [1]; a = a++"), njs_str("1") }, { njs_str("var a = {}; a = a++"), njs_str("NaN") }, { njs_str("var a = { valueOf: function() { return 1 } }; a = a++"), njs_str("1") }, { njs_str("var a = { valueOf: function() { return '1' } }; a = a++"), njs_str("1") }, { njs_str("var a = { valueOf: function() { return [1] } }; a = a++"), njs_str("NaN") }, { njs_str("var a = { valueOf: function() { return {} } }; a = a++"), njs_str("NaN") }, /**/ { njs_str("var a = 1; var b = a++; a +' '+ b"), njs_str("2 1") }, { njs_str("var a = '1'; var b = a++; a +' '+ b"), njs_str("2 1") }, { njs_str("var a = [1]; var b = a++; a +' '+ b"), njs_str("2 1") }, { njs_str("var a = {}; var b = a++; a +' '+ b"), njs_str("NaN NaN") }, { njs_str("var a = { valueOf: function() { return 1 } };" "var b = a++; a +' '+ b"), njs_str("2 1") }, { njs_str("var a = { valueOf: function() { return '1' } };" "var b = a++; a +' '+ b"), njs_str("2 1") }, { njs_str("var a = { valueOf: function() { return [1] } };" "var b = a++; a +' '+ b"), njs_str("NaN NaN") }, { njs_str("var a = { valueOf: function() { return {} } };" "var b = a++; a +' '+ b"), njs_str("NaN NaN") }, { njs_str("var a = 0; a = a + a++; a"), njs_str("0") }, { njs_str("var a = 0; a += a + a++; a"), njs_str("0") }, { njs_str("var i = 1, arr = ['a', 'b'];" "arr[i] = arr[i] + arr[i++]; arr"), njs_str("a,bb") }, { njs_str("var i = 1, arr = ['a', 'b'];" "arr[i] += arr[i] + arr[i++]; arr"), njs_str("a,bbb") }, /* Decrement. */ { njs_str("var a = 1; --a"), njs_str("0") }, { njs_str("var a = '1'; --a"), njs_str("0") }, { njs_str("var a = [1]; --a"), njs_str("0") }, { njs_str("var a = {}; --a"), njs_str("NaN") }, { njs_str("var a = { valueOf: function() { return 1} }; --a"), njs_str("0") }, { njs_str("var a = { valueOf: function() { return '1'} }; --a"), njs_str("0") }, { njs_str("var a = { valueOf: function() { return [1]} }; --a"), njs_str("NaN") }, { njs_str("var a = { valueOf: function() { return {} } }; --a"), njs_str("NaN") }, /**/ { njs_str("var a = 1; a = --a"), njs_str("0") }, { njs_str("var a = '1'; a = --a"), njs_str("0") }, { njs_str("var a = [1]; a = --a"), njs_str("0") }, { njs_str("var a = {}; a = --a"), njs_str("NaN") }, { njs_str("var a = { valueOf: function() { return 1} }; a = --a"), njs_str("0") }, { njs_str("var a = { valueOf: function() { return '1'} }; a = --a"), njs_str("0") }, { njs_str("var a = { valueOf: function() { return [1]} }; a = --a"), njs_str("NaN") }, { njs_str("var a = { valueOf: function() { return {} } }; a = --a"), njs_str("NaN") }, /**/ { njs_str("var a = 1; var b = --a; a +' '+ b"), njs_str("0 0") }, { njs_str("var a = '1'; var b = --a; a +' '+ b"), njs_str("0 0") }, { njs_str("var a = [1]; var b = --a; a +' '+ b"), njs_str("0 0") }, { njs_str("var a = {}; var b = --a; a +' '+ b"), njs_str("NaN NaN") }, { njs_str("var a = { valueOf: function() { return 1 } };" "var b = --a; a +' '+ b"), njs_str("0 0") }, { njs_str("var a = { valueOf: function() { return '1' } };" "var b = --a; a +' '+ b"), njs_str("0 0") }, { njs_str("var a = { valueOf: function() { return [1] } };" "var b = --a; a +' '+ b"), njs_str("NaN NaN") }, { njs_str("var a = { valueOf: function() { return {} } };" "var b = --a; a +' '+ b"), njs_str("NaN NaN") }, { njs_str("var a = 0; a = a + --a; a"), njs_str("-1") }, { njs_str("var a = 0; a -= a + --a; a"), njs_str("1") }, { njs_str("var i = 1, arr = ['a', 'b'];" "arr[i] = arr[i] + arr[--i]; arr"), njs_str("a,ba") }, { njs_str("var i = 1, arr = ['a', 'b'];" "arr[i] += arr[i] + arr[--i]; arr"), njs_str("a,bba") }, /* Post decrement. */ { njs_str("var a = 1; a--"), njs_str("1") }, { njs_str("var a = '1'; a--"), njs_str("1") }, { njs_str("var a = [1]; a--"), njs_str("1") }, { njs_str("var a = {}; a--"), njs_str("NaN") }, { njs_str("var a = { valueOf: function() { return 1 } }; a--"), njs_str("1") }, { njs_str("var a = { valueOf: function() { return '1' } }; a--"), njs_str("1") }, { njs_str("var a = { valueOf: function() { return [1] } }; a--"), njs_str("NaN") }, { njs_str("var a = { valueOf: function() { return {} } }; a--"), njs_str("NaN") }, /**/ { njs_str("var a = 1; a = a--"), njs_str("1") }, { njs_str("var a = '1'; a = a--"), njs_str("1") }, { njs_str("var a = [1]; a = a--"), njs_str("1") }, { njs_str("var a = {}; a = a--"), njs_str("NaN") }, { njs_str("var a = { valueOf: function() { return 1 } }; a = a--"), njs_str("1") }, { njs_str("var a = { valueOf: function() { return '1' } }; a = a--"), njs_str("1") }, { njs_str("var a = { valueOf: function() { return [1] } }; a = a--"), njs_str("NaN") }, { njs_str("var a = { valueOf: function() { return {} } }; a = a--"), njs_str("NaN") }, /**/ { njs_str("var a = 1; var b = a--; a +' '+ b"), njs_str("0 1") }, { njs_str("var a = '1'; var b = a--; a +' '+ b"), njs_str("0 1") }, { njs_str("var a = [1]; var b = a--; a +' '+ b"), njs_str("0 1") }, { njs_str("var a = {}; var b = a--; a +' '+ b"), njs_str("NaN NaN") }, { njs_str("var a = { valueOf: function() { return 1 } };" "var b = a--; a +' '+ b"), njs_str("0 1") }, { njs_str("var a = { valueOf: function() { return '1' } };" "var b = a--; a +' '+ b"), njs_str("0 1") }, { njs_str("var a = { valueOf: function() { return [1] } };" "var b = a--; a +' '+ b"), njs_str("NaN NaN") }, { njs_str("var a = { valueOf: function() { return {} } };" "var b = a--; a +' '+ b"), njs_str("NaN NaN") }, { njs_str("var a = 0; a = a + a--; a"), njs_str("0") }, { njs_str("var a = 0; a += a + a--; a"), njs_str("0") }, { njs_str("var i = 1, arr = ['a', 'b'];" "arr[i] = arr[i] + arr[i--]; arr"), njs_str("a,bb") }, { njs_str("var i = 1, arr = ['a', 'b'];" "arr[i] += arr[i] + arr[i--]; arr"), njs_str("a,bbb") }, /**/ { njs_str("var a, b; a = 2; b = ++a + ++a; a + ' ' + b"), njs_str("4 7") }, { njs_str("var a, b; a = 2; b = a++ + a++; a + ' ' + b"), njs_str("4 5") }, { njs_str("var a, b; a = b = 7; a +' '+ b"), njs_str("7 7") }, { njs_str("var a, b, c; a = b = c = 5; a +' '+ b +' '+ c"), njs_str("5 5 5") }, { njs_str("var a, b, c; a = b = (c = 5) + 2; a +' '+ b +' '+ c"), njs_str("7 7 5") }, { njs_str("1, 2 + 5, 3"), njs_str("3") }, { njs_str("var a, b; a = 1 /* YES */\n b = a + 2 \n \n + 1 \n + 3"), njs_str("7") }, { njs_str("var a, b; a = 1 // YES \n b = a + 2 \n \n + 1 \n + 3"), njs_str("7") }, { njs_str("var a; a = 0; ++ \n a"), njs_str("1") }, { njs_str("var a; a = 0\n ++a"), njs_str("1") }, { njs_str("a = 0; a \n ++"), njs_str("SyntaxError: Unexpected end of input in 2") }, { njs_str("a = 0; a \n --"), njs_str("SyntaxError: Unexpected end of input in 2") }, { njs_str("var a = 0; a \n + 1"), njs_str("1") }, { njs_str("var a = 0; a \n +\n 1"), njs_str("1") }, { njs_str("var a; a = 1 ? 2 \n : 3"), njs_str("2") }, { njs_str("var a, b, c;" "a = 0 / 0; b = 1 / 0; c = -1 / 0; a +' '+ b +' '+ c"), njs_str("NaN Infinity -Infinity") }, { njs_str("var a, b; a = (b = 7) + 5; var c; a +' '+ b +' '+ c"), njs_str("12 7 undefined") }, { njs_str("var a, b = 1, c; a +' '+ b +' '+ c"), njs_str("undefined 1 undefined") }, { njs_str("var a = 1, b = a + 1; a +' '+ b"), njs_str("1 2") }, { njs_str("var a; a = a = 1"), njs_str("1") }, { njs_str("var a = 1, \n b; a +' '+ b"), njs_str("1 undefined") }, { njs_str("var a; a = b + 1; var b; a +' '+ b"), njs_str("NaN undefined") }, { njs_str("var a += 1"), njs_str("SyntaxError: Unexpected token \"+=\" in 1") }, { njs_str("var a = a + 1"), njs_str("undefined") }, { njs_str("var a; a = b + 1; var b = 1; a +' '+ b"), njs_str("NaN 1") }, { njs_str("var a; (a) = 1"), njs_str("1") }, { njs_str("a"), njs_str("ReferenceError: \"a\" is not defined") }, { njs_str("\na"), njs_str("ReferenceError: \"a\" is not defined") }, { njs_str("\n\na"), njs_str("ReferenceError: \"a\" is not defined") }, { njs_str("a + a"), njs_str("ReferenceError: \"a\" is not defined") }, { njs_str("a = b + 1"), njs_str("ReferenceError: \"a\" is not defined") }, { njs_str("a = a + 1"), njs_str("ReferenceError: \"a\" is not defined") }, { njs_str("a += 1"), njs_str("ReferenceError: \"a\" is not defined") }, { njs_str("a += 1; var a = 2"), njs_str("undefined") }, { njs_str("var a = 1"), njs_str("undefined") }, { njs_str("var a = 1; a = (a = 2) + a"), njs_str("4") }, { njs_str("var a = 1; a = a + (a = 2)"), njs_str("3") }, { njs_str("var a = 1; a += (a = 2)"), njs_str("3") }, { njs_str("var a = b = 1; var b; a +' '+ b"), njs_str("1 1") }, { njs_str("var a \n if (!a) a = 3; a"), njs_str("3") }, /* automatic semicolon insertion. */ { njs_str("(a\n--"), njs_str("SyntaxError: Unexpected token \"--\" in 2") }, { njs_str("(a\n++"), njs_str("SyntaxError: Unexpected token \"++\" in 2") }, { njs_str("var x = 0, y = 2; x\n--\ny; [x,y]"), njs_str("0,1") }, { njs_str("function f() {return\n}"), njs_str("undefined") }, /* if. */ { njs_str("if (0);"), njs_str("undefined") }, { njs_str("if (0) {}"), njs_str("undefined") }, { njs_str("if (0);else;"), njs_str("undefined") }, { njs_str("var a = 1; if (true); else a = 2; a"), njs_str("1") }, { njs_str("var a = 1; if (false); else a = 2; a"), njs_str("2") }, { njs_str("var a = 1; if (true) a = 2; else a = 3; a"), njs_str("2") }, { njs_str("var a = 3; if (true) if (false); else a = 2; a"), njs_str("2") }, { njs_str("var a = 3; if (true) if (false); else; a = 2; a"), njs_str("2") }, { njs_str("var a = [3], b; if (1==1||2==2) { b = '1'+'2'+a[0] }; b"), njs_str("123") }, { njs_str("(function(){ if(true) return 1 else return 0; })()"), njs_str("SyntaxError: Unexpected token \"else\" in 1") }, { njs_str("(function(){ if(true) return 1; else return 0; })()"), njs_str("1") }, { njs_str("(function(){ if(true) return 1;; else return 0; })()"), njs_str("SyntaxError: Unexpected token \"else\" in 1") }, { njs_str("(function(){ if(true) return 1\n else return 0; })()"), njs_str("1") }, { njs_str("(function(){ if(true) return 1\n;\n else return 0; })()"), njs_str("1") }, { njs_str("function f(n) {if (n) throw 'foo' else return 1}; f(0)"), njs_str("SyntaxError: Unexpected token \"else\" in 1") }, { njs_str("function f(n) {if (n)\n throw 'foo'\nelse return 1}; f(0)"), njs_str("1") }, { njs_str("function f(n) {if (n)\n throw 'foo'\nelse return 1}; f(1)"), njs_str("foo") }, { njs_str("function f(n) {if (n == 1) throw 'foo'\nelse if (n == 2) return 1}; f(2)"), njs_str("1") }, { njs_str("(function(){ for (var p in [1] ){ if (1) break else return 0; }})()"), njs_str("SyntaxError: Unexpected token \"else\" in 1") }, { njs_str("(function(){ for (var p in [1] ){ if (1) break\n else return 0; }})()"), njs_str("undefined") }, { njs_str("(function(){ for (var p in [1] ){ if (1) break; else return 0; }})()"), njs_str("undefined") }, { njs_str("(function(){ for (var p in [1] ){ if (1) continue else return 0; }})()"), njs_str("SyntaxError: Unexpected token \"else\" in 1") }, { njs_str("(function(){ for (var p in [1] ){ if (1) continue\n else return 0; }})()"), njs_str("undefined") }, { njs_str("(function(){ for (var p in [1] ){ if (1) continue; else return 0; }})()"), njs_str("undefined") }, { njs_str("(function(x){ if\n(x) return -1; else return 0; })(0)"), njs_str("0") }, { njs_str("(function(x){ if\n(\nx) return -1; else return 0; })(0)"), njs_str("0") }, { njs_str("(function(x){ if\n(\nx)\nreturn -1; else return 0; })(0)"), njs_str("0") }, { njs_str("(function(x){ if\n(\nx)\nreturn -1\n else return 0; })(0)"), njs_str("0") }, { njs_str("(function(x){ if\n(\nx)\nreturn -1\n else\nreturn 0; })(0)"), njs_str("0") }, /* do while. */ { njs_str("do { break } if (false)"), njs_str("SyntaxError: Unexpected token \"if\" in 1") }, /* for in. */ { njs_str("for (null in undefined);"), njs_str("ReferenceError: Invalid left-hand side \"null\" in for-in statement in 1") }, { njs_str("for (var a, b in []);"), njs_str("SyntaxError: Unexpected token \"in\" in 1") }, { njs_str("var s = ''; for (var p in [1,2]) {s += p}; s"), njs_str("01") }, { njs_str("var s; for (var p in [1]) {s = typeof(p)}; s"), njs_str("string") }, { njs_str("var s = ''; for (var p in {a:1, b:2}) {s += p}; s"), njs_str("ab") }, { njs_str("var s = '';" "var o = Object.defineProperty({}, 'x', {value:1});" "Object.defineProperty(o, 'y', {value:2, enumerable:true});" "for (var p in o) {s += p}; s"), njs_str("y") }, { njs_str("var o = {a:1, b:2}; var arr = []; " "for (var a in o) {arr.push(a)}; arr"), njs_str("a,b") }, { njs_str("var o = {a:1, b:2}; var arr = []; delete o.a; " "for (var a in o) {arr.push(a)}; arr"), njs_str("b") }, { njs_str("var a = []; for (var k in new Uint8Array([1,2,3])) { a.push(k); }; a"), njs_str("0,1,2") }, { njs_str("var i=0, a=[], r=[], d=[3,5];" "function ret_a() {r.push('ret_a'); return a};" "function ret_d() {r.push('ret_d'); return d};" "for (ret_a()[i++] in 0 || ret_d()) {d[2]=22; r.push(a)}; r"), njs_str("ret_d,ret_a,0,1,ret_a,0,1") }, { njs_str("this.a = 0; for (a in {b:1}) {}; a;"), njs_str("b") }, { njs_str("for (var x = x in [1,2]; ; ) {break};"), njs_str("SyntaxError: Invalid left-hand side in for-loop in 1") }, { njs_str("for (x = x in [1,2]; ; ) {break};"), njs_str("SyntaxError: Invalid left-hand side in for-loop in 1") }, { njs_str("for (var x = (x in [1,2]); ; ) {break}; x;"), njs_str("false") }, { njs_str("var x; for (x = (x in [1,2]); ; ) {break}; x;"), njs_str("false") }, { njs_str("for (++a in {}; ; ) {break}"), njs_str("SyntaxError: Invalid left-hand side in for-loop in 1") }, { njs_str("var a, b, c, d = 1; for (a + b, c = d; ; ){break}; c"), njs_str("1") }, { njs_str("var x = 1, y, z = 'a', u = {a:1};" "for (var a = x, y = z in u; ; ) {break}; y"), njs_str("SyntaxError: Invalid left-hand side in for-loop in 1") }, { njs_str("var x = 1, y, z = 'a', u = {a:1};" "for (var a = x, y= (z in u) ; ; ) {break}; y"), njs_str("true") }, { njs_str("var a = 0; for (++a; ; ) {break}; a"), njs_str("1") }, { njs_str("var a = 0; for (a++; ; ) {break}; a"), njs_str("1") }, { njs_str("var a = 0; for (+a; ; ) {break}; a"), njs_str("0") }, { njs_str("for (in + j;;) {}"), njs_str("SyntaxError: Unexpected token \"in\" in 1") }, { njs_str("for (true ? 0 in {}: 0; false; ) ;"), njs_str("undefined") }, { njs_str("for (true ? 0 : 0 in {}; false; ) ;"), njs_str("SyntaxError: Invalid left-hand side in for-loop in 1") }, { njs_str("for ((a in b)) {}"), njs_str("SyntaxError: Unexpected token \")\" in 1") }, { njs_str("var a='a', b={b:1}; for ((a in b); ; ) {break}; a"), njs_str("a") }, { njs_str("for ((a,b,c) => {};;) {break}"), njs_str("undefined") }, { njs_str("for(I in``[)8"), njs_str("SyntaxError: Unexpected token \")\" in 1") }, { njs_str("for(9A=>>"), njs_str("SyntaxError: Unexpected token \"A\" in 1") }, { njs_str("for(A?{,"), njs_str("SyntaxError: Unexpected token \",\" in 1") }, { njs_str("for(Symbol(A=>A+ in 'A') P/$"), njs_str("SyntaxError: Unexpected token \"in\" in 1") }, { njs_str("for (a(b * in d) ;"), njs_str("SyntaxError: Unexpected token \"in\" in 1") }, { njs_str("for(c=let c"), njs_str("SyntaxError: Unexpected token \"let\" in 1") }, { njs_str("for(var``>0; 0 ;) ;"), njs_str("SyntaxError: Unexpected token \"`\" in 1") }, { njs_str("for(1;;)for(-x;;)fr({-x;;)f"), njs_str("SyntaxError: Unexpected token \"-\" in 1") }, { njs_str("for(i;;)for(-new+3;;)break;"), njs_str("SyntaxError: Unexpected token \"+\" in 1") }, /* switch. */ { njs_str("switch"), njs_str("SyntaxError: Unexpected end of input in 1") }, { njs_str("switch (1);"), njs_str("SyntaxError: Unexpected token \";\" in 1") }, { njs_str("switch (1) { do { } while (1) }"), njs_str("SyntaxError: Unexpected token \"do\" in 1") }, { njs_str("switch (1) {}"), njs_str("undefined") }, { njs_str("switch (1) {default:}"), njs_str("undefined") }, { njs_str("switch (1) {case 0:}"), njs_str("undefined") }, { njs_str("switch (1) {default:;}"), njs_str("undefined") }, { njs_str("switch (1) {default:; default:}"), njs_str("SyntaxError: More than one default clause in switch statement in 1") }, { njs_str("switch (1) {case 0:;}"), njs_str("undefined") }, { njs_str("var a = 'A'; switch (a) {" "case 0: a += '0';" "case 1: a += '1';" "}; a"), njs_str("A") }, { njs_str("var a = 'A'; switch (0) {" "case 0: a += '0';" "case 1: a += '1';" "}; a"), njs_str("A01") }, { njs_str("var a = 'A'; switch (0) {" "case 0: a += '0'; break;" "case 1: a += '1';" "}; a"), njs_str("A0") }, { njs_str("var a = 'A'; switch (1) {" "case 0: a += '0';" "case 1: a += '1';" "}; a"), njs_str("A1") }, { njs_str("var a = 'A'; switch (2) {" "case 0: a += '0';" "case 1: a += '1';" "default: a += 'D';" "}; a"), njs_str("AD") }, { njs_str("var a = 'A'; switch (2) {" "case 0: a += '0';" "default: a += 'D';" "case 1: a += '1';" "}; a"), njs_str("AD1") }, { njs_str("var a = 'A'; function f(x) { a += x; return 0 }" "switch (a) {" "case f(1):" "default:" "case f(2): a += 'D';" "case f(3): a += 'T';" "} a"), njs_str("A123DT") }, { njs_str("[isNaN, undefined, isFinite]." "map((v)=>{switch(v) { case isNaN: return 1; default: return 0;}})"), njs_str("1,0,0") }, { njs_str("switch (1) {case 1: ii > 1; ii => default:}"), njs_str("SyntaxError: Unexpected token \"default\" in 1") }, { njs_str("switch (1) {case 1: ii > 1; var a = functin () {default:}"), njs_str("SyntaxError: Unexpected token \"{\" in 1") }, { njs_str("switch (1) {default: ii > 1; ii => case 2:}"), njs_str("SyntaxError: Unexpected token \"case\" in 1") }, { njs_str("switch (1) {default: ii > 1; var a = functin () {case 2:}"), njs_str("SyntaxError: Unexpected token \"{\" in 1") }, { njs_str("switch (1) {case 1: ii > 1; ii => case 2:}"), njs_str("SyntaxError: Unexpected token \"case\" in 1") }, { njs_str("switch (1) {case 1: ii > 1; var a = functin () {case 2:}"), njs_str("SyntaxError: Unexpected token \"{\" in 1") }, /* continue. */ { njs_str("continue"), njs_str("SyntaxError: Illegal continue statement in 1") }, { njs_str("\n{\ncontinue;\n}"), njs_str("SyntaxError: Illegal continue statement in 3") }, { njs_str("do continue while (false)"), njs_str("SyntaxError: Unexpected token \"while\" in 1") }, { njs_str("do continue; while (false)"), njs_str("undefined") }, { njs_str("do { continue } while (false)"), njs_str("undefined") }, { njs_str("var i = 0; do if (i++ > 9) continue; while (i < 100); i"), njs_str("100") }, { njs_str("while (false) continue"), njs_str("undefined") }, { njs_str("while (false) continue;"), njs_str("undefined") }, { njs_str("while (false) { continue }"), njs_str("undefined") }, { njs_str("var i = 0; while (i < 100) if (i++ > 9) continue; i"), njs_str("100") }, { njs_str("for ( ;null; ) continue"), njs_str("undefined") }, { njs_str("for ( ;null; ) continue;"), njs_str("undefined") }, { njs_str("for ( ;null; ) { continue }"), njs_str("undefined") }, { njs_str("var i; for (i = 0; i < 100; i++) if (i > 9) continue; i"), njs_str("100") }, { njs_str("var a = [], i; for (i in a) continue"), njs_str("undefined") }, { njs_str("var a = [], i; for (i in a) continue;"), njs_str("undefined") }, { njs_str("var a = [], i; for (i in a) { continue }"), njs_str("undefined") }, { njs_str("var a = [1,2,3,4,5]; var s = 0, i;" "for (i in a) { if (a[i] > 4) continue; else s += a[i] } s"), njs_str("10") }, { njs_str("var a = [1,2,3,4,5]; var s = 0, i;" "for (i in a) { if (a[i] > 4) continue; s += a[i] } s"), njs_str("10") }, { njs_str("var a; for (a = 1; a; a--) switch (a) { case 0: continue }"), njs_str("undefined") }, { njs_str("var a = [1,2,3], i; for (i in a) {Object.seal({})}"), njs_str("undefined") }, { njs_str("var i; for (i in [1,2,3]) {Object.seal({});}"), njs_str("undefined") }, { njs_str("while (0) {continue\n}"), njs_str("undefined") }, /* break. */ { njs_str("break"), njs_str("SyntaxError: Illegal break statement in 1") }, { njs_str("{break}"), njs_str("SyntaxError: Illegal break statement in 1") }, { njs_str("\nbreak"), njs_str("SyntaxError: Illegal break statement in 2") }, { njs_str("do break while (true)"), njs_str("SyntaxError: Unexpected token \"while\" in 1") }, { njs_str("do break; while (true)"), njs_str("undefined") }, { njs_str("do { break } while (true)"), njs_str("undefined") }, { njs_str("var i = 0; do if (i++ > 9) break; while (i < 100); i"), njs_str("11") }, { njs_str("while (true) break"), njs_str("undefined") }, { njs_str("while (true) break;"), njs_str("undefined") }, { njs_str("while (true) { break }"), njs_str("undefined") }, { njs_str("var i = 0; while (i < 100) if (i++ > 9) break; i"), njs_str("11") }, { njs_str("for ( ;; ) break"), njs_str("undefined") }, { njs_str("for ( ;; ) break;"), njs_str("undefined") }, { njs_str("for ( ;; ) { break }"), njs_str("undefined") }, { njs_str("var i; for (i = 0; i < 100; i++) if (i > 9) break; i"), njs_str("10") }, { njs_str("var a = [], i; for (i in a) break"), njs_str("undefined") }, { njs_str("var a = [], i; for (i in a) break;"), njs_str("undefined") }, { njs_str("var a = [], i; for (i in a) { break }"), njs_str("undefined") }, { njs_str("var a = [1,2,3,4,5]; var s = 0, i;" "for (i in a) { if (a[i] > 4) break; else s += a[i] } s"), njs_str("10") }, { njs_str("var a = [1,2,3,4,5]; var s = 0, i;" "for (i in a) { if (a[i] > 4) break; s += a[i] } s"), njs_str("10") }, { njs_str("var a = [1,2,3,4,5]; var s = 0, i;" "for (i in a) if (a[i] > 4) break; s += a[i]; s"), njs_str("5") }, { njs_str("while (0) {break\n}"), njs_str("undefined") }, /* Labels. */ { njs_str("var n = 0; a:{n++}; a:{n++}; n"), njs_str("2") }, { njs_str("a: throw 'a'"), njs_str("a") }, { njs_str("a\n:\n1"), njs_str("1") }, { njs_str("a\n\n:1"), njs_str("1") }, { njs_str("a:\n\n1"), njs_str("1") }, { njs_str("a:;"), njs_str("undefined") }, { njs_str("a:\n\n"), njs_str("SyntaxError: Unexpected end of input in 3") }, { njs_str("a : var n = 0; b :++n"), njs_str("1") }, { njs_str("a:{a:1}"), njs_str("SyntaxError: Label \"a\" has already been declared in 1") }, { njs_str("for (var i in [1]) {break b}"), njs_str("SyntaxError: Undefined label \"b\" in 1") }, { njs_str("for (var i in [1]) {continue b}"), njs_str("SyntaxError: Undefined label \"b\" in 1") }, { njs_str("a:{break b}"), njs_str("SyntaxError: Undefined label \"b\" in 1") }, { njs_str("a:{continue b}"), njs_str("SyntaxError: Undefined label \"b\" in 1") }, { njs_str("a:function name() {}"), njs_str("SyntaxError: In strict mode code, functions can only be declared at top level or inside a block.") }, #if 0 /* TODO */ { njs_str("a:{1; break a}"), njs_str("1") }, #endif { njs_str("var r='ok'; a:if(1){break a; r='!ok'}; r"), njs_str("ok") }, { njs_str("var r='ok'; a:if(0){break a; r='!ok1'} else {break a; r='!ok2'}; r"), njs_str("ok") }, { njs_str("var a = 0; a:{a++}; a"), njs_str("1") }, { njs_str("var a = 0; a:{break a; a++}; a"), njs_str("0") }, { njs_str("var r = 0; " "out: for (var i in [1,2,3]) { if (i == 2) {break out;}; r++}; r"), njs_str("2") }, { njs_str("var r = 0; " "out: for (var i = 0; i < 5; i++) { if (i == 2) {break out;}; r++}; r"), njs_str("2") }, { njs_str("var l1 = 0, l2 = 0; " "out: " "for (var i in [1,2,3]) { " " for (var j in [1,2,3]) { " " if (i == 1 && j == 1) {break;}" " l2++;" " }" " l1++;" "}; [l1, l2]"), njs_str("3,7") }, { njs_str("var l1 = 0, l2 = 0; " "out: " "for (var i in [1,2,3]) { " " for (var j in [1,2,3]) { " " if (i == 1 && j == 1) {break out;}" " l2++;" " }" " l1++;" "}; [l1, l2]"), njs_str("1,4") }, { njs_str("var l1 = 0, l2 = 0; " "out: " "for (var i in [1,2,3]) { " " for (var j in [1,2,3]) { " " if (i == 1 && j == 1) {continue out;}" " l2++;" " }" " l1++;" "}; [l1, l2]"), njs_str("2,7") }, { njs_str("var l1 = 0, l2 = 0; " "out: " "for (var i in [1,2,3]) { " " l1++;" " switch (i) { " " case '1':" " break out;" " default:" " }" " l2++;" "}; [l1, l2]"), njs_str("2,1") }, { njs_str("var l1 = 0, l2 = 0; " "out: " "for (var i in [1,2,3]) { " " l1++;" " switch (i) { " " case '1':" " continue out;" " default:" " }" " l2++;" "}; [l1, l2]"), njs_str("3,2") }, { njs_str("var l1 = 0, l2 = 0, i = 0, j; " "out: " "while (i < 3) { " " j = 0;" " while (j < 3) { " " if (i == 1 && j == 1) {break out;}" " l2++;" " j++;" " }" " l1++;" " i++;" "}; [l1, l2]"), njs_str("1,4") }, { njs_str("var l1 = 0, l2 = 0, i = 0, j; " "out: " "while (i < 3) { " " j = 0;" " while (j < 3) { " " if (i == 1 && j == 1) {i++; continue out;}" " l2++;" " j++;" " }" " l1++;" " i++;" "}; [l1, l2]"), njs_str("2,7") }, { njs_str("var l1 = 0, l2 = 0, i = 0, j; " "out: " "do { " " j = 0;" " do { " " if (i == 1 && j == 1) {break out;}" " l2++;" " j++;" " } while (j < 3)" " l1++;" " i++;" "} while (i < 3); [l1, l2]"), njs_str("1,4") }, { njs_str("var l1 = 0, l2 = 0, i = 0, j; " "out: " "do { " " j = 0;" " do { " " if (i == 1 && j == 1) {i++; continue out;}" " l2++;" " j++;" " } while (j < 3)" " l1++;" " i++;" "} while (i < 3); [l1, l2]"), njs_str("2,7") }, { njs_str("out1: while (1) { out2: while (1) { " " try { break out1; break out2; } catch (e) {}" "}}"), njs_str("InternalError: break/return instructions with different labels " "(\"out1\" vs \"out2\") from try-catch block are not supported") }, { njs_str("out1: while (1) { out2: while (1) { " " try { } catch (e) {break out1; break out2;} finally {}" "}}"), njs_str("InternalError: break/return instructions with different labels " "(\"out1\" vs \"out2\") from try-catch block are not supported") }, { njs_str("out1: while (1) { out2: while (1) { " " try { break out1; } catch (e) {break out2;} finally {}" "}}"), njs_str("InternalError: try break/return instructions with different labels " "(\"out1\" vs \"out2\") from try-catch block are not supported") }, { njs_str("out1: while (1) { out2: while (1) { " " try { break out1; break out2; } finally {}" "}}"), njs_str("InternalError: break/return instructions with different labels " "(\"out1\" vs \"out2\") from try-catch block are not supported") }, { njs_str("out1: while (1) { out2: while (1) { " " try { continue out1; continue out2; } catch (e) {}" "}}"), njs_str("InternalError: continue instructions with different labels " "(\"out1\" vs \"out2\") from try-catch block are not supported") }, { njs_str("out1: while (1) { out2: while (1) { " " try { continue out1; } catch (e) {continue out2;} finally {}" "}}"), njs_str("InternalError: try continue instructions with different labels " "(\"out1\" vs \"out2\") from try-catch block are not supported") }, { njs_str("function f() {" " a:{ try { try { return 'a'; } catch (e) {break a;} finally {} } " " catch (e) {} finally {}; }" "}"), njs_str("InternalError: try break/return instructions with different labels " "(\"@return\" vs \"a\") from try-catch block are not supported") }, { njs_str("a:{ try { try { continue a; } catch (e) {} finally {} } " " catch (e) {} finally {}; " "}"), njs_str("SyntaxError: Illegal continue statement in 1") }, { njs_str("var i = 0, j = 0, r = 0;" "out1: while (i < 3) " "{ " " i++;" " out2: while (j < 3) { " " j++; try { break out1; } catch (e) {} finally {r++}" " }" "}; [i, j, r]"), njs_str("1,1,1") }, { njs_str("var i = 0, j = 0, r = 0;" "out1: while (i < 3) " "{ " " i++;" " out2: while (j < 3) { " " j++; try { continue out1; } catch (e) {} finally {r++}" " }" "}; [i, j, r]"), njs_str("3,3,3") }, { njs_str("var c=0,fin=0;" "try {" " while (c < 2) {" " try { c += 1; throw 'e';}" " finally { fin = 1; break;}" " fin = -1;" " c += 2;" " }" "} catch(e) {c = 10;}; [c, fin]"), njs_str("1,1") }, { njs_str("function v1() {" "function v2 () {}" "v3:;" "1;" "} v1();"), njs_str("undefined") }, { njs_str("function v1() {" "function v2 () {}" "v3:;" "} v1();"), njs_str("undefined") }, { njs_str("{v1:;}"), njs_str("undefined") }, /* jumping out of a nested try-catch block. */ { njs_str("var r = 0; " "function f () { try { try {return 'a';} finally { r++; }} " " finally { r++; } }; " "[f(), r]"), njs_str("a,2") }, { njs_str("function f(n) { " " var r1 = 0, r2 = 0, r3 = 0;" " a:{ try { try { " " if (n == 0) { break a; } " " if (n == 1) { throw 'a'; } " " } " " catch (e) { break a; } finally { r1++; } } " " catch (e) {} " " finally { r2++; } " " r3++; " " }; " "return [r1, r2, r3]" "}; njs.dump([f(0), f(1), f(3)])"), njs_str("[[1,1,0],[1,1,0],[1,1,1]]") }, { njs_str("function f(n) {" " while (1)" " try {" " if (n == 0) { break; }" " if (n == 1) { throw 'a'; }" "" " try { return 42; }" " catch (a) {}" "" " } catch (b) { return b; }" "};" "njs.dump([f(0), f(1), f(2)])"), njs_str("[undefined,'a',42]") }, { njs_str("function f(n, r) {" " while (1)" " try {" " if (n == 0) { break; }" " if (n == 1) { throw 'a'; }" "" " try { return 42; }" " catch (a) {}" " finally { r.push('in');}" "" " } catch (b) { return b; }" " finally { r.push('out'); }" "};" "function g(n) { var r = []; return [f(n, r), r]}" "njs.dump([g(0), g(1), g(2)])"), njs_str("[[undefined,['out']],['a',['out']],[42,['in','out']]]") }, /**/ { njs_str("function f() { Object.prototype.toString = 1; };" "Object.prototype.toString = f;" "(function () { try { 's'[{}](); } catch (e) { throw e; } })()"), njs_str("TypeError: (intermediate value)[\"undefined\"] is not a function") }, { njs_str("var i; for (i = 0; i < 10; i++) { i += 1 } i"), njs_str("10") }, /* Factorial. */ { njs_str("var n = 5, f = 1; while (n--) f *= n + 1; f"), njs_str("120") }, { njs_str("var n = 5, f = 1; while (n) { f *= n; n-- } f"), njs_str("120") }, /* Fibonacci. */ { njs_str("var n = 50, x, i, j, k;" "for(i=0,j=1,k=0; k'a'}] += 1; o.a"), njs_str("2") }, { njs_str("var o = {true:1}; o[true]++; o.true"), njs_str("2") }, { njs_str("var o = {false:1}; o[false]++; o.false"), njs_str("2") }, { njs_str("var o = {undefined:1}; o[undefined]++; o.undefined"), njs_str("2") }, { njs_str("var o = {'5':1}; o[5]++; o[5]"), njs_str("2") }, { njs_str("var o = {a:1}; o[{toString:()=>'a'}]++; o.a"), njs_str("2") }, { njs_str("var a = undefined; a.b++; a.b"), njs_str("TypeError: cannot get property \"b\" of undefined") }, { njs_str("var a = null; a.b++; a.b"), njs_str("TypeError: cannot get property \"b\" of null") }, { njs_str("var a = true; a.b++; a.b"), njs_str("TypeError: property set on primitive boolean type") }, { njs_str("var a = 1; a.b++; a.b"), njs_str("TypeError: property set on primitive number type") }, { njs_str("var n = 1, o = { p: n += 1 }; o.p"), njs_str("2") }, { njs_str("var a = {}; a.b = {}; a.b.c = 1; a.b['c']"), njs_str("1") }, { njs_str("var a = {}; a.b = {}; a.b.c = 1; a['b']['c']"), njs_str("1") }, { njs_str("var a = {}; a.b = {}; var c = 'd'; a.b.d = 1; a['b'][c]"), njs_str("1") }, { njs_str("var a = {}; a.b = 1; var c = a.b++; a.b +' '+ c"), njs_str("2 1") }, { njs_str("var a = 2; a.b = 1; var c = a.b++; a +' '+ a.b +' '+ c"), njs_str("TypeError: property set on primitive number type") }, { njs_str("var x = { a: 1 }; x.a"), njs_str("1") }, { njs_str("var a = { x:1 }; var b = { y:2 }; a.x = b.y; a.x"), njs_str("2") }, { njs_str("var a = { x:1 }; var b = { y:2 }; var c; c = a.x = b.y"), njs_str("2") }, { njs_str("var a = { x:1 }; var b = { y:2 }; var c = a.x = b.y; c"), njs_str("2") }, { njs_str("var a = { x:1 }; var b = { y:2 }; a.x = b.y"), njs_str("2") }, { njs_str("var a = { x:1 }; var b = a.x = 1 + 2; a.x +' '+ b"), njs_str("3 3") }, { njs_str("var a = { x:1 }; var b = { y:2 }; var c = {};" "c.x = a.x = b.y; c.x"), njs_str("2") }, { njs_str("var y = 2, x = { a:1, b: y + 5, c:3 };" "x.a +' '+ x.b +' '+ x.c"), njs_str("1 7 3") }, { njs_str("var x = { a: 1, b: { a:2, c:5 } };" "x.a +' '+ x.b.a +' '+ x.b.c"), njs_str("1 2 5") }, { njs_str("var y = 5, x = { a:y }; x.a"), njs_str("5") }, { njs_str("var x = { a: 1; b: 2 }"), njs_str("SyntaxError: Unexpected token \";\" in 1") }, { njs_str("var x = { a: 1, b: x.a }"), njs_str("TypeError: cannot get property \"a\" of undefined") }, { njs_str("var a = { b: 2 }; a.b += 1"), njs_str("3") }, { njs_str("var o = {a:1}, c = o; o.a = o = {b:5};" "o.a +' '+ o.b +' '+ c.a.b"), njs_str("undefined 5 5") }, { njs_str("var y = { a: 2 }, x = { a: 1, b: y.a }; x.a +' '+ x.b"), njs_str("1 2") }, { njs_str("var y = { a: 1 }, x = { a: y.a++, b: y.a++ }\n" "x.a +' '+ x.b +' '+ y.a"), njs_str("1 2 3") }, { njs_str("var a='', o = {a:1, b:2}, p;" "for (p in o) { a += p +':'+ o[p] +',' } a"), njs_str("a:1,b:2,") }, { njs_str("var x = { a: 1 }, b = delete x.a; x.a +' '+ b"), njs_str("undefined true") }, /* Object shorthand property. */ { njs_str("var a = 1; njs.dump({a})"), njs_str("{a:1}") }, { njs_str("var a = 1, b; njs.dump({a,b})"), njs_str("{a:1,b:undefined}") }, { njs_str("var a = 1, b = 2; ({a,b,c})"), njs_str("ReferenceError: \"c\" is not defined") }, { njs_str("var a = 1, b = 2; njs.dump({a,b,c:3})"), njs_str("{a:1,b:2,c:3}") }, { njs_str("var b = 2, c = 3; njs.dump({a:1,b,c})"), njs_str("{a:1,b:2,c:3}") }, { njs_str("({1})"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("({default})"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("({var})"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("({this})"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("typeof ({Number}).Number"), njs_str("function") }, { njs_str("typeof ({eval}).eval"), njs_str("function") }, { njs_str("typeof ({Math}).Math.sin"), njs_str("function") }, { njs_str("delete null"), njs_str("true") }, { njs_str("var a; delete a"), njs_str("SyntaxError: Delete of an unqualified identifier in 1") }, { njs_str("delete undefined"), njs_str("SyntaxError: Delete of an unqualified identifier in 1") }, { njs_str("delete this !== true"), njs_str("false") }, /* Object shorthand methods. */ { njs_str("var o = {m(){}}; new o.m();"), njs_str("TypeError: function is not a constructor") }, { njs_str("var o = {sum(a, b){return a + b;}}; o.sum(1, 2)"), njs_str("3") }, /* Object computed property. */ { njs_str("var o = { [0]: 1, [-0]: 2 }; o[0];"), njs_str("2") }, { njs_str("var k = 'abc'.split('');var o = {[k[0]]: 'baz'}; o.a"), njs_str("baz") }, { njs_str("var k = {}; var o = {[k]() {return 'baz'}}; o[k]()"), njs_str("baz") }, { njs_str("njs.dump({[{toString(){return 'xx'}}]:1})"), njs_str("{xx:1}") }, { njs_str("var o = {}; Object.defineProperty(o, 'toString', {value:()=>'xx'});" "njs.dump({[o]:1})"), njs_str("{xx:1}") }, { njs_str("({[{toString(){return {}}}]:1})"), njs_str("TypeError: Cannot convert object to primitive value") }, { njs_str("({['a' + 'v'](){}}).av.name"), njs_str("av") }, { njs_str("({[Symbol.toStringTag](){}})[Symbol.toStringTag].name"), njs_str("[Symbol.toStringTag]") }, { njs_str("var anonSym = Symbol(); ({[anonSym]: () => {}})[anonSym].name"), njs_str("") }, { njs_str("var named = Symbol('xxx'); ({[named]: () => {}})[named].name"), njs_str("[xxx]") }, { njs_str("var obj = {}; ({[obj](){}}); typeof obj"), njs_str("object") }, { njs_str("[function(){}][0].name"), njs_str("") }, { njs_str("var called = false;" "({" " [{toString(){ if (called) throw 'OOps'; called = true; return 'a'}}](){}" "}).a.name"), njs_str("a") }, { njs_str("var o = { [new Number(12345)]: 1000 }; o[12345]"), njs_str("1000") }, { njs_str("delete NaN"), njs_str("SyntaxError: Delete of an unqualified identifier in 1") }, { njs_str("delete Infinity"), njs_str("SyntaxError: Delete of an unqualified identifier in 1") }, { njs_str("delete -Infinity"), njs_str("true") }, { njs_str("delete (1/0)"), njs_str("true") }, { njs_str("delete 1"), njs_str("true") }, { njs_str("var a = []; delete a[1]"), njs_str("true") }, { njs_str("var o = {}; [delete o.m, delete o.m]"), njs_str("true,true") }, { njs_str("[delete Array.nonexistent, delete Array.Array]"), njs_str("true,true") }, { njs_str("var a; delete (a = 1); a"), njs_str("1") }, { njs_str("delete a"), njs_str("SyntaxError: Delete of an unqualified identifier in 1") }, { njs_str("var a = 1; delete a"), njs_str("SyntaxError: Delete of an unqualified identifier in 1") }, { njs_str("function f(){} delete f"), njs_str("SyntaxError: Delete of an unqualified identifier in 1") }, { njs_str("var a = { x:1 }; ('x' in a) +' '+ (1 in a)"), njs_str("true false") }, { njs_str("delete --[][1]"), njs_str("true") }, { njs_str("var a = [1,2]; delete a.length"), njs_str("TypeError: Cannot delete property \"length\" of array") }, { njs_str("var a = [1,2,3]; a.x = 10; delete a[1]"), njs_str("true") }, { njs_str("var o = Object.create({a:1}); o.a = 2; delete o.a; o.a"), njs_str("1") }, { njs_str("delete Array.name"), njs_str("true") }, { njs_str("delete Math.max"), njs_str("true") }, { njs_str("delete Math.max.length"), njs_str("true") }, { njs_str("function f(a,b) {} " "[f.length, delete f.length, f.length, delete f.length]"), njs_str("2,true,0,true") }, { njs_str("njs.dump({break:1,3:2,'a':4,\"b\":2,true:1,null:0,async:2})"), njs_str("{3:2,break:1,a:4,b:2,true:1,null:0,async:2}") }, { njs_str("var o1 = {a:1,b:2}, o2 = {c:3}; o1.a + o2.c"), njs_str("4") }, { njs_str("({[]:1})"), njs_str("SyntaxError: Unexpected token \"]\" in 1") }, { njs_str("({'AB\\ncd':1})['AB\\ncd']"), njs_str("1") }, /* Inheritance. */ { njs_str("function Foo() {this.bar = 10;}; Foo.prototype.bar = 42; " "var v = new Foo(); delete v.bar; v.bar"), njs_str("42") }, { njs_str("function Cl(x,y) {this.x = x; this.y = y}; " "var c = new Cl('a', 'b'); Cl.prototype.z = 1; c.z"), njs_str("1") }, /**/ { njs_str("delete Math.E"), njs_str("TypeError: Cannot delete property \"E\" of object") }, { njs_str("Math.E = 1"), njs_str("TypeError: Cannot assign to read-only property \"E\" of object") }, /* "in" operation. */ { njs_str("var o = { 'a': 1, 'b': 2 }; var i; " "for (i in o) { delete o.a; delete o.b; }; njs.dump(o)"), njs_str("{}") }, { njs_str("var o = {}; Object.defineProperty(o, 'a', {value:1, configurable:1}); " "delete o.a; o.a=2; o.a"), njs_str("2") }, { njs_str("var a = {}; 1 in a"), njs_str("false") }, { njs_str("'a' in {a:1}"), njs_str("true") }, { njs_str("Symbol.unscopables in { [Symbol.unscopables]: 1 }"), njs_str("true") }, { njs_str("Object(Symbol.toStringTag) in Math"), njs_str("true") }, { njs_str("'1' in [0,,2]"), njs_str("false") }, { njs_str("var o = {}; Object.defineProperty(o, 'a', {get:()=>und}); 'a' in o"), njs_str("true") }, { njs_str("var o = {}; Object.defineProperty(o, 'a', {value:1}); ({toString(){return 'a'}}) in o"), njs_str("true") }, { njs_str("'a' in Object.create({a:1})"), njs_str("true") }, { njs_str("[false, NaN, '', Symbol()]" ".map((x) => { " " try { 'toString' in x; } " " catch (e) { return e instanceof TypeError ? e.message : ''; } " "})" ".every((x) => x.startsWith('property \"in\" on primitive'))"), njs_str("true") }, { njs_str("var p = new String('test');" "p.toString = () => { throw new Error('failed') };" "p in 1"), njs_str("TypeError: property \"in\" on primitive number type") }, { njs_str("var n = { toString: function() { return 'a' } };" "var o = { a: 5 }; o[n]"), njs_str("5") }, { njs_str("var n = { valueOf: function() { return 'a' } };" "var o = { a: 5, '[object Object]': 7 }; o[n]"), njs_str("7") }, { njs_str("var o = {}; o.new = 'OK'; o.new"), njs_str("OK") }, { njs_str("var o = { new: 'OK'}; o.new"), njs_str("OK") }, /* Arrays */ /* Empty array to primitive. */ { njs_str("3 + []"), njs_str("3") }, { njs_str("3 * []"), njs_str("0") }, /* Single element array to primitive. */ { njs_str("3 + [5]"), njs_str("35") }, { njs_str("3 * [5]"), njs_str("15") }, /* Array to primitive. */ { njs_str("3 + [5,7]"), njs_str("35,7") }, { njs_str("3 * [5,7]"), njs_str("NaN") }, { njs_str("var a = [ 1, 2, 3 ]; a[0] + a[1] + a[2]"), njs_str("6") }, { njs_str("var a = [ 1, 2, 3 ]; a[-1] = 4; a + a[-1]"), njs_str("1,2,34") }, { njs_str("var a = [ 1, 2, 3 ]; a[4294967295] = 4; a + a[4294967295]"), njs_str("1,2,34") }, { njs_str("var a = [ 1, 2, 3 ]; a[4294967296] = 4; a + a[4294967296]"), njs_str("1,2,34") }, { njs_str("delete[]['4e9']"), njs_str("true") }, { njs_str("var n = 1, a = [ n += 1 ]; a"), njs_str("2") }, { njs_str("var a = [ 1, 2; 3 ]; a[0] + a[1] + a[2]"), njs_str("SyntaxError: Unexpected token \";\" in 1") }, { njs_str("var a = [ 1, 2, 3 ]; a[0] +' '+ a[1] +' '+ a[2] +' '+ a[3]"), njs_str("1 2 3 undefined") }, { njs_str("var a = [ 5, 6, 7 ]; a['1']"), njs_str("6") }, { njs_str("var a = [ 5, 6, 7 ]; a['01']"), njs_str("undefined") }, { njs_str("var a = [ 5, 6, 7 ]; a[0x1]"), njs_str("6") }, { njs_str("var a = [ 5, 6, 7 ]; a['0x1']"), njs_str("undefined") }, { njs_str("[] - 2"), njs_str("-2") }, { njs_str("[1] - 2"), njs_str("-1") }, { njs_str("[[1]] - 2"), njs_str("-1") }, { njs_str("[[[1]]] - 2"), njs_str("-1") }, { njs_str("var a = []; a - 2"), njs_str("-2") }, { njs_str("var a = [1]; a - 2"), njs_str("-1") }, { njs_str("var a = []; a[0] = 1; a - 2"), njs_str("-1") }, { njs_str("[] + 2 + 3"), njs_str("23") }, { njs_str("[1] + 2 + 3"), njs_str("123") }, { njs_str("var a = []; a + 2 + 3"), njs_str("23") }, { njs_str("var a = [1]; a + 2 + 3"), njs_str("123") }, { njs_str("var a = [1,2], i = 0; a[i++] += a[0] = 5 + i;" "a[0] +' '+ a[1]"), njs_str("7 2") }, { njs_str("var a = []; a[0] = 1; a + 2 + 3"), njs_str("123") }, { njs_str("var a = []; a['0'] = 1; a + 2 + 3"), njs_str("123") }, { njs_str("var a = []; a[2] = 1; a[2]"), njs_str("1") }, { njs_str("var a = [1, 2]; 1 in a"), njs_str("true") }, { njs_str("var a = [1, 2]; 2 in a"), njs_str("false") }, { njs_str("var a = [1, 2]; delete a[0]; 0 in a"), njs_str("false") }, { njs_str("var a = [ function(a) {return a + 1} ]; a[0](5)"), njs_str("6") }, { njs_str("var s = '', a = [5,1,2], i;" "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"), njs_str("0:0,1:1,2:2,null:null,undefined:defined,false:false," "true:true,Infinity:Infinity,-Infinity:-Infinity,NaN:NaN,") }, { njs_str("--[][3e9]"), njs_str("NaN") }, { njs_str("[].length"), njs_str("0") }, { njs_str("[1,2].length"), njs_str("2") }, { njs_str("var a = [1,2]; a.length"), njs_str("2") }, { njs_str("[\n1]"), njs_str("1") }, { njs_str("\n[\n1\n]"), njs_str("1") }, { njs_str("\n[\n1\n,\n2]\n[\n0]"), njs_str("1") }, { njs_str("Object.create([1,2]).length"), njs_str("2") }, { njs_str("Object.create(['α','β'])[1]"), njs_str("β") }, { njs_str("Object.create(['α','β'])[false]"), njs_str("undefined") }, { njs_str("var a = ['abc']; var o = Object.create(a); o[0] = 32;" "[a,o[0]]"), njs_str("abc,32") }, /* Array.length setter */ { njs_str("[].length = {}"), njs_str("RangeError: Invalid array length") }, { njs_str("[].length = 2**32 - 1"), njs_str("4294967295") }, { njs_str("var a = []; a.length = 2**32 - 1; a.length"), njs_str("4294967295") }, { njs_str("[].length = 3e9"), njs_str("3000000000") }, { njs_str("var a = []; Object.defineProperty(a, 'length',{value: 2**32 - 1}); a.length"), njs_str("4294967295") }, { njs_str("[].length = 2**32"), njs_str("RangeError: Invalid array length") }, { njs_str("[].length = 2**32 + 1"), njs_str("RangeError: Invalid array length") }, { njs_str("[].length = -1"), njs_str("RangeError: Invalid array length") }, { njs_str("var a = [1];" "typeof (a.length = '') == 'string' && a.length == 0"), njs_str("true") }, { njs_str("var a = [1]; " "typeof (a.length = Object(2)) == 'object' && a.length == 2"), njs_str("true") }, { njs_str("var a = [1]; " "typeof (a.length = Object('2')) == 'object'"), njs_str("true") }, { njs_str("var a = [1]; " "a.length = { valueOf: () => 2 }; a.length == 2"), njs_str("true") }, { njs_str("var a = [1]; " "a.length = { toString: () => '2' }; a.length == 2"), njs_str("true") }, { njs_str("var a = []; a.length = 0; JSON.stringify(a)"), njs_str("[]") }, { njs_str("var a = []; a.length = 1; JSON.stringify(a)"), njs_str("[null]") }, { njs_str("var a = [1]; a.length = 1; JSON.stringify(a)"), njs_str("[1]") }, { njs_str("var a = [1]; a.length = 2; JSON.stringify(a)"), njs_str("[1,null]") }, { njs_str("var a = [1]; a.length = 4; a.length = 0; JSON.stringify(a)"), njs_str("[]") }, { njs_str("var a = [1,2,3]; a.length = 2; JSON.stringify(a)"), njs_str("[1,2]") }, { njs_str("var a = [1,2,3]; a.length = 3; a"), njs_str("1,2,3") }, { njs_str("var a = [1,2,3]; a.length = 16; a"), njs_str("1,2,3,,,,,,,,,,,,,") }, { njs_str("var a = [1,2,3]; a.join()"), njs_str("1,2,3") }, { njs_str("var a = [1,2,3]; a.join(':')"), njs_str("1:2:3") }, { njs_str("[" " []," " ['β', 'γ']," "]" ".map(v=>{v.length = 2**14+1; var out = v.join('α'); return [out[0], out[out.length - 1],out.length]})" ".map(v=>njs.dump(v))"), njs_str("['α','α',16384],['β','α',16386]") }, { njs_str("[" " 'α'.repeat(33)," "]" ".map(v=>{var out = ['β', 'γ'].join(v); return out.length})"), njs_str("35") }, { njs_str("[" " []," " ['β', 'γ']," " [NaN, Math.pow(2,123.2), Infinity, -1]," " [new String('β'),{toString(){return 'γ'}}]," "]" ".map(v=>{var out = v.join('α'); return [out, out[out.length - 1],out.length]})" ".map(v=>njs.dump(v))"), njs_str("['',undefined,0]," "['βαγ','γ',3]," "['NaNα1.2215056097393134e+37αInfinityα-1','1',38]," "['βαγ','γ',3]") }, { njs_str("var a = ['β','γ']; a.join('').length"), njs_str("2") }, { njs_str("var a = []; a[5] = 5; a.join()"), njs_str(",,,,,5") }, { njs_str("var a = [,null,undefined,false,true,0,1]; a.join()"), njs_str(",,,false,true,0,1") }, { njs_str("var o = { toString: function() { return null } };" "[o].join()"), njs_str("null") }, { njs_str("var o = { toString: function() { return undefined } };" "[o].join()"), njs_str("undefined") }, { njs_str("var a = [0,,2,3];" "Object.defineProperty(Array.prototype, 1, {get: ()=> {a[32] = 32; return 1}, configurable:true});" "a.join()"), njs_str("0,1,2,3") }, { njs_str("Array.prototype.join.call(new Uint8Array([0,1,2]))"), njs_str("0,1,2") }, { njs_str("var a = []; a[5] = 5; a"), njs_str(",,,,,5") }, { njs_str("var a = []; a.concat([],[1],[])"), njs_str("1") }, { njs_str("var a = []; a[Symbol.isConcatSpreadable] = undefined; [].concat(a).length"), njs_str("0") }, { njs_str("var a = []; Object.defineProperty(a, Symbol.isConcatSpreadable, {get:()=>{throw 'Oops'}}); " "[].concat(a)"), njs_str("Oops") }, { njs_str("var a = [].concat([1,2,3], {length:3, 1:4, 2:5, [Symbol.isConcatSpreadable]:1});" "njs.dump([a, a.length])"), njs_str("[[1,2,3,,4,5],6]") }, { njs_str("njs.dump([].concat([1,2,3], {length:3, 1:4, 2:5}))"), njs_str("[1,2,3,{1:4,2:5,length:3}]") }, { njs_str("Array.prototype[1] = 1; var x = [0]; x.length = 2; " "x.concat().hasOwnProperty('1') === true"), njs_str("true") }, { njs_str(njs_declare_sparse_array("a", 64) "a[32] = 1; a = a.concat([1]);" "njs.dump([a[0], a[32],a.length])"), njs_str("[undefined,1,65]") }, { njs_str(njs_declare_sparse_array("a", 64) "a[32] = 1; a = [1].concat(a);" "njs.dump([a[0], a[33],a.length])"), njs_str("[1,1,65]") }, { njs_str("var a = [1]; a[1111111] = 2;" "var a2 = [3].concat(a, [4]);" "njs.dump(a2)"), njs_str("[3,1,<1111110 empty items>,2,4]") }, { njs_str("var re = /abc/; re[Symbol.isConcatSpreadable] = true;" "re[0] = 1, re[1] = 2, re[2] = 3, re.length = 3;" "[].concat(re)"), njs_str("1,2,3") }, { njs_str("var s = new String('yuck\\uD83D\\uDCA9'); s[Symbol.isConcatSpreadable] = true;" "[].concat(s)"), njs_str("y,u,c,k,💩") }, { njs_str("var s = { toString: function() { return 'S' } };" "var v = { toString: 8, valueOf: function() { return 'V' } };" "var o = [9]; o.join = function() { return 'O' };" "var a = [1,2,3,[4,5,6],s,v,o]; a.join('')"), njs_str("1234,5,6SVO") }, { njs_str("var s = { toString: function() { return 'S' } };" "var v = { toString: 8, valueOf: function() { return 'V' } };" "var o = [9]; o.join = function() { return 'O' };" "var a = [1,2,3,[4,5,6],s,v,o]; a"), njs_str("1,2,3,4,5,6,S,V,O") }, /* Array.toString(). */ { njs_str("var a = [1,2,3]; a.join = 'NO';" "Object.prototype.toString = function () { return 'A' }; a"), njs_str("[object Array]") }, { njs_str("Array.prototype.toString.call(1)"), njs_str("[object Number]") }, { njs_str("Array.prototype.toString.call('abc')"), njs_str("[object String]") }, { njs_str(njs_declare_sparse_array("a", 32769) "var s = a.toString(); [s.length]"), njs_str("32768") }, /* Empty array elements. */ { njs_str("[,,]"), njs_str(",") }, { njs_str("[,,,]"), njs_str(",,") }, { njs_str("[1,2,]"), njs_str("1,2") }, { njs_str("[1,2,,3]"), njs_str("1,2,,3") }, { njs_str("[,,].length"), njs_str("2") }, { njs_str("[,,,].length"), njs_str("3") }, { njs_str("[1,2,,3].length"), njs_str("4") }, /**/ { njs_str("var n = { toString: function() { return 1 } }; [1,2][n]"), njs_str("2") }, { njs_str("var n = { toString: function() { return '1' } }; [1,2][n]"), njs_str("2") }, { njs_str("var n = { toString: function() { return 1 }," " valueOf: function() { return 0 } }; [1,2][n]"), njs_str("2") }, { njs_str("var n = { toString: function() { return 1.5 } };" "var a = [1,2]; a[1.5] = 5; a[n]"), njs_str("5") }, { njs_str("var n = { toString: function() { return 1.5 } };" "var a = [1,2]; a[n] = 5; a[1.5]"), njs_str("5") }, { njs_str("var n = { toString: function() { return '1.5' } };" "var a = [1,2]; a[1.5] = 5; a[n]"), njs_str("5") }, { njs_str("var n = { toString: function() { return '1.5' } };" "var a = [1,2]; a[n] = 5; a[1.5]"), njs_str("5") }, { njs_str("var n = { toString: function() { return 1.5 } };" "var a = [1,2]; a[1.5] = 5; n in a"), njs_str("true") }, { njs_str("var n = { toString: function() { return '1.5' } };" "var a = [1,2]; a[1.5] = 5; '' + (n in a) + (delete a[n])"), njs_str("truetrue") }, { njs_str("var o = {}, v = o;" "v[{toString: () => { v = 'V'; return 'a';}}] = 1;" "[v, o.a]"), njs_str("V,1") }, { njs_str("var o = null; o[{toString:()=>{throw 'OOps'}}]"), njs_str("TypeError: cannot get property \"[object Object]\" of null") }, { njs_str("var o = null; o[{toString:()=>{throw 'OOps'}}]()"), njs_str("TypeError: cannot get property \"[object Object]\" of null") }, { njs_str("var o = null; o[{toString:()=>{throw 'OOps'}}] = 1"), njs_str("TypeError: cannot set property \"[object Object]\" of null") }, { njs_str("var o = null; o[{toString:()=>{throw 'OOps'}}] += 1"), njs_str("TypeError: cannot get property \"[object Object]\" of null") }, /**/ { njs_str("Array.isArray()"), njs_str("false") }, { njs_str("Array.isArray(1)"), njs_str("false") }, { njs_str("Array.isArray(1) ? 'true' : 'false'"), njs_str("false") }, { njs_str("Array.isArray([])"), njs_str("true") }, { njs_str("Array.isArray([]) ? 'true' : 'false'"), njs_str("true") }, { njs_str("[" " [undefined]," " [null]," " ['foo']," " ['foo', c => c.toUpperCase()]," " [{length: 3, 1:'a', 2:'b'}]," " [[7,,9], v => v*2]," "].map(args => { try { return Array.from.apply(Array,args) }" " catch (e) {return e.toString()}})"), njs_str("TypeError: cannot convert null or undefined to object," "TypeError: cannot convert null or undefined to object," "f,o,o," "F,O,O," ",a,b," "14,NaN,18" ) }, { njs_str("function f() {return Array.from(arguments);}; f(1,2,3)"), njs_str("1,2,3") }, { njs_str("Array.from({ length: 5 }, (v, i) => i)"), njs_str("0,1,2,3,4") }, { njs_str("const range = (start, stop, step) =>" "Array.from({ length: (stop - start) / step + 1 }, (_, i) => start + i * step);" "range(1, 10, 2)"), njs_str("1,3,5,7,9") }, { njs_str("var a = Array.from.call(Object, { length: 2, 0:7, 1:9 });" "[a[0], a[1], Array.isArray(a)]"), njs_str("7,9,false") }, { njs_str("Array.of()"), njs_str("") }, { njs_str("Array.of(1,2,3)"), njs_str("1,2,3") }, { njs_str("Array.of(undefined,1)"), njs_str(",1") }, { njs_str("Array.of(NaN,-1,{})"), njs_str("NaN,-1,[object Object]") }, { njs_str("var a = [1,2,3]; a.concat(4, [5, 6, 7], 8)"), njs_str("1,2,3,4,5,6,7,8") }, { njs_str("var a = []; a[100] = a.length; a[100] +' '+ a.length"), njs_str("0 101") }, { njs_str("var a = [1,2]; a[100] = 100; a[100] +' '+ a.length"), njs_str("100 101") }, { njs_str("var a = []; Object.defineProperty(a, 'length', {writable:0});" "Object.getOwnPropertyDescriptor(a, 'length').writable"), njs_str("false") }, { njs_str("var a = []; Object.defineProperty(a, 'length', {writable:0});" "Object.defineProperty(a, 'length', {writable:true})"), njs_str("TypeError: Cannot redefine property: \"length\"") }, { njs_str("var a = [0,1]; Object.defineProperty(a, 'length', {writable: false}); " "Object.defineProperty(a, 'length', {value:12})"), njs_str("TypeError: Cannot redefine property: \"length\"") }, { njs_str("var a = [0,1]; Object.defineProperty(a, 'length', {writable: false}); " "Object.defineProperty(a, 'length', {value:2}); a.length"), njs_str("2") }, { njs_str ("var a =[0,1,2]; Object.defineProperty(a, 100, {value:100});" "njs.dump(a);"), njs_str("[0,1,2,<97 empty items>,100]") }, { njs_str("var a =[0,1,2]; Object.defineProperty(a, 3, {value:30});" "njs.dump(Object.getOwnPropertyDescriptor(a,3));"), njs_str("{value:30,writable:false,enumerable:false,configurable:false}") }, { njs_str("var a =[0,1,2]; Object.defineProperty(a, 3, {value:30});" "a[3]=33;"), njs_str("TypeError: Cannot assign to read-only property \"3\" of array") }, { njs_str("[1, 2, 3, 4, 5].copyWithin(0, 3)"), njs_str("4,5,3,4,5") }, { njs_str("[1, 2, 3, 4, 5].copyWithin(0, 3, 4)"), njs_str("4,2,3,4,5") }, { njs_str("[1, 2, 3, 4, 5].copyWithin(0, -2, -1)"), njs_str("4,2,3,4,5") }, { njs_str("[1, 2, 3, 4, 5].copyWithin(100, 200, 500)"), njs_str("1,2,3,4,5") }, { njs_str("[0, 1, , , 1].copyWithin(0, 1, 4)"), njs_str("1,,,,1") }, { njs_str("[0, 1, 2, 3].copyWithin(0, 1, -10)"), njs_str("0,1,2,3") }, { njs_str("var o = [0, 1, , , 1].copyWithin(0, 1, 4); typeof o"), njs_str("object") }, { njs_str("[].copyWithin.call({length: 5, 3: 1}, 0, 3)"), njs_str("[object Object]") }, { njs_str("var o = [1, 2, 3, 4]; Object.defineProperties(o, { 5: {value: 'abc'}});" "[].copyWithin.call(o, 0, 3, 4);"), njs_str("4,2,3,4,,abc") }, { njs_str("var obj = {length: 5, 3: 1}; [].copyWithin.call(obj, 0, 3);" "Object.keys(obj)"), njs_str("0,3,length") }, { njs_str("var obj = {length: 5, 1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e'};" "[].copyWithin.call(obj, 0, -2, -1);" "Object.keys(obj) + '|' + Object.values(obj)"), njs_str("0,1,2,3,4,5,length|c,a,b,c,d,e,5") }, { njs_str("var o = {length:1}; Object.defineProperty(o, '0', {get:()=>{throw Error('Oops')}});" "Array.prototype.copyWithin.call(o, 0, 0)"), njs_str("Error: Oops") }, { njs_str("Array.prototype.slice(1)"), njs_str("") }, { njs_str("Array.prototype.slice(1,2)"), njs_str("") }, { njs_str("Array.prototype.slice.call(undefined)"), njs_str("TypeError: cannot convert null or undefined to object") }, { njs_str("Array.prototype.slice.call(1)"), njs_str("") }, { njs_str("Array.prototype.slice.call(false)"), njs_str("") }, { njs_str("Array.prototype.slice.call({'0':'a', '1':'b', length:1})"), njs_str("a") }, { njs_str("Array.prototype.slice.call({'0':'a', '1':'b', length:2})"), njs_str("a,b") }, { njs_str("Array.prototype.slice.call({'0':'a', '1':'b', length:4})"), njs_str("a,b,,") }, { njs_str("Array.prototype.slice.call({'0':'a', '1':'b', length:2}, 1)"), njs_str("b") }, { njs_str("Array.prototype.slice.call({'0':'a', '1':'b', length:2}, 1, 2)"), njs_str("b") }, { njs_str("Array.prototype.slice.call({length:'2'})"), njs_str(",") }, { njs_str("njs.dump(Array.prototype.slice.call({length: 3, 1: undefined }))"), njs_str("[,undefined,]") }, { njs_str("Array.prototype.slice.call({length:new Number(3)})"), njs_str(",,") }, { njs_str("Array.prototype.slice.call({length: { valueOf: function() { return 2; } }})"), njs_str(",") }, { njs_str("Array.prototype.slice.call({ length: Object.create(null) })"), njs_str("TypeError: Cannot convert object to primitive value") }, { njs_str("Array.prototype.slice.call({length:-1})"), njs_str("") }, { njs_str("Array.prototype[1] = 1; var x = [0]; x.length = 2;" "var a = x.slice(); a.hasOwnProperty('1')"), njs_str("true") }, { njs_str("Array.prototype.slice.call('αβZγ')"), njs_str("α,β,Z,γ") }, { njs_str("Array.prototype.slice.call('αβZγ', 1)"), njs_str("β,Z,γ") }, { njs_str("Array.prototype.slice.call('αβZγ', 2)"), njs_str("Z,γ") }, { njs_str("Array.prototype.slice.call('αβZγ', 3)"), njs_str("γ") }, { njs_str("Array.prototype.slice.call('αβZγ', 4)"), njs_str("") }, { njs_str("Array.prototype.slice.call('αβZγ', 0, 1)"), njs_str("α") }, { njs_str("Array.prototype.slice.call('αβZγ', 1, 2)"), njs_str("β") }, { njs_str("Array.prototype.slice.call('αβZγ').length"), njs_str("4") }, { njs_str("Array.prototype.slice.call('αβZγ')[1].length"), njs_str("1") }, { njs_str("Array.prototype.slice.call(new String('αβZγ'))"), njs_str("α,β,Z,γ") }, { njs_str("1..__proto__.length = '2';" "Array.prototype.slice.call(1, 0, 2)"), njs_str(",") }, { njs_str("var a = [1, /**/, 3, 4];" "Object.defineProperty(a.__proto__, 1, {" " get: () => {" " a.length = 10**6;" " return 2;" " }" "});" "a.slice(1)"), njs_str("2,3,4") }, { njs_str("Array.prototype.pop()"), njs_str("undefined") }, { njs_str("var o = {0: 'a', 1: 'b', 2: 'c', 'length': 3};" "Array.prototype.pop.call(o); var res = o.length;" "Array.prototype.forEach.call(o, (v) => {res += ', ' + v}); res"), njs_str("2, a, b") }, { njs_str("var obj = {}; obj.pop = Array.prototype.pop; obj.pop(); obj.length === 0"), njs_str("true") }, { njs_str("Array.prototype[1] = 1; [0,,].pop()"), njs_str("1") }, { njs_str("Array.prototype[1] = 1; var a = [0,,]; a.pop(); a.length"), njs_str("1") }, { njs_str("Object.prototype[1] = 1; Object.prototype.length = 2; Array.prototype.pop.call({0:0})"), njs_str("1") }, { njs_str("var a = []; Object.freeze(a); Object.getOwnPropertyDescriptor(a, 'length').writable"), njs_str("false") }, { njs_str("var o = Object.freeze([0,1,2]); o.length=3"), njs_str("TypeError: Cannot assign to read-only property \"length\" of array") }, { njs_str("var o = Object.freeze({0: 0, 1: 1, length: 2}); Array.prototype.pop.call(o)"), njs_str("TypeError: Cannot delete property \"1\" of object") }, { njs_str("var i = 0; var o = {get length() {i++}};" "try {Array.prototype.pop.call(o);}" "catch (e) {i += '; ' + e} i"), njs_str("1; TypeError: Cannot set property \"length\" of object which has only a getter") }, { njs_str("Array.prototype.pop.call({ length: 3 })"), njs_str("undefined") }, { njs_str("var o = { length: 3 }; Array.prototype.pop.call(o); o.length"), njs_str("2") }, { njs_str(njs_declare_sparse_array("a", 16) "a[a.length - 1] = 'z'; a[a.length -2] = 'y';" "Array.prototype.pop.call(a); [a.length, a[a.length - 1]]"), njs_str("15,y") }, { njs_str("var a = new Array(1), arrayPrototypeGet0Calls = 0;" "Object.defineProperty(Array.prototype, '0', {" " get() { Object.defineProperty(a, 'length', {writable: false});" " arrayPrototypeGet0Calls++;" " }," "});" "var e = null;" "try { a.pop(); } catch (ee) { e = ee.name };" "[e, a.length, arrayPrototypeGet0Calls]"), njs_str("TypeError,1,1") }, { njs_str("[0,1].slice()"), njs_str("0,1") }, { njs_str("[1,2,3,,,4].slice()"), njs_str("1,2,3,,,4") }, { njs_str("[0,1].slice(undefined)"), njs_str("0,1") }, { njs_str("[0,1].slice(undefined, undefined)"), njs_str("0,1") }, { njs_str("[0,1,2,3,4].slice(1,4)"), njs_str("1,2,3") }, { njs_str("[0,1,2,3,4].slice(6,7)"), njs_str("") }, { njs_str("var a = [1,2,3,4,5], b = a.slice(3);" "b[0] +' '+ b[1] +' '+ b[2]"), njs_str("4 5 undefined") }, { njs_str("var a = [1,2]; a.pop() +' '+ a.length +' '+ a"), njs_str("2 1 1") }, { njs_str("var a = [1,2], len = a.push(3); len +' '+ a.pop() +' '+ a"), njs_str("3 3 1,2") }, { njs_str("var a = [1,2], len = a.push(3,4,5);" "len +' '+ a.pop() +' '+ a"), njs_str("5 5 1,2,3,4") }, { njs_str("var x = {'0': 'a', '1': 'b', '2': 'c', 'length': 3};" "Array.prototype.push.call(x, 'x', 'y', 'z', 123) +' '+ x[0] +' '+ x.length"), njs_str("7 a 7") }, { njs_str("var x = {'0': 'a', '1': 'b', '2': 'c', 'length': 3}; var a = [];" "Array.prototype.push.call(x, 'x', 'y', 'z', 123);" "Array.prototype.forEach.call(x, (v) => a.push(v)); a"), njs_str("a,b,c,x,y,z,123") }, { njs_str("var i = 0; var o = {get length() {i++}};" "try {Array.prototype.push.call(o);}" "catch (e) {i += '; ' + e} i"), njs_str("1; TypeError: Cannot set property \"length\" of object which has only a getter") }, { njs_str("var x = []; x.length = 4294967295; var push = x.push(); push === 4294967295"), njs_str("true") }, { njs_str("var x = []; x.length = 4294967295; x.push(); x.push(1)"), njs_str("RangeError: Invalid array length") }, { njs_str("var x = []; x.length = 4294967295; x.push(); " "try {x.push('x')} catch (e) {}; x[4294967295]"), njs_str("x") }, { njs_str("var x = []; x.length = 4294967295; x.push(); " "try {x.push('x')} catch (e) {}; x.length"), njs_str("4294967295") }, { njs_str("[" " [2**53-2, [1]]," " [2**53-2, [1,2]]," " [2**53-1, [1]]," " [2**53, []]," " [Number.POSITIVE_INFINITY, []]," "]" ".map(args=>{ try {return Array.prototype.push.apply({length:args[0]}, args[1])}" " catch (e) {return e.name} })"), njs_str("9007199254740991,TypeError,TypeError,9007199254740991,9007199254740991") }, { njs_str("Array.prototype.shift()"), njs_str("undefined") }, { njs_str("var a = [1,2,3]; a.shift() +' '+ a[0] +' '+ a.length"), njs_str("1 2 2") }, { njs_str("Array.prototype[0] = 1; var x = [,2]; x.length = 2; x.shift()"), njs_str("1") }, { njs_str("var x = {'0': 'x', '1': 'y', '2': 'z', 'length': 3};" "Array.prototype.shift.call(x) +' '+ x[0] +' '+ x.length"), njs_str("x y 2") }, { njs_str("var i = 0; var o = {get length() {i++}};" "try {Array.prototype.shift.call(o);}" "catch (e) {i += '; ' + e} i"), njs_str("1; TypeError: Cannot set property \"length\" of object which has only a getter") }, { njs_str("var a = [1,2], len = a.unshift(3);" "len +' '+ a +' '+ a.shift()"), njs_str("3 3,1,2 3") }, { njs_str("var a = [1,2], len = a.unshift(3,4,5);" "len +' '+ a +' '+ a.shift()"), njs_str("5 3,4,5,1,2 3") }, { njs_str("var x = {'0': 'x', '1': 'y', '2': 'z', 'length': 3}; var a = [];" "Array.prototype.unshift.call(x, 'a', 'b', 'c');" "Array.prototype.forEach.call(x, (v) => a.push(v)); a + ', ' + x.length"), njs_str("a,b,c,x,y,z, 6") }, { njs_str("var x = {}; x.length = 1; var a = [];" "Array.prototype.unshift.call(x, 'a', 'b', 1025, 'c', 'oh my');" "Array.prototype.forEach.call(x, (v) => a.push(v)); a + ', ' + x.length"), njs_str("a,b,1025,c,oh my, 6") }, { njs_str("var x = {5: 1, 6: 2, length: 7}; var a = [];" "Array.prototype.unshift.call(x, '0');" "Array.prototype.forEach.call(x, (v) => a.push(v)); a + ', ' + x.length"), njs_str("0,1,2, 8") }, { njs_str("var x = {5: 2, 10: 3, 11: 4, 12: 5, 20: 6, length: 21}; var a = [];" "Array.prototype.unshift.call(x, '0', '1');" "Array.prototype.forEach.call(x, (v, k) => a.push(k + ':' + v)); a + ', ' + x.length"), njs_str("0:0,1:1,7:2,12:3,13:4,14:5,22:6, 23") }, { njs_str("var x = {0: 0, length: 2**32-2};" "Array.prototype.unshift.call(x, '0', '1'); Object.keys(x).sort()"), njs_str("0,1,2,length") }, { njs_str("var x = {0: 0, length: 2**53-3};" "Array.prototype.unshift.call(x, '0', '1'); x.length"), njs_str("9007199254740991") }, { njs_str("var x = {0: 0}; Array.prototype.unshift.call(x); x.length"), njs_str("0") }, { njs_str("var obj = {'10000000': 'x', '10000001': 'y', '10000002': 'z'}; var a = [];" "obj.length = 90000000;" "Array.prototype.unshift.call(obj, 'a', 'b', 'c');" "Array.prototype.forEach.call(obj, (v) => a.push(v)); a"), njs_str("a,b,c,x,y,z")}, { njs_str("var i = 0; var o = {get length() {i++}};" "try {Array.prototype.unshift.call(o);}" "catch (e) {i += '; ' + e} i"), njs_str("1; TypeError: Cannot set property \"length\" of object which has only a getter") }, { njs_str("var a=[0], n = 64; while(--n) {a.push(n); a.shift()}; a"), njs_str("1") }, { njs_str("Array.prototype.shift.call({ length: 3 })"), njs_str("undefined") }, { njs_str("var o = { length: 3 }; Array.prototype.shift.call(o); o.length"), njs_str("2") }, { njs_str("var a = [1,2,3];" "Object.defineProperty(a, '1', {enumerable:false});" "a.shift(); a"), njs_str("2,3") }, { njs_str("var arr = [1,2];" "arr.shift();" "arr[2**20] = 3;" "arr[2**20]"), njs_str("3") }, { njs_str("var a = []; a.splice()"), njs_str("") }, { njs_str("[].splice(0,5,0)"), njs_str("") }, { njs_str("[1,2,3,4,5].splice(-2,3,0)"), njs_str("4,5") }, { njs_str("[].__proto__.splice(0,1,0)"), njs_str("") }, { njs_str("var a = [];" "a.splice(9,0,1,2).join(':') + '|' + a"), njs_str("|1,2") }, { njs_str("var a = [0,1,2,3,4,5,6,7];" "a.splice(3).join(':') + '|' + a"), njs_str("3:4:5:6:7|0,1,2") }, { njs_str("var a = [0,1,2,3,4,5,6,7];" "a.splice(3, 2).join(':') + '|' + a"), njs_str("3:4|0,1,2,5,6,7") }, { njs_str("var a = [0,1,2,3,4,5,6,7];" "a.splice(3, 2, 8, 9, 10, 11 ).join(':') + '|' + a"), njs_str("3:4|0,1,2,8,9,10,11,5,6,7") }, { njs_str("[" " []," " [1]," " [1, 2]," " [1, 2, 'a']," " [1, 2, 'a', 'b']," " [1, 2, 'a', 'b', 'c']," "]" ".map(args=>{var a = [0,1,3,4,5]; a.splice.apply(a, args); return a})" ".map(v=>v.join(''))"), njs_str("01345,0,045,0a45,0ab45,0abc45") }, { njs_str("[" " []," " [1]," " [1, 1, 'a']," " [1, 2, 'a']," " [1, 2, 'a', 'b']," " [1, 2, 'a', 'b', 'c']," "]" ".map(args=>{var a = [0,1,3,4,5]; return a.splice.apply(a, args);})" ".map(v=>v.join(''))"), njs_str(",1345,1,13,13,13") }, { njs_str("Object.prototype.splice = Array.prototype.splice;" "Object.prototype.join = Array.prototype.join;" "[" " []," " [1]," " [1, 2]," " [1, 1, 'a']," " [1, 2, 'a']," " [1, 2, 'a', 'b']," " [1, 2, 'a', 'b', 'c']," "]" ".map(args=>{var a = {0:0, 1:1, 2:3, 3:4, 4:5, length:5};" " a.splice.apply(a, args); return a})" ".map(v=>v.join(''))"), njs_str("01345,0,045,0a345,0a45,0ab45,0abc45") }, { njs_str("Object.prototype.splice = Array.prototype.splice;" "Object.prototype.join = Array.prototype.join;" "[" " []," " [1]," " [1, 0, 'a']," " [1, 1, 'a']," " [1, 2, 'a']," " [1, 2, 'a', 'b']," " [1, 2, 'a', 'b', 'c']," "]" ".map(args=>{var a = {0:0, 1:1, 2:3, 3:4, 4:5, length:5};" " return a.splice.apply(a, args);})" ".map(v=>v.join(''))"), njs_str(",1345,,1,13,13,13") }, { njs_str("var a = ['x'];" "var d = a.splice(0, { valueOf() { a.length = 0; return 10; } });" "njs.dump(d)"), njs_str("[]") }, { njs_str("var a = ['a', 'b', 'c'];" "var d = a.splice(0, { valueOf() { a.length = 2; return 3; } });" "njs.dump(d)"), njs_str("['a','b',]") }, #if NJS_HAVE_LARGE_STACK { njs_str("let arr = [ 'x' ];" "let a = { toString() {" " new Float64Array(100).set([" " {toString() {Array.prototype.splice.call(arr, a)}}" " ])" " }};" "a.toString()"), njs_str("RangeError: Maximum call stack size exceeded") }, #endif { njs_str("var o = { toString: () => {" " for (var i = 0; i < 0x10; i++) {a.push(1)};" " return {};" "}};" "var a = [o];" "a.join()"), njs_str("TypeError: Cannot convert object to primitive value") }, { njs_str("Array.prototype.splice.call({0:0,1:1,2:2,3:3,length:4},0,3,4,5)"), njs_str("0,1,2") }, { njs_str("var obj = {0:0,1:1,2:2,3:3,length:4};" "Array.prototype.splice.call(obj,0,3,4,5); obj[3]"), njs_str("undefined") }, { njs_str("var obj = {4294967294: 'x', length:-1};" "Array.prototype.splice.call(obj, 4294967294, 1); obj.length"), njs_str("0") }, { njs_str("var obj = {0:0, 1:1, 2:2};" "Object.defineProperty(obj, 'length', {value:3, writable:false});" "Array.prototype.splice.call(obj, 1, 2, 4)"), njs_str("TypeError: Cannot assign to read-only property \"length\" of object") }, { njs_str("var obj = {'9007199254740988': 'A', '9007199254740989': 'B'," " '9007199254740990': 'C', '9007199254740991': 'D', " " length: 2 ** 53 + 2};" "Array.prototype.splice.call(obj, 2**53-3, 2 ** 53 + 4)"), njs_str("B,C") }, { njs_str("var obj = {'9007199254740988': 'A', '9007199254740989': 'B'," " '9007199254740990': 'C', '9007199254740991': 'D', " " length: 2 ** 53 + 2};" "Array.prototype.splice.call(obj, 2**53-3, 2 ** 53 + 4);" "obj['9007199254740988'] == 'A' && obj['9007199254740991'] == 'D'"), njs_str("true") }, { njs_str("var obj = {'9007199254740990': 'A', '9007199254740991': 'B'," " length: 2 ** 53 - 1};" "Array.prototype.splice.call(obj, 2**53-2, 1, 'C');" "obj['9007199254740990'] == 'C' && obj['9007199254740991'] == 'B'"), njs_str("true") }, { njs_str("var obj = {'9007199254740990': 'A', '9007199254740991': 'B'," " length: 2 ** 53 - 1};" "Array.prototype.splice.call(obj, 2**53-2, 0, 'C');"), njs_str("TypeError: Invalid length") }, { njs_str("var a = {1: 'B', length: 2};" "Array.prototype.splice.call(a, 0)"), njs_str(",B") }, { njs_str("var a = new Uint8Array();" "a.__proto__ = [1,2,3];" "a.splice(0)"), njs_str(",,") }, { njs_str("'/A/B/C/D/'.split('/').toSpliced(1,1).join('/')"), njs_str("/B/C/D/") }, { njs_str("let r, arr = new Array(4);" "Object.defineProperty(arr, 0, { get: () => { throw 'Oops'; } });" "try { r = arr.toSpliced(0, 0); } catch (e) { }" "r.toString()"), njs_str("TypeError: cannot get property \"toString\" of undefined") }, { njs_str("var a = []; a.reverse()"), njs_str("") }, { njs_str("var a = [1]; a.reverse()"), njs_str("1") }, { njs_str("var a = [1,2]; a.reverse()"), njs_str("2,1") }, { njs_str("var a = [1,2,3]; a.reverse()"), njs_str("3,2,1") }, { njs_str("var a = [1,2,3,4]; a.reverse()"), njs_str("4,3,2,1") }, { njs_str("[1,2,3,,,].reverse()"), njs_str(",,3,2,1") }, { njs_str("[,2,3,,,].reverse()"), njs_str(",,3,2,") }, { njs_str("[,,,3,2,1].reverse()"), njs_str("1,2,3,,,") }, { njs_str("var a = [,,2,1];" "Object.defineProperty(a.__proto__, 0, {" " get: () => {" " a.length = 10**6;" " return 4;" " }," " set: (setval) => { Object.defineProperty(a, 0, { value: setval }); }," "});" "a.reverse();" "a.slice(0, 4)"), njs_str("1,2,,4") }, { njs_str("var o = {1:true, 2:'', length:-2}; Array.prototype.reverse.call(o) === o"), njs_str("true") }, { njs_str("[" " ['a','b','c']," " ['a','b','c','d']," " [,'b','c','d']," " ['a','b','c',]," " [,'b','c',]," "]" ".map(v=>Object.defineProperty(v, 1, {value:v[1], enumerable:false}))" ".map(v=>v.reverse().join(''))"), njs_str("cba,dcba,dcb,cba,cb") }, { njs_str("Array.prototype[1] = 1; var x = [0]; x.length = 2; x.reverse(); x"), njs_str("1,0") }, { njs_str("Array.prototype[0] = 0; var x = [,1]; x.reverse(); x"), njs_str("1,0") }, { njs_str("let r, arr = new Array(4);" "Object.defineProperty(arr, 0, { get: () => { throw 'Oops'; } });" "try { r = arr.toReversed(0, 0); } catch (e) { }" "r.toString()"), njs_str("TypeError: cannot get property \"toString\" of undefined") }, { njs_str("var a = [,3,2,1]; njs.dump([a.toReversed(),a])"), njs_str("[[1,2,3,undefined],[,3,2,1]]") }, { njs_str("var a = [1,2,3,4]; a.indexOf()"), njs_str("-1") }, { njs_str("var a = [1,2,3,4]; a.indexOf(5)"), njs_str("-1") }, { njs_str("var a = [1,2,3,4]; a.indexOf(4, 3)"), njs_str("3") }, { njs_str("var a = [1,2,3,4]; a.indexOf(4, 4)"), njs_str("-1") }, { njs_str("var a = [1,2,3,4,3,4]; a.indexOf(3, '2')"), njs_str("2") }, { njs_str("var a = [1,2,3,4,3,4]; a.indexOf(4, -1)"), njs_str("5") }, { njs_str("var a = [1,2,3,4,3,4]; a.indexOf(3, -10)"), njs_str("2") }, { njs_str("[].indexOf.bind(0)(0, 0)"), njs_str("-1") }, { njs_str("var o = 'abcd';" "Array.prototype.indexOf.call(o, 'c')"), njs_str("2") }, { njs_str("var o = {0: 'a', 1: 'b', 2: 'c'};" "Object.defineProperty(o, 'length', {get: () => 4});" "Object.defineProperty(o, '3', {get: () => 'd'});" "Array.prototype.indexOf.call(o, 'd')"), njs_str("3") }, { njs_str("var i = 0; var o = {get length() {i++}};" "Array.prototype.indexOf.call(o); i"), njs_str("1") }, { njs_str("var a = new Array(); a[100] =1; a[99999] = ''; a[10] = new Object(); " "a[5555] = 5.5; a[123456] = 'str'; a[5] = 1E+309; " "[1, '', 'str', 1E+309, 5.5, true, 5, 'str1', null, new Object()].map(v=>a.indexOf(v))"), njs_str("100,99999,123456,5,5555,-1,-1,-1,-1,-1") }, { njs_str("Array.prototype.indexOf.call({199:true, 200:'200.59', length:200}, '200.59')"), njs_str("-1") }, { njs_str("Array.prototype.indexOf.call({199:true, 200:'200.59', length:201}, '200.59')"), njs_str("200") }, { njs_str("Array.prototype.indexOf.call({1:true, 2:'200.59', length:2}, '200.59')"), njs_str("-1") }, { njs_str("Array.prototype.indexOf.call({1:true, 2:'200.59', length:3}, '200.59')"), njs_str("2") }, { njs_str("var stopped = 0;" "var o = {length:3}; " "Object.defineProperty(o, '1',{get:()=>{throw 'Oops'}});" "Object.defineProperty(o, '2', {get:()=>stopped++});" "try { Array.prototype.indexOf.call(o, 7)} catch (e) {};" "stopped"), njs_str("0") }, { njs_str("[].lastIndexOf(1, -1)"), njs_str("-1") }, { njs_str("[undefined].lastIndexOf()"), njs_str("0") }, { njs_str("[undefined].lastIndexOf(undefined)"), njs_str("0") }, { njs_str("var a = [1,2,3,4]; a.lastIndexOf()"), njs_str("-1") }, { njs_str("var a = [1,2,3,4]; a.lastIndexOf(5)"), njs_str("-1") }, { njs_str("var a = [1,2,3,4,3,4]; a.lastIndexOf(1, 0)"), njs_str("0") }, { njs_str("var a = [1,2,3,4,3,4]; a.lastIndexOf(3, '2')"), njs_str("2") }, { njs_str("var a = [1,2,3,4,3,4]; a.lastIndexOf(1, 6)"), njs_str("0") }, { njs_str("var a = [1,2,3,4,3,4]; a.lastIndexOf(2, 6)"), njs_str("1") }, { njs_str("var a = [1,2,3,4,3,4]; a.lastIndexOf(4, -1)"), njs_str("5") }, { njs_str("var a = [1,2,3,4,3,4]; a.lastIndexOf(4, -6)"), njs_str("-1") }, { njs_str("var a = [1,2,3,4,3,4]; a.lastIndexOf(3, -10)"), njs_str("-1") }, { njs_str("[1,2,3].lastIndexOf(1, -5.3)"), njs_str("-1") }, { njs_str("[1,2,1].lastIndexOf(2,undefined)"), njs_str("-1") }, { njs_str("[1,2,1].lastIndexOf(1,undefined)"), njs_str("0") }, { njs_str("[1,2,1].lastIndexOf(1)"), njs_str("2") }, { njs_str("var stopped = 0;" "var o = {length:3}; " "Object.defineProperty(o, '1', {get:()=>stopped++});" "Object.defineProperty(o, '2',{get:()=>{throw 'Oops'}});" "try { Array.prototype.lastIndexOf.call(o)} catch (e) {};" "stopped"), njs_str("0") }, { njs_str("var o = 'addc';" "Array.prototype.lastIndexOf.call(o, 'd')"), njs_str("2") }, { njs_str("var o = 'dddd';" "Array.prototype.lastIndexOf.call(o, 'd')"), njs_str("3") }, { njs_str("var o = 'dabc';" "Array.prototype.lastIndexOf.call(o, 'd')"), njs_str("0") }, { njs_str("var o = 'АБВГ';" "Array.prototype.lastIndexOf.call(o, 'Г')"), njs_str("3") }, { njs_str("var o = 'ГВБА';" "Array.prototype.lastIndexOf.call(o, 'Г')"), njs_str("0") }, { njs_str("var o = 'ВГБА';" "Array.prototype.lastIndexOf.call(o, 'Г')"), njs_str("1") }, { njs_str("var o = {0: 'a', 1: 'd', 2: 'd'};" "Object.defineProperty(o, 'length', {get: () => 4});" "Object.defineProperty(o, '3', {get: () => 'd'});" "Array.prototype.lastIndexOf.call(o, 'd')"), njs_str("3") }, { njs_str("var a = new Array(); a[100] =1; a[99999] = ''; a[10] = new Object(); " "a[5555] = 5.5; a[123456] = 'str'; a[5] = 1E+309; " "[1,'', 'str', 1E+309, 5.5, true, 5, 'str1', null, new Object()].map(v=>a.lastIndexOf(v))"), njs_str("100,99999,123456,5,5555,-1,-1,-1,-1,-1") }, { njs_str("var obj = {'10000000': 'x', '10000001': 'y', '10000002': 'z'}; var a = [];" "obj.length = 90000000;" "Array.prototype.lastIndexOf.call(obj, 'y');"), njs_str("10000001")}, { njs_str("var i = 0; var o = {get length() {i++}};" "Array.prototype.lastIndexOf.call(o); i"), njs_str("1") }, { njs_str("Array.prototype.lastIndexOf.call({199:true, 200:'200.59', length:200}, '200.59')"), njs_str("-1") }, { njs_str("Array.prototype.lastIndexOf.call({0:'undefined', length:0}, 'undefined')"), njs_str("-1") }, { njs_str("[1,0,-1,-2].map(v => Array.prototype.lastIndexOf.call('Ф', 'Ф', v))"), njs_str("0,0,0,-1") }, { njs_str("[''].lastIndexOf.call('00000000000000000000000000000а00')"), njs_str("-1") }, { njs_str("var o = 'ГВБА';" "Array.prototype.lastIndexOf.call(o, 'Г', 0)"), njs_str("0") }, { njs_str("var o = 'ГВБА';" "Array.prototype.lastIndexOf.call(o, 'Г', 4)"), njs_str("0") }, { njs_str("[1,2,3,4].includes()"), njs_str("false") }, { njs_str("[1,2,3,4].includes(5)"), njs_str("false") }, { njs_str("[1,2,3,4].includes(4, 3)"), njs_str("true") }, { njs_str("[1,2,3,4].includes(4, 4)"), njs_str("false") }, { njs_str("[1,2,3,4,3,4].includes(3, '2')"), njs_str("true") }, { njs_str("[1,2,3,4,3,4].includes(4, -1)"), njs_str("true") }, { njs_str("[1,2,3,4,3,4].includes(3, -10)"), njs_str("true") }, { njs_str("[1,2,3,NaN,3,4].includes(NaN)"), njs_str("true") }, { njs_str("[1,2,3,4,5].includes(NaN)"), njs_str("false") }, { njs_str("[].includes.bind(0)(0, 0)"), njs_str("false") }, { njs_str("var o = {0: 'a', 1: 'b', 2: 'c'};" "Object.defineProperty(o, 'length', {get: () => 4});" "Object.defineProperty(o, '3', {get: () => 'd'});" "Array.prototype.includes.call(o, 'd')"), njs_str("true") }, { njs_str("var obj = {'0': 'a', '1': 'b', '10000000': 'c', '10000001': 'd', '10000002': 'e'};" "var fromIndex = 1;" "obj.length = 90000000;" "Array.prototype.includes.call(obj, 'c', fromIndex);"), njs_str("true") }, { njs_str("var obj = {'0': 'a', '1': 'b', '10000000': 'c', '10000001': 'd', '10000002': 'e'};" "var fromIndex = 1;" "obj.length = 90000000;" "Array.prototype.includes.call(obj, 'a', fromIndex);"), njs_str("false") }, { njs_str("var stopped = 0;" "var o = {length:3}; " "Object.defineProperty(o, '1',{get:()=>{throw 'Oops'}});" "Object.defineProperty(o, '2', {get:()=>stopped++});" "try { Array.prototype.includes.call(o, 7)} catch (e) {};" "stopped"), njs_str("0") }, { njs_str("var a = []; var s = { sum: 0 };" "a.forEach(function(v, i, a) { this.sum += v }, s); s.sum"), njs_str("0") }, { njs_str("var a = new Array(3); var s = { sum: 0 };" "a.forEach(function(v, i, a) { this.sum += v }, s); s.sum"), njs_str("0") }, { njs_str("var a = [,,,]; var s = { sum: 0 };" "a.forEach(function(v, i, a) { this.sum += v }, s); s.sum"), njs_str("0") }, { njs_str("var a = [1,2,3]; var s = { sum: 0 };" "a.forEach(function(v, i, a) { this.sum += v }, s); s.sum"), njs_str("6") }, { njs_str("var a = [1,2,3];" "a.forEach(function(v, i, a) { a[i+3] = a.length }); a"), njs_str("1,2,3,3,4,5") }, { njs_str("function f() { var c; [1].forEach(function(v) { c })}; f()"), njs_str("undefined") }, { njs_str("var a = [1,2,3]; var s = { sum: 0 };" "[].forEach.call(a, function(v, i, a) { this.sum += v }, s);" "s.sum"), njs_str("6") }, { njs_str("var a = [1,2,3]; var s = { sum: 0 };" "[].forEach.apply(a," "[ function(v, i, a) { this.sum += v }, s ]);" "s.sum"), njs_str("6") }, { njs_str("var a = []; var c = 0;" "a.forEach(function(v, i, a) { c++ }); c"), njs_str("0") }, { njs_str("var a = [,,,,]; var c = 0;" "a.forEach(function(v, i, a) { c++ }); c"), njs_str("0") }, { njs_str("var o = {0: 'a', 1: 'b', 2: 'c'};" "Object.defineProperty(o, 'length', {get: () => 4});" "Object.defineProperty(o, '3', {get: () => 'd'});" "var r = ''; Array.prototype.forEach.call(o, function(v, i, a) { r += v }); r"), njs_str("abcd") }, { njs_str("var s = 't'; var t = '';" "Array.prototype.forEach.call(s, function (a, b, c) {t = typeof c;}); [t, typeof s];"), njs_str("object,string") }, { njs_str("[].some(function(v) { return v > 1 })"), njs_str("false") }, { njs_str("[11].some(function(v) { return 5 })"), njs_str("true") }, { njs_str("[1,2,3].some(function(v) { return v > 1 })"), njs_str("true") }, { njs_str("[1,2,3].some(function(v) { return v > 2 })"), njs_str("true") }, { njs_str("[1,2,3].some(function(v) { return v > 3 })"), njs_str("false") }, { njs_str("var o = {0: 'a', 1: 'b', 2: 'c', 'length': { valueOf: function() { return 3 }}};" "var r = Array.prototype.some.call(o, function(el, i, arr) {return el == 'c'}); r"), njs_str("true") }, { njs_str("var o = {0: 'a', 1: 'b', 2: 'd', 'length': { valueOf: function() { return 3 }}};" "var r = Array.prototype.some.call(o, function(el, i, arr) {return el == 'c'}); r"), njs_str("false") }, { njs_str("var o = {0: 'a', 1: 'b', 2: 'c'};" "Object.defineProperty(o, 'length', {get: () => 4});" "Object.defineProperty(o, '3', {get: () => 'd'});" "var r = Array.prototype.some.call(o, function(v, i, a) { return v === 'd' }); r"), njs_str("true") }, { njs_str("[].every(function(v) { return v > 1 })"), njs_str("true") }, { njs_str("var accessed = false;" "[1].every((v) => {accessed = true; return 0; }) === false && accessed === true"), njs_str("true") }, { njs_str("[3,2,1].every(function(v) { return v > 3 })"), njs_str("false") }, { njs_str("[3,2,1].every(function(v) { return v > 2 })"), njs_str("false") }, { njs_str("[3,2,1].every(function(v) { return v > 0 })"), njs_str("true") }, { njs_str("var o = {0: 'c', 1: 'b', 2: 'c', 'length': { valueOf() { return 3 }}};" "var r = Array.prototype.every.call(o, function(el, i, arr) {return el == 'c'}); r"), njs_str("false") }, { njs_str("var o = {0: 'c', 1: 'c', 2: 'c', 'length': { valueOf() { return 3 }}};" "var r = Array.prototype.every.call(o, function(el, i, arr) {return el == 'c'}); r"), njs_str("true") }, { njs_str("var obj = new Date(); obj.length = 1; obj[0] = 1;" "Array.prototype.every.call(obj, (val,idx,obj)=>!(obj instanceof Date))"), njs_str("false") }, { njs_str("Array.prototype.every.call({0:11,1:9,length:2**32+1}, val=>val>10)"), njs_str("false") }, { njs_str("var vis = false; var a = []; " "Object.defineProperty(a, '0', {get:()=>{vis = true; return 11;}, configurable:true});" "Object.defineProperty(a, '1', {get:()=>{if (vis) {return 9;} else {return 11}}, configurable:true});" "a.every(val=>val > 10)"), njs_str("false") }, { njs_str("var o = {0: 'x', 1: 'y', 2: 'z'};" "Object.defineProperty(o, 'length', {get: () => 4});" "Object.defineProperty(o, '3', {get: () => 'a'});" "var r = Array.prototype.some.call(o, function(v, i, a) { return v === 'a' }); r"), njs_str("true") }, { njs_str("var o = {0: 'a', 1: 'b', 2: 'c'};" "Object.defineProperty(o, 'length', {get: () => 4});" "Object.defineProperty(o, '3', {get: () => 'b'});" "var r = Array.prototype.some.call(o, function(v, i, a) { return v === 'y' }); r"), njs_str("false") }, { njs_str("[].fill(1);"), njs_str("") }, { njs_str("[1,2,3].fill(5);"), njs_str("5,5,5") }, { njs_str("[1,2,3].fill(5, 0);"), njs_str("5,5,5") }, { njs_str("[1,2,3].fill(5, 1);"), njs_str("1,5,5") }, { njs_str("[1,2,3].fill(5, 4);"), njs_str("1,2,3") }, { njs_str("[1,2,3].fill(5, -2);"), njs_str("1,5,5") }, { njs_str("[1,2,3].fill(5, -3);"), njs_str("5,5,5") }, { njs_str("[1,2,3].fill(5, -4);"), njs_str("5,5,5") }, { njs_str("[1,2,3].fill(5, 1, 0);"), njs_str("1,2,3") }, { njs_str("[1,2,3].fill(5, 1, 1);"), njs_str("1,2,3") }, { njs_str("[1,2,3].fill(5, 1, 2);"), njs_str("1,5,3") }, { njs_str("[1,2,3].fill(5, 1, 3);"), njs_str("1,5,5") }, { njs_str("[1,2,3].fill(5, 1, 4);"), njs_str("1,5,5") }, { njs_str("[1,2,3].fill(5, 1, -1);"), njs_str("1,5,3") }, { njs_str("[1,2,3].fill(5, 1, -3);"), njs_str("1,2,3") }, { njs_str("[1,2,3].fill(5, 1, -4);"), njs_str("1,2,3") }, { njs_str("[1,2,3].fill(\"a\", 1, 2);"), njs_str("1,a,3") }, { njs_str("[1,2,3].fill({a:\"b\"}, 1, 2);"), njs_str("1,[object Object],3") }, { njs_str("Array(3).fill().reduce(function(a, x)" "{ return a + (x === undefined); }, 0)"), njs_str("3") }, { njs_str("var a = Array.prototype.fill.apply(" "Object({length: 40}), [\"a\", 1, 20]); Object.values(a)"), njs_str("a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,40") }, { njs_str("var a = Array.prototype.fill.apply({length: " "{ valueOf: function() { return 40 }}}, [\"a\", 1, 20]);" "Object.values(a)"), njs_str("a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,[object Object]") }, { njs_str("[NaN, false, ''].map(" "(x) => Array.prototype.fill.call(x)" ").every((x) => typeof x == 'object')"), njs_str("true") }, { njs_str("var o = {}; Object.defineProperty(o, 'length', {get:()=>2}); " "Array.prototype.slice.call(Array.prototype.fill.call(o, 1))"), njs_str("1,1") }, { njs_str("var o = {}; Object.defineProperty(o, 'length', {get:()=>'0x0002'}); " "Array.prototype.slice.call(Array.prototype.fill.call(o, 1))"), njs_str("1,1") }, { njs_str("Array.prototype.slice.call({length:2**32})"), njs_str("RangeError: Invalid array length") }, { njs_str("Array.prototype.slice.call({0:'x', [2**32-1]:'y',length:2**32}, 0, 2**32)"), njs_str("RangeError: Invalid array length") }, { njs_str("Array.prototype.slice.call({length:2**32+2, [2**32]:'x', [2**32+1]:'y'}, 2**32)"), njs_str("x,y") }, { njs_str("Array.prototype.slice.call({length:2**53+2, [2**53-3]:'x', [2**53-2]:'y', [2**53-1]:'z'}, 2**53-3)"), njs_str("x,y") }, { njs_str("var o = {}; Object.defineProperty(o, 'length', {get:()=> {throw TypeError('Boom')}}); " "Array.prototype.fill.call(o, 1)"), njs_str("TypeError: Boom") }, { njs_str("var o = Object({length: 3});" "Object.defineProperty(o, '0', {set: ()=>{throw TypeError('Boom')}});" "Array.prototype.fill.call(o, 1)"), njs_str("TypeError: Boom") }, { njs_str("var o = Object({length: 3});" "Object.defineProperty(o, '0', {set: function(v){this.a = 2 * v}});" "Array.prototype.fill.call(o, 2).a"), njs_str("4") }, { njs_str("var a = (new Array(2**10)).fill(0);" "var start = {valueOf() {" " var len = a.length - 2;" " for (var i = 0; i < len; i++) { a.shift(); }; " " return 0;" " }};" "a.fill('xxx', start)"), njs_str("xxx,xxx") }, { njs_str("Array.prototype.fill.call(new Int32Array(1))"), njs_str("0") }, { njs_str("ArrayBuffer()"), njs_str("TypeError: Constructor ArrayBuffer requires 'new'") }, { njs_str("new ArrayBuffer()"), njs_str("[object ArrayBuffer]") }, { njs_str("ArrayBuffer.prototype.constructor.name === 'ArrayBuffer'"), njs_str("true") }, { njs_str("ArrayBuffer.prototype.constructor()"), njs_str("TypeError: Constructor ArrayBuffer requires 'new'") }, { njs_str("ArrayBuffer.name"), njs_str("ArrayBuffer") }, { njs_str("ArrayBuffer[Symbol.species]"), njs_str("[object Function]") }, { njs_str("ArrayBuffer.prototype[Symbol.toStringTag]"), njs_str("ArrayBuffer") }, { njs_str("var desc = Object.getOwnPropertyDescriptor(ArrayBuffer," "Symbol.species); desc.get"), njs_str("[object Function]") }, { njs_str("var ctor = ArrayBuffer[Symbol.species]; var a = new ctor(100);" "a.byteLength;"), njs_str("100") }, { njs_str("var a = new ArrayBuffer(); a.byteLength"), njs_str("0") }, { njs_str("var a = new ArrayBuffer.prototype.constructor(10); a.byteLength"), njs_str("10") }, { njs_str("var get = Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'byteLength').get;" "get.call([])"), njs_str("TypeError: Method ArrayBuffer.prototype.byteLength called on incompatible receiver") }, { njs_str("[undefined, 1, 10, 1000, null, NaN, false, {}, [1,2,3], Object(1),'10'," " -1, -Infinity, Infinity, 2**50]" ".map(v=>{ var a; try { a = new ArrayBuffer(v) } catch (e) {return e.name} return a.byteLength})"), njs_str("0,1,10,1000,0,0,0,0,0,1,10,RangeError,RangeError,RangeError,RangeError") }, { njs_str("var buffer = new ArrayBuffer(16);" "[[4,12], [-1,-1], [-1,10], [0, -1], [0, -16], [0,-17]]" ".map(pr=>buffer.slice(pr[0], pr[1]).byteLength)"), njs_str("8,0,0,15,0,0") }, { njs_str("[false,NaN,1]" ".map(v=>(new Uint8Array(v)).length)"), njs_str("0,0,1") }, #define NJS_TYPED_ARRAY_LIST "[Uint8Array,Uint8ClampedArray,Int8Array," \ " Uint16Array,Int16Array,Uint32Array," \ " Int32Array, Float32Array,Float64Array]" #define NJS_INT_TYPED_ARRAY_LIST "[Uint8Array,Uint8ClampedArray,Int8Array," \ " Uint16Array,Int16Array,Uint32Array," \ " Int32Array]" #define NJS_FLOAT_TYPED_ARRAY_LIST "[Float32Array,Float64Array]" { njs_str("var TypedArray = Object.getPrototypeOf(Uint8Array);" "[TypedArray.name, TypedArray.length]"), njs_str("TypedArray,0") }, { njs_str("Object.getPrototypeOf(Uint8Array)()"), njs_str("TypeError: Abstract class TypedArray not directly constructable") }, { njs_str("var TypedArray = Object.getPrototypeOf(Uint8Array);" NJS_TYPED_ARRAY_LIST ".every(v=>Object.getPrototypeOf(v) === TypedArray)"), njs_str("true") }, { njs_str("var TypedArray = Object.getPrototypeOf(Uint8Array);" NJS_TYPED_ARRAY_LIST ".every(v=>Object.getPrototypeOf(v.prototype) === TypedArray.prototype)"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v.prototype.constructor(0); " " return njs.dump(a) === `${v.name} []`})"), njs_str("true") }, { njs_str("var global = this;" NJS_TYPED_ARRAY_LIST ".every(v=>ArrayBuffer.isView(new global[v.name]))"), njs_str("true") }, { njs_str("var global = this;" NJS_TYPED_ARRAY_LIST ".every(v=>global[v.name][Symbol.species].name === v.name)"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v({length:2, '0':1, '1':2}); " " return a[0] == 1 && a[1] == 2 && a.length == 2})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{try{ new v({length:Math.pow(2,53)}) } " " catch(e) {return e.name == 'InternalError' || e.name == 'RangeError'}})"), njs_str("true") }, { njs_str(NJS_FLOAT_TYPED_ARRAY_LIST ".every(v=>{var a = new v({length:5, 0: null, 2:42, 3:'7', 4:NaN, 5:Symbol('1')}); " " return njs.dump(a) === `${v.name} [0,NaN,42,7,NaN]`})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var o = {length:2, '0':1}; Object.defineProperty(o, '1', {get(){throw 'Oops'}});" " try {new v(o)} catch (e) { return e == 'Oops'}})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v(3); Object.defineProperty(a, '1', {value:1});" " return njs.dump(a) === `${v.name} [0,1,0]`})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v([1,1,1]); Object.defineProperty(a, '1', {});" " return njs.dump(a) === `${v.name} [1,1,1]`})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{try {var a = new v([1,1]); Object.defineProperty(a, '1', {configurable:true})} " " catch (e) { return e.message == 'Cannot redefine property: \"1\"'}})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{try {var a = new v([1,1]); Object.defineProperty(a, '1', {enumerable:false})} " " catch (e) { return e.message == 'Cannot redefine property: \"1\"'}})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{try {var a = new v([1,1]); Object.defineProperty(a, '1', {writable:false})} " " catch (e) { return e.message == 'Cannot redefine property: \"1\"'}})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{try {var a = new v([1,1]); Object.defineProperty(a, '1', {get(){return 22}})} " " catch (e) { return e.message == 'Cannot redefine property: \"1\"'}})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v(3);" " return [a.hasOwnProperty('1'), a.hasOwnProperty('4')].toString() === 'true,false'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var descs = Object.getOwnPropertyNames(v).sort().toString(); " " return descs === 'BYTES_PER_ELEMENT,length,name,prototype'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".map(v=>{var a = new ArrayBuffer(8); return (new v(a).length)})"), njs_str("8,8,8,4,4,2,2,2,1") }, { njs_str(NJS_TYPED_ARRAY_LIST ".map(v=>{var a = new v(1); a[0]--; return a[0]})"), njs_str("255,0,-1,65535,-1,4294967295,-1,-1,-1") }, { njs_str(NJS_NOT_CANONICAL_INDICES ".every(v=>{var a = new Uint8Array([1,2]); return a[v] === undefined})"), njs_str("true") }, { njs_str(NJS_NOT_CANONICAL_INDICES ".map(v=>{var a = new Uint8Array([1,2]); a[v] = 'a'; return a[v]})"), njs_str("a,,a,a,a,,,,,a") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{v.prototype[10] = 'foo'; return (new v(16))[10] === 0})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{v.prototype[20] = 'foo'; return (new v(16))[20] === undefined})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{v.prototype.foo = 'bar'; return (new v(16)).foo === 'bar'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{v.prototype[-1] = 'foo'; return (new v(8))[-1] === undefined})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".map(v=>v.BYTES_PER_ELEMENT)"), njs_str("1,1,1,2,2,4,4,4,8") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>v.length === 3)"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v(1); return --a[0] == -1})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = v.of(); return njs.dump(a) === `${v.name} []`})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = v.of(1); return njs.dump(a) === `${v.name} [1]`})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = v.of(1,2,3,4,5); return njs.dump(a) === `${v.name} [1,2,3,4,5]`})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{ try{ v.of(Symbol()); } catch (e) { return e.name === 'TypeError'}})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{ try{ v.of.call(()=>1); } catch (e) { return e.name === 'TypeError'}})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{ try{ v.of.call(function(){}); } catch (e) { return e.name === 'TypeError'}})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = v.from([1,2]); return njs.dump(a) === `${v.name} [1,2]`})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = v.from([1,2], v=>2*v); return njs.dump(a) === `${v.name} [2,4]`})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = v.from([1,2], function(v){return v * this.m}, {m:3}); " " return njs.dump(a) === `${v.name} [3,6]`})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = v.from([1,2], function(v){return v * this.m}, {m:3}); " " return njs.dump(a) === `${v.name} [3,6]`})"), njs_str("true") }, { njs_str(NJS_INT_TYPED_ARRAY_LIST ".every(v=>{var a = v.from({length:3, 0:1, 2:'a'});" " return njs.dump(a) === `${v.name} [1,0,0]`})"), njs_str("true") }, { njs_str(NJS_FLOAT_TYPED_ARRAY_LIST ".every(v=>{var a = v.from({length:3, 0:1, 2:'a'});" " return njs.dump(a) === `${v.name} [1,NaN,NaN]`})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v(4); a.fill(42); return (a[0] === 42 && a.length == 4)})"), njs_str("true") }, { njs_str(NJS_INT_TYPED_ARRAY_LIST ".every(v=>{var a = new v(1); a.fill({}); return a[0] === 0})"), njs_str("true") }, { njs_str(NJS_FLOAT_TYPED_ARRAY_LIST ".every(v=>{var a = new v(1); a.fill({}); return isNaN(a[0])})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v([1,2,3]); a.fill({valueOf(){return 12}}, 1,2); " " return (a[0] === 1 && a[1] === 12 && a[2] === 3 && a.length == 3)})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v([0,0,0,0,0]).fill(8, -1, -3); " " return njs.dump(a) === `${v.name} [0,0,0,0,0]`;})"), njs_str("true") }, { njs_str(NJS_INT_TYPED_ARRAY_LIST ".every(v=>{var a = new v([1,2,3]); a.fill('qq', 1, 2); " " return (a[0] === 1 && a[1] === 0 && a[2] === 3 && a.length == 3)})"), njs_str("true") }, { njs_str(NJS_FLOAT_TYPED_ARRAY_LIST ".every(v=>{var a = new v([1,2,3]); a.fill('qq', 1, 2); " " return (a[0] === 1 && isNaN(a[1]) && a[2] === 3 && a.length == 3)})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v([1,2,3]); var d = Object.getOwnPropertyDescriptors(a)[1];" " return (d.value === 2 && d.writable && d.enumerable && !d.configurable)})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v([1,2,3]); return Object.keys(a).toString() === '0,1,2'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v([1,2,3]); return Object.values(a).toString() === '1,2,3'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v([1,2,3]); return Object.entries(a).toString() === '0,1,1,2,2,3'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{try {delete (new v(1))[0]} catch (e) { return e.name == 'TypeError'}})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{return delete (new v(1))[-1]; })"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v(8), b = new v(a.buffer); a[0] = 42; return b[0] === 42})"), njs_str("true") }, { njs_str(NJS_INT_TYPED_ARRAY_LIST ".map(v=>{var init = new Uint8Array([1,2,3]);" " try { return new v(init.buffer, 0, 2)} catch (e) {return e.name}})"), njs_str("1,2,1,2,1,2,RangeError,RangeError,RangeError,RangeError") }, { njs_str(NJS_INT_TYPED_ARRAY_LIST ".map(v=>{var init = new Uint8Array([1,2,3]);" " try { return new v(init.buffer, 1)} catch (e) {return e.name}})"), njs_str("2,3,2,3,2,3,RangeError,RangeError,RangeError,RangeError") }, { njs_str(NJS_INT_TYPED_ARRAY_LIST ".map(v=>{var init = new Uint32Array([0xaabbccdd]);" " try { return new v(init.buffer, 0, 2)} catch (e) {return e.name}})"), njs_str(njs_evar("221,204,221,204,-35,-52,52445,43707,-13091,-21829,RangeError,RangeError", "170,187,170,187,-86,-69,43707,52445,-21829,-13091,RangeError,RangeError")) }, { njs_str(NJS_INT_TYPED_ARRAY_LIST ".map(v=>{var init = new Uint32Array([0xaabbccdd]);" " try { return new v(init.buffer, 1, 2)} catch (e) {return e.name}})"), njs_str(njs_evar("204,187,204,187,-52,-69,RangeError,RangeError,RangeError,RangeError", "187,204,187,204,-69,-52,RangeError,RangeError,RangeError,RangeError")) }, { njs_str(NJS_INT_TYPED_ARRAY_LIST ".map(v=>{var init = new Uint32Array([0xaabbccdd,0xdeadbeef]);" " try { return new v(init.buffer, 0, 2)} catch (e) {return e.name}})"), njs_str(njs_evar("221,204,221,204,-35,-52,52445,43707,-13091,-21829," "2864434397,3735928559,-1430532899,-559038737", "170,187,170,187,-86,-69,43707,52445,-21829,-13091," "2864434397,3735928559,-1430532899,-559038737")) }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var buffer1 = new ArrayBuffer(8 * v.BYTES_PER_ELEMENT);" " var ta1 = new v(buffer1);" " var ta2 = new v(ta1.buffer, 4 * v.BYTES_PER_ELEMENT); " " ta1[5] = 100; ta1[7] = 101;" " return ta2.toString() === '0,100,0,101'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v([42,11]); return a.toString() === '42,11'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v(3); a[1] = 42; return a.toString() === '0,42,0'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v(0); return a.toString() === ''})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v([42,11]); return a.join('|') === '42|11'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v([42,11]); return a.join('α').length === 5})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{return njs.dump(new v()) === `${v.name} []`})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{return njs.dump(new v([42,11])) === `${v.name} [42,11]`})"), njs_str("true") }, { njs_str("var a = new Uint8Array(8); var b = new Uint32Array(a.buffer);" "a[0] = 0xff; a[1] = 0xff; a[2] = 0xff; a[3] = 0xff; b[0];"), njs_str("4294967295") }, { njs_str("[1,300,-100]" ".map(v=>{var a = new Uint8Array(1); a[0] = v; return a[0];})"), njs_str("1,44,156") }, { njs_str("[1,300,-100]" ".map(v=>{var a = new Uint8ClampedArray(1); a[0] = v; return a[0];})"), njs_str("1,255,0") }, { njs_str("[1,300,-100]" ".map(v=>{var a = new Uint8ClampedArray(1); a.set([v], 0); return a[0];})"), njs_str("1,255,0") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var init = [1,2,3,4]; var a = new v(4);" " a.set(init); return a.toString() === '1,2,3,4'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var init = {length:4, 0:1,1:2,2:3,3:4}; var a = new v(4);" " a.set(init); return a.toString() === '1,2,3,4'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var init = new v([1,2,3,4]); var a = new v(init);" " return a.toString() === '1,2,3,4'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var init = new v([1,2,3,4]); var a = new v(4);" " a.set(init); return a.toString() === '1,2,3,4'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var init = {length:2, 0:1,1:2}; var a = new v(4);" " a.set(init,2); return a.toString() === '0,0,1,2'})"), njs_str("true") }, { njs_str(NJS_INT_TYPED_ARRAY_LIST ".every(v=>{var init = {length:4, 0:1,1:2,3:4}; var a = new v(4);" " a.set(init); return a.toString() === '1,2,0,4'})"), njs_str("true") }, { njs_str(NJS_FLOAT_TYPED_ARRAY_LIST ".every(v=>{var init = {length:4, 0:1,1:2,3:4}; var a = new v(4);" " a.set(init); return a.toString() === '1,2,NaN,4'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var init = [1,2,3]; var a = new v(4);" " a.set(init); return a.toString() === '1,2,3,0'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var init = new v([1,2,3]); var a = new v(4);" " a.set(init); return a.toString() === '1,2,3,0'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var init = [1,2,3,4]; var a = new v(4);" " a.set(init, 0); return a.toString() === '1,2,3,4'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var init = [1,2]; var a = new v(4);" " a.set(init, 2); return a.toString() === '0,0,1,2'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var init = new v([1,2]); var a = new v(4);" " a.set(init,2); return a.toString() === '0,0,1,2'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var init = [1,2,3,4]; var a = new v(4);" " try {a.set(init,2)} catch (e) {return e.name == 'RangeError'};})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var init = [1,2,3,4]; var a = new v(4);" " try {a.set(init,Infinity)} catch (e) {return e.name == 'RangeError'};})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var init = [1,2,3,4,5]; var a = new v(4);" " try {a.set(init)} catch (e) {return e.name == 'RangeError'};})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var init = {length:5}; var a = new v(4);" " try {a.set(init)} catch (e) {return e.name == 'RangeError'};})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var init = {length:3}; var a = new v(4);" " try {a.set(init,2)} catch (e) {return e.name == 'RangeError'};})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var init = {length:3}; var a = new v(4);" " try {a.set(init,Infinity)} catch (e) {return e.name == 'RangeError'};})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var init = new v([1,2,3,4,5]); var a = new v(4);" " try {a.set(init)} catch (e) {return e.name == 'RangeError'};})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var init = new v([1,2,3]); var a = new v(4);" " try {a.set(init,2)} catch (e) {return e.name == 'RangeError'};})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var init = new v([1,2,3]); var a = new v(4);" " try {a.set(init,Infinity)} catch (e) {return e.name == 'RangeError'};})"), njs_str("true") }, { njs_str("[-1,-1.00001,-Infinity]" ".every(v=>{ try {(new Uint8Array(10)).set([], v)} catch (ee) {return ee.name === 'RangeError'}})"), njs_str("true") }, { njs_str("[0.1,2.5,{},{'1': '10'},[1000]]" ".map(v=>{var a = new Uint8Array(1); a.set([v], 0); return a[0]})"), njs_str("0,2,0,0,232") }, { njs_str("[1.0, -1234.0]" ".map(v=>{var a = new Float32Array(1); a[0] = v; var b = new Uint8Array(a.buffer);" " return (b[0] << 24 | b[1] << 16| b[2] <<8 | b[3]).toString(16).padStart(8, '0');})"), njs_str(njs_evar("0000803f,00409ac4", "3f800000,-3b65c000")) }, { njs_str("var a = new ArrayBuffer(0); a.slice(0, 0).byteLength"), njs_str("0") }, { njs_str("var a = new ArrayBuffer(10); a.slice(1).byteLength"), njs_str("9") }, { njs_str("var a = new ArrayBuffer(10); a.slice(1,2).byteLength"), njs_str("1") }, { njs_str("var a = new ArrayBuffer(10); a.slice(0,-1).byteLength"), njs_str("9") }, { njs_str(NJS_TYPED_ARRAY_LIST ".map(v=>{var buffer = new ArrayBuffer(8); var view = new v(buffer);" " view[0] = 511; return new Uint8Array(buffer.slice(0,4))})"), njs_str(njs_evar("255,0,0,0,255,0,0,0,255,0,0,0,255,1,0,0,255,1," "0,0,255,1,0,0,255,1,0,0,0,128,255,67,0,0,0,0", "255,0,0,0,255,0,0,0,255,0,0,0,1,255,0,0,1,255,0,0," "0,0,1,255,0,0,1,255,67,255,128,0,64,127,240,0")) }, { njs_str(NJS_TYPED_ARRAY_LIST ".map(v=>{var buffer = new ArrayBuffer(8); var view = new v(buffer);" " view[view.length - 1] = 511; return new Uint8Array(buffer.slice(4))})"), njs_str(njs_evar("0,0,0,255,0,0,0,255,0,0,0,255,0,0,255,1,0,0,255,1," "255,1,0,0,255,1,0,0,0,128,255,67,0,240,127,64", "0,0,0,255,0,0,0,255,0,0,0,255,0,0,1,255,0,0,1,255," "0,0,1,255,0,0,1,255,67,255,128,0,0,0,0,0")) }, { njs_str(NJS_TYPED_ARRAY_LIST ".map(v=>{var buffer = new ArrayBuffer(8); var view = new v(buffer);" " view[0] = 511; return new Uint8Array(buffer.slice(0,-4))})"), njs_str(njs_evar("255,0,0,0,255,0,0,0,255,0,0,0,255,1,0,0,255,1,0,0," "255,1,0,0,255,1,0,0,0,128,255,67,0,0,0,0", "255,0,0,0,255,0,0,0,255,0,0,0,1,255,0,0,1,255,0,0," "0,0,1,255,0,0,1,255,67,255,128,0,64,127,240,0")) }, { njs_str("var a = new Uint8Array(10); var b = a.slice(1); b.length"), njs_str("9") }, { njs_str("var a = new Uint8Array(10); var b = a.slice(0,9); b.length"), njs_str("9") }, { njs_str("var a = new Uint8Array(10); var b = a.slice(9,10); b.length"), njs_str("1") }, { njs_str("var a = new Uint8Array(10); var b = a.slice(); b.length"), njs_str("10") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v([42]); " " var r = a.slice();" " return njs.dump(r) === `${v.name} [42]`;})"), njs_str("true") }, { njs_str(NJS_INT_TYPED_ARRAY_LIST ".map(v=>{var init = new Uint8Array([1,2,3,4,5,6,7,8]); var view = new v(init.buffer);" " return view.slice(0,2)})"), njs_str(njs_evar("1,2,1,2,1,2,513,1027,513,1027,67305985,134678021,67305985,134678021", "1,2,1,2,1,2,258,772,258,772,16909060,84281096,16909060,84281096")) }, { njs_str(NJS_INT_TYPED_ARRAY_LIST ".map(v=>{var init = new Uint8Array([1,2,3,4,5,6,7,8]); var view = new v(init.buffer);" " return view.slice(0,-2)})"), njs_str(njs_evar("1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,513,1027,513,1027,,", "1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,258,772,258,772,,")) }, { njs_str("var other = new Uint8Array([0xff,0xff,0xff,0xff]);" NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v([42]); " " a.constructor = {[Symbol.species]: function () {return other;}}; " " var r = a.slice(0,0);" " return r == other && r.length == 4 && r[0] == 0xff;})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v([1,2,3]); " " a.constructor = {[Symbol.species]: function () {return new v([0xff,0xee]);}}; " " try {a.slice(0)} catch(e) {return e.name == 'TypeError'}})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v([1,2,3]); " " a.constructor = {[Symbol.species]: function () {return new Date();}}; " " try {a.slice(0)} catch(e) {return e.name == 'TypeError'}})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v(2); " " a.constructor = {}; " " a.constructor[Symbol.species] = function() { return new v()};" " try {a.filter(v=>true)} catch(e) {return e.name == 'TypeError'}})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v([1,2,3]); " " var r = a.slice(1,3);" " return a.buffer !== r.buffer;})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v([1,2,3]); " " var r = a.slice(1,3);" " a[1] = 0;" " return njs.dump(r) === `${v.name} [2,3]`;})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v([1,2,3,4]); " " var r = a.subarray(1,3);" " a[1] = 0;" " return njs.dump(r) === `${v.name} [0,3]`;})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v([1,2,3,4]); " " a.subarray(1,3).fill(0);" " return njs.dump(a) === `${v.name} [1,0,0,4]`;})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v([1,2,3,4]); " " a.subarray(1,10).fill(0);" " return njs.dump(a) === `${v.name} [1,0,0,0]`;})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v([1,2,3]); " " var r = a.subarray(1,3);" " return a.buffer === r.buffer;})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v([1,2,3]); " " return a.subarray(3).length === 0;})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v([1,2,3,4]); a.copyWithin(2); " " return a.toString() === '1,2,1,2'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v([1,2,3,4]); a.copyWithin(2,1); " " return a.toString() === '1,2,2,3'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v([1,2,3,4]); a.copyWithin(2,1,2); " " return a.toString() === '1,2,2,4'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v([1,2,3,4]); a.copyWithin(-1,1,2); " " return a.toString() === '1,2,3,2'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v([1,2,3,4]); a.copyWithin(-1,-4,2); " " return a.toString() === '1,2,3,1'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v([1,2,3,4]); a.copyWithin(-1,-2); " " return a.toString() === '1,2,3,3'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var orig = new v([255,255,1,2,3,4,5]);" " var a = new v(orig.buffer, 2* v.BYTES_PER_ELEMENT);" " a.copyWithin(0,3);" " return a.toString() === '4,5,3,4,5'})"), njs_str("true") }, { njs_str("Uint8Array.prototype.every.call(1)"), njs_str("TypeError: this is not a typed array") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{return (new v([1,2,3])).every(e=>e>0) === true})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{return (new v([1,2,3])).every(function(e) {" " if (this != undefined) {throw 'Oops';}" " return e > 0}) === true})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{return (new v([1,2,3])).every(function(e) {" " if (this != 'QQ') {throw 'Oops';}" " return e > 0}, 'QQ') === true})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{return (new v([1,2,3])).every(e=>e>1) === false})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var orig = new v([255,255,1,2,3,255]);" " var a = new v(orig.buffer, 2 * v.BYTES_PER_ELEMENT, 3);" " return a.every(e=>e<4)})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var collect = []; (new v([42,43])).forEach(e=>collect.push(e)); " " return collect.join('|') === '42|43'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{return (new v([7,10,3,8,5])).filter(q=>q%2).join('|') === '7|3|5'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var orig = new v([255,255,7,10,3,8,5,255]);" " var a = new v(orig.buffer, 2 * v.BYTES_PER_ELEMENT, 5);" " return a.filter(q=>q%2).join('|') === '7|3|5'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var orig = new v([255,255,1,2,3,255]);" " var a = new v(orig.buffer, 2 * v.BYTES_PER_ELEMENT, 3);" " return a.find(e=>e>2) === 3})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var orig = new v([255,255,1,2,3,255]);" " var a = new v(orig.buffer, 2 * v.BYTES_PER_ELEMENT, 3);" " return a.find(e=>e===255) === undefined})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var orig = new v([255,255,1,2,3,255]);" " var a = new v(orig.buffer, 2 * v.BYTES_PER_ELEMENT, 3);" " return a.findIndex(e=>e>2) === 2})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var orig = new v([255,255,1,2,3,255]);" " var a = new v(orig.buffer, 2 * v.BYTES_PER_ELEMENT, 3);" " return a.findIndex(e=>e===255) === -1})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{return (new v([1,2,3])).some(e=>e==2)})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var orig = new v([255,255,1,2,3,255]);" " var a = new v(orig.buffer, 2 * v.BYTES_PER_ELEMENT, 3);" " return a.some(e=>e==255)})"), njs_str("false") }, { njs_str("Uint8Array.prototype.includes.call(1)"), njs_str("TypeError: this is not a typed array") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{return (new v()).includes(0, {valueOf(){throw 'Oops'}}) === false})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{return (new v([0,1,2,3])).includes(2) === true})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{return (new v([0,1,2,3])).includes(2,3) === false})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v(5);" " return a.includes(0, 4) === true " " && a.includes(0, 5) === false;})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{return (new v([0,1,2,3])).includes(-0) === true})"), njs_str("true") }, { njs_str(NJS_FLOAT_TYPED_ARRAY_LIST ".every(v=>{return (new v([42, 43, NaN, 41])).includes(NaN) === true})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var orig = new v([255,255,0,2,3,255]);" " var a = new v(orig.buffer, 2 * v.BYTES_PER_ELEMENT, 3);" " return a.includes(255) === false})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{return (new v()).indexOf(0, {valueOf(){throw 'Oops'}}) === -1})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{return (new v([3,2,1])).indexOf(2) === 1})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{return (new v([3,2,1])).indexOf(2,2) === -1})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{return (new v([3,2,1])).indexOf(2,Infinity) === -1})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{return (new v([3,2,1])).indexOf(2,-Infinity) === 1})"), njs_str("true") }, { njs_str(NJS_FLOAT_TYPED_ARRAY_LIST ".every(v=>{return (new v([42, 43, NaN, 41])).indexOf(NaN) === -1})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{return (new v([3,2,1])).indexOf(257) === -1})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{return (new v([3,2,1])).indexOf(2.00001) === -1})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{return (new v([3,2,1,0])).indexOf(-0) === 3})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v(5);" " return a.indexOf(0, 4) === 4" " && a.indexOf(0, 5) === -1;})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var orig = new v([255,255,0,2,3,255]);" " var a = new v(orig.buffer, 2 * v.BYTES_PER_ELEMENT, 3);" " return a.indexOf(255) === -1})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{return (new v([3,2,1,2])).lastIndexOf(2) === 3})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{return (new v([3,2,1,2])).lastIndexOf(4) === -1})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{return (new v([42, 43])).lastIndexOf(42,0) === 0})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{return (new v([42, 43, 43, 41])).lastIndexOf(43,Infinity) === 2})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{return (new v([42, 43, 43, 41])).lastIndexOf(43,-Infinity) === -1})"), njs_str("true") }, { njs_str(NJS_FLOAT_TYPED_ARRAY_LIST ".every(v=>{return (new v([42, 43, NaN, 41])).lastIndexOf(NaN) === -1})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var orig = new v([255,255,0,2,3,255]);" " var a = new v(orig.buffer, 2 * v.BYTES_PER_ELEMENT, 3);" " return a.lastIndexOf(255) === -1})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{return (new v([3,2,1])).map(q=>2*q).join('|') === '6|4|2'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var orig = new v([255,255,6,4,2,255]);" " var a = new v(orig.buffer, 2 * v.BYTES_PER_ELEMENT, 3);" " return a.map(q=>q/2).join('|') === '3|2|1'})"), njs_str("true") }, { njs_str("const arr = new Uint8Array([1,2,3]);" "const sep = {toString(){$262.detachArrayBuffer(arr.buffer); return ','}};" "arr.join(sep)"), njs_str("TypeError: detached buffer") }, { njs_str("Uint8Array.prototype.reduce.call(1)"), njs_str("TypeError: this is not a typed array") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{ try { (new v([])).reduce((p, q) => p + q) } " " catch (e) { return e.name == 'TypeError'} })"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{ return (new v([])).reduce((p, q) => p + q, 10) == 10})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{ return (new v([7])).reduce((p, q) => p + q) == 7})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{ return (new v([7])).reduce((p, q) => p + q, 10) == 17})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{ return (new v([1,2,3])).reduce((p, q) => p + q) == 6})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{ return (new v([1,2,3])).reduce((p, q) => p + q, 10) == 16})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{ return (new v([1,2,3])).reduce((p, q) => p + q, '') == '123'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{ return (new v([3,2,1])).reduce((p, q, i) => { " " if (q + i != 3) {throw 'Oops'}; " " return p + q;}) == 6})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{ var a = new v([3,2,1]); " " return a.reduce((p, q, _, o) => { " " if (a != o) {throw 'Oops'}; " " return p + q;}) == 6})"), njs_str("true") }, { njs_str("var a = [3,2,1]; a.reduce((p, v, _, o) => { if (a != o) {throw 'Oops'};return p + v})"), njs_str("6") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{ try { (new v([])).reduceRight((p, q) => p + q) } " " catch (e) { return e.name == 'TypeError'} })"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{ return (new v([])).reduceRight((p, q) => p + q, 10) == 10})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{ return (new v([7])).reduceRight((p, q) => p + q) == 7})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{ return (new v([7])).reduceRight((p, q) => p + q, 10) == 17})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{ return (new v([1,2,3])).reduceRight((p, q) => p + q) == 6})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{ return (new v([1,2,3])).reduceRight((p, q) => p + q, 10) == 16})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{ return (new v([1,2,3])).reduceRight((p, q) => p + q, '') == '321'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{ return (new v([3,2,1])).reduceRight((p, q, i) => { " " if (q + i != 3) {throw 'Oops'}; " " return p + q;}) == 6})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{ var a = new v([3,2,1]); " " return a.reduceRight((p, q, _, o) => { " " if (a != o) {throw 'Oops'}; " " return p + q;}) == 6})"), njs_str("true") }, { njs_str("var a = [3,2,1]; a.reduceRight((p, v, _, o) => { if (a != o) {throw 'Oops'};return p + v})"), njs_str("6") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{ return (new v([1,2,3])).reverse().join('|') == '3|2|1'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{ return (new v([1,2,3,4])).reverse().join('|') == '4|3|2|1'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v([3,2,1]);" " return [a.toReversed(), a].toString() === '1,2,3,3,2,1'})"), njs_str("true") }, { njs_str("Uint8Array.prototype.sort.call(1)"), njs_str("TypeError: this is not a typed array") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v([]); a.sort(); " " return a.toString() === ''})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v([5]); a.sort(); " " return a.toString() === '5'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v([3,3,2,1]); a.sort(); " " return a.toString() === '1,2,3,3'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v([3,3,2,1]); a.sort((x,y)=>x-y); " " return a.toString() === '1,2,3,3'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = (new v([255,255,3,3,2,1])).slice(2); a.sort(); " " return a.toString() === '1,2,3,3'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{ var a = [];" " var b = {toString() {a.length = 65535; return 99;}};" " for (var c = 0; c < 3; c++) { a[c] = b; }" " var ta = new v(6); ta.set(a);" " return ta.toString() == '99,99,99,0,0,0';" " })"), njs_str("true") }, { njs_str("(new Float32Array([255,255,NaN,3,NaN,Infinity,3,-Infinity,0,-0,2,1,-5])).slice(2).sort()"), njs_str("-Infinity,-5,0,0,1,2,3,3,Infinity,NaN,NaN") }, { njs_str("(new Float64Array([255,255,NaN,3,NaN,Infinity,3,-Infinity,0,-0,2,1,-5])).slice(2).sort()"), njs_str("-Infinity,-5,0,0,1,2,3,3,Infinity,NaN,NaN") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = new v([3,2,1]);" " return [a.toSorted(),a].toString() === '1,2,3,3,2,1'})"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every(v=>{var a = (new v([3,2,1]));" " a.constructor = (v == Uint8Array) ? Uint32Array : Uint8Array;" " return Object.getPrototypeOf(a.toSorted()) === v.prototype})"), njs_str("true") }, { njs_str("(new DataView(new ArrayBuffer(3)))"), njs_str("[object DataView]") }, { njs_str("var x = new ArrayBuffer(3); [typeof x, typeof new DataView(x)]"), njs_str("object,object") }, { njs_str("(new DataView(new ArrayBuffer(3))).buffer"), njs_str("[object ArrayBuffer]") }, { njs_str("(new DataView(new ArrayBuffer(3))).byteLength"), njs_str("3") }, { njs_str("(new DataView(new ArrayBuffer(3), 1)).byteLength"), njs_str("2") }, { njs_str("(new DataView(new ArrayBuffer(3), 3)).byteLength"), njs_str("0") }, { njs_str("(new DataView(new ArrayBuffer(3), 1)).byteOffset"), njs_str("1") }, { njs_str("(new DataView(new ArrayBuffer(3), 1, 1)).byteLength"), njs_str("1") }, { njs_str("(new DataView(new ArrayBuffer(3), 4))"), njs_str("RangeError: byteOffset 4 is outside the bound of the buffer") }, { njs_str("(new DataView(new ArrayBuffer(3), 1,3))"), njs_str("RangeError: Invalid DataView length: 3") }, { njs_str("var u8 = new Uint8Array([255, 129, 130, 131, 4, 5, 6, 7, 8, 9, 255]); " "var dv = new DataView(u8.buffer, 1); " "['getUint8', 'getInt8'," " 'getUint16', 'getInt16'," " 'getUint32', 'getInt32'," " 'getFloat32','getFloat64'" "]" ".map(fn => [dv[fn](0), dv[fn](0,1), dv[fn](1), dv[fn](1,1)])"), njs_str("129,129,130,130," "-127,-127,-126,-126," "33154,33409,33411,33666," "-32382,-32127,-32125,-31870," "2172814084,75727489,2189624325,84181890," "-2122153212,75727489,-2105342971,84181890," "-4.794245620412925e-38,3.091780090135418e-36,-1.9251027092506622e-37,6.230764342760857e-36," "-2.159546358334202e-301,5.447603729090798e-270,-1.4538065947240604e-296,3.72581468952343e-265") }, { njs_str("var u8 = new Uint8Array(10);" "var dv = new DataView(u8.buffer, 1);" "var u8view = new Uint8Array(u8.buffer, 1);" "function run(test) {" " var fn = test[0];" " var val = test[1];" " var size = parseInt(fn.match(/\\d+/)) / 8;" " " " return [[0], [0,1],[1], [1,1]].map(args => {" " var offset = args[0];" " var le = args[1];" " u8.fill(0); " " dv[fn].apply(dv, [offset, val, le]);" " return `[${u8view.subarray(0, offset + size)}]`;" " })" "};" "[" " ['setUint8', 129]," " ['setInt8', -127]," " ['setUint16', 33154]," " ['setInt16', -32382]," " ['setUint32', 2172814084]," " ['setInt32', -2122153212]," " ['setFloat32', -4.794245620412925e-38]," " ['setFloat64', -2.159546358334202e-301]," "]" ".map(t => run(t))"), njs_str("[129],[129],[0,129],[0,129]," "[129],[129],[0,129],[0,129]," "[129,130],[130,129],[0,129,130],[0,130,129]," "[129,130],[130,129],[0,129,130],[0,130,129]," "[129,130,131,4],[4,131,130,129],[0,129,130,131,4],[0,4,131,130,129]," "[129,130,131,4],[4,131,130,129],[0,129,130,131,4],[0,4,131,130,129]," "[129,130,131,4],[4,131,130,129],[0,129,130,131,4],[0,4,131,130,129]," "[129,130,131,4,5,6,7,8],[8,7,6,5,4,131,130,129],[0,129,130,131,4,5,6,7,8],[0,8,7,6,5,4,131,130,129]" ) }, { njs_str("var u8 = new Uint8Array([1,2,3]); " "var dv = new DataView(u8.buffer); " "dv.getUint16(2)"), njs_str("RangeError: index 2 is outside the bound of the buffer") }, #if NJS_HAVE_LARGE_STACK { njs_str("var o = Object({length: 3});" "Object.defineProperty(o, '0', {set: function(v){this[0] = 2 * v}});" "Array.prototype.fill.call(o, 2)"), njs_str("RangeError: Maximum call stack size exceeded") }, #endif { njs_str("var i = 0; var o = {get length() {i++}};" "Array.prototype.fill.call(o); i"), njs_str("1") }, { njs_str("var a = [];" "a.filter(function(v, i, a) { return v > 1 })"), njs_str("") }, { njs_str("var a = [1,2,3,-1,5];" "a.filter(function(v, i, a) { return v > 1 })"), njs_str("2,3,5") }, { njs_str("var a = [1,2,3,4,5,6,7,8];" "a.filter(function(v, i, a) { a.pop(); return v > 1 })"), njs_str("2,3,4") }, { njs_str("var a = [1,2,3,4,5,6,7,8];" "a.filter(function(v, i, a) { a.shift(); return v > 1 })"), njs_str("3,5,7") }, { njs_str("var a = [1,2,3,4,5,6,7];" "a.filter(function(v, i, a) { a.pop(); return v > 1 })"), njs_str("2,3,4") }, { njs_str("var a = [1,2,3,4,5,6,7];" "a.filter(function(v, i, a) { a.shift(); return v > 1 })"), njs_str("3,5,7") }, { njs_str("var a = [1,2,3,4,5,6,7];" "a.filter(function(v, i, a) { a[i] = v + 1; return true })"), njs_str("1,2,3,4,5,6,7") }, { njs_str("var a = [1,2,3,4,5,6,7];" "a.filter(function(v, i, a) { a[i+1] = v+10; return true })"), njs_str("1,11,21,31,41,51,61") }, { njs_str("var o = {0: 'c', 1: 'b', 2: 'c', 'length': { valueOf() { return 3 }}};" "var r = Array.prototype.filter.call(o, function(el, i, arr) {return el == 'c'}); r"), njs_str("c,c") }, { njs_str("var o = {0: 'c', 1: 'a', 2: 'b'};" "Object.defineProperty(o, 'length', {get: () => 4});" "Object.defineProperty(o, '3', {get: () => 'c'});" "var r = Array.prototype.filter.call(o, function(el, i, arr) { return el == 'c' }); r"), njs_str("c,c") }, { njs_str("var a = [];" "a.find(function(v, i, a) { return v > 1 })"), njs_str("undefined") }, { njs_str("var a = [,NaN,0,-1];" "a.find(function(v, i, a) { return v > 1 })"), njs_str("undefined") }, { njs_str("var a = [,NaN,0,-1,2];" "a.find(function(v, i, a) { return v > 1 })"), njs_str("2") }, { njs_str("var a = [1,2,3,-1,5];" "a.find(function(v, i, a) { return v > 1 })"), njs_str("2") }, { njs_str("var a = [,1,,-1,5];" "a.find(function(v, i, a) { return v > 1 })"), njs_str("5") }, { njs_str("var a = [,1,,-1,5,6];" "a.find(function(v, i, a) { return v > 1 })"), njs_str("5") }, { njs_str("[].find(function(v) { return (v === undefined) })"), njs_str("undefined") }, { njs_str("var a = [,3];" "a.find(function(v) { return (v === 3 || v === undefined) })"), njs_str("undefined") }, { njs_str("var a = [1,,3];" "a.find(function(v) { return (v === 3 || v === undefined) })"), njs_str("undefined") }, { njs_str("var a = [1,2,3,4,5,6];" "a.find(function(v, i, a) { a.shift(); return v == 3 })"), njs_str("3") }, { njs_str("var a = [1,2,3,4,5,6];" "a.find(function(v, i, a) { a.shift(); return v == 4 })"), njs_str("undefined") }, { njs_str("var o = {0: 'a', 1: 'b', 2: 'c', 'length': { valueOf() { return 3 }}};" "var r = Array.prototype.find.call(o, function(el, i, arr) {return el == 'b'}); r"), njs_str("b") }, { njs_str("var o = {0: 'a', 1: 'b', 2: 'c', 'length': { valueOf() { return 3 }}};" "var r = Array.prototype.find.call(o, function(el, i, arr) {delete o['1']; return el == 'c'}); r"), njs_str("c") }, { njs_str("var o = {0: 'a', 1: 'b', 2: 'c'};" "Object.defineProperty(o, 'length', {get: () => 4});" "Object.defineProperty(o, '3', {get: () => 'd'});" "var r = Array.prototype.find.call(o, function(el, i, arr) { return el == 'd' }); r"), njs_str("d") }, { njs_str("var i = 0; var o = {get length() {i++}};" "try {Array.prototype.filter.call(o);}" "catch (e) {i += '; ' + e}; i"), njs_str("1; TypeError: callback argument is not callable") }, { njs_str("var callz = 0, res = [], arr = 'abc'.split('');" "void arr.find((k) => { if (0 == callz++) { arr.splice(1,1); } res.push(k) });" "res.join(',')"), njs_str("a,c,") }, { njs_str("var a = [];" "a.findIndex(function(v, i, a) { return v > 1 })"), njs_str("-1") }, { njs_str("var a = [,NaN,0,-1];" "a.findIndex(function(v, i, a) { return v > 1 })"), njs_str("-1") }, { njs_str("var a = [,NaN,0,-1,2];" "a.findIndex(function(v, i, a) { return v > 1 })"), njs_str("4") }, { njs_str("var a = [1,2,3,-1,5];" "a.findIndex(function(v, i, a) { return v > 1 })"), njs_str("1") }, { njs_str("var a = [,1,,-1,5];" "a.findIndex(function(v, i, a) { return v > 1 })"), njs_str("4") }, { njs_str("var a = [,1,,-1,5,6];" "a.findIndex(function(v, i, a) { return v > 1 })"), njs_str("4") }, { njs_str("[].findIndex(function(v) { return (v === undefined) })"), njs_str("-1") }, { njs_str("[,].findIndex(function(v) { return (v === undefined) })"), njs_str("0") }, { njs_str("[1,2,,3].findIndex(function(el){return el === undefined})"), njs_str("2") }, { njs_str("[,2,,3].findIndex(function(el){return el === undefined})"), njs_str("0") }, { njs_str("var a = [1,2,3,4,5,6];" "a.findIndex(function(v, i, a) { a.shift(); return v == 3 })"), njs_str("1") }, { njs_str("var a = [1,2,3,4,5,6];" "a.findIndex(function(v, i, a) { a.shift(); return v == 4 })"), njs_str("-1") }, { njs_str("var o = {0: 'a', 1: 'b', 2: 'c', 'length': { valueOf() { return 3 }}};" "var r = Array.prototype.findIndex.call(o, function(el, i, arr) {return el == 'b'}); r"), njs_str("1") }, { njs_str("var o = {0: 'a', 1: 'b', 2: 'c'};" "Object.defineProperty(o, 'length', {get: () => 4});" "Object.defineProperty(o, '3', {get: () => 'd'});" "var r = Array.prototype.findIndex.call(o, function(el, i, arr) { return el == 'd' }); r"), njs_str("3") }, { njs_str("var callz = 0, res = [], arr = 'abc'.split('');" "void arr.findIndex((k) => { if (0 == callz++) { arr.splice(1,1); } res.push(k) });" "res.join(',')"), njs_str("a,c,") }, { njs_str("var a = [];" "a.map(function(v, i, a) { return v + 1 })"), njs_str("") }, { njs_str("var a = [,,,];" "a.map(function(v, i, a) { return v + 1 })"), njs_str(",,") }, { njs_str("var a = [,,,1];" "a.map(function(v, i, a) { return v + 1 })"), njs_str(",,,2") }, { njs_str("var a = [1,2,3];" "a.map(function(v, i, a) { return v + 1 })"), njs_str("2,3,4") }, { njs_str("var a = [1,2,3,4,5,6];" "a.map(function(v, i, a) { a.pop(); return v + 1 })"), njs_str("2,3,4,,,") }, { njs_str("var a = [1,2,3,4,5,6];" "a.map(function(v, i, a) { a.shift(); return v + 1 })"), njs_str("2,4,6,,,") }, { njs_str("var o = {0: 'a', 1: 'b', 2: 'c', 'length': { valueOf() { return 3 }}};" "var r = Array.prototype.map.call(o, num => num + '1'); r"), njs_str("a1,b1,c1") }, { njs_str("var o = {0: 'a', 1: 'b', 2: 'c'};" "Object.defineProperty(o, 'length', {get: () => 4});" "Object.defineProperty(o, '3', {get: () => 'd'});" "var r = Array.prototype.map.call(o, function(el, i, arr) { return el + '1' }); r"), njs_str("a1,b1,c1,d1") }, { njs_str("Array.prototype.map.call(new String('abc')," " (val, idx, obj) => {return obj instanceof String})" ".every(x => x === true)"), njs_str("true") }, { njs_str("Array.prototype.map.call('abcdef', (val, idx, obj) => {return val === 100})"), njs_str("false,false,false,false,false,false") }, { njs_str("function callbackfn(val, idx, obj) {return idx === 1 && typeof val === 'undefined';}" "var obj = {2: 2, length: 10};" "var res = Array.prototype.map.call(obj, callbackfn); typeof res[7]"), njs_str("undefined") }, { njs_str("function callbackfn(val, idx, obj) {return idx === 1 && typeof val === 'undefined';}" "var obj = {2: 2, length: 9000};" "var res = Array.prototype.map.call(obj, callbackfn); typeof res[8000]"), njs_str("undefined") }, { njs_str("var a = [1,2,3]; Object.defineProperty(a, '1', {enumerable:false});" "a.map(v=>v)"), njs_str("1,2,3") }, { njs_str("Array.prototype.map.call({0:9, length:2**16}, val=>val<10).length"), njs_str("65536") }, { njs_str("[].reduce((p, v) => p + v)"), njs_str("TypeError: Reduce of empty object with no initial value") }, { njs_str("[].reduce((p, v) => p + v, 10)"), njs_str("10") }, { njs_str("[,,].reduce((p, v) => p + v)"), njs_str("TypeError: Reduce of empty object with no initial value") }, { njs_str("[,,].reduce((p, v) => p + v, 10)"), njs_str("10") }, { njs_str("[1].reduce((p, v) => p + v)"), njs_str("1") }, { njs_str("[1].reduce((p, v) => p + v, 10)"), njs_str("11") }, { njs_str("[1,2,3].reduce((p, v) => p + v)"), njs_str("6") }, { njs_str("[1,2,3].reduce((p, v) => p + v, 10)"), njs_str("16") }, { njs_str("[3,2,1].reduce((p, v, i) => { if (v + i != 3) {throw 'Oops'};return p + v})"), njs_str("6") }, { njs_str("var a = [3,2,1]; a.reduce((p, v, _, o) => { if (a != o) {throw 'Oops'};return p + v})"), njs_str("6") }, { njs_str("[[0, 1], [2, 3], [4, 5]].reduce((a, b) => a.concat(b), [])"), njs_str("0,1,2,3,4,5") }, { njs_str("var o = {0: 'a', 1: 'b', 2: 'c', 'length': { valueOf() { return 3 }}};" "var reducer = (a, b) => a + b;" "var a = Array.prototype.reduce.call(o, reducer); a"), njs_str("abc") }, { njs_str("function reducer(a, b, i, arr) {" " if (i == 2) Object.defineProperty(arr, i, {enumerable:false}); " " return a + b;" "};" "Array.prototype.reduce.call([1,2,3,4], reducer)"), njs_str("10") }, { njs_str("var o = {0: 'a', 1: 'b', 2: 'c'};" "Object.defineProperty(o, 'length', {get: () => 4});" "Object.defineProperty(o, '3', {get: () => 'd'});" "var r = Array.prototype.reduce.call(o, (a, b) => a + b); r"), njs_str("abcd") }, { njs_str("var o = {1: 'b', 2: 'c', 3: 'd'};" "Object.defineProperty(o, 'length', {get: () => 4});" "Object.defineProperty(o, '0', {get: () => 'a'});" "var r = Array.prototype.reduce.call(o, (a, b) => a + b); r"), njs_str("abcd") }, { njs_str("[].reduceRight((p, v) => p + v)"), njs_str("TypeError: Reduce of empty object with no initial value") }, { njs_str("[].reduceRight((p, v) => p + v, 10)"), njs_str("10") }, { njs_str("[,,].reduceRight((p, v) => p + v)"), njs_str("TypeError: Reduce of empty object with no initial value") }, { njs_str("[,,].reduceRight((p, v) => p + v, 10)"), njs_str("10") }, { njs_str("[1].reduceRight((p, v) => p + v)"), njs_str("1") }, { njs_str("[1].reduceRight((p, v) => p + v, 10)"), njs_str("11") }, { njs_str("[1,2,3].reduceRight((p, v) => p + v)"), njs_str("6") }, { njs_str("[1,2,3].reduceRight((p, v) => p + v, 10)"), njs_str("16") }, { njs_str("[3,2,1].reduceRight((p, v, i) => { if (v + i != 3) {throw 'Oops'};return p + v})"), njs_str("6") }, { njs_str("var a = [1,2,3];" "a.reduceRight(function(p, v, _, a) { a.shift(); return p + v })"), njs_str("7") }, { njs_str("var a = [1,2,3];" "a.reduceRight(function(p, v, _, a) { a.shift(); return p + v }, 10)"), njs_str("19") }, { njs_str("var o = {0: 'a', 1: 'b', 2: 'c'};" "Object.defineProperty(o, 'length', {get: () => 4});" "Object.defineProperty(o, '3', {get: () => 'd'});" "Array.prototype.reduceRight.call(o, (p, v) => p + v)"), njs_str("dcba") }, { njs_str("var i = 0; var o = {get length() {i++}};" "try {Array.prototype.reduceRight.call(o);}" "catch (e) {i += '; ' + e} i"), njs_str("1; TypeError: callback argument is not callable") }, { njs_str("var m = [];" "[''].reduceRight.call('00000000000000000000000000000а00', (p, v, i, a) => {m.push(v)});" "m.join('')"), njs_str("0а00000000000000000000000000000") }, { njs_str("function reducer(a, b, i, arr) {" " if (i == 2) Object.defineProperty(arr, i, {enumerable:false}); " " return a + b;" "};" "Array.prototype.reduceRight.call([1,2,3,4], reducer)"), njs_str("10") }, { njs_str("Array.prototype[0] = 1; Array.prototype[1] = 2; Array.prototype[2] = 3;" "[,,].reduceRight((a,b)=>a+b)"), njs_str("3") }, { njs_str("var a = ['1','2','3','4','5','6']; a.sort()"), njs_str("1,2,3,4,5,6") }, { njs_str("var a = ['a', 'ab', '', 'aa']; a.sort()"), njs_str(",a,aa,ab") }, { njs_str("var a = ['a', 'ab', '', 'aa']; a.sort()"), njs_str(",a,aa,ab") }, { njs_str("var a = [23,1,8,5]; Object.defineProperty(a, '0', {enumerable:false});" "a.sort((x,y)=>x-y)"), njs_str("1,5,8,23") }, { njs_str("var a = {0:23,1:1,2:8,3:5,length:4};" "Array.prototype.sort.call(a, (x,y)=>x-y);" "Array.prototype.join.call(a)"), njs_str("1,5,8,23") }, { njs_str(njs_declare_sparse_array("a", 1024) "a[100] = 1; a[512] = -1; a[5] = undefined;" "a.sort();" "a[0] == -1 && a[1] == 1 && a[2] == undefined"), njs_str("true") }, { njs_str(njs_declare_sparse_array("a", 1024) "a.fill(1, 256, 512); a.fill(undefined, 1000, 1010);" "Object.defineProperty(a, '256', {value: a[256], enumerable:false});" "a.sort();" "a[0] == 1 && a[255] == 1 && Object.getOwnPropertyDescriptor(a, '256').value == undefined"), njs_str("true") }, { njs_str("var a = [23,1,,undefined,8,,5]; Object.defineProperty(a, '0', {enumerable:false});" "a.sort((x,y)=>x-y)"), njs_str("1,5,8,23,,,") }, { njs_str("var o = { toString: function() { return 5 } };" "var a = [6,o,4,3,2,1]; a.sort(undefined)"), njs_str("1,2,3,4,5,6") }, { njs_str("var a = [undefined,1]; a.sort()"), njs_str("1,") }, { njs_str("var a = [,1]; a.sort()"), njs_str("1,") }, { njs_str("var a = [,1,undefined]; a.sort()"), njs_str("1,,") }, { njs_str("var a = ['1','2','3','4','5','6']; a.sort()"), njs_str("1,2,3,4,5,6") }, { njs_str("var a = [1,2,3,4,5,6]; a.sort()"), njs_str("1,2,3,4,5,6") }, { njs_str("var a = {0:3,1:2,2:1}; Array.prototype.sort.call(a) === a"), njs_str("true") }, { njs_str("var a = {0:3,1:2,2:1,length:0}; Array.prototype.sort.call(a) === a"), njs_str("true") }, { njs_str("var a = [1,2,3,4,5,6];" "a.sort(function(x, y) { return x - y })"), njs_str("1,2,3,4,5,6") }, { njs_str("var a = Array(128).fill().map((v,i,a)=>a.length-i);" "a.sort((a,b)=>a-b);" "a.every((v,i,a)=> (i < 1 || v >= a[i-1]))"), njs_str("true") }, { njs_str("var a = [2,2,2,1,1,1];" "a.sort(function(x, y) { return x - y })"), njs_str("1,1,1,2,2,2") }, { njs_str("var a = [,,,2,2,2,1,1,1];" "a.sort(function(x, y) { return x - y })"), njs_str("1,1,1,2,2,2,,,") }, { njs_str("var a = [,,,,];" "a.sort(function(x, y) { return x - y })"), njs_str(",,,") }, { njs_str("var a = [,,undefined,undefined,,undefined];" "a.sort(function(x, y) { return x - y }); njs.dump(a)"), njs_str("[undefined,undefined,undefined,<3 empty items>]") }, { njs_str("var a = [1,,undefined,8,undefined,,undefined,,2];" "a.sort(function(x, y) { return x - y }); njs.dump(a)"), njs_str("[1,2,8,undefined,undefined,undefined,<3 empty items>]") }, { njs_str("var a = [1,,];" "a.sort(function(x, y) { return x - y })"), njs_str("1,") }, { njs_str("var a = [{ n: 'A', r: 2 }," " { n: 'B', r: 3 }," " { n: 'C', r: 2 }," " { n: 'D', r: 3 }," " { n: 'E', r: 3 }];" "a.sort((a, b) => b.r - a.r).map(v=>v.n).join('')"), njs_str("BDEAC") }, { njs_str("[1,2,3].sort(()=>-1)"), njs_str("3,2,1") }, { njs_str("njs.dump([undefined,1,2,3].sort(()=>0))"), njs_str("[1,2,3,undefined]") }, { njs_str("njs.dump([1,,2,3].sort(()=>0))"), njs_str("[1,2,3,]") }, { njs_str("var count = 0;" "[4,3,2,1].sort(function(x, y) { if (count++ == 2) {throw Error('Oops'); }; return x - y })"), njs_str("Error: Oops") }, { njs_str("[1,2].sort(1)"), njs_str("TypeError: comparefn must be callable or undefined") }, { njs_str("var a = [1,2]; a.sort(() => {a.length = 65535}); a.length"), njs_str("65535") }, { njs_str("var a = [];" "var shift = true;" "for (let i = 0; i < 64; i++) {" " a[i] = { toString() {" " if (shift) { a.shift() };" " return (63 - i).toString().padStart(2, '0');" " }" " };" "}" "a.sort();" "shift = false;" "[a.length, a[0].toString(), a[63].toString()]"), njs_str("64,00,63") }, { njs_str("var a = [];" "var shift = true;" "for (let i = 0; i < 64; i++) {" " a[i] = { toString() {" " if (shift) { a.shift() };" " return (i).toString().padStart(2, '0');" " }" " };" "}" "a.sort();" "shift = false;" "[a.length, a[0].toString(), a[63].toString()]"), njs_str("64,00,63") }, { njs_str("Object.prototype[2] = 4;" "njs.dump([undefined, 3, /*hole*/, 2, undefined, /*hole*/, 1].sort())"), njs_str("[1,2,3,4,undefined,undefined,]") }, { njs_str("var a = [3,2,1]; [a.toSorted(), a]"), njs_str("1,2,3,3,2,1") }, { njs_str("var a = [3,,1]; njs.dump([a.toSorted(), a.sort()])"), njs_str("[[1,3,undefined],[1,3,]]") }, { njs_str("var a = {length:3, 0:'Z', 2:'A'};" "njs.dump([Array.prototype.toSorted.call(a), Array.prototype.sort.call(a)])"), njs_str("[['A','Z',undefined],{0:'A',1:'Z',length:3}]") }, { njs_str("var a = {length: 1}; a.__proto__ = {0:'A'};" "njs.dump([Array.prototype.toSorted.call(a), Array.prototype.sort.call(a)])"), njs_str("[['A'],{length:1}]") }, { njs_str("Array.prototype.toSorted.call(true)"), njs_str("") }, { njs_str("Array.prototype.toSorted.call({length: -2})"), njs_str("") }, { njs_str("Array.prototype.toSorted.call({length: NaN})"), njs_str("") }, { njs_str("Array.prototype.toSorted.call({length: 2**32})"), njs_str("RangeError: Invalid array length") }, /* Array.prototype.keys() Array.prototype.values() Array.prototype.entries() */ { njs_str("['keys', 'values', 'entries', Symbol.iterator]" ".every((x) => typeof Array.prototype[x] == 'function')"), njs_str("true") }, { njs_str("['keys', 'values', 'entries', Symbol.iterator]" ".every((x) => Array.prototype[x].length === 0)"), njs_str("true") }, #if 0 { njs_str("Array.prototype[Symbol.iterator] === Array.prototype.values"), njs_str("true") }, #endif { njs_str("['keys', 'values', 'entries', Symbol.iterator]" ".every((x) => typeof [][x]() == 'object')"), njs_str("true") }, { njs_str("['keys', 'values', 'entries', Symbol.iterator]" ".every((x) => typeof [][x]().next == 'function')"), njs_str("true") }, { njs_str("var i = [1,2,3].keys();" "[i.next(), i.next(), i.next(), i.next()].map((x) => x.value)"), njs_str("0,1,2,") }, { njs_str("var i = [1,2,3].values();" "[i.next(), i.next(), i.next(), i.next()].map((x) => x.value)"), njs_str("1,2,3,") }, { njs_str("[].values().constructor()"), njs_str("[object Object]") }, { njs_str("var a = [], i = a.values();" "a.push(1); a.push(2); a.push(3);" "[i.next(), i.next(), i.next(), i.next()].map((x) => x.value)"), njs_str("1,2,3,") }, { njs_str("var a = [], i = a.values(); i.next();" "a.push(1); a.push(2); a.push(3);" "[i.next(), i.next(), i.next(), i.next()].map((x) => x.value)"), njs_str(",,,") }, { njs_str("var i = [1,2,3].entries();" "[i.next(), i.next(), i.next(), i.next()].map((x) => x.value)"), njs_str("0,1,1,2,2,3,") }, { njs_str("var i = Array.prototype.keys.call('abc');" "[i.next(), i.next(), i.next(), i.next()].map((x) => x.done)"), njs_str("false,false,false,true") }, { njs_str("var i = Array.prototype.values.call('abc');" "[i.next(), i.next(), i.next(), i.next()].map((x) => x.value)"), njs_str("a,b,c,") }, { njs_str("var x = [true, 1, Symbol()];" "x.map((x) => Array.prototype.keys.call(x).next()).every((x) => x.done)"), njs_str("true") }, { njs_str("var x = [true, 1, Symbol()];" "x.forEach((x) => Object.getPrototypeOf(Object(x)).length = 1);" "x.map((x) => Array.prototype.keys.call(x).next()).every((x) => !x.done)"), njs_str("true") }, /* TypedArray.prototype.keys() TypedArray.prototype.values() TypedArray.prototype.entries() */ { njs_str("['keys', 'values', 'entries', Symbol.iterator]" ".every((x) => typeof Buffer.prototype[x] == 'function')"), njs_str("true") }, { njs_str("var i = Buffer.from([1,2,3]).keys();" "[i.next(), i.next(), i.next(), i.next()].map((x) => x.value)"), njs_str("0,1,2,") }, { njs_str("var i = Buffer.from([1,2,3]).values();" "[i.next(), i.next(), i.next(), i.next()].map((x) => x.value)"), njs_str("1,2,3,") }, { njs_str("var i = Buffer.from([1,2,3]).entries();" "[i.next(), i.next(), i.next(), i.next()].map((x) => x.value)"), njs_str("0,1,1,2,2,3,") }, { njs_str("[true, 1, Symbol(), 'test', [], { length: 1 }]" ".map((x) => { try { Buffer.prototype.keys.call(x); return x; } catch (e) { return e; } })" ".every((x) => x instanceof TypeError)"), njs_str("true") }, /* %IteratorPrototype% */ { njs_str("var x = Object.getPrototypeOf(Object.getPrototypeOf([].keys()));" "typeof x[Symbol.iterator] == 'function'"), njs_str("true") }, { njs_str("var x = Object.getPrototypeOf(Object.getPrototypeOf([].keys()));" "x[Symbol.iterator]() === x"), njs_str("true") }, /* %ArrayIteratorPrototype% */ { njs_str("var x = Object.getPrototypeOf([].keys());" "typeof x.next == 'function'"), njs_str("true") }, { njs_str("var x = Object.getPrototypeOf([].keys());" "x[Symbol.toStringTag] == 'Array Iterator'"), njs_str("true") }, /* %StringIteratorPrototype% */ { njs_str("typeof String.prototype[Symbol.iterator] == 'function'"), njs_str("true") }, { njs_str("var x = Object.getPrototypeOf(''[Symbol.iterator]());" "typeof x.next == 'function'"), njs_str("true") }, #if 0 { njs_str("var x = Object.getPrototypeOf(''[Symbol.iterator]());" "x[Symbol.toStringTag] == 'String Iterator'"), njs_str("true") }, #else { njs_str("var x = Object.getPrototypeOf(''[Symbol.iterator]());" "x[Symbol.toStringTag] == 'Array Iterator'"), njs_str("true") }, #endif /* Template literal. */ { njs_str("`"), njs_str("SyntaxError: Unterminated template literal in 1") }, { njs_str("`$"), njs_str("SyntaxError: Unterminated template literal in 1") }, { njs_str("`${"), njs_str("SyntaxError: Unexpected end of input in 1") }, { njs_str("`${a"), njs_str("SyntaxError: Missing \"}\" in template expression in 1") }, { njs_str("`${}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("`${a}"), njs_str("SyntaxError: Unterminated template literal in 1") }, { njs_str("`${a}bc"), njs_str("SyntaxError: Unterminated template literal in 1") }, { njs_str("`\\"), njs_str("SyntaxError: Unterminated template literal in 1") }, { njs_str("`\\${a}bc"), njs_str("SyntaxError: Unterminated template literal in 1") }, { njs_str("var v = undefined; var u8 = 'α';" "[`undefined${u8}`.length, `undefineQ${u8}`.length]"), njs_str("10,10") }, { njs_str("`text1\ntext2`;"), njs_str("text1\ntext2") }, { njs_str("var o = 1; `o = \\`${o}\\``"), njs_str("o = `1`") }, { njs_str("`\\unicode`"), njs_str("SyntaxError: Invalid Unicode code point \"\\unicode\" in 1") }, { njs_str("var a = 5; var b = 10;" "`Fifteen is ${a + b} and \nnot ${2 * a + b}.`;"), njs_str("Fifteen is 15 and \nnot 20.") }, { njs_str("var s = `1undefined`; s;"), njs_str("1undefined") }, { njs_str("var s = '0'; s = `x${s += '1'}`;"), njs_str("x01") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45, 12, 625);" "var something = 'test'; var one = 1; var two = 2;" "`[${d.toISOString()}] the message contents ${something} ${one + two}`"), njs_str("[2011-06-24T18:45:12.625Z] the message contents test 3") }, { njs_str("function isLargeScreen() { return false; }" "var item = { isCollapsed: true };" "`header ${ isLargeScreen() ? '' : `icon-${item.isCollapsed ? 'expander' : 'collapser'}` }`;"), njs_str("header icon-expander") }, { njs_str("function foo(strings, person, age) { return `${strings[0]}${strings[1]}${person}${age}` };" "var person = 'Mike'; var age = 21;" "foo`That ${person} is a ${age}`;"), njs_str("That is a Mike21") }, { njs_str("`\n`.length"), njs_str("1") }, /* Strings. */ { njs_str("var a = '0123456789' + '012345';" "var b = 'abcdefghij' + 'klmnop';" " a = b"), njs_str("abcdefghijklmnop") }, { njs_str("String.prototype.my = function f() {return 7}; 'a'.my()"), njs_str("7") }, { njs_str("'a'.my"), njs_str("undefined") }, { njs_str("var a = '123'\n[2].toString();a"), njs_str("3") }, { njs_str("'\xE5\x96\x9C\xE3\x81\xB6'"), njs_str("喜ぶ") }, /* Broken UTF-8 literals.*/ { njs_str("'\x96\xE5\x9C\xE3\x81\xB6'"), njs_str("��ぶ") }, { njs_str("'\x96\xE5\x9C'"), njs_str("��") }, { njs_str("'\x96\xE5'"), njs_str("��") }, { njs_str("'\x96'"), njs_str("�") }, { njs_str("'\xF3'"), njs_str("�") }, { njs_str("'\xF3\xFF'"), njs_str("��") }, { njs_str("'\x96\x96\xE5\x9C\xE3\x81\xB6'"), njs_str("���ぶ") }, { njs_str("'\x9C\x96\xE5\xE3\x81\xB6'"), njs_str("���ぶ") }, { njs_str("'\xE5\x9C\xE3\x81\xB6'"), njs_str("�ぶ") }, { njs_str("'\xEF\xBF\xBD\xE3\x81\xB6'"), njs_str("�ぶ") }, { njs_str("'\xE5\xF6\x9C\xE3\x81\xB6'"), njs_str("���ぶ") }, { njs_str("var a = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\xF3'; " "[a.length, a[33], a[34]]"), njs_str("35,a,�") }, /* Spaces: U+0009U+000BU+000CU+0020U+00A0U+000AU+000DU+2028U+2029 */ { njs_str("\x09\x0a\x0b\x0c\x0d \xc2\xa0'a'\xe2\x80\xa8+\xe2\x80\xa9'b'"), njs_str("ab") }, /* Escape strings. */ { njs_str("'\\a \\' \\\" \\\\ \\0 \\b \\f \\n \\r \\t \\v'"), njs_str("a ' \" \\ \0 \b \f \n \r \t \v") }, { njs_str("'\\\n'"), njs_str("") }, { njs_str("'\\\r'"), njs_str("") }, { njs_str("'\\\r\n'"), njs_str("") }, { njs_str("'a\\\nb'"), njs_str("ab") }, { njs_str("'a\\\rb'"), njs_str("ab") }, { njs_str("'a\\\r\nb'"), njs_str("ab") }, { njs_str("'a\\\n\rb'"), njs_str("SyntaxError: Unterminated string \"'a\\\n\r\" in 1") }, { njs_str("'a\\\nb\nc'"), njs_str("SyntaxError: Unterminated string \"'a\\\nb\n\" in 1") }, { njs_str("'abcde"), njs_str("SyntaxError: Unterminated string \"'abcde\" in 1") }, { njs_str("'\\"), njs_str("SyntaxError: Unterminated string \"'\\\" in 1") }, { njs_str("'\\\r\n"), njs_str("SyntaxError: Unterminated string \"'\\\r\n\" in 1") }, { njs_str("'\\'"), njs_str("SyntaxError: Unterminated string \"'\\'\" in 1") }, { njs_str("'a\n"), njs_str("SyntaxError: Unterminated string \"'a\n\" in 1") }, { njs_str("'a\r"), njs_str("SyntaxError: Unterminated string \"'a\r\" in 1") }, { njs_str("\"a\n"), njs_str("SyntaxError: Unterminated string \"\"a\n\" in 1") }, { njs_str("'\\u03B1'"), njs_str("α") }, { njs_str("'\\u'"), njs_str("SyntaxError: Invalid Unicode code point \"\\u\" in 1") }, { njs_str("'\\uzzzz'"), njs_str("SyntaxError: Invalid Unicode code point \"\\uzzzz\" in 1") }, { njs_str("'\\u03B'"), njs_str("SyntaxError: Invalid Unicode code point \"\\u03B\" in 1") }, { njs_str("'\\u03BG'"), njs_str("SyntaxError: Invalid Unicode code point \"\\u03BG\" in 1") }, { njs_str("'\\u03B '"), njs_str("SyntaxError: Invalid Unicode code point \"\\u03B \" in 1") }, { njs_str("'\\u{61}\\u{3B1}\\u{20AC}'"), njs_str("aα€") }, { njs_str("'\\u'"), njs_str("SyntaxError: Invalid Unicode code point \"\\u\" in 1") }, { njs_str("'\\u{'"), njs_str("SyntaxError: Invalid Unicode code point \"\\u{\" in 1") }, { njs_str("'\\u{}'"), njs_str("SyntaxError: Invalid Unicode code point \"\\u{}\" in 1") }, { njs_str("'\\u{1234567}'"), njs_str("SyntaxError: Invalid Unicode code point \"\\u{1234567}\" in 1") }, { njs_str("'\\u{a00000}'"), njs_str("SyntaxError: Invalid Unicode code point \"\\u{a00000}\" in 1") }, { njs_str("'\\x61'"), njs_str("a") }, { njs_str("''.length"), njs_str("0") }, { njs_str("'abc'.length"), njs_str("3") }, { njs_str("'привет\\n'.length"), njs_str("7") }, { njs_str("'привет\\n\\u{61}\\u{3B1}\\u{20AC}'.length"), njs_str("10") }, { njs_str("'\\ud83d\\udc4d'"), njs_str("\xf0\x9f\x91\x8d") }, { njs_str("'\\ud83d\\udc4d'.length"), njs_str("1") }, { njs_str("'\\ud83d abc \\udc4d'"), njs_str("� abc �") }, { njs_str("'\\ud83d'"), njs_str("�") }, { njs_str("'\\ud83d\\uabcd'"), njs_str("�ꯍ") }, { njs_str("'\\u{d800}\\u{dB00}'"), njs_str("��") }, { njs_str("'\\u{d800}\\u{d7ff}'"), njs_str("�퟿") }, { njs_str("'\\u{d800}['"), njs_str("�[") }, { njs_str("'\\u{D800}\\u{'"), njs_str("SyntaxError: Invalid Unicode code point \"\\u{D800}\\u{\" in 1") }, { njs_str("'α' !== '\\α'"), njs_str("false") }, { njs_str("'r' !== '\\r'"), njs_str("true") }, /* Octal escape sequences are not allowed in strict mode.*/ { njs_str("'\\0a'"), njs_str("\0a") }, { njs_str("'\\1a'"), njs_str("SyntaxError: Octal escape sequences can't be used in untagged template literals or in strict mode code in 1") }, { njs_str("'a\\2a'"), njs_str("SyntaxError: Octal escape sequences can't be used in untagged template literals or in strict mode code in 1") }, { njs_str("'\\3a'"), njs_str("SyntaxError: Octal escape sequences can't be used in untagged template literals or in strict mode code in 1") }, { njs_str("'a\\4a'"), njs_str("SyntaxError: Octal escape sequences can't be used in untagged template literals or in strict mode code in 1") }, { njs_str("'\\5a'"), njs_str("SyntaxError: Octal escape sequences can't be used in untagged template literals or in strict mode code in 1") }, { njs_str("'a\\6a'"), njs_str("SyntaxError: Octal escape sequences can't be used in untagged template literals or in strict mode code in 1") }, { njs_str("'\\7a'"), njs_str("SyntaxError: Octal escape sequences can't be used in untagged template literals or in strict mode code in 1") }, { njs_str("'\\8a'"), njs_str("SyntaxError: The escapes \\8 and \\9 can't be used in untagged template literals or in strict mode code in 1") }, { njs_str("'\\9a'"), njs_str("SyntaxError: The escapes \\8 and \\9 can't be used in untagged template literals or in strict mode code in 1") }, { njs_str("'\\aa'"), njs_str("aa") }, { njs_str("'\\*a'"), njs_str("*a") }, { njs_str("`\\7`"), njs_str("SyntaxError: Octal escape sequences can't be used in untagged template literals or in strict mode code in 1") }, { njs_str("`\\9`"), njs_str("SyntaxError: The escapes \\8 and \\9 can't be used in untagged template literals or in strict mode code in 1") }, /* Octal escape sequences are allowed in tagged template literals in strict mode.*/ #if 0 /* FIXME: tag function runtime semantics */ { njs_str("function x (s) {return s[0]}; x`\\7`"), njs_str("undefined") }, { njs_str("function x (s) {return s[0]}; x`\\9`"), njs_str("undefined") }, { njs_str("function x (s) {return s.raw[0]}; x`\\9`"), njs_str("\\9") }, { njs_str("function x (s) {return s.raw[0]}; x`\\7`"), njs_str("\\7") }, #endif /* Broken UTF-8 literals.*/ { njs_str("'\\a\x96\xE5\x9C\xE3\x81\xB6'"), njs_str("a��ぶ") }, { njs_str("'\x96\\a\xE5\x9C'"), njs_str("�a�") }, { njs_str("'\x96\xE5\\a'"), njs_str("��a") }, { njs_str("'\\a\x96\\a'"), njs_str("a�a") }, { njs_str("'\xF3\\a'"), njs_str("�a") }, { njs_str("'\xF3\\a\xFF'"), njs_str("�a�") }, { njs_str("'\\a\x96\x96\xE5\x9C\xE3\x81\xB6'"), njs_str("a���ぶ") }, { njs_str("'\\a\x9C\x96\xE5\xE3\x81\xB6'"), njs_str("a���ぶ") }, { njs_str("'\\a\xE5\x9C\xE3\x81\xB6'"), njs_str("a�ぶ") }, { njs_str("'\\a\xEF\xBF\xBD\xE3\x81\xB6'"), njs_str("a�ぶ") }, { njs_str("'\\a\xE5\xF6\x9C\xE3\x81\xB6'"), njs_str("a���ぶ") }, { njs_str("var a = '\\aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\xF3'; " "[a.length, a[34], a[35]]"), njs_str("36,a,�") }, { njs_str("''.hasOwnProperty('length')"), njs_str("true") }, { njs_str("'abc'.hasOwnProperty('length')"), njs_str("true") }, { njs_str("(new String('abc')).hasOwnProperty('length')"), njs_str("true") }, { njs_str("'абв'.length"), njs_str("3") }, { njs_str("(new TextEncoder()).encode('абв').length"), njs_str("6") }, { njs_str("'αβγ'.length"), njs_str("3") }, { njs_str("(new TextEncoder()).encode('αβγ').length"), njs_str("6") }, { njs_str("'絵文字'.length"), njs_str("3") }, { njs_str("(new TextEncoder()).encode('絵文字').length"), njs_str("9") }, { njs_str("'えもじ'.length"), njs_str("3") }, { njs_str("(new TextEncoder()).encode('えもじ').length"), njs_str("9") }, { njs_str("'囲碁織'.length"), njs_str("3") }, { njs_str("(new TextEncoder()).encode('囲碁織').length"), njs_str("9") }, { njs_str("var a = 'abc'; a.length"), njs_str("3") }, { njs_str("var a = 'abc'; a['length']"), njs_str("3") }, { njs_str("var a = 'абв'; a.length"), njs_str("3") }, { njs_str("var a = 'abc' + 'абв'; a.length"), njs_str("6") }, { njs_str("var a = 'abc' + 1 + 'абв'; a +' '+ a.length"), njs_str("abc1абв 7") }, { njs_str("var a = 1; a.length"), njs_str("undefined") }, { njs_str("var a = 'abc'; a.concat('абв', 123)"), njs_str("abcабв123") }, { njs_str("''.concat.call(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)"), njs_str("0123456789") }, { njs_str("''.concat.apply(0, [1, 2, 3, 4, 5, 6, 7, 8, 9])"), njs_str("0123456789") }, { njs_str("var f = ''.concat.bind(0, 1, 2, 3, 4); f(5, 6, 7, 8, 9)"), njs_str("0123456789") }, { njs_str("var f = ''.concat.bind(0, 1, 2, 3, 4); f(Math.sqrt(25))"), njs_str("012345") }, { njs_str("var f = String.prototype.concat.bind(0, 1); f(2)"), njs_str("012") }, { njs_str("var f = Function.prototype.call.bind" " (String.prototype.concat, 0, 1);" "f(2)"), njs_str("012") }, { njs_str("var f = String.prototype.concat.bind(0, 1);" "var o = { toString: f }; o"), njs_str("01") }, { njs_str("''.concat.bind(0, 1, 2, 3, 4).call(5, 6, 7, 8, 9)"), njs_str("012346789") }, { njs_str("''.concat.bind(0, 1, 2, 3, 4).apply(5,[6, 7, 8, 9])"), njs_str("012346789") }, { njs_str("var f = Array.prototype.join.bind([0, 1, 2]); f()"), njs_str("0,1,2") }, { njs_str("var f = Array.prototype.join.bind([0, 1, 2]);" "var o = { toString: f }; o"), njs_str("0,1,2") }, { njs_str("var f = Array.prototype.join.bind([0, 1, 2]); f('.')"), njs_str("0.1.2") }, { njs_str("var f = Array.prototype.join.bind([0, 1, 2], '.');" "var o = { toString: f }; o"), njs_str("0.1.2") }, { njs_str("var f = Array.prototype.toString.bind([0, 1, 2]); f()"), njs_str("0,1,2") }, { njs_str("var f = Array.prototype.toString.bind([0, 1, 2]);" "var o = { toString: f }; o"), njs_str("0,1,2") }, { njs_str("var f = function F() {};" "[f.name, f.bind().name, f.bind().bind().name]"), njs_str("F,bound F,bound bound F") }, { njs_str("var f = Object.defineProperty(function() {}, 'name', {value: 'F'});" "[f.name, f.bind().name, f.bind().bind().name]"), njs_str("F,bound F,bound bound F") }, { njs_str("var f = Object.defineProperty(function() {}, 'name', {value: undefined});" "[f.name, f.bind().name, f.bind().bind().name]"), njs_str(",bound ,bound bound ") }, { njs_str("var f = Object.defineProperty(function() {}, 'name', {get:()=>'F'});" "[f.name, f.bind().name]"), njs_str("F,bound F") }, { njs_str("var s = Symbol('F'); var f = Object.defineProperty(function() {}, 'name', {get:()=>s});" "[f.name.description, f.bind().name]"), njs_str("F,bound ") }, { njs_str("/a/[Symbol.replace].bind().name"), njs_str("bound [Symbol.replace]") }, { njs_str("var f = Object.defineProperty(function() {}, 'name', {get:()=>{throw Error('Oops')}});" "f.bind().name"), njs_str("Error: Oops") }, { njs_str("var f = function() {}; f.a = 'a'; [f.bind().a, f.a]"), njs_str(",a") }, { njs_str("var f = function() {}; var bf = f.bind(); bf.b = 'b'; " "[f.b, bf.b]"), njs_str(",b") }, { njs_str("function f(x,y) {return {args:arguments,length:arguments.length}};" "var nf = Function.prototype.bind.call(f, {}, 'a', 'b');" "var o = new nf();[o.length, o.args[0]]"), njs_str("2,a") }, { njs_str("function f(x,y) {return {args:arguments,length:arguments.length}};" "var nf = Function.prototype.bind.call(f, {});" "var o = new nf('a', 'b');[o.length, o.args[0]]"), njs_str("2,a") }, { njs_str("var f = function(a,b) { this.a = a; this.b = b; };" "f.prototype.X = 'X';" "var bf = f.bind(null, 1,2);" "var o = new bf(); " "[Object.keys(o), o.X,(o instanceof f) && (o instanceof bf),bf.prototype]"), njs_str("a,b,X,true,") }, { njs_str("var bArray = Array.bind(null, 10,16); bArray()"), njs_str("10,16") }, { njs_str("var bArray = Array.bind(null, 10,16); new bArray()"), njs_str("10,16") }, { njs_str("var bArray = Array.bind(null, 10); new bArray(16)"), njs_str("10,16") }, { njs_str("function f(x,y) {return {args:arguments,length:arguments.length}};" "var bf = f.bind({}, 'a'); var bbf = bf.bind({},'b'); var o = bbf('c');" "[o.args[0], o.args[2], o.length]"), njs_str("a,c,3") }, { njs_str("var f = function (a, b) {return [this, a, b]};" "var b1 = f.bind('THIS', 'x');" "var b2 = b1.bind('WAKA', 'y');" "njs.dump([f(2,3), b1(3), b2()])"), njs_str("[[undefined,2,3],['THIS','x',3],['THIS','x','y']]") }, { njs_str("var f = Math.max;" "var b1 = f.bind('THIS', 4);" "var b2 = b1.bind('WAKA', 5);" "njs.dump([f(2,3), b1(3), b2()])"), njs_str("[3,4,5]") }, { njs_str("var s = { toString: function() { return '123' } };" "var a = 'abc'; a.concat('абв', s)"), njs_str("abcабв123") }, { njs_str("var r = /^\\x80$/; r.source + r.source.length"), njs_str("^\\x80$6") }, { njs_str("var r = /^\\\\x80$/; r.source + r.source.length"), njs_str("^\\\\x80$7") }, { njs_str("var s = 'x'.repeat(2**10).repeat(2**14);" "var a = Array(200).fill(s);" "String.prototype.concat.apply(s, a.slice(1))"), njs_str("RangeError: invalid string length") }, { njs_str("var a = 'abcdefgh'; a.substr(3, 15)"), njs_str("defgh") }, { njs_str("'abcdefgh'.substr(3, 15)"), njs_str("defgh") }, { njs_str("'abcdefghijklmno'.substr(3, 4)"), njs_str("defg") }, { njs_str("'abcdefghijklmno'.substr(-3, 2)"), njs_str("mn") }, { njs_str("'abcdefgh'.substr(100, 120)"), njs_str("") }, { njs_str("('abc' + 'defgh').substr(1, 4)"), njs_str("bcde") }, { njs_str("'abcdefghijklmno'.substring(3, 5)"), njs_str("de") }, { njs_str("'abcdefgh'.substring(3)"), njs_str("defgh") }, { njs_str("'abcdefgh'.substring(5, 3)"), njs_str("de") }, { njs_str("'abcdefgh'.substring(100, 120)"), njs_str("") }, { njs_str("'α'.repeat(32).substring(32)"), njs_str("") }, { njs_str("'α'.repeat(32).substring(32,32)"), njs_str("") }, { njs_str("'abcdefghijklmno'.slice(NaN, 5)"), njs_str("abcde") }, { njs_str("'abcdefghijklmno'.slice(NaN, Infinity)"), njs_str("abcdefghijklmno") }, { njs_str("'abcdefghijklmno'.slice(-Infinity, Infinity)"), njs_str("abcdefghijklmno") }, { njs_str("'abcdefghijklmno'.slice('0', '5')"), njs_str("abcde") }, { njs_str("'abcdefghijklmno'.slice(3, 5)"), njs_str("de") }, { njs_str("'abcdefgh'.slice(3)"), njs_str("defgh") }, { njs_str("'abcdefgh'.slice(undefined, undefined)"), njs_str("abcdefgh") }, { njs_str("'abcdefgh'.slice(undefined)"), njs_str("abcdefgh") }, { njs_str("'abcdefgh'.slice(undefined, 1)"), njs_str("a") }, { njs_str("'abcdefgh'.slice(3, undefined)"), njs_str("defgh") }, { njs_str("'abcde'.slice(50)"), njs_str("") }, { njs_str("'abcde'.slice(1, 50)"), njs_str("bcde") }, { njs_str("'abcdefgh'.slice(3, -2)"), njs_str("def") }, { njs_str("'abcdefgh'.slice(5, 3)"), njs_str("") }, { njs_str("'abcdefgh'.slice(100, 120)"), njs_str("") }, { njs_str("String.prototype.substring(1, 5)"), njs_str("") }, { njs_str("String.prototype.substr.call({toString:()=>{throw new Error('Oops')}})"), njs_str("Error: Oops") }, { njs_str("String.prototype.slice(1, 5)"), njs_str("") }, { njs_str("'abc'.charAt(1 + 1)"), njs_str("c") }, { njs_str("'abc'.charAt(3)"), njs_str("") }, { njs_str("'abc'.charAt(undefined)"), njs_str("a") }, { njs_str("'abc'.charAt(null)"), njs_str("a") }, { njs_str("'abc'.charAt(false)"), njs_str("a") }, { njs_str("'abc'.charAt(true)"), njs_str("b") }, { njs_str("'abc'.charAt(NaN)"), njs_str("a") }, { njs_str("'abc'.charAt(Infinity)"), njs_str("") }, { njs_str("'abc'.charAt(-Infinity)"), njs_str("") }, { njs_str("var o = { valueOf: function() {return 2} };" "'abc'.charAt(o)"), njs_str("c") }, { njs_str("var o = { toString: function() {return '2'} };" "'abc'.charAt(o)"), njs_str("c") }, { njs_str("'abc'.charCodeAt(1 + 1)"), njs_str("99") }, { njs_str("'abc'.charCodeAt(3)"), njs_str("NaN") }, { njs_str("'abc'.charCodeAt(undefined)"), njs_str("97") }, { njs_str("var a = 'abcdef'; a.3"), njs_str("SyntaxError: Unexpected token \".3\" in 1") }, { njs_str("'abcdef'[3]"), njs_str("d") }, { njs_str("'abcdef'[0]"), njs_str("a") }, { njs_str("'abcdef'[-1]"), njs_str("undefined") }, { njs_str("'abcdef'[NaN]"), njs_str("undefined") }, { njs_str("'abcdef'[3.5]"), njs_str("undefined") }, { njs_str("'abcdef'[8]"), njs_str("undefined") }, { njs_str("'abcdef'['1']"), njs_str("b") }, { njs_str("'abcdef'[' 1']"), njs_str("undefined") }, { njs_str("'abcdef'['1 ']"), njs_str("undefined") }, { njs_str("'abcdef'['']"), njs_str("undefined") }, { njs_str("'abcdef'['-']"), njs_str("undefined") }, { njs_str("'abcdef'['-1']"), njs_str("undefined") }, { njs_str("'abcdef'['01']"), njs_str("undefined") }, { njs_str("'abcdef'['0x01']"), njs_str("undefined") }, { njs_str("var a = 'abcdef', b = 1 + 2; a[b]"), njs_str("d") }, /**/ { njs_str("'abc'.toString()"), njs_str("abc") }, { njs_str("''.toString.call('abc')"), njs_str("abc") }, { njs_str("String.prototype.toString.call('abc')"), njs_str("abc") }, { njs_str("String.prototype.toString.call(new String('abc'))"), njs_str("abc") }, { njs_str("String.prototype.toString.call(1)"), njs_str("TypeError: unexpected value type:number") }, { njs_str("'abc'.valueOf()"), njs_str("abc") }, /**/ { njs_str("var n = { toString: function() { return 1 } }; '12'[n]"), njs_str("2") }, { njs_str("var n = { toString: function() { return '1' } }; '12'[n]"), njs_str("2") }, { njs_str("var n = { toString: function() { return 1 }," " valueOf: function() { return 0 } }; '12'[n]"), njs_str("2") }, { njs_str("var a = { toString: function() { return 1 } }; a"), njs_str("1") }, { njs_str("var a = { valueOf: function() { return 1 } }; a"), njs_str("[object Object]") }, { njs_str("var a = { toString: 2," " valueOf: function() { return 1 } }; a"), njs_str("1") }, { njs_str("var a = { toString: function() { return [] }," " valueOf: function() { return 1 } }; a"), njs_str("1") }, { njs_str("var a = { toString: function() { return [] }," " valueOf: function() { return 1 } };" "var o = {}; o[a] = 'test'"), njs_str("test") }, { njs_str("({})[{}] = 'test'"), njs_str("test") }, /**/ { njs_str("'абвгдеёжзийклмнопрстуфхцчшщъыьэюя'.charCodeAt(5)"), njs_str("1077") }, { njs_str("'12345абвгдеёжзийклмнопрстуфхцчшщъыьэюя'.charCodeAt(35)"), njs_str("1101") }, { njs_str("'12345абвгдеёжзийклмнопрстуфхцчшщъыьэюя'.substring(35)"), njs_str("эюя") }, { njs_str("'abcdef'.substr(-5, 4).substring(3, 1).charAt(1)"), njs_str("d") }, { njs_str("'abcdef'.substr(2, 4).charAt(2)"), njs_str("e") }, { njs_str("var a = 'abcdef'.substr(2, 4).charAt(2).length; a"), njs_str("1") }, { njs_str("var a = 'abcdef'.substr(2, 4).charAt(2) + '1234'; a"), njs_str("e1234") }, { njs_str("var a = ('abcdef'.substr(2, 5 * 2 - 6).charAt(2) + '1234')" " .length; a"), njs_str("5") }, { njs_str("String.fromCharCode('_').charCodeAt(0)"), njs_str("0") }, { njs_str("String.fromCodePoint('_')"), njs_str("RangeError: invalid code point") }, { njs_str("String.fromCharCode(65.14)"), njs_str("A") }, { njs_str("String.fromCodePoint(3.14)"), njs_str("RangeError: invalid code point") }, { njs_str("String.fromCharCode(65.14 + 65536)"), njs_str("A") }, { njs_str("String.fromCharCode(0xD83D, 0xDCA9)"), njs_str("💩") }, { njs_str("String.fromCharCode(0xD83D, 0xDCA9).length"), njs_str("1") }, { njs_str("String.fromCharCode(0xD83D)"), njs_str("�") }, { njs_str("String.fromCharCode(0xD83D).length"), njs_str("1") }, { njs_str("String.fromCharCode(0xD83D) + String.fromCharCode(0xDCA9)"), njs_str("��") }, { njs_str("String.fromCodePoint(65 + 65536)"), njs_str("𐁁") }, { njs_str("String.fromCodePoint(0xD83D, 0xDCA9)"), njs_str("💩") }, { njs_str("String.fromCodePoint(0xD83D, 0xDCA9).length"), njs_str("1") }, { njs_str("String.fromCodePoint(0xD83D)"), njs_str("�") }, { njs_str("String.fromCodePoint(0xD83D).length"), njs_str("1") }, { njs_str("String.fromCharCode(2**53 + 10)"), njs_str("\n") }, { njs_str("String.fromCodePoint(1114111 + 1)"), njs_str("RangeError: invalid code point") }, { njs_str("String.fromCharCode(65, 90) + String.fromCodePoint(65, 90)"), njs_str("AZAZ") }, { njs_str("String.fromCharCode(945, 946, 947) + String.fromCodePoint(945, 946, 947)"), njs_str("αβγαβγ") }, { njs_str("(function() {" " var n;" " for (n = 0; n <= 65536; n++) {" /* From U+D800 to U+DFFF is surrogate pair. Not valid in UTF-8. */ " if ((n < 0xD800 || n > 0xDFFF) && String.fromCharCode(n).charCodeAt(0) !== n)" " return n;" " }" " return -1" "})()"), njs_str("65536") }, #if (!NJS_HAVE_MEMORY_SANITIZER) /* very long test under MSAN */ { njs_str("(function() {" " var n;" " for (n = 0; n <= 1114111; n++) {" " if ((n < 0xD800 || n > 0xDFFF) && String.fromCodePoint(n).codePointAt(0) !== n)" " return n;" " }" " return -1" "})()"), njs_str("-1") }, #endif { njs_str("var a = 'abcdef'; function f(a) {" "return a.slice(a.indexOf('cd')) } f(a)"), njs_str("cdef") }, { njs_str("var a = 'abcdef'; a.slice(a.indexOf('cd'))"), njs_str("cdef") }, { njs_str("'abcdef'.indexOf('de', 2)"), njs_str("3") }, { njs_str("'абвгдеёжзийклмнопрстуфхцчшщъыьэюя'.indexOf('лмно', 10)"), njs_str("12") }, { njs_str("'abcdef'.indexOf('a', 10)"), njs_str("-1") }, { njs_str("'abcdef'.indexOf('q', 0)"), njs_str("-1") }, { njs_str("'abcdef'.indexOf('', 10)"), njs_str("6") }, { njs_str("'abcdef'.indexOf('', 3)"), njs_str("3") }, { njs_str("'12345'.indexOf()"), njs_str("-1") }, { njs_str("''.indexOf('')"), njs_str("0") }, { njs_str("'12345'.indexOf(45, '0')"), njs_str("3") }, { njs_str("'12'.indexOf('12345')"), njs_str("-1") }, { njs_str("''.indexOf.call(12345, 45, '0')"), njs_str("3") }, { njs_str("var r = new String('undefined').indexOf(x); var x; r"), njs_str("0") }, { njs_str("'aaa'.lastIndexOf()"), njs_str("-1") }, { njs_str("'aaa'.lastIndexOf('')"), njs_str("3") }, { njs_str("'aaa'.lastIndexOf('a')"), njs_str("2") }, { njs_str("'aaa'.lastIndexOf('aa')"), njs_str("1") }, { njs_str("'aaa'.lastIndexOf('aaa')"), njs_str("0") }, { njs_str("'aaa'.lastIndexOf('aaaa')"), njs_str("-1") }, { njs_str("'a'.repeat(16).lastIndexOf(String.fromCodePoint(65533).repeat(15))"), njs_str("-1") }, { njs_str("('α'+'a'.repeat(15)).lastIndexOf(String.fromCodePoint(65533).repeat(15))"), njs_str("-1") }, { njs_str("'abc abc abc abc'.lastIndexOf('abc')"), njs_str("12") }, { njs_str("'abc abc abc abc'.lastIndexOf('abc', 11)"), njs_str("8") }, { njs_str("'abc abc abc abc'.lastIndexOf('abc', 0)"), njs_str("0") }, { njs_str("'abc abc abc abc'.lastIndexOf('', 0)"), njs_str("0") }, { njs_str("'abc abc abc abc'.lastIndexOf('', 5)"), njs_str("5") }, { njs_str("'abc abc абвгд abc'.lastIndexOf('абвгд')"), njs_str("8") }, { njs_str("'abc abc абвгд abc'.lastIndexOf('абвгд', undefined)"), njs_str("8") }, { njs_str("'abc abc абвгд abc'.lastIndexOf('абвгд', NaN)"), njs_str("8") }, { njs_str("'abc abc абвгд abc'.lastIndexOf('абвгд', {})"), njs_str("8") }, { njs_str("String.prototype.lastIndexOf.call({toString:()=>'abc abc абвгд abc'}, 'абвгд')"), njs_str("8") }, { njs_str("'abc abc абвгдежз'.lastIndexOf('абвгд')"), njs_str("8") }, { njs_str("'abc abc абвгдежз'.lastIndexOf('абвгд', 11)"), njs_str("8") }, { njs_str("'abc abc абвгдежз'.lastIndexOf('абвгд', 12)"), njs_str("8") }, { njs_str("'abc abc абвгдежз'.lastIndexOf('абвгд', 13)"), njs_str("8") }, { njs_str("'abc abc абвгдежз'.lastIndexOf('абвгд', 14)"), njs_str("8") }, { njs_str("'abc abc абвгдежз'.lastIndexOf('абвгд', 15)"), njs_str("8") }, { njs_str("'abc abc абвгдежз'.lastIndexOf('')"), njs_str("16") }, { njs_str("'abc abc абвгдежз'.lastIndexOf('', 12)"), njs_str("12") }, { njs_str("''.lastIndexOf('')"), njs_str("0") }, { njs_str("''.lastIndexOf()"), njs_str("-1") }, { njs_str("''.lastIndexOf(undefined)"), njs_str("-1") }, { njs_str("'β'.repeat(32).lastIndexOf('β')"), njs_str("31") }, { njs_str("'β'.repeat(32).lastIndexOf('β'.repeat(32))"), njs_str("0") }, { njs_str("'β'.repeat(32).lastIndexOf``"), njs_str("32") }, { njs_str("JSON.stringify(Array(24).fill(true).map((v,i) => 'abc abc ab abc абвгдежзab'.lastIndexOf('abc', i)))" "== JSON.stringify([].concat(Array(4).fill(0), Array(7).fill(4), Array(13).fill(11)))"), njs_str("true") }, { njs_str("''.includes('')"), njs_str("true") }, { njs_str("'12345'.includes()"), njs_str("false") }, { njs_str("'абвгдеёжзийклмнопрстуфхцчшщъыьэюя'.includes('лмно', 10)"), njs_str("true") }, { njs_str("'абв абв абвгдежз'.includes('абвгд', 7)"), njs_str("true") }, { njs_str("'абв абв абвгдежз'.includes('абвгд', 8)"), njs_str("true") }, { njs_str("'абв абв абвгдежз'.includes('абвгд', 9)"), njs_str("false") }, { njs_str("var i = 0; var o = {get length() {i++}};" "Array.prototype.includes.call(o); i"), njs_str("1") }, { njs_str("[,,,].includes(undefined)"), njs_str("true") }, { njs_str("''.startsWith('')"), njs_str("true") }, { njs_str("'12345'.startsWith()"), njs_str("false") }, { njs_str("'abc'.startsWith('abc')"), njs_str("true") }, { njs_str("'abc'.startsWith('abc', 1)"), njs_str("false") }, { njs_str("'abc'.startsWith('abc', -1)"), njs_str("true") }, { njs_str("'абв абв абвгдежз'.startsWith('абвгд', 8)"), njs_str("true") }, { njs_str("'абв абв абвгдежз'.startsWith('абвгд', 9)"), njs_str("false") }, { njs_str("''.endsWith('')"), njs_str("true") }, { njs_str("'12345'.endsWith()"), njs_str("false") }, { njs_str("'abc'.endsWith('abc')"), njs_str("true") }, { njs_str("'abc'.endsWith('abc', 4)"), njs_str("true") }, { njs_str("'abc'.endsWith('abc', 1)"), njs_str("false") }, { njs_str("'abc'.endsWith('abc', -1)"), njs_str("true") }, { njs_str("'абв абв абвгдежз'.endsWith('абвгд', 13)"), njs_str("true") }, { njs_str("'абв абв абвгдежз'.endsWith('абвгд', 14)"), njs_str("false") }, { njs_str("'\x00АБВГДЕЁЖЗ'.toLowerCase().length"), njs_str("10") }, { njs_str("'ΑΒΓ'.toLowerCase()"), njs_str("αβγ") }, { njs_str("'АБВ'.toLowerCase()"), njs_str("абв") }, { njs_str("'Ȿ'.repeat(256).toLowerCase() === 'ȿ'.repeat(256)"), njs_str("true") }, { njs_str("'abc'.toUpperCase()"), njs_str("ABC") }, { njs_str("'αβγ'.toUpperCase()"), njs_str("ΑΒΓ") }, { njs_str("'ȿ'.repeat(256).toUpperCase() === 'Ȿ'.repeat(256)"), njs_str("true") }, { njs_str("'\x00абвгдеёжз'.toUpperCase().length"), njs_str("10") }, #if (!NJS_HAVE_MEMORY_SANITIZER) /* very long tests under MSAN */ { njs_str("var a = [], code;" "for (code = 0; code <= 1114111; code++) {" " var s = String.fromCodePoint(code);" " var n = s.toUpperCase();" " if (s != n && s != n.toLowerCase())" " a.push(code);" "} a"), njs_str("181,305,383,453,456,459,498,837,962,976,977,981,982,1008,1009,1013,7296,7297,7298,7299,7300,7301,7302,7303,7304,7835,8126") }, { njs_str("var a = [], code;" "for (code = 0; code <= 1114111; code++) {" " var s = String.fromCodePoint(code);" " var n = s.toLowerCase();" " if (s != n && s != n.toUpperCase())" " a.push(code);" "} a"), njs_str("304,453,456,459,498,1012,7838,8486,8490,8491") }, #endif { njs_str("'abc'.trimStart().trim().trimEnd()"), njs_str("abc") }, { njs_str("''.trim()"), njs_str("") }, { njs_str("' '.trim()"), njs_str("") }, { njs_str("'abc '.trimEnd()"), njs_str("abc") }, { njs_str("' abc'.trimStart()"), njs_str("abc") }, { njs_str("' abc '.trim()"), njs_str("abc") }, { njs_str("'абв '.trimEnd()"), njs_str("абв") }, { njs_str("' абв'.trimStart()"), njs_str("абв") }, { njs_str("' абв '.trimStart().trimEnd()"), njs_str("абв") }, { njs_str("[" " String.fromCodePoint(0x2028)," " String.fromCodePoint(0x20, 0x2028)," " String.fromCodePoint(0x0009, 0x20, 0x2028)," " String.fromCodePoint(0xFEFF)," "].every(v => v.trimEnd() == '')"), njs_str("true") }, { njs_str("'\\u2029abc\\uFEFF\\u2028'.trim()"), njs_str("abc") }, #if (!NJS_HAVE_MEMORY_SANITIZER) /* very long test under MSAN */ { njs_str("var a = [], code;" "for (code = 0; code <= 1114111; code++) {" " var ws = String.fromCodePoint(code);" " if ((ws + '-' + ws).trim() === '-')" " a.push(code);" "} a"), njs_str("9,10,11,12,13,32,160,5760,8192,8193,8194,8195,8196,8197,8198,8199,8200,8201,8202,8232,8233,8239,8287,12288,65279") }, #endif { njs_str("'abcdefgh'.search()"), njs_str("0") }, { njs_str("'abcdefgh'.search('')"), njs_str("0") }, { njs_str("'abcdefgh'.search(undefined)"), njs_str("0") }, { njs_str("'abcdefgh'.search(/def/)"), njs_str("3") }, { njs_str("'abcdefgh'.search('def')"), njs_str("3") }, { njs_str("'123456'.search('45')"), njs_str("3") }, { njs_str("'123456'.search(45)"), njs_str("3") }, { njs_str("'123456'.search(String(45))"), njs_str("3") }, { njs_str("'123456'.search(Number('45'))"), njs_str("3") }, { njs_str("var r = { toString: function() { return '45' } };" "'123456'.search(r)"), njs_str("3") }, { njs_str("var r = { toString: function() { return 45 } };" "'123456'.search(r)"), njs_str("3") }, { njs_str("var r = { toString: function() { return /45/ } };" "'123456'.search(r)"), njs_str("TypeError: Cannot convert object to primitive value") }, { njs_str("var r = { toString: function() { return /34/ }," " valueOf: function() { return 45 } };" "'123456'.search(r)"), njs_str("3") }, { njs_str("'abc'.replace()"), njs_str("abc") }, { njs_str("'abc'.replaceAll()"), njs_str("abc") }, { njs_str("'ABC'.replace('B')"), njs_str("AundefinedC") }, { njs_str("'ABCB'.replaceAll('B')"), njs_str("AundefinedCundefined") }, { njs_str("'ABC'.replace('B', undefined)"), njs_str("AundefinedC") }, { njs_str("'ABBC'.replaceAll('B', undefined)"), njs_str("AundefinedundefinedC") }, { njs_str("'a'.repeat(16).replace('a'.repeat(17)) === 'a'.repeat(16)"), njs_str("true") }, { njs_str("'a'.repeat(16).replaceAll('a'.repeat(17)) === 'a'.repeat(16)"), njs_str("true") }, { njs_str("'α'.repeat(16).replace('α'.repeat(17)) === 'α'.repeat(16)"), njs_str("true") }, { njs_str("'α'.repeat(16).replaceAll('α'.repeat(17)) === 'α'.repeat(16)"), njs_str("true") }, { njs_str("'ABC'.replace('B', null)"), njs_str("AnullC") }, { njs_str("'BABCB'.replaceAll('B', null)"), njs_str("nullAnullCnull") }, { njs_str("'abc'.replace('c', 1)"), njs_str("ab1") }, { njs_str("'cacbc'.replaceAll('c', 1)"), njs_str("1a1b1") }, { njs_str("'abc'.replace('a', 'X')"), njs_str("Xbc") }, { njs_str("'cabac'.replaceAll('a', 'X')"), njs_str("cXbXc") }, { njs_str("'abc'.replace('b', 'X')"), njs_str("aXc") }, { njs_str("'abc'.replaceAll('b', 'X')"), njs_str("aXc") }, { njs_str("('a'.repeat(33) + 'bb').replace('bb', 'CC').slice(31)"), njs_str("aaCC") }, { njs_str("('a'.repeat(33) + 'bb').replaceAll('b', 'C').slice(31)"), njs_str("aaCC") }, { njs_str("var r = 'abc'.replace('c', 'X'); [r, r.length]"), njs_str("abX,3") }, { njs_str("var r = 'acbc'.replaceAll('c', 'X'); [r, r.length]"), njs_str("aXbX,4") }, { njs_str("var r = 'αβγ'.replace('α', 'X'); [r, r.length]"), njs_str("Xβγ,3") }, { njs_str("var r = 'αβαγα'.replaceAll('α', 'X'); [r, r.length]"), njs_str("XβXγX,5") }, { njs_str("var r = 'αβγ'.replace('β', 'X'); [r, r.length]"), njs_str("αXγ,3") }, { njs_str("var r = 'αβγ'.replaceAll('β', 'X'); [r, r.length]"), njs_str("αXγ,3") }, { njs_str("var r = 'αβγ'.replace('γ', 'X'); [r, r.length]"), njs_str("αβX,3") }, { njs_str("var r = 'αβγγ'.replaceAll('γ', 'X'); [r, r.length]"), njs_str("αβXX,4") }, { njs_str("var r = 'αβγ'.replace('', 'X'); [r, r.length]"), njs_str("Xαβγ,4") }, { njs_str("var r = 'αβγ'.replaceAll('', 'X'); [r, r.length]"), njs_str("XαXβXγX,7") }, { njs_str("var r = ''.replaceAll('', 'z'); [r, r.length]"), njs_str("z,1") }, { njs_str("var r = 'α'.padStart(32).replaceAll('', 'z'); [r, r.length]"), njs_str("z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z z zαz,65") }, { njs_str("'abc'.replace('b', (m, o, s) => `|${s}|${o}|${m}|`)"), njs_str("a|abc|1|b|c") }, { njs_str("'abcb'.replaceAll('b', (m, o, s) => `|${s}|${o}|${m}|`)"), njs_str("a|abcb|1|b|c|abcb|3|b|") }, { njs_str("'abcdbe'.replace('b', '|$`X$\\'|')"), njs_str("a|aXcdbe|cdbe") }, { njs_str("'abcdbe'.replaceAll('b', '|$`X$\\'|')"), njs_str("a|aXcdbe|cd|abcdXe|e") }, { njs_str("var r = 'αβγ'.replace('β', \"$'\"); [r, r.length]"), njs_str("αγγ,3") }, { njs_str("var r = 'αβγαβγ'.replaceAll('β', \"$'\"); [r, r.length]"), njs_str("αγαβγγαγγ,9") }, { njs_str("var r = 'αβγ'.replace('β', \"$`\"); [r, r.length]"), njs_str("ααγ,3") }, { njs_str("var r = 'αβγαβγ'.replaceAll('β', \"$`\"); [r, r.length]"), njs_str("ααγααβγαγ,9") }, { njs_str("'ABC'.replace('B', '$')"), njs_str("A$C") }, { njs_str("'ABCB'.replaceAll('B', '$')"), njs_str("A$C$") }, { njs_str("'ABC'.replace('B', '$23')"), njs_str("A$23C") }, { njs_str("'ABBC'.replaceAll('B', '$23')"), njs_str("A$23$23C") }, { njs_str("'undefined'.replace(void 0, 'x')"), njs_str("x") }, { njs_str("'undefinedundefined'.replaceAll(void 0, 'x')"), njs_str("xx") }, { njs_str("'12345'.replace(3, () => 0)"), njs_str("12045") }, { njs_str("'123435'.replaceAll(3, () => 0)"), njs_str("120405") }, { njs_str("var r = new String('undefined').replace(x, Function('return arguments[1]+42;')); var x; r"), njs_str("42") }, { njs_str("var r = new String('undefined undefined').replaceAll(x, Function('return arguments[1]+42;')); var x; r"), njs_str("42 52") }, { njs_str("'123'.replace(3, function() { return {toString: ()=>({})}; })"), njs_str("TypeError: Cannot convert object to primitive value") }, { njs_str("'123'.replaceAll(3, function() { return {toString: ()=>({})}; })"), njs_str("TypeError: Cannot convert object to primitive value") }, { njs_str("'12345'.replace(3, () => ({toString: () => 'aaaa'}))"), njs_str("12aaaa45") }, { njs_str("'123435'.replaceAll(3, () => ({toString: () => 'aaaa'}))"), njs_str("12aaaa4aaaa5") }, { njs_str("'ABC'.replace('B', () => {throw 'OOps'})"), njs_str("OOps") }, { njs_str("'ABCB'.replaceAll('B', () => {throw 'OOps'})"), njs_str("OOps") }, { njs_str("'abc'.replace(/a/, 'X')"), njs_str("Xbc") }, { njs_str("'abc'.replaceAll(/a/, 'X')"), njs_str("TypeError: String.prototype.replaceAll called with a non-global RegExp argument") }, { njs_str("'abac'.replaceAll(/a/g, 'X')"), njs_str("XbXc") }, { njs_str("'abccd'.replace(/c/, 'X')"), njs_str("abXcd") }, { njs_str("'abccd'.replaceAll(/c/g, 'X')"), njs_str("abXXd") }, { njs_str("'abc'.replace(/c/, 'X')"), njs_str("abX") }, { njs_str("'abc'.replaceAll(/c/g, 'X')"), njs_str("abX") }, { njs_str("'abccd'.replace(/c+/, 'X')"), njs_str("abXd") }, { njs_str("'acccbccd'.replaceAll(/c+/g, 'X')"), njs_str("aXbXd") }, { njs_str("'abc'.replace(/f/, 'X')"), njs_str("abc") }, { njs_str("'abc'.replaceAll(/f/g, 'X')"), njs_str("abc") }, { njs_str("'AB=C==='.replace(/=*$/, '')"), njs_str("AB=C") }, { njs_str("'AB=C==='.replaceAll(/=*$/g, '')"), njs_str("AB=C") }, { njs_str("('a'.repeat(33) + 'bb').replace(/bb/, 'CC').slice(31)"), njs_str("aaCC") }, { njs_str("('a'.repeat(33) + 'bb').replaceAll(/b/g, 'C').slice(31)"), njs_str("aaCC") }, { njs_str("'abccd'.replace(/c/g, 'X')"), njs_str("abXXd") }, { njs_str("'abccd'.replaceAll(/c/g, 'X')"), njs_str("abXXd") }, { njs_str("('a'.repeat(33) + 'bb').replace(/bb/g, 'CC').slice(31)"), njs_str("aaCC") }, { njs_str("('a'.repeat(33) + 'bb').replaceAll(/bb/g, 'CC').slice(31)"), njs_str("aaCC") }, { njs_str("'abccd'.replace(/[ac]/g, 'X')"), njs_str("XbXXd") }, { njs_str("'abccd'.replaceAll(/[ac]/g, 'X')"), njs_str("XbXXd") }, { njs_str("'ab'.replace(/q*/g, 'X')"), njs_str("XaXbX") }, { njs_str("'ab'.replaceAll(/q*/g, 'X')"), njs_str("XaXbX") }, { njs_str("'αβ'.replace(/q*/g, 'X')"), njs_str("XαXβX") }, { njs_str("'αβ'.replaceAll(/q*/g, 'X')"), njs_str("XαXβX") }, { njs_str("'αβ'.replace(/(q)*/g, 'X')"), njs_str("XαXβX") }, { njs_str("'αβ'.replaceAll(/(q)*/g, 'X')"), njs_str("XαXβX") }, { njs_str("'αβ'.replace(/q*/g, 'γ')"), njs_str("γαγβγ") }, { njs_str("'αβ'.replaceAll(/q*/g, 'γ')"), njs_str("γαγβγ") }, { njs_str("':α:β:γ:'.replace(/:/g, '')"), njs_str("αβγ") }, { njs_str("':α:β:γ:'.replaceAll(/:/g, '')"), njs_str("αβγ") }, { njs_str("':α:β:γ:'.replace(/[αβγ]/g, '')"), njs_str("::::") }, { njs_str("':α:β:γ:'.replaceAll(/[αβγ]/g, '')"), njs_str("::::") }, { njs_str("'aabbccaa'.replace(/a*/g, '')"), njs_str("bbcc") }, { njs_str("'aabbccaa'.replaceAll(/a*/g, '')"), njs_str("bbcc") }, { njs_str("'aabbccaab'.replace(/z*/g, '')"), njs_str("aabbccaab") }, { njs_str("'aabbccaab'.replaceAll(/z*/g, '')"), njs_str("aabbccaab") }, { njs_str("''.replace(/a*/g, '')"), njs_str("") }, { njs_str("''.replaceAll(/a*/g, '')"), njs_str("") }, { njs_str("'abcde'.replace(/d/, (m, o, s) => `|${s}|${o}|${m}|`)"), njs_str("abc|abcde|3|d|e") }, { njs_str("'abcdde'.replaceAll(/d/g, (m, o, s) => `|${s}|${o}|${m}|`)"), njs_str("abc|abcdde|3|d||abcdde|4|d|e") }, { njs_str("'abcde'.replace(/(d)/, (m, p, o, s) => `|${s}|${o}|${m}|${p}|`)"), njs_str("abc|abcde|3|d|d|e") }, { njs_str("'abcded'.replaceAll(/(d)/g, (m, p, o, s) => `|${s}|${o}|${m}|${p}|`)"), njs_str("abc|abcded|3|d|d|e|abcded|5|d|d|") }, { njs_str("'abc'.replace(/b/, () => 1)"), njs_str("a1c") }, { njs_str("'abcb'.replaceAll(/b/g, () => 1)"), njs_str("a1c1") }, { njs_str("var n = 0; 'abbbc'.replace(/b/g, () => ++n)"), njs_str("a123c") }, { njs_str("var n = 0; 'abbbc'.replaceAll(/b/g, () => ++n)"), njs_str("a123c") }, { njs_str("'abc'.replace(/x/, (m, o, s) => `|${s}|${o}|${m}|`)"), njs_str("abc") }, { njs_str("'abc'.replaceAll(/x/g, (m, o, s) => `|${s}|${o}|${m}|`)"), njs_str("abc") }, { njs_str("'abc12345#$*%'.replace(/([^\\d]*)(\\d*)([^\\w]*)/," " (_, p1, p2, p3) => [p1, p2, p3].join('-'))"), njs_str("abc-12345-#$*%") }, { njs_str("'abc12345#$*%'.replaceAll(/([^\\d]*)(\\d*)([^\\w]*)/g," " (_, p1, p2, p3) => [p1, p2, p3].join('-'))"), njs_str("abc-12345-#$*%--") }, { njs_str("'abc'.replace(/(?b)/, (m, p, o, s, gr) => `|${gr.named}|`)"), njs_str("a|b|c") }, { njs_str("'abcb'.replaceAll(/(?b)/g, (m, p, o, s, gr) => `|${gr.named}|`)"), njs_str("a|b|c|b|") }, { njs_str("'ABC'.replace(/[A-Z]/g, m => '-' + m.toLowerCase())"), njs_str("-a-b-c") }, { njs_str("'ABC'.replaceAll(/[A-Z]/g, m => '-' + m.toLowerCase())"), njs_str("-a-b-c") }, { njs_str("'abc'.replace(/(b)c/g, '|$01|')"), njs_str("a|b|") }, { njs_str("'abc'.replaceAll(/(b)c/g, '|$01|')"), njs_str("a|b|") }, { njs_str("'abc'.replace(/(b)c/g, '@$0|$01|$00@')"), njs_str("a@$0|b|$00@") }, { njs_str("'abc'.replaceAll(/(b)c/g, '@$0|$01|$00@')"), njs_str("a@$0|b|$00@") }, { njs_str("'abcdeFGHIJ'.replace(/(a)(b)(c)(d)(e)(F)(G)(H)(I)(J)/, '$9|$10|$11|$01')"), njs_str("I|J|a1|a") }, { njs_str("'abcdeFGHIJ abcdeFGHIJ'.replaceAll(/(a)(b)(c)(d)(e)(F)(G)(H)(I)(J)/g, '$9|$10|$11|$01')"), njs_str("I|J|a1|a I|J|a1|a") }, { njs_str("'abcdbe'.replace(/(b)/g, '$2$23')"), njs_str("a$2$23cd$2$23e") }, { njs_str("'abcdbe'.replaceAll(/(b)/g, '$2$23')"), njs_str("a$2$23cd$2$23e") }, { njs_str("'abcdbe'.replace(/(b)/g, '$2$23X$$Y')"), njs_str("a$2$23X$Ycd$2$23X$Ye") }, { njs_str("'abcdbe'.replaceAll(/(b)/g, '$2$23X$$Y')"), njs_str("a$2$23X$Ycd$2$23X$Ye") }, { njs_str("'abcdbe'.replace(/b/, '|$`X$\\'|')"), njs_str("a|aXcdbe|cdbe") }, { njs_str("'abcdbe'.replaceAll(/b/g, '|$`X$\\'|')"), njs_str("a|aXcdbe|cd|abcdXe|e") }, { njs_str("'abcdbefbgh'.replace(/b/g, '|$`X$\\'|')"), njs_str("a|aXcdbefbgh|cd|abcdXefbgh|ef|abcdbefXgh|gh") }, { njs_str("'abcdbefbgh'.replaceAll(/b/g, '|$`X$\\'|')"), njs_str("a|aXcdbefbgh|cd|abcdXefbgh|ef|abcdbefXgh|gh") }, { njs_str("'abc12345#$*%'.replace(/([^\\d]*)(\\d*)([^\\w]*)/, '$1-$2-$3')"), njs_str("abc-12345-#$*%") }, { njs_str("'abc12345#$*%'.replaceAll(/([^\\d]*)(\\d*)([^\\w]*)/g, '$1-$2-$3')"), njs_str("abc-12345-#$*%--") }, { njs_str("'$1,$2'.replace(/(\\$(\\d))/g, '$$1-$1$2')"), njs_str("$1-$11,$1-$22") }, { njs_str("'$1,$2'.replaceAll(/(\\$(\\d))/g, '$$1-$1$2')"), njs_str("$1-$11,$1-$22") }, { njs_str("'ABC'.replace(/(h*)(z*)(g*)/g, '$1@$2α$3')"), njs_str("@αA@αB@αC@α") }, { njs_str("'ABC'.replaceAll(/(h*)(z*)(g*)/g, '$1@$2α$3')"), njs_str("@αA@αB@αC@α") }, { njs_str("'abc'.replace(/(h*)(z*)/g, '$1@$2#$3:')"), njs_str("@#$3:a@#$3:b@#$3:c@#$3:") }, { njs_str("'abc'.replaceAll(/(h*)(z*)/g, '$1@$2#$3:')"), njs_str("@#$3:a@#$3:b@#$3:c@#$3:") }, { njs_str("/b(c)(z)?(.)/[Symbol.replace]('abcde', '[$1$2$3]')"), njs_str("a[cd]e") }, { njs_str("/b(c)(z)?(.)/[Symbol.replace]('abcde', '[$01$02$03$04$00]')"), njs_str("a[cd$04$00]e") }, { njs_str("var r = /./; r.exec = () => {return {}};" "r[Symbol.replace]('ABCD', 'b')"), njs_str("b") }, { njs_str("var r = /./; r.exec = () => {return {}};" "r[Symbol.replace]('ABCD', (m,p,o) => `${m}|${p}|${o}`)"), njs_str("undefined|0|ABCD") }, { njs_str("var r = /./; r.exec = () => Buffer.from([]).toJSON().data;" "r[Symbol.replace]('ABCD', 'b')"), njs_str("b") }, { njs_str("'α'.replace(/(h*)/g, '$1βγ')"), njs_str("βγαβγ") }, { njs_str("'α'.replaceAll(/(h*)/g, '$1βγ')"), njs_str("βγαβγ") }, { njs_str("'αg'.replace(/(h*)/g, '$1βγ')"), njs_str("βγαβγgβγ") }, { njs_str("'αg'.replaceAll(/(h*)/g, '$1βγ')"), njs_str("βγαβγgβγ") }, { njs_str("'αg'.replace(/(α*)/g, '$1βγ')"), njs_str("αβγβγgβγ") }, { njs_str("'αg'.replaceAll(/(α*)/g, '$1βγ')"), njs_str("αβγβγgβγ") }, { njs_str("'αg'.replace(/(h*)/g, 'fg$1βγ')"), njs_str("fgβγαfgβγgfgβγ") }, { njs_str("'αg'.replaceAll(/(h*)/g, 'fg$1βγ')"), njs_str("fgβγαfgβγgfgβγ") }, { njs_str("'αgβfγ'.replace(/(gβ)/g, 'n$1i')"), njs_str("αngβifγ") }, { njs_str("'αgβfγ'.replaceAll(/(gβ)/g, 'n$1i')"), njs_str("αngβifγ") }, { njs_str("'abc'.replace(/b/g, '|$&|')"), njs_str("a|b|c") }, { njs_str("'abc'.replaceAll(/b/g, '|$&|')"), njs_str("a|b|c") }, { njs_str("'ABC'.replace(/((A)B)/g, '($1|$&|$2)')"), njs_str("(AB|AB|A)C") }, { njs_str("'ABC'.replaceAll(/((A)B)/g, '($1|$&|$2)')"), njs_str("(AB|AB|A)C") }, { njs_str("'abc'.replace(/b/g, '$0')"), njs_str("a$0c") }, { njs_str("'abc'.replaceAll(/b/g, '$0')"), njs_str("a$0c") }, { njs_str("'abc'.replace(/^/g, '|$&|')"), njs_str("||abc") }, { njs_str("'abc'.replaceAll(/^/g, '|$&|')"), njs_str("||abc") }, { njs_str("('α'.repeat(30) + 'aa').replace(/a/g, '#')"), njs_str("αααααααααααααααααααααααααααααα##") }, { njs_str("('α'.repeat(30) + 'aa').replaceAll(/a/g, '#')"), njs_str("αααααααααααααααααααααααααααααα##") }, { njs_str("var uri ='/u/v1/Aa/bB?type=m3u8&mt=42';" "uri.replace(/^\\/u\\/v1\\/[^/]*\\/([^\?]*)\\?.*(mt=[^&]*).*$/, '$1|$2')"), njs_str("bB|mt=42") }, { njs_str("var uri ='/u/v1/Aa/bB?type=m3u8&mt=42 /u/v1/Aa/bB?type=m3u8&mt=43';" "uri.replaceAll(/^\\/u\\/v1\\/[^/]*\\/([^\?]*)\\?.*(mt=[^&]*).*$/g, '$1|$2')"), njs_str("bB|mt=43") }, { njs_str("'ABC'.replace(/B/, '$')"), njs_str("A$C") }, { njs_str("'ABBC'.replaceAll(/B/g, '$')"), njs_str("A$$C") }, { njs_str("'ABC'.replace(/(?B)/, '|$|@$@')"), njs_str("A|B|@@C") }, { njs_str("'ABBC'.replaceAll(/(?B)/g, '|$|@$@')"), njs_str("A|B|@@|B|@@C") }, { njs_str("'ABC'.replace(/(?B)/, '|$B)/g, '|$B)/, '|$@')"), njs_str("A|@C") }, { njs_str("'ABCB'.replaceAll(/(?B)/g, '|$@')"), njs_str("A|@C|@") }, { njs_str("'α'.repeat(8).replace(/()/g, '$`') == 'α'.repeat(44)"), njs_str("true") }, { njs_str("('β' + 'α'.repeat(33)+'β').replace(/(α+)(β+)/, (m, p1) => p1[32])"), njs_str("βα") }, { njs_str("('β' + 'α'.repeat(33)+'β').replaceAll(/(α+)(β+)/g, (m, p1) => p1[32])"), njs_str("βα") }, { njs_str("'abc'.replace(/(z*)/g, () => '@')"), njs_str("@a@b@c@") }, { njs_str("'abc'.replaceAll(/(z*)/g, () => '@')"), njs_str("@a@b@c@") }, { njs_str("'abc'.replace(/(a*)/g, () => '@')"), njs_str("@@b@c@") }, { njs_str("'abc'.replaceAll(/(a*)/g, () => '@')"), njs_str("@@b@c@") }, { njs_str("var O = RegExp.prototype[Symbol.replace];" "RegExp.prototype[Symbol.replace] = function (s, rep) { return O.call(this, s, `|${rep}|`); };" "'ABC'.replace(/B/, '+')"), njs_str("A|+|C") }, { njs_str("var O = RegExp.prototype[Symbol.replace];" "RegExp.prototype[Symbol.replace] = function (s, rep) { return O.call(this, s, `|${rep}|`); };" "'ABC'.replaceAll(/B/g, '+')"), njs_str("A|+|C") }, { njs_str("var O = RegExp.prototype.exec;" "function mangled(s) { var r = O.call(this, s);" " Object.defineProperty(r, '0', {enumerable:false}); " " return r; };" "RegExp.prototype.exec = mangled;" "'ABC'.replace(/(B)/, (m, p1, off, s) => `@${m}|${p1}|${off}|${s}@`)"), njs_str("A@B|B|1|ABC@C") }, { njs_str("var O = RegExp.prototype.exec;" "function mangled(s) { var r = O.call(this, s);" " Object.defineProperty(r, '0', {enumerable:false}); " " return r; };" "RegExp.prototype.exec = mangled;" "'ABC'.replaceAll(/(B)/g, (m, p1, off, s) => `@${m}|${p1}|${off}|${s}@`)"), njs_str("TypeError: Object.defineProperty is called on non-object") }, { njs_str("var O = RegExp.prototype.exec;" "function mangled(s) { var r = O.call(this, s);" " Object.defineProperty(r, 'groups', {value: {g:1}}); " " return r; };" "RegExp.prototype.exec = mangled;" "'ABC'.replace(/(B)/, '$')"), njs_str("A1C") }, { njs_str("var O = RegExp.prototype.exec;" "function mangled(s) { var r = O.call(this, s);" " Object.defineProperty(r, 'groups', {value: {g:1}}); " " return r; };" "RegExp.prototype.exec = mangled;" "'ABC'.replaceAll(/(B)/g, '$')"), njs_str("TypeError: Object.defineProperty is called on non-object") }, { njs_str("var O = RegExp.prototype.exec;" "function mangled(s) { var r = O.call(this, s);" " Object.defineProperty(r, 'groups', {value: {get g() {throw 'OOps'}}}); " " return r; };" "RegExp.prototype.exec = mangled;" "'ABC'.replace(/(B)/, '$')"), njs_str("OOps") }, { njs_str("var O = RegExp.prototype.exec;" "function mangled(s) { var r = O.call(this, s);" " Object.defineProperty(r, 'groups', {value: {get g() {throw 'OOps'}}}); " " return r; };" "RegExp.prototype.exec = mangled;" "'ABC'.replaceAll(/(B)/g, '$')"), njs_str("TypeError: Object.defineProperty is called on non-object") }, { njs_str("var name = /a/g[Symbol.replace].name; [name, typeof name]"), njs_str("[Symbol.replace],string") }, { njs_str("RegExp.prototype[Symbol.replace].call()"), njs_str("TypeError: \"this\" is not object") }, { njs_str("RegExp.prototype[Symbol.replace].call(1)"), njs_str("TypeError: \"this\" is not object") }, { njs_str("RegExp.prototype[Symbol.replace].call(/b/, 'abc','B')"), njs_str("aBc") }, { njs_str("var m; var r = /./; r.exec = function() { return []; };" "r[Symbol.replace]('foo', function() {m = arguments[0]}); [m, typeof m]"), njs_str("undefined,string") }, { njs_str("var a = [];" "a[2] = '';" "var re = /any_regexp/;" "re.exec = function () {" " return a;" "};" "'any_string'.replace(re)"), njs_str("undefinedg") }, { njs_str("var cnt = 0;" "var a = [];" "a[2] = '';" "var re = /any_regexp/g;" "re.exec = function () {" " if (cnt++ > 1) return null;" " return a;" "};" "'any_string'.replace(re)"), njs_str("undefinedg") }, { njs_str("var cnt = 0;" "var a = [];" "a[2] = '';" "var re = /any_regexp/g;" "re.exec = function () {" " if (cnt++ > 1) return null;" " return a;" "};" "'any_string'.replaceAll(re)"), njs_str("undefinedg") }, { njs_str("var a = [];" "a[2] = {toString() {a[2**20] = 1; return 'X';}}; " "a[4] = 'Y';" "a[99] = 'Z';" "a[100] = '*';" "a[200] = '!';" "var re = /b/;" "re.exec = () => a;" "'abc'.replace(re, '@$1|$2|$3|$4|$99|$100|@')"), njs_str("@|X||Y|Z|0|@") }, { njs_str("var cnt = 0;" "var a = [];" "a[2] = {toString() {a[2**20] = 1; return 'X';}}; " "a[4] = 'Y';" "a[99] = 'Z';" "a[100] = '*';" "a[200] = '!';" "var re = /b/g;" "re.exec = () => {if (cnt++ > 1) return null; return a};" "'abc'.replace(re, '@$1|$2|$3|$4|$99|$100|@')"), njs_str("@|X||Y|Z|0|@") }, { njs_str("var cnt = 0;" "var a = [];" "a[2] = {toString() {a[2**20] = 1; return 'X';}}; " "a[4] = 'Y';" "a[99] = 'Z';" "a[100] = '*';" "a[200] = '!';" "var re = /b/g;" "re.exec = () => {if (cnt++ > 1) return null; return a};" "'abc'.replaceAll(re, '@$1|$2|$3|$4|$99|$100|@')"), njs_str("@|X||Y|Z|0|@") }, { njs_str("var a = [];" "Object.defineProperty(a, 32768, {});" "var re = /any_regexp/;" "re.exec = function () {" " return a;" "};" "'any_string'.replace(re)"), njs_str("undefinedg") }, { njs_str("var cnt = 0;" "var a = [];" "Object.defineProperty(a, 32768, {});" "var re = /any_regexp/g;" "re.exec = function () {" " if (cnt++ > 1) return null;" " return a;" "};" "'any_string'.replace(re)"), njs_str("undefinedg") }, { njs_str("var cnt = 0;" "var a = [];" "Object.defineProperty(a, 32768, {});" "var re = /any_regexp/g;" "re.exec = function () {" " if (cnt++ > 1) return null;" " return a;" "};" "'any_string'.replace(re)"), njs_str("undefinedg") }, { njs_str("var r = /h/g;" "Object.defineProperty(r,'flags',{value: ''});" "''.replaceAll(r,'');"), njs_str("TypeError: String.prototype.replaceAll called with a non-global RegExp argument") }, { njs_str("/=/"), njs_str("/=/") }, { njs_str("/["), njs_str("SyntaxError: Unterminated RegExp \"/[\" in 1") }, { njs_str("/[\\"), njs_str("SyntaxError: Unterminated RegExp \"/[\\\" in 1") }, { njs_str("/\\s*;\\s*/"), njs_str("/\\s*;\\s*/") }, #ifndef NJS_HAVE_PCRE2 { njs_str("/]/"), njs_str("/\\]/") }, { njs_str("RegExp(']')"), njs_str("/\\]/") }, { njs_str("RegExp('[\\\\\\\\]]')"), njs_str("/[\\\\]\\]/") }, { njs_str("/[\\\\]]/"), njs_str("/[\\\\]\\]/") }, { njs_str("/\\]/"), njs_str("/\\]/") }, { njs_str("RegExp('\\]')"), njs_str("/\\]/") }, { njs_str("/ab]cd/"), njs_str("/ab\\]cd/") }, { njs_str("/ab]/"), njs_str("/ab\\]/") }, { njs_str("/]cd/"), njs_str("/\\]cd/") }, #endif { njs_str("RegExp('[\\\\')"), njs_str("SyntaxError: " njs_pcre_var("pcre_compile2(\"[\\\") failed: \\ at end of pattern at \"\"", "pcre_compile(\"[\\\") failed: \\ at end of pattern")) }, { njs_str("RegExp('\\\\0').source[1]"), njs_str("0") }, { njs_str("RegExp(undefined, 'g').global"), njs_str("true") }, { njs_str("RegExp('', 'g').global"), njs_str("true") }, { njs_str("var x; RegExp(x, 'g')"), njs_str("/(?:)/g") }, { njs_str("']'.match(/]/)"), njs_str("]") }, { njs_str("'ab]cd'.match(/]/)"), njs_str("]") }, { njs_str("'ab]'.match(/]/)"), njs_str("]") }, { njs_str("']cd'.match(/]/)"), njs_str("]") }, { njs_str("'ab]cd'.match(/\\]/)"), njs_str("]") }, { njs_str("'abc'.match(/a*/g)"), njs_str("a,,,") }, { njs_str("'abc'.match(/z*/g)"), njs_str(",,,") }, { njs_str("'abc'.match(/.?/g)"), njs_str("a,b,c,") }, { njs_str("''.match(/a*/g)"), njs_str("") }, { njs_str("''.match(/.?/g)"), njs_str("") }, { njs_str("'абв'.match(/я?/g)"), njs_str(",,,") }, { njs_str("'αβγ'.match(/z*/g)"), njs_str(",,,") }, { njs_str("'囲碁織'.match(/z*/g)"), njs_str(",,,") }, { njs_str("'𝟘𝟙𝟚𝟛'.match(/z*/g)"), njs_str(",,,,") }, { njs_str("'abcdefgh'.match()"), njs_str("") }, { njs_str("'abcdefgh'.match('')"), njs_str("") }, { njs_str("'abcdefgh'.match(undefined)"), njs_str("") }, { njs_str("'abcdefgh'.match(/def/)"), njs_str("def") }, { njs_str("'abcdefgh'.match('def')"), njs_str("def") }, { njs_str("'123456'.match('45')"), njs_str("45") }, { njs_str("'123456'.match(45)"), njs_str("45") }, { njs_str("'123456'.match(String(45))"), njs_str("45") }, { njs_str("'123456'.match(Number('45'))"), njs_str("45") }, { njs_str("var r = { toString: function() { return '45' } };" "'123456'.match(r)"), njs_str("45") }, { njs_str("var r = { toString: function() { return 45 } };" "'123456'.match(r)"), njs_str("45") }, { njs_str("var r = { toString: function() { return /45/ } };" "'123456'.match(r)"), njs_str("TypeError: Cannot convert object to primitive value") }, { njs_str("var r = { toString: function() { return /34/ }," " valueOf: function() { return 45 } };" "'123456'.match(r)"), njs_str("45") }, { njs_str("''.match(/^$/)"), njs_str("") }, { njs_str("''.match(/^$/g)"), njs_str("") }, { njs_str("'abcdefgh'.match(/def/)"), njs_str("def") }, { njs_str("'abc abc abc'.match('abc')"), njs_str("abc") }, { njs_str("'abc abc abc'.match(/abc/)"), njs_str("abc") }, { njs_str("'abc abc abc'.match(/abc/g)"), njs_str("abc,abc,abc") }, { njs_str("'abc ABC aBc'.match(/abc/ig)"), njs_str("abc,ABC,aBc") }, { njs_str("var a = 'α'.match(/α/g)[0] + 'α';" "a +' '+ a.length"), njs_str("αα 2") }, { njs_str("('β' + 'α'.repeat(33) +'β').match(/α+/g)[0][32]"), njs_str("α") }, { njs_str("'abc'.split()"), njs_str("abc") }, { njs_str("'abc'.split(undefined)"), njs_str("abc") }, { njs_str("''.split('').length"), njs_str("0") }, { njs_str("'abc'.split('')"), njs_str("a,b,c") }, { njs_str("'αβγ'.split('')"), njs_str("α,β,γ") }, { njs_str("'囲碁織'.split('')"), njs_str("囲,碁,織") }, { njs_str("'𝟘𝟙𝟚𝟛'.split('')"), njs_str("𝟘,𝟙,𝟚,𝟛") }, { njs_str("'囲α碁α織'.split('α')"), njs_str("囲,碁,織") }, { njs_str("'a'.repeat(16).split('a'.repeat(15))"), njs_str(",a") }, { njs_str("('α'+'β'.repeat(33)).repeat(2).split('α')[1][32]"), njs_str("β") }, { njs_str("'abc'.split('abc')"), njs_str(",") }, { njs_str("'a bc def'.split(' ')"), njs_str("a,bc,def") }, { njs_str("'a bc def'.split(' ')"), njs_str("a,bc,,def") }, { njs_str("'a bc def'.split(' ', 3)"), njs_str("a,bc,") }, { njs_str("'abc'.split('abc')"), njs_str(",") }, { njs_str("'ab'.split('123')"), njs_str("ab") }, { njs_str("''.split(/0/).length"), njs_str("1") }, { njs_str("'abc'.split(/(?:)/)"), njs_str("a,b,c") }, { njs_str("'a bc def'.split(/ /)"), njs_str("a,bc,def") }, { njs_str("'a bc def'.split(/ /)"), njs_str("a,bc,,def") }, { njs_str("'abc'.split(/abc/)"), njs_str(",") }, { njs_str("('α'.repeat(32)).split(/./).length"), njs_str("33") }, { njs_str("'AbcDefGhi'.split(/([A-Z][a-z]+)/)"), njs_str(",Abc,,Def,,Ghi,") }, { njs_str("'myCamelCaseString'.split(/(?=[A-Z])/)"), njs_str("my,Camel,Case,String") }, { njs_str("'мояВерблюжьяСтрока'.split(/(?=[А-Я])/)"), njs_str("моя,Верблюжья,Строка") }, { njs_str("`aaaaaaaaaaaaaaaaa`.split(/(.*)/)"), njs_str(",aaaaaaaaaaaaaaaaa,") }, { njs_str("'Harry Trump ;Fred Barney; Helen Rigby ; Bill Abel ;Chris Hand '.split( /\\s*(?:;|$)\\s*/)"), njs_str("Harry Trump,Fred Barney,Helen Rigby,Bill Abel,Chris Hand,") }, { njs_str("'Гарри Трамп ;Фрэд Барни; Хелен Ригби ; Билл Абель'.split(/\\s*;\\s*/)"), njs_str("Гарри Трамп,Фрэд Барни,Хелен Ригби,Билл Абель") }, { njs_str("'Hello 1 world. Sentence number 2.'.split(/(\\d)/)"), njs_str("Hello ,1, world. Sentence number ,2,.") }, { njs_str("'Привет 1 мир. Предложение номер 2.'.split(/(\\d)/)"), njs_str("Привет ,1, мир. Предложение номер ,2,.") }, { njs_str("'0123456789'.split('').reverse().join('')"), njs_str("9876543210") }, { njs_str("/-/[Symbol.split]('a-b-c')"), njs_str("a,b,c") }, { njs_str("var O = RegExp.prototype[Symbol.split];" "RegExp.prototype[Symbol.split] = function (s, limit) { " " return O.call(this, s, limit).map(v => `@${v}#`); " "};" "'2016-01-02'.split(/-/)"), njs_str("@2016#,@01#,@02#") }, { njs_str("'abc'.repeat(3)"), njs_str("abcabcabc") }, { njs_str("'абв'.repeat(3)"), njs_str("абвабвабв") }, { njs_str("''.repeat(3)"), njs_str("") }, { njs_str("'abc'.repeat(0)"), njs_str("") }, { njs_str("'abc'.repeat(NaN)"), njs_str("") }, { njs_str("'abc'.repeat(Infinity)"), njs_str("RangeError: invalid count value") }, { njs_str("'abc'.repeat(-1)"), njs_str("RangeError: invalid count value") }, { njs_str("''.repeat(-1)"), njs_str("RangeError: invalid count value") }, { njs_str("'a'.repeat(2147483647)"), njs_str("RangeError: invalid string length") }, { njs_str("'a'.repeat(2147483648)"), njs_str("RangeError: invalid string length") }, { njs_str("'a'.repeat(Infinity)"), njs_str("RangeError: invalid count value") }, { njs_str("'a'.repeat(NaN)"), njs_str("") }, { njs_str("''.repeat(2147483646)"), njs_str("") }, { njs_str("''.repeat(2147483647)"), njs_str("") }, { njs_str("''.repeat(2147483648)"), njs_str("") }, { njs_str("'aaaaaaaa'.repeat(2**64+1)"), njs_str("RangeError: invalid count value") }, { njs_str("''.repeat(Infinity)"), njs_str("RangeError: invalid count value") }, { njs_str("''.repeat(NaN)"), njs_str("") }, { njs_str("String.prototype.repeat.call({},2)"), njs_str("[object Object][object Object]") }, { njs_str("'abc'.padStart(7)"), njs_str(" abc") }, { njs_str("'абв'.padStart(7)"), njs_str(" абв") }, { njs_str("'abc'.padStart(3)"), njs_str("abc") }, { njs_str("'абв'.padStart(0)"), njs_str("абв") }, { njs_str("'abc'.padStart(NaN)"), njs_str("abc") }, { njs_str("'abc'.padStart(2147483647)"), njs_str("RangeError: invalid string length") }, { njs_str("'abc'.padStart(2147483646, '')"), njs_str("abc") }, { njs_str("''.padStart(0, '')"), njs_str("") }, { njs_str("'1'.padStart(5, 0)"), njs_str("00001") }, { njs_str("''.padStart(1, 'я')"), njs_str("я") }, { njs_str("'abc'.padStart(6, NaN)"), njs_str("NaNabc") }, { njs_str("'abc'.padStart(11, 123)"), njs_str("12312312abc") }, { njs_str("'abc'.padStart(6, 12345)"), njs_str("123abc") }, { njs_str("'абв'.padStart(6, 'эюя')"), njs_str("эюяабв") }, { njs_str("'абв'.padStart(4, 'эюя')"), njs_str("эабв") }, { njs_str("'абв'.padStart(7, 'эюя')"), njs_str("эюяэабв") }, { njs_str("'абв'.padStart(10, 'эю')"), njs_str("эюэюэюэабв") }, { njs_str("'abc'.padStart(10, Symbol())"), njs_str("TypeError: Cannot convert a Symbol value to a string") }, { njs_str("'1234'.padEnd(4)"), njs_str("1234") }, { njs_str("'1234'.padEnd(-1)"), njs_str("1234") }, { njs_str("'я'.padEnd(1)"), njs_str("я") }, { njs_str("'1234'.padEnd(5)"), njs_str("1234 ") }, { njs_str("'я'.padEnd(6)"), njs_str("я ") }, { njs_str("'я'.padEnd(2147483647)"), njs_str("RangeError: invalid string length") }, { njs_str("'я'.padEnd(2147483646, '')"), njs_str("я") }, { njs_str("''.padEnd(0, '')"), njs_str("") }, { njs_str("'эю'.padEnd(3, 'я')"), njs_str("эюя") }, { njs_str("''.padEnd(1, 0)"), njs_str("0") }, { njs_str("'1234'.padEnd(8, 'abcd')"), njs_str("1234abcd") }, { njs_str("'1234'.padEnd(10, 'abcd')"), njs_str("1234abcdab") }, { njs_str("'1234'.padEnd(7, 'abcd')"), njs_str("1234abc") }, { njs_str("'абв'.padEnd(5, 'ГД')"), njs_str("абвГД") }, { njs_str("'абв'.padEnd(4, 'ГДУ')"), njs_str("абвГ") }, { njs_str("'абвг'.padEnd(10, 'ДЕЖЗ')"), njs_str("абвгДЕЖЗДЕ") }, { njs_str("'abc'.padEnd(10, Symbol())"), njs_str("TypeError: Cannot convert a Symbol value to a string") }, { njs_str("encodeURI.name"), njs_str("encodeURI")}, { njs_str("encodeURI.length"), njs_str("1")}, { njs_str("encodeURI('012абв')"), njs_str("012%D0%B0%D0%B1%D0%B2")}, { njs_str("encodeURI(String.fromCharCode(0xD800)+String.fromCharCode(0xDC00))"), njs_str("%EF%BF%BD%EF%BF%BD")}, { njs_str("encodeURI('~}|{`_^]\\\\[@?>=<;:/.-,+*)(\\\'&%$#\"! ')"), njs_str("~%7D%7C%7B%60_%5E%5D%5C%5B@?%3E=%3C;:/.-,+*)('&%25$#%22!%20")}, { njs_str("encodeURIComponent.name"), njs_str("encodeURIComponent")}, { njs_str("encodeURIComponent.length"), njs_str("1")}, { njs_str("encodeURIComponent('~}|{`_^]\\\\[@?>=<;:/.-,+*)(\\\'&%$#\"! ')"), njs_str("~%7D%7C%7B%60_%5E%5D%5C%5B%40%3F%3E%3D%3C%3B%3A%2F.-%2C%2B*)('%26%25%24%23%22!%20")}, { njs_str("decodeURI.name"), njs_str("decodeURI")}, { njs_str("decodeURI.length"), njs_str("1")}, { njs_str("decodeURI('%00')"), njs_str("\0")}, { njs_str("decodeURI('%3012%D0%B0%D0%B1%D0%B2')"), njs_str("012абв")}, { njs_str("decodeURI('%7e%7d%7c%7b%60%5f%5e%5d%5c%5b%40%3f%3e%3d%3c%3b%3a%2f%2e%2c%2b%2a%29%28%27%26%25%24%23%22%21%20')"), njs_str("~}|{`_^]\\[%40%3f>%3d<%3b%3a%2f.%2c%2b*)('%26%%24%23\"! ")}, { njs_str("decodeURIComponent.name"), njs_str("decodeURIComponent")}, { njs_str("decodeURIComponent.length"), njs_str("1")}, { njs_str("decodeURIComponent('%7e%7d%7c%7b%60%5f%5e%5d%5c%5b%40%3f%3e%3d%3c%3b%3a%2f%2e%2c%2b%2a%29%28%27%26%25%24%23%22%21%20')"), njs_str("~}|{`_^]\\[@?>=<;:/.,+*)('&%$#\"! ")}, { njs_str("decodeURI('%41%42%43').length"), njs_str("3")}, { njs_str("decodeURI('%D0%B0%D0%B1%D0%B2').length"), njs_str("3")}, { njs_str("[" " '%'," " '%0'," " '%QQ'," " '%C0%' + '0'," " '%C0%10'," " '%C0%80'," " '%DC%C7'," " '%80%81%82'," " '%EF%5C%A0'," " '%EF%A0%5E'," " '%E0%EF%' + '0'," " '%E0%EF%A0'," " '%E0%A0%EF'," " '%F0%A2%95%' + '0'," " '%FF%A2%95%BB'," "].every(v=>{try { decodeURI(v)} catch(e) {return e.name == 'URIError'}})"), njs_str("true")}, { njs_str("[" " 'abc'," " 'αβγ'," " '𝟘𝟙𝟚𝟛'," " String.fromCodePoint(0x20000)," "].every(v=>decodeURI(encodeURI(v)) === v)"), njs_str("true")}, { njs_str("[encodeURI, encodeURIComponent, decodeURI, decodeURIComponent]" ".every(v=>{var r = v(); return (typeof r === 'string') && r === 'undefined';})"), njs_str("true")}, /* btoa() */ { njs_str("[" " undefined," " ''," " '\\x00'," " '\\x00\\x01'," " '\\x00\\x01\\x02'," " '\\x00\\xfe\\xff'," " String.fromCodePoint(0x100)," " String.fromCodePoint(0x00, 0x100)," " String.fromCodePoint(0x00, 0x01, 0x100)," "].map(v => { try { return btoa(v); } catch (e) { return '#'} })"), njs_str("dW5kZWZpbmVk,,AA==,AAE=,AAEC,AP7/,#,#,#")}, /* atob() */ { njs_str("function c(s) {" " let cp = [];" " for (var i = 0; i < s.length; i++) {" " cp.push(s.codePointAt(i));" " }" " return cp;" "};" "" "[" " undefined," " ''," " '='," " '=='," " '==='," " '===='," " 'AA@'," " '@'," " 'A==A'," " btoa(String.fromCharCode.apply(null, [1]))," " btoa(String.fromCharCode.apply(null, [1, 2]))," " btoa(String.fromCharCode.apply(null, [1, 2, 255]))," " btoa(String.fromCharCode.apply(null, [255, 1, 2, 3]))," "].map(v => { try { return njs.dump(c(atob(v))); } catch (e) { return '#'} })"), njs_str("#,[],#,#,#,#,#,#,#,[1],[1,2],[1,2,255],[255,1,2,3]")}, { njs_str("function c(s) {" " let cp = [];" " for (var i = 0; i < s.length; i++) {" " cp.push(s.codePointAt(i));" " }" " return cp;" "};" "" "[" " 'CDRW'," " ' CDRW'," " 'C DRW'," " 'CD RW'," " 'CDR W'," " 'CDRW '," " ' C D R W '," "].every(v => c(atob(v)).toString() == '8,52,86')"), njs_str("true")}, { njs_str("atob('aGVsbG8=')"), njs_str("hello") }, { njs_str("atob('aGVsbG8')"), njs_str("hello") }, { njs_str("atob('TQ==')"), njs_str("M") }, { njs_str("atob('TQ')"), njs_str("M") }, /* Functions. */ { njs_str("return"), njs_str("SyntaxError: Illegal return statement in 1") }, { njs_str("{return}"), njs_str("SyntaxError: Illegal return statement in 1") }, { njs_str("\n{\nreturn;\n}"), njs_str("SyntaxError: Illegal return statement in 3") }, { njs_str("function f () {return a +}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("`${function(){return n=>}}`"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("(function(){return a +})"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("if (1) function f(){}"), njs_str("SyntaxError: Functions can only be declared at top level or inside a block in 1") }, { njs_str("if (1) { function f(){}}"), njs_str("undefined") }, { njs_str("while (1) function f() { }"), njs_str("SyntaxError: Functions can only be declared at top level or inside a block in 1") }, { njs_str("while (1) { break; function f(){}}"), njs_str("undefined") }, { njs_str("for (;;) function f() { }"), njs_str("SyntaxError: Functions can only be declared at top level or inside a block in 1") }, { njs_str("for (;;) { break; function f(){}}"), njs_str("undefined") }, { njs_str("do function f() { } while (0)"), njs_str("SyntaxError: Functions can only be declared at top level or inside a block in 1") }, { njs_str("function f() { return 1; } { function f() { return 2; } } f()"), njs_str("1") }, { njs_str("function f() { return 1; } { function f() { return 2; } { function f() { return 3; } }} f()"), njs_str("1") }, { njs_str("{function f() {} {} f() }"), njs_str("undefined") }, { njs_str("{ var f; function f() {} }"), njs_str("SyntaxError: \"f\" has already been declared in 1") }, { njs_str("{ function f() {} var f; }"), njs_str("SyntaxError: \"f\" has already been declared in 1") }, { njs_str("{ function f() {} { var f }}"), njs_str("SyntaxError: \"f\" has already been declared in 1") }, #if NJS_HAVE_LARGE_STACK { njs_str("function f() { return f() } f()"), njs_str("RangeError: Maximum call stack size exceeded") }, #endif { njs_str("function () { } f()"), njs_str("SyntaxError: Unexpected token \"(\" in 1") }, { njs_str("function f() { }"), njs_str("undefined") }, { njs_str("function f() { }; f.length"), njs_str("0") }, { njs_str("function f() { }; f.length = 1"), njs_str("TypeError: Cannot assign to read-only property \"length\" of function") }, { njs_str("function f(...rest) { }; f.length"), njs_str("0") }, { njs_str("function f(...rest) { }; var binded = f.bind(this, [1,2]);" "binded.length"), njs_str("0") }, { njs_str("function f(a,a) { };"), njs_str("SyntaxError: Duplicate parameter names in 1") }, { njs_str("function f(a,b,a) { };"), njs_str("SyntaxError: Duplicate parameter names in 1") }, { njs_str("function f(a, ...a) { };"), njs_str("SyntaxError: Duplicate parameter names in 1") }, { njs_str("(function(a,a) { })"), njs_str("SyntaxError: Duplicate parameter names in 1") }, { njs_str("(function(a,...a) { })"), njs_str("SyntaxError: Duplicate parameter names in 1") }, { njs_str("(function f(a,a) { })"), njs_str("SyntaxError: Duplicate parameter names in 1") }, { njs_str("(function f(a,...a) { })"), njs_str("SyntaxError: Duplicate parameter names in 1") }, { njs_str("function f(a,b) { }; f.length"), njs_str("2") }, { njs_str("function f(a,...rest) { }; f.length"), njs_str("1") }, { njs_str("function f(...) {}"), njs_str("SyntaxError: Unexpected token \")\" in 1") }, { njs_str("(function (...) {})()"), njs_str("SyntaxError: Unexpected token \")\" in 1") }, { njs_str("function f(a,b) { }; var ff = f.bind(f, 1); ff.length"), njs_str("1") }, { njs_str("Object((new Date(0)).toJSON())+0"), njs_str("1970-01-01T00:00:00.000Z0") }, { njs_str("Object((new Array(0)).toString())+0"), njs_str("0") }, { njs_str("JSON.parse.length"), njs_str("2") }, { njs_str("JSON.parse.bind(JSON, '[]').length"), njs_str("1") }, { njs_str("var o = {}; o.hasOwnProperty.length"), njs_str("1") }, { njs_str("var x; function f() { }"), njs_str("undefined") }, { njs_str("function f() { } f()"), njs_str("undefined") }, { njs_str("function f() { ; } f()"), njs_str("undefined") }, { njs_str("function f() { ;; } f()"), njs_str("undefined") }, { njs_str("function f() { return } f()"), njs_str("undefined") }, { njs_str("function f() { return; } f()"), njs_str("undefined") }, { njs_str("function f() { return;; } f()"), njs_str("undefined") }, { njs_str("function f() { return 1 } f()"), njs_str("1") }, { njs_str("function f() { return 1; } f()"), njs_str("1") }, { njs_str("function f() { return 1;; } f()"), njs_str("1") }, { njs_str("function f() { return 1\n 2 } f()"), njs_str("1") }, { njs_str("function f() { return 1\n 2 } f()"), njs_str("1") }, { njs_str("(function f() { return 2.toString(); })()"), njs_str("SyntaxError: Unexpected token \"toString\" in 1") }, { njs_str("(function f() { return 2..toString(); })()"), njs_str("2") }, { njs_str("function f(a) { if (a) return 'OK' } f(1)+f(0)"), njs_str("OKundefined") }, { njs_str("function f(a) { if (a) return 'OK'; } f(1)+f(0)"), njs_str("OKundefined") }, { njs_str("function f(a) { if (a) return 'OK';; } f(1)+f(0)"), njs_str("OKundefined") }, { njs_str("var a = 1; a()"), njs_str("TypeError: number is not a function") }, { njs_str("var o = {a:1}; o.a()"), njs_str("TypeError: (intermediate value)[\"a\"] is not a function") }, { njs_str("(function(){})()"), njs_str("undefined") }, { njs_str("var q = 1; function x(a, b, c) { q = a } x(5); q"), njs_str("5") }, { njs_str("function x(a) { while (a < 2) a++; return a + 1 } x(1) "), njs_str("3") }, { njs_str("(function(){" "(function(){" "(function(){" "(function(){" "(function(){" "(function(){" "(function(){" "(function(){" "(function(){})" "})" "})" "})" "})" "})" "})" "})" "})"), njs_str("[object Function]") }, { njs_str("Function.prototype.toString = function () {return 'X'};" "eval"), njs_str("X") }, { njs_str("var o = {f:function(x){ return x**2}}; o.f\n(2)"), njs_str("4") }, { njs_str("var o = {f:function(x){ return x**2}}; o\n.f\n(2)"), njs_str("4") }, { njs_str("var o = {f:function(x){ return x**2}}; o\n.\nf\n(2)"), njs_str("4") }, { njs_str("function f(x){ return x**2}; [f(2)\n, f\n(2),\nf\n(\n2),\nf\n(\n2\n)]"), njs_str("4,4,4,4") }, { njs_str("function f (x){ return x**2}; f\n(2)"), njs_str("4") }, { njs_str("function f (x){ return x**2}; f\n(\n2)"), njs_str("4") }, { njs_str("function f (x){ return x**2}; f\n(\n2\n)"), njs_str("4") }, { njs_str("function f (x){ return x**2}; f\n(2\n)"), njs_str("4") }, { njs_str("function f (x){ return x**2}; f(2\n)"), njs_str("4") }, { njs_str("var fn = Function.prototype.call; fn.call(() => 1)"), njs_str("1") }, { njs_str("var fn = Function.prototype.call; fn.call(fn, () => 1)"), njs_str("1") }, { njs_str("var fn = Function.prototype.call; fn.call(fn, fn, () => 1)"), njs_str("1") }, { njs_str("eval.call.call(Number)"), njs_str("0") }, { njs_str("URIError.apply.apply(RegExp)"), njs_str("/(?:)/") }, { njs_str("[0].some(function(){return Array.call.bind(isNaN)}())"), njs_str("false") }, { njs_str("(function (undefined, NaN, Infinity){ return undefined + NaN + Infinity})('x', 'y', 'z')"), njs_str("xyz") }, { njs_str("function f(undefined,NaN, Infinity){ return undefined + NaN + Infinity}; f('x', 'y', 'z')"), njs_str("xyz") }, { njs_str("(function (Object, Array, Boolean){ return Object + Array + Boolean})('x', 'y', 'z')"), njs_str("xyz") }, { njs_str("var n = 11, res;" "function a() {return b}" "res = a()(2);" "function b(k) {var x = b; return 1 + k + n} res"), njs_str("14") }, { njs_str("var y = 9, res;" "function a(n) {function b() {return c(n + 2)} return b()}" "res = a(1);" "function c(m) {var x = c; return m + 3 + y} res"), njs_str("15") }, { njs_str("var res;" "closure();" "res = globalThis.funcall(1);" "function closure() {" " var y = 9, res;" " globalThis.funcall = a;" " function a(n) { function b() {return c(2)} return b() }" " function c(m) {var x = c; return m + 3 + y}" "} res"), njs_str("14") }, { njs_str("function a() {" " var x = 1;" " function b() {var n = x; x = undefined; return n}" " return b;" "}" "[a()(), a()()];"), njs_str("1,1") }, { njs_str("function a(obj, name) {!Object.prototype.hasOwnProperty.call(obj, name)}" "a(this, 'b');" "function b() {}"), njs_str("undefined") }, { njs_str("function abc() {" "function x() {var a = x.arr; x.arr = 123; return a}" "return x;" "} [abc()(),abc()()]"), njs_str(",") }, { njs_str("function x() {var a = x.arr; x.arr = 123; return a} [x(),x()]"), njs_str(",123") }, { njs_str("var obj;" "function make(desc) {obj = {'a': 123}}" "function a(desc) {make()}" "a(); obj.a"), njs_str("123") }, { njs_str("var res;" "function cls() {" " var obj = {'a': 123};" " Object.defineProperty(obj, \"length\", {" " get: function() {res = obj}" " });" " return obj;" "}" "var obj = cls();" "[].includes.call(obj); res.a"), njs_str("123") }, { njs_str("function f(){} typeof(f)"), njs_str("function") }, { njs_str("function let() {}"), njs_str("SyntaxError: Unexpected token \"let\" in 1") }, { njs_str("function static() {}"), njs_str("SyntaxError: Unexpected token \"static\" in 1") }, { njs_str("var arr = [];" "function fn(one) {" " var x = one + 1;" " let y = one + 2;" " const u = one + 4;" " {" " {" " let z = one + 3;" " const v = one + 5;" " function f() {" " arr.push(one); arr.push(x);" " arr.push(y); arr.push(z);" " arr.push(u); arr.push(v);" " }" " f();" " }" " }" "}" "fn(1); arr"), njs_str("1,2,3,4,5,6") }, { njs_str("function f(){};" "Object.defineProperty(f, 'length', {set: () => {}});" "Object.defineProperty(f, 'length', {value: 42});" "f.length"), njs_str("42") }, { njs_str("function f(){}; f.name"), njs_str("f") }, { njs_str("function f(){}; njs.dump(Object.getOwnPropertyDescriptor(f, 'name'))"), njs_str("{value:'f',writable:false,enumerable:false,configurable:true}") }, { njs_str("function f(){}; Object.defineProperty(f, 'name', {value: 'F'}); f.name"), njs_str("F") }, { njs_str("function f(){}; Object.defineProperty(f, 'name', {value: 'F'});" "njs.dump(Object.getOwnPropertyDescriptor(f, 'name'))"), njs_str("{value:'F',writable:false,enumerable:false,configurable:true}") }, { njs_str("function f() {}; f.name = 'a'"), njs_str("TypeError: Cannot assign to read-only property \"name\" of function") }, { njs_str("(function f () { return f.name})()"), njs_str("f") }, { njs_str("var a = function () {}; a.name"), njs_str("a") }, { njs_str("(function () {}).name"), njs_str("") }, { njs_str("var a = (null, function () {}); a.name"), njs_str("") }, { njs_str("var a = async function () {}; a.name"), njs_str("a") }, { njs_str("let a = () => {}; a.name"), njs_str("a") }, { njs_str("let a = async () => {}; a.name"), njs_str("a") }, { njs_str("Function().name"), njs_str("anonymous") }, { njs_str("var o = {f: function (){}, g: () => {}, h: async function(){}};" "[o.f.name, o.g.name, o.h.name]"), njs_str("f,g,h") }, { njs_str("({t(){}}).t.name"), njs_str("t") }, /* Function nesting depth. */ { njs_str("() => () => () => () => () => () => () => () => () => () => () =>" "() => () => () => () => () => () => () => () => () => () => () =>" "() => () => () => () => () => () => () => () => () => () => () =>" "() => () => () => () => () => () => () => () => () => () => () =>" "() => () => () => () => () => () => () => () => () => () => () =>" "() => () => () => () => () => () => () => () => () => () => () =>" "() => () => () => () => () => () => () => () => () => () => () =>" "() => () => () => () => () => () => () => () => () => () => () =>" "() => () => () => () => () => () => () => () => () => () => () =>" "() => () => () => () => () => () => () => () => () => () => () =>" "() => () => () => () => () => () => () => () => () => () => () =>" "() => () => () => () => () => () => 1"), njs_str("[object Function]") }, { njs_str("() => () => () => () => () => () => () => () => () => () => () =>" "() => () => () => () => () => () => () => () => () => () => () =>" "() => () => () => () => () => () => () => () => () => () => () =>" "() => () => () => () => () => () => () => () => () => () => () =>" "() => () => () => () => () => () => () => () => () => () => () =>" "() => () => () => () => () => () => () => () => () => () => () =>" "() => () => () => () => () => () => () => () => () => () => () =>" "() => () => () => () => () => () => () => () => () => () => () =>" "() => () => () => () => () => () => () => () => () => () => () =>" "() => () => () => () => () => () => () => () => () => () => () =>" "() => () => () => () => () => () => () => () => () => () => () =>" "() => () => () => () => () => () => () => 1"), njs_str("RangeError: Maximum function nesting depth exceeded") }, /* Recursive factorial. */ { njs_str("function f(a) {" " if (a > 1)" " return a * f(a - 1)\n" " return 1" "}" "f(10)"), njs_str("3628800") }, /* Recursive factorial. */ { njs_str("function f(a) { return (a > 1) ? a * f(a - 1) : 1 } f(10)"), njs_str("3628800") }, { njs_str("var g = function f(a) { return (a > 1) ? a * f(a - 1) : 1 };" "g(10)"), njs_str("3628800") }, { njs_str("(function f(a) { return (a > 1) ? a * f(a - 1) : 1 })(10)"), njs_str("3628800") }, /* Nested functions and closures. */ { njs_str("function f() { var x = 4; " "function g() { return x }; return g(); } f()"), njs_str("4") }, { njs_str("function f(a) { function g(b) { return a + b } return g }" "var k = f('a'); k('b')"), njs_str("ab") }, { njs_str("function f(a) { return function(b) { return a + b } }" "var k = f('a'); k('b')"), njs_str("ab") }, { njs_str("function f(a) { return function(b) { return a + b } }" "var k = f('a'), m = f('b'); k('c') + m('d')"), njs_str("acbd") }, { njs_str("function f(a) { return " "function(b) { return function(c) { return a + b + c } } }" "var g = f('a'), k = g('b'), m = g('c'); k('d') + m('e')"), njs_str("abdace") }, { njs_str("function f(a) {" "function g() { return a }; return g; }" "var y = f(4); y()"), njs_str("4") }, { njs_str("function f() { var x = 4; " "return function() { return x } }" "var y = f(); y()"), njs_str("4") }, { njs_str("function f() { var x = 4;" "function g() { if (1) { return 2 + x; } }; return g }" "var y = f(); y()"), njs_str("6") }, { njs_str("var x; var y = 4;" "function f() { function h() { x = 3; return y; } }"), njs_str("undefined") }, { njs_str("function f() {" " var a = 'a';" " if (0) { a = 'b' };" " function f2() { return a };" " return f2" "};" "f()()"), njs_str("a") }, { njs_str("function f() {" " var a = 'a'; " " if (0) { if (0) {a = 'b'} };" " function f2() { return a };" " return f2" "};" "f()()"), njs_str("a") }, { njs_str("function f() { var a = f2(); }"), njs_str("undefined") }, { njs_str("function f() { var a = f2(); } f();"), njs_str("ReferenceError: \"f2\" is not defined") }, { njs_str("typeof Buffer !== 'undefined' ? Buffer : function Buffer(){}"), njs_str("[object Function]") }, { njs_str("1 == 2 ? func() : '123'"), njs_str("123") }, { njs_str("1 == 1 ? func() : '123'"), njs_str("ReferenceError: \"func\" is not defined") }, { njs_str("function f(){ if (1 == 1) { 1 == 2 ? some_var : '123' } }; f()"), njs_str("undefined") }, { njs_str("function f(){ if (1 == 1) { 1 == 1 ? some_var : '123' } }; f()"), njs_str("ReferenceError: \"some_var\" is not defined") }, { njs_str("function f(){ if (1 == 1) { 1 == 2 ? some_func() : '123' } }; f()"), njs_str("undefined") }, { njs_str("function f(){ if (1 == 1) { 1 == 1 ? some_func() : '123' } }; f()"), njs_str("ReferenceError: \"some_func\" is not defined") }, { njs_str("(function(){ function f() {return f}; return f()})()"), njs_str("[object Function]") }, { njs_str("function f() {}; f.toString = ()=> 'F'; ({'F':1})[f]"), njs_str("1") }, { njs_str("var a = ''; " "function f(list) {" " function add(v) {a+=v};" " list.forEach(function(v) {add(v)});" "};" "f(['a', 'b', 'c']); a"), njs_str("abc") }, { njs_str("var l = [];" "var f = function() { " " function f2() { l.push(f); l.push(f2); }; " " l.push(f); l.push(f2); " " f2(); " "}; " "f(); " "l.every(function(v) {return typeof v == 'function'})"), njs_str("true") }, { njs_str("var l = [];" "function baz() {" " function foo(v) {" " function bar() { foo(0); }" " l.push(v);" " if (v === 1) { bar(); }" " }" " foo(1);" "}; baz(); l"), njs_str("1,0") }, { njs_str("var gen = (function(){ " " var s = 0; " " return { inc: function() {s++}, " " s: function() {return s} };});" "var o1 = gen(); var o2 = gen();" "[o1.s(),o2.s(),o1.inc(),o1.s(),o2.s(),o2.inc(),o1.s(),o2.s()]"), njs_str("0,0,,1,0,,1,1") }, /* Recursive fibonacci. */ { njs_str("function fibo(n) {" " if (n > 1)" " return fibo(n-1) + fibo(n-2)\n" " return 1" "}" "fibo(10)"), njs_str("89") }, { njs_str("function fibo(n) {" " if (n > 1)" " return fibo(n-1) + fibo(n-2)\n" " return '.'" "}" "fibo(10).length"), njs_str("89") }, { njs_str("function fibo(n) {" " if (n > 1)" " return fibo(n-1) + fibo(n-2)\n" " return 1" "}" "fibo('10')"), njs_str("89") }, { njs_str("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)"), njs_str("115") }, { njs_str("function a(x, y) { return x + y }" "function b(x, y) { return x * y }" "a(3, b(4, 5))"), njs_str("23") }, { njs_str("function x(n) { return n }; x('12'.substr(1))"), njs_str("2") }, { njs_str("function f(a) { a *= 2 } f(10)"), njs_str("undefined") }, { njs_str("function f() { return 5 } f()"), njs_str("5") }, { njs_str("function g(x) { return x + 1 }" "function f(x) { return x } f(g)(2)"), njs_str("3") }, { njs_str("function f() { return 5 } f(1)"), njs_str("5") }, { njs_str("function f() {} f()"), njs_str("undefined") }, { njs_str("function f() {;} f()"), njs_str("undefined") }, { njs_str("function f(){return} f()"), njs_str("undefined") }, { njs_str("function f(){return;} f()"), njs_str("undefined") }, { njs_str("function f(){return\n1} f()"), njs_str("undefined") }, { njs_str("function f(a) { return a + 1 } var b = f(2); b"), njs_str("3") }, { njs_str("var f = function(a) { a *= 2; return a }; f(10)"), njs_str("20") }, { njs_str("var f = function b(a) { a *= 2; return a }; f(10)"), njs_str("20") }, { njs_str("var f = function b(a) { a *= 2; return a }; b(10)"), njs_str("ReferenceError: \"b\" is not defined") }, { njs_str("var f; f = function(a) { a *= 2; return a }; f(10)"), njs_str("20") }, { njs_str("var f; f = function b(a) { a *= 2; return a }; f(10)"), njs_str("20") }, { njs_str("var a, f = a = function(a) { a *= 2; return a }; f(10)"), njs_str("20") }, { njs_str("var a, f = a = function(a) { a *= 2; return a }; a(10)"), njs_str("20") }, { njs_str("var f = function b(a) { a *= 2; return a } = 5"), njs_str("ReferenceError: Invalid left-hand side in assignment in 1") }, { njs_str("function a() { return { x:2} }; var b = a(); b.x"), njs_str("2") }, { njs_str("var a = {}; function f(a) { return a + 1 } a.b = f(2); a.b"), njs_str("3") }, { njs_str("(function(x) { return x + 1 })(2)"), njs_str("3") }, { njs_str("(function(x) { return x + 1 }(2))"), njs_str("3") }, { njs_str("var a = function() { return 1 }(); a"), njs_str("1") }, { njs_str("var a = (function() { return 1 })(); a"), njs_str("1") }, { njs_str("var a = (function(a) { return a + 1 })(2); a"), njs_str("3") }, { njs_str("var a = (function(a) { return a + 1 }(2)); a"), njs_str("3") }, { njs_str("var a = +function(a) { return a + 1 }(2); a"), njs_str("3") }, { njs_str("var a = -function(a) { return a + 1 }(2); a"), njs_str("-3") }, { njs_str("var a = !function(a) { return a + 1 }(2); a"), njs_str("false") }, { njs_str("var a = ~function(a) { return a + 1 }(2); a"), njs_str("-4") }, { njs_str("var a = void function(a) { return a + 1 }(2); a"), njs_str("undefined") }, { njs_str("var a = true && function(a) { return a + 1 }(2); a"), njs_str("3") }, { njs_str("var a; a = 0, function(a) { return a + 1 }(2); a"), njs_str("0") }, { njs_str("var a = (0, function(a) { return a + 1 }(2)); a"), njs_str("3") }, { njs_str("var a = 0, function(a) { return a + 1 }(2); a"), njs_str("SyntaxError: Unexpected token \"function\" in 1") }, { njs_str("var a = (0, function(a) { return a + 1 }(2)); a"), njs_str("3") }, { njs_str("var a = +function f(a) { return a + 1 }(2);" "var b = f(5); a"), njs_str("ReferenceError: \"f\" is not defined") }, { njs_str("var o = { f: function(a) { return a * 2 } }; o.f(5)"), njs_str("10") }, { njs_str("var o = {}; o.f = function(a) { return a * 2 }; o.f(5)"), njs_str("10") }, { njs_str("var o = { x: 1, f: function() { return this.x } }; o.f()"), njs_str("1") }, { njs_str("var o = { x: 1, f: function(a) { return this.x += a } };" "o.f(5) +' '+ o.x"), njs_str("6 6") }, { njs_str("var f = function(a) { return 3 }; f.call()"), njs_str("3") }, { njs_str("var f = function(a) { return this }; f.call(5)"), njs_str("5") }, { njs_str("var f = function(a, b) { return this + a }; f.call(5, 1)"), njs_str("6") }, { njs_str("var f = function(a, b) { return this + a + b };" "f.call(5, 1, 2)"), njs_str("8") }, { njs_str("var f = function(a) { return 3 }; f.apply()"), njs_str("3") }, { njs_str("var f = function(a) { return this }; f.apply(5)"), njs_str("5") }, { njs_str("var f = function(a) { return this + a }; f.apply(5, 1)"), njs_str("TypeError: second argument is not an array-like object") }, { njs_str("var f = function(a, b) { return this + a + b };" "f.apply(5, [1, 2])"), njs_str("8") }, { njs_str("var f = function(a, b) { return this + a + b };" "f.apply(5, [1, 2], 3)"), njs_str("8") }, { njs_str("var f = function(a, b) { return this + a + b };" "f.apply(5, {'length':2, '0':1, '1':2})"), njs_str("8") }, { njs_str("var f = function(a, b) { return this + a + b };" "f.apply(5, {'length':2, '0':1, '1':2, '2':3})"), njs_str("8") }, { njs_str("var f = function(a, b, c) { return this + a + b + c};" "f.apply(\"a\", {'length':2, '0':1, '1':2, '2':3})"), njs_str("a12undefined") }, { njs_str("var f = function(a, b) { return this + a + b };" "f.apply(5, {'length':3, '0':1, '1':2, '2':3})"), njs_str("8") }, { njs_str("var f = function(a) { return this + a };" "f.apply(5, {'nolength':3, '0':1, '1':2})"), njs_str("NaN") }, { njs_str("var f = function(a) { return this };" "f.apply(5, {'nolength':3, '0':1, '1':2})"), njs_str("5") }, { njs_str("var f = function(a, b, c) { return this + a + b + c };" "f.apply(\"a\", {'length':3, '0':1, '1':2})"), njs_str("a12undefined") }, { njs_str("var f = function(a, b) { return this + a + b };" "f.apply(\"a\", {'length':2, '0':undefined, '1':null})"), njs_str("aundefinednull") }, { njs_str("var f = function() { return this };" "f.apply(123, {})"), njs_str("123") }, { njs_str("(function(index, ...rest){ return rest[index];})" ".apply({}, [1022].concat(Array(1023).fill(1).map((v,i)=>i.toString(16))))"), njs_str("3fe") }, { njs_str("String.prototype.concat.apply('a', " "{length:2, 0:{toString:function() {return 'b'}}, 1:'c'})"), njs_str("abc") }, { njs_str("String.prototype.concat.apply('a'," "{length: {valueOf: () => 2}, 0:'b', 1:'c'})"), njs_str("abc") }, { njs_str("var o = {0:'b', 1:'c'}; Object.defineProperty(o, 'length', {get: () => 2});" "String.prototype.concat.apply('a', o)"), njs_str("abc") }, { njs_str("var a = function() { return 1 } + ''; a"), njs_str("[object Function]") }, { njs_str("''.concat.call()"), njs_str("TypeError: \"this\" argument is null or undefined") }, { njs_str("''.concat.call('a', 'b', 'c')"), njs_str("abc") }, { njs_str("''.concat.call('a')"), njs_str("a") }, { njs_str("''.concat.call('a', [ 'b', 'c' ])"), njs_str("ab,c") }, { njs_str("''.concat.call('a', [ 'b', 'c' ], 'd')"), njs_str("ab,cd") }, { njs_str("''.concat.apply()"), njs_str("TypeError: \"this\" argument is null or undefined") }, { njs_str("''.concat.apply('a')"), njs_str("a") }, { njs_str("''.concat.apply('a', 'b')"), njs_str("TypeError: second argument is not an array-like object") }, { njs_str("''.concat.apply('a', [ 'b', 'c' ])"), njs_str("abc") }, { njs_str("''.concat.apply('a', [ 'b', 'c' ], 'd')"), njs_str("abc") }, { njs_str("[].join.call([1,2,3])"), njs_str("1,2,3") }, { njs_str("[].join.call([1,2,3], ':')"), njs_str("1:2:3") }, { njs_str("[].join.call([1,2,3], 55)"), njs_str("1552553") }, { njs_str("[].join.call()"), njs_str("TypeError: cannot convert null or undefined to object") }, { njs_str("[1,2,3].join(undefined)"), njs_str("1,2,3") }, { njs_str("Array.prototype[1] = 1; var x = [0]; x.length = 2; x.join()"), njs_str("0,1") }, { njs_str("Object.prototype[1] = 1; Object.prototype.length = 2; Array.prototype.join.call({0:0})"), njs_str("0,1") }, { njs_str("var x = [0,,4]; x.length = 3; " "Object.defineProperty(Array.prototype, 1, " "{get:()=>{Object.defineProperty(x, 2, {value:'x', enumerable:false}); return 1}});" "x.join()"), njs_str("0,1,x") }, { njs_str("[].slice.call()"), njs_str("TypeError: cannot convert null or undefined to object") }, { njs_str("function f(a) {} ; var a = f; var b = f; a === b"), njs_str("true") }, { njs_str("function f() {} ; f.toString()"), njs_str("[object Function]") }, { njs_str("function f() {}; f"), njs_str("[object Function]") }, { njs_str("function f() {}; f = f + 1; f"), njs_str("[object Function]1") }, { njs_str("function a() { return 1 }" "function b() { return a }" "function c() { return b }" "c()()()"), njs_str("1") }, { njs_str("function f() {}; f += 1; f"), njs_str("[object Function]1") }, { njs_str("function f() { function g() { g = undefined; }; g(); g(); };" "f()"), njs_str("TypeError: undefined is not a function") }, { njs_str("function f() {}; function g() { return f }; g()"), njs_str("[object Function]") }, { njs_str("function f(a) { return this+a }; var a = f; a.call('0', 1)"), njs_str("01") }, { njs_str("function f(a) { return this+a }; f.call('0', 1)"), njs_str("01") }, { njs_str("function f(a) { return this+a };" "function g(f, a, b) { return f.call(a, b) }; g(f, '0', 1)"), njs_str("01") }, { njs_str("function f(a) { return this+a };" "var o = { g: function (f, a, b) { return f.call(a, b) } };" "o.g(f, '0', 1)"), njs_str("01") }, { njs_str("var concat = ''.concat; concat(1,2,3)"), njs_str("TypeError: \"this\" argument is null or undefined") }, { njs_str("var concat = ''.concat; concat.call(1,2,3)"), njs_str("123") }, { njs_str("var concat = ''.concat; concat.yes = 'OK';" "concat.call(1,2,3, concat.yes)"), njs_str("123OK") }, { njs_str("var f = function(a, b) { return this + a + b };" "var b = f.bind('1'); b('2', '3')"), njs_str("123") }, { njs_str("var f = function(a, b) { return this + a + b };" "var b = f.bind('1', '2'); b('3')"), njs_str("123") }, { njs_str("var f = function(a, b) { return this + a + b };" "var b = f.bind('1', 2, '3'); b()"), njs_str("123") }, { njs_str("var f = function(a, b) { return this + a + b };" "var b = f.bind('1'); b.call('0', '2', '3')"), njs_str("123") }, { njs_str("var f = function(a, b) { return this + a + b };" "var b = f.bind('1', '2'); b.call('0', '3')"), njs_str("123") }, { njs_str("var f = function(a, b) { return this + a + b };" "var b = f.bind('1', '2', '3'); b.call('0')"), njs_str("123") }, { njs_str("var f = function(a, b) { return this + a + b };" "var b = f.bind('1', '2', '3'); b.call()"), njs_str("123") }, { njs_str("var f = function(a, b) { return this + a + b };" "var b = f.bind('1'); b.apply('0', ['2', '3'])"), njs_str("123") }, { njs_str("var f = function(a, b) { return this + a + b };" "var b = f.bind('1', '2'); b.apply('0', ['3'])"), njs_str("123") }, { njs_str("var f = function(a, b) { return this + a + b };" "var b = f.bind('1', '2', '3'); b.apply('0')"), njs_str("123") }, { njs_str("var f = function(a, b) { return this + a + b };" "var b = f.bind('1', '2', '3'); b.apply()"), njs_str("123") }, { njs_str("function f(a, b) { return this + a + b }" "var b = f.bind('1', '2', '3'); b.apply()"), njs_str("123") }, { njs_str("function f() { var a; return (function() { a = 1; return a; }).bind()() } f()"), njs_str("1") }, { njs_str("function f() { var a; function baz() { a = 1; return a; } return baz.bind()(); } f()"), njs_str("1") }, { njs_str("(function(){ var a = 1; return (function() { return a; })})().bind()()"), njs_str("1") }, { njs_str("var r = (function(){ var a = 1; return (function() { return {a,args:arguments}; })})().bind()('b');" "njs.dump(r)"), njs_str("{a:1,args:{0:'b'}}") }, { njs_str("function f() { var a = 1; function baz() { return a; } return baz; } f().bind()()"), njs_str("1") }, { njs_str("function f() { var t = 1; function baz() { return t; } return baz; }" "f().bind()();"), njs_str("1") }, { njs_str("(function(a) { var s = typeof g, q = g; var g = 1; s += typeof g; function g(b) { return a + b }; return q; })(1)(2)"), njs_str("3")}, { njs_str("(function(a) { var g = f; var f = 1; function f() { return a; } return g; })(42)()"), njs_str("42") }, { njs_str("function f(a, b) { return a + b }" "f(3,4) === f.bind()(3,4)"), njs_str("true") }, { njs_str("var obj = {prop:'abc'}; " "var func = function(x) { " " return this === obj && x === 1 && arguments[0] === 1 " " && arguments.length === 1 && this.prop === 'abc';" "};" "Function.prototype.bind.call(func, obj, 1)()"), njs_str("true") }, { njs_str("function F(a, b) { this.a = a + b }" "var o = new F(1, 2);" "o.a"), njs_str("3") }, { njs_str("function F(a, b) { this.a = a + b; return { a: 7 } }" "var o = new F(1, 2);" "o.a"), njs_str("7") }, { njs_str("function F(a, b) { return }" "F.prototype.constructor === F"), njs_str("true") }, { njs_str("function F() { return }" "F.prototype.ok = 'OK';" "var o = new F(); o.ok"), njs_str("OK") }, { njs_str("function F() { return }" "var o = new F();" "o.constructor === F"), njs_str("true") }, { njs_str("function F() { return }" "var o = new F();" "o.__proto__ === F.prototype"), njs_str("true") }, { njs_str("var f = { F: function(){} }; var o = new f.F();" "o.__proto__ === f.F.prototype"), njs_str("true") }, { njs_str("function F(){}; typeof F.prototype"), njs_str("object") }, { njs_str("var F = function (){}; typeof F.prototype"), njs_str("object") }, { njs_str("var F = function (){}; F.prototype = NaN; ({}) instanceof F"), njs_str("TypeError: Function has non-object prototype in instanceof") }, { njs_str("var F = function() {};" "[F, F].map((x)=>Object.getOwnPropertyDescriptor(x, 'prototype').writable)" ".every((x)=> x === true)"), njs_str("true") }, { njs_str("var F = function() {}, a = {t: 1}, b = {t: 2}, x, y; " "F.prototype = a; x = new F();" "F.prototype = b; y = new F();" "x.t == 1 && y.t == 2"), njs_str("true") }, { njs_str("function A(){}; A.tag = 'A'; var a = new A();" "(function B(){}).prototype = A.prototype;" "a.constructor.tag"), njs_str("A") }, { njs_str("function A(){}; A.tag = 'A'; var a = new A();" "(function B(){}).prototype = a.constructor.prototype;" "a.constructor.tag"), njs_str("A") }, { njs_str("var x = {}, y = function() {}, z; y.prototype = x; z = new y();" "(z instanceof y) && (z.__proto__ == y.prototype) && (x.isPrototypeOf(z))"), njs_str("true") }, { njs_str("var x = {}, y = function() {}, z; y.prototype = x; z = new y();" "(z instanceof y) && (z.__proto__ == y.prototype) && (x.isPrototypeOf(z))"), njs_str("true") }, { njs_str("[undefined, null, false, NaN, '']" ".map((x) => { var f = function() {}; f.prototype = x; " " return Object.getPrototypeOf(new f()); })" ".every((x) => x == Object.prototype)"), njs_str("true") }, { njs_str("[undefined, null, false, NaN, '']" ".map((x) => { var f = function() {}; f.prototype = x; return f; })" ".map((x) => { try { return ({} instanceof x) ? 1 : 2; } " " catch (e) { return (e instanceof TypeError) ? 3 : 4; } })" ".every((x) => x == 3)"), njs_str("true")}, { njs_str("new decodeURI('%00')"), njs_str("TypeError: function is not a constructor")}, { njs_str("new ''.toString"), njs_str("TypeError: function is not a constructor")}, { njs_str("function F() { return Number }" "var o = new (F())(5);" "typeof o +' '+ o"), njs_str("object 5") }, { njs_str("function F() { return Number }" "var o = new (F());" "typeof o +' '+ o"), njs_str("object 0") }, { njs_str("var o = new function F() { return Number }()(5);" "typeof o +' '+ o"), njs_str("number 5") }, { njs_str("var o = new (function F() { return Number }())(5);" "typeof o +' '+ o"), njs_str("object 5") }, { njs_str("var o = new (new function F() { return Number }())(5);" "typeof o +' '+ o"), njs_str("object 5") }, { njs_str("var o = new new function F() { return Number }()(5);" "typeof o +' '+ o"), njs_str("object 5") }, { njs_str("var b; function F(x) { return {a:x} }" "function G(y) { b = y; return F }" "var o = new G(3)(5);" "b + ' ' + o.a"), njs_str("3 5") }, { njs_str("var b; function F(x) { return {a:x} }" "function G(y) { b = y; return F }" "var o = new (new G(3))(5);" "b + ' ' + o.a"), njs_str("3 5") }, { njs_str("var b; function F(x) { return {a:x} }" "function G(y) { b = y; return F }" "var o = new new G(3)(5);" "b + ' ' + o.a"), njs_str("3 5") }, { njs_str("var b; function F(x) { return {a:x} }" "var g = { G: function (y) { b = y; return F } };" "var o = new new g.G(3)(5);" "b + ' ' + o.a"), njs_str("3 5") }, { njs_str("function a() { return function(x) { return x + 1 } }" "var b = a(); b(2)"), njs_str("3") }, /* arguments object. */ { njs_str("arguments"), njs_str("SyntaxError: \"arguments\" object in global scope in 1") }, { njs_str("{arguments}"), njs_str("SyntaxError: \"arguments\" object in global scope in 1") }, { njs_str("var arguments"), njs_str("SyntaxError: Identifier \"arguments\" is forbidden in var declaration in 1") }, { njs_str("for (var arguments in []) {}"), njs_str("SyntaxError: Identifier \"arguments\" is forbidden in var declaration in 1") }, { njs_str("function arguments(){}"), njs_str("SyntaxError: Identifier \"arguments\" is forbidden in function declaration in 1") }, { njs_str("(function () {arguments = [];})"), njs_str("SyntaxError: Identifier \"arguments\" is forbidden as left-hand in assignment in 1") }, { njs_str("(function(){return arguments;})()"), njs_str("[object Arguments]") }, { njs_str("(function(){return arguments[0];})(1,2,3)"), njs_str("1") }, { njs_str("(function(){return arguments[2];})(1,2,3)"), njs_str("3") }, { njs_str("(function(){return arguments[3];})(1,2,3)"), njs_str("undefined") }, { njs_str("(function(a,b,c){return a;})(1,2,3)"), njs_str("1") }, { njs_str("(function(a,b,c){arguments[0] = 4; return a;})(1,2,3)"), njs_str("1") }, { njs_str("(function(a,b,c){a = 4; return arguments[0];})(1,2,3)"), njs_str("1") }, { njs_str("function check(v) {if (v == false) {throw TypeError('Too few arguments')}}; " "function f() {check(arguments.length > 1); return 1}; f()"), njs_str("TypeError: Too few arguments") }, { njs_str("function check(v) {if (v == false) {throw TypeError('Too few arguments')}}; " "function f() {check(arguments.length > 1); return 1}; f(1,2)"), njs_str("1") }, { njs_str("(function(a,b){delete arguments[0]; return arguments[0]})(1,1)"), njs_str("undefined") }, { njs_str("(function(){return arguments.length;})()"), njs_str("0") }, { njs_str("(function(){return arguments.length;})(1,2,3)"), njs_str("3") }, { njs_str("(function(){arguments.length = 1; return arguments.length;})(1,2,3)"), njs_str("1") }, { njs_str("(function(){return arguments[3];}).bind(null, 0)('a','b','c')"), njs_str("c") }, { njs_str("function sum() { var args = Array.prototype.slice.call(arguments); " "return args.reduce(function(prev, curr) {return prev + curr})};" "[sum(1), sum(1,2), sum(1,2,3), sum(1,2,3,4)]"), njs_str("1,3,6,10") }, { njs_str("function concat(sep) { var args = Array.prototype.slice.call(arguments, 1); " "return args.join(sep)};" "[concat('.',1,2,3), concat('+',1,2,3,4)]"), njs_str("1.2.3,1+2+3+4") }, /* strict mode restrictions */ { njs_str("(function() {}).caller"), njs_str("TypeError: \"caller\", \"callee\", \"arguments\" properties may not be accessed") }, { njs_str("(function() {}).arguments"), njs_str("TypeError: \"caller\", \"callee\", \"arguments\" properties may not be accessed") }, { njs_str("var desc = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(Math.min), 'caller');" "desc.get === desc.set"), njs_str("true") }, { njs_str("var desc = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(Math.min), 'caller');" "1/desc.get"), njs_str("NaN") }, { njs_str("var p = Object.getPrototypeOf(function() {});" "var d = Object.getOwnPropertyDescriptor(p, 'caller');" "typeof d.get == 'function' && typeof d.get == typeof d.set" " && d.configurable && !d.enumerable"), njs_str("true") }, { njs_str("var p = Object.getPrototypeOf(function() {});" "var d = Object.getOwnPropertyDescriptor(p, 'arguments');" "typeof d.get == 'function' && typeof d.get == typeof d.set" " && d.configurable && !d.enumerable"), njs_str("true") }, { njs_str("(function(){return arguments.callee;})()"), njs_str("TypeError: \"caller\", \"callee\", \"arguments\" properties may not be accessed") }, { njs_str("var f = function() { return arguments; };" "Object.getOwnPropertyDescriptor(f(), 'caller')"), njs_str("undefined") }, { njs_str("var f = function() { return arguments; };" "var d = Object.getOwnPropertyDescriptor(f(), 'callee');" "typeof d.get == 'function' && typeof d.get == typeof d.set" " && !d.configurable && !d.enumerable"), njs_str("true") }, /* rest parameters. */ { njs_str("function myFoo(a,b,...other) { return other };" "myFoo(1,2,3,4,5);" ), njs_str("3,4,5") }, { njs_str("function myFoo(a,b,...other, c) { return other };"), njs_str("SyntaxError: Rest parameter must be last formal parameter in 1") }, { njs_str("function sum(a, b, c, ...other) { return a+b+c+other[2] };" "sum(\"one \",2,\" three \",\"four \",\"five \",\"the long enough sixth argument \");"), njs_str("one 2 three the long enough sixth argument ") }, { njs_str("function myFoo1(a,...other) { return other };" "function myFoo2(a,b,...other) { return other };" "myFoo1(1,2,3,4,5,myFoo2(1,2,3,4));"), njs_str("2,3,4,5,3,4") }, { njs_str("function myFoo(...other) { return (other instanceof Array) };" "myFoo(1);" ), njs_str("true") }, { njs_str("function myFoo(a,...other) { return other.length };" "myFoo(1,2,3,4,5);" ), njs_str("4") }, { njs_str("function myFoo(a,b,...other) { return other };" "myFoo(1,2);" ), njs_str("") }, { njs_str("function f(...rest) {};" "function f(a, b) {return a + b};" "f(1,2)"), njs_str("3") }, { njs_str("function f() { function q() {} };" "function f() { };" "f()"), njs_str("undefined") }, /* arrow functions. */ { njs_str("()"), njs_str("SyntaxError: Unexpected token \")\" in 1") }, { njs_str("() => "), njs_str("SyntaxError: Unexpected end of input in 1") }, { njs_str("() => {"), njs_str("SyntaxError: Unexpected end of input in 1") }, { njs_str("a\n => 1"), njs_str("SyntaxError: Unexpected token \"=>\" in 2") }, { njs_str("new (()=>1)"), njs_str("TypeError: function is not a constructor")}, { njs_str("(\n) => {}"), njs_str("[object Function]") }, { njs_str("a => 1"), njs_str("[object Function]") }, { njs_str("({f:()=>1, g:()=>2}).f()"), njs_str("1") }, { njs_str("var f = f => {return 1;}; f()"), njs_str("1") }, { njs_str("var f = (f) => {return 1;}; f()"), njs_str("1") }, { njs_str("var f = (f, a, b) => {return 1;}; f()"), njs_str("1") }, { njs_str("var f = () => {return 1;}; f()"), njs_str("1") }, { njs_str("(f => {return 1;})()"), njs_str("1") }, { njs_str("((f) => {return 1;})()"), njs_str("1") }, { njs_str("(((f) => {return 1;}))()"), njs_str("1") }, { njs_str("var f = f => 1; f()"), njs_str("1") }, { njs_str("() => 1"), njs_str("[object Function]") }, { njs_str("var f = ()=>{}; f()"), njs_str("undefined") }, { njs_str("var f = ()=>({}); f()"), njs_str("[object Object]") }, { njs_str("var materials = ['Hydrogen', 'Helium', 'Lithium', 'Beryllium'];" "materials.map(material => { return material.length; });"), njs_str("8,6,7,9") }, { njs_str("var materials = ['Hydrogen', 'Helium', 'Lithium', 'Beryllium'];" "materials.map(material => material.length);"), njs_str("8,6,7,9") }, { njs_str("var materials = ['Hydrogen', 'Helium', 'Lithium', 'Beryllium'];" "materials.map(material => { material.length });"), njs_str(",,,") }, { njs_str("function f(a, b, c) {a = 1; return () => { return arguments[1]; };};" "f(1, 2, 3)('a', 'b');"), njs_str("2") }, { njs_str("var f = (...c) => { return (function() { return arguments.length; }).bind(null, c); };" "var x = f(1,'a',false, {}); x()"), njs_str("1") }, { njs_str("var f = (...c) => { return (function() { return arguments.length; }).bind(null, c); };" "var x = f(1,'a',false, {}); x(1,2,3)"), njs_str("4") }, { njs_str("function Car(){ this.age = 0; (() => { this.age++;})();}" "(new Car()).age"), njs_str("1") }, { njs_str("function Car(){ this.age = 0; (function(){ this.age++;})();}" "(new Car()).age"), njs_str("TypeError: cannot get property \"age\" of undefined") }, /* arrow functions + global this. */ { njs_str("(() => this)()"), njs_str("[object global]") }, { njs_str("(() => this).call('abc')"), njs_str("[object global]") }, { njs_str("(() => this).apply('abc')"), njs_str("[object global]") }, { njs_str("(() => this).bind('abc')()"), njs_str("[object global]") }, { njs_str("(function() { return (() => this); })()()"), njs_str("undefined") }, { njs_str("(function() { return (() => this); }).call('abc')()"), njs_str("abc") }, { njs_str("(function() { return (() => this); }).bind('abc')()()"), njs_str("abc") }, { njs_str("(function() { return (() => this); })" ".call('abc').call('bca')"), njs_str("abc") }, { njs_str("(function() { return (() => this); })" ".call('abc').bind('bca')()"), njs_str("abc") }, { njs_str("(function() { return function() { return () => this; }; })" ".call('bca').call('abc')()"), njs_str("abc") }, { njs_str("var f = () => 1; f.prototype"), njs_str("undefined") }, { njs_str("var f = (a,b) => 0; f.length"), njs_str("2") }, { njs_str("var o = Object.create(f => 1); o.length = 3"), njs_str("TypeError: Cannot assign to read-only property \"length\" of object") }, /* Scopes. */ { njs_str("function f(x) { a = x } var a; f(5); a"), njs_str("5") }, { njs_str("function f(x) { var a = x } var a = 2; f(5); a"), njs_str("2") }, { njs_str("function f(a) { return a } var a = '2'; a + f(5)"), njs_str("25") }, { njs_str("for (var i = 0; i < 5; i++); i"), njs_str("5") }, { njs_str("for (var i = 0, j; i < 5; i++); j"), njs_str("undefined") }, { njs_str("for (var i = 0, j, k = 3; i < 5; i++); k"), njs_str("3") }, { njs_str("var o = { a: 1, b: 2, c: 3 }, s = ''; " "for (var i in o) { s += i }; s"), njs_str("abc") }, { njs_str("var o = { a: 1, b: 2, c: 3 }; for (var i in o); i"), njs_str("c") }, { njs_str("var o = {}; i = 7; for (var i in o); i"), njs_str("7") }, { njs_str("var a = [1,2,3]; for (var i in a); i"), njs_str("2") }, /* RegExp. */ { njs_str("/"), njs_str("SyntaxError: Unterminated RegExp \"/\" in 1") }, { njs_str("/a\n/"), njs_str("SyntaxError: Unterminated RegExp \"/a\" in 1") }, { njs_str("/a\r/"), njs_str("SyntaxError: Unterminated RegExp \"/a\" in 1") }, #ifndef NJS_HAVE_PCRE2 { njs_str("/a\\q/"), njs_str("/a\\q/") }, #endif { njs_str("/\\\\/"), njs_str("/\\\\/") }, { njs_str("/\\\\\\/"), njs_str("SyntaxError: Unterminated RegExp \"/\\\\\\/\" in 1") }, { njs_str("/\\\\\\\\/"), njs_str("/\\\\\\\\/") }, { njs_str("/\\\\\\//"), njs_str("/\\\\\\//") }, { njs_str("/[A-Z/]/"), njs_str("/[A-Z/]/") }, { njs_str("/a/gim"), njs_str("/a/gim") }, { njs_str("/a/y"), njs_str("/a/y") }, { njs_str("/[A-Z\n]/"), njs_str("SyntaxError: Unterminated RegExp \"/[A-Z\" in 1") }, { njs_str("/[A-Z\\\n]/"), njs_str("SyntaxError: Unterminated RegExp \"/[A-Z\\\" in 1") }, { njs_str("/\\\n/"), njs_str("SyntaxError: Unterminated RegExp \"/\\\" in 1") }, { njs_str("/./x"), njs_str("SyntaxError: Invalid RegExp flags \"x\" in 1") }, { njs_str("/./.exec === RegExp.prototype.exec"), njs_str("true") }, { njs_str("/./['exec'] === RegExp.prototype.exec"), njs_str("true") }, { njs_str("/./[Symbol.replace] === RegExp.prototype[Symbol.replace]"), njs_str("true") }, { njs_str("/^[A-Za-z0-9+/]{4}$/.test('////')"), njs_str("true") }, { njs_str("'[]!\"#$%&\\'()*+,.\\/:;<=>?@\\^_`{|}-'.split('')" ".every(ch=>/[\\]\\[!\"#$%&'()*+,.\\/:;<=>?@\\^_`{|}-]/.test(ch))"), njs_str("true") }, #ifndef NJS_HAVE_PCRE2 { njs_str("/a\\q/.test('a\\q')"), njs_str("true") }, #endif #ifdef PCRE2_EXTRA_ALLOW_SURROGATE_ESCAPES { njs_str("/\\u200d\\ud800-/"), njs_str("/\\u200d\\ud800-/") }, #endif { njs_str("/(\\.(?!com|org)|\\/)/.test('ah.info')"), njs_str("true") }, { njs_str("/(/.test('')"), njs_str("SyntaxError: " njs_pcre_var("pcre_compile2(\"(\") failed: missing closing parenthesis at \"\" in 1", "pcre_compile(\"(\") failed: missing ) in 1")) }, { njs_str("/+/.test('')"), njs_str("SyntaxError: " njs_pcre_var("pcre_compile2(\"+\") failed: quantifier does not follow a repeatable item at \"+\" in 1", "pcre_compile(\"+\") failed: nothing to repeat at \"+\" in 1")) }, { njs_str("/^$/.test('')"), njs_str("true") }, { njs_str("var a = /\\d/; a.test('123')"), njs_str("true") }, { njs_str("var a = /\\d/; a.test('abc')"), njs_str("false") }, { njs_str("/\\d/.test('123')"), njs_str("true") }, { njs_str("/\\d/.test(123)"), njs_str("true") }, { njs_str("/undef/.test()"), njs_str("true") }, { njs_str("var s = { toString: function() { return 123 } };" "/\\d/.test(s)"), njs_str("true") }, { njs_str("/\\d/.test('abc')"), njs_str("false") }, { njs_str("/abc/i.test('ABC')"), njs_str("true") }, #if (!NJS_HAVE_MEMORY_SANITIZER) /* FIXME */ { njs_str("/абв/i.test('АБВ')"), njs_str("true") }, #endif { njs_str("var re = /<(?[\\w\\-\\.\\:]+)>(?.*?)<\\/\\1>/g;" "['XXX', 'XX'].map(s=>re.test(s))"), njs_str("true,false") }, { njs_str("/\\x80/.test('\\u0080')"), njs_str("true") }, { njs_str("/α/.test('\\u03B1')"), njs_str("true") }, { njs_str("/[A-Za-z]/.test('S')"), njs_str("true") }, { njs_str("/[A-Za-z]/.test('ø')"), njs_str("false") }, { njs_str("var r = /abc/y; r.test('abc'); r.lastIndex"), njs_str("3") }, { njs_str("/\\d/.exec('123')"), njs_str("1") }, { njs_str("/\\d/.exec(123)"), njs_str("1") }, { njs_str("/undef/.exec()"), njs_str("undef") }, { njs_str("var s = { toString: function() { return 123 } };" "/\\d/.exec(s)"), njs_str("1") }, { njs_str("var a = /^$/.exec(''); a.length +' '+ a"), njs_str("1 ") }, { njs_str("var r = /3/g; r.exec('123') +' '+ r.exec('3')"), njs_str("3 null") }, #if (!NJS_HAVE_MEMORY_SANITIZER) /* FIXME */ { njs_str("var r = /бв/ig;" "var a = r.exec('АБВ');" "r.lastIndex +' '+ a +' '+ " "r.source +' '+ r.source.length +' '+ r"), njs_str("3 БВ бв 2 /бв/gi") }, #endif { njs_str("var descs = Object.getOwnPropertyDescriptors(RegExp('a'));" "Object.keys(descs)"), njs_str("lastIndex") }, { njs_str("var props = Object.getOwnPropertyDescriptor(RegExp('a'), 'lastIndex');" "props.writable && !props.enumerable && !props.configurable"), njs_str("true") }, { njs_str("var re = /a/; re.lastIndex"), njs_str("0") }, { njs_str("var re = /aα/g; re.exec('aα'.repeat(32)); re.lastIndex"), njs_str("2") }, { njs_str("var re = new RegExp('α'.repeat(33), 'g'); re.exec('α'.repeat(33)); re.lastIndex"), njs_str("33") }, { njs_str("var re = new RegExp('α'.repeat(33), 'g'); re.exec('α'.repeat(33)); " "re.lastIndex = 67; re.lastIndex"), njs_str("67") }, { njs_str("var re = /a/; re.lastIndex = 4; Object.create(re).lastIndex"), njs_str("4") }, { njs_str("var re = /a/g; re.lastIndex = {valueOf(){throw 'Oops'}}; typeof re.lastIndex"), njs_str("object") }, { njs_str("var re = /a/g; re.lastIndex = {valueOf(){throw 'Oops'}}; re.exec('a')"), njs_str("Oops") }, { njs_str("var re = /a/; Object.defineProperty(re, 'lastIndex', {value:'qq'}); re.lastIndex"), njs_str("qq") }, { njs_str("var re = /a/; re.lastIndex = 'qq'; Object.create(re).lastIndex"), njs_str("qq") }, { njs_str("var re = /(?:ab|cd)\\d?/g; re.lastIndex=-1; re.test('aacd22 '); re.lastIndex"), njs_str("5") }, { njs_str("var re = /(?:ab|cd)\\d?/g; re.lastIndex=-1; re.test('@@'); re.lastIndex"), njs_str("0") }, { njs_str("var r = /a/; var gets = 0;" "var counter = { valueOf: function() { gets++; return 0; } };" "r.lastIndex = counter;" "njs.dump([r.exec('nbc'), r.lastIndex === counter, gets])"), njs_str("[null,true,1]") }, /* * It seems that "/стоп/ig" fails on early PCRE versions. * It fails at least in 8.1 and works at least in 8.31. */ #if (!NJS_HAVE_MEMORY_SANITIZER) /* FIXME */ { njs_str("var r = /Стоп/ig;" "var a = r.exec('АБВДЕЁЖЗИКЛМНОПРСТУФХЦЧШЩЬЫЪЭЮЯСТОП');" "r.lastIndex +' '+ a +' '+ r.source +' '+ r"), njs_str("35 СТОП Стоп /Стоп/gi") }, #endif { njs_str("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"), njs_str("Quick Brown Fox Jumps Brown Jumps undefined " "4 25 The Quick Brown Fox Jumps Over The Lazy Dog") }, { njs_str("var r = /a/.exec('a'); ['groups' in r, typeof r.groups]"), njs_str("true,undefined") }, { njs_str("var r = /(?[0-9]{2})\\/(?[0-9]{2})\\/(?[0-9]{4})/;" "var g = r.exec('12/31/1986').groups;" "g.d + '.' + g.m + '.' + g.y"), njs_str("31.12.1986") }, #if (!NJS_HAVE_MEMORY_SANITIZER) /* PCRE bug in groups code */ { njs_str("var g = /(?(?no)?(?yes)?)/.exec('yes').groups;" "[Object.keys(g).length,'no' in g, typeof g.no, g.yes, g.r]"), njs_str("3,true,undefined,yes,yes") }, #endif { njs_str("var s; var r = /./g; while (s = r.exec('abc')); s"), njs_str("null") }, { njs_str("(/α/).exec('γαβγ').index"), njs_str("1") }, { njs_str("var r = /LS/i.exec(false); r[0]"), njs_str("ls") }, { njs_str("var r = (/^.+$/mg), s = 'ab\\nc'; [r.exec(s), r.exec(s)]"), njs_str("ab,c") }, { njs_str("var r = (/^.+$/mg); [r.global, r.multiline, r.ignoreCase, r.sticky]"), njs_str("true,true,false,false") }, { njs_str("['global', 'ignoreCase', 'multiline', 'sticky']" ".map(v => Object.getOwnPropertyDescriptor(RegExp.prototype, v))" ".every(desc => (typeof desc.get === 'function' && typeof desc.set === 'undefined'))"), njs_str("true") }, { njs_str("var re = /./, re2 = /./y; re.lastIndex = 1; re2.lastIndex = 1;" "[re.exec('abc')[0], re2.exec('abc')[0]]"), njs_str("a,b") }, { njs_str("var re = /c/, re2 = /c/y;" "njs.dump([re.exec('abc')[0], re2.exec('abc')])"), njs_str("['c',null]") }, { njs_str("['global', 'ignoreCase', 'multiline']" ".map(v => Object.getOwnPropertyDescriptor(RegExp.prototype, v))" ".every(desc => (typeof desc.get === 'function' && typeof desc.set === 'undefined'))"), njs_str("true") }, { njs_str("var r = /./; r"), njs_str("/./") }, { njs_str("/[^]+|[^]+/.test('\\n| ')"), njs_str("true") }, { njs_str("/[^]+|[^][^]/.test('|aa')"), njs_str("true") }, { njs_str("/a[]/.test('a')"), njs_str("false") }, { njs_str("/[]a/.test('a')"), njs_str("false") }, #ifdef NJS_HAVE_PCRE2 { njs_str("/[]*a/.test('a')"), njs_str("true") }, #endif { njs_str("/Ca++BB/"), njs_str("SyntaxError: Invalid regular expression \"Ca++BB\" nothing to repeat in 1") }, { njs_str("/a*+/"), njs_str("SyntaxError: Invalid regular expression \"a*+\" nothing to repeat in 1") }, { njs_str("/a?+/"), njs_str("SyntaxError: Invalid regular expression \"a?+\" nothing to repeat in 1") }, { njs_str(" /\\[[]++\\]/"), njs_str("SyntaxError: Invalid regular expression \"\\[[]++\\]\" nothing to repeat in 1") }, { njs_str("/\\?+/"), njs_str("/\\?+/") }, { njs_str("var r = new RegExp(); r"), njs_str("/(?:)/") }, { njs_str("var r = new RegExp('.'); r"), njs_str("/./") }, { njs_str("var r = new RegExp('.', 'igm'); r"), njs_str("/./gim") }, { njs_str("var r = new RegExp('.', 'y'); r"), njs_str("/./y") }, { njs_str("var r = new RegExp('abc'); r.test('00abc11')"), njs_str("true") }, { njs_str("var r = new RegExp('abc', 'i'); r.test('00ABC11')"), njs_str("true") }, { njs_str("RegExp('α'.repeat(33)).toString()[32]"), njs_str("α") }, { njs_str("new RegExp('', 'x')"), njs_str("SyntaxError: Invalid RegExp flags \"x\"") }, { njs_str("new RegExp('', 'g ')"), njs_str("SyntaxError: Invalid RegExp flags \"g \"") }, { njs_str("new RegExp('', '')"), njs_str("/(?:)/") }, { njs_str("new RegExp('', {toString:()=>'g'})"), njs_str("/(?:)/g") }, { njs_str("RegExp({})"), njs_str("/[object Object]/") }, { njs_str("RegExp(true)"), njs_str("/true/") }, { njs_str("RegExp(undefined)"), njs_str("/(?:)/") }, { njs_str("RegExp('abc', undefined)"), njs_str("/abc/") }, { njs_str("RegExp('abc', {})"), njs_str("SyntaxError: Invalid RegExp flags \"[object Object]\"") }, { njs_str("RegExp(/expr/)"), njs_str("/expr/") }, { njs_str("RegExp(/expr/i).ignoreCase"), njs_str("true") }, { njs_str("RegExp(/expr/, 'x')"), njs_str("SyntaxError: Invalid RegExp flags \"x\"") }, { njs_str("RegExp(new RegExp('expr'))"), njs_str("/expr/") }, { njs_str("RegExp(RegExp('[^]+|[^][^]')).test('| \\na')"), njs_str("true") }, { njs_str("RegExp('a++')"), njs_str("SyntaxError: Invalid regular expression \"a++\" nothing to repeat") }, { njs_str("RegExp('[a++]')"), njs_str("/[a++]/") }, { njs_str("RegExp(new RegExp('expr')).multiline"), njs_str("false") }, { njs_str("RegExp(new RegExp('expr'), 'm').multiline"), njs_str("true") }, { njs_str("new RegExp('[')"), njs_str("SyntaxError: " njs_pcre_var("pcre_compile2(\"[\") failed: missing terminating ] for character class at \"\"", "pcre_compile(\"[\") failed: missing terminating ] for character class")) }, { njs_str("new RegExp('['.repeat(16))"), njs_str("SyntaxError: " njs_pcre_var("pcre_compile2(\"[[[[[[[[[[[[[[[[\") failed: missing terminating ] for character class at \"\"", "pcre_compile(\"[[[[[[[[[[[[[[[[\") failed: missing terminating ] for character class")) }, { njs_str("new RegExp('\\\\')"), njs_str("SyntaxError: " njs_pcre_var("pcre_compile2(\"\\\") failed: \\ at end of pattern at \"\"", "pcre_compile(\"\\\") failed: \\ at end of pattern")) }, { njs_str("[0].map(RegExp().toString)"), njs_str("TypeError: \"this\" argument is not an object") }, { njs_str("var arr = /\\1(A)/.exec('AA');" "[arr[0], arr[1]]"), njs_str("A,A") }, /* Non-standard ECMA-262 features. */ /* 0x10400 is not a surrogate pair of 0xD801 and 0xDC00. */ { njs_str("var chars = '𐐀'; chars.length +' '+ chars.charCodeAt(0)"), njs_str("1 66560") }, /* es5id: 6.1, 0x104A0 is not a surrogate pair of 0xD801 and 0xDCA0. */ { njs_str("var chars = '𐒠'; chars.length +' '+ chars.charCodeAt(0)"), njs_str("1 66720") }, /* Error object. */ { njs_str("Error()"), njs_str("Error") }, { njs_str("new Error()"), njs_str("Error") }, { njs_str("Error('e')"), njs_str("Error: e") }, { njs_str("Error(123)"), njs_str("Error: 123") }, { njs_str("Error({toString(){return 'e'}})"), njs_str("Error: e") }, { njs_str("Error([1,'α'])"), njs_str("Error: 1,α") }, { njs_str("var e = TypeError(Error('e')); e"), njs_str("TypeError: Error: e") }, { njs_str("Error('α'.repeat(33)).toString().length"), njs_str("40") }, { njs_str("var e = Error('e'); e.name = {toString(){return 'E'}}; e"), njs_str("E: e") }, { njs_str("var e = Error('e'); Object.defineProperty(e, 'name', {get(){return 'E'}}); e"), njs_str("E: e") }, { njs_str("var e = Error('e'); e.name = ''; e"), njs_str("e") }, { njs_str("var e = Error(); e.name = ''; e"), njs_str("") }, { njs_str("var e = Error(); e.name = ''; e.message = 'e'; e"), njs_str("e") }, { njs_str("Error('e').name + ': ' + Error('e').message"), njs_str("Error: e") }, { njs_str("Error(1)"), njs_str("Error: 1") }, { njs_str("Error.__proto__ == Function.prototype"), njs_str("true") }, { njs_str("Error.prototype.name"), njs_str("Error") }, { njs_str("Error.prototype.message"), njs_str("") }, { njs_str("Error.prototype.constructor == Error"), njs_str("true") }, { njs_str("Error.prototype.hasOwnProperty('constructor')"), njs_str("true") }, { njs_str("Error().__proto__ == Error.prototype"), njs_str("true") }, { njs_str("Error().__proto__.__proto__ == Object.prototype"), njs_str("true") }, { njs_str("Error.prototype.message = 'm';" "Error.prototype.name = 'n';" "new Error()"), njs_str("n: m") }, { njs_str("var e = RangeError('e'); Object.preventExtensions(e);e"), njs_str("RangeError: e") }, /* AggregateError. */ { njs_str("AggregateError()"), njs_str("TypeError: first argument is not iterable") }, { njs_str("AggregateError([1, 2, 3])"), njs_str("AggregateError") }, { njs_str("let e = AggregateError([1, 2, 3], 'm'); e.message"), njs_str("m") }, { njs_str("let e = AggregateError([1, 2, 3], 'm'); e.errors"), njs_str("1,2,3") }, { njs_str("let e = AggregateError('abc'); e.errors"), njs_str("a,b,c") }, { njs_str("let e = AggregateError('1234567'); e.errors"), njs_str("1,2,3,4,5,6,7") }, { njs_str("let e = AggregateError([1, 2, 3], 'm'); e"), njs_str("AggregateError: m") }, { njs_str("var v = Object.defineProperty([], 1025, {get: () => 1});" "AggregateError(v).errors[23]"), njs_str("undefined") }, { njs_str("var v = Object.defineProperty([], 2**20, {get: () => 1});" "AggregateError(v).errors[2**19]"), njs_str("undefined") }, /* Memory object is immutable. */ { njs_str("var e = MemoryError('e'); e.name = 'E'"), njs_str("TypeError: Cannot add property \"name\", object is not extensible") }, { njs_str("EvalError.prototype.name"), njs_str("EvalError") }, { njs_str("InternalError.prototype.name"), njs_str("InternalError") }, { njs_str("RangeError.prototype.name"), njs_str("RangeError") }, { njs_str("ReferenceError.prototype.name"), njs_str("ReferenceError") }, { njs_str("SyntaxError.prototype.name"), njs_str("SyntaxError") }, { njs_str("TypeError.prototype.name"), njs_str("TypeError") }, { njs_str("URIError.prototype.name"), njs_str("URIError") }, { njs_str("MemoryError.prototype.name"), njs_str("InternalError") }, /* NativeErrors. */ { njs_str( "var global = this;" "function isValidNativeError(e) {" " var inst;" " var proto = Object.getPrototypeOf(e) === Error;" " var proto2 = e.__proto__ === Error;" " var iproto = e().__proto__ === e.prototype;" " var iproto2 = e().__proto__.__proto__ === Error.prototype;" " var tpof = typeof e() === 'object';" " var ctor = e.prototype.constructor === e;" " var msg = e.prototype.message === '';" " var name = e('e').toString() === `${e.prototype.name}: e`;" " var name2 = (inst = e('e'), inst.name = 'E', inst.toString() === 'E: e');" " var name3 = (inst = e('e'), inst.name = '', inst.toString() === 'e');" " var name4 = e().toString() === `${e.prototype.name}`;" " var name_prop = Object.getOwnPropertyDescriptor(e.prototype, 'message');" " name_prop = name_prop.writable && !name_prop.enumerable && name_prop.configurable;" " var own_proto_ctor = e.prototype.hasOwnProperty('constructor');" " var props = Object.getOwnPropertyDescriptor(global, e.prototype.name);" " props = props.writable && !props.enumerable && props.configurable;" " var same = e === global[e.prototype.name];" "" " return proto && proto2 && iproto && iproto2 " " && tpof && ctor && msg && name && name2 && name3 && name4 " " && name_prop && own_proto_ctor && props && same;" "};" "[" " EvalError," " InternalError," " RangeError," " ReferenceError," " SyntaxError," " TypeError," " URIError," "].every(e => isValidNativeError(e))"), njs_str("true") }, /* Exceptions. */ { njs_str("throw null"), njs_str("null") }, { njs_str("var a; try { throw null } catch (e) { a = e } a"), njs_str("null") }, { njs_str("var a; try { throw Error('e') } catch (e) { a = e.message } a"), njs_str("e") }, { njs_str("var a; try { NaN.toString(NaN) } catch (e) { a = e.name } a"), njs_str("RangeError") }, { njs_str("try { throw null } catch (e) { throw e }"), njs_str("null") }, { njs_str("try { throw Error('e') } catch (e) { throw Error(e.message + '2') }"), njs_str("Error: e2") }, { njs_str("try { throw null } catch (null) { throw e }"), njs_str("SyntaxError: Unexpected token \"null\" in 1") }, { njs_str("'a'.f()"), njs_str("TypeError: (intermediate value)[\"f\"] is not a function") }, { njs_str("1..f()"), njs_str("TypeError: (intermediate value)[\"f\"] is not a function") }, { njs_str("try {}"), njs_str("SyntaxError: Missing catch or finally after try in 1") }, { njs_str("try{}catch(a[]"), njs_str("SyntaxError: Unexpected token \"[\" in 1") }, { njs_str("function f(a) {return a;}; " "function thrower() {throw TypeError('Oops')}; " "f(thrower())"), njs_str("TypeError: Oops") }, { njs_str("var a = 0; try { a = 5 }" "catch (e) { a = 9 } finally { a++ } a"), njs_str("6") }, { njs_str("var a = 0; try { throw 3 }" "catch (e) { a = e } finally { a++ } a"), njs_str("4") }, { njs_str("var a = 0; try { throw 3 }" "catch (e) { throw e + 1 } finally { a++ }"), njs_str("4") }, { njs_str("var a = 0; try { throw 3 }" "catch (e) { a = e } finally { throw a }"), njs_str("3") }, { njs_str("try { throw null } catch (e) { } finally { }"), njs_str("undefined") }, { njs_str("var a = 0; try { throw 3 }" "catch (e) { throw 4 } finally { throw a }"), njs_str("0") }, { njs_str("var a = 0; try { a = 5 } finally { a++ } a"), njs_str("6") }, { njs_str("var a = 0; try { throw 5 } finally { a++ }"), njs_str("5") }, { njs_str("var a = 0; try { a = 5 } finally { throw 7 }"), njs_str("7") }, { njs_str("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"), njs_str("9") }, { njs_str("var a; try { try { throw 5 } catch (e) { a = e } throw 3 }" " catch(x) { a += x } a"), njs_str("8") }, { njs_str("throw\nnull"), njs_str("SyntaxError: Illegal newline after throw in 2") }, { njs_str("for (var x in [1,2]) { try{ continue; } catch(e) {} } throw 1"), njs_str("1") }, { njs_str("for (var x in [1,2]) { try{ break; } catch(e) {} } throw 1"), njs_str("1") }, { njs_str("try\n {\n continue; } catch(e) {}"), njs_str("SyntaxError: Illegal continue statement in 3") }, { njs_str("var a = 1; " "switch (a) {" "default:" " try\n {\n continue; } " " catch(e) {}" "}"), njs_str("SyntaxError: Illegal continue statement in 3") }, { njs_str("try\n {\n break; } catch(e) {}"), njs_str("SyntaxError: Illegal break statement in 3") }, { njs_str("try\n { }\n catch(e) {continue;}"), njs_str("SyntaxError: Illegal continue statement in 3") }, { njs_str("try { } catch(e) {break;}"), njs_str("SyntaxError: Illegal break statement in 1") }, { njs_str("try { continue; } finally {}"), njs_str("SyntaxError: Illegal continue statement in 1") }, { njs_str("try { break; } finally {}"), njs_str("SyntaxError: Illegal break statement in 1") }, { njs_str("try\n {\n try\n {\n continue; } finally {} } finally {}"), njs_str("SyntaxError: Illegal continue statement in 5") }, /* break from try in try/catch. */ { njs_str("function f(n) {" " var pre = 0; var post = 0;" " for (var x in [1, 2, 3]) {" " pre++;" " try { " " if (n == 'b') {break;}" " }" " catch (e) {};" " post++" " }" " return [pre, post];" "}; njs.dump([f(),f('b')])"), njs_str("[[3,3]," "[1,0]]") }, { njs_str("function f(v, n) {" " var pre = 0; var post = 0; var case2 = 0;" " switch (v) {" " case 1: " " pre++;" " try { " " if (n == 'b') {break;}" " }" " catch (e) {};" " post++;" " break;" " default:" " case2++;" " }" " return [pre, post, case2];" "}; njs.dump([f(),f(1)])"), njs_str("[[0,0,1]," "[1,1,0]]") }, /* continue from try in try/catch. */ { njs_str("function f(n) {" " var pre = 0; var post = 0;" " for (var x in [1, 2, 3]) {" " pre++;" " try { " " if (n == 'c') {continue;}" " }" " catch (e) {};" " post++" " }" " return [pre, post];" "}; njs.dump([f(),f('c')])"), njs_str("[[3,3]," "[3,0]]") }, /* Multiple break/continue from try in try/catch. */ { njs_str("function f(n) {" " var pre = 0; var mid = 0; var post = 0;" " for (var x in [1, 2, 3]) {" " pre++;" " try { " " if (n == 'c') {continue;}" " if (n == 'b') {break;}" " mid++;" " if (n == 'c2') {continue;}" " if (n == 'b2') {break;}" " }" " catch (e) {};" " post++" " }" " return [pre, mid, post];" "}; njs.dump([f(),f('c'),f('b'),f('c2'),f('b2')])"), njs_str("[[3,3,3]," "[3,0,0]," "[1,0,0]," "[3,3,0]," "[1,1,0]]") }, /* Multiple break/continue from catch in try/catch. */ { njs_str("function f(t, n) {" " var pre = 0; var mid = 0; var post = 0;" " for (var x in [1, 2, 3]) {" " pre++;" " try { " " if (t) {throw 'a'}" " }" " catch (e) {" " if (n == 'c') {continue;}" " if (n == 'b') {break;}" " mid++;" " if (n == 'c2') {continue;}" " if (n == 'b2') {break;}" " };" " post++" " }" " return [pre, mid, post];" "}; njs.dump([f(), f(1), f(1, 'c'), f(1, 'b'), f(1, 'c2'), f(1, 'b2')])"), njs_str("[[3,0,3]," "[3,3,3]," "[3,0,0]," "[1,0,0]," "[3,3,0]," "[1,1,0]]") }, /* break from try in try/finally. */ { njs_str("function f(n) {" " var pre = 0; var mid = 0; var fin = 0; var post = 0;" " for (var x in [1, 2, 3]) {" " pre++;" " try { " " if (n == 'c') {continue;}" " if (n == 'b') {break;}" " mid++;" " if (n == 'c2') {continue;}" " if (n == 'b2') {break;}" " }" " finally {fin++};" " post++" " }" " return [pre, mid, fin, post];" "}; njs.dump([f(),f('c'),f('b'),f('c2'),f('b2')])"), njs_str("[[3,3,3,3]," "[3,0,3,0]," "[1,0,1,0]," "[3,3,3,0]," "[1,1,1,0]]") }, /* Multiple break/continue from try in try/catch/finally. */ { njs_str("function f(n) {" " var pre = 0; var mid = 0; var fin = 0; var post = 0;" " for (var x in [1, 2, 3]) {" " pre++;" " try { " " if (n == 'c') {continue;}" " if (n == 'b') {break;}" " mid++;" " if (n == 'c2') {continue;}" " if (n == 'b2') {break;}" " }" " catch (e) {}" " finally {fin++};" " post++" " }" " return [pre, mid, fin, post];" "}; njs.dump([f(),f('c'),f('b'),f('c2'),f('b2')])"), njs_str("[[3,3,3,3]," "[3,0,3,0]," "[1,0,1,0]," "[3,3,3,0]," "[1,1,1,0]]") }, /* Multiple break/continue from catch in try/catch/finally. */ { njs_str("function f(t, n) {" " var pre = 0; var mid = 0; var fin = 0; var post = 0;" " for (var x in [1, 2, 3]) {" " pre++;" " try {if (t) {throw 'a'}}" " catch (e) { " " if (n == 'c') {continue;}" " if (n == 'b') {break;}" " mid++;" " if (n == 'c2') {continue;}" " if (n == 'b2') {break;}" " }" " finally {fin++};" " post++" " }" " return [pre, mid, fin, post];" "}; njs.dump([f(), f(1), f(1, 'c'), f(1, 'b'), f(1, 'c2'), f(1, 'b2')])"), njs_str("[[3,0,3,3]," "[3,3,3,3]," "[3,0,3,0]," "[1,0,1,0]," "[3,3,3,0]," "[1,1,1,0]]") }, /* Multiple return from try. */ { njs_str("var r = 0; " "function f(i, n) {" " try { " " var a = 'x'; " " if (i != 0) {" " return a.repeat(n);" " } else {" " return;" " }" " }" " catch (e) { } " " finally { r++; }};" "[f(1,1), f(1,2), f(0), r]"), njs_str("x,xx,,3") }, { njs_str("var r = 0; " "function f(i) {" " try { " " return i;" " }" " catch (e) { } " " finally { r++; }};" "[f(true), f(false), r]"), njs_str("true,false,2") }, /* Multiple return from catch. */ { njs_str("var r = 0; " "function f(i, n) {" " try { " " throw 1;" " }" " catch (e) { " " var a = 'x'; " " if (i != 0) {" " return a.repeat(n);" " } else {" " return;" " }" " } " " finally { r++; }};" "[f(1,1), f(1,2), f(0), r]"), njs_str("x,xx,,3") }, /* return overrun by finally. */ { njs_str("function f() {" " try { " " return 'a';" " }" " catch (e) { " " } " " finally { " " return 'b'; " " }}; " "f()"), njs_str("b") }, { njs_str("(function (f, val) { " " try { return f(val); } " " finally { return val; }" "})(function () {throw 'a'}, 'v')"), njs_str("v") }, { njs_str("(function() { try { return ['a'];} finally {} } )()"), njs_str("a") }, { njs_str("function f(){}; try {f((new RegExp('a**')))} catch (e) { }"), njs_str("undefined") }, { njs_str("function f(){}; try {f(f((new RegExp('a**'))))} catch (e) { }"), njs_str("undefined") }, { njs_str("function f(){}; (function(){try {f(f((new RegExp('a**'))))} catch (e) { return 1}})()"), njs_str("1") }, { njs_str("var before, during, after;" "" "try {" " throw 'exception';" "} catch (err) {" " before = err;" "" " for (var err in { name: null }) {" " during = err;" " }" "" " after = err;" "}" "" "[before === 'exception', during === 'name', after === 'name']"), njs_str("true,true,true") }, { njs_str("var arr = [];" "foo = \"outside\";" "" "try {" " throw new Error();" "}" "catch (foo) {" " var foo = \"inside\";" " arr.push(foo);" "}" "" "arr.push(foo);" "arr"), njs_str("inside,outside") }, { njs_str("var o = { valueOf: function() { return '3' } }; --o"), njs_str("2") }, { njs_str("var o = { valueOf: function() { return [3] } }; --o"), njs_str("NaN") }, { njs_str("var o = { valueOf: function() { return '3' } }; 10 - o"), njs_str("7") }, { njs_str("var o = { valueOf: function() { return [3] } }; 10 - o"), njs_str("NaN") }, { njs_str("var o = { toString: function() { return 'OK' } }; 'o:' + o"), njs_str("o:OK") }, { njs_str("var o = { toString: function() { return [1] } }; o"), njs_str("TypeError: Cannot convert object to primitive value") }, { njs_str("var o = { toString: function() { return [1] } }; 'o:' + o"), njs_str("TypeError: Cannot convert object to primitive value") }, { njs_str("var a = { valueOf: function() { return '3' } };" "var b = { toString: function() { return 10 - a + 'OK' } };" "var c = { toString: function() { return b + 'YES' } };" "'c:' + c"), njs_str("c:7OKYES") }, { njs_str("[1,2,3].valueOf()"), njs_str("1,2,3") }, { njs_str("var o = { valueOf: function() { return 'OK' } };" "o.valueOf()"), njs_str("OK") }, { njs_str("false.__proto__ === true.__proto__"), njs_str("true") }, { njs_str("0..__proto__ === 1..__proto__"), njs_str("true") }, { njs_str("[].__proto__ === [1,2].__proto__"), njs_str("true") }, { njs_str("/./.__proto__ === /a/.__proto__"), njs_str("true") }, { njs_str("''.__proto__ === 'abc'.__proto__"), njs_str("true") }, { njs_str("[].__proto__.join.call([1,2,3], ':')"), njs_str("1:2:3") }, { njs_str("''.__proto__.concat.call('a', 'b', 'c')"), njs_str("abc") }, { njs_str("/./.__proto__.test.call(/a{2}/, 'aaa')"), njs_str("true") }, { njs_str("true instanceof Boolean"), njs_str("false") }, { njs_str("1 instanceof Number"), njs_str("false") }, { njs_str("'' instanceof String"), njs_str("false") }, { njs_str("({}) instanceof Object"), njs_str("true") }, { njs_str("[] instanceof []"), njs_str("TypeError: right argument is not callable") }, { njs_str("[] instanceof Array"), njs_str("true") }, { njs_str("[] instanceof Object"), njs_str("true") }, { njs_str("/./ instanceof RegExp"), njs_str("true") }, { njs_str("/./ instanceof Object"), njs_str("true") }, { njs_str("Object.defineProperty(Function.prototype, \"prototype\", {get: ()=>Array.prototype});" "[] instanceof Function.prototype"), njs_str("true") }, { njs_str("Object.defineProperty(Function.prototype, \"prototype\", {get: ()=>{throw Error('Oops')}});" "[] instanceof Function.prototype"), njs_str("Error: Oops") }, { njs_str("var o = {};" "Object.defineProperty(o, 'foo', { get: function() {return () => 1} });" "o.foo()"), njs_str("1") }, /* global this. */ { njs_str("this"), njs_str("[object global]") }, { njs_str("Object.getOwnPropertyDescriptor(this, 'NaN').value"), njs_str("NaN") }, { njs_str("Object.getOwnPropertyDescriptors(this).NaN.value"), njs_str("NaN") }, { njs_str("Object.getOwnPropertyNames(this).includes('NaN')"), njs_str("true") }, { njs_str("this.a = 1; this.a"), njs_str("1") }, { njs_str("this.a = 1; a"), njs_str("1") }, { njs_str("this.a = 1; a = 3; this.a"), njs_str("3") }, { njs_str("this.a = 1; ++a; this.a"), njs_str("2") }, { njs_str("this.a = 1; a += 3; this.a"), njs_str("4") }, { njs_str("var b=11;" "var t = function () {b += 5; return b};" "t() === 16 && b === 16 && this.b === 16" ), njs_str("true") }, { njs_str("this.c=15;" "var t = function () {c += 5; return c};" "t() === 20 && c === 20 && this.c === 20" ), njs_str("true") }, { njs_str("--undefined"), njs_str("TypeError: Cannot assign to read-only property \"undefined\" of object") }, { njs_str("this.a = 2; this.b = 3; a * b - a"), njs_str("4") }, { njs_str("this.a = 1; a()"), njs_str("TypeError: number is not a function") }, { njs_str("this.a = ()=>1; a()"), njs_str("1") }, { njs_str("var a = 1; this.a = 42; a"), njs_str("42") }, { njs_str("var a = 1; global.a = 42; a"), njs_str("42") }, { njs_str("var a = 1; globalThis.a = 42; a"), njs_str("42") }, { njs_str("global.a = 1; globalThis.a"), njs_str("1") }, { njs_str("var a = {}; globalThis.a === a"), njs_str("true") }, { njs_str("globalThis.a = 1; var a; a"), njs_str("1") }, { njs_str("var count = 0; function f() {return ++count}; [f(), global.f()]"), njs_str("1,2") }, { njs_str("Object.defineProperty(global, 'a', {value:1}); a"), njs_str("1") }, { njs_str("Object.defineProperty(global, 'a', {get:()=>123}); a"), njs_str("123") }, { njs_str("Object.defineProperties(global, {a:{value:1}, b:{value:2}}); [a,b]"), njs_str("1,2") }, #if 0 /* FIXME: for scope. */ { njs_str("var r1 = global.a; for (var a = 1; false;) {}; [r1, global.a]"), njs_str(",") }, #endif { njs_str("var global = this;" "function isImmutableConstant(v) {" " var d = Object.getOwnPropertyDescriptor(global, v);" " return !d.writable && !d.enumerable && !d.configurable;" "};" "['undefined', 'NaN', 'Infinity'].every((v)=>isImmutableConstant(v))"), njs_str("true") }, { njs_str("this.undefined = 42"), njs_str("TypeError: Cannot assign to read-only property \"undefined\" of object") }, { njs_str("this.Infinity = 42"), njs_str("TypeError: Cannot assign to read-only property \"Infinity\" of object") }, { njs_str("this.NaN = 42"), njs_str("TypeError: Cannot assign to read-only property \"NaN\" of object") }, { njs_str("typeof this.undefined"), njs_str("undefined") }, { njs_str("typeof this.Infinity"), njs_str("number") }, { njs_str("this.Infinity + 1"), njs_str("Infinity") }, { njs_str("typeof this.NaN"), njs_str("number") }, { njs_str("this.NaN + 1"), njs_str("NaN") }, { njs_str("{this}"), njs_str("undefined") }, { njs_str("if (1) {new this}"), njs_str("TypeError: object is not a function") }, { njs_str("if (1) {this()}"), njs_str("TypeError: object is not a function") }, { njs_str("var ex; try {new this} catch (e) {ex = e}; ex"), njs_str("TypeError: object is not a function") }, { njs_str("var ex; try {({}) instanceof this} catch (e) {ex = e}; ex"), njs_str("TypeError: right argument is not callable") }, { njs_str("delete global.global; global"), njs_str("ReferenceError: \"global\" is not defined") }, { njs_str("njs"), njs_str("[object njs]") }, { njs_str("var o = Object(); o"), njs_str("[object Object]") }, { njs_str("var o = new Object(); o"), njs_str("[object Object]") }, { njs_str("var o = new Object(1); o"), njs_str("1") }, { njs_str("var o = {}; o === Object(o)"), njs_str("true") }, { njs_str("var o = {}; o === new Object(o)"), njs_str("true") }, { njs_str("var o = Object([]); Object.prototype.toString.call(o)"), njs_str("[object Array]") }, { njs_str("Object.name"), njs_str("Object") }, { njs_str("Object.length"), njs_str("1") }, { njs_str("Object.__proto__ === Function.prototype"), njs_str("true") }, { njs_str("Object.prototype.constructor === Object"), njs_str("true") }, { njs_str("Object.prototype.hasOwnProperty('constructor')"), njs_str("true") }, { njs_str("Object.prototype.__proto__ === null"), njs_str("true") }, { njs_str("Object.prototype.__proto__ = {}"), njs_str("TypeError: Cyclic __proto__ value") }, { njs_str("var o = {}; var o2 = Object.create(o); o.__proto__ = o2"), njs_str("TypeError: Cyclic __proto__ value") }, { njs_str("Object.prototype.__proto__.f()"), njs_str("TypeError: cannot get property \"f\" of null") }, { njs_str("var obj = Object.create(null); obj.one = 1;" "var res = [];" "for (var val in obj) res.push(val); res"), njs_str("one") }, { njs_str("var o1 = Object.create(null); o1.one = 1;" "var o2 = Object.create(o1); o2.two = 2;" "var o3 = Object.create(o2); o3.three = 3;" "var res = [];" "for (var val in o3) res.push(val); res"), njs_str("three,two,one") }, { njs_str("var o1 = Object.create(null); o1.one = 1;" "var o2 = Object.create(o1);" "var o3 = Object.create(o2); o3.three = 3;" "var res = [];" "for (var val in o3) res.push(val); res"), njs_str("three,one") }, { njs_str("var o1 = Object.create(null); o1.one = 1;" "var o2 = Object.create(o1);" "var o3 = Object.create(o2);" "var res = [];" "for (var val in o3) res.push(val); res"), njs_str("one") }, { njs_str("var o1 = Object.create(null); o1.one = 1;" "var o2 = Object.create(o1); o2.two = 2;" "var o3 = Object.create(o2); o3.three = 3;" "o3.two = -2; o3.one = -1;" "var res = [];" "for (var val in o3) res.push(val); res"), njs_str("three,two,one") }, { njs_str("var a = []; for(var p in 'abc') a.push(p); a"), njs_str("0,1,2") }, { njs_str("var a = []; for(var p in Object('abc')) a.push(p); a"), njs_str("0,1,2") }, { njs_str("var o = Object('abc'); var x = Object.create(o);" "x.a = 1; x.b = 2;" "var a = []; for(var p in x) a.push(p); a"), njs_str("a,b,0,1,2") }, { njs_str("var o = Object.create(Math); o.abs = -5; Math.abs(o.abs)"), njs_str("5") }, { njs_str("Object.prototype.toString.call(Object.prototype)"), njs_str("[object Object]") }, { njs_str("Object.prototype.toString.call(new Array)"), njs_str("[object Array]") }, { njs_str("Object.prototype.toString.call(new URIError)"), njs_str("[object Error]") }, { njs_str("Object.prototype.toString.call(URIError.prototype)"), njs_str("[object Object]") }, { njs_str("Object.prototype"), njs_str("[object Object]") }, { njs_str("Object.prototype.valueOf.prototype"), njs_str("undefined") }, { njs_str("Object.prototype.valueOf.call()"), njs_str("TypeError: cannot convert null or undefined to object") }, { njs_str("Object.prototype.valueOf.call(null)"), njs_str("TypeError: cannot convert null or undefined to object") }, { njs_str("[false, NaN, Symbol(), '']" ".map((x) => Object.prototype.valueOf.call(x))" ".map((x) => Object.prototype.toString.call(x))"), njs_str("[object Boolean],[object Number],[object Symbol],[object String]") }, { njs_str("Object.constructor === Function"), njs_str("true") }, { njs_str("({}).__proto__ === Object.prototype"), njs_str("true") }, { njs_str("({}).__proto__ = 1"), njs_str("1") }, { njs_str("({}).__proto__ = null"), njs_str("null") }, { njs_str("({__proto__:null}).__proto__"), njs_str("undefined") }, { njs_str("({__proto__:null, a:1}).a"), njs_str("1") }, { njs_str("Object.getPrototypeOf({__proto__:null})"), njs_str("null") }, { njs_str("Object.getPrototypeOf({__proto__:1}) === Object.prototype"), njs_str("true") }, { njs_str("Object.getPrototypeOf({__proto__:Array.prototype}) === Array.prototype"), njs_str("true") }, { njs_str("({__proto__: []}) instanceof Array"), njs_str("true") }, { njs_str("({__proto__: Array.prototype}) instanceof Array"), njs_str("true") }, { njs_str("var o = {};" "o.__proto__ = Array.prototype;" "Object.getPrototypeOf(o) === Array.prototype"), njs_str("true") }, { njs_str("var o = Object.preventExtensions({});" "o.__proto__ = Array.prototype;" "Object.getPrototypeOf(o) === Object.prototype"), njs_str("true") }, { njs_str("var o = {__proto__: Array.prototype, length:3}; o.fill('a')[2]"), njs_str("a") }, { njs_str("({__proto__:null, __proto__: null})"), njs_str("SyntaxError: Duplicate __proto__ fields are not allowed in object literals in 1") }, { njs_str("({__proto__:null, '__proto__': null})"), njs_str("SyntaxError: Duplicate __proto__ fields are not allowed in object literals in 1") }, { njs_str("({__proto__:null, '\\x5f_proto__': null})"), njs_str("SyntaxError: Duplicate __proto__ fields are not allowed in object literals in 1") }, { njs_str("var __proto__ = 'a'; ({__proto__}).__proto__"), njs_str("a") }, { njs_str("var __proto__ = 'a'; ({__proto__, '__proto__':'b'}).__proto__"), njs_str("a") }, { njs_str("var __proto__ = 'a'; ({__proto__:null, __proto__}).__proto__"), njs_str("a") }, { njs_str("({__proto__:null, ['__proto__']:'a'}).__proto__"), njs_str("a") }, { njs_str("({__proto__() { return 123; }}).__proto__()"), njs_str("123") }, { njs_str("({['__prot' + 'o__']: 123}).__proto__"), njs_str("123") }, { njs_str("({}).__proto__.constructor === Object"), njs_str("true") }, { njs_str("({}).constructor === Object"), njs_str("true") }, { njs_str("({}).constructor()"), njs_str("[object Object]") }, { njs_str("var a = Object.__proto__; a()"), njs_str("undefined") }, { njs_str("Object.__proto__()"), njs_str("undefined") }, { njs_str("var a = Array(3); a"), njs_str(",,") }, { njs_str("var a = Array(); a.length"), njs_str("0") }, { njs_str("var a = Array(0); a.length"), njs_str("0") }, { njs_str("var a = Array(true); a"), njs_str("true") }, { njs_str("var a = Array(1,'two',3); a"), njs_str("1,two,3") }, { njs_str("var a = Array(-1)"), njs_str("RangeError: Invalid array length") }, { njs_str("var a = Array(2.5)"), njs_str("RangeError: Invalid array length") }, { njs_str("var a = Array(NaN)"), njs_str("RangeError: Invalid array length") }, { njs_str("var a = Array(Infinity)"), njs_str("RangeError: Invalid array length") }, { njs_str("var a = Array(1111111111); a[1111111112] = 1; a.length"), njs_str("1111111113") }, { njs_str("var a = Array(1111111111); a[1111111112] = 1; a[1111111112]"), njs_str("1") }, { njs_str("var a = Array(1111111111); a[1] = 2; a[1111111112] = 1; Object.keys(a)"), njs_str("1,1111111112") }, { njs_str("var a = Array(1111111111); a[1] = 2; a[1111111112] = 1; Object.entries(a)"), njs_str("1,2,1111111112,1") }, { njs_str("var x = Array(2**32)"), njs_str("RangeError: Invalid array length") }, { njs_str("var x = Array(2**28)"), njs_str("undefined") }, { njs_str("Array.prototype[2] = -1; var x = [0,1,3]; x.length = 2; x[2]"), njs_str("-1") }, { njs_str("Array.prototype[1] = 1; var x = [0]; x.length = 2; x[1]"), njs_str("1") }, { njs_str("var a = new Array(4); Object.defineProperty(a, '0', {enumerable:false}); a[2] = 's';" "Array.prototype.length"), njs_str("0") }, { njs_str("var x = [0, 1, 2]; x[4294967294] = 4294967294; x.length = 2;" "njs.dump([x,x.length,Array.prototype.length])"), njs_str("[[0,1],2,0]") }, #if 0 /* TODO: length 2**53-1. */ { njs_str("var x = Array(2**20), y = Array(2**12).fill(x);" "Array.prototype.concat.apply(y[0], y.slice(1))"), njs_str("RangeError: Invalid array length") }, #endif { njs_str("var a = new Array(3); a"), njs_str(",,") }, { njs_str("Array.name"), njs_str("Array") }, { njs_str("Array.length"), njs_str("1") }, { njs_str("delete this.Array; Array"), njs_str("ReferenceError: \"Array\" is not defined") }, { njs_str("Array.__proto__ === Function.prototype"), njs_str("true") }, { njs_str("Array.prototype.constructor === Array"), njs_str("true") }, { njs_str("Array.prototype.hasOwnProperty('constructor')"), njs_str("true") }, { njs_str("Array.prototype.__proto__ === Object.prototype"), njs_str("true") }, { njs_str("Object.prototype.toString.call(Array.prototype)"), njs_str("[object Array]") }, { njs_str("Array.prototype"), njs_str("") }, { njs_str("Array.prototype.length"), njs_str("0") }, { njs_str("Array.prototype.length = 3, Array.prototype"), njs_str(",,") }, { njs_str("var o = Object.create(Array.prototype); o.length = 3;" "[Array.prototype, Array.prototype.length, o.length]"), njs_str(",0,3") }, { njs_str("var o = Object.create(Array.prototype);" "Object.defineProperty(o, 'length', {value: 3});" "[Array.prototype, Array.prototype.length, o.length]"), njs_str(",0,3") }, { njs_str("Array.constructor === Function"), njs_str("true") }, { njs_str("var a = []; a.join = 'OK'; a"), njs_str("[object Array]") }, { njs_str("[].__proto__ === Array.prototype"), njs_str("true") }, { njs_str("[].__proto__.constructor === Array"), njs_str("true") }, { njs_str("[].constructor === Array"), njs_str("true") }, { njs_str("([]).constructor()"), njs_str("") }, { njs_str("Boolean()"), njs_str("false") }, { njs_str("new Boolean()"), njs_str("false") }, { njs_str("new Boolean"), njs_str("false") }, { njs_str("Boolean(0)"), njs_str("false") }, { njs_str("Boolean('')"), njs_str("false") }, { njs_str("Boolean(1)"), njs_str("true") }, { njs_str("Boolean('a')"), njs_str("true") }, { njs_str("Boolean({})"), njs_str("true") }, { njs_str("Boolean([])"), njs_str("true") }, { njs_str("typeof Boolean(1)"), njs_str("boolean") }, { njs_str("typeof new Boolean(1)"), njs_str("object") }, { njs_str("typeof new Boolean"), njs_str("object") }, { njs_str("Boolean.name"), njs_str("Boolean") }, { njs_str("Boolean.length"), njs_str("1") }, { njs_str("Boolean.__proto__ === Function.prototype"), njs_str("true") }, { njs_str("Boolean.prototype.constructor === Boolean"), njs_str("true") }, { njs_str("Boolean.prototype.constructor = 1;" "Boolean.prototype.constructor"), njs_str("1") }, { njs_str("var c = Boolean.prototype.constructor;" "Boolean.prototype.constructor = 1;" "[c(0), Boolean.prototype.constructor]"), njs_str("false,1") }, { njs_str("Boolean.prototype.hasOwnProperty('constructor')"), njs_str("true") }, { njs_str("Boolean.prototype.__proto__ === Object.prototype"), njs_str("true") }, { njs_str("Object.prototype.toString.call(Boolean.prototype)"), njs_str("[object Boolean]") }, { njs_str("Boolean.prototype"), njs_str("false") }, { njs_str("Boolean.constructor === Function"), njs_str("true") }, { njs_str("true.__proto__ === Boolean.prototype"), njs_str("true") }, { njs_str("false.__proto__ === Boolean.prototype"), njs_str("true") }, { njs_str("var b = Boolean(1); b.__proto__ === Boolean.prototype"), njs_str("true") }, { njs_str("var b = new Boolean(1); b.__proto__ === Boolean.prototype"), njs_str("true") }, { njs_str("Boolean(1).toString() === 'true'"), njs_str("true") }, { njs_str("Boolean(0).toString() === 'false'"), njs_str("true") }, { njs_str("Number()"), njs_str("0") }, { njs_str("new Number()"), njs_str("0") }, { njs_str("new Number"), njs_str("0") }, { njs_str("new Number(undefined)"), njs_str("NaN") }, { njs_str("new Number(null)"), njs_str("0") }, { njs_str("new Number(true)"), njs_str("1") }, { njs_str("new Number(false)"), njs_str("0") }, { njs_str("Number(undefined)"), njs_str("NaN") }, { njs_str("Number(null)"), njs_str("0") }, { njs_str("Number(true)"), njs_str("1") }, { njs_str("Number(false)"), njs_str("0") }, { njs_str("Number('0b111')"), njs_str("7") }, { njs_str("Number('0B111')"), njs_str("7") }, { njs_str("Number('0b1_11')"), njs_str("NaN") }, { njs_str("Number('-0b111')"), njs_str("NaN") }, { njs_str("Number(123)"), njs_str("123") }, { njs_str("Number('123')"), njs_str("123") }, { njs_str("Number('0o123')"), njs_str("83") }, { njs_str("Number('0O123')"), njs_str("83") }, { njs_str("Number('0o1_23')"), njs_str("NaN") }, { njs_str("Number('-0o123')"), njs_str("NaN") }, { njs_str("Number('0x123')"), njs_str("291") }, { njs_str("Number('0X123')"), njs_str("291") }, { njs_str("Number('0x1_23')"), njs_str("NaN") }, { njs_str("Number('-0x123')"), njs_str("NaN") }, { njs_str("['1', ' 1 ', '1\\t', '1\\n', '1\\r\\n'].reduce((a, x) => a + Number(x), 0)"), njs_str("5") }, { njs_str("Number('0.'+'1'.repeat(128))"), njs_str("0.1111111111111111") }, { njs_str("Number('1'.repeat(128))"), njs_str("1.1111111111111113e+127") }, { njs_str("Number('1'.repeat(129))"), njs_str("1.1111111111111112e+128") }, { njs_str("Number('1'.repeat(129))"), njs_str("1.1111111111111112e+128") }, { njs_str("Number('1'.repeat(129)+'e-100')"), njs_str("1.1111111111111112e+28") }, { njs_str("Number('1'.repeat(310))"), njs_str("Infinity") }, { njs_str("var o = { valueOf: function() { return 123 } };" "Number(o)"), njs_str("123") }, { njs_str("var o = { valueOf: function() { return 123 } };" "new Number(o)"), njs_str("123") }, { njs_str("typeof Number(1)"), njs_str("number") }, { njs_str("typeof new Number(1)"), njs_str("object") }, { njs_str("typeof new Number"), njs_str("object") }, { njs_str("Number.name"), njs_str("Number") }, { njs_str("Number.length"), njs_str("1") }, { njs_str("Number.__proto__ === Function.prototype"), njs_str("true") }, { njs_str("Number.prototype.constructor === Number"), njs_str("true") }, { njs_str("Number.prototype.hasOwnProperty('constructor')"), njs_str("true") }, { njs_str("Number.prototype.__proto__ === Object.prototype"), njs_str("true") }, { njs_str("Object.prototype.toString.call(Number.prototype)"), njs_str("[object Number]") }, { njs_str("Number.prototype"), njs_str("0") }, { njs_str("Number.constructor === Function"), njs_str("true") }, { njs_str("0..__proto__ === Number.prototype"), njs_str("true") }, { njs_str("var n = Number(1); n.__proto__ === Number.prototype"), njs_str("true") }, { njs_str("var n = new Number(1); n.__proto__ === Number.prototype"), njs_str("true") }, { njs_str("Number.isFinite()"), njs_str("false") }, { njs_str("Number.isFinite(123)"), njs_str("true") }, { njs_str("Number.isFinite('123')"), njs_str("false") }, { njs_str("Number.isFinite(Infinity)"), njs_str("false") }, { njs_str("Number.isFinite(NaN)"), njs_str("false") }, { njs_str("Number.isInteger()"), njs_str("false") }, { njs_str("Number.isInteger('123')"), njs_str("false") }, { njs_str("Number.isInteger(123)"), njs_str("true") }, { njs_str("Number.isInteger(-123.0)"), njs_str("true") }, { njs_str("Number.isInteger(123.4)"), njs_str("false") }, { njs_str("Number.isInteger(Infinity)"), njs_str("false") }, { njs_str("Number.isInteger(NaN)"), njs_str("false") }, { njs_str("Number.isSafeInteger()"), njs_str("false") }, { njs_str("Number.isSafeInteger('123')"), njs_str("false") }, { njs_str("Number.isSafeInteger(9007199254740991)"), njs_str("true") }, { njs_str("Number.isSafeInteger(-9007199254740991.0)"), njs_str("true") }, { njs_str("Number.isSafeInteger(9007199254740992)"), njs_str("false") }, { njs_str("Number.isSafeInteger(-9007199254740992.0)"), njs_str("false") }, { njs_str("Number.isSafeInteger(123.4)"), njs_str("false") }, { njs_str("Number.isSafeInteger(Infinity)"), njs_str("false") }, { njs_str("Number.isSafeInteger(-Infinity)"), njs_str("false") }, { njs_str("Number.isSafeInteger(NaN)"), njs_str("false") }, { njs_str("Number.isNaN()"), njs_str("false") }, { njs_str("Number.isNaN('NaN')"), njs_str("false") }, { njs_str("Number.isNaN(NaN)"), njs_str("true") }, { njs_str("Number.isNaN(123)"), njs_str("false") }, { njs_str("Number.isNaN(Infinity)"), njs_str("false") }, #if 0 { njs_str("parseFloat === Number.parseFloat"), njs_str("true") }, { njs_str("parseInt === Number.parseInt"), njs_str("true") }, #endif /* Symbol */ { njs_str("typeof Symbol"), njs_str("function") }, { njs_str("this.Symbol === Symbol"), njs_str("true") }, { njs_str("Symbol.name"), njs_str("Symbol") }, { njs_str("Object.getOwnPropertyDescriptor(Symbol, 'name').configurable"), njs_str("true") }, { njs_str("Symbol.length"), njs_str("0") }, { njs_str("Object.getOwnPropertyDescriptor(Symbol, 'length').configurable"), njs_str("true") }, { njs_str("typeof Symbol.for"), njs_str("function") }, { njs_str("Symbol.for.length"), njs_str("1") }, { njs_str("typeof Symbol.keyFor"), njs_str("function") }, { njs_str("Symbol.keyFor.length"), njs_str("1") }, { njs_str("Symbol.prototype.constructor === Symbol"), njs_str("true") }, { njs_str("Symbol.prototype.__proto__ === Object.prototype"), njs_str("true") }, { njs_str("Object.prototype.toString.call(Symbol.prototype)"), njs_str("[object Symbol]") }, { njs_str("Symbol.prototype.toString()"), njs_str("TypeError: unexpected value type:object") }, { njs_str("new Symbol()"), njs_str("TypeError: Symbol is not a constructor") }, { njs_str("typeof Symbol()"), njs_str("symbol") }, { njs_str("typeof Symbol('desc')"), njs_str("symbol") }, { njs_str("Symbol() === Symbol()"), njs_str("false") }, { njs_str("Symbol('desc') === Symbol('desc')"), njs_str("false") }, { njs_str("Symbol() == Symbol()"), njs_str("false") }, { njs_str("Symbol('desc') == Symbol('desc')"), njs_str("false") }, { njs_str("Symbol() == true"), njs_str("false") }, { njs_str("Symbol() == false"), njs_str("false") }, { njs_str("Symbol() != true"), njs_str("true") }, { njs_str("Symbol() != 0"), njs_str("true") }, { njs_str("Symbol() != ''"), njs_str("true") }, { njs_str("Symbol() != undefined"), njs_str("true") }, { njs_str("typeof Object(Symbol())"), njs_str("object") }, { njs_str("typeof Object(Symbol('desc'))"), njs_str("object") }, { njs_str("var x = Symbol(), o = Object(x); x == o"), njs_str("true") }, { njs_str("var x = Symbol(), o = Object(x); o == x"), njs_str("true") }, { njs_str("var x = Symbol(), o = Object(x); x !== o"), njs_str("true") }, { njs_str("var x = Symbol(), o = Object(x); x === o.valueOf()"), njs_str("true") }, { njs_str("var x = Symbol(); Object(x) == Object(x)"), njs_str("false") }, { njs_str("!Symbol()"), njs_str("false") }, { njs_str("!!Symbol()"), njs_str("true") }, { njs_str("(Symbol('a') && Symbol('b')).toString()"), njs_str("Symbol(b)") }, { njs_str("(Symbol('a') || Symbol('b')).toString()"), njs_str("Symbol(a)") }, { njs_str("+Symbol()"), njs_str("TypeError: Cannot convert a Symbol value to a number") }, { njs_str("-Symbol()"), njs_str("TypeError: Cannot convert a Symbol value to a number") }, { njs_str("~Symbol()"), njs_str("TypeError: Cannot convert a Symbol value to a number") }, { njs_str("var x = Symbol(); ++x"), njs_str("TypeError: Cannot convert a Symbol value to a number") }, { njs_str("var x = Symbol(); x++"), njs_str("TypeError: Cannot convert a Symbol value to a number") }, { njs_str("var x = Symbol(); --x"), njs_str("TypeError: Cannot convert a Symbol value to a number") }, { njs_str("var x = Symbol(); x--"), njs_str("TypeError: Cannot convert a Symbol value to a number") }, { njs_str("var msg = 'Cannot convert a Symbol value to a number'," " ops = ['+', '-', '*', '**', '/', '%', '>>>', '>>', '<<'," " '&', '|', '^', '<', '<=', '>', '>=']," " test = (lhs, rhs, cls, msg, stop, op) => {" " if (stop) {" " return stop;" " }" " var op = `${lhs} ${op} ${rhs}`;" " try {" " (new Function(op))();" " } catch (e) {" " if (e instanceof cls && msg == e.message) {" " return '';" " }" " }" " return `'${op}' - failed`;" " };" "ops.reduce(test.bind({}, 'Symbol()', '42', TypeError, msg), '') ||" "ops.reduce(test.bind({}, '42', 'Symbol()', TypeError, msg), '');"), njs_str("") }, { njs_str("Symbol() > 'abc'"), njs_str("TypeError: Cannot convert a Symbol value to a number") }, { njs_str("'abc' > Symbol()"), njs_str("TypeError: Cannot convert a Symbol value to a number") }, { njs_str("'abc' + Symbol()"), njs_str("TypeError: Cannot convert a Symbol value to a string") }, { njs_str("Symbol() + 'abc'"), njs_str("TypeError: Cannot convert a Symbol value to a string") }, { njs_str("Math.min(Symbol())"), njs_str("TypeError: Cannot convert a Symbol value to a number") }, { njs_str("[Symbol(), Symbol()].join()"), njs_str("TypeError: Cannot convert a Symbol value to a string") }, { njs_str("Symbol().toString()"), njs_str("Symbol()") }, { njs_str("Symbol('desc').toString()"), njs_str("Symbol(desc)") }, { njs_str("Symbol('α'.repeat(16)).toString()"), njs_str("Symbol(αααααααααααααααα)") }, { njs_str("Symbol(undefined).toString()"), njs_str("Symbol()") }, { njs_str("Symbol(null).toString()"), njs_str("Symbol(null)") }, { njs_str("Symbol(123).toString()"), njs_str("Symbol(123)") }, { njs_str("Symbol(false).toString()"), njs_str("Symbol(false)") }, { njs_str("Symbol(Symbol()).toString()"), njs_str("TypeError: Cannot convert a Symbol value to a string") }, { njs_str("var all = [ 'asyncIterator'," " 'hasInstance'," " 'isConcatSpreadable'," " 'iterator'," " 'match'," " 'matchAll'," " 'replace'," " 'search'," " 'species'," " 'split'," " 'toPrimitive'," " 'toStringTag'," " 'unscopables' ]; " "Object.getOwnPropertyNames(Symbol)" ".filter((x) => typeof Symbol[x] == 'symbol')" ".length == all.length"), njs_str("true") }, { njs_str("var all = [ 'asyncIterator'," " 'hasInstance'," " 'isConcatSpreadable'," " 'iterator'," " 'match'," " 'matchAll'," " 'replace'," " 'search'," " 'species'," " 'split'," " 'toPrimitive'," " 'toStringTag'," " 'unscopables' ]; " "Object.getOwnPropertyNames(Symbol)" ".filter((x) => typeof Symbol[x] == 'symbol')" ".filter((x, i) => !all.includes(x))"), njs_str("") }, { njs_str("Object.getOwnPropertyNames(Symbol)" ".filter((x) => typeof Symbol[x] == 'symbol')" ".map(x => ({ k: x, v: Symbol[x] }))" ".every((x) => 'Symbol(Symbol.' + x.k + ')' == String(x.v))"), njs_str("true") }, { njs_str("Symbol.for('desc').toString()"), njs_str("Symbol(desc)") }, { njs_str("Symbol.for({toString: () => 'desc'}).description"), njs_str("desc") }, { njs_str("Symbol.for().toString()"), njs_str("Symbol(undefined)") }, { njs_str("Symbol.for('desc') === Symbol.for('desc')"), njs_str("true") }, { njs_str("Symbol.keyFor(Symbol())"), njs_str("undefined") }, { njs_str("Symbol.keyFor(Symbol('desc'))"), njs_str("undefined") }, { njs_str("Symbol.keyFor(1)"), njs_str("TypeError: is not a symbol") }, { njs_str("Symbol.keyFor(Symbol.for('desc'))"), njs_str("desc") }, { njs_str("[ 'asyncIterator'," " 'hasInstance'," " 'isConcatSpreadable'," " 'iterator'," " 'match'," " 'matchAll'," " 'replace'," " 'search'," " 'species'," " 'split'," " 'toPrimitive'," " 'toStringTag'," " 'unscopables' ]" ".every(v => Symbol.keyFor(Symbol[v]) === undefined)"), njs_str("true") }, { njs_str("typeof Symbol.prototype.description"), njs_str("TypeError: unexpected value type:object") }, { njs_str("Symbol.prototype.description = 1"), njs_str("TypeError: Cannot set property \"description\" of object which has only a getter") }, { njs_str("typeof Symbol().description"), njs_str("undefined") }, { njs_str("Symbol('desc').description"), njs_str("desc") }, { njs_str("Symbol.iterator.description"), njs_str("Symbol.iterator") }, { njs_str("var o = {}; o[Symbol.isConcatSpreadable] = true; " "o[Symbol.isConcatSpreadable]"), njs_str("true") }, { njs_str("var o = {[Symbol.isConcatSpreadable]:true}; " "o[Symbol.isConcatSpreadable]"), njs_str("true") }, { njs_str("var o = {[Symbol.isConcatSpreadable]:true}; " "delete o[Symbol.isConcatSpreadable];" "o[Symbol.isConcatSpreadable]"), njs_str("undefined") }, { njs_str("var o = {get [Symbol.isConcatSpreadable](){return true}}; " "o[Symbol.isConcatSpreadable]"), njs_str("true") }, { njs_str("({[Symbol.isConcatSpreadable]:()=>true})[Symbol.isConcatSpreadable]()"), njs_str("true") }, { njs_str("var o = {a:1, [Symbol.isConcatSpreadable]:true}; " "[" " 'getOwnPropertyNames'," " 'keys', " " 'getOwnPropertySymbols'," " 'getOwnPropertyDescriptors'," "]" ".map(v=>Object[v](o)).map(v=>njs.dump(v))"), njs_str("['a'],['a'],[Symbol(Symbol.isConcatSpreadable)]," "{a:{value:1,writable:true,enumerable:true,configurable:true}," "Symbol(Symbol.isConcatSpreadable):{value:true,writable:true,enumerable:true,configurable:true}}") }, { njs_str("var o = {}; o[Symbol.isConcatSpreadable] = true; " "Object.getOwnPropertyDescriptors(o)[Symbol.isConcatSpreadable].value"), njs_str("true") }, { njs_str("var o = Object.defineProperties({}, {[Symbol.isConcatSpreadable]:{value:true}}); " "o[Symbol.isConcatSpreadable]"), njs_str("true") }, { njs_str("var o = Object.defineProperty({}, Symbol.isConcatSpreadable, " "{configurable:false, writable:false, value:true}); " "o[Symbol.isConcatSpreadable]"), njs_str("true") }, { njs_str("var o = {}; o[Symbol.isConcatSpreadable] = true; " "Object.getOwnPropertyDescriptor(o, Symbol.isConcatSpreadable).value"), njs_str("true") }, { njs_str("var a = [1];" "var b = [2, /**/, 4, 5];" "Object.defineProperty(b.__proto__, 1, {" " get: () => {" " b.length = 10**6;" " return 3;" " }" "});" "a.concat(b)"), njs_str("1,2,3,4,5") }, { njs_str("Boolean.prototype.length = 2;" "Boolean.prototype[0] = 'a';" "Boolean.prototype[1] = 'b';" "Boolean.prototype[Symbol.isConcatSpreadable] = true;" "[].concat(new Boolean(true))"), njs_str("a,b") }, { njs_str("var o = {}, n = 5381 /* NJS_DJB_HASH_INIT */;" "while(n--) o[Symbol()] = 'test'; o[''];"), njs_str("undefined") }, { njs_str("var symA = Symbol('A'); var obj = {[symA]:1}; Object.freeze(obj); " "obj[symA] = 2"), njs_str("TypeError: Cannot assign to read-only property \"Symbol(A)\" of object") }, { njs_str("var symA = Symbol('A'); var obj = {[symA]:1}; Object.freeze(obj); " "delete obj[symA]"), njs_str("TypeError: Cannot delete property \"Symbol(A)\" of object") }, { njs_str("typeof Object.getOwnPropertySymbols(globalThis);"), njs_str("object") }, { njs_str("[" " Object.prototype," " Symbol.prototype," " Math," " JSON," " process," " njs," " this," "]" ".map(v=>Object.getOwnPropertyDescriptor(v, Symbol.toStringTag))" ".map(d=>{if (d && !d.writable && !d.enumerable && d.configurable) return d.value})" ".map(v=>njs.dump(v))"), njs_str("undefined,Symbol,Math,JSON,process,njs,global") }, /* String */ { njs_str("String()"), njs_str("") }, { njs_str("new String()"), njs_str("") }, { njs_str("new String"), njs_str("") }, { njs_str("String(123)"), njs_str("123") }, { njs_str("new String(123)"), njs_str("123") }, { njs_str("String(Symbol())"), njs_str("Symbol()") }, { njs_str("String(Symbol('desc'))"), njs_str("Symbol(desc)") }, { njs_str("new String(Symbol())"), njs_str("TypeError: Cannot convert a Symbol value to a string") }, { njs_str("String(Object(Symbol()))"), njs_str("TypeError: Cannot convert a Symbol value to a string") }, { njs_str("Object('123').length"), njs_str("3") }, { njs_str("new String(123).length"), njs_str("3") }, { njs_str("new String(123).toString()"), njs_str("123") }, { njs_str("String([1,2,3])"), njs_str("1,2,3") }, { njs_str("new String([1,2,3])"), njs_str("1,2,3") }, { njs_str("var s = new String('αβ'); s.one = 1; 'one' in s"), njs_str("true") }, { njs_str("var s = new String('αβ'); 'one' in s"), njs_str("false") }, { njs_str("var s = new String('αβ'); s.one = 1; '1' in s"), njs_str("true") }, { njs_str("var s = new String('αβ'); s.one = 1; 1 in s"), njs_str("true") }, { njs_str("var s = new String('αβ'); s.one = 1; 2 in s"), njs_str("false") }, { njs_str("var s = new String('αβ'); s[1]"), njs_str("β") }, { njs_str("Object.create(new String('αβ'))[1]"), njs_str("β") }, { njs_str("var s = new String('αβ'); s[1] = 'b'"), njs_str("TypeError: Cannot assign to read-only property \"1\" of object") }, { njs_str("var o = Object.create(new String('αβ')); o[1] = 'a'"), njs_str("TypeError: Cannot assign to read-only property \"1\" of object") }, { njs_str("var s = new String('αβ'); s[4] = 'ab'; s[4]"), njs_str("ab") }, { njs_str("Object.create(new String('αβ')).length"), njs_str("2") }, { njs_str("var s = new String('αβ'); s.valueOf()[1]"), njs_str("β") }, { njs_str("var o = { toString: function() { return 'OK' } };" "String(o)"), njs_str("OK") }, { njs_str("var o = { toString: function() { return 'OK' } };" "new String(o)"), njs_str("OK") }, { njs_str("typeof String('abc')"), njs_str("string") }, { njs_str("typeof new String('abc')"), njs_str("object") }, { njs_str("typeof new String"), njs_str("object") }, { njs_str("String.name"), njs_str("String") }, { njs_str("String.length"), njs_str("1") }, /* values_hash long vs long string collision. */ { njs_str("'XXXXXXXXXXXXXXXQWEEAB' + 'XXXXXXXXXXXXXXXZHGP'"), njs_str("XXXXXXXXXXXXXXXQWEEABXXXXXXXXXXXXXXXZHGP") }, /* values_hash short vs long string collision. */ { njs_str("'SHAAAB' + 'XXXXXXXXXXXXXXXUETBF'"), njs_str("SHAAABXXXXXXXXXXXXXXXUETBF") }, /* values_hash long vs short string collision. */ { njs_str("'XXXXXXXXXXXXXXXUETBF' + 'SHAAAB'"), njs_str("XXXXXXXXXXXXXXXUETBFSHAAAB") }, /* values_hash short vs short string collision. */ { njs_str("'XUBAAAB' + 'XGYXKY'"), njs_str("XUBAAABXGYXKY") }, { njs_str("String.__proto__ === Function.prototype"), njs_str("true") }, { njs_str("Object.prototype.toString.call(String.prototype)"), njs_str("[object String]") }, { njs_str("String.prototype"), njs_str("") }, { njs_str("String.prototype.length"), njs_str("0") }, { njs_str("String.prototype.constructor === String"), njs_str("true") }, { njs_str("String.prototype.hasOwnProperty('constructor')"), njs_str("true") }, { njs_str("String.prototype.__proto__ === Object.prototype"), njs_str("true") }, { njs_str("''.__proto__ === String.prototype"), njs_str("true") }, { njs_str("String.constructor === Function"), njs_str("true") }, { njs_str("'test'.__proto__ === String.prototype"), njs_str("true") }, { njs_str("var s = String('abc'); s.__proto__ === String.prototype"), njs_str("true") }, { njs_str("var s = new String('abc'); s.__proto__ === String.prototype"), njs_str("true") }, { njs_str("'test'.constructor === String"), njs_str("true") }, { njs_str("'test'.constructor.prototype === String.prototype"), njs_str("true") }, { njs_str("var o = Object.create(String.prototype); o.length = 1"), njs_str("TypeError: Cannot assign to read-only property \"length\" of object") }, { njs_str("Function.name"), njs_str("Function") }, { njs_str("Function.length"), njs_str("1") }, { njs_str("Function.__proto__ === Function.prototype"), njs_str("true") }, { njs_str("Function.prototype.constructor === Function"), njs_str("true") }, { njs_str("Function.prototype.hasOwnProperty('constructor')"), njs_str("true") }, { njs_str("Function.prototype.__proto__ === Object.prototype"), njs_str("true") }, { njs_str("Object.prototype.toString.call(Function.prototype)"), njs_str("[object Function]") }, { njs_str("Function.prototype"), njs_str("[object Function]") }, { njs_str("Function.prototype.length"), njs_str("0") }, { njs_str("Function.constructor === Function"), njs_str("true") }, { njs_str("Function.constructor()"), njs_str("[object Function]") }, { njs_str("function f() {} f.__proto__ === Function.prototype"), njs_str("true") }, { njs_str("Function()"), njs_str("[object Function]") }, { njs_str("new Function();"), njs_str("[object Function]") }, { njs_str("(function(){}).constructor === Function"), njs_str("true") }, { njs_str("new Function('('.repeat(2**13));"), njs_str("SyntaxError: Unexpected token \"}\" in runtime:1") }, { njs_str("new Function('{'.repeat(2**13));"), njs_str("SyntaxError: Unexpected token \")\" in runtime:1") }, { njs_str("new Function('['.repeat(2**13));"), njs_str("SyntaxError: Unexpected token \"}\" in runtime:1") }, { njs_str("new Function('`'.repeat(2**13));"), njs_str("[object Function]") }, { njs_str("new Function('{['.repeat(2**13));"), njs_str("SyntaxError: Unexpected token \"}\" in runtime:1") }, { njs_str("new Function('{;'.repeat(2**13));"), njs_str("SyntaxError: Unexpected token \")\" in runtime:1") }, { njs_str("(new Function('1;'.repeat(2**13) + 'return 2'))()"), njs_str("2") }, { njs_str("(new Function('return' + '~'.repeat(2**13) + '3'))()"), njs_str("3") }, { njs_str("(new Function('return' + '~'.repeat(2**13+1) + '3'))()"), njs_str("-4") }, { njs_str("new Function('new '.repeat(2**13));"), njs_str("SyntaxError: Unexpected token \"}\" in runtime:1") }, { njs_str("(new Function('return ' + 'typeof '.repeat(2**13) + 'x'))()"), njs_str("string") }, { njs_str("(new Function('return 5' + '** 1'.repeat(2**13)))()"), njs_str("5") }, { njs_str("var a = (new Function('return [' + ','.repeat(2**16) + ']'))();" "njs.dump(a)"), njs_str("[<65536 empty items>]") }, { njs_str("(new Function('var a = 7; return a' + '= a'.repeat(2**13)))()"), njs_str("7") }, { njs_str("var a = (new Function('return [' + '1,'.repeat(2**13) + ']'))();" "a.push(5); [a[2**13 - 1], a[2**13]]"), njs_str("1,5") }, { njs_str("var f = new Function('return 1;'); f();"), njs_str("1") }, { njs_str("var sum = new Function('a', 'b', 'return a + b');" "sum(2, 4);"), njs_str("6") }, { njs_str("var sum = new Function('a, b', 'return a + b');" "sum(2, 4);"), njs_str("6") }, { njs_str("var sum = new Function('a, b', 'c', 'return a + b + c');" "sum(2, 4, 4);"), njs_str("10") }, { njs_str("(new Function({ toString() { return '...a'; }}, { toString() { return 'return a;' }}))(1,2,3)"), njs_str("1,2,3") }, { njs_str("var x = 10; function foo() { var x = 20; return new Function('return x;'); }" "var f = foo(); f()"), njs_str("10") }, { njs_str("var fn = (function() { return new Function('return this'); }).call({}), o = {}; " "fn.call(o) == o && fn.bind(o).call(this) == o"), njs_str("true") }, { njs_str("(new Function('return this'))() === globalThis"), njs_str("true") }, { njs_str("var f = Function.call(this, 'return this.a');" "var r1 = f(); var a = 1; var r2 = f(); [r1,r2]"), njs_str(",1") }, { njs_str("(new Function('a', 'return a')).length"), njs_str("1") }, { njs_str("(new Function('a','b', 'return a + b')).length"), njs_str("2") }, { njs_str("var o = {}; (new Function('return this')).call(o) === o"), njs_str("true") }, { njs_str("(new Function('function foo(){return 1}; return foo()'))();" "foo"), njs_str("ReferenceError: \"foo\" is not defined") }, { njs_str("this.NN = {}; var f = Function('eval = 42;'); f()"), njs_str("SyntaxError: Identifier \"eval\" is forbidden as left-hand in assignment in runtime:1") }, { njs_str("RegExp()"), njs_str("/(?:)/") }, { njs_str("RegExp('')"), njs_str("/(?:)/") }, { njs_str("RegExp(123)"), njs_str("/123/") }, { njs_str("RegExp.name"), njs_str("RegExp") }, { njs_str("RegExp.length"), njs_str("2") }, { njs_str("RegExp.__proto__ === Function.prototype"), njs_str("true") }, { njs_str("RegExp.prototype.constructor === RegExp"), njs_str("true") }, { njs_str("RegExp.prototype.hasOwnProperty('constructor')"), njs_str("true") }, { njs_str("RegExp.prototype.__proto__ === Object.prototype"), njs_str("true") }, { njs_str("Object.prototype.toString.call(RegExp.prototype)"), njs_str("[object Object]") }, { njs_str("RegExp.prototype"), njs_str("/(?:)/") }, { njs_str("RegExp.constructor === Function"), njs_str("true") }, { njs_str("/./.__proto__ === RegExp.prototype"), njs_str("true") }, { njs_str("toString()"), njs_str("[object Undefined]") }, { njs_str("toString() + Object.prototype.toString"), njs_str("[object Undefined][object Function]") }, #if 0 { njs_str("toString === Object.prototype.toString"), njs_str("true") }, { njs_str("Object.prototype.toString.yes = 'OK'; toString.yes"), njs_str("OK") }, #endif { njs_str("Object.prototype.toString.call()"), njs_str("[object Undefined]") }, { njs_str("Object.prototype.toString.call(undefined)"), njs_str("[object Undefined]") }, { njs_str("Object.prototype.toString.call(null)"), njs_str("[object Null]") }, { njs_str("Object.prototype.toString.call(true)"), njs_str("[object Boolean]") }, { njs_str("Boolean.prototype[Symbol.toStringTag] = 'XXX';" "Object.prototype.toString.call(true)"), njs_str("[object XXX]") }, { njs_str("Object.prototype.toString.call(1)"), njs_str("[object Number]") }, { njs_str("Object.prototype.toString.call('')"), njs_str("[object String]") }, { njs_str("Object.prototype.toString.call({})"), njs_str("[object Object]") }, { njs_str("Object.prototype.toString.call([])"), njs_str("[object Array]") }, { njs_str("var a = []; a[Symbol.toStringTag] = 'XXX';" "Object.prototype.toString.call(a)"), njs_str("[object XXX]") }, { njs_str("Object.prototype.toString.call(new Object(true))"), njs_str("[object Boolean]") }, { njs_str("Object.prototype.toString.call(new Number(1))"), njs_str("[object Number]") }, { njs_str("Object.prototype.toString.call(new Object(1))"), njs_str("[object Number]") }, { njs_str("Object.prototype.toString.call(new Object(Symbol()))"), njs_str("[object Symbol]") }, { njs_str("Object.prototype.toString.call(new Object(Symbol('desc')))"), njs_str("[object Symbol]") }, { njs_str("Object.prototype.toString.call(new Object(''))"), njs_str("[object String]") }, { njs_str("Object.prototype.toString.call(function(){})"), njs_str("[object Function]") }, { njs_str("var f = ()=>1; f[Symbol.toStringTag] = 'α'.repeat(32);" "var toStr = Object.prototype.toString.call(f); [toStr, toStr.length]"), njs_str("[object αααααααααααααααααααααααααααααααα],41") }, { njs_str("Object.prototype.toString.call(/./)"), njs_str("[object RegExp]") }, { njs_str("Object.prototype.toString.call(Math)"), njs_str("[object Math]") }, { njs_str("Object.prototype.toString.call(JSON)"), njs_str("[object JSON]") }, { njs_str("var p = { a:5 }; var o = Object.create(p); o.a"), njs_str("5") }, { njs_str("var p = { a:5 }; var o = Object.create(p);" "o.__proto__ === p"), njs_str("true") }, /* Object.create() */ { njs_str("var o = Object.create(Object.prototype);" "o.__proto__ === Object.prototype"), njs_str("true") }, { njs_str("var o = Object.create(null); '__proto__' in o"), njs_str("false") }, { njs_str("Object.create()"), njs_str("TypeError: prototype may only be an object or null: undefined") }, { njs_str("Object.create(1)"), njs_str("TypeError: prototype may only be an object or null: number") }, { njs_str("var o = Object.create(null, { a: { value: 1 } }); o.a"), njs_str("1") }, { njs_str("var o = Object.create({ a: 0 }, { a: { value: 1 } }); o.a"), njs_str("1") }, { njs_str("var o = Object.create({ get a() { return this.b; } }, { b: { value: 1 } }); o.a"), njs_str("1") }, { njs_str("var o = {a:1, b:2, c:3};" "Object.keys(o)"), njs_str("a,b,c") }, { njs_str("var a = []; a.one = 7; Object.keys(a)"), njs_str("one") }, { njs_str("var a = [,,]; a.one = 7; Object.keys(a)"), njs_str("one") }, { njs_str("var a = [,6,,3]; a.one = 7; Object.keys(a)"), njs_str("1,3,one") }, { njs_str("var o = {a:1,b:2}; delete o.a; Object.keys(o)"), njs_str("b") }, { njs_str("Object.keys()"), njs_str("TypeError: cannot convert undefined argument to object") }, { njs_str("Object.keys('αβZ')"), njs_str("0,1,2") }, { njs_str("Object.keys(new String('αβZ'))"), njs_str("0,1,2") }, { njs_str("var s = new String('αβZ'); s.a = 1; Object.keys(s)"), njs_str("0,1,2,a") }, { njs_str("var r = new RegExp('αbc'); r.a = 1; Object.keys(r)"), njs_str("a") }, { njs_str("Object.keys(Object.create(new String('abc')))"), njs_str("") }, { njs_str("Object.keys(1)"), njs_str("") }, { njs_str("Object.keys(true)"), njs_str("") }, { njs_str("var o = {a:3, b:2, c:1}; Object.values(o)"), njs_str("3,2,1") }, { njs_str("Object.values('s')"), njs_str("s") }, { njs_str("Object.values('абв abc')"), njs_str("а,б,в, ,a,b,c") }, { njs_str("var s = new String('abc'); s.three = 3; Object.values(s)"), njs_str("a,b,c,3") }, { njs_str("var a = [,,5,,4,,,3,2]; a.one = 1; Object.values(a)"), njs_str("5,4,3,2,1") }, { njs_str("Object.values([{}, null, false, NaN, function() {}])"), njs_str("[object Object],,false,NaN,[object Function]") }, { njs_str("Object.values(1)"), njs_str("") }, { njs_str("Object.values(njs)[1] === njs.version"), njs_str("true") }, { njs_str("njs.version.split('.')" ".map(v => parseInt(v)).reduce((p, c) => p * 256 + c) == njs.version_number"), njs_str("true") }, { njs_str("Object.values(process)"), njs_str("") }, { njs_str("Object.keys(process.env).sort()"), njs_str("DUP,TZ") }, { njs_str("Object.values()"), njs_str("TypeError: cannot convert undefined argument to object") }, { njs_str("Object.entries('abc')"), njs_str("0,a,1,b,2,c") }, { njs_str("JSON.stringify(Object.entries('эюя'))"), njs_str("[[\"0\",\"э\"],[\"1\",\"ю\"],[\"2\",\"я\"]]") }, { njs_str("var o = {a:\"а\", b:\"б\", c:\"ц\"};" "JSON.stringify(Object.entries(o))"), njs_str("[[\"a\",\"а\"],[\"b\",\"б\"],[\"c\",\"ц\"]]") }, { njs_str("JSON.stringify(Object.entries([0]))"), njs_str("[[\"0\",0]]") }, { njs_str("var s = new String('αβ'); s.two = null; s[3] = true;" "Object.entries(s)"), njs_str("0,α,1,β,3,true,two,") }, { njs_str("Object.entries(true)"), njs_str("") }, { njs_str("Object.entries(njs)[1][1] === njs.version"), njs_str("true") }, { njs_str("Object.entries()"), njs_str("TypeError: cannot convert undefined argument to object") }, { njs_str("var o = {}; Object.defineProperty(o, 'a', {}); o.a"), njs_str("undefined") }, { njs_str("Object.defineProperty({}, 'a', {value:1})"), njs_str("[object Object]") }, { njs_str("var o = {}; Object.defineProperty(o, 'a', {value:1}); o.a"), njs_str("1") }, { njs_str("var o = {}; Object.defineProperty(o, 'a', Object.create({value:1})); o.a"), njs_str("1") }, { njs_str("var o = {}; Object.defineProperty(o, 'a', {writable:'x', enumerable:'y', configurable:'z'});" "var d = Object.getOwnPropertyDescriptor(o, 'a');" "d.writable && d.enumerable && d.configurable"), njs_str("true") }, { njs_str("var o = {a:1, c:2}; Object.defineProperty(o, 'b', {});" "Object.keys(o)"), njs_str("a,c") }, { njs_str("var o = {a:1, c:2};" "Object.defineProperty(o, 'b', {enumerable:false});" "Object.keys(o)"), njs_str("a,c") }, { njs_str("var o = {a:1, c:3};" "Object.defineProperty(o, 'b', {enumerable:true});" "Object.keys(o)"), njs_str("a,c,b") }, { njs_str("var o = {a:1, c:2}; Object.defineProperty(o, 'b', {});" "Object.values(o)"), njs_str("1,2") }, { njs_str("var o = {a:1, c:2};" "Object.defineProperty(o, 'b', {enumerable:false, value:3});" "Object.values(o)"), njs_str("1,2") }, { njs_str("var o = {a:1, c:3};" "Object.defineProperty(o, 'b', {enumerable:true, value:2});" "Object.values(o)"), njs_str("1,3,2") }, { njs_str("var o = { a: 'A', get b() { this.c = 'C'; return 'B'; } };" "Object.values(o).length"), njs_str("2") }, { njs_str("var o = {a:1, c:2}; Object.defineProperty(o, 'b', {});" "Object.entries(o)"), njs_str("a,1,c,2") }, { njs_str("var o = {a:1, c:2};" "Object.defineProperty(o, 'b', {enumerable:false, value:3});" "Object.entries(o)"), njs_str("a,1,c,2") }, { njs_str("var o = {a:1, c:3};" "Object.defineProperty(o, 'b', {enumerable:true, value:2});" "Object.entries(o)"), njs_str("a,1,c,3,b,2") }, { njs_str("var o = { a: 'A', get b() { this.c = 'C'; return 'B'; } };" "Object.entries(o).length"), njs_str("2") }, { njs_str("var o = {}; Object.defineProperty(o, 'a', {}); o.a = 1"), njs_str("TypeError: Cannot assign to read-only property \"a\" of object") }, { njs_str("var o = {}; Object.defineProperty(o, 'a', {writable:false}); o.a = 1"), njs_str("TypeError: Cannot assign to read-only property \"a\" of object") }, { njs_str("var o = {}; Object.defineProperty(o, 'a', {writable:true});" "o.a = 1; o.a"), njs_str("1") }, { njs_str("Object.defineProperty(Object.prototype, 'a', {writable:false});" "var o = {a: 1}; [o.a++, o.a]"), njs_str("1,2") }, { njs_str("var o = {};" "Object.defineProperty(Object.prototype, 'a', {writable:false});" "o.a = 1"), njs_str("TypeError: Cannot assign to read-only property \"a\" of object") }, { njs_str("var o = {};" "Object.defineProperty(Object.prototype, 'a', {writable:true});" "o.a = 1; o.a"), njs_str("1") }, { njs_str("var p = Object.create(Function);" "Object.defineProperty(p, 'length', {writable: true});" "p.length = 32; p.length"), njs_str("32") }, { njs_str("var p = Object.create(Math.abs);" "Object.defineProperty(p, 'length', {writable: true});" "p.length = 23; p.length"), njs_str("23") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {value:1}); delete o.a"), njs_str("TypeError: Cannot delete property \"a\" of object") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {value:1, configurable:true});" "delete o.a; o.a"), njs_str("undefined") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {value:1, configurable:false});" "delete o.a"), njs_str("TypeError: Cannot delete property \"a\" of object") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', Object.create({value:2})); o.a"), njs_str("2") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {configurable:false});" "Object.defineProperty(o, 'a', {configurable:true})"), njs_str("TypeError: Cannot redefine property: \"a\"") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {configurable:false});" "Object.defineProperty(o, 'a', {enumerable:true})"), njs_str("TypeError: Cannot redefine property: \"a\"") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {configurable:false});" "Object.defineProperty(o, 'a', {writable:true})"), njs_str("TypeError: Cannot redefine property: \"a\"") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {configurable:false});" "Object.defineProperty(o, 'a', {enumerable:false}).a"), njs_str("undefined") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {configurable:false});" "Object.defineProperty(o, 'a', {}).a"), njs_str("undefined") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {configurable:false, writable:true});" "Object.defineProperty(o, 'a', {writable:false}).a"), njs_str("undefined") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {configurable:true, writable:false});" "Object.defineProperty(o, 'a', {writable:true}).a"), njs_str("undefined") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {});" "Object.defineProperty(o, 'a', {}).a"), njs_str("undefined") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {value:1});" "Object.defineProperty(o, 'a', {value:1}).a"), njs_str("1") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {value:1});" "Object.defineProperty(o, 'a', {value:2}).a"), njs_str("TypeError: Cannot redefine property: \"a\"") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {configurable:true});" "Object.defineProperty(o, 'a', {value:1}).a"), njs_str("1") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {configurable:true, get:()=>1});" "Object.defineProperty(o, 'a', {value:1});" "var d = Object.getOwnPropertyDescriptor(o, 'a'); " "[d.value, d.writable, d.enumerable, d.configurable, d.get, d.set]"), njs_str("1,false,false,true,,") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {configurable:true, value:1});" "Object.defineProperty(o, 'a', {set:()=>1});" "var d = Object.getOwnPropertyDescriptor(o, 'a'); " "[d.value, d.writable, d.enumerable, d.configurable, d.get, d.set]"), njs_str(",,false,true,,[object Function]") }, { njs_str("var o = {}; Object.defineProperty(o, 'a', {get: ()=>1, configurable:true}); " "Object.defineProperty(o, 'a', {value:123}); o.a =2"), njs_str("TypeError: Cannot assign to read-only property \"a\" of object") }, { njs_str("var o = {}; Object.defineProperty(o, 'a', {get: ()=>1, configurable:true}); " "Object.defineProperty(o, 'a', {writable:false}); o.a"), njs_str("undefined") }, { njs_str("var o = {}; Object.defineProperty(o, 'a', {value: 1, configurable:true}); " "Object.defineProperty(o, 'a', {get:()=>1}); o.a = 2"), njs_str("TypeError: Cannot set property \"a\" of object which has only a getter") }, { njs_str("var o = Object.create(Object.defineProperty({}, 'x', { set: function(v) { this.y = v; }})); " "o.x = 123; Object.getOwnPropertyDescriptor(o, 'y').value"), njs_str("123") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', { configurable: true, value: 0 });" "Object.defineProperty(o, 'a', { value: 1 });" "Object.defineProperty(o, 'a', { configurable: false, value: 2 }).a"), njs_str("2") }, { njs_str("var o = {}; Object.defineProperty()"), njs_str("TypeError: Object.defineProperty is called on non-object") }, { njs_str("var o = {}; Object.defineProperty(o)"), njs_str("TypeError: descriptor is not an object") }, { njs_str("Object.defineProperty(Function.prototype, 'name', {value:'x'}).name"), njs_str("x") }, { njs_str("Object.defineProperty(Function.prototype, 'xxx', {value:'x'}).xxx"), njs_str("x") }, { njs_str("Object.defineProperty(Object, 'name', {value:'x'}).name"), njs_str("x") }, { njs_str("Object.defineProperty(Object.prototype, 'toString', {value:1}).toString"), njs_str("1") }, { njs_str("var o = Object.defineProperties({}, {a:{value:1}}); o.a"), njs_str("1") }, { njs_str("var o = Object.defineProperties({}, {a:{enumerable:true}, b:{enumerable:true}});" "Object.keys(o)"), njs_str("a,b") }, { njs_str("var desc = Object.defineProperty({b:{value:1, enumerable:true}}, 'a', {});" "var o = Object.defineProperties({}, desc);" "Object.keys(o)"), njs_str("b") }, { njs_str("var o = Object.defineProperties({}, { get x() { return { value: 1 }; } });" "Object.getOwnPropertyDescriptor(o, 'x').value"), njs_str("1") }, { njs_str("Object.defineProperties({}, { get x() { return 1; } })"), njs_str("TypeError: property descriptor must be an object") }, { njs_str("var obj = {}; var desc = {value:NaN}; Object.defineProperty(obj, 'foo', desc); " "Object.defineProperties(obj, { foo: desc } ).foo"), njs_str("NaN") }, { njs_str("var obj = {}; var desc = {value:-0}; Object.defineProperty(obj, 'foo', desc); " "Object.defineProperties(obj, { foo: desc } ).foo"), njs_str("-0") }, { njs_str("var obj = {}; var desc = {value:-0}; Object.defineProperty(obj, 'foo', {value:0}); " "Object.defineProperties(obj, { foo: desc } ).foo"), njs_str("TypeError: Cannot redefine property: \"foo\"") }, { njs_str("var obj = {}; var desc = {value:0}; Object.defineProperty(obj, 'foo', {value:-0}); " "Object.defineProperties(obj, { foo: desc } ).foo"), njs_str("TypeError: Cannot redefine property: \"foo\"") }, { njs_str("var descs = {a:{value:1}}; Object.defineProperty(descs, 'b', {value:{value:2}});" "var o = Object.defineProperties({}, descs);" "njs.dump([o.a, o.b])"), njs_str("[1,undefined]") }, { njs_str("var descs = {a:{value:1}}; Object.defineProperty(descs, 'b', {value:{value:2}, enumerable:true});" "var o = Object.defineProperties({}, descs);" "njs.dump([o.a, o.b])"), njs_str("[1,2]") }, { njs_str("var o = {a:1}; delete o.a;" "Object.defineProperty(o, 'a', { value: 1 }); o.a"), njs_str("1") }, { njs_str("var o = {a:1}; delete o.a;" "Object.defineProperty(o, 'a', { value: 1 }); o.a = 2; o.a"), njs_str("TypeError: Cannot assign to read-only property \"a\" of object") }, { njs_str("var o = {a:1}; delete o.a;" "Object.defineProperty(o, 'a', { value: 1, writable:1 }); o.a = 2; o.a"), njs_str("2") }, { njs_str("var o = {};" "Object.defineProperty(o, new String('a'), { value: 1}); o.a"), njs_str("1") }, { njs_str("var o = {};" "Object.defineProperty(o, {toString:function(){return 'a'}}, { value: 1}); o.a"), njs_str("1") }, { njs_str("var a = [1, 2];" "Object.defineProperty(a, '1', {value: 5}); a[1]"), njs_str("5") }, { njs_str("var a = [1, 2];" "Object.defineProperty(a, '3', {}); njs.dump(a)"), njs_str("[1,2,,undefined]") }, { njs_str("var a = [1, 2];" "Object.defineProperty(a, 'length', {}); a"), njs_str("1,2") }, { njs_str("var a = [1, 2];" "Object.defineProperty(a, 'length', {value: 1}); a"), njs_str("1") }, { njs_str("var a = [1, 2];" "Object.defineProperty(a, 'length', {value: 5}); a"), njs_str("1,2,,,") }, { njs_str("var o = Object.defineProperties({a:1}, {}); o.a"), njs_str("1") }, { njs_str("var arr = [0, 1]; Object.defineProperty(arr, 'length', {value:3}); arr.length"), njs_str("3") }, { njs_str("Object.defineProperty(Array.prototype, 'toString', { get: function() {return () => 1}});" "'a' + []"), njs_str("a1") }, { njs_str("Object.defineProperty(Array.prototype, 'toJSON', { get: function() {return () => 1}});" "JSON.stringify([])"), njs_str("1") }, { njs_str("Object.defineProperty(Array.prototype, 'join', { get: function() {return () => 1}});" "([]).toString()"), njs_str("1") }, { njs_str("var o = {}, desc = {};" "Object.defineProperty(desc, 'get', { get() { return () => 1 } });" "Object.defineProperty(o, 'a', desc); o.a"), njs_str("1") }, { njs_str("Object.defineProperty(Error.prototype, 'message', { get() {return 'm'}});" "Object.defineProperty(Error.prototype, 'name', { get() {return 'n'}});" "Error()"), njs_str("n: m") }, { njs_str("var o = {}, desc = {};" "Object.defineProperty(desc, 'value', { get() { return 'x'}});" "Object.defineProperty(o, 'a', desc); o.a"), njs_str("x") }, { njs_str("var o = {}, desc = {};" "Object.defineProperty(desc, 'value', { get() { return 'x'}});" "Object.defineProperty(desc, 'enumerable', { get() { return !NaN}});" "Object.defineProperty(desc, 'writable', { get() { return 'x'}});" "Object.defineProperty(desc, 'configurable', { get() { return 1}});" "Object.defineProperty(o, 'a', desc);" "var d = Object.getOwnPropertyDescriptor(o, 'a');" "d.enumerable && d.writable && d.configurable"), njs_str("true") }, { njs_str("const arr = [1,2];" "function f(arg) {" " const desc = {get: arg};" " Object.defineProperty(desc, 'set', desc);" " Object.defineProperty(arr, 1, desc);" "}" "f(f);" "njs.dump(arr)"), njs_str("[1,'[Getter]']") }, { njs_str("Object.defineProperties()"), njs_str("TypeError: Object.defineProperties is called on non-object") }, { njs_str("Object.defineProperties(1, {})"), njs_str("TypeError: Object.defineProperties is called on non-object") }, { njs_str("njs.dump(Object.defineProperties({}, 1))"), njs_str("{}") }, { njs_str("Object.defineProperties(Object.freeze({b:1}), {b:{value:1}}).b"), njs_str("1") }, { njs_str("Object.defineProperties(Object.freeze({b:1}), {b:{value:2}})"), njs_str("TypeError: Cannot redefine property: \"b\"") }, { njs_str("Object.defineProperties(Object.freeze({b:1}), {c:{value:1}})"), njs_str("TypeError: Cannot add property \"c\", object is not extensible") }, { njs_str("var o = {a:1}; o.hasOwnProperty('a')"), njs_str("true") }, { njs_str("var o = Object.create({a:2}); o.hasOwnProperty('a')"), njs_str("false") }, { njs_str("var o = {a:1}; o.hasOwnProperty('b')"), njs_str("false") }, { njs_str("var a = []; a.hasOwnProperty('0')"), njs_str("false") }, { njs_str("var a = [,,]; a.hasOwnProperty('0')"), njs_str("false") }, { njs_str("var a = [3,,]; a.hasOwnProperty('0')"), njs_str("true") }, { njs_str("var a = [,4]; a.hasOwnProperty('1')"), njs_str("true") }, { njs_str("var a = [3,4]; a.hasOwnProperty('2')"), njs_str("false") }, { njs_str("var a = [3,4]; a.one = 1; a.hasOwnProperty('one')"), njs_str("true") }, { njs_str("var o = {a:1}; o.hasOwnProperty(o)"), njs_str("false") }, { njs_str("var o = {a:1}; o.hasOwnProperty(1)"), njs_str("false") }, { njs_str("var o = {a:1}; o.hasOwnProperty()"), njs_str("false") }, { njs_str("[,].hasOwnProperty()"), njs_str("false") }, { njs_str("[1,2].hasOwnProperty('len')"), njs_str("false") }, { njs_str("[1,2].hasOwnProperty('0')"), njs_str("true") }, { njs_str("[1,2].hasOwnProperty('2')"), njs_str("false") }, { njs_str("[].hasOwnProperty('length')"), njs_str("true") }, { njs_str("[1,2].hasOwnProperty('length')"), njs_str("true") }, { njs_str("(new Array()).hasOwnProperty('length')"), njs_str("true") }, { njs_str("(new Array(10)).hasOwnProperty('length')"), njs_str("true") }, { njs_str("Object.valueOf.hasOwnProperty()"), njs_str("false") }, { njs_str("1..hasOwnProperty('b')"), njs_str("false") }, { njs_str("'s'.hasOwnProperty('b')"), njs_str("false") }, { njs_str("'s'.hasOwnProperty('0')"), njs_str("true") }, { njs_str("'s'.hasOwnProperty('1')"), njs_str("false") }, { njs_str("Object.hasOwnProperty('hasOwnProperty')"), njs_str("false") }, { njs_str("Object.prototype.hasOwnProperty('hasOwnProperty')"), njs_str("true") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {get:undefined, set:undefined}).a"), njs_str("undefined") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {get:1})"), njs_str("TypeError: Getter must be a function") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {get:undefined}).a"), njs_str("undefined") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {get:function(){return 1}}).a"), njs_str("1") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {set:1})"), njs_str("TypeError: Setter must be a function") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {set:undefined}); o.a"), njs_str("undefined") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {set:undefined}); o.a = 4;"), njs_str("TypeError: Cannot set property \"a\" of object which has only a getter") }, { njs_str("var o = {a: 0};" "Object.defineProperty(o, 'b', {set:function(x){this.a = x / 2;}}); o.b = 4; o.a;"), njs_str("2") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {value:undefined});" "Object.defineProperty(o, 'a', {get:undefined})"), njs_str("TypeError: Cannot redefine property: \"a\"") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {});" "Object.defineProperty(o, 'a', {get:undefined})"), njs_str("TypeError: Cannot redefine property: \"a\"") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {get:undefined});" "Object.defineProperty(o, 'a', {get:undefined}).a"), njs_str("undefined") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {get:undefined});" "Object.defineProperty(o, 'a', {set:undefined}).a"), njs_str("undefined") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {get:undefined});" "Object.defineProperty(o, 'a', {}); o.a"), njs_str("undefined") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {get:undefined});" "Object.defineProperty(o, 'a', {set:function(){}})"), njs_str("TypeError: Cannot redefine property: \"a\"") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {get:undefined});" "Object.defineProperty(o, 'a', {get:function(){}})"), njs_str("TypeError: Cannot redefine property: \"a\"") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {get:()=>1, configurable:true});" "Object.defineProperty(o, 'a', {get:()=>2}); o.a"), njs_str("2") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {set:function(v){this.aa=v;}, configurable:true});" "Object.defineProperty(o, 'a', {get:function(){return this.aa}}); o.a = 1; o.a"), njs_str("1") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {get:()=>1, configurable:true});" "Object.defineProperty(o, 'a', {value:2}).a"), njs_str("2") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {get:()=>1, configurable:true});" "Object.defineProperty(o, 'a', {value:2});" "var d = Object.getOwnPropertyDescriptor(o, 'a');" "d.get === undefined && d.set === undefined"), njs_str("true") }, { njs_str("var o = {};" "Object.defineProperty(o, 'a', {value:1, configurable:true});" "Object.defineProperty(o, 'a', {get:()=>2});" "var d = Object.getOwnPropertyDescriptor(o, 'a');" "d.writable === undefined && d.value === undefined"), njs_str("true") }, { njs_str("Object.defineProperty(Date.prototype, 'year', {get: function() { return this.getFullYear();}});" "var d = new Date(0); d.year"), njs_str("1970") }, { njs_str("var o = [];" "Object.defineProperty(o, 'fill', {get:undefined}).fill"), njs_str("undefined") }, { njs_str("var o = [];" "Object.defineProperty(o, 'length', {get:undefined})"), njs_str("TypeError: Cannot redefine property: \"length\"") }, { njs_str("var o = {};" "Object.defineProperty(o, 'foo', {get:()=>10});" "Object.preventExtensions(o);Object.isFrozen(o)"), njs_str("true"), }, { njs_str("var o = {}; Object.defineProperty(o, 'r', { get: function() { return this.r_value; } }); o.r_value = 1; o.r;"), njs_str("1") }, { njs_str("var o = {}; Object.defineProperty(o, 'rw', { get: function() { return this.rw_value; }, set: function(x) { this.rw_value = x / 2; } }); o.rw = 10; o.rw"), njs_str("5") }, { njs_str("var o = {}; function foo() {}; Object.defineProperty(o, 'a', {get: foo});" "Object.getOwnPropertyDescriptor(o, 'a').get === foo"), njs_str("true") }, { njs_str("var o = {}; Object.defineProperty(o, 'a', {get: undefined});" "JSON.stringify(Object.getOwnPropertyDescriptor(o, 'a')).set"), njs_str("undefined") }, { njs_str("var a = []; Object.defineProperty(a, 4294967294, {value:100}); " "[a.hasOwnProperty('4294967294'), a.length, a[4294967294]]"), njs_str("true,4294967295,100") }, { njs_str("var a = []; Object.defineProperty(a, 'length', {value:4294967294}); " "a.length"), njs_str("4294967294") }, { njs_str("var a = []; Object.defineProperty(a, 'length', {value:4294967295}); " "a.length"), njs_str("4294967295") }, { njs_str("njs.dump(Object.defineProperty([], 'length', {value:4294967295}))"), njs_str("[<4294967295 empty items>]") }, { njs_str("var a = [1]; Object.defineProperty(a, '1', {get:()=>2}); " "[a[0], a[1], a.length]"), njs_str("1,2,2") }, { njs_str("var a = [1,2,3]; Object.defineProperty(a, '1', {configurable:false}); " "[a[0], a[1], a.length]"), njs_str("1,2,3") }, { njs_str("var a = [1,2,3]; Object.defineProperty(a, '1', {configurable:false}); " "delete a[1]"), njs_str("TypeError: Cannot delete property \"1\" of array") }, { njs_str("var a = [1,2,3]; Object.defineProperty(a, '1', {configurable:false}); " "a.length = 1"), njs_str("TypeError: Cannot delete property \"1\" of array") }, { njs_str("var a = [1,2,3]; Object.defineProperty(a, '1', {writable:false}); " "a[1]=4"), njs_str("TypeError: Cannot assign to read-only property \"1\" of array") }, { njs_str("var a = [1,2,3]; Object.defineProperty(a, '1', {enumerable:false}); " "njs.dump([Object.keys(a), Object.values(a), Object.entries(a)])"), njs_str("[['0','2'],[1,3],[['0',1],['2',3]]]") }, { njs_str("var a = [1]; Object.defineProperty(a, '1', {get:()=>2});" "Object.defineProperty(a, 'length', {value:1})"), njs_str("TypeError: Cannot delete property \"1\" of array") }, { njs_str("var a = [1]; Object.defineProperty(a, '1', {get:()=>2});" "Object.defineProperty(a, 'length', {enumerable:true})"), njs_str("TypeError: Cannot redefine property: \"length\"") }, { njs_str("var a = [1]; Object.defineProperty(a, '1', {get:()=>2, configurable:true});" "Object.defineProperty(a, 'length', {value:1}); a[1]"), njs_str("undefined") }, { njs_str("var a = [1,2,3]; Object.defineProperty(a, '1', {enumerable:false});" "Object.getOwnPropertyDescriptor(a, '1').enumerable"), njs_str("false") }, { njs_str("var args = (function(a, b, c) {return arguments;})(1,2,3); " "Object.defineProperty(args, \"length\", {value:6}); args.length"), njs_str("6") }, { njs_str("var get = 'get'; var o = { get }; o.get"), njs_str("get") }, { njs_str("var o = { get foo() { return 'bar'; } }; o.foo"), njs_str("bar") }, { njs_str("var o = { get get() { return 'bar'; } }; o.get"), njs_str("bar") }, { njs_str("var d = Object.getOwnPropertyDescriptor({ get a() { return 'bar'; } }, 'a');" "d.hasOwnProperty('set')"), njs_str("true") }, { njs_str("var o = { get() { return 'bar'; } }; o.get()"), njs_str("bar") }, { njs_str("var o = { get lazy() { delete this.lazy; return this.lazy = Math.pow(2,3)} };o.lazy"), njs_str("8") }, { njs_str("var o = { get lazy() { delete this.lazy; return this.lazy = Math.pow(2,3)} }; o.lazy;" "Object.getOwnPropertyDescriptor(o, 'lazy').value"), njs_str("8") }, { njs_str("var expr = 'foo'; var o = { get [expr]() { return 'bar'; } }; o.foo"), njs_str("bar") }, { njs_str("var o = { get [{toString(){return 'get'}}]() { return 'bar'; } }; o.get"), njs_str("bar") }, { njs_str("var o = { get [{toString(){return {} }}]() { return 'bar'; } }; o.get"), njs_str("InternalError: failed conversion of type \"object\" to string while property define") }, { njs_str("var o = { get foo(v1, v2) { return 'bar'; } }; o.foo"), njs_str("SyntaxError: Getter must not have any formal parameters in 1") }, { njs_str("var o = { baz: 'bar', set foo(v) { this.baz = v; } }; o.foo = 'baz'; o.baz"), njs_str("baz") }, { njs_str("var o = { baz: 'bar', set set(v) { this.baz = v; } }; o.set = 'baz'; o.baz"), njs_str("baz") }, { njs_str("var expr = 'foo'; var o = { baz: 'bar', set [expr](v) { this.baz = v; } }; o.foo = 'baz'; o.baz"), njs_str("baz") }, { njs_str("var o = { baz: 'bar', set foo(v1, v2) { this.baz = v; } }; o.foo = 'baz'; o.baz"), njs_str("SyntaxError: Setter must have exactly one formal parameter in 1") }, { njs_str("var o = { get foo() { return 'bar'; }, set foo(v) { this.baz = v; } }; o.foo"), njs_str("bar") }, { njs_str("var expr = 'foo'; var o = { get [expr]() { return 'bar'; }, set [expr](v) { this.baz = v; } }; o.foo"), njs_str("bar") }, { njs_str("Object.getOwnPropertyDescriptor({get foo() {}}, 'foo').enumerable"), njs_str("true") }, { njs_str("Object.getOwnPropertyDescriptor({get foo() {}}, 'foo').configurable"), njs_str("true") }, { njs_str("var p = { a:5 }; var o = Object.create(p);" "Object.getPrototypeOf(o) === p"), njs_str("true") }, { njs_str("var p = { a:5 }; var o = Object.create(p);" "Object.getPrototypeOf(o) === o.__proto__"), njs_str("true") }, { njs_str("var o = Object.create(Object.prototype);" "Object.getPrototypeOf(o) === Object.prototype"), njs_str("true") }, { njs_str("[true, 42, '', Symbol()]" ".every((x) => Object.getPrototypeOf(x) == Object.getPrototypeOf(Object(x)))"), njs_str("true") }, /* Object.setPrototypeOf() */ { njs_str("Object.setPrototypeOf()"), njs_str("TypeError: cannot convert undefined argument to object") }, { njs_str("Object.setPrototypeOf(null)"), njs_str("TypeError: cannot convert null argument to object") }, { njs_str("Object.setPrototypeOf({})"), njs_str("TypeError: prototype may only be an object or null: undefined") }, { njs_str("Object.setPrototypeOf(Object.preventExtensions({}))"), njs_str("TypeError: prototype may only be an object or null: undefined") }, { njs_str("[true, 42, '', Symbol()]" ".every((x) => Object.setPrototypeOf(x, {}) === x)"), njs_str("true") }, { njs_str("Object.setPrototypeOf(Object.preventExtensions({}), {})"), njs_str("TypeError: Cannot set property \"prototype\", object is not extensible") }, { njs_str("var p = {}, o = Object.create(p); Object.setPrototypeOf(p, o)"), njs_str("TypeError: Cyclic __proto__ value") }, { njs_str("var p = {}, o = {}; Object.setPrototypeOf(o, p);" "p.isPrototypeOf(o)"), njs_str("true") }, { njs_str("var p = {}, o = Object.create(p); Object.setPrototypeOf(o, null);" "Object.getPrototypeOf(o)"), njs_str("null") }, { njs_str("typeof Object.setPrototypeOf({}, null)"), njs_str("object") }, { njs_str("Object.setPrototypeOf(Object.getPrototypeOf(''), null).__proto__"), njs_str("undefined") }, { njs_str("var p = {}; var o = Object.create(p);" "p.isPrototypeOf(o)"), njs_str("true") }, { njs_str("var pp = {}; var p = Object.create(pp);" "var o = Object.create(p);" "pp.isPrototypeOf(o)"), njs_str("true") }, { njs_str("var p = {}; var o = Object.create(p);" "o.isPrototypeOf(p)"), njs_str("false") }, { njs_str("var p = {}; var o = Object.create(p);" "o.isPrototypeOf()"), njs_str("false") }, { njs_str("Object.valueOf.isPrototypeOf()"), njs_str("false") }, { njs_str("var p = {}; var o = Object.create(p);" "o.isPrototypeOf(1)"), njs_str("false") }, { njs_str("var p = {}; var o = Object.create(p);" "1..isPrototypeOf(p)"), njs_str("false") }, { njs_str("Object.create(new String('asdf')).length"), njs_str("4") }, { njs_str("Object.create(Object('123')).length"), njs_str("3") }, { njs_str("Object.create([1,2]).length"), njs_str("2") }, { njs_str("Object.create(function(a,b,c){}).length"), njs_str("3") }, { njs_str("Object.create(Math).hasOwnProperty('abs')"), njs_str("false") }, { njs_str("var m = Object.create(Math); m.abs = 3;" "[m.hasOwnProperty('abs'), m.abs]"), njs_str("true,3") }, { njs_str("var m = Object.create(Math); m.abs = Math.floor;" "[m.hasOwnProperty('abs'), delete m.abs, m.abs(-1)]"), njs_str("true,true,1") }, { njs_str("var Q = Object.create({}, {a: {value: 'AAA'}, [Symbol.toStringTag]:{value: 'TAG'}});" "Q[Symbol.toStringTag]"), njs_str("TAG") }, { njs_str("Object.getOwnPropertyDescriptor({a:1}, 'a').value"), njs_str("1") }, { njs_str("Object.getOwnPropertyDescriptor({a:1}, 'a').configurable"), njs_str("true") }, { njs_str("Object.getOwnPropertyDescriptor({a:1}, 'a').enumerable"), njs_str("true") }, { njs_str("Object.getOwnPropertyDescriptor({a:1}, 'a').writable"), njs_str("true") }, { njs_str("Object.getOwnPropertyDescriptor({a:1}, 'b')"), njs_str("undefined") }, { njs_str("Object.getOwnPropertyDescriptor({}, 'a')"), njs_str("undefined") }, { njs_str("Object.getOwnPropertyDescriptor(Object.create({a:1}), 'a')"), njs_str("undefined") }, { njs_str("Object.getOwnPropertyDescriptor([3,4], '1').value"), njs_str("4") }, { njs_str("Object.getOwnPropertyDescriptor([3,4], 1).value"), njs_str("4") }, { njs_str("Object.getOwnPropertyDescriptor([], 'length').value"), njs_str("0") }, { njs_str("Object.getOwnPropertyDescriptor([], '0')"), njs_str("undefined") }, { njs_str("Object.getOwnPropertyDescriptor([1,2], '1').value"), njs_str("2") }, { njs_str("Object.getOwnPropertyDescriptor([1,2], new String('1')).value"), njs_str("2") }, { njs_str("Object.getOwnPropertyDescriptor({undefined:1}, void 0).value"), njs_str("1") }, { njs_str("Object.getOwnPropertyDescriptor([1,2], 1).value"), njs_str("2") }, { njs_str("Object.getOwnPropertyDescriptor([1,,,3], '1')"), njs_str("undefined") }, { njs_str("Object.getOwnPropertyDescriptor([1,2], '3')"), njs_str("undefined") }, { njs_str("JSON.stringify(Object.getOwnPropertyDescriptor([3,4], 'length'))"), njs_str("{\"value\":2,\"writable\":true,\"enumerable\":false,\"configurable\":false}") }, { njs_str("Object.getOwnPropertyDescriptor(Array.of, 'length').value"), njs_str("0") }, { njs_str("Object.getOwnPropertyDescriptor('αβγδ', '1').value"), njs_str("β") }, { njs_str("Object.getOwnPropertyDescriptor(new String('αβγδ'), '1').value"), njs_str("β") }, { njs_str("var s = new String('αβγδ'); s.a = 1;" "Object.getOwnPropertyDescriptor(s, 'a').value"), njs_str("1") }, { njs_str("JSON.stringify(Object.getOwnPropertyDescriptor('αβγδ', '2'))"), njs_str("{\"value\":\"γ\",\"writable\":false,\"enumerable\":true,\"configurable\":false}") }, { njs_str("JSON.stringify(Object.getOwnPropertyDescriptor(new String('abc'), 'length'))"), njs_str("{\"value\":3,\"writable\":false,\"enumerable\":false,\"configurable\":false}") }, { njs_str("Object.getOwnPropertyDescriptor(1, '0')"), njs_str("undefined") }, { njs_str("'αβγδ'.propertyIsEnumerable('0')"), njs_str("true") }, { njs_str("({a:1}).propertyIsEnumerable({toString:function () {return 'a';}})"), njs_str("true") }, { njs_str("'αβγδ'.propertyIsEnumerable('a')"), njs_str("false") }, { njs_str("'αβγδ'.propertyIsEnumerable('length')"), njs_str("false") }, { njs_str("var min = Object.getOwnPropertyDescriptor(Math, 'min').value;" "[min(1,2), min(2,1), min(-1,1)]"), njs_str("1,1,-1") }, { njs_str("Object.getOwnPropertyDescriptor()"), njs_str("TypeError: cannot convert undefined argument to object") }, { njs_str("Object.getOwnPropertyDescriptor(undefined)"), njs_str("TypeError: cannot convert undefined argument to object") }, { njs_str("var o = {}; o[void 0] = 'a'; Object.getOwnPropertyDescriptor(o).value"), njs_str("a") }, { njs_str("var o = {}; o[void 0] = 'a'; Object.getOwnPropertyDescriptor(o, undefined).value"), njs_str("a") }, { njs_str("Object.getOwnPropertyDescriptors()"), njs_str("TypeError: cannot convert undefined argument to object") }, { njs_str("typeof Object.getOwnPropertyDescriptors(1)"), njs_str("object") }, { njs_str("Object.keys(Object.getOwnPropertyDescriptors([]))"), njs_str("length") }, { njs_str("Object.getOwnPropertyDescriptors(function(a,b,c) {}).length.value"), njs_str("3") }, { njs_str("Object.values(Object.getOwnPropertyDescriptors('abc'))" ".reduce(function(a, x) { return a += x.value; }, '')"), njs_str("abc3") }, { njs_str("Object.getOwnPropertyNames()"), njs_str("TypeError: cannot convert undefined argument to object") }, { njs_str("Array.isArray(Object.getOwnPropertyNames({}))"), njs_str("true") }, { njs_str("Object.getOwnPropertyNames({a:1, b:1, c:1})"), njs_str("a,b,c") }, { njs_str("Object.getOwnPropertyNames(Object.defineProperty({a:1}, 'b', {}))"), njs_str("a,b") }, { njs_str("Object.getOwnPropertyNames(Object.defineProperty([], 'b', {}))"), njs_str("length,b") }, { njs_str("Object.getOwnPropertyNames(Object.defineProperty(new String(), 'b', {}))"), njs_str("length,b") }, { njs_str("Object.getOwnPropertyNames([1,2,3])"), njs_str("0,1,2,length") }, { njs_str("Object.getOwnPropertyNames('abc')"), njs_str("0,1,2,length") }, { njs_str("Object.getOwnPropertyNames(function() {})"), njs_str("length,name,prototype") }, { njs_str("Object.getOwnPropertyNames(Array)"), njs_str("length,name,prototype,from,isArray,of") }, { njs_str("Object.getOwnPropertyNames(Array.isArray)"), njs_str("length,name") }, { njs_str("var not_arr_ind_1st = 0xFFFFFFFF, not_arr_ind_2nd = not_arr_ind_1st + 1," "arr_ind_last = not_arr_ind_1st - 1, arr_ind_pre_last = not_arr_ind_1st - 2;" "var o = {};" "o[not_arr_ind_2nd] = 'not_arr_ind_2nd';" "o[not_arr_ind_1st] = 'not_arr_ind_1st';" "o[arr_ind_pre_last] = 'arr_ind_pre_last';" "o[arr_ind_last] = 'arr_ind_last';" "Object.entries(o)"), njs_str("4294967293,arr_ind_pre_last,4294967294,arr_ind_last," "4294967296,not_arr_ind_2nd,4294967295,not_arr_ind_1st") }, /* Object.freeze() */ { njs_str("[undefined, null, false, NaN, '', Symbol()]" ".every((x) => Object.is(Object.freeze(x), x))"), njs_str("true") }, { njs_str("var buf = new ArrayBuffer(8);" NJS_TYPED_ARRAY_LIST ".every((ctr) => {Object.freeze(new ctr([])); " " Object.freeze(new ctr(buf, 8)); return true; })"), njs_str("true") }, { njs_str("var buf = new ArrayBuffer(8);" NJS_TYPED_ARRAY_LIST ".map((ctr) => { try { Object.freeze(new ctr(buf)); } catch(e) { return e; } })" ".every((x) => x instanceof TypeError)"), njs_str("true") }, { njs_str("Object.freeze([1]).pop()"), njs_str("TypeError: Cannot delete property \"0\" of array") }, { njs_str("var a = Object.freeze([1]); a[0] = 2;"), njs_str("TypeError: Cannot assign to read-only property \"0\" of array") }, { njs_str("var a = Object.freeze([1]); a[1] = 2;"), njs_str("TypeError: Cannot add property \"1\", object is not extensible") }, { njs_str("var a = Object.freeze([1,,3]); a[1] = 2;"), njs_str("TypeError: Cannot add property \"1\", object is not extensible") }, { njs_str("var o = { a: 1 }; delete o.a; Object.freeze(o).a = 2;"), njs_str("TypeError: Cannot add property \"a\", object is not extensible") }, { njs_str("Object.defineProperty(Object.freeze({}), 'b', {})"), njs_str("TypeError: Cannot add property \"b\", object is not extensible") }, { njs_str("Object.defineProperties(Object.freeze({}), {b:{}})"), njs_str("TypeError: Cannot add property \"b\", object is not extensible") }, { njs_str("Object.freeze()"), njs_str("undefined") }, { njs_str("var o = Object.freeze({a:1}); o.a = 2"), njs_str("TypeError: Cannot assign to read-only property \"a\" of object") }, { njs_str("var o = Object.freeze({a:1}); delete o.a"), njs_str("TypeError: Cannot delete property \"a\" of object") }, { njs_str("var o = Object.freeze({a:1}); o.b = 1; o.b"), njs_str("TypeError: Cannot add property \"b\", object is not extensible") }, { njs_str("var o = Object.freeze(Object.create({a:1})); o.a = 2; o.a"), njs_str("TypeError: Cannot add property \"a\", object is not extensible") }, { njs_str("var o = Object.freeze({a:{b:1}}); o.a.b = 2; o.a.b"), njs_str("2") }, { njs_str("Object.defineProperty([1,2], 'a', {value:1}).a"), njs_str("1") }, { njs_str("var a = []; a[0] = 101; Object.defineProperty(a, 0, {});" "a[0]"), njs_str("101") }, { njs_str("var a = Object.freeze([1,2]);" "Object.defineProperty(a, 'a', {value:1}).a"), njs_str("TypeError: Cannot add property \"a\", object is not extensible") }, { njs_str("var a = [1,2]; a.a = 1; Object.freeze(a); delete a.a"), njs_str("TypeError: Cannot delete property \"a\" of array") }, { njs_str("var a = [1,2]; a.a = 1; Object.freeze(a); a.a = 2"), njs_str("TypeError: Cannot assign to read-only property \"a\" of array") }, { njs_str("var a = Object.freeze([1,2]); a.a = 1"), njs_str("TypeError: Cannot add property \"a\", object is not extensible") }, { njs_str("Object.defineProperty(function() {}, 'a', {value:1}).a"), njs_str("1") }, { njs_str("var f = Object.freeze(function() {});" "Object.defineProperty(f, 'a', {value:1}).a"), njs_str("TypeError: Cannot add property \"a\", object is not extensible") }, { njs_str("var f = function() {}; f.a = 1; Object.freeze(f); delete f.a"), njs_str("TypeError: Cannot delete property \"a\" of function") }, { njs_str("var f = function() {}; f.a = 1; Object.freeze(f); f.a = 2"), njs_str("TypeError: Cannot assign to read-only property \"a\" of function") }, { njs_str("var f = Object.freeze(function() {}); f.a = 1"), njs_str("TypeError: Cannot add property \"a\", object is not extensible") }, { njs_str("Object.defineProperty(new Date(''), 'a', {value:1}).a"), njs_str("1") }, { njs_str("var d = Object.freeze(new Date(''));" "Object.defineProperty(d, 'a', {value:1}).a"), njs_str("TypeError: Cannot add property \"a\", object is not extensible") }, { njs_str("var d = new Date(''); d.a = 1; Object.freeze(d);" "delete d.a"), njs_str("TypeError: Cannot delete property \"a\" of date") }, { njs_str("var d = new Date(''); d.a = 1; Object.freeze(d); d.a = 2"), njs_str("TypeError: Cannot assign to read-only property \"a\" of date") }, { njs_str("var d = Object.freeze(new Date('')); d.a = 1"), njs_str("TypeError: Cannot add property \"a\", object is not extensible") }, { njs_str("Object.defineProperty(new RegExp(''), 'a', {value:1}).a"), njs_str("1") }, { njs_str("var r = Object.freeze(new RegExp(''));" "Object.defineProperty(r, 'a', {value:1}).a"), njs_str("TypeError: Cannot add property \"a\", object is not extensible") }, { njs_str("var r = new RegExp(''); r.a = 1; Object.freeze(r); delete r.a"), njs_str("TypeError: Cannot delete property \"a\" of regexp") }, { njs_str("var r = new RegExp(''); r.a = 1; Object.freeze(r); r.a = 2"), njs_str("TypeError: Cannot assign to read-only property \"a\" of regexp") }, { njs_str("var r = Object.freeze(new RegExp('')); r.a = 1"), njs_str("TypeError: Cannot add property \"a\", object is not extensible") }, { njs_str("var o = Object.freeze({ get x() { return 10; } }); o.x"), njs_str("10") }, { njs_str("var o = Object.freeze({ get x() { return 10; } });" "Object.getOwnPropertyDescriptors(o).x.get"), njs_str("[object Function]") }, { njs_str("var o = Object.freeze({ get x() { return 10; } });" "Object.getOwnPropertyDescriptor(o, 'x').writable"), njs_str("undefined") }, /* Object.isFrozen() */ { njs_str("[undefined, null, false, NaN, '', Symbol()]" ".every((x) => Object.isFrozen(x))"), njs_str("true") }, { njs_str("[[], {}]" ".every((x) => Object.isFrozen(Object.preventExtensions(x)))"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every((ctr) => !Object.isFrozen(new ctr([])))"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every((ctr) => Object.isFrozen(Object.preventExtensions(new ctr([]))))"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".map((ctr) => new ctr([]))" ".map((x) => { x.broken = true; return x; })" ".every((x) => !Object.isFrozen(Object.preventExtensions(x)))"), njs_str("true") }, { njs_str("var buf = new ArrayBuffer(8);" NJS_TYPED_ARRAY_LIST ".every((ctr) => !Object.isFrozen(Object.preventExtensions(new ctr(buf))))"), njs_str("true") }, { njs_str("[{a:1}, [1,2], function() {}, new Date(''), new RegExp('')]" ".every((x) => !Object.isFrozen(x))"), njs_str("true") }, { njs_str("Object.isFrozen()"), njs_str("true") }, { njs_str("Object.isFrozen(Object.defineProperties({}, {a:{value:1}}))"), njs_str("false") }, { njs_str("var o = Object.defineProperties({}, {a:{}, b:{}});" "o = Object.preventExtensions(o);" "Object.isFrozen(o)"), njs_str("true") }, { njs_str("var o = Object.defineProperties({}, {a:{}, b:{writable:1}});" "o = Object.preventExtensions(o);" "Object.isFrozen(o)"), njs_str("false") }, { njs_str("var o = Object.defineProperties({}, {a:{writable:1}});" "o = Object.preventExtensions(o);" "Object.isFrozen(o)"), njs_str("false") }, { njs_str("var o = Object.defineProperties({}, {a:{configurable:1}});" "o = Object.preventExtensions(o);" "Object.isFrozen(o)"), njs_str("false") }, { njs_str("var o = Object.preventExtensions({a:1});" "Object.isFrozen(o)"), njs_str("false") }, { njs_str("var o = Object.freeze({a:1}); Object.isFrozen(o)"), njs_str("true") }, /* Object.seal() */ { njs_str("[undefined, null, false, NaN, '', Symbol()]" ".every((x) => Object.is(Object.seal(x), x))"), njs_str("true") }, { njs_str("Object.seal()"), njs_str("undefined") }, { njs_str("Object.seal([1]).pop()"), njs_str("TypeError: Cannot delete property \"0\" of array") }, { njs_str("var a = Object.seal([1]); a[0] = 2; a"), njs_str("2") }, { njs_str("var a = Object.seal([1]); a[1] = 2;"), njs_str("TypeError: Cannot add property \"1\", object is not extensible") }, { njs_str("var a = Object.seal([1,,3]); a[1] = 2;"), njs_str("TypeError: Cannot add property \"1\", object is not extensible") }, { njs_str("var o = { a: 1 }; delete o.a; Object.seal(o).a = 2"), njs_str("TypeError: Cannot add property \"a\", object is not extensible") }, { njs_str("var o = Object.seal({a:1}); o.a = 2; o.a"), njs_str("2") }, { njs_str("var o = Object.seal({a:1}); delete o.a"), njs_str("TypeError: Cannot delete property \"a\" of object") }, { njs_str("var o = Object.seal({a:1}); o.b = 1; o.b"), njs_str("TypeError: Cannot add property \"b\", object is not extensible") }, { njs_str("var o = Object.seal(Object.create({a:1})); o.a = 2; o.a"), njs_str("TypeError: Cannot add property \"a\", object is not extensible") }, { njs_str("var o = Object.seal({a:{b:1}}); o.a.b = 2; o.a.b"), njs_str("2") }, /* Object.isSealed() */ { njs_str("[undefined, null, false, NaN, '', Symbol()]" ".every((x) => Object.isSealed(x))"), njs_str("true") }, { njs_str("[[], {}]" ".every((x) => Object.isSealed(Object.preventExtensions(x)))"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every((ctr) => !Object.isSealed(new ctr([])))"), njs_str("true") }, { njs_str(NJS_TYPED_ARRAY_LIST ".every((ctr) => Object.isSealed(Object.preventExtensions(new ctr([]))))"), njs_str("true") }, { njs_str("var buf = new ArrayBuffer(8);" NJS_TYPED_ARRAY_LIST ".every((ctr) => Object.isSealed(Object.preventExtensions(new ctr(buf))))"), njs_str("true") }, { njs_str("var buf = new ArrayBuffer(8);" NJS_TYPED_ARRAY_LIST ".map((ctr) => new ctr(buf))" ".map((x) => { x.broken = true; return x; })" ".every((x) => !Object.isSealed(Object.preventExtensions(x)))"), njs_str("true") }, { njs_str("[{a:1}, [1,2], function() {}, new Date(''), new RegExp('')]" ".every((x) => !Object.isSealed(x))"), njs_str("true") }, { njs_str("Object.isSealed()"), njs_str("true") }, { njs_str("Object.isSealed(Object.defineProperties({}, {a:{value:1}}))"), njs_str("false") }, { njs_str("var o = Object.defineProperties({}, {a:{}, b:{}});" "o = Object.preventExtensions(o);" "Object.isSealed(o)"), njs_str("true") }, { njs_str("var o = Object.defineProperties({}, {a:{}, b:{writable:1}});" "o = Object.preventExtensions(o);" "Object.isSealed(o)"), njs_str("true") }, { njs_str("var o = Object.defineProperties({}, {a:{writable:1}});" "o = Object.preventExtensions(o);" "Object.isSealed(o)"), njs_str("true") }, { njs_str("var o = Object.defineProperties({}, {a:{configurable:1}});" "o = Object.preventExtensions(o);" "Object.isSealed(o)"), njs_str("false") }, { njs_str("var o = Object.preventExtensions({a:1});" "Object.isFrozen(o)"), njs_str("false") }, { njs_str("var o = Object.freeze({a:1}); Object.isFrozen(o)"), njs_str("true") }, /* Object.preventExtensions() */ { njs_str("var o = Object.preventExtensions({a:1});" "Object.defineProperty(o, 'b', {value:1})"), njs_str("TypeError: Cannot add property \"b\", object is not extensible") }, { njs_str("var o = Object.preventExtensions({});" "Object.defineProperty(o, Symbol.unscopables, {})"), njs_str("TypeError: Cannot add property \"Symbol(Symbol.unscopables)\", object is not extensible") }, { njs_str("var o = Object.preventExtensions({a:1});" "Object.defineProperties(o, {b:{value:1}})"), njs_str("TypeError: Cannot add property \"b\", object is not extensible") }, { njs_str("var o = Object.preventExtensions({a:1}); o.a = 2; o.a"), njs_str("2") }, { njs_str("var o = Object.preventExtensions({a:1}); delete o.a; o.a"), njs_str("undefined") }, { njs_str("var o = Object.preventExtensions({a:1}); o.b = 1; o.b"), njs_str("TypeError: Cannot add property \"b\", object is not extensible") }, { njs_str("var o = Object.preventExtensions({a:1}); o[Symbol.unscopables] = 1"), njs_str("TypeError: Cannot add property \"Symbol(Symbol.unscopables)\", object is not extensible") }, { njs_str("var o = { a: 1 }; delete o.a; Object.preventExtensions(o).a = 1"), njs_str("TypeError: Cannot add property \"a\", object is not extensible") }, { njs_str("Object.preventExtensions()"), njs_str("undefined") }, { njs_str("Object.preventExtensions(1)"), njs_str("1") }, { njs_str("Object.preventExtensions('')"), njs_str("") }, { njs_str("Object.isExtensible({})"), njs_str("true") }, { njs_str("Object.isExtensible([])"), njs_str("true") }, { njs_str("var arrObj = [];Object.preventExtensions(arrObj); arrObj[1] = 1"), njs_str("TypeError: Cannot add property \"1\", object is not extensible") }, { njs_str("var arrObj = [1,2];Object.preventExtensions(arrObj); arrObj[1] = 1"), njs_str("1") }, { njs_str("Object.isExtensible(function() {})"), njs_str("true") }, { njs_str("Object.isExtensible(new Date(''))"), njs_str("true") }, { njs_str("Object.isExtensible(new RegExp(''))"), njs_str("true") }, { njs_str("Object.isExtensible()"), njs_str("false") }, { njs_str("Object.isExtensible(1)"), njs_str("false") }, { njs_str("Object.isExtensible('')"), njs_str("false") }, { njs_str("Object.isExtensible(Object.preventExtensions({}))"), njs_str("false") }, { njs_str("Object.isExtensible(Object.preventExtensions([]))"), njs_str("false") }, { njs_str("Object.isExtensible(Object.freeze({}))"), njs_str("false") }, { njs_str("Object.isExtensible(Object.freeze([]))"), njs_str("false") }, { njs_str("Object.isExtensible(undefined)"), njs_str("false") }, /* Object.is() */ { njs_str("typeof Object.is"), njs_str("function") }, { njs_str("Object.is.length == 2"), njs_str("true") }, { njs_str("Object.is()"), njs_str("true") }, { njs_str("[undefined, null, false, NaN, '', Symbol(), {}]" ".map((x) => Object.is(x, x))" ".every((x) => x === true)"), njs_str("true") }, { njs_str("[null, false, NaN, '', Symbol(), {}]" ".map((x) => Object.is(x) || Object.is(void 0, x))" ".every((x) => x === false)"), njs_str("true") }, { njs_str("[false, NaN, '', Symbol()]" ".map((x) => Object.is(Object(x), Object(x)))" ".every((x) => x === false)"), njs_str("true") }, { njs_str("Object.is(0, -0)"), njs_str("false") }, { njs_str("Object.is(0, null)"), njs_str("false") }, { njs_str("Object.is(42, '42')"), njs_str("false") }, { njs_str( "var fail;" "function isConfigurableMethods(o) {" " var except = [" " 'prototype'," " 'caller'," " 'arguments'," " 'description'," " ];" " return Object.getOwnPropertyNames(o)" " .filter(v => !except.includes(v)" " && typeof o[v] == 'function')" " .every(v => Object.getOwnPropertyDescriptor(o, v)" " .configurable" " || !(fail = `${o.name}.${v}`));" "}" "[" " Boolean, Boolean.prototype," " Number, Number.prototype," " Symbol, Symbol.prototype," " String, String.prototype," " Object, Object.prototype," " Array, Array.prototype," " Function, Function.prototype," " RegExp, RegExp.prototype," " Date, Date.prototype," " Error, Error.prototype," " Math," " JSON," "].every(obj => isConfigurableMethods(obj)) || fail"), njs_str("true") }, { njs_str( "var fail;" "function isWritableMethods(o) {" " var except = [" " 'prototype'," " 'caller'," " 'arguments'," " 'description'," " ];" " return Object.getOwnPropertyNames(o)" " .filter(v => !except.includes(v)" " && typeof o[v] == 'function')" " .every(v => Object.getOwnPropertyDescriptor(o, v)" " .writable" " || !(fail = `${o.name}.${v}`));" "}" "[" " Boolean, Boolean.prototype," " Number, Number.prototype," " Symbol, Symbol.prototype," " String, String.prototype," " Object, Object.prototype," " Array, Array.prototype," " Function, Function.prototype," " RegExp, RegExp.prototype," " Date, Date.prototype," " Error, Error.prototype," " Math," " JSON," "].every(obj => isWritableMethods(obj)) || fail"), njs_str("true") }, { njs_str("new Date(undefined)"), njs_str("Invalid Date") }, { njs_str("new Date(Infinity)"), njs_str("Invalid Date") }, { njs_str("new Date(NaN)"), njs_str("Invalid Date") }, { njs_str("new Date(1,undefined)"), njs_str("Invalid Date") }, { njs_str("new Date(1,Infinity)"), njs_str("Invalid Date") }, { njs_str("new Date(1,NaN)"), njs_str("Invalid Date") }, { njs_str("new Date(8.65e15)"), njs_str("Invalid Date") }, { njs_str("var d = new Date(1308895200000); new Date(d.getTime(), d.getTime())"), njs_str("Invalid Date") }, { njs_str("new Date(275760, 1, 2**61)"), njs_str("Invalid Date") }, { njs_str("new Date(275760, 1, 1, 2**61)"), njs_str("Invalid Date") }, { njs_str("new Date(275760, 1, 1, 1, 2**61)"), njs_str("Invalid Date") }, { njs_str("new Date(275760, 1, 1, 1, 1, 2**61)"), njs_str("Invalid Date") }, { njs_str("new Date(275760, 1, 1, 1, 1, 1, 2**61)"), njs_str("Invalid Date") }, { njs_str("njs.dump([new Date(8.65e15)])"), njs_str("[Invalid Date]") }, { njs_str("new Date(0e0.o0)"), njs_str("Invalid Date") }, { njs_str("(new Date(8.639e15)).getTime()"), njs_str("8639000000000000") }, { njs_str("new Date(8.641e15)"), njs_str("Invalid Date") }, { njs_str("(new Date(null)).getTime()"), njs_str("0") }, { njs_str("(new Date(86400)).getTime()"), njs_str("86400") }, { njs_str("Date().split(' ')[0] in {'Mon':1, 'Tue':1, 'Wed':1, 'Thu':1, 'Fri':1, 'Sat':1, 'Sun':1}"), njs_str("true") }, { njs_str("var d = new Date(''); d +' '+ d.getTime()"), njs_str("Invalid Date NaN") }, { njs_str("var d = new Date(1); d = d + ''; d.slice(0, 33)"), njs_str("Thu Jan 01 1970 00:00:00 GMT+0000") }, { njs_str("var d = new Date({valueOf:()=>86400000}); d = d + ''; d.slice(0, 33)"), njs_str("Fri Jan 02 1970 00:00:00 GMT+0000") }, { njs_str("(new Date({toString:()=>'2011'})).getTime()"), njs_str("1293840000000") }, { njs_str("(new Date({valueOf: ()=>86400, toString:()=>'2011'})).getTime()"), njs_str("86400") }, { njs_str("var d = new Date(1308895200000); d.getTime()"), njs_str("1308895200000") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45); d.getTime()"), njs_str("1308941100000") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45); d.valueOf()"), njs_str("1308941100000") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45);" "d.toString().slice(0, 33)"), njs_str("Fri Jun 24 2011 18:45:00 GMT+0000") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45); d.toDateString()"), njs_str("Fri Jun 24 2011") }, { njs_str("new Date(NaN).toDateString()"), njs_str("Invalid Date") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45);" "d.toTimeString().slice(0, 17)"), njs_str("18:45:00 GMT+0000") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45); d.toUTCString()"), njs_str("Fri, 24 Jun 2011 18:45:00 GMT") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45, 12, 625);" "d.toISOString()"), njs_str("2011-06-24T18:45:12.625Z") }, { njs_str("var d = new Date(1999, 9, 10, 10, 10, 10, 10);" "var local = new Date(d.getTime() - d.getTimezoneOffset() * 60000);" "local.toISOString()"), njs_str("1999-10-10T10:10:10.010Z") }, #if (NJS_TIME_T_SIZE == 8) { njs_str("[" "'-010000-01-01T00:00:00.000Z'," "'+010000-01-01T00:00:00.000Z'," "'0002-01-01T00:00:00.000Z'," "'0123-01-01T00:00:00.000Z'," "].every((iso)=> (new Date(iso)).toISOString() === iso)"), njs_str("true") }, { njs_str("new Date('0020-01-01T00:00:00Z').toUTCString()"), njs_str("Wed, 01 Jan 0020 00:00:00 GMT") }, { njs_str("new Date('0020-01-01T00:00:00Z').toString().slice(0, 15)"), njs_str("Wed Jan 01 0020") }, { njs_str("(new Date('-000001-07-01T00:00Z')).toUTCString()"), njs_str("Thu, 01 Jul -0001 00:00:00 GMT") }, { njs_str("(new Date('-000012-07-01T00:00Z')).toUTCString()"), njs_str("Fri, 01 Jul -0012 00:00:00 GMT") }, { njs_str("(new Date('-000123-07-01T00:00Z')).toUTCString()"), njs_str("Sun, 01 Jul -0123 00:00:00 GMT") }, { njs_str("(new Date('-001234-07-01T00:00Z')).toUTCString()"), njs_str("Fri, 01 Jul -1234 00:00:00 GMT") }, { njs_str("(new Date('-012345-07-01T00:00Z')).toUTCString()"), njs_str("Thu, 01 Jul -12345 00:00:00 GMT") }, { njs_str("new Date(NaN).toUTCString()"), njs_str("Invalid Date") }, { njs_str("var d = new Date(-62167219200000); d.toISOString()"), njs_str("0000-01-01T00:00:00.000Z") }, { njs_str("var d = new Date(-62135596800000); d.toISOString()"), njs_str("0001-01-01T00:00:00.000Z") }, { njs_str("var d = new Date(-62198755200000); d.toISOString()"), njs_str("-000001-01-01T00:00:00.000Z") }, { njs_str("new Date(NaN).toISOString()"), njs_str("RangeError: Invalid time value") }, #endif { njs_str("Date.UTC(2011, 5, 24, 6, 0)"), njs_str("1308895200000") }, { njs_str("Date.UTC({valueOf:()=>2011}, 5, 24, 6, 0)"), njs_str("1308895200000") }, { njs_str("Date.UTC()"), njs_str("NaN") }, { njs_str("Date.UTC(Infinity)"), njs_str("NaN") }, { njs_str("Date.UTC(Infinity, 0)"), njs_str("NaN") }, { njs_str("Date.UTC(1970)"), njs_str("0") }, { njs_str("Date.UTC(1968, 24)"), njs_str("0") }, { njs_str("[-1,0,1,99,100].map(yr => Date.UTC(yr))"), njs_str("-62198755200000,-2208988800000,-2177452800000,915148800000,-59011459200000") }, { njs_str("Date.UTC(1970.9, 0.9, 1.9, 0.9, 0.9, 0.9, 0.9)"), njs_str("0") }, { njs_str("Date.UTC(-1970.9, -0.9, -0.9, -0.9, -0.9, -0.9, -0.9)"), njs_str("-124334438400000") }, { njs_str("Date.UTC(275760, 8, 13, 0, 0, 0, 0)"), njs_str("8640000000000000") }, { njs_str("Date.UTC(275760, 8, 13, 0, 0, 0, 1)"), njs_str("NaN") }, { njs_str("Date.UTC(-271821, 3, 20, 0, 0, 0, 0)"), njs_str("-8640000000000000") }, { njs_str("Date.UTC(-271821, 3, 20, 0, 0, 0, -1)"), njs_str("NaN") }, { njs_str("Date.UTC(1970, 0)"), njs_str("0") }, { njs_str("Date.UTC(1970, 0, 0)"), njs_str("-86400000") }, { njs_str("Date.parse()"), njs_str("NaN") }, { njs_str("Date.parse('2011')"), njs_str("1293840000000") }, { njs_str("Date.parse('+002011')"), njs_str("1293840000000") }, { njs_str("Date.parse('2011-06')"), njs_str("1306886400000") }, { njs_str("Date.parse('2011-06-24')"), njs_str("1308873600000") }, { njs_str("Date.parse('2011-06-24T06')"), njs_str("NaN") }, { njs_str("Date.parse('2011-06-24T06:')"), njs_str("NaN") }, { njs_str("Date.parse('2011-06-24T06:01:')"), njs_str("NaN") }, { njs_str("Date.parse('2011-06-24T06:01Z')"), njs_str("1308895260000") }, { njs_str("Date.parse('2011-06-24T06:01:02:')"), njs_str("NaN") }, { njs_str("Date.parse('2011-06-24T06:01:02Z')"), njs_str("1308895262000") }, { njs_str("Date.parse('2011-06-24T06:01:02.Z')"), njs_str("NaN") }, { njs_str("Date.parse('2011-06-24T06:01:02.6Z')"), njs_str("1308895262600") }, { njs_str("Date.parse('2011-06-24T06:01:02.62Z')"), njs_str("1308895262620") }, { njs_str("Date.parse('2011-06-24T06:01:02:625Z')"), njs_str("NaN") }, { njs_str("Date.parse('2011-06-24T06:01:02.625Z')"), njs_str("1308895262625") }, { njs_str("Date.parse('2011-06-24T06:01:02.625+00:00')"), njs_str("1308895262625") }, { njs_str("Date.parse('2011-06-24T06:01:02.625+0000')"), njs_str("1308895262625") }, { njs_str("Date.parse('2011-06-24T06:01:02.625+00:0')"), njs_str("NaN") }, { njs_str("Date.parse('2011-06-24T06:01:02.625+00:')"), njs_str("NaN") }, { njs_str("Date.parse('2011-06-24T06:01:02.625+00')"), njs_str("NaN") }, { njs_str("Date.parse('2011-06-24T06:01:02.625+0')"), njs_str("NaN") }, { njs_str("Date.parse('2011-06-24T06:01:02.625+')"), njs_str("NaN") }, { njs_str("Date.parse('2011-06-24T06:01:02.625-01:15')"), njs_str("1308899762625") }, { njs_str("Date.parse('2011-06-24T06:01:02.625-01:60')"), njs_str("NaN") }, { njs_str("Date.parse('2011-06-24T06:01:02.625-25:59')"), njs_str("NaN") }, { njs_str("Date.parse('2011-06-24T06:01:02.625+01:15')"), njs_str("1308890762625") }, { njs_str("Date.parse('2011-06-24T06:01:02.625-23:59')"), njs_str("1308981602625") }, { njs_str("Date.parse('2011-06-24T06:01:02.625+23:59')"), njs_str("1308808922625") }, { njs_str("Date.parse('2011-06-24T06:01:02.6255Z')"), njs_str("1308895262625") }, { njs_str("Date.parse('2011-06-24T06:01:02.62555Z')"), njs_str("1308895262625") }, { njs_str("Date.parse('2011-06-24T06:01:02.625555Z')"), njs_str("1308895262625") }, { njs_str("Date.parse('2011-06-24T06:01:02.6255555Z')"), njs_str("1308895262625") }, { njs_str("Date.parse('2011-06-24T06:01:02.625555555Z')"), njs_str("1308895262625") }, { njs_str("Date.parse('2011-06-24T06:01:02.62555555599999Z')"), njs_str("1308895262625") }, { njs_str("Date.parse('2011-06-24T06:01:02.625555Z5')"), njs_str("NaN") }, { njs_str("var tzoffzet = new Date(0).getTimezoneOffset() * 60000;" "Date.parse('1970-01-01T00:00:00') == tzoffzet"), njs_str("true") }, { njs_str("Date.parse('1970-01-01')"), njs_str("0") }, { njs_str("var d = new Date(); var str = d.toISOString();" "var diff = Date.parse(str) - Date.parse(str.substring(0, str.length - 1));" "d.getTimezoneOffset() == -diff/1000/60"), njs_str("true") }, { njs_str("Date.parse('24 Jun 2011')"), njs_str("1308873600000") }, { njs_str("Date.parse('Fri, 24 Jun 2011 18:48')"), njs_str("1308941280000") }, { njs_str("Date.parse('Fri, 24 Jun 2011 18:48:02')"), njs_str("1308941282000") }, { njs_str("Date.parse('Fri, 24 Jun 2011 18:48:02 GMT')"), njs_str("1308941282000") }, { njs_str("Date.parse('Fri, 24 Jun 2011 18:48:02 +1245')"), njs_str("1308895382000") }, { njs_str("Date.parse('Jun 24 2011')"), njs_str("1308873600000") }, { njs_str("Date.parse('Fri Jun 24 2011 18:48')"), njs_str("1308941280000") }, { njs_str("Date.parse('Fri Jun 24 2011 18:48:02')"), njs_str("1308941282000") }, { njs_str("Date.parse('Fri Jun 24 2011 18:48:02 GMT+1245')"), njs_str("1308895382000") }, /* Jan 1, 1. */ { njs_str("Date.parse('+000001-01-01T00:00:00.000Z')"), njs_str("-62135596800000") }, /* Mar 2, 1 BCE. */ { njs_str("Date.parse('+000000-03-02T00:00:00.000Z')"), njs_str("-62161948800000") }, /* Mar 1, 1 BCE. */ { njs_str("Date.parse('+000000-03-01T00:00:00.000Z')"), njs_str("-62162035200000") }, /* Feb 29, 1 BCE. */ { njs_str("Date.parse('+000000-02-29T00:00:00.000Z')"), njs_str("-62162121600000") }, /* Feb 28, 1 BCE. */ { njs_str("Date.parse('+000000-02-28T00:00:00.000Z')"), njs_str("-62162208000000") }, /* Jan 1, 1 BCE. */ { njs_str("Date.parse('+000000-01-01T00:00:00.000Z')"), njs_str("-62167219200000") }, /* Jan 1, 2 BCE. */ { njs_str("Date.parse('-000001-01-01T00:00:00.000Z')"), njs_str("-62198755200000") }, { njs_str("var d = new Date(); d == Date.parse(d.toISOString())"), njs_str("true") }, { njs_str("var s = Date(); s === Date(Date.parse(s))"), njs_str("true") }, { njs_str("var n = Date.now(); n == new Date(n)"), njs_str("true") }, { njs_str("var d = new Date(2011,0); d.getFullYear()"), njs_str("2011") }, { njs_str("var d = new Date(2011, 0, 1, 0, 0, 0, -1); d.getFullYear()"), njs_str("2010") }, { njs_str("var d = new Date(2011, 11, 31, 23, 59, 59, 999); d.getFullYear()"), njs_str("2011") }, { njs_str("var d = new Date(2011, 11, 31, 23, 59, 59, 1000); d.getFullYear()"), njs_str("2012") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45); d.getFullYear()"), njs_str("2011") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45); d.getUTCFullYear()"), njs_str("2011") }, { njs_str("var d = new Date(2011, 5); d.getMonth()"), njs_str("5") }, { njs_str("var d = new Date(2011, 6, 0, 0, 0, 0, -1); d.getMonth()"), njs_str("5") }, { njs_str("var d = new Date(2011, 6, 31, 23, 59, 59, 999); d.getMonth()"), njs_str("6") }, { njs_str("var d = new Date(2011, 6, 31, 23, 59, 59, 1000); d.getMonth()"), njs_str("7") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45); d.getMonth()"), njs_str("5") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45); d.getUTCMonth()"), njs_str("5") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45); d.getDate()"), njs_str("24") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45); d.getUTCDate()"), njs_str("24") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45); d.getDay()"), njs_str("5") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45); d.getUTCDay()"), njs_str("5") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45); d.getHours()"), njs_str("18") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45); d.getUTCHours()"), njs_str("18") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45); d.getMinutes()"), njs_str("45") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45); d.getUTCMinutes()"), njs_str("45") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45, 12);" "d.getSeconds()"), njs_str("12") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45, 12);" "d.getUTCSeconds()"), njs_str("12") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45, 12, 625);" "d.getMilliseconds()"), njs_str("625") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45, 12, 625);" "d.getUTCMilliseconds()"), njs_str("625") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45, 12, 625);" "d.getTimezoneOffset()"), njs_str("0") }, { njs_str("var d = new Date(); d.setTime(1308895200000); d.getTime()"), njs_str("1308895200000") }, { njs_str("var d = new Date(); d.setTime(); d.getTime()"), njs_str("NaN") }, { njs_str("var d = new Date(); d.setTime(Infinity); d.getTime()"), njs_str("NaN") }, { njs_str("var d = new Date(); d.setTime(8.64e15 +1); d.getTime()"), njs_str("NaN") }, { njs_str("var d = new Date(NaN); d.setTime(0); d.getTime()"), njs_str("0") }, { njs_str("var d = new Date(); d.setTime(8.64e15); d.getTime()"), njs_str("8640000000000000") }, { njs_str("var d = new Date(1308895201625); d.setMilliseconds(5003);" "d.getTime()"), njs_str("1308895206003") }, { njs_str("var d = new Date(1308895201625); d.setSeconds(2, 5003);" "d.getTime()"), njs_str("1308895207003") }, { njs_str("var d = new Date(1308895201625); d.setSeconds(2);" "d.getTime()"), njs_str("1308895202625") }, { njs_str("var d = new Date(1308895323625); d.setMinutes(3, 2, 5003);" "d.getTime()"), njs_str("1308895387003") }, { njs_str("var d = new Date(1308895323625); d.setMinutes(3, 2);" "d.getTime()"), njs_str("1308895382625") }, { njs_str("var d = new Date(1308895323625); d.setMinutes(3);" "d.getTime()"), njs_str("1308895383625") }, { njs_str("var d = new Date(1308895323625); d.setUTCMinutes(3, 2, 5003);" "d.getTime()"), njs_str("1308895387003") }, { njs_str("var d = new Date(1308895323625); d.setUTCMinutes(3, 2, 5003, 111111);" "d.getTime()"), njs_str("1308895387003") }, { njs_str("var d = new Date(1308895323625); d.setUTCMinutes(3, 2);" "d.getTime()"), njs_str("1308895382625") }, { njs_str("var d = new Date(1308895323625); d.setUTCMinutes(3);" "d.getTime()"), njs_str("1308895383625") }, { njs_str("var d = new Date(1308895323625); d.setHours(20, 3, 2, 5003);" "d.getTime()"), njs_str("1308945787003") }, { njs_str("var d = new Date(1308895323625); d.setHours(20, 3, 2);" "d.getTime()"), njs_str("1308945782625") }, { njs_str("var d = new Date(1308895323625); d.setHours(20, 3);" "d.getTime()"), njs_str("1308945783625") }, { njs_str("var d = new Date(1308895323625); d.setHours(20);" "d.getTime()"), njs_str("1308945723625") }, { njs_str("var d = new Date(1308895323625);" "d.setUTCHours(20, 3, 2, 5003); d.getTime()"), njs_str("1308945787003") }, { njs_str("var d = new Date(1308895323625); d.setUTCHours(20, 3, 2);" "d.getTime()"), njs_str("1308945782625") }, { njs_str("var d = new Date(1308895323625); d.setUTCHours(20, 3);" "d.getTime()"), njs_str("1308945783625") }, { njs_str("var d = new Date(1308895323625); d.setUTCHours(20);" "d.getTime()"), njs_str("1308945723625") }, { njs_str("var d = new Date(1308895323625); d.setDate(10);" "d.getTime()"), njs_str("1307685723625") }, { njs_str("var d = new Date(1308895323625); d.setUTCDate(10);" "d.getTime()"), njs_str("1307685723625") }, { njs_str("var d = new Date(1308895323625); d.setMonth(2, 10);" "d.getTime()"), njs_str("1299736923625") }, { njs_str("var d = new Date(1308895323625); d.setUTCMonth(2, 10);" "d.getTime()"), njs_str("1299736923625") }, { njs_str("var d = new Date(1308895323625); d.setMonth(2);" "d.getTime()"), njs_str("1300946523625") }, { njs_str("var d = new Date(1308895323625); d.setMonth(2, undefined);" "d.getTime()"), njs_str("NaN") }, { njs_str("var d = new Date(1308895323625); d.setMonth(2, undefined)"), njs_str("NaN") }, { njs_str("var d = new Date(1308895323625); d.setUTCMonth(2);" "d.getTime()"), njs_str("1300946523625") }, { njs_str("var d = new Date(1308895323625); d.setFullYear(2010, 2, 10);" "d.getTime()"), njs_str("1268200923625") }, { njs_str("var d = new Date(NaN); d.setFullYear(2010);" "d.getTime() === (new Date(2010,0)).getTime()"), njs_str("true") }, { njs_str("var d = new Date(1308895323625);" "d.setUTCFullYear(2010, 2, 10); d.getTime()"), njs_str("1268200923625") }, { njs_str("var d = new Date(1308895323625); d.setFullYear(2010, 2);" "d.getTime()"), njs_str("1269410523625") }, { njs_str("var d = new Date(1308895323625); d.setUTCFullYear(2010, 2);" "d.getTime()"), njs_str("1269410523625") }, { njs_str("var d = new Date(1308895323625); d.setFullYear(2010);" "d.getTime()"), njs_str("1277359323625") }, { njs_str("var date = new Date(2016, 6, 7, 11, 36, 23, 2); " "date.setFullYear(null) === new Date(-1, 18, 7, 11, 36, 23, 2).getTime()"), njs_str("true") }, { njs_str("var d = new Date(1308895323625); d.setUTCFullYear(2010);" "d.getTime()"), njs_str("1277359323625") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45, 12, 625);" "d.toJSON(1)"), njs_str("2011-06-24T18:45:12.625Z") }, { njs_str("var o = { toISOString: function() { return 'OK' } };" "Date.prototype.toJSON.call(o, 1)"), njs_str("OK") }, { njs_str("var d = new Date(); d.__proto__ === Date.prototype"), njs_str("true") }, { njs_str("new Date(NaN)"), njs_str("Invalid Date") }, { njs_str("new Date(0, 9e99)"), njs_str("Invalid Date") }, #ifndef NJS_SUNC { njs_str("new Date(-0).getTime()"), njs_str("0") }, #endif { njs_str("new Date(6.54321).valueOf()"), njs_str("6") }, { njs_str("[0].map(new Date().getDate)"), njs_str("TypeError: cannot convert undefined to date") }, { njs_str("new Date(eval)"), njs_str("Invalid Date") }, { njs_str("Date.UTC(eval)"), njs_str("NaN") }, { njs_str("Date.name"), njs_str("Date") }, { njs_str("Date.length"), njs_str("7") }, { njs_str("Date.__proto__ === Function.prototype"), njs_str("true") }, { njs_str("Date.prototype.constructor === Date"), njs_str("true") }, { njs_str("Date.prototype.hasOwnProperty('constructor')"), njs_str("true") }, { njs_str("Date.prototype.__proto__ === Object.prototype"), njs_str("true") }, { njs_str("njs.dump(Date.prototype)"), njs_str("{}") }, { njs_str("Date.prototype.valueOf()"), njs_str("TypeError: cannot convert object to date") }, { njs_str("Date.constructor === Function"), njs_str("true") }, /* eval(). */ { njs_str("eval.name"), njs_str("eval") }, { njs_str("eval.length"), njs_str("1") }, { njs_str("eval.prototype"), njs_str("undefined") }, { njs_str("eval.__proto__ === Function.prototype"), njs_str("true") }, { njs_str("eval.constructor === Function"), njs_str("true") }, { njs_str("eval()"), njs_str("InternalError: Not implemented") }, { njs_str("delete this.eval; eval"), njs_str("ReferenceError: \"eval\" is not defined") }, { njs_str("var d = Object.getOwnPropertyDescriptor(this, 'eval');" "d.writable && !d.enumerable && d.configurable"), njs_str("true") }, /* Math. */ { njs_str("Math.PI"), njs_str("3.141592653589793") }, { njs_str("Math.abs()"), njs_str("NaN") }, { njs_str("Math.abs(5)"), njs_str("5") }, { njs_str("Math.abs(-5)"), njs_str("5") }, { njs_str("Math.abs('5.0')"), njs_str("5") }, { njs_str("Math.abs('abc')"), njs_str("NaN") }, { njs_str("Math.acos()"), njs_str("NaN") }, { njs_str("Math.acos(NaN)"), njs_str("NaN") }, { njs_str("Math.acos('abc')"), njs_str("NaN") }, { njs_str("Math.acos(1.1)"), njs_str("NaN") }, { njs_str("Math.acos(-1.1)"), njs_str("NaN") }, { njs_str("Math.acos('1')"), njs_str("0") }, { njs_str("Math.acos(0) - Math.PI/2"), njs_str("0") }, { njs_str("Math.acosh()"), njs_str("NaN") }, { njs_str("Math.acosh('abc')"), njs_str("NaN") }, { njs_str("Math.acosh(0.9)"), njs_str("NaN") }, { njs_str("Math.acosh(1)"), njs_str("0") }, { njs_str("Math.acosh('Infinity')"), njs_str("Infinity") }, /* * The difference is Number.EPSILON on Linux/i686 * and zero on other platforms. */ { njs_str("Math.abs(Math.acosh((1/Math.E + Math.E)/2) - 1)" " <= Number.EPSILON"), njs_str("true") }, { njs_str("Math.asin()"), njs_str("NaN") }, { njs_str("Math.asin(NaN)"), njs_str("NaN") }, { njs_str("Math.asin('abc')"), njs_str("NaN") }, { njs_str("Math.asin(1.1)"), njs_str("NaN") }, { njs_str("Math.asin(-1.1)"), njs_str("NaN") }, { njs_str("Math.asin(0)"), njs_str("0") }, { njs_str("Math.asin('-0')"), njs_str("-0") }, { njs_str("Math.asin(1) - Math.PI/2"), njs_str("0") }, { njs_str("Math.asinh()"), njs_str("NaN") }, { njs_str("Math.asinh('abc')"), njs_str("NaN") }, { njs_str("Math.asinh(0)"), njs_str("0") }, { njs_str("Math.asinh('-0')"), njs_str("-0") }, { njs_str("Math.asinh(Infinity)"), njs_str("Infinity") }, { njs_str("Math.asinh(-Infinity)"), njs_str("-Infinity") }, { njs_str("Math.asinh((Math.E - 1/Math.E)/2)"), njs_str("1") }, { njs_str("Math.atan()"), njs_str("NaN") }, { njs_str("Math.atan(NaN)"), njs_str("NaN") }, { njs_str("Math.atan('abc')"), njs_str("NaN") }, { njs_str("Math.atan('Infinity') - Math.PI/2"), njs_str("0") }, { njs_str("Math.atan(-Infinity) + Math.PI/2"), njs_str("0") }, { njs_str("Math.atan(0)"), njs_str("0") }, { njs_str("Math.atan('-0')"), njs_str("-0") }, { njs_str("Math.atan(1) - Math.PI/4"), njs_str("0") }, { njs_str("Math.atan2()"), njs_str("NaN") }, { njs_str("Math.atan2(1)"), njs_str("NaN") }, { njs_str("Math.atan2('abc', 1)"), njs_str("NaN") }, { njs_str("Math.atan2(1, 0) - Math.PI/2"), njs_str("0") }, { njs_str("Math.atan2('1', -0) - Math.PI/2"), njs_str("0") }, { njs_str("Math.atan2(0, '1')"), njs_str("0") }, { njs_str("Math.atan2(0, 0)"), njs_str("0") }, { njs_str("Math.atan2(0, -0) - Math.PI"), njs_str("0") }, { njs_str("Math.atan2('0', -1) - Math.PI"), njs_str("0") }, { njs_str("Math.atan2(-0, '0.1')"), njs_str("-0") }, { njs_str("Math.atan2(-0, 0)"), njs_str("-0") }, { njs_str("Math.atan2(-0, -0) + Math.PI"), njs_str("0") }, { njs_str("Math.atan2('-0', '-1') + Math.PI"), njs_str("0") }, { njs_str("Math.atan2(-0.1, 0) + Math.PI/2"), njs_str("0") }, { njs_str("Math.atan2(-1, -0) + Math.PI/2"), njs_str("0") }, { njs_str("Math.atan2(1, 'Infinity')"), njs_str("0") }, { njs_str("Math.atan2(0.1, -Infinity) - Math.PI"), njs_str("0") }, { njs_str("Math.atan2(-1, Infinity)"), njs_str("-0") }, { njs_str("Math.atan2('-0.1', -Infinity) + Math.PI"), njs_str("0") }, { njs_str("Math.atan2(Infinity, -5) - Math.PI/2"), njs_str("0") }, { njs_str("Math.atan2(-Infinity, 5) + Math.PI/2"), njs_str("0") }, { njs_str("Math.atan2('Infinity', 'Infinity') - Math.PI/4"), njs_str("0") }, { njs_str("Math.atan2(Infinity, -Infinity) - 3*Math.PI/4"), njs_str("0") }, { njs_str("Math.atan2(-Infinity, 'Infinity') + Math.PI/4"), njs_str("0") }, { njs_str("Math.atan2('-Infinity', -Infinity) + 3*Math.PI/4"), njs_str("0") }, { njs_str("Math.atan2(1, 1) - Math.atan2(-5, -5) - Math.PI"), njs_str("0") }, { njs_str("Math.atanh()"), njs_str("NaN") }, { njs_str("Math.atanh('abc')"), njs_str("NaN") }, { njs_str("Math.atanh(-1.1)"), njs_str("NaN") }, { njs_str("Math.atanh(1.1)"), njs_str("NaN") }, { njs_str("Math.atanh(1)"), njs_str("Infinity") }, { njs_str("Math.atanh('-1')"), njs_str("-Infinity") }, { njs_str("Math.atanh(0)"), njs_str("0") }, { njs_str("Math.atanh(-0)"), njs_str("-0") }, /* * The difference is Number.EPSILON on Linux/i686 * and zero on other platforms. */ { njs_str("Math.abs(1 - Math.atanh((Math.E - 1)/(Math.E + 1)) * 2)" " <= Number.EPSILON"), njs_str("true") }, { njs_str("Math.cbrt()"), njs_str("NaN") }, { njs_str("Math.cbrt('abc')"), njs_str("NaN") }, { njs_str("Math.cbrt(0)"), njs_str("0") }, { njs_str("Math.cbrt('-0')"), njs_str("-0") }, { njs_str("Math.cbrt(Infinity)"), njs_str("Infinity") }, { njs_str("Math.cbrt(-Infinity)"), njs_str("-Infinity") }, { njs_str("(Math.cbrt('27') - 3) < 1e-15"), njs_str("true") }, { njs_str("Math.cbrt(-1)"), njs_str("-1") }, { njs_str("Math.ceil()"), njs_str("NaN") }, { njs_str("Math.ceil('abc')"), njs_str("NaN") }, { njs_str("Math.ceil(0)"), njs_str("0") }, { njs_str("Math.ceil('-0')"), njs_str("-0") }, { njs_str("Math.ceil('Infinity')"), njs_str("Infinity") }, { njs_str("Math.ceil(-Infinity)"), njs_str("-Infinity") }, { njs_str("Math.ceil(-0.9)"), njs_str("-0") }, { njs_str("Math.ceil(3.1)"), njs_str("4") }, { njs_str("Math.clz32()"), njs_str("32") }, { njs_str("Math.clz32('abc')"), njs_str("32") }, { njs_str("Math.clz32(NaN)"), njs_str("32") }, { njs_str("Math.clz32(Infinity)"), njs_str("32") }, { njs_str("Math.clz32('1')"), njs_str("31") }, { njs_str("Math.clz32(0)"), njs_str("32") }, { njs_str("Math.clz32('65535')"), njs_str("16") }, { njs_str("Math.clz32(-1)"), njs_str("0") }, { njs_str("Math.clz32(4294967298)"), njs_str("30") }, { njs_str("Math.cos()"), njs_str("NaN") }, { njs_str("Math.cos('abc')"), njs_str("NaN") }, { njs_str("Math.cos('0')"), njs_str("1") }, { njs_str("Math.cos(-0)"), njs_str("1") }, { njs_str("Math.cos(Infinity)"), njs_str("NaN") }, { njs_str("Math.cos(-Infinity)"), njs_str("NaN") }, { njs_str("Math.cos(Math.PI*2)"), njs_str("1") }, { njs_str("Math.cosh()"), njs_str("NaN") }, { njs_str("Math.cosh('abc')"), njs_str("NaN") }, { njs_str("Math.cosh('0')"), njs_str("1") }, { njs_str("Math.cosh(-0)"), njs_str("1") }, { njs_str("Math.cosh(Infinity)"), njs_str("Infinity") }, { njs_str("Math.cosh(-Infinity)"), njs_str("Infinity") }, /* * The difference is Number.EPSILON on FreeBSD * and zero on other platforms. */ { njs_str("Math.abs(Math.cosh(1) - (1/Math.E + Math.E)/2)" " <= Number.EPSILON"), njs_str("true") }, { njs_str("Math.exp()"), njs_str("NaN") }, { njs_str("Math.exp('abc')"), njs_str("NaN") }, { njs_str("Math.exp('0')"), njs_str("1") }, { njs_str("Math.exp(-0)"), njs_str("1") }, { njs_str("Math.exp(Infinity)"), njs_str("Infinity") }, { njs_str("Math.exp(-Infinity)"), njs_str("0") }, /* * The difference is 2 * Number.EPSILON on FreeBSD * and zero on other platforms. */ { njs_str("Math.abs(Math.exp(1) - Math.E) <= 2 * Number.EPSILON"), njs_str("true") }, { njs_str("Math.expm1()"), njs_str("NaN") }, { njs_str("Math.expm1('abc')"), njs_str("NaN") }, { njs_str("Math.expm1('0')"), njs_str("0") }, { njs_str("Math.expm1(-0)"), njs_str("-0") }, { njs_str("Math.expm1(Infinity)"), njs_str("Infinity") }, { njs_str("Math.expm1(-Infinity)"), njs_str("-1") }, /* * The difference is 2 * Number.EPSILON on FreeBSD, Solaris, * and MacOSX and zero on other platforms. */ { njs_str("Math.abs(1 + Math.expm1(1) - Math.E) <= 2 * Number.EPSILON"), njs_str("true") }, { njs_str("Math.floor()"), njs_str("NaN") }, { njs_str("Math.floor('abc')"), njs_str("NaN") }, { njs_str("Math.floor(0)"), njs_str("0") }, { njs_str("Math.floor('-0')"), njs_str("-0") }, { njs_str("Math.floor('Infinity')"), njs_str("Infinity") }, { njs_str("Math.floor(-Infinity)"), njs_str("-Infinity") }, { njs_str("Math.floor(0.9)"), njs_str("0") }, { njs_str("Math.floor(-3.1)"), njs_str("-4") }, { njs_str("Math.fround()"), njs_str("NaN") }, { njs_str("Math.fround('abc')"), njs_str("NaN") }, { njs_str("Math.fround(0)"), njs_str("0") }, { njs_str("Math.fround('-0')"), njs_str("-0") }, { njs_str("Math.fround('Infinity')"), njs_str("Infinity") }, { njs_str("Math.fround(-Infinity)"), njs_str("-Infinity") }, { njs_str("Math.fround('-1.5')"), njs_str("-1.5") }, { njs_str("Math.fround(16777216)"), njs_str("16777216") }, { njs_str("Math.fround(-16777217)"), njs_str("-16777216") }, { njs_str("Math.hypot()"), njs_str("0") }, { njs_str("Math.hypot(1, 2, 'abc')"), njs_str("NaN") }, { njs_str("Math.hypot(1, NaN, 3)"), njs_str("NaN") }, { njs_str("Math.hypot(1, NaN, -Infinity)"), njs_str("Infinity") }, { njs_str("Math.hypot(-42)"), njs_str("42") }, { njs_str("Math.hypot(8, -15)"), njs_str("17") }, { njs_str("Math.hypot(3, -4, 12.0, '84', 132)"), njs_str("157") }, { njs_str("Math.imul()"), njs_str("0") }, { njs_str("Math.imul(1)"), njs_str("0") }, { njs_str("Math.imul('a', 1)"), njs_str("0") }, { njs_str("Math.imul(1, NaN)"), njs_str("0") }, { njs_str("Math.imul(2, '3')"), njs_str("6") }, { njs_str("Math.imul('3.9', -2.1)"), njs_str("-6") }, { njs_str("Math.imul(2, 2147483647)"), njs_str("-2") }, { njs_str("Math.imul(Number.MAX_SAFE_INTEGER, 2)"), njs_str("-2") }, { njs_str("Math.imul(1, Number.MAX_SAFE_INTEGER + 1)"), njs_str("0") }, { njs_str("Math.imul(2, Number.MIN_SAFE_INTEGER)"), njs_str("2") }, { njs_str("Math.imul(Number.MIN_SAFE_INTEGER - 1, 1)"), njs_str("0") }, { njs_str("Math.imul(2, 4294967297)"), njs_str("2") }, { njs_str("Math.imul(-4294967297, 4294967297)"), njs_str("-1") }, { njs_str("Math.imul(4294967297, -4294967298)"), njs_str("-2") }, { njs_str("Math.imul(-4294967290, 4294967290)"), njs_str("-36") }, { njs_str("Math.imul(-Infinity, 1)"), njs_str("0") }, { njs_str("Math.imul(1, Infinity)"), njs_str("0") }, { njs_str("Math.imul(Number.MAX_VALUE, 1)"), njs_str("0") }, { njs_str("Math.imul(1, -Number.MAX_VALUE)"), njs_str("0") }, { njs_str("Math.log()"), njs_str("NaN") }, { njs_str("Math.log('abc')"), njs_str("NaN") }, { njs_str("Math.log(-1)"), njs_str("NaN") }, { njs_str("Math.log(0)"), njs_str("-Infinity") }, { njs_str("Math.log('-0')"), njs_str("-Infinity") }, { njs_str("Math.log(1)"), njs_str("0") }, { njs_str("Math.log(Infinity)"), njs_str("Infinity") }, { njs_str("Math.log(Math.E)"), njs_str("1") }, { njs_str("Math.log10()"), njs_str("NaN") }, { njs_str("Math.log10('abc')"), njs_str("NaN") }, { njs_str("Math.log10(-1)"), njs_str("NaN") }, { njs_str("Math.log10(0)"), njs_str("-Infinity") }, { njs_str("Math.log10('-0')"), njs_str("-Infinity") }, { njs_str("Math.log10(1)"), njs_str("0") }, { njs_str("Math.log10(Infinity)"), njs_str("Infinity") }, { njs_str("Math.log10(1000)"), njs_str("3") }, { njs_str("Math.log1p()"), njs_str("NaN") }, { njs_str("Math.log1p('abc')"), njs_str("NaN") }, { njs_str("Math.log1p(-2)"), njs_str("NaN") }, { njs_str("Math.log1p('-1')"), njs_str("-Infinity") }, { njs_str("Math.log1p(0)"), njs_str("0") }, { njs_str("Math.log1p(-0)"), njs_str("-0") }, { njs_str("Math.log1p(Infinity)"), njs_str("Infinity") }, { njs_str("Math.log1p(Math.E - 1)"), njs_str("1") }, { njs_str("Math.log2()"), njs_str("NaN") }, { njs_str("Math.log2('abc')"), njs_str("NaN") }, { njs_str("Math.log2(-1)"), njs_str("NaN") }, { njs_str("Math.log2(0)"), njs_str("-Infinity") }, { njs_str("Math.log2('-0')"), njs_str("-Infinity") }, { njs_str("Math.log2(1)"), njs_str("0") }, { njs_str("Math.log2(Infinity)"), njs_str("Infinity") }, { njs_str("Math.log2(128)"), njs_str("7") }, { njs_str("Math.max()"), njs_str("-Infinity") }, { njs_str("Math.max(0, -0)"), njs_str("0") }, { njs_str("Math.max(-0, 0)"), njs_str("0") }, { njs_str("Math.max(null)"), njs_str("0") }, { njs_str("Math.max(undefined)"), njs_str("NaN") }, { njs_str("Math.max(1, 2, 3, undefined)"), njs_str("NaN") }, { njs_str("Math.max(1, 2, 3, NaN)"), njs_str("NaN") }, { njs_str("Math.max('1', '2', '5')"), njs_str("5") }, { njs_str("Math.max(5, {valueOf: function () {return 10}}, 6)"), njs_str("10") }, { njs_str("Math.max(5, {valueOf: function () {return 10}}, 20)"), njs_str("20") }, { njs_str("Math.max(5, undefined, 20)"), njs_str("NaN") }, { njs_str("Math.max(-10, null, -30)"), njs_str("0") }, { njs_str("Math.min()"), njs_str("Infinity") }, { njs_str("Math.min(0, -0)"), njs_str("-0") }, { njs_str("Math.min(-0, 0)"), njs_str("-0") }, { njs_str("Math.min(null)"), njs_str("0") }, { njs_str("Math.min(undefined)"), njs_str("NaN") }, { njs_str("Math.min(1, 2, 3, undefined)"), njs_str("NaN") }, { njs_str("Math.min(1, 2, 3, NaN)"), njs_str("NaN") }, { njs_str("Math.min('1', '2', '5')"), njs_str("1") }, { njs_str("Math.pow(2, 5)"), njs_str("32") }, { njs_str("Math.pow(2)"), njs_str("NaN") }, { njs_str("Math.pow()"), njs_str("NaN") }, { njs_str("Math.pow(1, NaN)"), njs_str("NaN") }, { njs_str("Math.pow(3, NaN)"), njs_str("NaN") }, { njs_str("Math.pow('a', -0)"), njs_str("1") }, { njs_str("Math.pow(1.1, Infinity)"), njs_str("Infinity") }, { njs_str("Math.pow(-1.1, -Infinity)"), njs_str("0") }, { njs_str("Math.pow(-1, Infinity)"), njs_str("NaN") }, { njs_str("Math.pow(1, -Infinity)"), njs_str("NaN") }, { njs_str("Math.pow(-0.9, Infinity)"), njs_str("0") }, { njs_str("Math.pow(0.9, -Infinity)"), njs_str("Infinity") }, { njs_str("Math.pow('Infinity', 0.1)"), njs_str("Infinity") }, { njs_str("Math.pow(Infinity, '-0.1')"), njs_str("0") }, { njs_str("Math.pow(-Infinity, 3)"), njs_str("-Infinity") }, { njs_str("Math.pow('-Infinity', '3.1')"), njs_str("Infinity") }, { njs_str("Math.pow(-Infinity, '-3')"), njs_str("-0") }, { njs_str("Math.pow('-Infinity', -2)"), njs_str("0") }, { njs_str("Math.pow('0', 0.1)"), njs_str("0") }, #ifndef __NetBSD__ /* NetBSD 7: pow(0, negative) == -Infinity. */ { njs_str("Math.pow(0, '-0.1')"), njs_str("Infinity") }, #endif { njs_str("Math.pow(-0, 3)"), njs_str("-0") }, { njs_str("Math.pow('-0', '3.1')"), njs_str("0") }, { njs_str("Math.pow(-0, '-3')"), njs_str("-Infinity") }, #ifndef __NetBSD__ /* NetBSD 7: pow(0, negative) == -Infinity. */ { njs_str("Math.pow('-0', -2)"), njs_str("Infinity") }, #endif { njs_str("Math.pow(-3, 0.1)"), njs_str("NaN") }, { njs_str("var a = Math.random(); a >= 0 && a < 1"), njs_str("true") }, { njs_str("Math.round()"), njs_str("NaN") }, { njs_str("Math.round('abc')"), njs_str("NaN") }, { njs_str("Math.round(0)"), njs_str("0") }, { njs_str("Math.round('-0')"), njs_str("-0") }, { njs_str("Math.round('Infinity')"), njs_str("Infinity") }, { njs_str("Math.round(-Infinity)"), njs_str("-Infinity") }, { njs_str("Math.round(0.4)"), njs_str("0") }, { njs_str("Math.round('0.5')"), njs_str("1") }, { njs_str("Math.round('-0.4')"), njs_str("-0") }, { njs_str("Math.round(-0.5)"), njs_str("-0") }, { njs_str("Math.round(-0.50000000000000001)"), njs_str("-0") }, { njs_str("Math.round(-0.5000000000000001)"), njs_str("-1") }, { njs_str("[0.500001, 0.5000001,0.50000001].map((v)=>Math.round((2**32) + v) - 2**32)"), njs_str("1,1,1") }, { njs_str("[0.500001, 0.5000001,0.50000001].map((v)=>Math.round(-(2**32) + v) + 2**32)"), njs_str("1,1,1") }, { njs_str("[0.500001, 0.5000001,0.50000001].map((v)=>Math.round((2**32) - v) - 2**32)"), njs_str("-1,0,0") }, { njs_str("[0.500001, 0.5000001,0.50000001].map((v)=>Math.round(-(2**32) - v) + 2**32)"), njs_str("-1,0,0") }, { njs_str("Math.sign(5)"), njs_str("1") }, { njs_str("Math.sign(-5)"), njs_str("-1") }, { njs_str("Math.sign(0)"), njs_str("0") }, { njs_str("Math.sign(-0.0)"), njs_str("-0") }, { njs_str("Math.sign(NaN)"), njs_str("NaN") }, { njs_str("Math.sign()"), njs_str("NaN") }, { njs_str("Math.sin()"), njs_str("NaN") }, { njs_str("Math.sin('abc')"), njs_str("NaN") }, { njs_str("Math.sin('0')"), njs_str("0") }, { njs_str("Math.sin(-0)"), njs_str("-0") }, { njs_str("Math.sin(Infinity)"), njs_str("NaN") }, { njs_str("Math.sin(-Infinity)"), njs_str("NaN") }, { njs_str("Math.sin(-Math.PI/2)"), njs_str("-1") }, { njs_str("Math.sinh()"), njs_str("NaN") }, { njs_str("Math.sinh('abc')"), njs_str("NaN") }, { njs_str("Math.sinh('0')"), njs_str("0") }, { njs_str("Math.sinh(-0)"), njs_str("-0") }, { njs_str("Math.sinh(Infinity)"), njs_str("Infinity") }, { njs_str("Math.sinh(-Infinity)"), njs_str("-Infinity") }, /* * The difference is Number.EPSILON on Solaris * and zero on other platforms. */ { njs_str("Math.abs(Math.sinh(1) - (Math.E - 1/Math.E)/2)" " <= Number.EPSILON"), njs_str("true") }, { njs_str("Math.sqrt()"), njs_str("NaN") }, { njs_str("Math.sqrt('abc')"), njs_str("NaN") }, { njs_str("Math.sqrt('0')"), njs_str("0") }, { njs_str("Math.sqrt(-0)"), njs_str("-0") }, { njs_str("Math.sqrt(Infinity)"), njs_str("Infinity") }, { njs_str("Math.sqrt(-0.1)"), njs_str("NaN") }, { njs_str("Math.sqrt('9.0')"), njs_str("3") }, { njs_str("Math.tan()"), njs_str("NaN") }, { njs_str("Math.tan('abc')"), njs_str("NaN") }, { njs_str("Math.tan('0')"), njs_str("0") }, { njs_str("Math.tan(-0)"), njs_str("-0") }, { njs_str("Math.tan(Infinity)"), njs_str("NaN") }, { njs_str("Math.tan(-Infinity)"), njs_str("NaN") }, { njs_str("Math.tan(Math.PI/3) + Math.tan(-Math.PI/3)"), njs_str("0") }, { njs_str("Math.tanh()"), njs_str("NaN") }, { njs_str("Math.tanh('abc')"), njs_str("NaN") }, { njs_str("Math.tanh('0')"), njs_str("0") }, { njs_str("Math.tanh(-0)"), njs_str("-0") }, { njs_str("Math.tanh(Infinity)"), njs_str("1") }, { njs_str("Math.tanh(-Infinity)"), njs_str("-1") }, { njs_str("Math.tanh(0.5) - (Math.E - 1)/(Math.E + 1)"), njs_str("0") }, { njs_str("Math.trunc(3.9)"), njs_str("3") }, { njs_str("Math.trunc(-3.9)"), njs_str("-3") }, { njs_str("Math.trunc(0)"), njs_str("0") }, { njs_str("Math.trunc(-0)"), njs_str("-0") }, { njs_str("Math.trunc(0.9)"), njs_str("0") }, { njs_str("Math.trunc(-0.9)"), njs_str("-0") }, { njs_str("Math.trunc(Infinity)"), njs_str("Infinity") }, { njs_str("Math.trunc(-Infinity)"), njs_str("-Infinity") }, { njs_str("Math.trunc(NaN)"), njs_str("NaN") }, { njs_str("Math.trunc()"), njs_str("NaN") }, { njs_str("Math"), njs_str("[object Math]") }, { njs_str("Math.x = function (x) {return 2*x;}; Math.x(3)"), njs_str("6") }, { njs_str("isNaN"), njs_str("[object Function]") }, { njs_str("isNaN.name"), njs_str("isNaN") }, { njs_str("isNaN.length"), njs_str("1") }, { njs_str("typeof isNaN"), njs_str("function") }, { njs_str("typeof isNaN.length"), njs_str("number") }, { njs_str("isNaN()"), njs_str("true") }, { njs_str("isNaN(123)"), njs_str("false") }, { njs_str("isNaN('123')"), njs_str("false") }, { njs_str("isNaN('Infinity')"), njs_str("false") }, { njs_str("isNaN('abc')"), njs_str("true") }, { njs_str("isFinite"), njs_str("[object Function]") }, { njs_str("isFinite.name"), njs_str("isFinite") }, { njs_str("isFinite.length"), njs_str("1") }, { njs_str("isFinite()"), njs_str("false") }, { njs_str("isFinite(123)"), njs_str("true") }, { njs_str("isFinite('123')"), njs_str("true") }, { njs_str("isFinite('Infinity')"), njs_str("false") }, { njs_str("isFinite('abc')"), njs_str("false") }, { njs_str("parseInt.name"), njs_str("parseInt") }, { njs_str("parseInt.length"), njs_str("2") }, { njs_str("parseInt()"), njs_str("NaN") }, { njs_str("parseInt('12345abc')"), njs_str("12345") }, { njs_str("parseInt('123', 0)"), njs_str("123") }, { njs_str("parseInt('0XaBc', 0)"), njs_str("2748") }, { njs_str("parseInt(' 123')"), njs_str("123") }, { njs_str("parseInt('\\u0009123')"), njs_str("123") }, { njs_str("parseInt('\\u200A123')"), njs_str("123") }, { njs_str("parseInt('1010', 2)"), njs_str("10") }, { njs_str("parseInt('111111111111111111111111111111111111111111111111111111111111111111111111111110000000000000', 2)"), njs_str("1.2379400392853803e+27") }, { njs_str("parseInt('7777777777777777777777777777777777700000000000000000000000000000000', 8)"), njs_str("3.2138760885179806e+60") }, { njs_str("parseInt('0x21bc2b266d3a3600000000000000000000000000000000000000000000000000000')"), njs_str("6.25e+79") }, { njs_str("parseInt('aBc', 16)"), njs_str("2748") }, { njs_str("parseInt('0XaBc')"), njs_str("2748") }, { njs_str("parseInt('-0xabc')"), njs_str("-2748") }, { njs_str("parseInt('njscript', 36)"), njs_str("1845449130881") }, { njs_str("parseInt('0x')"), njs_str("NaN") }, { njs_str("parseInt('z')"), njs_str("NaN") }, { njs_str("parseInt('0xz')"), njs_str("NaN") }, { njs_str("parseInt('0x', 16)"), njs_str("NaN") }, { njs_str("parseInt('0x', 33)"), njs_str("0") }, { njs_str("parseInt('0x', 34)"), njs_str("33") }, { njs_str("parseInt('0', 1)"), njs_str("NaN") }, { njs_str("parseInt('0', 37)"), njs_str("NaN") }, { njs_str("1/parseInt('-0')"), njs_str("-Infinity") }, { njs_str("parseInt('11', new Number(Infinity)) === parseInt('11', Infinity)"), njs_str("true") }, { njs_str("parseInt('11', Number.POSITIVE_INFINITY)"), njs_str("11") }, { njs_str("parseFloat.name"), njs_str("parseFloat") }, { njs_str("parseFloat.length"), njs_str("1") }, { njs_str("parseFloat('12345abc')"), njs_str("12345") }, { njs_str("parseFloat()"), njs_str("NaN") }, { njs_str("parseFloat('')"), njs_str("NaN") }, { njs_str("parseFloat(' \t')"), njs_str("NaN") }, { njs_str("parseFloat('\\u20281')"), njs_str("1") }, { njs_str("parseFloat('e11')"), njs_str("NaN") }, { njs_str("parseFloat({toString(){return ' 1'}})"), njs_str("1") }, { njs_str("parseFloat('1e2147483647')"), njs_str("Infinity") }, { njs_str("parseFloat('1e-2147483647')"), njs_str("0") }, { njs_str("parseFloat('1e-2147483648')"), njs_str("0") }, { njs_str("parseFloat('1e' + '5'.repeat(16))"), njs_str("Infinity") }, { njs_str("parseFloat('1e-' + '5'.repeat(16))"), njs_str("0") }, { njs_str("parseFloat('0x')"), njs_str("0") }, { njs_str("parseFloat('0xff')"), njs_str("0") }, { njs_str("parseFloat('Infinity')"), njs_str("Infinity") }, { njs_str("parseFloat(' Infinityzz')"), njs_str("Infinity") }, { njs_str("parseFloat('Infinit')"), njs_str("NaN") }, { njs_str("parseFloat('5.7e1')"), njs_str("57") }, { njs_str("parseFloat('-5.7e-1')"), njs_str("-0.57") }, { njs_str("parseFloat('-5.e-1')"), njs_str("-0.5") }, { njs_str("parseFloat('5.7e+01')"), njs_str("57") }, { njs_str("parseFloat(' 5.7e+01abc')"), njs_str("57") }, { njs_str("parseFloat('-5.7e-1abc')"), njs_str("-0.57") }, { njs_str("parseFloat('-5.7e')"), njs_str("-5.7") }, { njs_str("parseFloat('-5.7e+')"), njs_str("-5.7") }, { njs_str("parseFloat('-5.7e+abc')"), njs_str("-5.7") }, /* debugger. */ { njs_str("debugger"), njs_str("undefined") }, { njs_str("debugger;"), njs_str("undefined") }, { njs_str("while (false) debugger;"), njs_str("undefined") }, { njs_str("1 + debugger"), njs_str("SyntaxError: Unexpected token \"debugger\" in 1") }, { njs_str("debugger + 1"), njs_str("SyntaxError: Unexpected token \"+\" in 1") }, /* Top-level objects. */ { njs_str("var global = this;" "function isMutableObject(v) {" " var d = Object.getOwnPropertyDescriptor(global, v);" " /* Custom top-level objects are enumerable. */" " var enumerable = (v in {'njs':1, 'process':1}) ^ !d.enumerable;" " return d.writable && enumerable && d.configurable;" "};" "['njs', 'process', 'Math', 'JSON'].every((v)=>isMutableObject(v))"), njs_str("true") }, { njs_str("njs === njs"), njs_str("true") }, { njs_str("this.njs = 1; njs"), njs_str("1") }, { njs_str("process === process"), njs_str("true") }, { njs_str("this.process = 1; process"), njs_str("1") }, { njs_str("Math === Math"), njs_str("true") }, { njs_str("this.Math = 1; Math"), njs_str("1") }, { njs_str("JSON"), njs_str("[object JSON]") }, { njs_str("JSON === JSON"), njs_str("true") }, { njs_str("this.JSON = 1; JSON"), njs_str("1") }, { njs_str("delete this.JSON; JSON"), njs_str("ReferenceError: \"JSON\" is not defined") }, /* Top-level constructors. */ { njs_str( "var global = this;" "function isValidConstructor(c) {" " var props = Object.getOwnPropertyDescriptor(global, c.name);" " props = props.writable && !props.enumerable && props.configurable;" " var same = c === global[c.name];" "" " return props && same;" "};" "Object.getOwnPropertyNames(global)" ".filter((k)=>(global[k] && global[k].prototype && global[k].prototype.constructor))" ".map(k=>global[k])" ".every(c => isValidConstructor(c))"), njs_str("true") }, /* JSON.parse() */ { njs_str("JSON.parse('null')"), njs_str("null") }, { njs_str("JSON.parse('true')"), njs_str("true") }, { njs_str("JSON.parse('false')"), njs_str("false") }, { njs_str("JSON.parse('0')"), njs_str("0") }, { njs_str("JSON.parse('-1234.56e2')"), njs_str("-123456") }, { njs_str("typeof(JSON.parse('true'))"), njs_str("boolean") }, { njs_str("typeof(JSON.parse('false'))"), njs_str("boolean") }, { njs_str("typeof(JSON.parse('1'))"), njs_str("number") }, { njs_str("typeof(JSON.parse('\"\"'))"), njs_str("string") }, { njs_str("typeof(JSON.parse('{}'))"), njs_str("object") }, { njs_str("typeof(JSON.parse('[]'))"), njs_str("object") }, { njs_str("JSON.parse('\"abc\"')"), njs_str("abc") }, { njs_str("JSON.parse('\"\\\\\"\"')"), njs_str("\"") }, { njs_str("JSON.parse('\"\\\\n\"')"), njs_str("\n") }, { njs_str("JSON.parse('\"\\\\t\"')"), njs_str("\t") }, { njs_str("JSON.parse('\"ab\\\\\"c\"')"), njs_str("ab\"c") }, { njs_str("JSON.parse('\"abcdefghijklmopqr\\\\\"s\"')"), njs_str("abcdefghijklmopqr\"s") }, { njs_str("JSON.parse('\"ab\\\\\"c\"').length"), njs_str("4") }, { njs_str("JSON.parse('\"аб\\\\\"в\"')"), njs_str("аб\"в") }, { njs_str("JSON.parse('\"аб\\\\\"в\"').length"), njs_str("4") }, { njs_str("JSON.parse('\"абвгдеёжзийкл\"').length"), njs_str("13") }, { njs_str("JSON.parse('[\"' + 'α'.repeat(33) + '\"]')[0][32]"), njs_str("α") }, { njs_str("JSON.parse('\"\\\\u03B1\"')"), njs_str("α") }, { njs_str("JSON.parse('\"\\\\uD801\\\\uDC00\"')"), njs_str("𐐀") }, { njs_str("JSON.parse('\"\\\\u03B1\"') == JSON.parse('\"\\\\u03b1\"')"), njs_str("true") }, { njs_str("JSON.parse('\"\\\\u03B1\"').length"), njs_str("1") }, { njs_str("JSON.parse('{\"a\":1}').a"), njs_str("1") }, { njs_str("JSON.parse('{\"a\":1,\"a\":2}').a"), njs_str("2") }, { njs_str("JSON.parse('{ \"a\" : \"b\" }').a"), njs_str("b") }, { njs_str("JSON.parse('{\"a\":{\"b\":1}}').a.b"), njs_str("1") }, { njs_str("JSON.parse('[{}, true ,1.1e2, {\"a\":[3,\"b\"]}]')[3].a[1]"), njs_str("b") }, { njs_str("var o = JSON.parse('{\"a\":2}');" "Object.getOwnPropertyDescriptor(o, 'a').configurable"), njs_str("true") }, { njs_str("var o = JSON.parse('{\"a\":2}');" "Object.getOwnPropertyDescriptor(o, 'a').writable"), njs_str("true") }, { njs_str("var o = JSON.parse('{\"a\":2}');" "Object.getOwnPropertyDescriptor(o, 'a').enumerable"), njs_str("true") }, { njs_str("var o = JSON.parse('{\"a\":2}');" "o.a = 3; o.a"), njs_str("3") }, { njs_str("var o = JSON.parse('{\"a\":2}');" "o.b = 3; o.b"), njs_str("3") }, { njs_str("JSON.parse('2') || 10"), njs_str("2") }, { njs_str("JSON.parse('0') || 10"), njs_str("10") }, { njs_str("JSON.parse('-0') || 10"), njs_str("10") }, { njs_str("JSON.parse('\"a\"') || 10"), njs_str("a") }, { njs_str("JSON.parse('\"\"') || 10"), njs_str("10") }, { njs_str("JSON.parse('true') || 10"), njs_str("true") }, { njs_str("JSON.parse('false') || 10"), njs_str("10") }, { njs_str("JSON.parse('null') || 10"), njs_str("10") }, { njs_str("var o = JSON.parse('{}', function(k, v) {return v;}); o"), njs_str("[object Object]") }, { njs_str("var o = JSON.parse('{\"a\":2, \"b\":4, \"a\":{}}'," " function(k, v) {return undefined;});" "o"), njs_str("undefined") }, { njs_str("var o = JSON.parse('{\"a\":2, \"c\":4, \"b\":\"x\"}'," " function(k, v) {if (k === '' || typeof v === 'number') return v });" "Object.keys(o)"), njs_str("a,c") }, { njs_str("var o = JSON.parse('{\"a\":2, \"b\":{}}'," " function(k, v) {return k;});" "o+typeof(o)"), njs_str("string") }, { njs_str("var o = JSON.parse('[\"a\", \"b\"]'," " function(k, v) {return v;});" "o"), njs_str("a,b") }, { njs_str("var o = JSON.parse('{\"a\":[1,{\"b\":1},3]}'," " function(k, v) {return v;});" "o.a[1].b"), njs_str("1") }, { njs_str("var o = JSON.parse('{\"a\":[1,2]}'," " function(k, v) {if (k === '' || k === 'a') {return v;}});" "o.a"), njs_str(",") }, { njs_str("var o = JSON.parse('{\"a\":[1,2]}'," " function(k, v) {return (k === '' || k === 'a') ? v : v*2});" "o.a"), njs_str("2,4") }, { njs_str("var o = JSON.parse('{\"a\":2, \"b\":{\"c\":[\"xx\"]}}'," " function(k, v) {return typeof v === 'number' ? v * 2 : v;});" "o.a+o.b.c[0]"), njs_str("4xx") }, { njs_str("var o = JSON.parse('{\"aa\":{\"b\":1}, \"abb\":1, \"c\":1}'," " function(k, v) {return (k === '' || /^a/.test(k)) ? v : undefined;});" "Object.keys(o)"), njs_str("aa,abb") }, { njs_str("var o = JSON.parse('{\"a\":\"x\"}'," " function(k, v) {if (k === 'a') {this.b='y';} return v});" "o.a+o.b"), njs_str("xy") }, { njs_str("var o = JSON.parse('{\"a\":\"x\"}'," " function(k, v) {return (k === 'a' ? {x:1} : v)});" "o.a.x"), njs_str("1") }, { njs_str("var keys = []; var o = JSON.parse('{\"a\":2, \"b\":{\"c\":\"xx\"}}'," " function(k, v) {keys.push(k); return v;});" "keys"), njs_str("a,c,b,") }, { njs_str("var args = []; var o = JSON.parse('[2,{\"a\":3}]'," " function(k, v) {args.push(k+\":\"+v); return v;});" "args.join('|')"), njs_str("0:2|a:3|1:[object Object]|:2,[object Object]") }, { njs_str("JSON.parse('[0,1,2]', function(k, v) {" " if (v == 2) {" " return undefined;" " }" " return v;" "});"), njs_str("0,1,") }, { njs_str("JSON.parse('[0,1,2]', function(k, v) {" " if (v == 0) {" " Object.defineProperty(this, '0', {value: undefined, enumerable: false});" " return undefined;" " }" " return v;" "});"), njs_str(",1,2") }, { njs_str("JSON.parse()"), njs_str("SyntaxError: Unexpected token at position 0") }, { njs_str("JSON.parse([])"), njs_str("SyntaxError: Unexpected end of input at position 0") }, { njs_str("JSON.parse('')"), njs_str("SyntaxError: Unexpected end of input at position 0") }, { njs_str("JSON.parse('fals')"), njs_str("SyntaxError: Unexpected token at position 0") }, { njs_str("JSON.parse(' t')"), njs_str("SyntaxError: Unexpected token at position 1") }, { njs_str("JSON.parse('nu')"), njs_str("SyntaxError: Unexpected token at position 0") }, { njs_str("JSON.parse('-')"), njs_str("SyntaxError: Unexpected number at position 0") }, { njs_str("JSON.parse('--')"), njs_str("SyntaxError: Unexpected number at position 1") }, { njs_str("JSON.parse('1-')"), njs_str("SyntaxError: Unexpected token at position 1") }, { njs_str("JSON.parse('1ee1')"), njs_str("SyntaxError: Unexpected token at position 1") }, { njs_str("JSON.parse('1eg')"), njs_str("SyntaxError: Unexpected token at position 1") }, { njs_str("JSON.parse('0x01')"), njs_str("SyntaxError: Unexpected token at position 1") }, { njs_str("JSON.parse('\"абв')"), njs_str("SyntaxError: Unexpected end of input at position 4") }, { njs_str("JSON.parse('\"\b')"), njs_str("SyntaxError: Forbidden source char at position 1") }, { njs_str("JSON.parse('\"\\\\u')"), njs_str("SyntaxError: Unexpected end of input at position 3") }, { njs_str("JSON.parse('\"\\\\q\"')"), njs_str("SyntaxError: Unknown escape char at position 2") }, { njs_str("JSON.parse('\"\\\\uDC01\"')"), njs_str("�") }, { njs_str("JSON.parse('\"\\\\uD801\\\\uE000\"')"), njs_str("�") }, { njs_str("JSON.parse('\"\\\\uD83D\"')"), njs_str("�") }, { njs_str("JSON.parse('\"\\\\uD800\\\\uDB00\"')"), njs_str("��") }, { njs_str("JSON.parse('\"\\\\ud800[\"')"), njs_str("�[") }, { njs_str("JSON.parse('{')"), njs_str("SyntaxError: Unexpected end of input at position 1") }, { njs_str("JSON.parse('{{')"), njs_str("SyntaxError: Unexpected token at position 1") }, { njs_str("JSON.parse('{[')"), njs_str("SyntaxError: Unexpected token at position 1") }, { njs_str("JSON.parse('{\"a\"')"), njs_str("SyntaxError: Unexpected token at position 4") }, { njs_str("JSON.parse('{\"a\":')"), njs_str("SyntaxError: Unexpected end of input at position 5") }, { njs_str("JSON.parse('{\"a\":{')"), njs_str("SyntaxError: Unexpected end of input at position 6") }, { njs_str("JSON.parse('{\"a\":{}')"), njs_str("SyntaxError: Unexpected end of input at position 7") }, { njs_str("JSON.parse('{\"a\":{}g')"), njs_str("SyntaxError: Unexpected token at position 7") }, { njs_str("JSON.parse('{\"a\":{},')"), njs_str("SyntaxError: Unexpected end of input at position 8") }, { njs_str("JSON.parse('{\"a\":{},}')"), njs_str("SyntaxError: Trailing comma at position 7") }, { njs_str("JSON.parse('{\"a\":{},,')"), njs_str("SyntaxError: Unexpected token at position 8") }, { njs_str("JSON.parse('{\"a\":{},,}')"), njs_str("SyntaxError: Unexpected token at position 8") }, { njs_str("JSON.parse('[')"), njs_str("SyntaxError: Unexpected end of input at position 1") }, { njs_str("JSON.parse('[q')"), njs_str("SyntaxError: Unexpected token at position 1") }, { njs_str("JSON.parse('[\"a')"), njs_str("SyntaxError: Unexpected end of input at position 3") }, { njs_str("JSON.parse('[1 ')"), njs_str("SyntaxError: Unexpected end of input at position 3") }, { njs_str("JSON.parse('[1,]')"), njs_str("SyntaxError: Trailing comma at position 2") }, { njs_str("JSON.parse('[1 , 5 ')"), njs_str("SyntaxError: Unexpected end of input at position 7") }, { njs_str("JSON.parse('{\"a\":'.repeat(32))"), njs_str("SyntaxError: Nested too deep at position 155") }, { njs_str("JSON.parse('['.repeat(32))"), njs_str("SyntaxError: Nested too deep at position 31") }, { njs_str("var o = JSON.parse('{', function(k, v) {return v;});o"), njs_str("SyntaxError: Unexpected end of input at position 1") }, { njs_str("var o = JSON.parse('{\"a\":1}', " " function(k, v) {return v.a.a;}); o"), njs_str("TypeError: cannot get property \"a\" of undefined") }, { njs_str("function func() {this[8] = 1; return new Int8Array(func)}" "JSON.parse('[1]', func);"), njs_str("") }, { njs_str("JSON.parse(JSON.stringify([Array(2**16)]), v => v)"), njs_str("") }, { njs_str("var order = []; function reviver(k, v) { order.push(k); };" "JSON.parse('{\"p1\":0,\"p2\":0,\"p1\":0,\"2\":0,\"1\":0}', reviver);" "order"), njs_str("1,2,p1,p2,") }, { njs_str("function reviver(k, v) {" " if (k == '0') Object.defineProperty(this, '1', {configurable: false});" " if (k == '1') return;" " return v;" " };" "JSON.parse('[1, 2]', reviver)"), njs_str("1,2") }, { njs_str("JSON.parse('0', (k, v) => {throw 'Oops'})"), njs_str("Oops") }, { njs_str("JSON.parse('{\"a\":1}', (k, v) => {if (k == 'a') {throw 'Oops'}; return v;})"), njs_str("Oops") }, { njs_str("JSON.parse('[2,3,43]', (k, v) => {if (v == 43) {throw 'Oops'}; return v;})"), njs_str("Oops") }, /* JSON.stringify() */ { njs_str("JSON.stringify()"), njs_str("undefined") }, { njs_str("JSON.stringify('')"), njs_str("\"\"") }, { njs_str("JSON.stringify('abc')"), njs_str("\"abc\"") }, { njs_str("JSON.stringify(new String('abc'))"), njs_str("\"abc\"") }, { njs_str("var s = new String('abc'); s.toString = () => 'xxx'; " "JSON.stringify(s)"), njs_str("\"xxx\"") }, { njs_str("JSON.stringify(123)"), njs_str("123") }, { njs_str("JSON.stringify(-0)"), njs_str("0") }, { njs_str("JSON.stringify(0.00000123)"), njs_str("0.00000123") }, { njs_str("JSON.stringify(new Number(123))"), njs_str("123") }, { njs_str("var n = new Number(8.5); n.valueOf = () => 42;" "JSON.stringify(n)"), njs_str("42") }, { njs_str("JSON.stringify(true)"), njs_str("true") }, { njs_str("JSON.stringify(false)"), njs_str("false") }, { njs_str("JSON.stringify(new Boolean(1))"), njs_str("true") }, { njs_str("JSON.stringify(new Boolean(0))"), njs_str("false") }, { njs_str("JSON.stringify(null)"), njs_str("null") }, { njs_str("JSON.stringify(undefined)"), njs_str("undefined") }, { njs_str("JSON.stringify(Symbol())"), njs_str("undefined") }, { njs_str("JSON.stringify({})"), njs_str("{}") }, { njs_str("JSON.stringify([])"), njs_str("[]") }, { njs_str("var a = [1]; a[2] = 'x'; JSON.stringify(a)"), njs_str("[1,null,\"x\"]") }, #if (!NJS_HAVE_MEMORY_SANITIZER) /* very long test under MSAN */ { njs_str(njs_declare_sparse_array("a", 32769) "a[32] = 'a'; a[64] = 'b';" "var s = JSON.stringify(a); " "[s.length,s.substring(162,163),s.match(/null/g).length]"), njs_str("163844,a,32767") }, #endif { njs_str(njs_declare_sparse_array("a", 8) "a[2] = 'a'; a[4] = 'b'; a.length = 3;" "JSON.stringify(a)"), njs_str("[null,null,\"a\"]") }, { njs_str(njs_declare_sparse_array("a", 8) "a[1] = 'a'; a[2] = 'b'; a.length = 3;" "JSON.stringify({a:1,b:2,c:3}, a)"), njs_str("{\"a\":1,\"b\":2}") }, { njs_str("var a = [1,2,3];" "Object.defineProperty(a, '1', {enumerable:false});" "JSON.stringify(a)"), njs_str("[1,2,3]") }, { njs_str("JSON.stringify({a:\"b\",c:19,e:null,t:true,f:false})"), njs_str("{\"a\":\"b\",\"c\":19,\"e\":null,\"t\":true,\"f\":false}") }, { njs_str("JSON.stringify({a:1, b:undefined})"), njs_str("{\"a\":1}") }, { njs_str("JSON.stringify({a:1, b:Symbol()})"), njs_str("{\"a\":1}") }, { njs_str("var o = {a:1, c:2};" "Object.defineProperty(o, 'b', {enumerable:false, value:3});" "JSON.stringify(o)"), njs_str("{\"a\":1,\"c\":2}") }, { njs_str("JSON.stringify({a:{}, b:[function(v){}]})"), njs_str("{\"a\":{},\"b\":[null]}") }, { njs_str("JSON.stringify(RegExp())"), njs_str("{}") }, { njs_str("JSON.stringify(Object(Symbol()))"), njs_str("{}") }, { njs_str("var s = Object(Symbol()); s.test = 'test'; JSON.stringify(s)"), njs_str("{\"test\":\"test\"}") }, { njs_str("JSON.stringify(SyntaxError('e'))"), njs_str("{}") }, { njs_str("JSON.stringify(URIError('e'))"), njs_str("{}") }, { njs_str("var e = URIError('e'); e.name = 'E'; JSON.stringify(e)"), njs_str("{\"name\":\"E\"}") }, { njs_str("var e = URIError('e'); e.message = 'E'; JSON.stringify(e)"), njs_str("{}") }, { njs_str("var e = URIError('e'); e.foo = 'E'; JSON.stringify(e)"), njs_str("{\"foo\":\"E\"}") }, { njs_str("JSON.stringify({get key() {throw new Error('Oops')}})"), njs_str("Error: Oops") }, { njs_str("JSON.stringify({toJSON() {throw new Error('Oops')}})"), njs_str("Error: Oops") }, /* Ignoring named properties of an array. */ { njs_str("var a = [1,2]; a.a = 1;" "JSON.stringify(a)"), njs_str("[1,2]") }, { njs_str("JSON.stringify({a:{b:{c:{d:1}, e:function(v){}}}})"), njs_str("{\"a\":{\"b\":{\"c\":{\"d\":1}}}}") }, { njs_str("JSON.stringify([[\"b\",undefined],1,[5],{a:1}])"), njs_str("[[\"b\",null],1,[5],{\"a\":1}]") }, { njs_str("var json = '{\"a\":{\"b\":{\"c\":{\"d\":1},\"e\":[true]}}}';" "json == JSON.stringify(JSON.parse(json))"), njs_str("true") }, { njs_str("var json = '{\"a\":\"абв\",\"b\":\"α\"}';" "json == JSON.stringify(JSON.parse(json))"), njs_str("true") }, /* Multibyte characters: z - 1 byte, α - 2 bytes, 𐐀 - 4 bytes */ { njs_str("JSON.stringify('α𐐀z'.repeat(10))"), njs_str("\"α𐐀zα𐐀zα𐐀zα𐐀zα𐐀zα𐐀zα𐐀zα𐐀zα𐐀zα𐐀z\"") }, { njs_str("JSON.stringify('α𐐀z'.repeat(10)).length"), njs_str("32") }, { njs_str("JSON.stringify('α'.repeat(33))[32]"), njs_str("α") }, { njs_str("JSON.stringify('a\\nbc')"), njs_str("\"a\\nbc\"") }, { njs_str("JSON.stringify('а\tбв')"), njs_str("\"а\\tбв\"") }, { njs_str("JSON.stringify('\\n\\t\\r\\\"\\f\\b')"), njs_str("\"\\n\\t\\r\\\"\\f\\b\"") }, { njs_str("JSON.stringify('\x00\x01\x02\x1f')"), njs_str("\"\\u0000\\u0001\\u0002\\u001f\"") }, { njs_str("JSON.stringify('abc\x00')"), njs_str("\"abc\\u0000\"") }, { njs_str("JSON.stringify('\x00zz')"), njs_str("\"\\u0000zz\"") }, { njs_str("JSON.stringify('\x00')"), njs_str("\"\\u0000\"") }, { njs_str("JSON.stringify('a\x00z')"), njs_str("\"a\\u0000z\"") }, { njs_str("JSON.stringify('\x00z\x00')"), njs_str("\"\\u0000z\\u0000\"") }, { njs_str("var i, s, r = true;" " for (i = 0; i < 128; i++) {" " s = 'α𐐀z'.repeat(i);" " r &= (JSON.stringify(s) == ('\"' + s + '\"'));" "}; r"), njs_str("1") }, { njs_str("JSON.stringify('\\u0000'.repeat(10)) == ('\"' + '\\\\u0000'.repeat(10) + '\"')"), njs_str("true") }, { njs_str("JSON.stringify('abc'.repeat(100)).length"), njs_str("302") }, { njs_str("JSON.stringify('абв'.repeat(100)).length"), njs_str("302") }, /* Optional arguments. */ { njs_str("JSON.stringify(undefined, undefined, 1)"), njs_str("undefined") }, { njs_str("JSON.stringify([{a:1,b:{c:2}},1], undefined, 0)"), njs_str("[{\"a\":1,\"b\":{\"c\":2}},1]") }, { njs_str("JSON.stringify([{a:1,b:{c:2}},1], undefined, 1)"), njs_str("[\n {\n \"a\": 1,\n \"b\": {\n \"c\": 2\n }\n },\n 1\n]") }, { njs_str("JSON.stringify([{a:1,b:{c:2}},1], undefined, ' ')"), njs_str("[\n {\n \"a\": 1,\n \"b\": {\n \"c\": 2\n }\n },\n 1\n]") }, { njs_str("JSON.stringify([{a:1,b:{c:2}},1], undefined, '#')"), njs_str("[\n#{\n##\"a\": 1,\n##\"b\": {\n###\"c\": 2\n##}\n#},\n#1\n]") }, { njs_str("JSON.stringify([1], null, 'AAAAABBBBBC')"), njs_str("[\nAAAAABBBBB1\n]") }, { njs_str("var s = new String('A'); s.toString = () => 'AAAAABBBBBC';" "JSON.stringify([1], null, s)"), njs_str("[\nAAAAABBBBB1\n]") }, { njs_str("JSON.stringify([1], null, '!βββββ').length"), njs_str("11") }, { njs_str("JSON.stringify([1], null, 'ABC') === JSON.stringify([1], null, new String('ABC'))"), njs_str("true") }, { njs_str("JSON.stringify([1], null, '!!βββββββββββββββββ').length"), njs_str("15") }, { njs_str("JSON.stringify([1], null, 11)"), njs_str("[\n 1\n]") }, { njs_str("JSON.stringify([1], null, 5) === JSON.stringify([1], null, 5.9)"), njs_str("true") }, { njs_str("JSON.stringify([1], null, 5) === JSON.stringify([1], null, new Number(5))"), njs_str("true") }, { njs_str("var s = new Number(23); s.valueOf = () => 5;" "JSON.stringify([1], null, s)"), njs_str("[\n 1\n]") }, { njs_str("JSON.stringify([{a:1,b:{c:2}},1], undefined, -1)"), njs_str("[{\"a\":1,\"b\":{\"c\":2}},1]") }, { njs_str("JSON.stringify([{a:1,b:{c:2}},1], undefined, new Date())"), njs_str("[{\"a\":1,\"b\":{\"c\":2}},1]") }, { njs_str("var o = Object.defineProperty({}, 'a', { get() { return ()=> 1}, enumerable: true });" "JSON.stringify(o)"), njs_str("{}") }, { njs_str("var o = Object.defineProperty({}, 'a', { get: () => ({b:1, c:2}), enumerable: true });" "JSON.stringify(o)"), njs_str("{\"a\":{\"b\":1,\"c\":2}}") }, { njs_str("var o = Object.defineProperty({}, 'a', { get: () => ({})});" "JSON.stringify(o)"), njs_str("{}") }, { njs_str("JSON.stringify({toJSON:function(k){}})"), njs_str("undefined") }, { njs_str("JSON.stringify({toJSON:function(k){return k}})"), njs_str("\"\"") }, { njs_str("JSON.stringify(new Date(1308895323625))"), njs_str("\"2011-06-24T06:02:03.625Z\"") }, { njs_str("JSON.stringify({a:new Date(1308895323625)})"), njs_str("{\"a\":\"2011-06-24T06:02:03.625Z\"}") }, { njs_str("JSON.stringify({b:{toJSON:function(k){return undefined}}})"), njs_str("{}") }, { njs_str("JSON.stringify({b:{toJSON:function(k){}},c:1})"), njs_str("{\"c\":1}") }, { njs_str("JSON.stringify({b:{toJSON:function(k){return k}}})"), njs_str("{\"b\":\"b\"}") }, { njs_str("JSON.stringify({a:1,b:new Date(1308895323625),c:2})"), njs_str("{\"a\":1,\"b\":\"2011-06-24T06:02:03.625Z\",\"c\":2}") }, { njs_str("JSON.stringify({a:{b:new Date(1308895323625)}})"), njs_str("{\"a\":{\"b\":\"2011-06-24T06:02:03.625Z\"}}") }, { njs_str("function key(k){return k}; function und(k){}" "JSON.stringify([{toJSON:key},{toJSON:und},{toJSON:key}])"), njs_str("[\"0\",null,\"2\"]") }, { njs_str("JSON.stringify({b:{a:1,c:[2]}}, function(k,v){return v})"), njs_str("{\"b\":{\"a\":1,\"c\":[2]}}") }, { njs_str("JSON.stringify([{a:1}, 2], function(k,v){return v})"), njs_str("[{\"a\":1},2]") }, { njs_str("JSON.stringify({a:{toJSON:function(k){}}}, function(k,v){return v})"), njs_str("{}") }, { njs_str("JSON.stringify({a:{toJSON:function(k){return 1}}}, function(k,v){return v})"), njs_str("{\"a\":1}") }, { njs_str("JSON.stringify([{toJSON:function(k){}}], function(k,v){return v})"), njs_str("[null]") }, { njs_str("JSON.stringify([{toJSON:function(k){return 1}}], function(k,v){return v})"), njs_str("[1]") }, { njs_str("JSON.stringify({a:new Date(1308895323625)}, function(k,v){return v})"), njs_str("{\"a\":\"2011-06-24T06:02:03.625Z\"}") }, { njs_str("JSON.stringify([new Date(1308895323625)], function(k,v){return v})"), njs_str("[\"2011-06-24T06:02:03.625Z\"]") }, { njs_str("JSON.stringify([new Date(1308895323625)], " " function(k,v){return (typeof v === 'string') ? v.toLowerCase() : v})"), njs_str("[\"2011-06-24t06:02:03.625z\"]") }, { njs_str("JSON.stringify([new Date(1308895323625)], " " function(k,v){return (typeof v === 'string') ? v.toLowerCase() : v}, '#')"), njs_str("[\n#\"2011-06-24t06:02:03.625z\"\n]") }, { njs_str("JSON.stringify({a:new Date(1308895323625),b:1,c:'a'}, " " function(k,v){return (typeof v === 'string') ? undefined : v})"), njs_str("{\"b\":1}") }, { njs_str("JSON.stringify({a:new Date(1308895323625),b:1,c:'a'}, " " function(k,v){return (typeof v === 'string') ? undefined : v}, '#')"), njs_str("{\n#\"b\": 1\n}") }, { njs_str("JSON.stringify([new Date(1308895323625),1,'a'], " " function(k,v){return (typeof v === 'string') ? undefined : v})"), njs_str("[null,1,null]") }, { njs_str("var keys = []; var o = JSON.stringify({a:2, b:{c:1}}," " function(k, v) {keys.push(k); return v;});" "keys"), njs_str(",a,b,c") }, { njs_str("JSON.stringify(['a', 'b', 'c'], " " function(i, v) { if (i === '0') {return undefined} " " else if (i == 1) {return 2} " " else {return v}})"), njs_str("[null,2,\"c\"]") }, { njs_str("JSON.stringify({a:2, b:{c:1}}," " function(k, v) {delete this['b']; return v;})"), njs_str("{\"a\":2}") }, { njs_str("JSON.stringify(JSON.parse('{\"a\":1,\"b\":2}', " " function(k, v) {delete this['b']; return v;}))"), njs_str("{\"a\":1}") }, { njs_str("var keys = []; var o = JSON.stringify([[1,2],{a:3}, 4]," " function(k, v) {keys.push(k); return v;});" "keys"), njs_str(",0,0,1,1,a,2") }, { njs_str("JSON.stringify({b:{a:1,c:[2]}}, ['a', undefined, 'b', {}, 'a'])"), njs_str("{\"b\":{\"a\":1}}") }, { njs_str("JSON.stringify({b:{a:1,c:[2]}}, [new String('a'), new String('b')])"), njs_str("{\"b\":{\"a\":1}}") }, { njs_str("JSON.stringify({'1':1,'2':2,'3':3}, [1, new Number(2)])"), njs_str("{\"1\":1,\"2\":2}") }, { njs_str("var s = new String('str'); s.toString = () => 'xxx';" "JSON.stringify({str:1,xxx:2}, [s])"), njs_str("{\"xxx\":2}") }, { njs_str("var n = new String(123); n.toString = () => '42';" "JSON.stringify({123:1,42:2}, [n])"), njs_str("{\"42\":2}") }, { njs_str("var objs = []; var o = JSON.stringify({a:1}," " function(k, v) {objs.push(this); return v});" "JSON.stringify(objs)"), njs_str("[{\"\":{\"a\":1}},{\"a\":1}]") }, { njs_str("JSON.stringify({a: () => 1, b: Symbol(), c: undefined}," "(k, v) => k.length ? String(v) : v)"), njs_str("{\"a\":\"[object Function]\",\"b\":\"Symbol()\",\"c\":\"undefined\"}") }, { njs_str("var a = []; a[0] = a; JSON.stringify(a)"), njs_str("TypeError: Nested too deep or a cyclic structure") }, { njs_str("var a = {}; a.a = a; JSON.stringify(a)"), njs_str("TypeError: Nested too deep or a cyclic structure") }, { njs_str("var array = [1,2,3];" "array[1] = {get value() {" " Object.defineProperty(array, '2', {get: () => 10}) }" "};" "JSON.stringify(array)"), njs_str("[1,{},10]") }, { njs_str("var array = [1];" "array[1] = {get value() {array[10] = 10}}; JSON.stringify(array)"), njs_str("[1,{}]") }, /* njs.dump(). */ { njs_str("njs.dump({a:1, b:[1,,2,{c:new Boolean(1)}]})"), njs_str("{a:1,b:[1,,2,{c:[Boolean: true]}]}") }, { njs_str("njs.dump([InternalError(),TypeError('msg'), new RegExp(), /^undef$/my, new Date(0)])"), njs_str("[InternalError,TypeError: msg,/(?:)/,/^undef$/my,1970-01-01T00:00:00.000Z]") }, { njs_str("njs.dump(Array.prototype.slice.call({'1':'b', length:2}))"), njs_str("[,'b']") }, { njs_str("var o = Object.defineProperty({}, 'a', { get: () => 1, enumerable: true }); njs.dump(o)"), njs_str("{a:'[Getter]'}") }, { njs_str("var o = Object.defineProperty({}, 'a', { get: () => 1, set(){}, enumerable: true }); njs.dump(o)"), njs_str("{a:'[Getter/Setter]'}") }, { njs_str("var o = Object.defineProperty({}, 'a', { set(){}, enumerable: true }); njs.dump(o)"), njs_str("{a:'[Setter]'}") }, { njs_str("var a = []; a[0] = a; njs.dump(a)"), njs_str("[[Circular]]") }, { njs_str("var a = []; njs.dump([a,a])"), njs_str("[[],[]]") }, { njs_str("var O = {}; O.x = O; njs.dump(O)"), njs_str("{x:[Circular]}") }, { njs_str("var O = {}; njs.dump({x:O, y:O})"), njs_str("{x:{},y:{}}") }, { njs_str("var a = [], b = [a]; a[0] = b; njs.dump(a)"), njs_str("[[[Circular]]]") }, { njs_str("var a = []; a.length = 2**31;" "njs.dump(a)"), njs_str("[<2147483648 empty items>]") }, { njs_str("var a = ['a',,'c']; a.length = 2**31;" "njs.dump(a)"), njs_str("['a',,'c',<2147483645 empty items>]") }, { njs_str("var a = [,'b','c']; a.length = 2**31;" "njs.dump(a)"), njs_str("[,'b','c',<2147483645 empty items>]") }, #if (!NJS_HAVE_MEMORY_SANITIZER) /* False-positive in MSAN? */ { njs_str("var a = []; a[2**31] = 'Z'; a[0] = 'A'; njs.dump(a)"), njs_str("['A',<2147483647 empty items>,'Z']") }, { njs_str("var a = []; a[2**31] = 'Z'; a[0] = 'A'; a.b = 'X'; njs.dump(a)"), njs_str("['A',<2147483647 empty items>,'Z',b:'X']") }, { njs_str("var a = []; a[2**31] = 'Z'; a[0] = 'A'; a.b = 'X'; a.length = 3; njs.dump(a)"), njs_str("['A',<2 empty items>,b:'X']") }, #endif { njs_str("var a = [1,2,3];Object.defineProperty(a, '1', {get:()=>2});njs.dump(a)"), njs_str("[1,'[Getter]',3]") }, { njs_str("var a = [1,2,3];Object.defineProperty(a, '1', {enumerable:false});njs.dump(a)"), njs_str("[1,2,3]") }, { njs_str("njs.dump(-0)"), njs_str("-0") }, { njs_str("njs.dump(Object(-0))"), njs_str("[Number: -0]") }, { njs_str("njs.dump([0, -0])"), njs_str("[0,-0]") }, { njs_str("njs.dump(Symbol())"), njs_str("Symbol()") }, { njs_str("njs.dump(Object(Symbol()))"), njs_str("[Symbol: Symbol()]") }, { njs_str("njs.dump(Symbol('desc'))"), njs_str("Symbol(desc)") }, { njs_str("njs.dump(Object(Symbol('desc')))"), njs_str("[Symbol: Symbol(desc)]") }, { njs_str("njs.dump(Symbol.iterator)"), njs_str("Symbol(Symbol.iterator)") }, { njs_str("njs.dump(Object(Symbol.iterator))"), njs_str("[Symbol: Symbol(Symbol.iterator)]") }, { njs_str("njs.dump(decodeURI)"), njs_str("[Function: decodeURI]") }, { njs_str("delete decodeURI.name; njs.dump(decodeURI)"), njs_str("[Function]") }, { njs_str("delete decodeURI.name; delete Function.prototype.name; " "decodeURI.name = 1; njs.dump(decodeURI)"), njs_str("[Function: native]") }, { njs_str("delete decodeURI.name; delete Function.prototype.name; " "decodeURI.name = 'XXX'; njs.dump(decodeURI)"), njs_str("[Function: XXX]") }, /* njs.on(). */ { njs_str("njs.on(decodeURI)"), njs_str("TypeError: hook type is not a string") }, { njs_str("njs.on('xxx')"), njs_str("TypeError: unknown hook type \"xxx\"") }, { njs_str("njs.on('exit')"), njs_str("TypeError: callback is not a function or null") }, { njs_str("njs.on('exit', null); 1"), njs_str("1") }, { njs_str("njs.on('exit', ()=>{}); 1"), njs_str("1") }, /* njs.memoryStats. */ { njs_str("Object.keys(njs.memoryStats).sort()"), njs_str("cluster_size,nblocks,page_size,size") }, { njs_str("typeof njs.memoryStats.size"), njs_str("number") }, { njs_str("njs.memoryStats.size > 4096"), njs_str("true") }, { njs_str("var size = njs.memoryStats.size;" "new Array(2**15);" "njs.memoryStats.size > size"), njs_str("true") }, /* Built-in methods name. */ { njs_str( "var fail;" "function isMethodsHaveName(o) {" " var except = [" " 'prototype'," " 'constructor'," " 'caller'," " 'arguments'," " 'description'," " ];" " return Object.getOwnPropertyNames(o)" " .filter(v => !except.includes(v)" " && typeof o[v] == 'function')" " .every(v => o[v].name == v" " || !(fail = `${o.name}.${v}: ${o[v].name}`));" "}" "[" " Boolean, Boolean.prototype," " Number, Number.prototype," " Symbol, Symbol.prototype," " String, String.prototype," " Object, Object.prototype," " Array, Array.prototype," " Function, Function.prototype," " RegExp, RegExp.prototype," " Date, Date.prototype," " Error, Error.prototype," " Math," " JSON," "].every(obj => isMethodsHaveName(obj)) || fail"), njs_str("true") }, /* require(). */ { njs_str("require('unknown_module')"), njs_str("Error: Cannot load module \"unknown_module\"") }, { njs_str("require()"), njs_str("TypeError: missing path") }, { njs_str("require.length"), njs_str("1") }, { njs_str("require.name"), njs_str("require") }, { njs_str("typeof require"), njs_str("function") }, { njs_str("require.hasOwnProperty('length')"), njs_str("true") }, /* Trick: number to boolean. */ { njs_str("var a = 0; !!a"), njs_str("false") }, { njs_str("var a = 5; !!a"), njs_str("true") }, /* Trick: flooring. */ { njs_str("var n = -10.12345; ~~n"), njs_str("-10") }, { njs_str("var n = 10.12345; ~~n"), njs_str("10") }, /* es5id: 8.2_A1_T1 */ /* es5id: 8.2_A1_T2 */ { njs_str("var x = null;"), njs_str("undefined") }, /* es5id: 8.2_A2 */ { njs_str("var null;"), njs_str("SyntaxError: Unexpected token \"null\" in 1") }, /* es5id: 8.2_A3 */ { njs_str("typeof(null) === \"object\""), njs_str("true") }, /* Module. */ { njs_str("import * from y"), njs_str("SyntaxError: Non-default import is not supported in 1") }, { njs_str("import 'x' from y"), njs_str("SyntaxError: Non-default import is not supported in 1") }, { njs_str("import {x} from y"), njs_str("SyntaxError: Non-default import is not supported in 1") }, { njs_str("import switch from y"), njs_str("SyntaxError: Unexpected token \"switch\" in 1") }, { njs_str("import x from y"), njs_str("SyntaxError: Unexpected token \"y\" in 1") }, { njs_str("import x from {"), njs_str("SyntaxError: Unexpected token \"{\" in 1") }, { njs_str("import x from ''"), njs_str("ReferenceError: Cannot load module \"\" in 1") }, { njs_str("export"), njs_str("SyntaxError: Illegal export statement in 1") }, { njs_str("Object.assign(undefined)"), njs_str("TypeError: cannot convert null or undefined to object") }, { njs_str("Object.assign(null)"), njs_str("TypeError: cannot convert null or undefined to object") }, { njs_str("Object.assign({x:123}).toString()"), njs_str("[object Object]") }, { njs_str("Object.assign({x:123}).x"), njs_str("123") }, { njs_str("Object.assign(true)"), njs_str("true") }, { njs_str("Object.assign(123)"), njs_str("123") }, { njs_str("var o1 = {a:1, b:1, c:1}; var o2 = {b:2, c:2}; " "var o3 = {c:3}; var obj = Object.assign({}, o1, o2, o3); " "Object.values(obj);"), njs_str("1,2,3") }, { njs_str("var v1 = 'abc'; var v2 = true; var v3 = 10; " "var obj = Object.assign({}, v1, null, v2, undefined, v3); " "Object.values(obj);"), njs_str("a,b,c") }, { njs_str("Object.assign(true, {a:123})"), njs_str("true") }, { njs_str("Object.assign(true, {a:123}).a"), njs_str("123") }, { njs_str("var y = Object.create({s:123}); y.z = 456;" "Object.assign({}, y).s;"), njs_str("undefined") }, { njs_str("var obj = {s:123}; Object.defineProperty(obj," "'p1', {value:12, enumerable:false});" "Object.assign({}, obj).p1"), njs_str("undefined") }, { njs_str("var obj = {s:123}; Object.defineProperty(obj," "'x', {value:12, writable:false});" "Object.assign(obj, {x:4})"), njs_str("TypeError: Cannot assign to read-only property \"x\" of object") }, { njs_str("var obj = {foo:1, get bar() {return 2;}};" "var copy = Object.assign({}, obj);" "Object.getOwnPropertyDescriptor(copy, 'bar').get"), njs_str("undefined") }, { njs_str("try{var x = Object.defineProperty({}, 'foo'," "{value:1, writable:false});" "Object.assign(x, {bar:2}, {foo:2});}catch(error){};" "x.bar"), njs_str("2") }, { njs_str("var a = Object.defineProperty({}, 'a'," "{get(){Object.defineProperty(this, 'b'," "{value:2,enumerable:false});" "return 1}, enumerable:1}); a.b =1;" "var x = Object.assign({}, a);x.b;"), njs_str("undefined") }, /* let and const */ { njs_str("var let = 123;" "let"), njs_str("SyntaxError: Unexpected token \"let\" in 1") }, { njs_str("var const = 123"), njs_str("SyntaxError: Unexpected token \"const\" in 1") }, /* Async */ { njs_str("var async;" "function f() {" " async\n" " function foo() {}" "}" "f()"), njs_str("undefined") }, { njs_str("var async;" "function f() {" " async;" " function foo() {}" "}" "f()"), njs_str("undefined") }, { njs_str("var async;" "async\n" "function foo() {}"), njs_str("undefined") }, { njs_str("new\""), njs_str("SyntaxError: Unterminated string \"\"\" in 1") }, { njs_str("new\"\\UFFFF"), njs_str("SyntaxError: Unterminated string \"\"\\UFFFF\" in 1") }, { njs_str("new/la"), njs_str("SyntaxError: Unterminated RegExp \"/la\" in 1") }, { njs_str("{name; {/ / /}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("[(]"), njs_str("SyntaxError: Unexpected token \"]\" in 1") }, #if 0 /* TODO spreading support. */ { njs_str("[...]"), njs_str("SyntaxError: Unexpected token \"]\" in 1") }, { njs_str("var id = (x) => x, x = id(...[1,2,3]); typeof x"), njs_str("number") }, #endif { njs_str("switch () {}"), njs_str("SyntaxError: Unexpected token \")\" in 1") }, { njs_str("switch ([(]) {}"), njs_str("SyntaxError: Unexpected token \"]\" in 1") }, { njs_str("{{}{-}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}{+}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}{delete}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}{++}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}{++1}"), njs_str("ReferenceError: Invalid left-hand side in prefix operation in 1") }, { njs_str("{{}{1++}"), njs_str("ReferenceError: Invalid left-hand side in postfix operation in 1") }, { njs_str("{{}{1/}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}{1>>}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}{r=}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}{var a = }"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}T=>}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}a = b +}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}a = b -}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}a = b *}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}a = b /}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}a = b %}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}a = b++}"), njs_str("ReferenceError: \"a\" is not defined") }, { njs_str("{{}a = b--}"), njs_str("ReferenceError: \"a\" is not defined") }, { njs_str("{{}a =}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}a +=}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}a -=}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}a *=}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}a /=}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}a %=}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}a ===}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}a ==}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}a !=}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}a !==}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}a >}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}a <}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}a <=}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}a &&}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}a ||}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}a ??}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}a &}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}a |}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}a ^}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}a <<}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}a >>}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}new}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}delete}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}void}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{}typeof}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("{{} ({a: 1, b: {}\n}\n})\n}"), njs_str("SyntaxError: Unexpected token \"}\" in 3") }, { njs_str("object?."), njs_str("SyntaxError: Unexpected end of input in 1") }, { njs_str("`${{a: 1, b}}`"), njs_str("ReferenceError: \"b\" is not defined") }, { njs_str("`${{a: 1, b:}}`"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("`${{a: 1, b:,}}`"), njs_str("SyntaxError: Unexpected token \",\" in 1") }, { njs_str("`${{a: 1, b: 2,}}`"), njs_str("[object Object]") }, { njs_str("`${{a: 1,, b: 2}}`"), njs_str("SyntaxError: Unexpected token \",\" in 1") }, { njs_str("`${{f(){-} - {}}`"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("for (;1-;) {}"), njs_str("SyntaxError: Unexpected token \";\" in 1") }, { njs_str("var str = String(str); str"), njs_str("undefined") }, { njs_str("var t = \"123\"; t = parseInt(t); t"), njs_str("123") }, /* TextEncoder. */ { njs_str("var en = new TextEncoder(); typeof en.encode()"), njs_str("object") }, { njs_str("var en = new TextEncoder(); en.encode()"), njs_str("") }, { njs_str("var en = new TextEncoder(); var res = en.encode('α'); res"), njs_str("206,177") }, { njs_str("var en = new TextEncoder(); var res = en.encode('α1α'); res[2]"), njs_str("49") }, { njs_str("var en = new TextEncoder(); en.encoding"), njs_str("utf-8") }, { njs_str("TextEncoder.prototype.encode.apply({}, [])"), njs_str("TypeError: \"this\" is not a TextEncoder") }, { njs_str("var en = new TextEncoder();" "var utf8 = new Uint8Array(5);" "var res = en.encodeInto('ααααα', utf8); njs.dump(res)"), njs_str("{read:2,written:4}") }, { njs_str("var en = new TextEncoder();" "var utf8 = new Uint8Array(10);" "var res = en.encodeInto('ααααα', utf8); njs.dump(res)"), njs_str("{read:5,written:10}") }, { njs_str("var en = new TextEncoder();" "var utf8 = new Uint8Array(10);" "en.encodeInto('ααααα', utf8.subarray(2)); utf8[0]"), njs_str("0") }, { njs_str("TextEncoder.prototype.encodeInto.apply({}, [])"), njs_str("TypeError: \"this\" is not a TextEncoder") }, { njs_str("(new TextEncoder()).encodeInto('', 0.12) "), njs_str("TypeError: The \"destination\" argument must be an instance of Uint8Array") }, /* TextDecoder. */ { njs_str("var de = new TextDecoder();" "var u8arr = new Uint8Array([240, 160, 174, 183]);" "var u16arr = new Uint16Array(u8arr.buffer);" "var u32arr = new Uint32Array(u8arr.buffer);" "[u8arr, u16arr, u32arr].map(v=>de.decode(v)).join(',')"), njs_str("𠮷,𠮷,𠮷") }, { njs_str("var de = new TextDecoder();" "[new Uint8Array([240, 160]), " " new Uint8Array([174]), " " new Uint8Array([183])].map(v=>de.decode(v, {stream: 1}))[2]"), njs_str("𠮷") }, { njs_str("var de = new TextDecoder();" "de.decode(new Uint8Array([240, 160]), {stream: 1});" "de.decode(new Uint8Array([174]), {stream: 1});" "de.decode(new Uint8Array([183]))"), njs_str("𠮷") }, { njs_str("var de = new TextDecoder();" "de.decode(new Uint8Array([240, 160]), {stream: 1});" "de.decode()"), njs_str("�") }, { njs_str("var de = new TextDecoder('utf-8', {fatal: true});" "de.decode(new Uint8Array([240, 160]))"), njs_str("TypeError: The encoded data was not valid") }, { njs_str("var de = new TextDecoder('utf-8', {fatal: false});" "de.decode(new Uint8Array([240, 160]))"), njs_str("�") }, { njs_str("var en = new TextEncoder();" "var de = new TextDecoder('utf-8', {ignoreBOM: true});" "en.encode(de.decode(new Uint8Array([239, 187, 191, 50])))"), njs_str("239,187,191,50") }, { njs_str("var en = new TextEncoder();" "var de = new TextDecoder('utf-8', {ignoreBOM: false});" "en.encode(de.decode(new Uint8Array([239, 187, 191, 50])))"), njs_str("50") }, { njs_str("var en = new TextEncoder(); var de = new TextDecoder();" "en.encode(de.decode(new Uint8Array([239, 187, 191, 50])))"), njs_str("50") }, { njs_str("var de = new TextDecoder(); de.decode('')"), njs_str("TypeError: The \"input\" argument must be an instance of TypedArray") }, { njs_str("var de = new TextDecoder({})"), njs_str("RangeError: The \"[object Object]\" encoding is not supported") }, { njs_str("var de = new TextDecoder('foo')"), njs_str("RangeError: The \"foo\" encoding is not supported") }, { njs_str("var de = new TextDecoder(); de.encoding"), njs_str("utf-8") }, { njs_str("var de = new TextDecoder(); de.fatal"), njs_str("false") }, { njs_str("var de = new TextDecoder(); de.ignoreBOM"), njs_str("false") }, { njs_str("TextDecoder.prototype.decode.apply({}, new Uint8Array([1]))"), njs_str("TypeError: \"this\" is not a TextDecoder") }, { njs_str("var de = new TextDecoder();" "var buf = new Uint32Array([1,2,3]).buffer;" "var en = new TextEncoder();" "njs.dump(new Uint32Array(en.encode(de.decode(buf)).buffer))"), njs_str("Uint32Array [1,2,3]") }, { njs_str("var de = new TextDecoder();" "var buf = new Uint32Array([1,2,3]).subarray(1,2);" "var en = new TextEncoder();" "njs.dump(new Uint32Array(en.encode(de.decode(buf)).buffer))"), njs_str("Uint32Array [2]") }, /* let */ { njs_str("let x"), njs_str("undefined") }, { njs_str("let x = 123; x"), njs_str("123") }, { njs_str("let x = [123]; x"), njs_str("123") }, { njs_str("let x = () => x; x()"), njs_str("[object Function]") }, { njs_str("let x = (() => x)()"), njs_str("ReferenceError: cannot access variable before initialization") }, { njs_str("x; let x"), njs_str("ReferenceError: cannot access variable before initialization") }, { njs_str("x; let x = 123"), njs_str("ReferenceError: cannot access variable before initialization") }, { njs_str("let x = x + 123"), njs_str("ReferenceError: cannot access variable before initialization") }, { njs_str("let x = (x, 1)"), njs_str("ReferenceError: cannot access variable before initialization") }, { njs_str("let x = x"), njs_str("ReferenceError: cannot access variable before initialization") }, { njs_str("let x; var x"), njs_str("SyntaxError: \"x\" has already been declared in 1") }, { njs_str("var x; let x"), njs_str("SyntaxError: \"x\" has already been declared in 1") }, { njs_str("let x; function x() {}"), njs_str("SyntaxError: \"x\" has already been declared in 1") }, { njs_str("function x() {} let x"), njs_str("SyntaxError: \"x\" has already been declared in 1") }, { njs_str("function x() {let x; var x}"), njs_str("SyntaxError: \"x\" has already been declared in 1") }, { njs_str("function x() {var x; let x}"), njs_str("SyntaxError: \"x\" has already been declared in 1") }, { njs_str("var x = function f() {let f}"), njs_str("undefined") }, { njs_str("let a; let x = 1;" "{let x = 2; a = x}" "[x, a]"), njs_str("1,2") }, { njs_str("let a; let x = 1;" "if (true) {let x = 2; a = x}" "[x, a]"), njs_str("1,2") }, { njs_str("var a = 5, b = 10, arr = [];" "{let a = 4; var b = 1; arr.push(a); arr.push(b)}" "arr.push(a); arr.push(b); arr"), njs_str("4,1,5,1") }, { njs_str("function func() {return x}" "let x = 123;" "func()"), njs_str("123") }, { njs_str("function func() {return x}" "func();" "let x = 123"), njs_str("ReferenceError: cannot access variable before initialization") }, { njs_str("function func() {return () => x}" "let x = 123;" "func()()"), njs_str("123") }, { njs_str("function func() {x = x + 1; let x}"), njs_str("undefined") }, { njs_str("function func() {return () => x}" "func()();" "let x = 123;"), njs_str("ReferenceError: cannot access variable before initialization") }, { njs_str("var arr = [];" "" "for (var i = 0; i < 10; i++) {" " let x = i;" "" " arr.push( (n) => {x += n; return x} );" "}" "" "[" " arr[0](2), arr[1](1), arr[2](4), arr[3](7), arr[4](0)," " arr[5](1), arr[6](2), arr[7](5), arr[8](8), arr[9](10)" "]"), njs_str("2,2,6,10,4,6,8,12,16,19") }, { njs_str("var arr = [];" "" "for (let i = 0; i < 10; i++) {" " arr.push( (n) => {i += n; return i} );" "}" "" "[" " arr[0](2), arr[1](1), arr[2](4), arr[3](7), arr[4](0)," " arr[5](1), arr[6](2), arr[7](5), arr[8](8), arr[9](10)" "]"), njs_str("2,2,6,10,4,6,8,12,16,19") }, { njs_str("for (let i = 0; i < 1; i++) {" " let i = i + 2;" "}"), njs_str("ReferenceError: cannot access variable before initialization") }, { njs_str("let arr = [], res = [];" "for (let i = 0, f = function() { return i }; i < 5; i++) {" " arr.push(f);" "}" "for (let i = 0; i < 5; i++) {" " res.push(arr[i]());" "} res"), njs_str("0,0,0,0,0") }, { njs_str("let arr = [], res = [];" "for (let i = 0; arr.push(() => i), i < 10; i++) {}" "for (let k = 0; k < 10; k++) {res.push(arr[k]())}" "res"), njs_str("0,1,2,3,4,5,6,7,8,9") }, { njs_str("let res = [];" "for (let n in [1,2,3]) {res.push(n)}" "res"), njs_str("0,1,2") }, { njs_str("let arr = [], res = [];" "" "for (let n in [1,2,3]) {" " arr.push(() => n);" "}" "" "for (let n in arr) {" " res.push(arr[n]());" "}" "res"), njs_str("0,1,2") }, { njs_str("let arr = [];" "" "for (let n in [1,2,3]) {" " let n = 1;" " arr.push(n);" "}" "arr"), njs_str("1,1,1") }, { njs_str("for (let n in [1,2,3]) {" " let n = n + 1;" "}"), njs_str("ReferenceError: cannot access variable before initialization") }, { njs_str("for (let n in [1,2,3]) {}" "n"), njs_str("ReferenceError: \"n\" is not defined") }, { njs_str("for (let n in [1,n,3]) {}"), njs_str("ReferenceError: cannot access variable before initialization") }, { njs_str("(function() {" "function f() {return x + 1}" "function abc() {f()};" "abc();" "let x;" "}())"), njs_str("ReferenceError: cannot access variable before initialization") }, { njs_str("function func() {var x = 1; {let x = x + 1} } func()"), njs_str("ReferenceError: cannot access variable before initialization") }, { njs_str("if (false) let x = 1"), njs_str("SyntaxError: let declaration cannot appear in a single-statement context in 1") }, { njs_str("while (false) let x = 1"), njs_str("SyntaxError: let declaration cannot appear in a single-statement context in 1") }, { njs_str("for (;;) let x = 1"), njs_str("SyntaxError: let declaration cannot appear in a single-statement context in 1") }, { njs_str("try {} catch (e) {let e}"), njs_str("SyntaxError: \"e\" has already been declared in 1") }, { njs_str("let arr = [], x = 2;" "switch(true) {default: let x = 1; arr.push(x)}" "arr.push(x); arr"), njs_str("1,2") }, { njs_str("switch(true) {case false: let x = 1; default: x = 2}"), njs_str("ReferenceError: cannot access variable before initialization") }, { njs_str("let res;" "switch(true) {case true: let x = 1; default: x = 2; res = x} res"), njs_str("2") }, { njs_str("let null"), njs_str("SyntaxError: Unexpected token \"null\" in 1") }, { njs_str("let continue"), njs_str("SyntaxError: Unexpected token \"continue\" in 1") }, { njs_str("let undefined"), njs_str("SyntaxError: \"undefined\" has already been declared in 1") }, { njs_str("let a = 1; globalThis.a"), njs_str("undefined") }, { njs_str("if (false) {x = 2} else {x = 1} let x;"), njs_str("ReferenceError: cannot access variable before initialization") }, { njs_str("let let"), njs_str("SyntaxError: Unexpected token \"let\" in 1") }, { njs_str("let null"), njs_str("SyntaxError: Unexpected token \"null\" in 1") }, { njs_str("function let() {}"), njs_str("SyntaxError: Unexpected token \"let\" in 1") }, { njs_str("function static() {}"), njs_str("SyntaxError: Unexpected token \"static\" in 1") }, /* const */ { njs_str("const x"), njs_str("SyntaxError: missing initializer in const declaration") }, { njs_str("const x = 1; x"), njs_str("1") }, { njs_str("const x = 1; x = 1"), njs_str("TypeError: assignment to constant variable") }, { njs_str("function abc() {const x}"), njs_str("SyntaxError: missing initializer in const declaration") }, { njs_str("const x = [123]; x"), njs_str("123") }, { njs_str("const x = () => x; x()"), njs_str("[object Function]") }, { njs_str("const x = (() => x)()"), njs_str("ReferenceError: cannot access variable before initialization") }, { njs_str("x; const x = 123"), njs_str("ReferenceError: cannot access variable before initialization") }, { njs_str("const x = x + 123"), njs_str("ReferenceError: cannot access variable before initialization") }, { njs_str("const x; var x"), njs_str("SyntaxError: \"x\" has already been declared in 1") }, { njs_str("const x; let x"), njs_str("SyntaxError: \"x\" has already been declared in 1") }, { njs_str("let x; const x"), njs_str("SyntaxError: \"x\" has already been declared in 1") }, { njs_str("const x = 1; function x() {}"), njs_str("SyntaxError: \"x\" has already been declared in 1") }, { njs_str("function x() {} const x = 1"), njs_str("SyntaxError: \"x\" has already been declared in 1") }, { njs_str("function x() {const x; var x}"), njs_str("SyntaxError: \"x\" has already been declared in 1") }, { njs_str("function x() {var x; const x}"), njs_str("SyntaxError: \"x\" has already been declared in 1") }, { njs_str("const x = function f() {const f = 1}"), njs_str("undefined") }, { njs_str("let res; const x = 1;" "{const x = 2; res = x}" "[x, res]"), njs_str("1,2") }, { njs_str("let res; const x = 1;" "if (true) {const x = 2; res = x}" "[x, res]"), njs_str("1,2") }, { njs_str("function func() {return x}" "const x = 123;" "func()"), njs_str("123") }, { njs_str("function func() {return x}" "func();" "const x = 123"), njs_str("ReferenceError: cannot access variable before initialization") }, { njs_str("function func() {return () => x}" "const x = 123;" "func()()"), njs_str("123") }, { njs_str("function func() {return () => x++}" "const x = 123;" "func()()"), njs_str("TypeError: assignment to constant variable") }, { njs_str("for (const i = 0; i < 1; i++) {}"), njs_str("TypeError: assignment to constant variable") }, { njs_str("let res = [];" "for (const n in [1,2,3]) {res.push(n)}" "res"), njs_str("0,1,2") }, { njs_str("let arr = [], res = [];" "" "for (const n in [1,2,3]) {" " arr.push(() => n);" "}" "" "for (let n in arr) {" " res.push(arr[n]());" "}" "res"), njs_str("0,1,2") }, { njs_str("let arr = [];" "" "for (const n in [1,2,3]) {" " let n = 1;" " arr.push(n);" "}" "arr"), njs_str("1,1,1") }, { njs_str("for (const n in [1,2,3]) {" " let n = n + 1;" "}"), njs_str("ReferenceError: cannot access variable before initialization") }, { njs_str("for (const n in [1,2,3]) {}" "n"), njs_str("ReferenceError: \"n\" is not defined") }, { njs_str("for (const n in [1,n,3]) {}"), njs_str("ReferenceError: cannot access variable before initialization") }, { njs_str("(function() {" "function f() {return x + 1}" "function abc() {f()};" "abc();" "const x = 1;" "}())"), njs_str("ReferenceError: cannot access variable before initialization") }, { njs_str("if (false) const x = 1"), njs_str("SyntaxError: const declaration cannot appear in a single-statement context in 1") }, { njs_str("while (false) const x = 1"), njs_str("SyntaxError: const declaration cannot appear in a single-statement context in 1") }, { njs_str("for (;;) const x = 1"), njs_str("SyntaxError: const declaration cannot appear in a single-statement context in 1") }, { njs_str("try {} catch (e) {const e = 1}"), njs_str("SyntaxError: \"e\" has already been declared in 1") }, { njs_str("let arr = []; const x = 2;" "switch(true) {default: const x = 1; arr.push(x)}" "arr.push(x); arr"), njs_str("1,2") }, { njs_str("let res;" "switch(true) {case true: const x = 1; default: x = 2; res = x} res"), njs_str("TypeError: assignment to constant variable") }, { njs_str("const null"), njs_str("SyntaxError: Unexpected token \"null\" in 1") }, { njs_str("const continue"), njs_str("SyntaxError: Unexpected token \"continue\" in 1") }, { njs_str("const undefined"), njs_str("SyntaxError: \"undefined\" has already been declared in 1") }, { njs_str("const a = 1; globalThis.a"), njs_str("undefined") }, { njs_str("if (false) {x = 2} else {x = 1} const x = 0"), njs_str("ReferenceError: cannot access variable before initialization") }, { njs_str("const const"), njs_str("SyntaxError: Unexpected token \"const\" in 1") }, /* Async/Await */ { njs_str("async function f() {}; f.prototype"), njs_str("undefined") }, { njs_str("async function f() {await 1}"), njs_str("undefined") }, { njs_str("function f() {await 1}"), njs_str("SyntaxError: await is only valid in async functions in 1") }, { njs_str("async function f() {function a() {await 1}}"), njs_str("SyntaxError: await is only valid in async functions in 1") }, { njs_str("async function f() {() => {await 1}}"), njs_str("SyntaxError: await is only valid in async functions in 1") }, { njs_str("function f() {async () => {await 1}}"), njs_str("undefined") }, { njs_str("let f = async () => {await 1}"), njs_str("undefined") }, { njs_str("let f = () => {await 1}"), njs_str("SyntaxError: await is only valid in async functions in 1") }, { njs_str("(async function() {await 1})"), njs_str("[object AsyncFunction]") }, { njs_str("(function() {await 1})"), njs_str("SyntaxError: await is only valid in async functions in 1") }, { njs_str("let ctor = Object.getPrototypeOf(async function(){}).constructor;" "ctor"), njs_str("[object Function]") }, { njs_str("let ctor = Object.getPrototypeOf(async function(){}).constructor;" "ctor()"), njs_str("[object AsyncFunction]") }, { njs_str("let ctor = Object.getPrototypeOf(async function(){}).constructor;" "new ctor();"), njs_str("[object AsyncFunction]") }, { njs_str("let f = new Function('x', 'await 1; return x'); f(1)"), njs_str("SyntaxError: await is only valid in async functions in runtime:1") }, { njs_str("new AsyncFunction()"), njs_str("ReferenceError: \"AsyncFunction\" is not defined") }, { njs_str("(async function() {console.log(await 111)})"), njs_str("SyntaxError: await in arguments not supported in 1") }, { njs_str("(async function() {console.log('Number: ' + await 111)})"), njs_str("SyntaxError: await in arguments not supported in 1") }, { njs_str("(async function() {f(await 111)})"), njs_str("SyntaxError: await in arguments not supported in 1") }, { njs_str("(async function() {f(f(1), await 111)})"), njs_str("SyntaxError: await in arguments not supported in 1") }, { njs_str("async () => [await x(1)(),]; async () => [await x(1)()]"), njs_str("[object AsyncFunction]") }, { njs_str("(async function() {f(1, 'a', await 111)})"), njs_str("SyntaxError: await in arguments not supported in 1") }, { njs_str("(async function() {f('Number: ' + await 111)})"), njs_str("SyntaxError: await in arguments not supported in 1") }, { njs_str("async function f1() {try {f(await f1)} catch(e) {}}"), njs_str("SyntaxError: await in arguments not supported in 1") }, { njs_str("async function af() {await encrypt({},}"), njs_str("SyntaxError: Unexpected token \"}\" in 1") }, { njs_str("let x = {async af() {await Promise.resolve(1)}}; x.af"), njs_str("[object AsyncFunction]") }, { njs_str("let name = 'af', x = {async [name]() {await Promise.resolve(1)}}; x.af"), njs_str("[object AsyncFunction]") }, }; static njs_unit_test_t njs_safe_test[] = { { njs_str("(new Function('return this'))() === globalThis"), njs_str("true") }, { njs_str("(new Function('return this;'))() === globalThis"), njs_str("true") }, { njs_str("(new Function('return this '))() === globalThis"), njs_str("true") }, { njs_str("(new Function('return thi'))()"), njs_str("TypeError: function constructor is disabled in \"safe\" mode") }, { njs_str("(new Function('){return 1337})//', 'return this'))()"), njs_str("TypeError: function constructor is disabled in \"safe\" mode") }, }; static njs_unit_test_t njs_denormals_test[] = { { njs_str("2.2250738585072014e-308"), njs_str("2.2250738585072014e-308") }, #ifndef NJS_SUNC { njs_str("2.2250738585072014E-308.toString(2) == ('0.' + '0'.repeat(1021) + '1')"), njs_str("true") }, { njs_str("Number('2.2250738585072014E-323')"), njs_str("2.5e-323") }, { njs_str("Number('2.2250738585072014E-323') + 0"), njs_str("2.5e-323") }, /* Smallest positive double (next_double(0)). */ { njs_str("5E-324.toString(36) === '0.' + '0'.repeat(207) + '3'"), njs_str("true") }, /* Maximum fraction length. */ { njs_str("2.2250738585072014E-323.toString(2) == ('0.' + '0'.repeat(1071) + '101')"), njs_str("true") }, /* Denormals. */ { njs_str("var zeros = count => '0'.repeat(count);" "[" " [1.8858070859709815e-308, `0.${zeros(1022)}1101100011110111011100000100011001111101110001010111`]," // FIXME: " [Number.MIN_VALUE, `0.${zeros(1073)}1`]" " [-5.06631661953108e-309, `-0.${zeros(1024)}11101001001010000001101111010101011111111011010111`]," " [6.22574126804e-313, `0.${zeros(1037)}11101010101101100111000110100111001`]," " [-4e-323, `-0.${zeros(1070)}1`]," "].every(t=>t[0].toString(2) === t[1])"), njs_str("true") }, { njs_str("4.94065645841246544176568792868e-324.toExponential()"), njs_str("5e-324") }, { njs_str("4.94065645841246544176568792868e-324.toExponential(10)"), njs_str("4.9406564584e-324") }, #endif }; static njs_unit_test_t njs_disabled_denormals_test[] = { { njs_str("Number('2.2250738585072014E-323')"), njs_str("0") }, { njs_str("Number('2.2250738585072014E-323') + 0"), njs_str("0") }, /* Smallest positive double (next_double(0)). */ { njs_str("5E-324.toString(36)"), njs_str("0") }, { njs_str("2.2250738585072014E-323.toString(2)"), njs_str("0") }, /* Smallest normal double. */ { njs_str("2.2250738585072014e-308"), njs_str("2.2250738585072014e-308") }, { njs_str("2.2250738585072014e-308/2"), njs_str("0") }, /* Denormals. */ { njs_str("[" "1.8858070859709815e-308," "-5.06631661953108e-309," "6.22574126804e-313," "-4e-323," "].map(v=>v.toString(2))"), njs_str("0,0,0,0") }, }; static njs_unit_test_t njs_fs_module_test[] = { { njs_str("var fs = require('fs'); typeof fs"), njs_str("object") }, { njs_str("var fs = require('fs'); Object.isExtensible(fs)"), njs_str("true") }, { njs_str("require('fs') === require('fs')"), njs_str("true") }, { njs_str("require('fs').a = 1; require('fs').a"), njs_str("1") }, { njs_str("var fs = require('fs');" "fs.readFile()"), njs_str("TypeError: \"path\" must be a string or Buffer") }, { njs_str("var fs = require('fs');" "var path = Buffer.from('/broken'); path[3] = 0;" "fs.readFile(path)"), njs_str("TypeError: \"path\" must be a Buffer without null bytes") }, { njs_str("var fs = require('fs');" "fs.readFile('/njs_unknown_path')"), njs_str("TypeError: \"callback\" must be a function") }, { njs_str("var fs = require('fs');" "fs.readFile('/njs_unknown_path', 'utf8')"), njs_str("TypeError: \"callback\" must be a function") }, { njs_str("var fs = require('fs');" "fs.readFile('/njs_unknown_path', {flag:'xx'})"), njs_str("TypeError: \"callback\" must be a function") }, { njs_str("var fs = require('fs');" "fs.readFile('/njs_unknown_path', {flag:'xx'}, 1)"), njs_str("TypeError: \"callback\" must be a function") }, { njs_str("var fs = require('fs');" "fs.readFile('/njs_unknown_path', {flag:'xx'}, function () {})"), njs_str("TypeError: Unknown file open flags: \"xx\"") }, { njs_str("var fs = require('fs');" "fs.readFile('/njs_unknown_path', {encoding:'ascii'}, function () {})"), njs_str("TypeError: \"ascii\" encoding is not supported") }, { njs_str("var fs = require('fs');" "fs.readFile('/njs_unknown_path', 'ascii', function () {})"), njs_str("TypeError: \"ascii\" encoding is not supported") }, /* require('fs').readFileSync() */ { njs_str("var fs = require('fs');" "fs.readFileSync()"), njs_str("TypeError: \"path\" must be a string or Buffer") }, { njs_str("var fs = require('fs');" "fs.readFileSync({})"), njs_str("TypeError: \"path\" must be a string or Buffer") }, { njs_str("var fs = require('fs');" "fs.readFileSync('/njs_unknown_path', {flag:'xx'})"), njs_str("TypeError: Unknown file open flags: \"xx\"") }, { njs_str("var fs = require('fs');" "fs.readFileSync(Buffer.from('/njs_unknown_path'), {encoding:'ascii'})"), njs_str("TypeError: \"ascii\" encoding is not supported") }, { njs_str("var fs = require('fs');" "fs.readFileSync('/njs_unknown_path', 'ascii')"), njs_str("TypeError: \"ascii\" encoding is not supported") }, { njs_str("var fs = require('fs');" "fs.readFileSync('/njs_unknown_path', true)"), njs_str("TypeError: Unknown options type (a string or object required)") }, /* require('fs').writeFile() */ { njs_str("var fs = require('fs');" "fs.writeFile()"), njs_str("TypeError: \"path\" must be a string or Buffer") }, { njs_str("var fs = require('fs');" "fs.writeFile({}, '', function () {})"), njs_str("TypeError: \"path\" must be a string or Buffer") }, { njs_str("var fs = require('fs');" "fs.writeFile('/njs_unknown_path')"), njs_str("TypeError: \"callback\" must be a function") }, { njs_str("var fs = require('fs');" "fs.writeFile('/njs_unknown_path', '')"), njs_str("TypeError: \"callback\" must be a function") }, { njs_str("var fs = require('fs');" "fs.writeFile('/njs_unknown_path', '', undefined)"), njs_str("TypeError: \"callback\" must be a function") }, { njs_str("var fs = require('fs');" "fs.writeFile('/njs_unknown_path', '', 'utf8')"), njs_str("TypeError: \"callback\" must be a function") }, { njs_str("var fs = require('fs');" "fs.writeFile('/njs_unknown_path', '', {flag:'xx'}, function () {})"), njs_str("TypeError: Unknown file open flags: \"xx\"") }, { njs_str("var fs = require('fs');" "fs.writeFile('/njs_unknown_path', '', {encoding:'ascii'}, function () {})"), njs_str("TypeError: \"ascii\" encoding is not supported") }, { njs_str("var fs = require('fs');" "fs.writeFile('/njs_unknown_path', '', 'ascii', function () {})"), njs_str("TypeError: \"ascii\" encoding is not supported") }, { njs_str("var fs = require('fs');" "fs.writeFile('/njs_unknown_path', '', true, function () {})"), njs_str("TypeError: Unknown options type (a string or object required)") }, /* require('fs').writeFileSync() */ { njs_str("var fs = require('fs');" "fs.writeFileSync()"), njs_str("TypeError: \"path\" must be a string or Buffer") }, { njs_str("var fs = require('fs');" "fs.writeFileSync({}, '')"), njs_str("TypeError: \"path\" must be a string or Buffer") }, { njs_str("var fs = require('fs');" "fs.writeFileSync('/njs_unknown_path', '', {flag:'xx'})"), njs_str("TypeError: Unknown file open flags: \"xx\"") }, { njs_str("var fs = require('fs');" "fs.writeFileSync('/njs_unknown_path', '', {encoding:'ascii'})"), njs_str("TypeError: \"ascii\" encoding is not supported") }, { njs_str("var fs = require('fs');" "fs.writeFileSync('/njs_unknown_path', '', 'ascii')"), njs_str("TypeError: \"ascii\" encoding is not supported") }, { njs_str("var fs = require('fs');" "fs.writeFileSync('/njs_unknown_path', '', true)"), njs_str("TypeError: Unknown options type (a string or object required)") }, /* require('fs').renameSync() */ { njs_str("var fs = require('fs');" "fs.renameSync()"), njs_str("TypeError: \"oldPath\" must be a string or Buffer") }, { njs_str("var fs = require('fs');" "fs.renameSync('/njs_unknown_path')"), njs_str("TypeError: \"newPath\" must be a string or Buffer") }, { njs_str("var fs = require('fs');" "[undefined, null, false, NaN, Symbol(), {}, Object('/njs_unknown_path')]" ".map((x) => { try { fs.renameSync(x, '/njs_unknown_path'); } " " catch (e) { return (e instanceof Error); } })" ".every((x) => x === true)"), njs_str("true")}, { njs_str("var fs = require('fs');" "[undefined, null, false, NaN, Symbol(), {}, Object('/njs_unknown_path')]" ".map((x) => { try { fs.renameSync('/njs_unknown_path', x); } " " catch (e) { return (e instanceof Error); } })" ".every((x) => x === true)"), njs_str("true")}, /* require('fs').access() */ { njs_str("var fs = require('fs');" "fs.access()"), njs_str("TypeError: \"path\" must be a string or Buffer") }, { njs_str("var fs = require('fs');" "fs.access('/njs_unknown_path')"), njs_str("TypeError: \"callback\" must be a function") }, { njs_str("var fs = require('fs');" "fs.access('/njs_unknown_path', fs.constants.F_OK)"), njs_str("TypeError: \"callback\" must be a function") }, { njs_str("var fs = require('fs');" "fs.access('/njs_unknown_path', 'fail', function () {})"), njs_str("TypeError: \"mode\" must be a number") }, /* require('fs').accessSync() */ { njs_str("var fs = require('fs');" "fs.accessSync()"), njs_str("TypeError: \"path\" must be a string or Buffer") }, { njs_str("var fs = require('fs');" "fs.accessSync('/njs_unknown_path', 'fail')"), njs_str("TypeError: \"mode\" must be a number") }, { njs_str("var " "fs = require('fs')," "func = [" "'access'," "'accessSync'," "'readFile'," "'readFileSync'," "'writeFile'," "'writeFileSync'," "'appendFile'," "'appendFileSync'," "'rename'," "'renameSync'," "'symlink'," "'symlinkSync'," "'unlink'," "'unlinkSync'," "'realpath'," "'realpathSync'," "'mkdir'," "'mkdirSync'," "'rmdir'," "'rmdirSync'," "'readdir'," "'readdirSync'," "]," "test = (fname) =>" "[undefined, null, false, NaN, Symbol(), {}, Object('/njs_unknown_path')]" ".map((x) => { try { fs[fname](x); } " " catch (e) { return (e instanceof Error); } })" ".every((x) => x === true);" "func.map(test).every((x) => x)"), njs_str("true")}, /* require('fs').promises */ { njs_str("var fs = require('fs');" "typeof fs.promises"), njs_str("object") }, { njs_str("var " "fs = require('fs').promises," "func = [" "'access'," "'readFile'," "'writeFile'," "'appendFile'," "'rename'," "'symlink'," "'unlink'," "'realpath'," "'mkdir'," "'rmdir'," "'readdir'," "];" "func.every((x) => typeof fs[x] == 'function')"), njs_str("true")}, /* require('fs').constants */ { njs_str("var fs = require('fs');" "typeof fs.constants"), njs_str("object") }, { njs_str("var " "fsc = require('fs').constants," "items = [" "'F_OK'," "'R_OK'," "'W_OK'," "'X_OK'," "];" "items.every((x) => typeof fsc[x] == 'number')"), njs_str("true")}, /* require('fs').Dirent */ { njs_str("var fs = require('fs');" "typeof fs.Dirent"), njs_str("function") }, { njs_str("var fs = require('fs');" "fs.Dirent('file', 123)"), njs_str("TypeError: the Dirent constructor must be called with new") }, { njs_str("var fs = require('fs');" "var e = new fs.Dirent('file', 123); [e.name, e.type]"), njs_str("file,123") }, { njs_str("var " "fs = require('fs')," "e = new fs.Dirent('file', 0)," "func = [" "'isDirectory'," "'isFile'," "'isBlockDevice'," "'isCharacterDevice'," "'isSymbolicLink'," "'isFIFO'," "'isSocket'," "];" "func.every((x) => typeof e[x] == 'function')"), njs_str("true")}, }; static njs_unit_test_t njs_crypto_module_test[] = { { njs_str("import x from 'crypto'"), njs_str("undefined") }, { njs_str("import x from 'crypto' 1"), njs_str("SyntaxError: Unexpected token \"1\" in 1") }, { njs_str("if (1) {import x from 'crypto'}"), njs_str("SyntaxError: Illegal import statement in 1") }, { njs_str("var h = require('crypto').createHash('sha1');" "[Object.prototype.toString.call(h), njs.dump(h),h]"), njs_str("[object Hash],Hash {},[object Hash]") }, { njs_str("var h = require('crypto').createHash('sha1');" "var Hash = h.constructor; " "Hash('sha1').update('AB').digest('hex')"), njs_str("06d945942aa26a61be18c3e22bf19bbca8dd2b5d") }, { njs_str("var hash = require('crypto').createHash.bind(undefined, 'md5');" "['hex', 'base64', 'base64url'].map(e => {" " var h = hash().update('AB').digest().toString(e);" " var h2 = hash().update(Buffer.from('XABX').subarray(1,3)).digest(e);" " var h3 = hash().update('A').update('B').digest(e);" " if (h !== h2) {throw new Error(`digest().toString($e):$h != digest($e):$h2`)};" " if (h !== h3) {throw new Error(`digest().toString($e):$h != update('A').update('B').digest($e):$h3`)};" " return h;" "})"), njs_str("b86fc6b051f63d73de262d4c34e3a0a9," "uG/GsFH2PXPeJi1MNOOgqQ==," "uG_GsFH2PXPeJi1MNOOgqQ") }, { njs_str("var hash = require('crypto').createHash.bind(undefined, 'sha1');" "['hex', 'base64', 'base64url'].map(e => {" " var h = hash().update('4142', 'hex').digest().toString(e);" " var h2 = hash().update(Buffer.from('XABX').subarray(1,3)).digest(e);" " var h3 = hash().update('A').update('B').digest(e);" " if (h !== h2) {throw new Error(`digest().toString($e):$h != digest($e):$h2`)};" " if (h !== h3) {throw new Error(`digest().toString($e):$h != update('A').update('B').digest($e):$h3`)};" " return h;" "})"), njs_str("06d945942aa26a61be18c3e22bf19bbca8dd2b5d," "BtlFlCqiamG+GMPiK/GbvKjdK10=," "BtlFlCqiamG-GMPiK_GbvKjdK10") }, { njs_str("var hash = require('crypto').createHash.bind(undefined, 'sha1');" "['hex', 'base64', 'base64url'].every(e => {" " var h = hash().digest(e);" " var h2 = hash().update('').digest(e);" " if (h !== h2) {throw new Error(`digest($e):$h != update('').digest($e):$h2`)};" " return true;" "})"), njs_str("true") }, { njs_str("var hash = require('crypto').createHash.bind(undefined, 'sha1');" "[" " ['AB']," " ['4142', 'hex']," " ['QUI=', 'base64']," " ['QUI', 'base64url']" "].every(args => {" " return hash().update(args[0], args[1]).digest('hex') === '06d945942aa26a61be18c3e22bf19bbca8dd2b5d';" "})"), njs_str("true") }, { njs_str("var hash = require('crypto').createHash.bind(undefined, 'sha256');" "['hex', 'base64', 'base64url'].map(e => {" " var h = hash().update('AB').digest().toString(e);" " var h2 = hash().update(Buffer.from('XABX').subarray(1,3)).digest(e);" " var h3 = hash().update('A').update('B').digest(e);" " if (h !== h2) {throw new Error(`digest().toString($e):$h != digest($e):$h2`)};" " if (h !== h3) {throw new Error(`digest().toString($e):$h != update('A').update('B').digest($e):$h3`)};" " return h;" "})"), njs_str("38164fbd17603d73f696b8b4d72664d735bb6a7c88577687fd2ae33fd6964153," "OBZPvRdgPXP2lri01yZk1zW7anyIV3aH/SrjP9aWQVM=," "OBZPvRdgPXP2lri01yZk1zW7anyIV3aH_SrjP9aWQVM") }, { njs_str("const crypto = require('crypto');" "let hash = crypto.createHash('sha256');" "let digests = [];" "hash.update('one');" "digests.push(hash.copy().digest('hex'));" "hash.update('two');" "digests.push(hash.copy().digest('hex'));" "hash.update('three');" "digests.push(hash.copy().digest('hex'));" "digests"), njs_str("7692c3ad3540bb803c020b3aee66cd8887123234ea0c6e7143c0add73ff431ed," "25b6746d5172ed6352966a013d93ac846e1110d5a25e8f183b5931f4688842a1," "4592092e1061c7ea85af2aed194621cc17a2762bae33a79bf8ce33fd0168b801") }, { njs_str("const crypto = require('crypto');" "let hash = crypto.createHash('sha256');" "hash.update('one').digest();" "hash.copy()"), njs_str("Error: Digest already called") }, { njs_str("var hash = require('crypto').createHash;" "njs.dump(['', 'abc'.repeat(100)].map(v => {" " return ['md5', 'sha1', 'sha256'].map(h => {" " return hash(h).update(v).digest('hex');" " })" "}))"), njs_str("[['d41d8cd98f00b204e9800998ecf8427e'," "'da39a3ee5e6b4b0d3255bfef95601890afd80709'," "'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855']," "['f571117acbd8153c8dc3c81b8817773a'," "'c95466320eaae6d19ee314ae4f135b12d45ced9a'," "'d9f5aeb06abebb3be3f38adec9a2e3b94228d52193be923eb4e24c9b56ee0930']]") }, { njs_str("var h = require('crypto').createHash()"), njs_str("TypeError: algorithm must be a string") }, { njs_str("var h = require('crypto').createHash([])"), njs_str("TypeError: algorithm must be a string") }, { njs_str("var h = require('crypto').createHash('sha512')"), njs_str("TypeError: not supported algorithm: \"sha512\"") }, { njs_str("var h = require('crypto').createHash('sha1');" "h.update()"), njs_str("TypeError: data is not a string or Buffer-like object") }, { njs_str("var h = require('crypto').createHash('sha1');" "h.update({})"), njs_str("TypeError: data is not a string or Buffer-like object") }, { njs_str("var h = require('crypto').createHash('sha1');" "h.update('A').digest('latin1')"), njs_str("TypeError: Unknown digest encoding: \"latin1\"") }, { njs_str("require('crypto').createHash('sha1').digest() instanceof Buffer"), njs_str("true") }, { njs_str("var h = require('crypto').createHash('sha1');" "h.update('A').digest('hex'); h.digest('hex')"), njs_str("Error: Digest already called") }, { njs_str("var h = require('crypto').createHash('sha1');" "h.update('A').digest('hex'); h.update('B')"), njs_str("Error: Digest already called") }, { njs_str("typeof require('crypto').createHash('md5')"), njs_str("object") }, { njs_str("var h = require('crypto').createHmac('sha1', '');" "[Object.prototype.toString.call(h), njs.dump(h),h]"), njs_str("[object Hmac],Hmac {},[object Hmac]") }, { njs_str("var hmac = require('crypto').createHmac.bind(undefined, 'md5', '');" "['hex', 'base64', 'base64url'].map(e => {" " var h = hmac().update('AB').digest().toString(e);" " var h2 = hmac().update(Buffer.from('XABX').subarray(1,3)).digest(e);" " var h3 = hmac().update('A').update('B').digest(e);" " if (h !== h2) {throw new Error(`digest().toString($e):$h != digest($e):$h2`)};" " if (h !== h3) {throw new Error(`digest().toString($e):$h != update('A').update('B').digest($e):$h3`)};" " return h;" "})"), njs_str("9e0e9e545ef63d41dfb653daecf8ebc7," "ng6eVF72PUHftlPa7Pjrxw==," "ng6eVF72PUHftlPa7Pjrxw") }, { njs_str("var hmac = require('crypto').createHmac.bind(undefined, 'sha1', '');" "['hex', 'base64', 'base64url'].map(e => {" " var h = hmac().update('AB').digest().toString(e);" " var h2 = hmac().update(Buffer.from('XABX').subarray(1,3)).digest(e);" " var h3 = hmac().update('A').update('B').digest(e);" " if (h !== h2) {throw new Error(`digest().toString($e):$h != digest($e):$h2`)};" " if (h !== h3) {throw new Error(`digest().toString($e):$h != update('A').update('B').digest($e):$h3`)};" " return h;" "})"), njs_str("d32c0b6637cc2dfe4670f3fe48ef4434123c4810," "0ywLZjfMLf5GcPP+SO9ENBI8SBA=," "0ywLZjfMLf5GcPP-SO9ENBI8SBA") }, { njs_str("var hash = require('crypto').createHmac.bind(undefined, 'sha1', '');" "[" " ['AB']," " ['4142', 'hex']," " ['QUI=', 'base64']," " ['QUI', 'base64url']" "].every(args => {" " return hash().update(args[0], args[1]).digest('hex') === 'd32c0b6637cc2dfe4670f3fe48ef4434123c4810';" "})"), njs_str("true") }, { njs_str("var hmac = require('crypto').createHmac.bind(undefined, 'sha256', '');" "['hex', 'base64', 'base64url'].map(e => {" " var h = hmac().update('AB').digest().toString(e);" " var h2 = hmac().update(Buffer.from('AB')).digest(e);" " var h3 = hmac().update('A').update('B').digest(e);" " if (h !== h2) {throw new Error(`digest().toString($e):$h != digest($e):$h2`)};" " if (h !== h3) {throw new Error(`digest().toString($e):$h != update('A').update('B').digest($e):$h3`)};" " return h;" "})"), njs_str("d53400095496267cf02e5dbd4b0bf9fbfb5f36f311ea7d9809af5487421743e3," "1TQACVSWJnzwLl29Swv5+/tfNvMR6n2YCa9Uh0IXQ+M=," "1TQACVSWJnzwLl29Swv5-_tfNvMR6n2YCa9Uh0IXQ-M") }, { njs_str("var hmac = require('crypto').createHmac;" "njs.dump(['', 'abc'.repeat(100)].map(v => {" " return ['md5', 'sha1', 'sha256'].map(h => {" " return hmac(h, Buffer.from('secret')).update(v).digest('hex');" " })" "}))"), njs_str("[['5c8db03f04cec0f43bcb060023914190'," "'25af6174a0fcecc4d346680a72b7ce644b9a88e8'," "'f9e66e179b6747ae54108f82f8ade8b3c25d76fd30afde6c395822c530196169']," "['91eb74a225cdd3bbfccc34396c6e3ac5'," "'0aac71e3a813a7acc4a809cfdedb2ecba04ffc5e'," "'8660d2d51d6f20f61d5aadfb6c43df7fd05fc2fc4967d8aec1846f3d9ec03987']]") }, { njs_str("var h = require('crypto').createHmac('sha1', '');" "var Hmac = h.constructor; " "Hmac('sha1', '').digest('hex')"), njs_str("fbdb1d1b18aa6c08324b7d64b71fb76370690e1d") }, { njs_str("require('crypto').createHmac('sha1', '').digest() instanceof Buffer"), njs_str("true") }, { njs_str("var h = require('crypto').createHmac('sha256', 'A'.repeat(64));" "h.update('AB').digest('hex')"), njs_str("ee9dce43b12eb3e865614ad9c1a8d4fad4b6eac2b64647bd24cd192888d3f367") }, { njs_str("var h = require('crypto').createHmac('sha256', 'A'.repeat(100));" "h.update('AB').digest('hex')"), njs_str("5647b6c429701ff512f0f18232b4507065d2376ca8899a816a0a6e721bf8ddcc") }, { njs_str("var h = require('crypto').createHmac()"), njs_str("TypeError: algorithm must be a string") }, { njs_str("var h = require('crypto').createHmac([])"), njs_str("TypeError: algorithm must be a string") }, { njs_str("var h = require('crypto').createHmac('sha512', '')"), njs_str("TypeError: not supported algorithm: \"sha512\"") }, { njs_str("var h = require('crypto').createHmac('sha1', [])"), njs_str("TypeError: key is not a string or Buffer-like object") }, { njs_str("var h = require('crypto').createHmac('sha1', 'secret key');" "h.update('A').digest('hex'); h.digest('hex')"), njs_str("Error: Digest already called") }, { njs_str("var h = require('crypto').createHmac('sha1', 'secret key');" "h.update('A').digest('hex'); h.update('B')"), njs_str("Error: Digest already called") }, { njs_str("typeof require('crypto').createHmac('md5', 'a')"), njs_str("object") }, { njs_str("var cr = require('crypto'); var h = cr.createHash('sha1');" "h.update.call(cr.createHmac('sha1', 's'), '')"), njs_str("TypeError: \"this\" is not a hash object") }, }; static njs_unit_test_t njs_querystring_module_test[] = { { njs_str("var qs = require('querystring');" "var obj = qs.parse('baz=fuz');" "njs.dump(obj)"), njs_str("{baz:'fuz'}") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('baz=');" "njs.dump(obj)"), njs_str("{baz:''}") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('baz=fuz&muz=tax');" "njs.dump(obj)"), njs_str("{baz:'fuz',muz:'tax'}") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('baz=fuz&');" "njs.dump(obj)"), njs_str("{baz:'fuz'}") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('&baz=fuz');" "njs.dump(obj)"), njs_str("{baz:'fuz'}") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('&&&&&baz=fuz');" "njs.dump(obj)"), njs_str("{baz:'fuz'}") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('=fuz');" "njs.dump(obj)"), njs_str("{:'fuz'}") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('=fuz=');" "njs.dump(obj)"), njs_str("{:'fuz='}") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('===fu=z');" "njs.dump(obj)"), njs_str("{:'==fu=z'}") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('baz=fuz&baz=tax');" "njs.dump(obj)"), njs_str("{baz:['fuz','tax']}") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('freespace');" "njs.dump(obj)"), njs_str("{freespace:''}") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('name&value=12');" "njs.dump(obj)"), njs_str("{name:'',value:'12'}") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('baz=fuz&muz=tax', 'fuz');" "njs.dump(obj)"), njs_str("{baz:'',&muz:'tax'}") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('baz=fuz&muz=tax', '');" "njs.dump(obj)"), njs_str("{baz:'fuz',muz:'tax'}") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('baz=fuz&muz=tax', null);" "njs.dump(obj)"), njs_str("{baz:'fuz',muz:'tax'}") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('baz=fuz&muz=tax', undefined);" "njs.dump(obj)"), njs_str("{baz:'fuz',muz:'tax'}") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('baz=fuz123muz=tax', 123);" "njs.dump(obj)"), njs_str("{baz:'fuz',muz:'tax'}") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('baz=fuzαααmuz=tax', 'ααα');" "njs.dump(obj)"), njs_str("{baz:'fuz',muz:'tax'}") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('baz=fuz&muz=tax', '=');" "njs.dump(obj)"), njs_str("{baz:'',fuz&muz:'',tax:''}") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('baz=fuz&muz=tax', null, 'fuz');" "njs.dump(obj)"), njs_str("{baz=:'',muz=tax:''}") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('baz=fuz&muz=tax', null, '&');" "njs.dump(obj)"), njs_str("{baz=fuz:'',muz=tax:''}") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('baz123fuz&muz123tax', null, 123);" "njs.dump(obj)"), njs_str("{baz:'fuz',muz:'tax'}") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('bazαααfuz&muzαααtax', null, 'ααα');" "njs.dump(obj)"), njs_str("{baz:'fuz',muz:'tax'}") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('baz=fuz&muz=tax', null, null, {maxKeys: 1});" "njs.dump(obj)"), njs_str("{baz:'fuz'}") }, { njs_str("var qs = require('querystring'); var out = [];" "var obj = qs.parse('baz=fuz&muz=tax', null, null, {decodeURIComponent: (key) => {out.push(key)}});" "out.join('; ');"), njs_str("baz; fuz; muz; tax") }, { njs_str("var qs = require('querystring'); var i = 0;" "var obj = qs.parse('baz=fuz&muz=tax', null, null, {decodeURIComponent: (key) => 'α' + i++});" "njs.dump(obj);"), njs_str("{α0:'α1',α2:'α3'}") }, { njs_str("var qs = require('querystring');" "qs.parse('baz=fuz&muz=tax', null, null, {decodeURIComponent: 123});"), njs_str("TypeError: option decodeURIComponent is not a function") }, { njs_str("var qs = require('querystring');" "qs.unescape = 123;" "qs.parse('baz=fuz&muz=tax');"), njs_str("TypeError: QueryString.unescape is not a function") }, { njs_str("var qs = require('querystring'); var out = [];" "qs.unescape = (key) => {out.push(key)};" "qs.parse('baz=fuz&muz=tax');" "out.join('; ');"), njs_str("baz; fuz; muz; tax") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('ba%32z=f%32uz');" "njs.dump(obj)"), njs_str("{ba2z:'f2uz'}") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('ba%32z=f%32uz');" "njs.dump(obj)"), njs_str("{ba2z:'f2uz'}") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('ba%F0%9F%92%A9z=f%F0%9F%92%A9uz');" "njs.dump(obj)"), njs_str("{ba💩z:'f💩uz'}") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('======');" "njs.dump(obj)"), njs_str("{:'====='}") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('baz=%F0%9F%A9');" "njs.dump(obj)"), njs_str("{baz:'�'}") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('baz=αααααα%\x00\x01\x02αααα');" "njs.dump(obj)"), njs_str("{baz:'αααααα%\\u0000\\u0001\\u0002αααα'}") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('baz=%F6α');" "njs.dump(obj)"), njs_str("{baz:'�α'}") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('baz=%F6');" "njs.dump(obj)"), njs_str("{baz:'�'}") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('baz=%FG');" "njs.dump(obj)"), njs_str("{baz:'%FG'}") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('baz=%F');" "njs.dump(obj)"), njs_str("{baz:'%F'}") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('baz=%');" "njs.dump(obj)"), njs_str("{baz:'%'}") }, { njs_str("var qs = require('querystring');" "var obj = qs.parse('ba+z=f+uz');" "njs.dump(obj)"), njs_str("{ba z:'f uz'}") }, { njs_str("var qs = require('querystring');" "qs.parse('X='+'α'.repeat(33)).X.length"), njs_str("33") }, { njs_str("var qs = require('querystring');" "var x = qs.parse('X='+'α1'.repeat(33)).X;" "[x.length, x[33], x[34]]"), njs_str("66,1,α") }, { njs_str("var qs = require('querystring');" "qs.stringify({'baz': 'fuz'})"), njs_str("baz=fuz") }, { njs_str("var qs = require('querystring');" "qs.stringify({'baz': 'fuz', 'muz': 'tax'})"), njs_str("baz=fuz&muz=tax") }, { njs_str("var qs = require('querystring');" "qs.stringify({'baαz': 'fαuz', 'muαz': 'tαax'});"), njs_str("ba%CE%B1z=f%CE%B1uz&mu%CE%B1z=t%CE%B1ax") }, { njs_str("var qs = require('querystring');" "qs.stringify({'baz': ['fuz', 'tax']})"), njs_str("baz=fuz&baz=tax") }, { njs_str("var qs = require('querystring');" njs_declare_sparse_array("arr", 2) "arr[0] = 0; arr[1] = 1.5;" "qs.stringify({'baz': arr})"), njs_str("baz=0&baz=1.5") }, { njs_str("var qs = require('querystring'); var out = [];" "qs.stringify({'baz': 'fuz', 'muz': 'tax'}, null, null, {encodeURIComponent: (key) => {out.push(key)}});" "out.join('; ')"), njs_str("baz; fuz; muz; tax") }, { njs_str("var qs = require('querystring');" "qs.stringify({'baz': 'fuz', 'muz': 'tax'}, null, null, {encodeURIComponent: 123});" "out.join('; ')"), njs_str("TypeError: option encodeURIComponent is not a function") }, { njs_str("var qs = require('querystring');" "qs.escape = 123;" "qs.stringify({'baz': 'fuz', 'muz': 'tax'})"), njs_str("TypeError: QueryString.escape is not a function") }, { njs_str("var qs = require('querystring'); var out = [];" "qs.escape = (key) => {out.push(key)};" "qs.stringify({'baz': 'fuz', 'muz': 'tax'});" "out.join('; ')"), njs_str("baz; fuz; muz; tax") }, { njs_str("var qs = require('querystring');" "qs.stringify({'baz': 'fuz', 'muz': 'tax'}, '****')"), njs_str("baz=fuz****muz=tax") }, { njs_str("var qs = require('querystring');" "qs.stringify({'baz': 'fuz', 'muz': 'tax'}, null, '^^^^')"), njs_str("baz^^^^fuz&muz^^^^tax") }, { njs_str("var qs = require('querystring');" "var obj = {A:'α'}; obj['δ'] = 'D';" "var s = qs.stringify(obj,'γ=','&β'); [s, s.length]"), njs_str("A&β%CE%B1γ=%CE%B4&βD,20") }, { njs_str("var qs = require('querystring');" "qs.stringify({'baz': 'fuz', 'muz': 'tax'}, '', '')"), njs_str("baz=fuz&muz=tax") }, { njs_str("var qs = require('querystring');" "qs.stringify({'baz': 'fuz', 'muz': 'tax'}, undefined, undefined)"), njs_str("baz=fuz&muz=tax") }, { njs_str("var qs = require('querystring');" "qs.stringify({'baz': 'fuz', 'muz': 'tax'}, '?', '/')"), njs_str("baz/fuz?muz/tax") }, { njs_str("var qs = require('querystring');" "qs.stringify('123')"), njs_str("") }, { njs_str("var qs = require('querystring');" "qs.stringify(123)"), njs_str("") }, { njs_str("var qs = require('querystring');" "qs.stringify({X:{toString(){return 3}}})"), njs_str("X=") }, { njs_str("var qs = require('querystring');" "qs.stringify({ name: undefined, age: 12 })"), njs_str("name=&age=12") }, { njs_str("var qs = require('querystring');" "qs.stringify(Object.create({ name: undefined, age: 12 }))"), njs_str("") }, { njs_str("var qs = require('querystring');" "qs.stringify([])"), njs_str("") }, { njs_str("var qs = require('querystring');" "qs.stringify(['','',''])"), njs_str("0=&1=&2=") }, { njs_str("var qs = require('querystring');" "qs.stringify([undefined, null, Symbol(), Object(0), Object('test'), Object(false),,,])"), njs_str("0=&1=&2=&3=&4=&5=") }, #if 0 { njs_str("var qs = require('querystring');" "qs.stringify([NaN, Infinity, -Infinity, 2**69, 2**70])"), njs_str("0=&1=&2=&3=590295810358705700000&4=1.1805916207174113e%2B21") }, #else { njs_str("var qs = require('querystring');" "qs.stringify([NaN, Infinity, -Infinity, 2**69, 2**70])"), njs_str("0=&1=&2=&3=590295810358705700000&4=1.1805916207174114e%2B21") }, #endif { njs_str("var qs = require('querystring');" "qs.stringify([[1,2,3],[4,5,6]])"), njs_str("0=1&0=2&0=3&1=4&1=5&1=6") }, { njs_str("var qs = require('querystring');" "qs.stringify([['a',,,],['b',,,]])"), njs_str("0=a&0=&0=&1=b&1=&1=") }, { njs_str("var qs = require('querystring');" "qs.stringify([[,'a','b',,]])"), njs_str("0=&0=a&0=b&0=") }, { njs_str("var qs = require('querystring');" "qs.escape('abcααααdef')"), njs_str("abc%CE%B1%CE%B1%CE%B1%CE%B1def") }, { njs_str("var qs = require('querystring');" "qs.unescape('abc%CE%B1%CE%B1%CE%B1%CE%B1def')"), njs_str("abcααααdef") }, }; static njs_unit_test_t njs_webcrypto_test[] = { /* Statistic test * bits1 is a random variable with Binomial distribution * Expected value is N / 2 * Standard deviation is sqrt(N / 4) */ { njs_str("function count1(v) {return v.toString(2).match(/1/g).length;}" "let buf = new Uint32Array(32);" "crypto.getRandomValues(buf);" "let bits1 = buf.reduce((a, v)=> a + count1(v), 0);" "let nbits = buf.length * 32;" "let mean = nbits / 2;" "let stddev = Math.sqrt(nbits / 4);" "let condition = bits1 > (mean - 10 * stddev) && bits1 < (mean + 10 * stddev);" "condition ? true : [buf, nbits, bits1, mean, stddev]"), njs_str("true") }, { njs_str("let buf = new Uint32Array(4);" "buf === crypto.getRandomValues(buf)"), njs_str("true") }, { njs_str("crypto.subtle;" "var d = Object.getOwnPropertyDescriptor(crypto, 'subtle');" "d.enumerable && !d.configurable && d.writable"), njs_str("true") }, }; #define NJS_XML_DOC "const xml = require('xml');" \ "let data = `ToveJani`;" \ "let doc = xml.parse(data);" static njs_unit_test_t njs_xml_test[] = { { njs_str(NJS_XML_DOC "[doc.note.$name," " doc.note.to.$text," " doc.note.$parent," " doc.note.to.$parent.$name," " doc.note.$tag$to.$text," " doc.note.to.$attr$b," " doc.note.$tags[1].$text," " doc.note.$tags$from[0].$text]"), njs_str("note,Tove,,note,Tove,bar,Jani,Jani") }, { njs_str("const xml = require('xml');" "let doc = xml.parse(`FOOBAR`);" "[doc.root.$tags$foo[0].$text," " doc.root.$tags$foo[1].$text," " doc.root.$tags$bar.length," " doc.root.$tags$.length]"), njs_str("FOO,BAR,0,2") }, { njs_str("const xml = require('xml');" "let doc = xml.parse(`GARBAGE`)"), njs_str("Error: failed to parse XML (libxml2: \"Start tag expected, '<' not found\" at 1:1)") }, { njs_str("const xml = require('xml');" "let doc = xml.parse(`TEXT`);" "doc.r.$text"), njs_str("TEXT") }, { njs_str("const xml = require('xml');" "let doc = xml.parse(`俄语данные`);" "doc.r.$text[2]"), njs_str("д") }, { njs_str("const xml = require('xml');" "let doc = xml.parse(`<俄语 լեզու=\"ռուսերեն\">данные`);" "[doc['俄语'].$name[1]," " doc['俄语']['$attr$լեզու'][7]," " doc['俄语'].$text[5]]"), njs_str("语,ն,е") }, { njs_str("const xml = require('xml');" "var doc = xml.parse(`" "foo`);" "[xml.c14n(doc.pdu.elem1)," " xml.exclusiveC14n(doc.pdu.elem1)," " xml.exclusiveC14n(doc.pdu.elem1, null, 1)," " xml.exclusiveC14n(doc.pdu.elem1, null, false, 'n0 n1')]" ".map(v => (new TextDecoder().decode(v)))"), njs_str("foo," "foo," "foo," "foo") }, { njs_str(NJS_XML_DOC "let dec = new TextDecoder();" "dec.decode(xml.exclusiveC14n(doc.note))"), njs_str("ToveJani") }, { njs_str(NJS_XML_DOC "let dec = new TextDecoder();" "dec.decode(xml.serialize(doc.note))"), njs_str("ToveJani") }, { njs_str(NJS_XML_DOC "xml.serializeToString(doc.note)"), njs_str("ToveJani") }, { njs_str(NJS_XML_DOC "let dec = new TextDecoder();" "dec.decode(xml.exclusiveC14n(doc.note, doc.note.to))"), njs_str("Jani") }, { njs_str(NJS_XML_DOC "njs.dump(doc)"), njs_str("XMLDoc {note:XMLNode {$name:'note'," "$tags:[XMLNode {$name:'to'," "$attrs:XMLAttr {b:'bar',a:'foo'}," "$text:'Tove'}," "XMLNode {$name:'from',$text:'Jani'}]}}") }, { njs_str(NJS_XML_DOC "JSON.stringify(doc)"), njs_str("{\"note\":{\"$name\":\"note\",\"$tags\":" "[{\"$name\":\"to\",\"$attrs\":{\"b\":\"bar\",\"a\":\"foo\"}," "\"$text\":\"Tove\"},{\"$name\":\"from\",\"$text\":\"Jani\"}]}}") }, { njs_str("var xml = require('xml');" "var doc = xml.parse(``); xml.exclusiveC14n(doc, 1)"), njs_str("TypeError: \"excluding\" argument is not a XMLNode object") }, { njs_str(NJS_XML_DOC "doc.$root.$text"), njs_str("ToveJani") }, { njs_str(NJS_XML_DOC "doc.$root.$text = 'WAKA';" "[doc.$root.$text, (new TextDecoder).decode(xml.c14n(doc))]"), njs_str("WAKA,WAKA") }, { njs_str(NJS_XML_DOC "doc.$root.setText('WAKA');" "[doc.$root.$text, (new TextDecoder).decode(xml.c14n(doc))]"), njs_str("WAKA,WAKA") }, { njs_str(NJS_XML_DOC "doc.$root.$text = '';" "[doc.$root.$text, (new TextDecoder).decode(xml.c14n(doc))]"), njs_str(",<WA&KA>") }, { njs_str(NJS_XML_DOC "doc.$root.setText('');" "[doc.$root.$text, (new TextDecoder).decode(xml.c14n(doc))]"), njs_str(",<WA&KA>") }, { njs_str(NJS_XML_DOC "doc.$root.$text = '\"WAKA\"';" "[doc.$root.$text, (new TextDecoder).decode(xml.c14n(doc))]"), njs_str("\"WAKA\",\"WAKA\"") }, { njs_str(NJS_XML_DOC "doc.$root.$text = '';" "[doc.$root.$text, (new TextDecoder).decode(xml.c14n(doc))]"), njs_str(",") }, { njs_str(NJS_XML_DOC "doc.$root.setText();" "[doc.$root.$text, (new TextDecoder).decode(xml.c14n(doc))]"), njs_str(",") }, { njs_str(NJS_XML_DOC "doc.$root.setText(null);" "[doc.$root.$text, (new TextDecoder).decode(xml.c14n(doc))]"), njs_str(",") }, { njs_str(NJS_XML_DOC "doc.$root.removeText();" "[doc.$root.$text, (new TextDecoder).decode(xml.c14n(doc))]"), njs_str(",") }, { njs_str(NJS_XML_DOC "let to = doc.note.to;" "doc.$root.$text = '';" "[to.$name, to.$text, to.$attr$b, to.$parent.$name]"), njs_str("to,Tove,bar,note") }, { njs_str(NJS_XML_DOC "doc.$root.$text = 'WAKA';" "doc.$root.$attr$aaa = 'foo';" "doc.$root.$attr$bbb = 'bar';" "[doc.$root.$attr$aaa, (new TextDecoder).decode(xml.c14n(doc))]"), njs_str("foo,WAKA") }, { njs_str(NJS_XML_DOC "doc.$root.$text = 'WAKA';" "doc.$root.setAttribute('aaa', 'foo');" "doc.$root.setAttribute('bbb', 'WAKA") }, { njs_str(NJS_XML_DOC "doc.note.to.setAttribute('a', null);" "(new TextDecoder).decode(xml.c14n(doc.note.to))"), njs_str("Tove") }, { njs_str(NJS_XML_DOC "doc.$root.setAttribute('<', 'xxx')"), njs_str("TypeError: attribute name \"<\" is not valid") }, { njs_str(NJS_XML_DOC "doc.$root.$text = 'WAKA';" "doc.$root['$attr$' + 'x'.repeat(1024)] = 1;"), njs_str("InternalError: njs_xml_str_to_c_string() very long string, length >= 511") }, { njs_str(NJS_XML_DOC "delete doc.note.to.$attr$a;" "(new TextDecoder).decode(xml.c14n(doc.note.to))"), njs_str("Tove") }, { njs_str(NJS_XML_DOC "doc.note.to.removeAttribute('a');" "(new TextDecoder).decode(xml.c14n(doc.note.to))"), njs_str("Tove") }, { njs_str(NJS_XML_DOC "delete doc.note.to.removeAttribute('c');" "(new TextDecoder).decode(xml.c14n(doc.note.to))"), njs_str("Tove") }, { njs_str(NJS_XML_DOC "delete doc.note.to.removeAllAttributes();" "(new TextDecoder).decode(xml.c14n(doc.note.to))"), njs_str("Tove") }, { njs_str(NJS_XML_DOC "delete doc.note.$tag$to;" "(new TextDecoder).decode(xml.c14n(doc))"), njs_str("Jani") }, { njs_str(NJS_XML_DOC "delete doc.note.to;" "(new TextDecoder).decode(xml.c14n(doc))"), njs_str("Jani") }, { njs_str("var xml = require('xml');" "var doc = xml.parse(``);" "delete doc.$root.a;" "(new TextDecoder).decode(xml.c14n(doc))"), njs_str("") }, { njs_str("var xml = require('xml');" "var doc = xml.parse(``);" "doc.$root.removeChildren('c');" "(new TextDecoder).decode(xml.c14n(doc))"), njs_str("") }, { njs_str("var xml = require('xml');" "var doc = xml.parse(``);" "doc.$root.removeChildren('a');" "(new TextDecoder).decode(xml.c14n(doc))"), njs_str("") }, { njs_str("var xml = require('xml');" "var doc = xml.parse(``);" "doc.$root.removeChildren();" "(new TextDecoder).decode(xml.c14n(doc))"), njs_str("") }, { njs_str(NJS_XML_DOC "doc.note.$tags = [];" "(new TextDecoder).decode(xml.c14n(doc))"), njs_str("") }, { njs_str(NJS_XML_DOC "var doc2 = xml.parse(``);" "doc.note.addChild(doc2);" "(new TextDecoder).decode(xml.c14n(doc))"), njs_str("ToveJani") }, { njs_str(NJS_XML_DOC "var doc2 = xml.parse(``);" "doc.note.addChild(doc2);" "doc.note.addChild(doc2);" "(new TextDecoder).decode(xml.c14n(doc))"), njs_str("ToveJani" "") }, { njs_str(NJS_XML_DOC "delete doc.note.$tags$;" "(new TextDecoder).decode(xml.c14n(doc))"), njs_str("") }, { njs_str(NJS_XML_DOC "var doc2 = xml.parse(``);" "doc.note.$tags = [doc.note.to, doc2];" "(new TextDecoder).decode(xml.c14n(doc))"), njs_str("Tove") }, { njs_str(NJS_XML_DOC "var doc2 = xml.parse(``);" "doc.note.$tags = [doc2, doc.note.to];" "(new TextDecoder).decode(xml.c14n(doc))"), njs_str("Tove") }, }; static njs_unit_test_t njs_module_test[] = { { njs_str("function f(){return 2}; var f; f()"), njs_str("SyntaxError: \"f\" has already been declared in 1") }, { njs_str("function f(){return 2}; var f = 1; f()"), njs_str("SyntaxError: \"f\" has already been declared in 1") }, { njs_str("function f(){return 1}; function f(){return 2}; f()"), njs_str("SyntaxError: \"f\" has already been declared in 1") }, { njs_str("var f = 1; function f() {};"), njs_str("SyntaxError: \"f\" has already been declared in 1") }, { njs_str("{ var f = 1; } function f() {};"), njs_str("SyntaxError: \"f\" has already been declared in 1") }, { njs_str("function f(v) {var f = v;}; f(1); f"), njs_str("[object Function]") }, }; static njs_unit_test_t njs_externals_test[] = { { njs_str("(new ExternalError('XXX')) instanceof ExternalError"), njs_str("true") }, { njs_str("(new ExternalError('XXX')) instanceof Error"), njs_str("true") }, { njs_str("(new ExternalError()).message"), njs_str("") }, { njs_str("(new ExternalError('XXX')).message"), njs_str("XXX") }, { njs_str("(new ExternalError('XXX')).constructor == ExternalError"), njs_str("true") }, { njs_str("(new ExternalError('XXX')).name"), njs_str("ExternalError") }, { njs_str("(new ExternalError('XXX')).__proto__.name"), njs_str("ExternalError") }, { njs_str("(new ExternalError('XXX')).__proto__.__proto__.name"), njs_str("Error") }, { njs_str("(new ExternalError('XXX'))"), njs_str("ExternalError: XXX") }, { njs_str("(new ExternalError('XXX')).toString()"), njs_str("ExternalError: XXX") }, { njs_str("njs.dump(new ExternalError('XXX'))"), njs_str("ExternalError: XXX") }, { njs_str("JSON.stringify(new ExternalError('XXX'))"), njs_str("{}") }, { njs_str("Object.getOwnPropertyNames(new ExternalError('XXX'))"), njs_str("message") }, { njs_str("var ee; try{ $r.customException() } catch (e) { if (!(e instanceof ExternalError)) { throw 'Oops'} ee = e;}; ee.toString()"), njs_str("ExternalError: Oops") }, { njs_str("typeof $r"), njs_str("object") }, { njs_str("var a = $r.uri, b = $r2.uri, c = $r3.uri; a+b+c"), njs_str("АБВαβγabc") }, { njs_str("var a = $r.uri; $r.uri = $r2.uri; $r2.uri = a; $r2.uri+$r.uri"), njs_str("АБВαβγ") }, { njs_str("var a = $r.uri; a +' '+ a.length +' '+ a"), njs_str("АБВ 3 АБВ") }, { njs_str("$r.uri = 'αβγ'; var a = $r.uri; a.length +' '+ a"), njs_str("3 αβγ") }, { njs_str("$r.uri.length +' '+ $r.uri"), njs_str("3 АБВ") }, { njs_str("var t; " "switch ($r3.uri) {" "case 'abc': " " t='A'; " " break; " "default: " " t='F'; " "}; t"), njs_str("A") }, { njs_str("$r.uri = $r.uri.substr(1); $r.uri.length +' '+ $r.uri"), njs_str("2 БВ") }, { njs_str("'' + $r.props.a + $r2.props.a + $r.props.a"), njs_str("121") }, { njs_str("var p1 = $r.props, p2 = $r2.props; '' + p2.a + p1.a"), njs_str("21") }, { njs_str("$r.props = $r2.props; $r.props.a"), njs_str("2") }, { njs_str("var p1 = $r.props, p2 = $r2.props; '' + p1.a + p2.a"), njs_str("12") }, { njs_str("var p = $r3.props; p.a = 1"), njs_str("TypeError: Cannot assign to read-only property \"a\" of object") }, { njs_str("var p = $r3.props; delete p.a"), njs_str("TypeError: Cannot delete property \"a\" of object") }, { njs_str("$r.vars.p + $r2.vars.q + $r3.vars.k"), njs_str("pvalqvalkval") }, { njs_str("$r.vars.unset"), njs_str("undefined") }, { njs_str("['k', 'unknown'].map(v=>v in $r3.consts)"), njs_str("true,false") }, { njs_str("['a', 'unknown'].map(v=>v in $r3.props)"), njs_str("true,false") }, { njs_str("var v = $r3.vars; v.k"), njs_str("kval") }, { njs_str("var v = $r3.vars; v.unset = 1; v.unset"), njs_str("1") }, { njs_str("$r3.a = 1; Object.getOwnPropertyDescriptors($r3).a.value"), njs_str("1") }, { njs_str("Object.defineProperty($r3.vars, 'a', {value:1}); $r3.vars.a"), njs_str("1") }, { njs_str("$r3.vars.p = 'a'; delete $r3.vars.p; $r3.vars.p"), njs_str("undefined") }, { njs_str("$r3.vars.p = 'a'; delete $r3.vars.p; $r3.vars.p = 'b'; $r3.vars.p"), njs_str("b") }, { njs_str("$r3.vars.error = 1"), njs_str("Error: cannot set \"error\" prop") }, { njs_str("delete $r3.vars.error"), njs_str("Error: cannot delete \"error\" prop") }, { njs_str("delete $r3.vars.e"), njs_str("true") }, { njs_str("delete $r.consts"), njs_str("true") }, { njs_str("$r3.consts.k"), njs_str("kval") }, { njs_str("$r3.consts.k = 1"), njs_str("TypeError: Cannot assign to read-only property \"k\" of object") }, { njs_str("delete $r3.consts.k"), njs_str("TypeError: Cannot delete property \"k\" of object") }, { njs_str("delete $r3.vars.p; $r3.vars.p"), njs_str("undefined") }, { njs_str("var a = $r.host; a +' '+ a.length +' '+ a"), njs_str("АБВГДЕЁЖЗИЙ 11 АБВГДЕЁЖЗИЙ") }, { njs_str("var a = $r.host; a.substr(1, 1)"), njs_str("Б") }, { njs_str("var a = $r.header['User-Agent']; a +' '+ a.length +' '+ a"), njs_str("User-Agent|АБВ 14 User-Agent|АБВ") }, { njs_str("var a='', p;" "for (p in $r.header) { a += p +':'+ $r.header[p] +',' }" "a"), njs_str("01:01|АБВ,02:02|АБВ,03:03|АБВ,") }, { njs_str("$r.method('YES')"), njs_str("АБВ") }, { njs_str("$r.create('XXX').uri"), njs_str("XXX") }, { njs_str("$r.create.call([], 'XXX')"), njs_str("TypeError: \"this\" is not an external") }, { njs_str("var sr = $r.create('XXX'); sr.uri = 'YYY'; sr.uri"), njs_str("YYY") }, { njs_str("var sr = $r.create('XXX'), sr2 = $r.create('YYY');" "sr.uri = 'ZZZ'; " "sr.uri + sr2.uri"), njs_str("ZZZYYY") }, { njs_str("var sr = $r.create('XXX'); sr.vars.p = 'a'; sr.vars.p"), njs_str("a") }, { njs_str("var r = new ExternalConstructor('XXX'); r.uri"), njs_str("XXX") }, { njs_str("var p; for (p in $r.method);"), njs_str("undefined") }, { njs_str("'uri' in $r"), njs_str("true") }, { njs_str("'one' in $r"), njs_str("false") }, { njs_str("'a' in $r.props"), njs_str("true") }, { njs_str("delete $r.uri"), njs_str("TypeError: Cannot delete property \"uri\" of object") }, { njs_str("delete $shared.uri"), njs_str("TypeError: Cannot delete property \"uri\" of object") }, { njs_str("delete $r.one"), njs_str("true") }, { njs_str("delete $r.vars"), njs_str("TypeError: Cannot delete property \"vars\" of object") }, { njs_str("delete $r.header; $r.header"), njs_str("undefined") }, { njs_str("$r.header = 1; $r.header"), njs_str("1") }, { njs_str("$r.method.call($r, 'YES')"), njs_str("АБВ") }, { njs_str("var f = $r.method.bind($r); f('YES')"), njs_str("АБВ") }, { njs_str("function f(fn, arg) {return fn(arg);}; f($r.method.bind($r), 'YES')"), njs_str("АБВ") }, { njs_str("$r.method.apply($r, ['YES'])"), njs_str("АБВ") }, { njs_str("$shared.method.apply($r, ['YES'])"), njs_str("АБВ") }, { njs_str("$r.method.call([], 'YES')"), njs_str("TypeError: \"this\" is not an external") }, { njs_str("$r.nonexistent"), njs_str("undefined") }, { njs_str("$shared.nonexistent"), njs_str("undefined") }, { njs_str("njs.dump($r).startsWith('External')"), njs_str("true") }, { njs_str("Object.keys($r.header)"), njs_str("01,02,03") }, { njs_str("Object.values($r.header)"), njs_str("01|АБВ,02|АБВ,03|АБВ") }, { njs_str("njs.dump(Object.entries($r.header))"), njs_str("[['01','01|АБВ'],['02','02|АБВ'],['03','03|АБВ']]") }, { njs_str("njs.dump($r.header)"), njs_str("Header {01:'01|АБВ',02:'02|АБВ',03:'03|АБВ'}") }, { njs_str("njs.dump($r.header2)"), njs_str("Header2 {01:'01|АБВ',02:'02|АБВ',03:'03|АБВ'}") }, { njs_str("var o = {b:$r.props.b}; o.b"), njs_str("42") }, { njs_str("$r2.uri == 'αβγ' && $r2.uri === 'αβγ'"), njs_str("true") }, #if (NJS_HAVE_OPENSSL) #define NCRYPTO "crypto," #else #define NCRYPTO "" #endif { njs_str("Object.keys(this).sort()"), njs_str("$262,$r,$r2,$r3,$shared,ExternalConstructor,ExternalError," "ExternalNull," NCRYPTO "global,njs,process") }, { njs_str("Object.getOwnPropertySymbols($r2)[0] == Symbol.toStringTag"), njs_str("true") }, { njs_str("Object.getOwnPropertyDescriptors($r2)[Symbol.toStringTag].value"), njs_str("External") }, { njs_str("Object.getPrototypeOf($r3) === Object.prototype"), njs_str("true") }, { njs_str("Object.isExtensible($r3)"), njs_str("true") }, { njs_str("$r3[0] = 0; $r3[1] = 1; $r3.length = 2;" "Array.prototype.join.call($r3, '|')"), njs_str("0|1") }, { njs_str("$r3.toJSON = ()=> 'R3';" "JSON.stringify($r3)"), njs_str("\"R3\"") }, { njs_str("$r3[0] = 0; $r3[1] = 1; $r3.length = 2;" "$r3.__proto__ = Array.prototype; $r3.join('|')"), njs_str("0|1") }, { njs_str("[this, global, globalThis]" ".every(v=> { var r = njs.dump(v); return ['$r', 'global', njs.version].every(v=>r.includes(v))})"), njs_str("true") }, { njs_str("var r = JSON.parse(JSON.stringify($r));" "[r.uri, r.host, r.props.a, njs.dump(r.vars), njs.dump(r.consts), r.header['02']]"), njs_str("АБВ,АБВГДЕЁЖЗИЙ,1,{},{},02|АБВ") }, { njs_str("var s = (new TextDecoder()).decode($r.buffer); [s, s.length]"), njs_str("АБВГДЕЁЖЗИЙ,11") }, { njs_str("var b = $r.buffer; " "b[4] = '@'.codePointAt(0); b[5] = '#'.codePointAt(0);" "var s = (new TextDecoder()).decode(b); [s, s.length]"), njs_str("АБ@#ГДЕЁЖЗИЙ,12") }, { njs_str("var b = $r.buffer; " "b.copyWithin(16,0,6);" "var s = (new TextDecoder()).decode(b); [s, s.length]"), njs_str("АБВГДЕЁЖАБВ,11") }, { njs_str("var b = $r.buffer; " "b.fill('#'.codePointAt(0));" "var s = (new TextDecoder()).decode(b); [s, s.length]"), njs_str("######################,22") }, { njs_str("var b = $r.buffer; " "b.set(['@'.codePointAt(0), '#'.codePointAt(0)], 4);" "var s = (new TextDecoder()).decode(b); [s, s.length]"), njs_str("АБ@#ГДЕЁЖЗИЙ,12") }, { njs_str("var b = $r.buffer; " "var u16 = new Uint16Array(b.buffer); u16.reverse();" "var s = (new TextDecoder()).decode(u16); [s, s.length]"), njs_str("ЙИЗЖЁЕДГВБА,11") }, { njs_str("new Uint8Array($r.buffer.sort().slice(0,3))"), njs_str("129,144,145") }, { njs_str("$r.buffer instanceof Buffer"), njs_str("true") }, { njs_str("let ctor = Object.getPrototypeOf(async function(){}).constructor;" "let f = new ctor();" "$r.retval(f())"), njs_str("[object Promise]") }, { njs_str("let ctor = Object.getPrototypeOf(async function(){}).constructor;" "let f = new ctor('x', 'await 1; return x');" "$r.retval(f(1))"), njs_str("[object Promise]") }, { njs_str("let ctor = Object.getPrototypeOf(async function(){}).constructor;" "let f = new ctor('x', 'await 1; return x');" "f(1).then($r.retval)"), njs_str("1") }, { njs_str("$r.retval(Promise.all([async () => [await x('X')]]))"), njs_str("[object Promise]") }, { njs_str("var r = [1].map(v => {" " function C(a) {" " a(a, parseInt);" " };" "" " Promise.all.apply(C);" "});" "r[0]"), /* TODO: RejectAbrupt() exception should not percolate */ njs_str("TypeError: resolve is not callable") }, { njs_str("Promise.all({length: 1025}) " ".then(v => $r.retval(v[0]))"), /* TODO: TypeError: object is not iterable */ njs_str("undefined") }, { njs_str("Promise.allSettled({length: 1025}) " ".then(v => $r.retval(v[0]))"), /* TODO: TypeError: object is not iterable */ njs_str("undefined") }, { njs_str("var r = [1].map(v => {" " function C(a) {" " a(a, parseInt);" " };" "" " Promise.race.apply(C);" "});" "r[0]"), /* TODO: RejectAbrupt() exception should not percolate */ njs_str("TypeError: resolve is not callable") }, { njs_str("let obj = { a: 1, b: 2};" "function cb(r) { r.retval(obj.a); }" "$r.subrequest($r)" ".then(reply => cb(reply))"), njs_str("1") }, { njs_str("let obj = { a: 1, b: 2};" "function cb(r, select) { r.retval(obj[select]); }" "$r.subrequest('b')" ".then(select => cb($r, select))"), njs_str("2") }, { njs_str("function pr(x) { return new Promise(resolve => {resolve(x + ':pr')}); };" "Promise.all(['a', 'b', 'c'].map(async (v) => {" " return await pr(v + ':async');" "}))" ".then(v => $r.retval(v))"), njs_str("a:async:pr,b:async:pr,c:async:pr") }, { njs_str("function pr(x) { return new Promise(resolve => {resolve(x + ':pr')}); };" "Promise.all(['a', 'b', 'c'].map(async (v) => {" " let r = await pr(v + ':async');" " let r2 = await pr(r + ':async2');" " return r2 + ':r';" "}))" ".then(v => $r.retval(v))"), njs_str("a:async:pr:async2:pr:r,b:async:pr:async2:pr:r,c:async:pr:async2:pr:r") }, { njs_str("async function f () {" " var p = await Promise.race({length:1});" " for (const v in 'test') { }" "};" "f().then(v => $r.retval('done'))"), njs_str("done") }, }; static njs_unit_test_t njs_async_handler_test[] = { { njs_str("globalThis.main = (function() {" " function cb(r) { r.retval(1); }" " function handler(r) {" " r.subrequest(r)" " .then(reply => cb(reply))" " };" " return {handler};" "})();" ), njs_str("1") }, { njs_str("globalThis.main = (function() {" " let obj = { a: 1, b: 2};" " function cb(r) { r.retval(obj.a); }" " function handler(r) {" " r.subrequest(r)" " .then(reply => cb(reply))" " };" " return {handler};" "})();" ), njs_str("1") }, }; static njs_unit_test_t njs_shared_test[] = { { njs_str("var cr = require('crypto'); cr.createHash"), njs_str("[object Function]") }, { njs_str("var cr = require('crypto'); cr.createHash('md5')"), njs_str("[object Hash]") }, { njs_str("import cr from 'crypto'; cr.createHash"), njs_str("[object Function]") }, { njs_str("import cr from 'crypto'; cr.createHash('md5')"), njs_str("[object Hash]") }, { njs_str("var fs = require('fs'); fs.a = 1; fs.a"), njs_str("1") }, { njs_str("require('fs').promises === require('fs').promises"), njs_str("true") }, { njs_str("isFinite()"), njs_str("false") }, { njs_str("Number.isFinite(function(){})"), njs_str("false") }, { njs_str("isFin()"), njs_str("ReferenceError: \"isFin\" is not defined\n" " at main (:1)\n") }, { njs_str("isNaN(function(){})"), njs_str("true") }, { njs_str("var a = $r.uri; $r.uri = $r2.uri; $r2.uri = a; $r2.uri + $r.uri"), njs_str("АБВαβγ") }, { njs_str("njs.dump($r.props)"), njs_str("{a:'1',b:42,c:{d:1024}}") }, { njs_str("njs.dump($shared.props)"), njs_str("{a:'11',b:42,c:{d:13}}") }, { njs_str("var r = JSON.parse(JSON.stringify($shared));" "[r.uri, r.host, r.props.a, njs.dump(r.vars), njs.dump(r.consts), r.header['02']]"), njs_str("shared,АБВГДЕЁЖЗИЙ,11,{},{},02|АБВ") }, { njs_str("$shared.toString()"), njs_str("[object External]") }, { njs_str("$shared.toString().length"), njs_str("17") }, { njs_str("delete $shared.method; $shared.method"), njs_str("undefined") }, { njs_str("$shared.method = () => 1; $shared.method()"), njs_str("1") }, { njs_str("$shared.method = function() {return this.props.a;}; $shared.method()"), njs_str("11") }, { njs_str("var r; for (var i = 0; i < 2**10; i++) {r = $r.create('XXX').uri;}"), njs_str("undefined") }, { njs_str("$r.vars.unset = 'a'; $r2.vars.unset = 'b';" "$r.vars.unset + $r2.vars.unset"), njs_str("ab") }, { njs_str("$r.vars.unset = 1; $r2.vars.unset = 2;" "$r.vars.unset + $r2.vars.unset"), njs_str("3") }, { njs_str("$r3.vars.p = 'a'; $r3.vars.p2 = 'b';" "$r3.vars.p + $r3.vars.p2"), njs_str("ab") }, { njs_str("delete $r3.vars.p; $r3.vars.p"), njs_str("undefined") }, { njs_str("var sr = $r.create('XXX'); sr.vars.p = 'a'; sr.vars.p"), njs_str("a") }, { njs_str("$r.bind('XXX', 37); XXX"), njs_str("37") }, { njs_str("var fs = require('fs'); fs.readFileSync()"), njs_str("TypeError: \"path\" must be a string or Buffer\n" " at fs.readFileSync (native)\n" " at main (:1)\n") }, { njs_str("import fs from 'fs'; fs.readFileSync()"), njs_str("TypeError: \"path\" must be a string or Buffer\n" " at fs.readFileSync (native)\n" " at main (:1)\n") }, { njs_str("var f = new Function('return 1;'); f();"), njs_str("1") }, { njs_str("var sum = new Function('a', 'b', 'return a + b');" "sum(2, 4);"), njs_str("6") }, { njs_str("var sum = new Function('a, b', 'return a + b');" "sum(2, 4);"), njs_str("6") }, { njs_str("ExternalNull.get()"), njs_str("undefined") }, { njs_str("ExternalNull.set(37); ExternalNull.get()"), njs_str("37") }, { njs_str("ExternalNull.set(23); ExternalNull.set(37); ExternalNull.get()"), njs_str("37") }, { njs_str("var v = Math.round(Math.random() * 1000); ExternalNull.set(v);" "ExternalNull.get() == v"), njs_str("true") }, #if (NJS_HAVE_OPENSSL) { njs_str("var cr = Object.entries(global).filter((v) => v[0] == 'crypto')[0][1];" "cr.abc = 1; cr.abc"), njs_str("1") }, #endif { njs_str("JSON.stringify(preload)"), njs_str("{\"a\":[1,{\"b\":2,\"c\":3}]}") }, { njs_str("preload.a.prop = 1"), njs_str("TypeError: Cannot add property \"prop\", object is not extensible\n" " at main (:1)\n") }, { njs_str("preload.a[0] = 2"), njs_str("TypeError: Cannot assign to read-only property \"0\" of array\n" " at main (:1)\n") }, { njs_str("preload.a.push(2)"), njs_str("TypeError: (intermediate value)[\"push\"] is not a function\n" " at main (:1)\n") }, { njs_str("Array.prototype.push.call(preload.a, 'waka')"), njs_str("TypeError: Cannot add property \"2\", object is not extensible\n" " at Array.prototype.push (native)\n" " at Function.prototype.call (native)\n" " at main (:1)\n") }, }; static njs_unit_test_t njs_tz_test[] = { { njs_str("var d = new Date(1); d = d + ''; d.slice(0, 33)"), njs_str("Thu Jan 01 1970 12:45:00 GMT+1245") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45); d.getTime()"), njs_str("1308895200000") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45); d.valueOf()"), njs_str("1308895200000") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45);" "d.toString().slice(0, 33)"), njs_str("Fri Jun 24 2011 18:45:00 GMT+1245") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45);" "d.toTimeString().slice(0, 17)"), njs_str("18:45:00 GMT+1245") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45); d.toUTCString()"), njs_str("Fri, 24 Jun 2011 06:00:00 GMT") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45, 12, 625);" "d.toISOString()"), njs_str("2011-06-24T06:00:12.625Z") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45); d.getUTCHours()"), njs_str("6") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45); d.getUTCMinutes()"), njs_str("0") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45, 12, 625);" "d.getTimezoneOffset()"), njs_str("-765") }, { njs_str("var d = new Date(1308895323625); d.setMinutes(3, 2, 5003);" "d.getTime()"), njs_str("1308892687003") }, { njs_str("var d = new Date(1308895323625); d.setMinutes(3, 2);" "d.getTime()"), njs_str("1308892682625") }, { njs_str("var d = new Date(1308895323625); d.setMinutes(3);" "d.getTime()"), njs_str("1308892683625") }, { njs_str("var d = new Date(1308895323625); d.setHours(20, 3, 2, 5003);" "d.getTime()"), njs_str("1308899887003") }, { njs_str("var d = new Date(1308895323625); d.setHours(20, 3, 2);" "d.getTime()"), njs_str("1308899882625") }, { njs_str("var d = new Date(1308895323625); d.setHours(20, 3);" "d.getTime()"), njs_str("1308899883625") }, { njs_str("var d = new Date(1308895323625); d.setHours(20);" "d.getTime()"), njs_str("1308902523625") }, { njs_str("var d = new Date(NaN); d.setHours(20);" "d.getTime()"), njs_str("NaN") }, { njs_str("var d = new Date(1308895323625); d.setMonth(2, 10);" "d.getTime()"), njs_str("1299733323625") }, { njs_str("var d = new Date(1308895323625); d.setMonth(2);" "d.getTime()"), njs_str("1300942923625") }, { njs_str("var d = new Date(1308895323625); d.setFullYear(2010, 2, 10);" "d.getTime()"), njs_str("1268197323625") }, { njs_str("var d = new Date(1308895323625); d.setFullYear(2010, 2);" "d.getTime()"), njs_str("1269406923625") }, { njs_str("var d = new Date(2011, 5, 24, 18, 45, 12, 625);" "d.toJSON(1)"), njs_str("2011-06-24T06:00:12.625Z") }, { njs_str("Date.parse('1970-09-28T06:00:00.000')"), njs_str("23303700000") }, #if (NJS_TIME_T_SIZE == 8) { njs_str("var d = new Date(-1, 0);" "d.toISOString()"), njs_str("-000002-12-31T11:47:00.000Z") }, { njs_str("var d = new Date(-1, 0);" "d.getTime()"), njs_str("-62198799180000") }, { njs_str("var d = new Date(-1, 0);" "d.getTimezoneOffset()"), njs_str("-733") }, #endif { njs_str("var d = new Date(1970, 6);" "d.getTimezoneOffset()"), njs_str("-765") }, }; static njs_unit_test_t njs_regexp_optional_tests[] = { { njs_str("/[\\\\u02E0-\\\\u02E4]/"), njs_str("/[\\\\u02E0-\\\\u02E4]/") }, { njs_str("/[\\u02E0-\\u02E4]/"), njs_str("/[\\u02E0-\\u02E4]/") }, { njs_str("RegExp('[\\\\u02E0-\\\\u02E4]')"), njs_str("/[\\u02E0-\\u02E4]/") }, { njs_str("/[\\u0430-\\u044f]+/.test('тест')"), njs_str("true") }, { njs_str("RegExp('[\\\\u0430-\\\\u044f]+').test('тест')"), njs_str("true") }, { njs_str("RegExp('[\\\\u0430-\\\\u044f]+').exec('тест')[0]"), njs_str("тест") }, { njs_str("/[\\uFDE0-\\uFFFD]/g; export default 1"), njs_str("SyntaxError: Illegal export statement in 1") }, { njs_str("RegExp(RegExp('\x00]]')).test('\x00]]')"), njs_str("true") }, { njs_str("RegExp('\0').test('\0')"), njs_str("true") }, { njs_str("RegExp('\x00').test('\0')"), njs_str("true") }, #ifndef NJS_HAVE_PCRE2 { njs_str("RegExp('\x00\\\\x00').source"), njs_str("\\u0000\\x00") }, { njs_str("/\\\0/"), njs_str("/\\\\u0000/") }, { njs_str("RegExp('\\\\\\0').source"), njs_str("\\\\u0000") }, #endif { njs_str("RegExp('[\0]').test('\0')"), njs_str("true") }, { njs_str("/[A-Za-z\\u00F8-\\u02FF]/.test('S')"), njs_str("true") }, { njs_str("/[A-Za-z\\u00F8-\\u02FF]/.test('ø')"), njs_str("true") }, }; static njs_unit_test_t njs_shell_test[] = { #define ENTER "\n\3" { njs_str("var a = 3" ENTER "a * 2" ENTER), njs_str("6") }, { njs_str("var a = \"aa\\naa\"" ENTER "a" ENTER), njs_str("'aa\\naa'") }, { njs_str("var a = 3" ENTER "var a = 'str'" ENTER "a" ENTER), njs_str("'str'") }, { njs_str("var a = 2" ENTER "a *= 2" ENTER "a *= 2" ENTER "a *= 2" ENTER), njs_str("16") }, { njs_str("var a = 2" ENTER "var b = 3" ENTER "a * b" ENTER), njs_str("6") }, { njs_str("var a = 2; var b = 3;" ENTER "a * b" ENTER), njs_str("6") }, { njs_str("function sq(f) { return f() * f() }" ENTER "sq(function () { return 3 })" ENTER), njs_str("9") }, { njs_str("var e = Error(); e.name = {}; e" ENTER), njs_str("[object Object]") }, { njs_str("var a = []; Object.defineProperty(a, 'b', {enumerable: true, get: Object}); a" ENTER), njs_str("[\n b: '[Getter]'\n]") }, { njs_str("var e = Error()" ENTER "Object.defineProperty(e, 'message', { configurable: true, set: Object })" ENTER "delete e.message; e" ENTER), njs_str("Error") }, { njs_str("var e = Error()" ENTER "Object.defineProperty(e, 'message', { configurable: true, get(){ return 'foo'} })" ENTER "e" ENTER), njs_str("Error: foo") }, { njs_str("function f() {};" ENTER "Object.defineProperty(f, 'name', { get() {void(0)} })" ENTER "f" ENTER), njs_str("[Function]") }, /* Temporary indexes */ { njs_str("var a = [1,2,3], i; for (i in a) {Object.seal({});}" ENTER), njs_str("undefined") }, { njs_str("var i; for (i in [1,2,3]) {Object.seal({});}" ENTER), njs_str("undefined") }, { njs_str("var a = 'A'; " "switch (a) {" "case 0: a += '0';" "case 1: a += '1';" "}; a" ENTER), njs_str("'A'") }, { njs_str("var a = 0; try { a = 5 }" "catch (e) { a = 9 } finally { a++ } a" ENTER), njs_str("6") }, { njs_str("/abc/i.test('ABC')" ENTER), njs_str("true") }, /* Interactive mode. */ { njs_str("var a = 1" ENTER "a" ENTER), njs_str("1") }, { njs_str("Number.prototype.test = 'test'" ENTER "Number.prototype.test" ENTER), njs_str("'test'") }, { njs_str("function f(a) {return a}" ENTER "function f(a) {return a}; f(2)" ENTER), njs_str("2") }, { njs_str("function f() {return 1}" ENTER "function f(a) {return 1}; f(2)" ENTER), njs_str("1") }, { njs_str("try {(new Function('function foo(){return 1}; ()=>{}breakhere'))} catch (e) {}" ENTER "foo()" ENTER), njs_str("ReferenceError: \"foo\" is not defined\n" " at main (:1)\n") }, /* Error handling */ { njs_str("var a = ;" ENTER "2 + 2" ENTER), njs_str("4") }, { njs_str("function f() { return b;" ENTER), njs_str("SyntaxError: Unexpected end of input in 1") }, { njs_str("function f() { return b;" ENTER "2 + 2" ENTER), njs_str("4") }, { njs_str("function f() { return function() { return 1" ENTER "2 + 2" ENTER), njs_str("4") }, { njs_str("function f() { return b;}" ENTER "2 + 2" ENTER), njs_str("4") }, { njs_str("function f(o) { return o.a.a;}; f{{}}" ENTER "2 + 2" ENTER), njs_str("4") }, { njs_str("function ff(o) {return o.a.a}" ENTER "function f(o) {try {return ff(o)} " " finally {return 1}}" ENTER "f({})" ENTER), njs_str("1") }, { njs_str("arguments" ENTER "function(){}()" ENTER), njs_str("SyntaxError: Unexpected token \"(\" in 1") }, { njs_str("var o = { toString: function() { return [1] } }; o" ENTER), njs_str("{\n toString: [Function: toString]\n}") }, { njs_str("var o = { toString: function() { return [1] } }" ENTER "o.toString()" ENTER), njs_str("[\n 1\n]") }, }; static njs_unit_test_t njs_backtraces_test[] = { { njs_str("function ff(o) {return o.a.a};" "function f(o) {return ff(o)};" "f({})"), njs_str("TypeError: cannot get property \"a\" of undefined\n" " at ff (:1)\n" " at f (:1)\n" " at main (:1)\n") }, { njs_str("function ff(o) {return o.a.a};" "function f(o) {try {return ff(o)} " " finally {return o.a.a}};" "f({})"), njs_str("TypeError: cannot get property \"a\" of undefined\n" " at f (:1)\n" " at main (:1)\n") }, { njs_str("function f(ff, o) {return ff(o)};" "f(function (o) {return o.a.a}, {})"), njs_str("TypeError: cannot get property \"a\" of undefined\n" " at anonymous (:1)\n" " at f (:1)\n" " at main (:1)\n") }, { njs_str("'str'.replace(/t/g," " function(m) {return m.a.a})"), njs_str("TypeError: cannot get property \"a\" of undefined\n" " at anonymous (:1)\n" " at RegExp.prototype[Symbol.replace] (native)\n" " at String.prototype.replace (native)\n" " at main (:1)\n") }, { njs_str("function f(o) {return Object.keys(o)};" "f()"), njs_str("TypeError: cannot convert undefined argument to object\n" " at Object.keys (native)\n" " at f (:1)\n" " at main (:1)\n") }, { njs_str("[].concat({}.a.a)"), njs_str("TypeError: cannot get property \"a\" of undefined\n" " at Array.prototype.concat (native)\n" " at main (:1)\n") }, { njs_str("''.repeat(-1)"), njs_str("RangeError: invalid count value\n" " at String.prototype.repeat (native)\n" " at main (:1)\n") }, { njs_str("Math.log({}.a.a)"), njs_str("TypeError: cannot get property \"a\" of undefined\n" " at Math.log (native)\n" " at main (:1)\n") }, { njs_str("var bound = Math.max.bind(null, {toString(){return {}}}); bound(1)"), njs_str("TypeError: Cannot convert object to primitive value\n" " at Math.max (native)\n" " at main (:1)\n") }, { njs_str("var ab = new ArrayBuffer(1);" "$262.detachArrayBuffer(ab);" "ab.byteLength"), njs_str("TypeError: detached buffer\n" " at ArrayBuffer.prototype.byteLength (native)\n" " at main (:1)\n") }, { njs_str("Object.prototype()"), njs_str("TypeError: (intermediate value)[\"prototype\"] is not a function\n" " at main (:1)\n") }, { njs_str("eval()"), njs_str("InternalError: Not implemented\n" " at eval (native)\n" " at main (:1)\n") }, { njs_str("$shared.method({}.a.a)"), njs_str("TypeError: cannot get property \"a\" of undefined\n" " at $shared.method (native)\n" " at main (:1)\n") }, { njs_str("new Function(\n\n@)"), njs_str("SyntaxError: Unexpected token \"@\" in 3") }, { njs_str("require()"), njs_str("TypeError: missing path\n" " at require (native)\n" " at main (:1)\n") }, { njs_str("require('crypto').createHash('sha')"), njs_str("TypeError: not supported algorithm: \"sha\"\n" " at crypto.createHash (native)\n" " at main (:1)\n") }, { njs_str("var h = require('crypto').createHash('sha1');" "h.update([])"), njs_str("TypeError: data is not a string or Buffer-like object\n" " at Hash.update (native)\n" " at main (:1)\n") }, { njs_str("require('crypto').createHmac('sha1', [])"), njs_str("TypeError: key is not a string or Buffer-like object\n" " at crypto.createHmac (native)\n" " at main (:1)\n") }, { njs_str("var h = require('crypto').createHmac('sha1', 'secret');" "h.update([])"), njs_str("TypeError: data is not a string or Buffer-like object\n" " at Hmac.update (native)\n" " at main (:1)\n") }, { njs_str("function f(o) {function f_in(o) {return o.a.a};" " return f_in(o)};" "f({})"), njs_str("TypeError: cannot get property \"a\" of undefined\n" " at f_in (:1)\n" " at f (:1)\n" " at main (:1)\n") }, { njs_str("function f(o) {var ff = function (o) {return o.a.a};" " return ff(o)};" "f({})"), njs_str("TypeError: cannot get property \"a\" of undefined\n" " at anonymous (:1)\n" " at f (:1)\n" " at main (:1)\n") }, { njs_str("var fs = require('fs');" "[" " 'access'," " 'accessSync'," " 'readFile'," " 'readFileSync'," " 'writeFile'," " 'writeFileSync'," " 'appendFile'," " 'appendFileSync'," " 'symlink'," " 'symlinkSync'," " 'unlink'," " 'unlinkSync'," " 'realpath'," " 'realpathSync'," "]" ".every(v=>{ try {fs[v]();} catch (e) { return e.stack.search(`fs.${v} `) >= 0}})"), njs_str("true") }, { njs_str("parseInt({ toString: function() { return [1] } })"), njs_str("TypeError: Cannot convert object to primitive value\n" " at parseInt (native)\n" " at main (:1)\n") }, { njs_str("function f(n) { if (n == 0) { throw 'a'; } return f(n-1); }; f(2)"), njs_str("a") }, { njs_str("Object.defineProperty(Function.__proto__, 'name', {get() { typeof 1;}});" "(new Uint8Array()).every()"), njs_str("TypeError: callback argument is not callable\n" " at TypedArray.prototype.every (native)\n" " at main (:1)\n") }, /* line numbers */ { njs_str("/**/(function(){throw Error();})()"), njs_str("Error\n" " at anonymous (:1)\n" " at main (:1)\n") }, { njs_str("/***/(function(){throw Error();})()"), njs_str("Error\n" " at anonymous (:1)\n" " at main (:1)\n") }, { njs_str("/*\n**/(function(){throw Error();})()"), njs_str("Error\n" " at anonymous (:2)\n" " at main (:2)\n") }, { njs_str("({})\n.a\n.a"), njs_str("TypeError: cannot get property \"a\" of undefined\n" " at main (:3)\n") }, { njs_str("1\n+a"), njs_str("ReferenceError: \"a\" is not defined\n" " at main (:2)\n") }, { njs_str("\n`\n${Object}\n${a}`"), njs_str("ReferenceError: \"a\" is not defined\n" " at main (:4)\n") }, { njs_str("function log(v) {}\nlog({}\n.a\n.a)"), njs_str("TypeError: cannot get property \"a\" of undefined\n" " at main (:4)\n") }, { njs_str("\nfor (var i = 0;\n i < a;\n i++) { }\n"), njs_str("ReferenceError: \"a\" is not defined\n" " at main (:3)\n") }, { njs_str("\nfor (var i = 0;\n i < 5;\n a) {\n }"), njs_str("ReferenceError: \"a\" is not defined\n" " at main (:4)\n") }, { njs_str("Math\n.min(1,\na)"), njs_str("ReferenceError: \"a\" is not defined\n" " at Math.min (native)\n" " at main (:3)\n") }, }; typedef struct { njs_bool_t disassemble; njs_str_t filter; njs_bool_t verbose; njs_uint_t externals; njs_bool_t module; njs_uint_t repeat; njs_bool_t unsafe; njs_bool_t backtrace; njs_bool_t handler; njs_bool_t async; njs_bool_t preload; unsigned seed; } njs_opts_t; typedef struct { njs_uint_t passed; njs_uint_t failed; } njs_stat_t; typedef struct { njs_vm_t *vm; njs_opaque_value_t retval; njs_external_env_t *env; njs_external_env_t env0; enum { sw_start = 0, sw_handler, sw_loop, sw_done } state; } njs_external_state_t; typedef struct { njs_external_state_t *states; njs_uint_t size; njs_uint_t current; } njs_runtime_t; static void njs_unit_test_report(njs_str_t *name, njs_stat_t *prev, njs_stat_t *current) { njs_stat_t stat; stat.failed = current->failed - prev->failed; stat.passed = current->passed - prev->passed; njs_printf("%V tests: %s [%d/%d]\n", name, stat.failed ? "FAILED" : "PASSED", stat.passed, stat.passed + stat.failed); } static njs_int_t njs_external_state_init(njs_vm_t *vm, njs_external_state_t *s, njs_opts_t *opts) { njs_int_t ret; if (opts->externals) { s->env = &s->env0; ret = njs_external_env_init(s->env); if (ret != NJS_OK) { njs_stderror("njs_external_env_init() failed\n"); return NJS_ERROR; } } else { s->env = NULL; } s->vm = njs_vm_clone(vm, s->env); if (s->vm == NULL) { njs_stderror("njs_vm_clone() failed\n"); return NJS_ERROR; } if (opts->externals) { ret = njs_externals_init(s->vm); if (ret != NJS_OK) { njs_stderror("njs_externals_init() failed\n"); return NJS_ERROR; } } s->state = sw_start; return NJS_OK; } static njs_int_t njs_external_retval(njs_external_state_t *state, njs_int_t ret, njs_str_t *s) { if (state->env != NULL && ret == NJS_OK && njs_value_is_valid(njs_value_arg(&state->env->retval))) { return njs_vm_value_string(state->vm, s, njs_value_arg(&state->env->retval)); } return njs_vm_value_string(state->vm, s, njs_value_arg(&state->retval)); } static njs_runtime_t * njs_runtime_init(njs_vm_t *vm, njs_opts_t *opts) { njs_int_t ret; njs_uint_t i; njs_runtime_t *rt; rt = njs_mp_alloc(njs_vm_memory_pool(vm), sizeof(njs_runtime_t)); if (rt == NULL) { return NULL; } rt->size = opts->repeat; rt->states = njs_mp_alloc(njs_vm_memory_pool(vm), sizeof(njs_external_state_t) * rt->size); if (rt->states == NULL) { return NULL; } rt->current = 0; srandom(opts->seed); for (i = 0; i < rt->size; i++) { ret = njs_external_state_init(vm, &rt->states[i], opts); if (ret != NJS_OK) { njs_stderror("njs_external_state_init() failed\n"); return NULL; } } return rt; } static njs_external_state_t * njs_runtime_next_state(njs_runtime_t *rt, njs_opts_t *opts) { unsigned next, n; n = 0; next = ((opts->async) ? (unsigned) random() : rt->current++) % rt->size; while (rt->states[next].state == sw_done) { next++; next = next % rt->size; n++; if (n == rt->size) { return NULL; } } return &rt->states[next]; } static void njs_runtime_destroy(njs_runtime_t *rt) { njs_uint_t i; for (i = 0; i < rt->size; i++) { if (rt->states[i].vm != NULL) { njs_vm_destroy(rt->states[i].vm); } } } static njs_int_t njs_process_test(njs_external_state_t *state, njs_opts_t *opts, njs_unit_test_t *expected) { njs_int_t ret; njs_str_t s; njs_bool_t success; njs_opaque_value_t request; static const njs_str_t handler_str = njs_str("main.handler"); static const njs_str_t request_str = njs_str("$r"); ret = NJS_OK; switch (state->state) { case sw_start: state->state = sw_handler; ret = njs_vm_start(state->vm, njs_value_arg(&state->retval)); if (ret != NJS_OK) { goto done; } if (opts->async) { return NJS_OK; } /* Fall through. */ case sw_handler: state->state = sw_loop; if (opts->handler) { ret = njs_vm_value(state->vm, &request_str, njs_value_arg(&request)); if (ret != NJS_OK) { njs_stderror("njs_vm_value(\"%V\") failed\n", &request_str); return NJS_ERROR; } ret = njs_external_call(state->vm, &handler_str, njs_value_arg(&request), 1); if (ret == NJS_ERROR) { goto done; } if (opts->async) { return NJS_OK; } } /* Fall through. */ case sw_loop: default: for ( ;; ) { for ( ;; ) { ret = njs_vm_execute_pending_job(state->vm); if (ret <= NJS_OK) { if (ret == NJS_ERROR) { return NJS_ERROR; } break; } } ret = njs_external_process_events(state->vm, state->env); if (ret == NJS_ERROR) { njs_stderror("njs_external_process_events() failed\n"); return NJS_ERROR; } if (ret == NJS_OK) { break; } if (opts->async) { return NJS_OK; } } } done: state->state = sw_done; if (njs_external_retval(state, ret, &s) != NJS_OK) { njs_stderror("njs_external_retval() failed\n"); return NJS_ERROR; } success = njs_strstr_eq(&expected->ret, &s); if (!success) { njs_stderror("njs(\"%V\")\nexpected: \"%V\"\n got: \"%V\"\n", &expected->script, &expected->ret, &s); return NJS_DECLINED; } njs_vm_destroy(state->vm); state->vm = NULL; return NJS_OK; } njs_module_t *njs_unit_test_addon_modules[] = { &njs_unit_test_262_module, NULL, }; njs_module_t *njs_unit_test_addon_external_modules[] = { &njs_unit_test_262_module, &njs_unit_test_external_module, NULL, }; static njs_int_t njs_unit_test(njs_unit_test_t tests[], size_t num, njs_str_t *name, njs_opts_t *opts, njs_stat_t *stat) { u_char *start, *end; njs_vm_t *vm; njs_int_t ret; njs_str_t s; njs_bool_t success; njs_uint_t i; njs_stat_t prev; njs_vm_opt_t options; njs_runtime_t *rt; njs_opaque_value_t retval; njs_external_state_t *state; njs_str_t preload = njs_str( "globalThis.preload = JSON.parse(" "'{\"a\": [1, {\"b\": 2, \"c\": 3}]}'," "function (k, v) {" "if (v instanceof Object) {" "Object.freeze(Object.setPrototypeOf(v, null));" "}" "return v;" "}" ");" ); vm = NULL; rt = NULL; prev = *stat; ret = NJS_ERROR; for (i = 0; i < num; i++) { if (opts->verbose) { njs_printf("\"%V\"\n", &tests[i].script); } njs_vm_opt_init(&options); options.init = opts->preload; options.module = opts->module; options.unsafe = opts->unsafe; options.backtrace = opts->backtrace; options.addons = opts->externals ? njs_unit_test_addon_external_modules : njs_unit_test_addon_modules; vm = njs_vm_create(&options); if (vm == NULL) { njs_printf("njs_vm_create() failed\n"); goto done; } if (opts->preload) { start = preload.start; end = start + preload.length; ret = njs_vm_compile(vm, &start, end); if (ret != NJS_OK) { njs_printf("njs_vm_compile() preload failed\n"); goto done; } ret = njs_vm_start(vm, njs_value_arg(&retval)); if (ret != NJS_OK) { njs_printf("njs_vm_start() preload failed\n"); goto done; } ret = njs_vm_reuse(vm); if (ret != NJS_OK) { njs_printf("njs_vm_reuse() failed\n"); goto done; } } start = tests[i].script.start; end = start + tests[i].script.length; ret = njs_vm_compile(vm, &start, end); if (ret == NJS_OK && start == end) { if (opts->disassemble) { njs_disassembler(vm); } rt = njs_runtime_init(vm, opts); if (rt == NULL) { njs_stderror("njs_runtime_init() failed\n"); goto done; } for ( ;; ) { state = njs_runtime_next_state(rt, opts); if (state == NULL) { break; } ret = njs_process_test(state, opts, &tests[i]); if (ret != NJS_OK) { if (ret == NJS_DECLINED) { break; } njs_stderror("njs_process_test() failed\n"); goto done; } } success = (ret == NJS_OK); if (rt != NULL) { njs_runtime_destroy(rt); rt = NULL; } } else { if (ret != NJS_OK) { if (njs_vm_exception_string(vm, &s) != NJS_OK) { njs_printf("njs_vm_exception_string() failed\n"); goto done; } } else { s = njs_str_value("Error: " "Extra characters at the end of the script"); } success = njs_strstr_eq(&tests[i].ret, &s); if (!success) { njs_stderror("njs(\"%V\")\nexpected: \"%V\"\n" " got: \"%V\"\n", &tests[i].script, &tests[i].ret, &s); } } if (success) { stat->passed++; } else { stat->failed++; } njs_vm_destroy(vm); vm = NULL; } ret = NJS_OK; done: if (rt != NULL) { njs_runtime_destroy(rt); } if (vm != NULL) { njs_vm_destroy(vm); } njs_unit_test_report(name, &prev, stat); return ret; } static njs_int_t njs_interactive_test(njs_unit_test_t tests[], size_t num, njs_str_t *name, njs_opts_t *opts, njs_stat_t *stat) { u_char *start, *last, *end; njs_vm_t *vm; njs_int_t ret; njs_str_t s; njs_uint_t i; njs_stat_t prev; njs_bool_t success; njs_vm_opt_t options; njs_opaque_value_t retval; vm = NULL; prev = *stat; ret = NJS_ERROR; for (i = 0; i < num; i++) { if (opts->verbose) { njs_printf("\"%V\"\n", &tests[i].script); } njs_vm_opt_init(&options); options.init = 1; options.interactive = 1; options.backtrace = 1; options.addons = opts->externals ? njs_unit_test_addon_external_modules : njs_unit_test_addon_modules; vm = njs_vm_create(&options); if (vm == NULL) { njs_printf("njs_vm_create() failed\n"); goto done; } if (opts->externals) { ret = njs_externals_init(vm); if (ret != NJS_OK) { goto done; } } start = tests[i].script.start; last = start + tests[i].script.length; end = NULL; for ( ;; ) { start = (end != NULL) ? end + njs_length(ENTER) : start; if (start >= last) { break; } end = (u_char *) strstr((char *) start, ENTER); ret = njs_vm_compile(vm, &start, end); if (ret == NJS_OK) { if (opts->disassemble) { njs_disassembler(vm); } ret = njs_vm_start(vm, njs_value_arg(&retval)); } } if (njs_vm_value_dump(vm, &s, njs_value_arg(&retval), 0, 1) != NJS_OK) { njs_printf("njs_vm_value_dump() failed\n"); goto done; } success = njs_strstr_eq(&tests[i].ret, &s); if (!success) { njs_printf("njs(\"%V\")\nexpected: \"%V\"\n got: \"%V\"\n", &tests[i].script, &tests[i].ret, &s); stat->failed++; } else { stat->passed++; } njs_vm_destroy(vm); vm = NULL; } ret = NJS_OK; done: if (vm != NULL) { njs_vm_destroy(vm); } njs_unit_test_report(name, &prev, stat); return ret; } static njs_int_t njs_timezone_optional_test(njs_unit_test_t tests[], size_t num, njs_str_t *name, njs_opts_t *opts, njs_stat_t *stat) { size_t size; u_char buf[16]; time_t clock; struct tm tm; njs_int_t ret; /* * Chatham Islands NZ-CHAT time zone. * Standard time: UTC+12:45, Daylight Saving time: UTC+13:45. */ (void) putenv((char *) "TZ=Pacific/Chatham"); tzset(); clock = 0; localtime_r(&clock, &tm); size = strftime((char *) buf, sizeof(buf), "%z", &tm); if (memcmp(buf, "+1245", size) == 0) { ret = njs_unit_test(tests, num, name, opts, stat); if (ret != NJS_OK) { return ret; } } else { njs_printf("njs timezone tests skipped, timezone is unavailable\n"); } return NJS_OK; } static njs_int_t njs_regexp_optional_test(njs_unit_test_t tests[], size_t num, njs_str_t *name, njs_opts_t *opts, njs_stat_t *stat) { njs_bool_t safe; #ifndef NJS_HAVE_PCRE2 int erroff; pcre *re1, *re2; const char *errstr; /* * pcre-8.21 crashes when it compiles unicode escape codes inside * square brackets when PCRE_UTF8 option is provided. * Catching it in runtime by compiling it without PCRE_UTF8. Normally it * should return NULL and "character value in \u.... sequence is too large" * error string. */ re1 = pcre_compile("/[\\u0410]/", PCRE_JAVASCRIPT_COMPAT, &errstr, &erroff, NULL); if (re1 != NULL) { pcre_free(re1); } /* * pcre-7.8 fails to compile unicode escape codes inside square brackets * even when PCRE_UTF8 option is provided. */ re2 = pcre_compile("/[\\u0410]/", PCRE_JAVASCRIPT_COMPAT | PCRE_UTF8, &errstr, &erroff, NULL); if (re2 != NULL) { pcre_free(re2); } safe = (re1 == NULL && re2 != NULL); #else safe = 1; #endif if (safe) { return njs_unit_test(tests, num, name, opts, stat); } njs_printf("regexp optional tests skipped\n"); return NJS_OK; } static njs_int_t njs_vm_json_test(njs_unit_test_t unused[], size_t num, njs_str_t *name, njs_opts_t *opts, njs_stat_t *stat) { njs_vm_t *vm; njs_int_t ret; njs_str_t s, *script; njs_uint_t i; njs_bool_t success; njs_stat_t prev; njs_vm_opt_t options; njs_opaque_value_t args[3], retval; static const njs_str_t fname = njs_str("replacer"); static const njs_str_t iname = njs_str("indent"); static njs_unit_test_t tests[] = { { njs_str("'[1, true, \"x\", {\"a\": {}}]'"), njs_str("[1,true,\"x\",{\"a\":{}}]") }, { njs_str("'{\"a\":{\"b\":1}}'"), njs_str("{\"a\":{\"b\":1}}") }, { njs_str("'[[[],{}]]'"), njs_str("[[[],{}]]") }, { njs_str("var indent = 1; '[]'"), njs_str("[\n \n]") }, { njs_str("function replacer(k, v) {return v}; '{\"a\":{\"b\":1}}'"), njs_str("{\"a\":{\"b\":1}}") }, { njs_str("function replacer(k, v) {" " return (typeof v === 'string') ? undefined : v};" "'{\"a\":1, \"b\":\"x\"}'"), njs_str("{\"a\":1}") }, }; vm = NULL; prev = *stat; ret = NJS_ERROR; for (i = 0; i < njs_nitems(tests); i++) { njs_vm_opt_init(&options); options.init = 1; vm = njs_vm_create(&options); if (vm == NULL) { njs_printf("njs_vm_create() failed\n"); goto done; } script = &tests[i].script; ret = njs_vm_compile(vm, &script->start, script->start + script->length); if (ret != NJS_OK) { njs_printf("njs_vm_compile() failed\n"); goto done; } ret = njs_vm_start(vm, njs_value_arg(&args[0])); if (ret != NJS_OK) { njs_printf("njs_vm_start() failed\n"); goto done; } ret = njs_vm_json_parse(vm, njs_value_arg(args), 1, njs_value_arg(&retval)); if (ret != NJS_OK) { njs_printf("njs_vm_json_parse() failed\n"); goto done; } njs_value_assign(&args[0], &retval); njs_vm_value(vm, &fname, njs_value_arg(&args[1])); njs_vm_value(vm, &iname, njs_value_arg(&args[2])); ret = njs_vm_json_stringify(vm, njs_value_arg(args), 3, njs_value_arg(&retval)); if (ret != NJS_OK) { njs_printf("njs_vm_json_stringify() failed\n"); goto done; } if (njs_vm_value_string(vm, &s, njs_value_arg(&retval)) != NJS_OK) { njs_printf("njs_vm_value_string() failed\n"); goto done; } success = njs_strstr_eq(&tests[i].ret, &s); if (!success) { njs_printf("njs_vm_json_test(\"%V\")\n" "expected: \"%V\"\n got: \"%V\"\n", script, &tests[i].ret, &s); stat->failed++; } else { stat->passed++; } njs_vm_destroy(vm); vm = NULL; } ret = NJS_OK; done: if (ret != NJS_OK) { if (njs_vm_exception_string(vm, &s) != NJS_OK) { njs_printf("njs_vm_exception_string() failed\n"); } else { njs_printf("%V\n", &s); } } njs_unit_test_report(name, &prev, stat); if (vm != NULL) { njs_vm_destroy(vm); } return ret; } static njs_int_t njs_vm_value_test(njs_unit_test_t unused[], size_t num, njs_str_t *name, njs_opts_t *opts, njs_stat_t *stat) { njs_vm_t *vm; njs_int_t ret; njs_str_t s, *script, path; njs_uint_t i; njs_bool_t success; njs_stat_t prev; njs_vm_opt_t options; njs_opaque_value_t retval; static struct { njs_str_t script; njs_str_t path; njs_str_t ret; } tests[] = { { .script = njs_str("var o = {a:1}"), .path = njs_str("o.a"), .ret = njs_str("1"), }, { .script = njs_str("var aaaaabbbbbcccccddddd = {e:2}"), .path = njs_str("aaaaabbbbbcccccddddd.e"), .ret = njs_str("2"), }, { .script = njs_str("var o = {a:{b:3}}"), .path = njs_str("o.a.b"), .ret = njs_str("3"), }, { .script = njs_str("var o = 1"), .path = njs_str("o.a"), .ret = njs_str("undefined"), }, { .script = njs_str(""), .path = njs_str("o"), .ret = njs_str("undefined"), }, { .script = njs_str("var o = {'':1}"), .path = njs_str("."), .ret = njs_str("TypeError: empty path element"), }, { .script = njs_str("var o = {'':1}"), .path = njs_str("o."), .ret = njs_str("TypeError: empty path element"), }, { .script = njs_str("var o = {'':1}"), .path = njs_str("o.."), .ret = njs_str("TypeError: empty path element"), }, }; vm = NULL; prev = *stat; ret = NJS_ERROR; for (i = 0; i < njs_nitems(tests); i++) { njs_vm_opt_init(&options); options.init = 1; vm = njs_vm_create(&options); if (vm == NULL) { njs_printf("njs_vm_create() failed\n"); goto done; } script = &tests[i].script; ret = njs_vm_compile(vm, &script->start, script->start + script->length); if (ret != NJS_OK) { njs_printf("njs_vm_compile() failed\n"); goto done; } ret = njs_vm_start(vm, njs_value_arg(&retval)); if (ret != NJS_OK) { njs_printf("njs_vm_run() failed\n"); goto done; } path = tests[i].path; path.start = njs_mp_alloc(njs_vm_memory_pool(vm), path.length); if (path.start == NULL) { njs_printf("njs_mp_alloc() failed\n"); goto done; } memcpy(path.start, tests[i].path.start, path.length); ret = njs_vm_value(vm, &path, njs_value_arg(&retval)); if (ret == NJS_OK) { if (njs_vm_value_string(vm, &s, njs_value_arg(&retval)) != NJS_OK) { njs_printf("njs_vm_value_string() failed\n"); goto done; } } else { if (njs_vm_exception_string(vm, &s) != NJS_OK) { njs_printf("njs_vm_exception_string() failed\n"); goto done; } } success = njs_strstr_eq(&tests[i].ret, &s); if (!success) { njs_printf("njs_vm_value_test(\"%V\")\n" "expected: \"%V\"\n got: \"%V\"\n", script, &tests[i].ret, &s); stat->failed++; } else { stat->passed++; } njs_vm_destroy(vm); vm = NULL; } ret = NJS_OK; done: if (ret != NJS_OK) { if (njs_vm_exception_string(vm, &s) != NJS_OK) { njs_printf("njs_vm_exception_string() failed\n"); } else { njs_printf("%V\n", &s); } } njs_unit_test_report(name, &prev, stat); if (vm != NULL) { njs_vm_destroy(vm); } return ret; } static njs_int_t njs_vm_object_alloc_test(njs_vm_t *vm, njs_opts_t *opts, njs_stat_t *stat) { njs_int_t ret; njs_opaque_value_t args[2], obj, num_key, bool_key; njs_value_number_set(njs_value_arg(&args[0]), 1); njs_value_boolean_set(njs_value_arg(&args[0]), 0); (void) njs_vm_value_string_create(vm, njs_value_arg(&num_key), (u_char *) "num", 3); (void) njs_vm_value_string_create(vm, njs_value_arg(&bool_key), (u_char *) "bool", 4); ret = njs_vm_object_alloc(vm, njs_value_arg(&obj), NULL); if (ret != NJS_OK) { return NJS_ERROR; } ret = njs_vm_object_alloc(vm, njs_value_arg(&obj), njs_value_arg(&num_key), NULL); if (ret == NJS_OK) { return NJS_ERROR; } ret = njs_vm_object_alloc(vm, njs_value_arg(&obj), njs_value_arg(&num_key), njs_value_arg(&args[0]), NULL); if (ret != NJS_OK) { return NJS_ERROR; } ret = njs_vm_object_alloc(vm, njs_value_arg(&obj), njs_value_arg(&num_key), njs_value_arg(&args[0]), njs_value_arg(&bool_key), njs_value_arg(&args[1]), NULL); if (ret != NJS_OK) { stat->failed++; return NJS_OK; } stat->passed++; return NJS_OK; } static njs_int_t njs_chb_test(njs_vm_t *vm, njs_opts_t *opts, njs_stat_t *stat) { u_char *p; njs_int_t ret, i; njs_chb_t chain; njs_str_t string, arg; static const njs_str_t expected = njs_str("arg: \"XYZ\" -5"); NJS_CHB_MP_INIT(&chain, vm); p = njs_chb_reserve(&chain, 513); if (p == NULL) { ret = NJS_ERROR; njs_printf("njs_chb_reserve() failed\n"); goto done; } njs_memset(p, 'Z', 256); njs_chb_written(&chain, 256); for (i = 0; i < 768; i++) { njs_chb_append(&chain, "X", 1); } ret = njs_chb_join(&chain, &string); if (ret != NJS_OK) { njs_printf("njs_chb_join() failed\n"); goto done; } if (string.length != 1024 || njs_chb_size(&chain) != 1024) { ret = NJS_ERROR; njs_printf("njs_chb_join() corrupts " "string.length:%z njs_chb_size(&chain):%z != 1024\n", string.length, njs_chb_size(&chain)); goto done; } for (i = 0; i < 1024; i++) { if (string.start[i] != ((i < 256) ? 'Z' : 'X')) { ret = NJS_ERROR; njs_printf("njs_chb_join() corrupts string[%i]:%c != '%c'\n", i, string.start[i], (i < 256) ? 'Z' : 'X'); goto done; } } njs_mp_free(njs_vm_memory_pool(vm), string.start); for (i = 0; i < 222; i++) {; njs_chb_drain(&chain, 3); } ret = njs_chb_join(&chain, &string); if (ret != NJS_OK) { njs_printf("njs_chb_join() failed\n"); goto done; } if (string.length != 358 || njs_chb_size(&chain) != 358) { ret = NJS_ERROR; njs_printf("njs_chb_join() corrupts " "string.length:%z njs_chb_size(&chain):%z != 358\n", string.length, njs_chb_size(&chain)); goto done; } for (i = 0; i < 358; i++) { if (string.start[i] != 'X') { ret = NJS_ERROR; njs_printf("njs_chb_join() corrupts string[%i]:%c != 'X'\n", i, string.start[i]); goto done; } } for (i = 0; i < 512; i++) { njs_chb_append(&chain, "ABC", 3); } for (i = 0; i < 447; i++) { njs_chb_drop(&chain, 2); } ret = njs_chb_join(&chain, &string); if (ret != NJS_OK) { njs_printf("njs_chb_join() failed\n"); goto done; } if (string.length != 1000 || njs_chb_size(&chain) != 1000) { ret = NJS_ERROR; njs_printf("njs_chb_join() corrupts " "string.length:%z njs_chb_size(&chain):%z != 1000\n", string.length, njs_chb_size(&chain)); goto done; } njs_chb_drop(&chain, 500); njs_chb_drain(&chain, 501); if (njs_chb_size(&chain) != 0) { ret = NJS_ERROR; njs_printf("njs_chb_drop() corrupts " "njs_chb_size(&chain):%z != 0\n", njs_chb_size(&chain)); goto done; } arg = njs_str_value("XYZ"); njs_chb_sprintf(&chain, 32, "arg: \"%V\" %d", &arg, -5); ret = njs_chb_join(&chain, &string); if (ret != NJS_OK) { njs_printf("njs_chb_join() failed\n"); goto done; } if (!njs_strstr_eq(&string, &expected)) { ret = NJS_ERROR; njs_printf("njs_chb_sprintf() corrupts \"%V\" != \"%V\"\n", &string, &expected); goto done; } njs_chb_destroy(&chain); njs_mp_free(njs_vm_memory_pool(vm), string.start); done: if (ret != NJS_OK) { stat->failed++; return NJS_OK; } stat->passed++; return NJS_OK; } typedef struct { size_t size; uint32_t array[32]; size_t esize; uint32_t expected[32]; } njs_sort_test_t; static int njs_sort_cmp(const void *a, const void *b, void *ctx) { njs_sort_test_t *c; c = ctx; switch (c->esize) { case 1: return *((uint8_t *) a) - *((uint8_t *) b); case 2: return *((uint16_t *) a) - *((uint16_t *) b); case 4: default: return *((uint32_t *) a) - *((uint32_t *) b); } } static njs_int_t njs_sort_test(njs_vm_t *vm, njs_opts_t *opts, njs_stat_t *stat) { u_char *p; njs_uint_t i, j, k; njs_sort_test_t *t; u_char array[sizeof(t->array)]; uint32_t sorted[sizeof(t->array) / sizeof(t->array[0])]; static const njs_sort_test_t tests[] = { { 1, { 5 }, 1, { 5 } }, { 3, { 3, 2, 1 }, 1, { 1, 2, 3 } }, { 4, { 4, 3, 2, 1 }, 1, { 1, 2, 3, 4 } }, { 5, { 5, 4, 3, 2, 1 }, 1, { 1, 2, 3, 4, 5 } }, { 5, { 1, 0, 9, 1, 8 }, 1, { 0, 1, 1, 8, 9 } }, { 8, { 0, 0, 0, 0, 0, 0, 0, 0 }, 1, { 0, 0, 0, 0, 0, 0, 0, 0 } }, { 8, { 4, 5, 1, 4, 2, 5, 5, 6 }, 1, { 1, 2, 4, 4, 5, 5, 5, 6 } }, { 4, { 512, 100, 65535, 0 }, 2, { 0, 100, 512, 65535 } }, { 3, { 65536, 3, 262141 }, 4, { 3, 65536, 262141 } }, }; for (i = 0; i < njs_nitems(tests); i++) { t = (njs_sort_test_t *) &tests[i]; p = array; for (k = 0; k < t->size; k++) { switch (t->esize) { case 1: *p = (uint8_t) t->array[k]; break; case 2: *(uint16_t *) p = (uint16_t) t->array[k]; break; case 4: default: *(uint32_t *) p = (uint32_t) t->array[k]; break; } p += t->esize; } njs_qsort(array, t->size, t->esize, njs_sort_cmp, t); p = array; for (k = 0; k < t->size; k++) { switch (t->esize) { case 1: sorted[k] = *p; break; case 2: sorted[k] = *(uint16_t *) p; break; case 4: default: sorted[k] = *(uint32_t *) p; break; } p += t->esize; } for (k = 0; k < t->size; k++) { if (sorted[k] != t->expected[k]) { goto failed; } } stat->passed++; continue; failed: njs_printf("njs_sort_test(["); for (j = 0; j < t->size; j++) { njs_printf("%uD%s", t->array[j], (j < t->size - 1) ? "," : ""); } njs_printf("]):\nexpected: ["); for (j = 0; j < t->size; j++) { njs_printf("%uD%s", t->expected[j], (j < t->size - 1) ? "," : ""); } njs_printf("]\n got: ["); for (j = 0; j < t->size; j++) { njs_printf("%uD%s", sorted[j], (j < t->size - 1) ? "," : ""); } njs_printf("]\n"); stat->failed++; } return NJS_OK; } static njs_int_t njs_string_to_index_test(njs_vm_t *vm, njs_opts_t *opts, njs_stat_t *stat) { double num; njs_str_t s; njs_int_t ret; njs_bool_t success; njs_uint_t i; njs_opaque_value_t value, input; static const struct { njs_str_t value; njs_str_t expected; } tests[] = { { njs_str(" 1"), njs_str("NaN") }, { njs_str(""), njs_str("NaN") }, { njs_str("+0"), njs_str("NaN") }, { njs_str("-"), njs_str("NaN") }, { njs_str("-0"), njs_str("-0") }, { njs_str("-1"), njs_str("-1") }, { njs_str("0"), njs_str("0") }, { njs_str("0."), njs_str("NaN") }, { njs_str("0.0"), njs_str("NaN") }, { njs_str("0x1"), njs_str("NaN") }, { njs_str("1 "), njs_str("NaN") }, { njs_str("1"), njs_str("1") }, { njs_str("1."), njs_str("NaN") }, { njs_str("1.1"), njs_str("1.1") }, { njs_str("100"), njs_str("100") }, { njs_str("1a"), njs_str("NaN") }, { njs_str("1e+19"), njs_str("NaN") }, { njs_str("1e+22"), njs_str("1e+22") }, { njs_str("1e22"), njs_str("NaN") }, { njs_str("4294967296"), njs_str("4294967296") }, }; for (i = 0; i < njs_nitems(tests); i++) { (void) njs_vm_value_string_create(vm, njs_value_arg(&input), tests[i].value.start, tests[i].value.length); num = njs_string_to_index(njs_value_arg(&input)); njs_value_number_set(njs_value_arg(&value), num); ret = njs_vm_value_dump(vm, &s, njs_value_arg(&value), 0, 0); if (ret != NJS_OK) { njs_printf("njs_string_to_index_test: " "njs_vm_value_dump() failed\n"); return NJS_ERROR; } success = njs_strstr_eq(&tests[i].expected, &s); if (!success) { njs_printf("njs_string_to_index_test(\"%V\"):\n" "expected: \"%V\"\n got: \"%V\"\n", &tests[i].value, &tests[i].expected, &s); stat->failed++; continue; } stat->passed++; } return NJS_OK; } #ifdef NJS_HAVE_ADDR2LINE static njs_int_t njs_addr2line_test(njs_vm_t *vm, njs_opts_t *opts, njs_stat_t *stat) { njs_str_t v; njs_uint_t i; u_char buf[512]; static const struct { void *fp; const char *name; } tests[] = { { njs_addr2line_test, njs_stringify(njs_addr2line_test) }, { njs_string_to_index_test, njs_stringify(njs_string_to_index_test) }, }; for (i = 0; i < njs_nitems(tests); i++) { v.start = buf; v.length = njs_sprintf(buf, &buf[512], "%P", tests[i].fp) - buf; if (memcmp(buf, tests[i].name, njs_strlen(tests[i].name))) { njs_printf("njs_addr2line_test(%p):\n" "expected: %s\n got: %V\n", tests[i].fp, tests[i].name, &v); stat->failed++; continue; } stat->passed++; } return NJS_OK; } #endif static njs_int_t njs_vm_internal_api_test(njs_unit_test_t unused[], size_t num, njs_str_t *name, njs_opts_t *opts, njs_stat_t *stat) { njs_vm_t *vm; njs_int_t ret; njs_uint_t i; njs_stat_t prev; njs_vm_opt_t options; static const struct { njs_int_t (*test)(njs_vm_t *, njs_opts_t *, njs_stat_t *stat); njs_str_t name; } tests[] = { { njs_vm_object_alloc_test, njs_str("njs_vm_object_alloc_test") }, { njs_chb_test, njs_str("njs_chb_test") }, { njs_sort_test, njs_str("njs_sort_test") }, { njs_string_to_index_test, njs_str("njs_string_to_index_test") }, #ifdef NJS_HAVE_ADDR2LINE { njs_addr2line_test, njs_str("njs_addr2line_test") }, #endif }; vm = NULL; njs_vm_opt_init(&options); prev = *stat; ret = NJS_ERROR; for (i = 0; i < njs_nitems(tests); i++) { vm = njs_vm_create(&options); if (vm == NULL) { njs_printf("njs_vm_create() failed\n"); goto done; } ret = tests[i].test(vm, opts, stat); if (njs_slow_path(ret != NJS_OK)) { njs_printf("njs_api_test: \"%V\" test failed\n", &tests[i].name); goto done; } njs_vm_destroy(vm); vm = NULL; } ret = NJS_OK; done: njs_unit_test_report(name, &prev, stat); if (vm != NULL) { njs_vm_destroy(vm); } return ret; } static njs_int_t njs_options_parse(njs_opts_t *opts, int argc, char **argv) { char *p; njs_int_t i; static const char help[] = "njs unit tests.\n" "\n" "njs_unit_test [options]" "\n" "Options:\n" " -d print disassembled code.\n" " -f PATTERN1[|PATTERN2..] filter test suites to run.\n" " -r count overrides repeat count for tests.\n" " -s seed sets seed for async tests.\n" " -v verbose mode.\n"; for (i = 1; i < argc; i++) { p = argv[i]; if (p[0] != '-') { goto help; } p++; switch (*p) { case '?': case 'h': njs_printf("%*s", njs_length(help), help); return NJS_DONE; case 'd': opts->disassemble = 1; break; case 'f': if (++i < argc) { opts->filter.start = (u_char *) argv[i]; opts->filter.length = njs_strlen(argv[i]); break; } njs_stderror("option \"-f\" requires argument\n"); return NJS_ERROR; case 'r': if (++i < argc) { opts->repeat = atoi(argv[i]); break; } njs_stderror("option \"-r\" requires argument\n"); return NJS_ERROR; case 's': if (++i < argc) { opts->seed = atoi(argv[i]); break; } njs_stderror("option \"-s\" requires argument\n"); return NJS_ERROR; case 'v': opts->verbose = 1; break; default: goto help; } } return NJS_OK; help: njs_stderror("Unknown argument: \"%s\" " "try \"%s -h\" for available options\n", argv[i], argv[0]); return NJS_ERROR; } static njs_int_t njs_match_test(njs_opts_t *opts, njs_str_t *name) { u_char *p, *start, *end; size_t len; if (name->length == 0) { return 0; } if (opts->filter.length == 0) { return 1; } start = opts->filter.start; end = start + opts->filter.length; for ( ;; ) { p = njs_strlchr(start, end, '|'); len = ((p != NULL) ? p : end) - start; len = njs_min(name->length, len); if (len != 0 && njs_strncmp(name->start, start, len) == 0) { return 1; } if (p == NULL) { break; } start = p + 1; } return 0; } typedef struct { njs_str_t name; njs_opts_t opts; njs_unit_test_t *tests; size_t n; njs_int_t (*run)(njs_unit_test_t tests[], size_t num, njs_str_t *name, njs_opts_t *opts, njs_stat_t *stat); } njs_test_suite_t; static njs_int_t njs_disabled_denormals_tests(njs_unit_test_t tests[], size_t num, njs_str_t *name, njs_opts_t *opts, njs_stat_t *stat) { njs_int_t ret; njs_mm_denormals(0); ret = njs_unit_test(tests, num, name, opts, stat); njs_mm_denormals(1); return ret; } static njs_test_suite_t njs_suites[] = { { njs_str("script"), { .repeat = 1, .unsafe = 1, .preload = 1 }, njs_test, njs_nitems(njs_test), njs_unit_test }, { njs_str("safe script"), { .repeat = 1}, njs_safe_test, njs_nitems(njs_safe_test), njs_unit_test }, { njs_str("denormals"), { .repeat = 1, .unsafe = 1 }, njs_denormals_test, njs_nitems(njs_denormals_test), njs_unit_test }, { #if (NJS_HAVE_DENORMALS_CONTROL) njs_str("disabled denormals"), #else njs_str(""), #endif { .repeat = 1, .unsafe = 1 }, njs_disabled_denormals_test, njs_nitems(njs_disabled_denormals_test), njs_disabled_denormals_tests }, { #if (NJS_HAVE_OPENSSL && !NJS_HAVE_MEMORY_SANITIZER) njs_str("webcrypto"), #else njs_str(""), #endif { .externals = 1, .repeat = 1, .unsafe = 1 }, njs_webcrypto_test, njs_nitems(njs_webcrypto_test), njs_unit_test }, { #if (NJS_HAVE_LIBXML2 && !NJS_HAVE_MEMORY_SANITIZER) njs_str("xml"), #else njs_str(""), #endif { .externals = 1, .repeat = 1, .unsafe = 1 }, njs_xml_test, njs_nitems(njs_xml_test), njs_unit_test }, { njs_str("module"), { .repeat = 1, .module = 1, .unsafe = 1 }, njs_module_test, njs_nitems(njs_module_test), njs_unit_test }, { njs_str("fs module"), { .repeat = 1, .unsafe = 1 }, njs_fs_module_test, njs_nitems(njs_fs_module_test), njs_unit_test }, { njs_str("crypto module"), { .repeat = 1, .unsafe = 1 }, njs_crypto_module_test, njs_nitems(njs_crypto_module_test), njs_unit_test }, { njs_str("querystring module"), { .repeat = 1, .unsafe = 1 }, njs_querystring_module_test, njs_nitems(njs_querystring_module_test), njs_unit_test }, { njs_str("externals"), { .externals = 1, .repeat = 1, .unsafe = 1 }, njs_externals_test, njs_nitems(njs_externals_test), njs_unit_test }, { njs_str("async handler"), { .async = 1, .externals = 1, .handler = 1, .repeat = 4, .seed = 2, .unsafe = 1 }, njs_async_handler_test, njs_nitems(njs_async_handler_test), njs_unit_test }, { njs_str("shared"), { .externals = 1, .repeat = 128, .seed = 42, .unsafe = 1, .preload = 1, .backtrace = 1 }, njs_shared_test, njs_nitems(njs_shared_test), njs_unit_test }, { njs_str("interactive"), { .externals = 1, .repeat = 1, .unsafe = 1 }, njs_shell_test, njs_nitems(njs_shell_test), njs_interactive_test }, { njs_str("backtraces"), { .backtrace = 1, .externals = 1, .repeat = 1, .unsafe = 1 }, njs_backtraces_test, njs_nitems(njs_backtraces_test), njs_unit_test }, { njs_str("timezone"), { .repeat = 1, .unsafe = 1 }, njs_tz_test, njs_nitems(njs_tz_test), njs_timezone_optional_test }, { njs_str("regexp optional"), { .repeat = 1, .unsafe = 1 }, njs_regexp_optional_tests, njs_nitems(njs_regexp_optional_tests), njs_regexp_optional_test }, { njs_str("vm_json"), { .repeat = 1, .unsafe = 1 }, NULL, 0, njs_vm_json_test }, { njs_str("vm_value"), { .repeat = 1, .unsafe = 1 }, NULL, 0, njs_vm_value_test }, { njs_str("vm_internal_api"), { .repeat = 1, .unsafe = 1 }, NULL, 0, njs_vm_internal_api_test }, }; static const char *restricted_environ[] = { "TZ=UTC", "DUP=bar", "dup=foo", NULL, }; int njs_cdecl main(int argc, char **argv) { njs_int_t ret; njs_uint_t i; njs_opts_t opts, op; njs_stat_t stat; njs_test_suite_t *suite; njs_memzero(&opts, sizeof(njs_opts_t)); ret = njs_options_parse(&opts, argc, argv); if (ret != NJS_OK) { return (ret == NJS_DONE) ? EXIT_SUCCESS: EXIT_FAILURE; } environ = (char **) restricted_environ; tzset(); njs_mm_denormals(1); njs_memzero(&stat, sizeof(njs_stat_t)); for (i = 0; i < njs_nitems(njs_suites); i++) { suite = &njs_suites[i]; if (!njs_match_test(&opts, &suite->name)) { continue; } op = suite->opts; op.disassemble = opts.disassemble; op.repeat = opts.repeat ? opts.repeat : op.repeat; op.seed = opts.seed ? opts.seed : op.seed; op.verbose = opts.verbose; ret = suite->run(suite->tests, suite->n, &suite->name, &op, &stat); if (ret != NJS_OK) { return ret; } } njs_printf("TOTAL: %s [%ui/%ui]\n", stat.failed ? "FAILED" : "PASSED", stat.passed, stat.passed + stat.failed); return stat.failed ? EXIT_FAILURE : EXIT_SUCCESS; } njs-0.8.9/src/test/random_unit_test.c000066400000000000000000000016441474132077100176250ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include static njs_int_t random_unit_test(void) { njs_uint_t n; njs_random_t r; njs_random_init(&r, -1); r.count = 400000; njs_random_add(&r, (u_char *) "arc4random", njs_length("arc4random")); /* * Test arc4random() numbers. * RC4 pseudorandom numbers would be 0x4642AFC3 and 0xBAF0FFF0. */ if (njs_random(&r) == 0xD6270B27) { for (n = 100000; n != 0; n--) { (void) njs_random(&r); } if (njs_random(&r) == 0x6FCAE186) { njs_printf("random unit test passed\n"); njs_random_stir(&r, getpid()); njs_printf("random unit test: 0x%08uXD\n", njs_random(&r)); return NJS_OK; } } njs_printf("random unit test failed\n"); return NJS_ERROR; } int main(void) { return random_unit_test(); } njs-0.8.9/src/test/rbtree_unit_test.c000066400000000000000000000074661474132077100176400ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include typedef struct { NJS_RBTREE_NODE (node); uint32_t key; } njs_rbtree_test_t; static intptr_t rbtree_unit_test_comparison(njs_rbtree_node_t *node1, njs_rbtree_node_t *node2); static njs_int_t rbtree_unit_test_compare(uint32_t key1, uint32_t key2); static int njs_cdecl rbtree_unit_test_sort_cmp(const void *one, const void *two, void *ctx); static njs_int_t rbtree_unit_test(njs_uint_t n) { void *mark; uint32_t key, *keys; njs_uint_t i; njs_rbtree_t tree; njs_rbtree_node_t *node; njs_rbtree_test_t *items, *item; njs_printf("rbtree unit test started: %l nodes\n", (long) n); njs_rbtree_init(&tree, rbtree_unit_test_comparison); mark = tree.sentinel.right; items = malloc(n * sizeof(njs_rbtree_test_t)); if (items == NULL) { return NJS_ERROR; } keys = malloc(n * sizeof(uint32_t)); if (keys == NULL) { free(items); return NJS_ERROR; } key = 0; for (i = 0; i < n; i++) { key = njs_murmur_hash2(&key, sizeof(uint32_t)); keys[i] = key; items[i].key = key; } njs_qsort(keys, n, sizeof(uint32_t), rbtree_unit_test_sort_cmp, NULL); for (i = 0; i < n; i++) { njs_rbtree_insert(&tree, &items[i].node); } for (i = 0; i < n; i++) { node = njs_rbtree_find(&tree, &items[i].node); if (node != (njs_rbtree_node_t *) &items[i].node) { njs_printf("rbtree unit test failed: %08uXD not found\n", items[i].key); goto fail; } } i = 0; node = njs_rbtree_min(&tree); while (njs_rbtree_is_there_successor(&tree, node)) { item = (njs_rbtree_test_t *) node; if (keys[i] != item->key) { njs_printf("rbtree unit test failed: %l: %08uXD %08uXD\n", (long) i, keys[i], item->key); goto fail; } i++; node = njs_rbtree_node_successor(&tree, node); } if (i != n) { njs_printf("rbtree unit test failed: %l\n", (long) i); goto fail; } for (i = 0; i < n; i++) { njs_rbtree_delete(&tree, &items[i].node); njs_memset(&items[i], 0xA5, sizeof(njs_rbtree_test_t)); } if (!njs_rbtree_is_empty(&tree)) { njs_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) { njs_printf("rbtree sentinel unit test failed\n"); goto fail; } free(keys); free(items); njs_printf("rbtree unit test passed\n"); return NJS_OK; fail: free(keys); free(items); return NJS_ERROR; } static intptr_t rbtree_unit_test_comparison(njs_rbtree_node_t *node1, njs_rbtree_node_t *node2) { njs_rbtree_test_t *item1, *item2; item1 = (njs_rbtree_test_t *) node1; item2 = (njs_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 njs_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 njs_cdecl rbtree_unit_test_sort_cmp(const void *one, const void *two, void *ctx) { 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); } njs-0.8.9/src/test/unicode_unit_test.c000066400000000000000000000167011474132077100177730ustar00rootroot00000000000000 /* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include #define NJS_UTF8_START_TEST 0xC2 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 njs_int_t utf8_overlong(u_char *overlong, size_t len) { u_char *p, utf8[4]; size_t size; uint32_t u, d; njs_uint_t i; const u_char *pp; njs_unicode_decode_t ctx; njs_utf8_decode_init(&ctx); pp = overlong; d = njs_utf8_decode(&ctx, &pp, overlong + len); len = pp - overlong; if (d != 0xFFFFFFFF) { p = njs_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]; } njs_printf("njs_utf8_decode(%05uXD, %uz) failed: %05uXD, %uz\n", u, len, d, size); return NJS_ERROR; } } return NJS_OK; } static njs_int_t utf8_unit_test(njs_uint_t start) { u_char *p, utf8[4]; size_t len; int32_t n; uint32_t u, d; njs_uint_t i, k, l, m; const u_char *pp; njs_unicode_decode_t ctx; njs_printf("utf8 test started\n"); /* Test valid UTF-8. */ for (u = 0; u <= NJS_UNICODE_MAX_CODEPOINT; u++) { p = njs_utf8_encode(utf8, u); if (p == NULL) { njs_printf("njs_utf8_encode(%05uXD) failed\n", u); return NJS_ERROR; } pp = utf8; njs_utf8_decode_init(&ctx); d = njs_utf8_decode(&ctx, &pp, p); /* In UTF-8 not allowed UTF-16 surrogate pair sequences. */ if (u >= 0xD800 && u <= 0xDFFF) { if (d != NJS_UNICODE_ERROR) { njs_printf("njs_utf8_decode(%05uXD) failed for " "surrogate pair: %05uxD\n", u, d); return NJS_ERROR; } continue; } if (u != d) { njs_printf("njs_utf8_decode(%05uXD) failed: %05uxD\n", u, d); return NJS_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; njs_utf8_decode_init(&ctx); d = njs_utf8_decode(&ctx, &pp, utf8 + len); if (d <= NJS_UNICODE_MAX_CODEPOINT) { u = 0; for (i = 0; i < len; i++) { u = (u << 8) + utf8[i]; } njs_printf("njs_utf8_decode(%05uXD, %uz) failed: %05uXD\n", u, len, d); return NJS_ERROR; } } /* Test all overlong UTF-8. */ for (i = start; i < 256; i++) { utf8[0] = i; if (utf8_overlong(utf8, 1) != NJS_OK) { return NJS_ERROR; } for (k = 0; k < 256; k++) { utf8[1] = k; if (utf8_overlong(utf8, 2) != NJS_OK) { return NJS_ERROR; } for (l = 0; l < 256; l++) { utf8[2] = l; if (utf8_overlong(utf8, 3) != NJS_OK) { return NJS_ERROR; } for (m = 0; m < 256; m++) { utf8[3] = m; if (utf8_overlong(utf8, 4) != NJS_OK) { return NJS_ERROR; } } } } } n = njs_utf8_casecmp((u_char *) "ABC АБВ ΑΒΓ", (u_char *) "abc абв αβγ", njs_length("ABC АБВ ΑΒΓ"), njs_length("abc абв αβγ")); if (n != 0) { njs_printf("njs_utf8_casecmp() failed\n"); return NJS_ERROR; } njs_printf("utf8 test passed\n"); return NJS_OK; } static njs_int_t utf16_unit_test() { int8_t length, length_to; u_char *start, *end, *end_to; uint32_t cp, i; njs_unicode_decode_t ctx; u_char buf[8], to[4]; njs_printf("utf16 test started\n"); end = buf + sizeof(buf); end_to = to + sizeof(to); for (i = 0; i <= NJS_UNICODE_MAX_CODEPOINT; i++) { /* Skip surrogate pair. */ if (i >= 0xD800 && i <= 0xDFFF) { continue; } start = buf; length = njs_utf16_encode(i, &start, end); if (length < NJS_OK) { njs_printf("utf16 test encode failed\n"); return NJS_ERROR; } njs_utf16_decode_init(&ctx); start = buf; cp = njs_utf16_decode(&ctx, (const u_char **) &start, start + length); if (cp > NJS_UNICODE_MAX_CODEPOINT) { njs_printf("utf16 test decode failed\n"); return NJS_ERROR; } if (cp != i) { njs_printf("utf16 test decode code point does not match\n"); return NJS_ERROR; } start = to; length_to = njs_utf16_encode(cp, &start, end_to); if (length_to < NJS_OK) { njs_printf("utf16 test encode failed\n"); return NJS_ERROR; } if (length_to != length || njs_strncmp(buf, to, length) != 0) { njs_printf("utf16 test decode-encode failed\n"); return NJS_ERROR; } } /* Surrogate pair. */ for (i = 0xD800; i <= 0xDFFF; i++) { start = buf; length = njs_utf16_encode(i, &start, end); if (length < NJS_OK) { njs_printf("utf16 test surrogate pair encode lead failed\n"); return NJS_ERROR; } length_to = njs_utf16_encode(i - 0xD800 + 0xDC00, &start, end); if (length_to < NJS_OK) { njs_printf("utf16 test surrogate pair encode failed\n"); return NJS_ERROR; } njs_utf16_decode_init(&ctx); start = buf; cp = njs_utf16_decode(&ctx, (const u_char **) &start, start + length + length_to); if (cp > NJS_UNICODE_MAX_CODEPOINT) { if (i < 0xDC00) { njs_printf("utf16 test surrogate pair decode failed\n"); return NJS_ERROR; } } } njs_printf("utf16 test passed\n"); return NJS_OK; } int main(int argc, char **argv) { njs_int_t ret; njs_uint_t start; njs_printf("unicode unit test started\n"); if (argc > 1 && argv[1][0] == 'a') { start = NJS_UTF8_START_TEST; } else { start = 256; } ret = utf8_unit_test(start); if (ret != NJS_OK) { return ret; } ret = utf16_unit_test(); if (ret != NJS_OK) { return ret; } njs_printf("unicode unit test passed\n"); return 0; } njs-0.8.9/test/000077500000000000000000000000001474132077100133075ustar00rootroot00000000000000njs-0.8.9/test/buffer.t.js000066400000000000000000001212661474132077100153700ustar00rootroot00000000000000/*--- includes: [compatBuffer.js, runTsuite.js, compareArray.js] flags: [async] ---*/ function p(args, default_opts) { let params = merge({}, default_opts); params = merge(params, args); return params; } let alloc_tsuite = { name: "Buffer.alloc() tests", skip: () => (!has_buffer()), T: async (params) => { let r = Buffer.alloc(params.size, params.fill, params.encoding); if (r.toString() !== params.expected) { throw Error(`unexpected output "${r.toString()}" != "${params.expected}"`); } return 'SUCCESS'; }, prepare_args: p, opts: { encoding: 'utf-8' }, tests: [ { size: 3, fill: 0x61, expected: 'aaa' }, { size: 3, fill: 'A', expected: 'AAA' }, { size: 3, fill: 'ABCD', expected: 'ABC' }, { size: 3, fill: '414243', encoding: 'hex', expected: 'ABC' }, { size: 4, fill: '414243', encoding: 'hex', expected: 'ABCA' }, { size: 3, fill: 'QUJD', encoding: 'base64', expected: 'ABC' }, { size: 3, fill: 'QUJD', encoding: 'base64url', expected: 'ABC' }, { size: 3, fill: Buffer.from('ABCD'), encoding: 'utf-8', expected: 'ABC' }, { size: 3, fill: Buffer.from('ABCD'), encoding: 'utf8', expected: 'ABC' }, { size: 3, fill: 'ABCD', encoding: 'utf-128', exception: 'TypeError: "utf-128" encoding is not supported' }, { size: 3, fill: Buffer.from('def'), expected: 'def' }, ], }; let byteLength_tsuite = { name: "Buffer.byteLength() tests", skip: () => (!has_buffer()), T: async (params) => { let r = Buffer.byteLength(params.value, params.encoding); if (r !== params.expected) { throw Error(`unexpected output "${r}" != "${params.expected}"`); } return 'SUCCESS'; }, prepare_args: p, opts: { encoding: 'utf-8' }, tests: [ { value: 'abc', expected: 3 }, { value: 'αβγ', expected: 6 }, { value: 'αβγ', encoding: 'utf-8', expected: 6 }, { value: 'αβγ', encoding: 'utf8', expected: 6 }, { value: 'αβγ', encoding: 'utf-128', exception: 'TypeError: "utf-128" encoding is not supported' }, { value: '414243', encoding: 'hex', expected: 3 }, { value: 'QUJD', encoding: 'base64', expected: 3 }, { value: 'QUJD', encoding: 'base64url', expected: 3 }, { value: Buffer.from('αβγ'), expected: 6 }, { value: Buffer.alloc(3).buffer, expected: 3 }, { value: Buffer.from(new Uint8Array([0x60, 0x61, 0x62, 0x63]).buffer, 1), expected: 3 }, ], }; let concat_tsuite = { name: "Buffer.concat() tests", skip: () => (!has_buffer()), T: async (params) => { let r = Buffer.concat(params.buffers, params.length); if (r.toString() !== params.expected) { throw Error(`unexpected output "${r.toString()}" != "${params.expected}"`); } return 'SUCCESS'; }, prepare_args: p, opts: {}, tests: [ { buffers: [ Buffer.from('abc'), Buffer.from(new Uint8Array([0x64, 0x65, 0x66]).buffer, 1) ], expected: 'abcef' }, { buffers: [ Buffer.from('abc'), Buffer.from('def'), Buffer.from('') ], expected: 'abcdef' }, { buffers: [ Buffer.from(''), Buffer.from('abc'), Buffer.from('def') ], length: 4, expected: 'abcd' }, ], }; let compare_tsuite = { name: "Buffer.compare() tests", skip: () => (!has_buffer()), T: async (params) => { let r = Buffer.compare(params.buf1, params.buf2); if (r !== params.expected) { throw Error(`unexpected output "${r}" != "${params.expected}"`); } return 'SUCCESS'; }, prepare_args: p, opts: {}, tests: [ { buf1: Buffer.from('abc'), buf2: Buffer.from('abc'), expected: 0 }, { buf1: Buffer.from('abc'), buf2: Buffer.from(new Uint8Array([0x60, 0x61, 0x62, 0x63]).buffer, 1), expected: 0 }, { buf1: Buffer.from(new Uint8Array([0x60, 0x61, 0x62, 0x63]).buffer, 1), buf2: Buffer.from('abc'), expected: 0 }, { buf1: Buffer.from('abc'), buf2: Buffer.from('def'), expected: -1 }, { buf1: Buffer.from('def'), buf2: Buffer.from('abc'), expected: 1 }, { buf1: Buffer.from('abc'), buf2: Buffer.from('abcd'), expected: -1 }, { buf1: Buffer.from('abcd'), buf2: Buffer.from('abc'), expected: 1 }, { buf1: Buffer.from('abc'), buf2: Buffer.from('ab'), expected: 1 }, { buf1: Buffer.from('ab'), buf2: Buffer.from('abc'), expected: -1 }, { buf1: Buffer.from('abc'), buf2: Buffer.from(''), expected: 1 }, { buf1: Buffer.from(''), buf2: Buffer.from('abc'), expected: -1 }, { buf1: Buffer.from(''), buf2: Buffer.from(''), expected: 0 }, ], }; let comparePrototype_tsuite = { name: "buf.compare() tests", skip: () => (!has_buffer()), T: async (params) => { let r = params.buf.compare(params.target, params.tStart, params.tEnd, params.sStart, params.sEnd); if (r !== params.expected) { throw Error(`unexpected output "${r}" != "${params.expected}"`); } return 'SUCCESS'; }, prepare_args: p, opts: {}, tests: [ { buf: Buffer.from('abc'), target: Buffer.from('abc'), expected: 0 }, { buf: Buffer.from('abc'), target: Buffer.from(new Uint8Array([0x60, 0x61, 0x62, 0x63]).buffer, 1), expected: 0 }, { buf: Buffer.from(new Uint8Array([0x60, 0x61, 0x62, 0x63]).buffer, 1), target: Buffer.from('abc'), expected: 0 }, { buf: Buffer.from('abc'), target: Buffer.from('def'), expected: -1 }, { buf: Buffer.from('def'), target: Buffer.from('abc'), expected: 1 }, { buf: Buffer.from('abc'), target: Buffer.from('abcd'), expected: -1 }, { buf: Buffer.from('abcd'), target: Buffer.from('abc'), expected: 1 }, { buf: Buffer.from('abc'), target: Buffer.from('ab'), expected: 1 }, { buf: Buffer.from('ab'), target: Buffer.from('abc'), expected: -1 }, { buf: Buffer.from('abc'), target: Buffer.from(''), expected: 1 }, { buf: Buffer.from(''), target: Buffer.from('abc'), expected: -1 }, { buf: Buffer.from(''), target: Buffer.from(''), expected: 0 }, { buf: Buffer.from('abcdef'), target: Buffer.from('abc'), sEnd: 3, expected: 0 }, { buf: Buffer.from('abcdef'), target: Buffer.from('def'), sStart: 3, expected: 0 }, { buf: Buffer.from('abcdef'), target: Buffer.from('abc'), sStart: 0, sEnd: 3, expected: 0 }, { buf: Buffer.from('abcdef'), target: Buffer.from('def'), sStart: 3, sEnd: 6, expected: 0 }, { buf: Buffer.from('abcdef'), target: Buffer.from('def'), sStart: 3, sEnd: 5, tStart: 0, tEnd: 2, expected: 0 }, ], }; let copy_tsuite = { name: "buf.copy() tests", skip: () => (!has_buffer()), T: async (params) => { let r = params.buf.copy(params.target, params.tStart, params.sStart, params.sEnd); if (r !== params.expected) { throw Error(`unexpected output "${r}" != "${params.expected}"`); } if (params.target.toString() !== params.expected_buf) { throw Error(`unexpected buf "${params.target.toString()}" != "${params.expected_buf}"`); } return 'SUCCESS'; }, prepare_args: p, opts: {}, tests: [ { buf: Buffer.from('abcdef'), target: Buffer.from('123456'), expected: 6, expected_buf: 'abcdef' }, { buf: Buffer.from('abcdef'), target: Buffer.from('123456'), tStart: 0, expected: 6, expected_buf: 'abcdef' }, { buf: Buffer.from('abc'), target: Buffer.from('123456789'), tStart: 5, expected: 3, expected_buf: '12345abc9' }, { buf: Buffer.from('abcdef'), target: Buffer.from('123456'), tStart: 0, sStart: 0, expected: 6, expected_buf: 'abcdef' }, { buf: Buffer.from('abcdef'), target: Buffer.from('123456'), tStart: 0, sStart: 0, sEnd: 3, expected: 3, expected_buf: 'abc456' }, { buf: Buffer.from('abcdef'), target: Buffer.from('123456'), tStart: 2, sStart: 2, sEnd: 3, expected: 1, expected_buf: '12c456' }, { buf: Buffer.from('abcdef'), target: Buffer.from('123456'), tStart: 7, exception: 'RangeError: \"targetStart\" is out of bounds' }, { buf: Buffer.from('abcdef'), target: Buffer.from('123456'), sStart: 7, exception: 'RangeError: \"sourceStart\" is out of bounds' }, ], }; let equals_tsuite = { name: "buf.equals() tests", skip: () => (!has_buffer()), T: async (params) => { let r = params.buf1.equals(params.buf2); if (r !== params.expected) { throw Error(`unexpected output "${r}" != "${params.expected}"`); } return 'SUCCESS'; }, prepare_args: p, opts: {}, tests: [ { buf1: Buffer.from('abc'), buf2: Buffer.from('abc'), expected: true }, { buf1: Buffer.from('abc'), buf2: Buffer.from(new Uint8Array([0x60, 0x61, 0x62, 0x63]).buffer, 1), expected: true }, { buf1: Buffer.from(new Uint8Array([0x60, 0x61, 0x62, 0x63]).buffer, 1), buf2: Buffer.from('abc'), expected: true }, { buf1: Buffer.from('abc'), buf2: Buffer.from('def'), expected: false }, { buf1: Buffer.from('def'), buf2: Buffer.from('abc'), expected: false }, { buf1: Buffer.from('abc'), buf2: Buffer.from('abcd'), expected: false }, { buf1: Buffer.from('abcd'), buf2: Buffer.from('abc'), expected: false }, { buf1: Buffer.from('abc'), buf2: Buffer.from('ab'), expected: false }, { buf1: Buffer.from('ab'), buf2: Buffer.from('abc'), expected: false }, { buf1: Buffer.from('abc'), buf2: Buffer.from(''), expected: false }, { buf1: Buffer.from(''), buf2: Buffer.from('abc'), expected: false }, { buf1: Buffer.from(''), buf2: Buffer.from(''), expected: true }, ], }; let fill_tsuite = { name: "buf.fill() tests", skip: () => (!has_buffer()), T: async (params) => { let r = params.buf.fill(params.value, params.offset, params.end); if (r.toString() !== params.expected) { throw Error(`unexpected output "${r.toString()}" != "${params.expected}"`); } return 'SUCCESS'; }, prepare_args: p, opts: {}, tests: [ { buf: Buffer.from('abc'), value: 0x61, expected: 'aaa' }, { buf: Buffer.from('abc'), value: 0x61, expected: 'aaa', offset: 0, end: 3 }, { buf: Buffer.from('abc'), value: 0x61, expected: 'abc', offset: 0, end: 0 }, { buf: Buffer.from('abc'), value: 'A', expected: 'AAA' }, { buf: Buffer.from('abc'), value: 'ABCD', expected: 'ABC' }, { buf: Buffer.from('abc'), value: '414243', offset: 'hex', expected: 'ABC' }, { buf: Buffer.from('abc'), value: '414243', offset: 'utf-128', exception: 'TypeError: "utf-128" encoding is not supported' }, { buf: Buffer.from('abc'), value: 'ABCD', offset: 1, expected: 'aAB' }, { buf: Buffer.from('abc'), value: Buffer.from('def'), expected: 'def' }, { buf: Buffer.from('def'), value: Buffer.from(new Uint8Array([0x60, 0x61, 0x62, 0x63]).buffer, 1), expected: 'abc' }, { buf: Buffer.from(new Uint8Array([0x60, 0x61, 0x62, 0x63]).buffer, 1), value: Buffer.from('def'), expected: 'def' }, ], }; let from_tsuite = { name: "Buffer.from() tests", skip: () => (!has_buffer()), T: async (params) => { let buf = Buffer.from.apply(null, params.args); if (params.modify) { params.modify(buf); } if (params.args[0] instanceof ArrayBuffer) { if (buf.buffer !== params.args[0]) { throw Error(`unexpected buffer "${buf.buffer}" != "${params.args[0]}"`); } } let r = buf.toString(params.fmt); if (r.length !== params.expected.length) { throw Error(`unexpected "${r}" length ${r.length} != ${params.expected.length}`); } if (r !== params.expected) { throw Error(`unexpected output "${r}" != "${params.expected}"`); } return 'SUCCESS'; }, prepare_args: p, opts: { fmt: 'utf-8' }, tests: [ { args: [[0x62, 0x75, 0x66, 0x66, 0x65, 0x72]], expected: 'buffer' }, { args: [{length:3, 0:0x62, 1:0x75, 2:0x66}], expected: 'buf' }, { args: [[-1, 1, 255, 22323, -Infinity, Infinity, NaN]], fmt: "hex", expected: 'ff01ff33000000' }, { args: [{length:5, 0:'A'.charCodeAt(0), 2:'X', 3:NaN, 4:0xfd}], fmt: "hex", expected: '41000000fd' }, { args: [[1, 2, 0.23, '5', 'A']], fmt: "hex", expected: '0102000500' }, { args: [new Uint8Array([0xff, 0xde, 0xba])], fmt: "hex", expected: 'ffdeba' }, { args: [(new Uint8Array([0xaa, 0xbb, 0xcc])).buffer], fmt: "hex", expected: 'aabbcc' }, { args: [(new Uint8Array([0xaa, 0xbb, 0xcc])).buffer, 1], fmt: "hex", expected: 'bbcc' }, { args: [(new Uint8Array([0xaa, 0xbb, 0xcc])).buffer, 1, 1], fmt: "hex", expected: 'bb' }, { args: [(new Uint8Array([0xaa, 0xbb, 0xcc])).buffer, '1', '1'], fmt: "hex", expected: 'bb' }, { args: [(new Uint8Array([0xaa, 0xbb, 0xcc])).buffer, 1, 0], fmt: "hex", expected: '' }, { args: [(new Uint8Array([0xaa, 0xbb, 0xcc])).buffer], fmt: "hex", modify: (buf) => { buf[1] = 0; }, expected: 'aa00cc' }, { args: [new Uint16Array([234, 123])], fmt: "hex", expected: 'ea7b' }, { args: [new Uint32Array([234, 123])], fmt: "hex", expected: 'ea7b' }, { args: [new Float32Array([234.001, 123.11])], fmt: "hex", expected: 'ea7b' }, { args: [new Uint32Array([234, 123])], fmt: "hex", expected: 'ea7b' }, { args: [new Float64Array([234.001, 123.11])], fmt: "hex", expected: 'ea7b' }, { args: [(new Uint8Array(2)).buffer, -1], exception: 'RangeError: invalid index' }, { args: [(new Uint8Array(2)).buffer, 3], exception: 'RangeError: \"offset\" is outside of buffer bounds' }, { args: [(new Uint8Array(2)).buffer, 1, 2], exception: 'RangeError: \"length\" is outside of buffer bounds' }, { args: [Buffer.from([0xaa, 0xbb, 0xcc]).toJSON()], fmt: "hex", expected: 'aabbcc' }, { args: [{type: 'Buffer', data: [0xaa, 0xbb, 0xcc]}], fmt: "hex", expected: 'aabbcc' }, { args: [new String('00aabbcc'), 'hex'], fmt: "hex", expected: '00aabbcc' }, { args: [Buffer.from([0xaa, 0xbb, 0xcc]).toJSON()], fmt: "hex", expected: 'aabbcc' }, { args: [(function() {var arr = new Array(1, 2, 3); arr.valueOf = () => arr; return arr})()], fmt: "hex", expected: '010203' }, { args: [(function() {var obj = new Object(); obj.valueOf = () => obj; return obj})()], exception: 'TypeError: first argument is not a string or Buffer-like object' }, { args: [(function() {var obj = new Object(); obj.valueOf = () => undefined; return obj})()], exception: 'TypeError: first argument is not a string or Buffer-like object' }, { args: [(function() {var obj = new Object(); obj.valueOf = () => null; return obj})()], exception: 'TypeError: first argument is not a string or Buffer-like object' }, { args: [(function() {var obj = new Object(); obj.valueOf = () => new Array(1, 2, 3); return obj})()], fmt: "hex", expected: '010203' }, { args: [(function() {var a = [1,2,3,4]; a[1] = { valueOf() { a.length = 3; return 1; } }; return a})()], fmt: "hex", expected: '01010300' }, { args: [{type: 'B'}], exception: 'TypeError: first argument is not a string or Buffer-like object' }, { args: [{type: undefined}], exception: 'TypeError: first argument is not a string or Buffer-like object' }, { args: [{type: 'Buffer'}], exception: 'TypeError: first argument is not a string or Buffer-like object' }, { args: [{type: 'Buffer', data: null}], exception: 'TypeError: first argument is not a string or Buffer-like object' }, { args: [{type: 'Buffer', data: {}}], exception: 'TypeError: first argument is not a string or Buffer-like object' }, { args: ['', 'utf-128'], exception: 'TypeError: "utf-128" encoding is not supported' }, { args: [''], fmt: "hex", expected: '' }, { args: ['α'], fmt: "hex", expected: 'ceb1' }, { args: ['α', 'utf-8'], fmt: "hex", expected: 'ceb1' }, { args: ['α', 'utf8'], fmt: "hex", expected: 'ceb1' }, { args: ['', 'hex'], fmt: "hex", expected: '' }, { args: ['aa0', 'hex'], fmt: "hex", expected: 'aa' }, { args: ['00aabbcc', 'hex'], fmt: "hex", expected: '00aabbcc' }, { args: ['deadBEEF##', 'hex'], fmt: "hex", expected: 'deadbeef' }, { args: ['6576696c', 'hex'], expected: 'evil' }, { args: ['f3', 'hex'], expected: '�' }, { args: ['', "base64"], expected: '' }, { args: ['#', "base64"], expected: '' }, { args: ['Q', "base64"], expected: '' }, { args: ['QQ', "base64"], expected: 'A' }, { args: ['QQ=', "base64"], expected: 'A' }, { args: ['QQ==', "base64"], expected: 'A' }, { args: ['QUI=', "base64"], expected: 'AB' }, { args: ['QUI', "base64"], expected: 'AB' }, { args: ['QUJD', "base64"], expected: 'ABC' }, { args: ['QUJDRA==', "base64"], expected: 'ABCD' }, { args: ['', "base64url"], expected: '' }, { args: ['QQ', "base64url"], expected: 'A' }, { args: ['QUI', "base64url"], expected: 'AB' }, { args: ['QUJD', "base64url"], expected: 'ABC' }, { args: ['QUJDRA', "base64url"], expected: 'ABCD' }, { args: ['QUJDRA#', "base64url"], expected: 'ABCD' }, ]}; let includes_tsuite = { name: "buf.includes() tests", skip: () => (!has_buffer()), T: async (params) => { let r = params.buf.includes(params.value, params.offset, params.encoding); if (r !== params.expected) { throw Error(`unexpected output "${r}" != "${params.expected}"`); } return 'SUCCESS'; }, prepare_args: p, opts: {}, tests: [ { buf: Buffer.from('abcdef'), value: 'abc', expected: true }, { buf: Buffer.from('abcdef'), value: 'def', expected: true }, { buf: Buffer.from('abcdef'), value: 'abc', offset: 1, expected: false }, { buf: Buffer.from('abcdef'), value: {}, exception: 'TypeError: "value" argument must be of type string or an instance of Buffer' }, ], }; let indexOf_tsuite = { name: "buf.indexOf() tests", skip: () => (!has_buffer()), T: async (params) => { let r = params.buf.indexOf(params.value, params.offset, params.encoding); if (r !== params.expected) { throw Error(`unexpected output "${r}" != "${params.expected}"`); } return 'SUCCESS'; }, prepare_args: p, opts: {}, tests: [ { buf: Buffer.from('abcdef'), value: 'abc', expected: 0 }, { buf: Buffer.from('abcdef'), value: 'def', expected: 3 }, { buf: Buffer.from('abcdef'), value: 'abc', offset: 1, expected: -1 }, { buf: Buffer.from('abcdef'), value: 'def', offset: 1, expected: 3 }, { buf: Buffer.from('abcdef'), value: 'def', offset: -3, expected: 3 }, { buf: Buffer.from('abcdef'), value: 'efgh', offset: 4, expected: -1 }, { buf: Buffer.from(''), value: '', expected: 0 }, { buf: Buffer.from(''), value: '', offset: -1, expected: 0 }, { buf: Buffer.from(''), value: '', offset: 0, expected: 0 }, { buf: Buffer.from(''), value: '', offset: 1, expected: 0 }, { buf: Buffer.from('abc'), value: '', offset: -4, expected: 0 }, { buf: Buffer.from('abc'), value: '', offset: -3, expected: 0 }, { buf: Buffer.from('abc'), value: '', offset: -2, expected: 1 }, { buf: Buffer.from('abc'), value: '', offset: -1, expected: 2 }, { buf: Buffer.from('abc'), value: '', offset: 0, expected: 0 }, { buf: Buffer.from('abc'), value: '', offset: 1, expected: 1 }, { buf: Buffer.from('abc'), value: '', offset: 2, expected: 2 }, { buf: Buffer.from('abc'), value: '', offset: 3, expected: 3 }, { buf: Buffer.from('abc'), value: '', offset: 4, expected: 3 }, { buf: Buffer.from('abcdef'), value: '626364', encoding: 'hex', expected: 1 }, { buf: Buffer.from('abcdef'), value: '626364', encoding: 'utf-128', exception: 'TypeError: "utf-128" encoding is not supported' }, { buf: Buffer.from('abcdef'), value: 0x62, expected: 1 }, { buf: Buffer.from('abcabc'), value: 0x61, offset: 1, expected: 3 }, { buf: Buffer.from('abcdef'), value: Buffer.from('def'), expected: 3 }, { buf: Buffer.from('abcdef'), value: Buffer.from(new Uint8Array([0x60, 0x62, 0x63]).buffer, 1), expected: 1 }, { buf: Buffer.from('abcdef'), value: {}, exception: 'TypeError: "value" argument must be of type string or an instance of Buffer' }, ], }; let isBuffer_tsuite = { name: "Buffer.isBuffer() tests", skip: () => (!has_buffer()), T: async (params) => { let r = Buffer.isBuffer(params.value); if (r !== params.expected) { throw Error(`unexpected output "${r}" != "${params.expected}"`); } return 'SUCCESS'; }, prepare_args: p, opts: {}, tests: [ { value: Buffer.from('α'), expected: true }, { value: new Uint8Array(10), expected: false }, { value: {}, expected: false }, { value: 1, expected: false }, ]}; let isEncoding_tsuite = { name: "Buffer.isEncoding() tests", skip: () => (!has_buffer()), T: async (params) => { let r = Buffer.isEncoding(params.value); if (r !== params.expected) { throw Error(`unexpected output "${r}" != "${params.expected}"`); } return 'SUCCESS'; }, prepare_args: p, opts: {}, tests: [ { value: 'utf-8', expected: true }, { value: 'utf8', expected: true }, { value: 'utf-128', expected: false }, { value: 'hex', expected: true }, { value: 'base64', expected: true }, { value: 'base64url', expected: true }, ], }; function compare_object(a, b) { if (a === b) { return true; } if (typeof a !== 'object' || typeof b !== 'object') { return false; } if (Object.keys(a).length !== Object.keys(b).length) { return false; } for (let key in a) { if (!compare_object(a[key], b[key])) { return false; } } return true; } let lastIndexOf_tsuite = { name: "buf.lastIndexOf() tests", skip: () => (!has_buffer()), T: async (params) => { let r = params.buf.lastIndexOf(params.value, params.offset, params.encoding); if (r !== params.expected) { throw Error(`unexpected output "${r}" != "${params.expected}"`); } return 'SUCCESS'; }, prepare_args: p, opts: {}, tests: [ { buf: Buffer.from('abcdef'), value: 'abc', expected: 0 }, { buf: Buffer.from('abcabc'), value: 'abc', expected: 3 }, { buf: Buffer.from('abcdef'), value: 'def', expected: 3 }, { buf: Buffer.from('abcdef'), value: 'abc', offset: 1, expected: 0 }, { buf: Buffer.from('abcdef'), value: 'def', offset: 1, expected: -1 }, { buf: Buffer.from('xxxABCx'), value: 'ABC', offset: 3, expected: 3 }, { buf: Buffer.from(''), value: '', expected: 0 }, { buf: Buffer.from(''), value: '', offset: -1, expected: 0 }, { buf: Buffer.from(''), value: '', offset: 0, expected: 0 }, { buf: Buffer.from(''), value: '', offset: 1, expected: 0 }, { buf: Buffer.from('abc'), value: '', offset: -4, expected: 0 }, { buf: Buffer.from('abc'), value: '', offset: -3, expected: 0 }, { buf: Buffer.from('abc'), value: '', offset: -2, expected: 1 }, { buf: Buffer.from('abc'), value: '', offset: -1, expected: 2 }, { buf: Buffer.from('abc'), value: '', offset: 0, expected: 0 }, { buf: Buffer.from('abc'), value: '', offset: 1, expected: 1 }, { buf: Buffer.from('abc'), value: '', offset: 2, expected: 2 }, { buf: Buffer.from('abc'), value: '', offset: 3, expected: 3 }, { buf: Buffer.from('abc'), value: '', offset: 4, expected: 3 }, { buf: Buffer.from(Buffer.alloc(7).fill('Zabcdef').buffer, 1), value: 'abcdef', expected: 0 }, { buf: Buffer.from(Buffer.alloc(7).fill('Zabcdef').buffer, 1), value: 'abcdefg', expected: -1 }, { buf: Buffer.from('abcdef'), value: '626364', encoding: 'hex', expected: 1 }, { buf: Buffer.from('abcdef'), value: '626364', encoding: 'utf-128', exception: 'TypeError: "utf-128" encoding is not supported' }, { buf: Buffer.from('abcabc'), value: 0x61, expected: 3 }, { buf: Buffer.from('abcabc'), value: 0x61, offset: 1, expected: 0 }, { buf: Buffer.from('ab'), value: 7, offset: 2, expected: -1 }, { buf: Buffer.from('abcdef'), value: Buffer.from('def'), expected: 3 }, { buf: Buffer.from('abcdef'), value: Buffer.from(new Uint8Array([0x60, 0x62, 0x63]).buffer, 1), expected: 1 }, { buf: Buffer.from('abcdef'), value: {}, exception: 'TypeError: "value" argument must be of type string or an instance of Buffer' }, ], }; let readXIntXX_tsuite = { name: "buf.readXIntXX() tests", skip: () => (!has_buffer()), T: async (params) => { let b = params.buf; let r = [ b.readInt8(params.offset), b.readUInt8(params.offset), b.readInt16LE(params.offset), b.readInt16BE(params.offset), b.readUInt16LE(params.offset), b.readUInt16BE(params.offset), b.readInt32LE(params.offset), b.readInt32BE(params.offset), b.readUInt32LE(params.offset), b.readUInt32BE(params.offset), ]; if (!compareArray(r, params.expected)) { throw Error(`unexpected output "${r}" != "${params.expected}"`); } return 'SUCCESS'; }, prepare_args: p, opts: {}, tests: [ { buf: Buffer.from([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]), offset: 0, expected: [ -86,170,-17494,-21829,48042,43707,-573785174,-1430532899,3721182122,2864434397 ] }, { buf: Buffer.from([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]), offset: 1, expected: [ -69,187,-13125,-17460,52411,48076,-287454021,-1144201746,4007513275,3150765550 ] }, { buf: Buffer.from([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]), offset: 2, expected: [ -52,204,-8756,-13091,56780,52445,-1122868,-857870593,4293844428,3437096703 ] }, { buf: Buffer.from([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]), offset: 3, exception: 'RangeError: Index out of range' }, ], }; let readFloat_tsuite = { name: "buf.readFloat() tests", skip: () => (!has_buffer()), T: async (params) => { let b = Buffer.alloc(9); let r = b.writeFloatLE(123.125, 0); if (r !== 4) { throw Error(`unexpected output "${r}" != "4"`); } if (b.readFloatLE(0) !== 123.125) { throw Error(`unexpected output "${b.readFloatLE(0)}" != "123.125"`); } r = b.writeFloatBE(123.125, 0); if (r !== 4) { throw Error(`unexpected output "${r}" != "4"`); } if (b.readFloatBE(0) !== 123.125) { throw Error(`unexpected output "${b.readFloatBE(0)}" != "123.125"`); } r = b.writeDoubleLE(123.125, 1); if (r !== 9) { throw Error(`unexpected output "${r}" != "9"`); } if (b.readDoubleLE(1) !== 123.125) { throw Error(`unexpected output "${b.readDoubleLE(1)}" != "123.125"`); } r = b.writeDoubleBE(123.125, 1); if (r !== 9) { throw Error(`unexpected output "${r}" != "9"`); } if (b.readDoubleBE(1) !== 123.125) { throw Error(`unexpected output "${b.readDoubleBE(1)}" != "123.125"`); } return 'SUCCESS'; }, prepare_args: p, opts: {}, tests: [ {} ], }; let readGeneric_tsuite = { name: "buf.readGeneric() tests", skip: () => (!has_buffer()), T: async (params) => { let b = params.buf; let r = [ b.readUIntLE(params.offset, params.length), b.readUIntBE(params.offset, params.length), b.readIntLE(params.offset, params.length), b.readIntBE(params.offset, params.length), ]; if (!compareArray(r, params.expected)) { throw Error(`unexpected output "${r}" != "${params.expected}"`); } return 'SUCCESS'; }, prepare_args: p, opts: {}, tests: [ { buf: Buffer.from([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]), offset: 0, length: 1, expected: [ 170, 170, -86, -86 ] }, { buf: Buffer.from([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]), offset: 1, length: 2, expected: [ 52411,48076,-13125,-17460 ] }, { buf: Buffer.from([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]), offset: 2, length: 3, expected: [ 15654348,13426158,-1122868,-3351058 ] }, { buf: Buffer.from([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]), offset: 3, length: 4, exception: 'RangeError: Index out of range' }, { buf: Buffer.from([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]), offset: 0, length: 0, exception: 'RangeError: byteLength must be <= 6' }, { buf: Buffer.from([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]), offset: 0, length: 5, expected: [ 1025923398570,733295205870,-73588229206,-366216421906 ] }, { buf: Buffer.from([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]), offset: 0, length: 6, expected: [ 281401388481450,187723572702975,-73588229206,-93751404007681 ] }, ], }; let slice_tsuite = { name: "buf.slice() tests", skip: () => (!has_buffer()), T: async (params) => { let r = params.buf.slice(params.start, params.end); if (r.toString() !== params.expected) { throw Error(`unexpected output "${r.toString()}" != "${params.expected}"`); } params.buf[2] = 0x5a; if (r.constructor.name !== 'Buffer' || r.__proto__ !== params.buf.__proto__) { throw Error(`unexpected output "${r.constructor.name}" != "Buffer"`); } return 'SUCCESS'; }, prepare_args: p, opts: {}, tests: [ { buf: Buffer.from('abcdef'), start: 1, expected: 'bcdef' }, { buf: Buffer.from('abcdef'), start: 1, end: 3, expected: 'bc' }, ], }; let subarray_tsuite = { name: "buf.subarray() tests", skip: () => (!has_buffer()), T: async (params) => { let r = params.buf.subarray(params.start, params.end); params.buf[0] = 0x5a; if (r.toString() !== params.expected) { throw Error(`unexpected output "${r.toString()}" != "${params.expected}"`); } if (r.constructor.name !== 'Buffer' || r.__proto__ !== params.buf.__proto__) { throw Error(`unexpected output "${r.constructor.name}" != "Buffer"`); } return 'SUCCESS'; }, prepare_args: p, opts: {}, tests: [ { buf: Buffer.from('abcdef'), start: 0, end: 3, expected: 'Zbc' }, { buf: Buffer.from('abcdef'), start: 1, expected: 'bcdef' }, ], }; let swap_tsuite = { name: "buf.swap() tests", skip: () => (!has_buffer()), T: async (params) => { let r = Buffer.from(params.value, 'hex')[params.swap](); if (r.toString('hex') !== params.expected) { throw Error(`unexpected output "${r.toString('hex')}" != "${params.expected}"`); } return 'SUCCESS'; }, prepare_args: p, opts: { swap: 'swap16' }, tests: [ { value: '01020304', expected: '02010403' }, { value: '010203', exception: 'RangeError: Buffer size must be a multiple of 2' }, { value: 'aabbccddeeff0011', swap: 'swap32', expected: 'ddccbbaa1100ffee' }, { value: 'aabbcc', swap: 'swap32', exception: 'RangeError: Buffer size must be a multiple of 4' }, { value: 'aabbccddeeff00112233445566778899', swap: 'swap64', expected: '1100ffeeddccbbaa9988776655443322' }, ], }; let toJSON_tsuite = { name: "Buffer.toJSON() tests", skip: () => (!has_buffer()), T: async (params) => { let r = Buffer.from(params.value).toJSON(); if (!compare_object(r, params.expected)) { throw Error(`unexpected output "${JSON.stringify(r)}" != "${JSON.stringify(params.expected)}"`); } return 'SUCCESS'; }, prepare_args: p, opts: {}, tests: [ { value: '', expected: { type: 'Buffer', data: [] } }, { value: 'αβγ', expected: { type: 'Buffer', data: [0xCE, 0xB1, 0xCE, 0xB2, 0xCE, 0xB3] } }, { value: new Uint8Array([0xff, 0xde, 0xba]), expected: { type: 'Buffer', data: [0xFF, 0xDE, 0xBA] } }, ], }; let toString_tsuite = { name: "Buffer.toString() tests", skip: () => (!has_buffer()), T: async (params) => { let r = Buffer.from(params.value).toString(params.fmt); if (r.length !== params.expected.length) { throw Error(`unexpected "${r}" length ${r.length} != ${params.expected.length}`); } if (r !== params.expected) { throw Error(`unexpected output "${r}" != "${params.expected}"`); } return 'SUCCESS'; }, prepare_args: p, opts: { fmt: 'utf-8' }, tests: [ { value: '💩', expected: '💩' }, { value: String.fromCharCode(0xD83D, 0xDCA9), expected: '💩' }, { value: String.fromCharCode(0xD83D, 0xDCA9), expected: String.fromCharCode(0xD83D, 0xDCA9) }, { value: new Uint8Array([0xff, 0xde, 0xba]), fmt: "hex", expected: 'ffdeba' }, { value: new Uint8Array([0xff, 0xde, 0xba]), fmt: "base64", expected: '/966' }, { value: new Uint8Array([0xff, 0xde, 0xba]), fmt: "base64url", expected: '_966' }, { value: "ABCD", fmt: "base64", expected: 'QUJDRA==' }, { value: "ABCD", fmt: "base64url", expected: 'QUJDRA' }, { value: '', fmt: "utf-128", exception: 'TypeError: "utf-128" encoding is not supported' }, ]}; let write_tsuite = { name: "buf.write() tests", skip: () => (!has_buffer()), T: async (params) => { let b = Buffer.alloc(10).fill('Z'); let r; if (typeof params.offset != 'undefined' && typeof params.length != 'undefined') { r = b.write(params.value, params.offset, params.length, params.encoding); } else if (typeof params.offset != 'undefined') { r = b.write(params.value, params.offset, params.encoding); } else { r = b.write(params.value, params.encoding); } if (r !== params.expected) { throw Error(`unexpected output "${r}" != "${params.expected}"`); } if (b.toString() !== params.expected_buf) { throw Error(`unexpected output "${b.toString()}" != "${params.expected_buf}"`); } return 'SUCCESS'; }, prepare_args: p, opts: {}, tests: [ { value: 'abc', expected: 3, expected_buf: 'abcZZZZZZZ' }, { value: 'abc', offset: 1, expected: 3, expected_buf: 'ZabcZZZZZZ' }, { value: 'abc', offset: 1, length: 2, expected: 2, expected_buf: 'ZabZZZZZZZ' }, { value: 'αβγ', offset: 1, expected: 6, expected_buf: 'ZαβγZZZ' }, { value: 'αβγ', offset: 1, length: 1, expected: 0, expected_buf: 'ZZZZZZZZZZ' }, { value: 'αβγ', offset: 1, length: 2, expected: 2, expected_buf: 'ZαZZZZZZZ' }, { value: '414243', encoding: 'hex', expected: 3, expected_buf: 'ABCZZZZZZZ' }, { value: '414243', encoding: 'hex', offset: 8, expected: 2, expected_buf: 'ZZZZZZZZAB' }, { value: "x".repeat(12), expected: 10, expected_buf: 'xxxxxxxxxx' }, { value: "x".repeat(12), offset: 1, expected: 9, expected_buf: 'Zxxxxxxxxx' }, { value: "x", offset: 1, length: 2, encoding: 'utf-128', exception: 'TypeError: "utf-128" encoding is not supported' }, { value: "x".repeat(10), offset: 10, expected: 0, expected_buf: 'ZZZZZZZZZZ' }, { value: "x".repeat(10), offset: 11, exception: 'RangeError: Index out of range' }, ], }; let writeXIntXX_tsuite = { name: "buf.writeXIntXX() tests", skip: () => (!has_buffer()), T: async (params) => { let b = Buffer.alloc(10).fill('Z'); let r = b[params.write](params.value, params.offset); if (params.exception) { throw Error(`expected exception "${params.exception}"`); } if (r !== params.expected) { throw Error(`unexpected output "${r}" != "${params.expected}"`); } if (b.toString('hex') !== params.expected_buf) { throw Error(`unexpected output "${b.toString('hex')}" != "${params.expected_buf}"`); } return 'SUCCESS'; }, prepare_args: p, opts: {}, tests: [ { write: 'writeInt8', value: 0xaa, exception: 'RangeError: Index out of range' }, { write: 'writeInt8', value: 0x00, offset: 3, expected: 4, expected_buf: '5a5a5a005a5a5a5a5a5a' }, { write: 'writeUInt8', value: 0xaa, offset: 0, expected: 1, expected_buf: 'aa5a5a5a5a5a5a5a5a5a' }, { write: 'writeInt16LE', value: 0xaabb, exception: 'RangeError: Index out of range' }, { write: 'writeInt16BE', value: 0x7788, offset: 1, expected: 3, expected_buf: '5a77885a5a5a5a5a5a5a' }, { write: 'writeUInt16LE', value: 0xaabb, offset: 0, expected: 2, expected_buf: 'bbaa5a5a5a5a5a5a5a5a' }, { write: 'writeUInt16BE', value: 0x7788, offset: 1, expected: 3, expected_buf: '5a77885a5a5a5a5a5a5a' }, { write: 'writeInt32LE', value: 0xaabbccdd, exception: 'RangeError: Index out of range' }, { write: 'writeInt32BE', value: 0x778899aa, offset: 1, expected: 5, expected_buf: '5a778899aa5a5a5a5a5a' }, { write: 'writeUInt32LE', value: 0xaabbccdd, offset: 0, expected: 4, expected_buf: 'ddccbbaa5a5a5a5a5a5a' }, { write: 'writeUInt32BE', value: 0x778899aa, offset: 1, expected: 5, expected_buf: '5a778899aa5a5a5a5a5a' }, ], }; let writeGeneric_tsuite = { name: "buf.writeGeneric() tests", skip: () => (!has_buffer()), T: async (params) => { let b = Buffer.alloc(10).fill('Z'); let r = b[params.write](params.value, params.offset, params.length); if (params.exception) { throw Error(`expected exception "${params.exception}"`); } if (r !== params.expected) { throw Error(`unexpected output "${r}" != "${params.expected}"`); } if (b.toString('hex') !== params.expected_buf) { throw Error(`unexpected output "${b.toString('hex')}" != "${params.expected_buf}"`); } return 'SUCCESS'; }, prepare_args: p, opts: {}, tests: [ { write: 'writeUIntLE', value: 0xaa, length: 1, exception: 'RangeError: Index out of range' }, { write: 'writeUIntLE', value: 0x44, length: 1, offset: 3, expected: 4, expected_buf: '5a5a5a445a5a5a5a5a5a' }, { write: 'writeUIntBE', value: 0xaabb, length: 2, offset: 0, expected: 2, expected_buf: 'aabb5a5a5a5a5a5a5a5a' }, { write: 'writeUIntBE', value: 0x7788, length: 2, offset: 1, expected: 3, expected_buf: '5a77885a5a5a5a5a5a5a' }, { write: 'writeIntLE', value: 0x445566, length: 3, offset: 5, expected: 8, expected_buf: '5a5a5a5a5a6655445a5a' }, { write: 'writeIntBE', value: 0x778899, length: 3, offset: 1, expected: 4, expected_buf: '5a7788995a5a5a5a5a5a' }, { write: 'writeIntLE', value: 0x44556677, length: 4, offset: 5, expected: 9, expected_buf: '5a5a5a5a5a776655445a' }, { write: 'writeIntBE', value: 0xaabbccdd, length: 4, offset: 1, exception: 'RangeError: Index out of range' }, { write: 'writeUIntLE', value: 0xaabbccddee, length: 5, offset: 0, expected: 5, expected_buf: 'eeddccbbaa5a5a5a5a5a' }, { write: 'writeUIntBE', value: 0x778899aabbcc, length: 6, offset: 1, expected: 7, expected_buf: '5a778899aabbcc5a5a5a' }, { write: 'writeUIntBE', value: 0, length: 7, exception: 'The value of "byteLength" is out of range' }, ], }; run([ alloc_tsuite, byteLength_tsuite, concat_tsuite, compare_tsuite, comparePrototype_tsuite, copy_tsuite, equals_tsuite, fill_tsuite, from_tsuite, includes_tsuite, indexOf_tsuite, isBuffer_tsuite, isEncoding_tsuite, lastIndexOf_tsuite, readXIntXX_tsuite, readFloat_tsuite, readGeneric_tsuite, slice_tsuite, subarray_tsuite, swap_tsuite, toJSON_tsuite, toString_tsuite, write_tsuite, writeXIntXX_tsuite, writeGeneric_tsuite, ]) .then($DONE, $DONE); njs-0.8.9/test/finalize000066400000000000000000000003101474132077100150250ustar00rootroot00000000000000#!/bin/sh # Copyright (C) Dmitry Volyntsev # Copyright (C) NGINX, Inc. if [ -z "$NJS_TEST_VERBOSE" ]; then verbose "Removing dir: $NJS_TEST_DIR\n" verbose "\n" rm -fr $NJS_TEST_DIR fi njs-0.8.9/test/fs/000077500000000000000000000000001474132077100137175ustar00rootroot00000000000000njs-0.8.9/test/fs/access.t.mjs000066400000000000000000000041751474132077100161440ustar00rootroot00000000000000/*--- includes: [compareArray.js, compatFs.js] flags: [async] ---*/ var fname = `${test_dir}/fs_promises_02`; let stages = []; var testSync = () => new Promise((resolve, reject) => { try { fs.writeFileSync(fname, fname); fs.accessSync(fname); fs.accessSync(fname, fs.constants.R_OK | fs.constants.W_OK); try { fs.accessSync(fname + '___'); reject(new Error('fs.accessSync error 1')); } catch (e) { if (e.syscall != 'access' || e.code != 'ENOENT') { reject(new Error('fs.accessSync error 2')); } } stages.push('testSync'); resolve(); } catch (e) { reject(e); } }); var testCallback = () => new Promise((resolve, reject) => { fs.writeFileSync(fname, fname); fs.access(fname, (err) => { if (err) { reject(new Error('fs.access error 1')); } fs.access(fname, fs.constants.R_OK | fs.constants.W_OK, (err) => { if (err) { reject(err); } fs.access(fname + '___', (err) => { if (!err || err.syscall != 'access' || err.code != 'ENOENT') { reject(new Error('fs.access error 2')); } stages.push('testCallback'); resolve(); }); }); }); }); let testFsp = () => Promise.resolve() .then(() => { fs.writeFileSync(fname, fname); return fsp.access(fname) .then(() => fsp.access(fname, fs.constants.R_OK | fs.constants.W_OK)) .then(() => fsp.access(fname + '___')); }) .then(() => { $DONOTEVALUATE(); }) .catch(e => { stages.push('testPromise'); assert.sameValue(e.syscall, 'access', 'testPromise'); assert.sameValue(e.path, fname + '___', 'testPromise'); assert.sameValue(e.code, 'ENOENT', 'testPromise'); }) let p = Promise.resolve() .then(testSync) .then(testCallback) .then(testFsp) .then(() => assert.compareArray(stages, ["testSync", "testCallback", "testPromise"])) p.then($DONE, $DONE); njs-0.8.9/test/fs/ascii000066400000000000000000000011301474132077100147250ustar00rootroot00000000000000xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxnjs-0.8.9/test/fs/methods.t.mjs000066400000000000000000000755631474132077100163570ustar00rootroot00000000000000/*--- includes: [compatFs.js, compatBuffer.js, runTsuite.js] flags: [async] ---*/ let unique = 0; function p(args, default_opts) { let params = Object.assign({}, default_opts, args); if (params.args) { let fname = params.args[0]; if (fname[0] == '@') { let gen = `${test_dir}/fs_test_${unique++}`; params.args = params.args.map(v => v); params.args[0] = gen + fname.slice(1); } } return params; } function promisify(f) { return function (...args) { return new Promise((resolve, reject) => { function callback(err, result) { if (err) { return reject(err); } else { resolve(result); } } args.push(callback); f.apply(this, args); }); }; } async function method(name, params) { let data = null; switch (params.type) { case "sync": data = fs[name + "Sync"].apply(null, params.args); break; case "callback": data = await promisify(fs[name]).apply(null, params.args); break; case "promise": data = await fs.promises[name].apply(null, params.args); break; } return data; } async function readfile_test(params) { let data = await method("readFile", params).catch(e => ({error:e})); if (params.slice && !data.error) { data = data.slice.apply(data, params.slice); } if (params.check) { if (!params.check(data, params)) { throw Error(`readFile failed check`); } } else if (params.exception) { throw data.error; } else { let success = true; if (data instanceof Buffer) { if (data.compare(params.expected) != 0) { success = false; } } else if (data != params.expected) { success = false; } if (!success) { throw Error(`readFile unexpected data`); } } return 'SUCCESS'; } let readfile_tests = () => [ { args: ["test/fs/utf8"], expected: Buffer.from("αβZγ") }, { args: [Buffer.from("@test/fs/utf8").slice(1)], expected: Buffer.from("αβZγ") }, { args: ["test/fs/utf8", "utf8"], expected: "αβZγ" }, { args: ["test/fs/utf8", {encoding: "utf8", flags:"r+"}], expected: "αβZγ" }, { args: ["test/fs/nonexistent"], check: (err, params) => { let e = err.error; if (e.syscall != 'open') { throw Error(`${e.syscall} unexpected syscall`); } if (e.code != "ENOENT") { throw Error(`${e.code} unexpected code`); } if (e.path != "test/fs/nonexistent") { throw Error(`${e.path} unexpected path`); } return true; } }, { args: ["test/fs/non_utf8", "utf8"], expected: "��", skip() { return njs && njs.engine == 'QuickJS'; } }, { args: ["test/fs/non_utf8", {encoding: "hex"}], expected: "8080" }, { args: ["test/fs/non_utf8", "base64"], expected: "gIA=" }, { args: ["test/fs/ascii", "utf8"], expected: "x".repeat(600) }, { args: ["test/fs/ascii", { encoding:"utf8", flags: "r+"}], expected: "x".repeat(600) }, { args: [Buffer.from([0x80, 0x80])], exception: "Error: No such file or directory" }, { args: ['x'.repeat(8192)], exception: "TypeError: \"path\" is too long" }, { args: ["/proc/version"], slice:[0,5], expected: Buffer.from("Linux"), check: (data, params) => { if (data.error) { let e = data.error; if (e.syscall != 'open') { throw Error(`${e.syscall} unexpected syscall`); } return true; } return data.compare(params.expected) == 0; } }, { args: ["/proc/cpuinfo"], check: (data, params) => { if (data.error) { let e = data.error; if (e.syscall != 'open') { throw Error(`${e.syscall} unexpected syscall`); } return true; } return /processor/.test(data); } }, ]; let readFile_tsuite = { name: "fs readFile", skip: () => (!has_buffer()), T: readfile_test, prepare_args: p, opts: { type: "callback" }, get tests() { return readfile_tests() }, }; let readFileSync_tsuite = { name: "fs readFileSync", skip: () => (!has_buffer()), T: readfile_test, prepare_args: p, opts: { type: "sync" }, get tests() { return readfile_tests() }, }; let readFileP_tsuite = { name: "fsp readFile", skip: () => (!has_buffer()), T: readfile_test, prepare_args: p, opts: { type: "promise" }, get tests() { return readfile_tests() }, }; async function writefile_test(params) { let fname = params.args[0]; try { fs.unlinkSync(fname); } catch (e) {} let data = await method("writeFile", params).catch(e => ({error:e})); if (!data) { data = fs.readFileSync(fname); } try { fs.unlinkSync(fname); } catch (e) {} if (params.check) { if (!params.check(data, params)) { throw Error(`writeFile failed check`); } } else if (params.exception) { throw data.error; } else { if (data.compare(params.expected) != 0) { throw Error(`writeFile unexpected data`); } } return 'SUCCESS'; } let writefile_tests = () => [ { args: ["@", Buffer.from(Buffer.alloc(4).fill(65).buffer, 1)], expected: Buffer.from("AAA") }, { args: ["@", Buffer.from("XYZ"), "utf8"], expected: Buffer.from("XYZ") }, { args: ["@", Buffer.from("XYZ"), {encoding: "utf8", mode: 0o666}], expected: Buffer.from("XYZ") }, { args: ["@", new DataView(Buffer.alloc(3).fill(66).buffer)], expected: Buffer.from("BBB"), skip() { return njs && njs.engine == 'QuickJS'; } }, { args: ["@", new Uint8Array(Buffer.from("ABCD"))], expected: Buffer.from("ABCD")}, { args: ["@", "XYZ"], expected: Buffer.from("XYZ")}, { args: ["@", "78797a", "hex"], expected: Buffer.from("xyz") }, { args: ["@", "eHl6", "base64"], expected: Buffer.from("xyz") }, { args: ["@", "eHl6", {encoding: "base64url"}], expected: Buffer.from("xyz"), optional: true }, { args: ["@", Symbol("XYZ")], exception: "TypeError: Cannot convert a Symbol value to a string"}, { args: ["/invalid_path", "XYZ"], check: (err, params) => { let e = err.error; if (e.syscall != 'open') { throw Error(`${e.syscall} unexpected syscall`); } if (e.code != "EACCES" && e.code != "EROFS") { throw Error(`${e.code} unexpected code`); } return true; } }, ]; let writeFile_tsuite = { name: "fs writeFile", skip: () => (!has_buffer()), T: writefile_test, prepare_args: p, opts: { type: "callback" }, get tests() { return writefile_tests() }, }; let writeFileSync_tsuite = { name: "fs writeFileSync", skip: () => (!has_buffer()), T: writefile_test, prepare_args: p, opts: { type: "sync" }, get tests() { return writefile_tests() }, }; let writeFileP_tsuite = { name: "fsp writeFile", skip: () => (!has_buffer()), T: writefile_test, prepare_args: p, opts: { type: "promise" }, get tests() { return writefile_tests() }, }; async function append_test(params) { let fname = params.args[0]; try { fs.unlinkSync(fname); } catch (e) {} let data = await method("appendFile", params).catch(e => ({error:e})); data = await method("appendFile", params).catch(e => ({error:e})); if (!data) { data = fs.readFileSync(fname); } try { fs.unlinkSync(fname); } catch (e) {} if (params.check) { if (!params.check(data, params)) { throw Error(`appendFile failed check`); } } else if (params.exception) { throw data.error; } else { if (data.compare(params.expected) != 0) { throw Error(`appendFile unexpected data`); } } return 'SUCCESS'; } let append_tests = () => [ { args: ["@", Buffer.from(Buffer.alloc(4).fill(65).buffer, 1)], expected: Buffer.from("AAAAAA") }, { args: ["@", Buffer.from("XYZ"), "utf8"], expected: Buffer.from("XYZXYZ") }, { args: ["@", Buffer.from("XYZ"), {encoding: "utf8", mode: 0o666}], expected: Buffer.from("XYZXYZ") }, { args: ["@", new DataView(Buffer.alloc(3).fill(66).buffer)], expected: Buffer.from("BBBBBB"), skip() { return njs && njs.engine == 'QuickJS'; } }, { args: ["@", new Uint8Array(Buffer.from("ABCD"))], expected: Buffer.from("ABCDABCD")}, { args: ["@", "XYZ"], expected: Buffer.from("XYZXYZ")}, { args: ["@", "78797a", "hex"], expected: Buffer.from("xyzxyz") }, { args: ["@", "eHl6", "base64"], expected: Buffer.from("xyzxyz") }, { args: ["@", "eHl6", {encoding: "base64url"}], expected: Buffer.from("xyzxyz"), optional: true }, { args: ["@", Symbol("XYZ")], exception: "TypeError: Cannot convert a Symbol value to a string"}, { args: ["/invalid_path", "XYZ"], check: (err, params) => { let e = err.error; if (e.syscall != 'open') { throw Error(`${e.syscall} unexpected syscall`); } if (e.code != "EACCES" && e.code != "EROFS") { throw Error(`${e.code} unexpected code`); } return true; } }, ]; let appendFile_tsuite = { name: "fs appendFile", skip: () => (!has_buffer()), T: append_test, prepare_args: p, opts: { type: "callback" }, get tests() { return append_tests() }, }; let appendFileSync_tsuite = { name: "fs appendFileSync", skip: () => (!has_buffer()), T: append_test, prepare_args: p, opts: { type: "sync" }, get tests() { return append_tests() }, }; let appendFileP_tsuite = { name: "fsp appendFile", skip: () => (!has_buffer()), T: append_test, prepare_args: p, opts: { type: "promise" }, get tests() { return append_tests() }, }; async function exists_test(params) { let res = await method("exists", params); if (res !== params.expected) { throw Error(`exists failed check`); } return 'SUCCESS'; } let exists_tests = () => [ { args: ["test/fs/ascii"], expected: true }, { args: ["test/fs"], expected: true }, { args: ["test/fs_NONEXISTENT/ascii"], expected: false }, { args: ["test/fs/ascii_NONEXISTENT"], expected: false }, ]; let existsSync_tsuite = { name: "fs existsSync", skip: () => (!has_buffer()), T: exists_test, prepare_args: p, opts: { type: "sync" }, get tests() { return exists_tests() }, }; async function realpath_test(params) { let data = await method("realpath", params); if (!params.check(data)) { throw Error(`realpath failed check`); } return 'SUCCESS'; } let realpath_tests = () => [ { args: ["test/fs/.."], check: (data) => data.endsWith("test") }, { args: ["test/fs/ascii", {encoding:'buffer'}], check: (data) => data instanceof Buffer }, ]; let realpath_tsuite = { name: "fs realpath", skip: () => (!has_buffer()), T: realpath_test, prepare_args: p, opts: { type: "callback" }, get tests() { return realpath_tests() }, }; let realpathSync_tsuite = { name: "fs realpathSync", skip: () => (!has_buffer()), T: realpath_test, prepare_args: p, opts: { type: "sync" }, get tests() { return realpath_tests() }, }; let realpathP_tsuite = { name: "fsp realpath", skip: () => (!has_buffer()), T: realpath_test, prepare_args: p, opts: { type: "promise" }, get tests() { return realpath_tests() }, }; async function readlink_test(params) { let lname = params.args[0]; try { fs.unlinkSync(lname); } catch (e) {} fs.symlinkSync("test/fs/ascii", lname); let data = await method("readlink", params); if (!params.check(data)) { throw Error(`readlink failed check`); } return 'SUCCESS'; } let readlink_tests = () => [ { args: [`${test_dir}/symlink`], check: (data) => data.endsWith("test/fs/ascii") }, { args: [`${test_dir}/symlink`, {encoding:'buffer'}], check: (data) => data instanceof Buffer }, { args: [`${test_dir}/symlink`, {encoding:'hex'}], check: (data) => data.endsWith("746573742f66732f6173636969") }, ]; let readlink_tsuite = { name: "fs readlink", skip: () => (!has_buffer()), T: readlink_test, prepare_args: p, opts: { type: "callback" }, get tests() { return readlink_tests() }, }; let readlinkSync_tsuite = { name: "fs readlinkSync", skip: () => (!has_buffer()), T: readlink_test, prepare_args: p, opts: { type: "sync" }, get tests() { return readlink_tests() }, }; let readlinkP_tsuite = { name: "fsp readlink", skip: () => (!has_buffer()), T: readlink_test, prepare_args: p, opts: { type: "promise" }, get tests() { return readlink_tests() }, }; async function method_test(params) { if (params.init) { params.init(params); } let ret = await method(params.method, params).catch(e => ({error:e})); if (params.check && !params.check(ret, params)) { throw Error(`${params.method} failed check`); } return 'SUCCESS'; } function contains(arr, elts) { return elts.every(el => { let r = arr.some(v => el == v); if (!r) { throw Error(`${el} is not found`); } return r; }); } let stat_tests = () => [ { args: ["/invalid_path"], check: (err, params) => { let e = err.error; if (e.syscall != params.method) { throw Error(`${e.syscall} unexpected syscall`); } if (e.code != "ENOENT") { throw Error(`${e.code} unexpected code`); } return true; } }, { args: ["@_link"], init: (params) => { let lname = params.args[0]; let fname = lname.slice(0, -5); /* making symbolic link. */ try { fs.unlinkSync(fname); } catch (e) {} try { fs.unlinkSync(lname); } catch (e) {} fs.writeFileSync(fname, fname); fname = fs.realpathSync(fname); fs.symlinkSync(fname, lname); }, check: (st, params) => { switch (params.method) { case "stat": if (!st.isFile()) { throw Error(`${params.args[0]} is not a file`); } break; case "lstat": if (!st.isSymbolicLink()) { throw Error(`${params.args[0]} is not a link`); } break; } return true; } }, { args: ["test/fs/ascii"], check: (st) => contains(Object.keys(st), [ "atime", "atimeMs", "birthtime", "birthtimeMs", "blksize", "blocks", "ctime", "ctimeMs", "dev", "gid", "ino", "mode", "mtime", "mtimeMs","nlink", "rdev", "size", "uid" ]) }, { args: ["test/fs/ascii"], check: (st) => Object.keys(st).every(p => { let v = st[p]; if (p == 'atime' || p == 'ctime' || p == 'mtime' || p == 'birthtime') { if (!(v instanceof Date)) { throw Error(`${p} is not an instance of Date`); } return true; } if ((typeof v) != 'number') { throw Error(`${p} is not an instance of Number`); } return true; }) }, { args: ["test/fs/ascii"], check: (st) => ['atime', 'birthtime', 'ctime', 'mtime'].every(p => { let date = st[p].valueOf(); let num = st[p + 'Ms']; if (Math.abs(date - num) > 1) { throw Error(`${p}:${date} != ${p+'Ms'}:${num}`); } return true; }) }, { args: [test_dir], check: (st) => ['isBlockDevice', 'isCharacterDevice', 'isDirectory', 'isFIFO', 'isFile', 'isSocket', 'isSymbolicLink'].every(m => { let r = st[m](); if (!(r == (m == 'isDirectory'))) { throw Error(`${m} is ${r}`); } return true; }) }, ]; let stat_tsuite = { name: "fs stat", skip: () => (!has_fs_symbolic_link() || !has_buffer()), T: method_test, prepare_args: p, opts: { type: "callback", method: "stat" }, get tests() { return stat_tests() }, }; let statSync_tsuite = { name: "fs statSync", skip: () => (!has_fs_symbolic_link() || !has_buffer()), T: method_test, prepare_args: p, opts: { type: "sync", method: "stat" }, get tests() { return stat_tests() }, }; let statP_tsuite = { name: "fsp stat", skip: () => (!has_fs_symbolic_link() || !has_buffer()), T: method_test, prepare_args: p, opts: { type: "promise", method: "stat" }, get tests() { return stat_tests() }, }; let lstat_tsuite = { name: "fs lstat", skip: () => (!has_fs_symbolic_link() || !has_buffer()), T: method_test, prepare_args: p, opts: { type: "callback", method: "lstat" }, get tests() { return stat_tests() }, }; let lstatSync_tsuite = { name: "fs lstatSync", skip: () => (!has_fs_symbolic_link() || !has_buffer()), T: method_test, prepare_args: p, opts: { type: "sync", method: "lstat" }, get tests() { return stat_tests() }, }; let lstatP_tsuite = { name: "fsp lstat", skip: () => (!has_fs_symbolic_link() || !has_buffer()), T: method_test, prepare_args: p, opts: { type: "promise", method: "lstat" }, get tests() { return stat_tests() }, }; let open_check = (fh, params) => { if (params.type == 'promise') { try { if (typeof fh.fd != 'number') { throw Error(`filehandle.fd:${fh.fd} is not an instance of Number`); } ['read', 'write', 'close', 'valueof'].every(v => { if (typeof fh[v] != 'function') { throw Error(`filehandle.close:${fh[v]} is not an instance of function`); } }); let mode = fs.fstatSync(fh.fd).mode & 0o777; if (params.mode && params.mode != mode) { throw Error(`opened mode ${mode} != ${params.mode}`); } } finally { fh.close(); } } else { try { if (typeof fh != 'number') { throw Error(`fd:${fh} is not an instance of Number`); } let mode = fs.fstatSync(fh).mode & 0o777; if (params.mode && params.mode != mode) { throw Error(`opened mode ${mode} != ${params.mode}`); } } finally { fs.closeSync(fh); } } return true; }; let open_tests = () => [ { args: ["test/fs/ascii"], check: open_check, }, { args: ["@", 'w', 0o600], mode: 0o600, check: open_check, }, { args: ["@", 'a', 0o700], mode: 0o700, check: open_check, }, { args: ["@", 'r'], check: (err, params) => { let e = err.error; if (e.syscall != params.method) { throw Error(`${e.syscall} unexpected syscall`); } if (e.code != "ENOENT") { throw Error(`${e.code} unexpected code`); } return true; }, }, { args: ["/invalid_path"], check: (err, params) => { let e = err.error; if (e.syscall != params.method) { throw Error(`${e.syscall} unexpected syscall`); } if (e.code != "ENOENT") { throw Error(`${e.code} unexpected code`); } return true; }, }, ]; let openSync_tsuite = { name: "fs openSync", skip: () => (!has_buffer()), T: method_test, prepare_args: p, opts: { type: "sync", method: "open" }, get tests() { return open_tests() }, }; let openP_tsuite = { name: "fsp open", skip: () => (!has_buffer()), T: method_test, prepare_args: p, opts: { type: "promise", method: "open" }, get tests() { return open_tests() }, }; let close_tests = () => [ { args: [ fs.openSync("test/fs/ascii") ], check: (undef, params) => undef === undefined, }, { args: [ (() => { let fd = fs.openSync("test/fs/ascii"); fs.closeSync(fd); return fd})() ], check: (err, params) => { let e = err.error; if (e.syscall != params.method) { throw Error(`${e.syscall} unexpected syscall`); } if (e.code != "EBADF") { throw Error(`${e.code} unexpected code`); } return true; }, }, ]; let closeSync_tsuite = { name: "fs closeSync", skip: () => (!has_buffer()), T: method_test, prepare_args: p, opts: { type: "sync", method: "close" }, get tests() { return close_tests() }, }; function read_test(params) { let fd, err; let fn = `${test_dir}/fs_read_test_${Math.round(Math.random() * 1000000)}`; let out = []; fs.writeFileSync(fn, params.content); try { fd = fs.openSync(fn); let buffer = Buffer.alloc(4); buffer.fill('#'); for (var i = 0; i < params.read.length; i++) { let args = params.read[i].map(v => v); args.unshift(buffer); args.unshift(fd); let bytesRead = fs.readSync.apply(null, args); out.push([bytesRead, Buffer.from(buffer)]); } } catch (e) { if (!e.syscall && !params.check) { throw e; } err = e; } finally { fs.closeSync(fd); } if (!err && params.expected) { let expected = params.expected; if (out.length != expected.length) { throw Error(`unexpected readSync number of outputs ${out.length} != ${expected.length}`); } for (var i = 0; i < expected.length; i++) { if (expected[i][0] != out[i][0]) { throw Error(`unexpected readSync bytesRead:${out[i][0]} != ${expected[i][0]}`); } if (expected[i][1].compare(out[i][1]) != 0) { throw Error(`unexpected readSync buffer:${out[i][1]} != ${expected[i][1]}`); } } } if (params.check && !params.check(err, params)) { throw Error(`${params.method} failed check`); } return 'SUCCESS'; } async function readFh_test(params) { let fh, err; let fn = `${test_dir}/fs_read_test_${Math.round(Math.random() * 1000000)}`; let out = []; fs.writeFileSync(fn, params.content); try { fh = await fs.promises.open(fn); let buffer = Buffer.alloc(4); buffer.fill('#'); for (var i = 0; i < params.read.length; i++) { let args = params.read[i].map(v => v); args.unshift(buffer); let bs = await fh.read.apply(fh, args); out.push([bs.bytesRead, Buffer.from(bs.buffer)]); } } catch (e) { if (!e.syscall && !params.check) { throw e; } err = e; } finally { await fh.close(); } if (!err && params.expected) { let expected = params.expected; if (out.length != expected.length) { throw Error(`unexpected read number of outputs ${out.length} != ${expected.length}`); } for (var i = 0; i < expected.length; i++) { if (expected[i][0] != out[i][0]) { throw Error(`unexpected read bytesRead:${out[i][0]} != ${expected[i][0]}`); } if (expected[i][1].compare(out[i][1]) != 0) { throw Error(`unexpected read buffer:${out[i][1]} != ${expected[i][1]}`); } } } if (params.check && !params.check(err, params)) { throw Error(`${params.method} failed check`); } return 'SUCCESS'; } let read_tests = () => [ { content: "ABC", read: [ [0, 3], ], expected: [ [3, Buffer.from("ABC#")], ], }, { content: "ABC", read: [ [1, 2], ], expected: [ [2, Buffer.from("#AB#")], ], }, { content: "ABC", read: [ [1, 2, 1], ], expected: [ [2, Buffer.from("#BC#")], ], }, { content: "__ABCDE", read: [ [0, 4], [0, 4], [2, 2, 0], [0, 4, null], ], expected: [ [4, Buffer.from("__AB")], [3, Buffer.from("CDEB")], [2, Buffer.from("CD__")], [0, Buffer.from("CD__")], ], }, { content: "ABC", read: [ [0, 5], ], check: (err, params) => { if (!(err instanceof RangeError)) { throw Error(`${err.message} unexpected exception`); } return true; }, }, { content: "ABC", read: [ [2, 3], ], check: (err, params) => { if (!(err instanceof RangeError)) { throw Error(`${err.message} unexpected exception`); } return true; }, }, ]; let readSync_tsuite = { name: "fs readSync", skip: () => (!has_buffer()), T: read_test, prepare_args: p, opts: {}, get tests() { return read_tests() }, }; let readFh_tsuite = { name: "fh read", skip: () => (!has_buffer()), T: readFh_test, prepare_args: p, opts: {}, get tests() { return read_tests() }, }; function write_test(params) { let fd, err; try { fd = fs.openSync.apply(null, params.args); for (var i = 0; i < params.write.length; i++) { let args = params.write[i].map(v => v); args.unshift(fd); let bytesWritten = fs.writeSync.apply(null, args); if (params.written && bytesWritten != params.written) { throw Error(`bw.bytesWritten unexpected value:${bw.bytesWritten}`); } } } catch (e) { if (!e.syscall && !params.check) { throw e; } err = e; } finally { fs.closeSync(fd); } if (!err && params.expected) { let data = fs.readFileSync(params.args[0]); if (data.compare(params.expected) != 0) { throw Error(`fh.write unexpected data:${data}`); } } if (params.check && !params.check(err, params)) { throw Error(`${params.method} failed check`); } return 'SUCCESS'; } async function writeFh_test(params) { let fh, err; try { fh = await fs.promises.open.apply(null, params.args); for (var i = 0; i < params.write.length; i++) { let bw = await fh.write.apply(fh, params.write[i]); if (params.written && bw.bytesWritten != params.written) { throw Error(`bw.bytesWritten unexpected value:${bw.bytesWritten}`); } if (params.buffer && (typeof params.buffer == 'string' && params.buffer != bw.buffer || typeof params.buffer == 'object' && params.buffer.compare(bw.buffer) != 0)) { throw Error(`bw.buffer unexpected value:${bw.buffer}`); } } } catch (e) { if (!e.syscall && !params.check) { throw e; } err = e; } finally { await fh.close(); } if (!err && params.expected) { let data = fs.readFileSync(params.args[0]); if (data.compare(params.expected) != 0) { throw Error(`fh.write unexpected data:${data}`); } } if (params.check && !params.check(err, params)) { throw Error(`${params.method} failed check`); } return 'SUCCESS'; } let write_tests = () => [ { args: ["@", 'w'], write: [ ["ABC", undefined], ["DE", null], ["F"], ], expected: Buffer.from("ABCDEF"), }, { args: ["@", 'w'], write: [ ["XXXXXX"], ["YYYY", 1], ["ZZ", 2], ], expected: Buffer.from("XYZZYX"), }, { args: ["@", 'w'], write: [ ["ABC", null, 'utf8'] ], written: 3, buffer: 'ABC', expected: Buffer.from("ABC"), }, { args: ["test/fs/ascii"], write: [ ["ABC"] ], check: (err, params) => { let e = err; if (e.syscall != 'write') { throw Error(`${e.syscall} unexpected syscall`); } if (e.code != "EBADF") { throw Error(`${e.code} unexpected code`); } return true; }, }, { args: ["@", 'w'], write: [ [Buffer.from("ABC"), 0, 3], [Buffer.from("DE"), 0, 2, null], [Buffer.from("F"), 0, 1], ], expected: Buffer.from("ABCDEF"), }, { args: ["@", 'w'], write: [ [Buffer.from("__XXXXXX"), 2], [Buffer.from("__YYYY__"), 2, 4, 1], [Buffer.from("ZZ"), 0, 2, 2], ], expected: Buffer.from("XYZZYX"), }, { args: ["@", 'w'], write: [ [Buffer.from("__ABC__"), 2, 3] ], written: 3, buffer: Buffer.from('__ABC__'), expected: Buffer.from("ABC"), }, { args: ["@", 'w'], write: [ [Buffer.from("__ABC__"), 7] ], written: 0, }, { args: ["@", 'w'], write: [ [Buffer.from("__ABC__"), 8] ], check: (err, params) => { if (!(err instanceof RangeError)) { throw Error(`${err.message} unexpected exception`); } return true; }, }, { args: ["@", 'w'], write: [ [Buffer.from("__ABC__"), 7, 1] ], check: (err, params) => { if (!(err instanceof RangeError)) { throw Error(`${err.message} unexpected exception`); } return true; }, }, ]; let writeSync_tsuite = { name: "fs writeSync", skip: () => (!has_buffer()), T: write_test, prepare_args: p, opts: {}, get tests() { return write_tests() }, }; let writeFh_tsuite = { name: "fh write", skip: () => (!has_buffer()), T: writeFh_test, prepare_args: p, opts: {}, get tests() { return write_tests() }, }; run([ readFile_tsuite, readFileSync_tsuite, readFileP_tsuite, writeFile_tsuite, writeFileSync_tsuite, writeFileP_tsuite, appendFile_tsuite, appendFileSync_tsuite, appendFileP_tsuite, existsSync_tsuite, realpath_tsuite, realpathSync_tsuite, realpathP_tsuite, readlink_tsuite, readlinkSync_tsuite, readlinkP_tsuite, stat_tsuite, statSync_tsuite, statP_tsuite, lstat_tsuite, lstatSync_tsuite, lstatP_tsuite, openSync_tsuite, openP_tsuite, readSync_tsuite, readFh_tsuite, writeSync_tsuite, writeFh_tsuite, closeSync_tsuite, ]) .then($DONE, $DONE); njs-0.8.9/test/fs/mkdir.t.mjs000066400000000000000000000072531474132077100160110ustar00rootroot00000000000000/*--- includes: [compareArray.js, compatFs.js] flags: [async] ---*/ var dname = `${test_dir}/fs_promises_05`; var dname_utf8 = `${test_dir}/fs_promises_αβγ_05`; var fname = (d) => d + '/fs_promises_05_file'; let stages = []; var testSync = () => new Promise((resolve, reject) => { try { try { fs.unlinkSync(fname(dname)); } catch (e) {} try { fs.unlinkSync(fname(dname_utf8)); } catch (e) {} try { fs.rmdirSync(dname); } catch (e) {} try { fs.rmdirSync(dname_utf8); } catch (e) {} fs.mkdirSync(dname); try { fs.mkdirSync(dname); } catch (e) { if (e.syscall != 'mkdir' || e.code != 'EEXIST') { throw e; } } fs.writeFileSync(fname(dname), fname(dname)); try { fs.rmdirSync(dname); } catch (e) { if (e.syscall != 'rmdir' || (e.code != 'ENOTEMPTY' && e.code != 'EEXIST')) { throw e; } } fs.unlinkSync(fname(dname)); fs.rmdirSync(dname); fs.mkdirSync(dname_utf8, 0o555); try { fs.writeFileSync(fname(dname_utf8), fname(dname_utf8)); const mode = fs.statSync(fname(dname_utf8)).mode & 0o777; if (mode == 0o555) { /* * Some file systems ignore the mode parameter for mkdir. * For example: a shared folder on a MacOS host mounted * to a Linux guest via Parallels Desktop. */ throw new Error('fs.writeFileSync did not respect mode'); } fs.unlinkSync(fname(dname_utf8)); } catch (e) { if (e.syscall != 'open' || e.code != 'EACCES') { throw e; } } try { fs.unlinkSync(dname_utf8); } catch (e) { if (e.syscall != 'unlink' || (e.code != 'EISDIR' && e.code != 'EPERM')) { throw e; } } fs.rmdirSync(dname_utf8); stages.push("mkdirSync"); resolve(); } catch (e) { reject(e); } }); var testCallback = () => new Promise((resolve, reject) => { try { try { fs.unlinkSync(fname(dname)); } catch (e) {} try { fs.unlinkSync(fname(dname_utf8)); } catch (e) {} try { fs.rmdirSync(dname); } catch (e) {} try { fs.rmdirSync(dname_utf8); } catch (e) {} fs.mkdir(dname, (err) => { if (err) { reject(err); } fs.mkdir(dname, (err) => { if (!err || err.code != 'EEXIST') { reject(new Error('fs.mkdir error 1')); } fs.rmdir(dname, (err) => { if (err) { reject(err); } stages.push("mkdir"); resolve(); }); }); }); } catch (e) { reject(e); } }); let testFsp = () => Promise.resolve() .then(() => { try { fs.unlinkSync(fname(dname)); } catch (e) {} try { fs.unlinkSync(fname(dname_utf8)); } catch (e) {} try { fs.rmdirSync(dname); } catch (e) {} try { fs.rmdirSync(dname_utf8); } catch (e) {} }) .then(() => fsp.mkdir(dname)) .then(() => fsp.mkdir(dname)) .catch((e) => { if (e.syscall != 'mkdir' || e.code != 'EEXIST') { throw e; } }) .then(() => fsp.rmdir(dname)) .then(() => { stages.push("fsp.mkdir"); }) let p = Promise.resolve() .then(testSync) .then(testCallback) .then(testFsp) .then(() => assert.compareArray(stages, ['mkdirSync', 'mkdir', 'fsp.mkdir'])) p.then($DONE, $DONE); njs-0.8.9/test/fs/mkdir2.t.mjs000066400000000000000000000040201474132077100160600ustar00rootroot00000000000000/*--- includes: [compareArray.js, compatFs.js] flags: [async] ---*/ var dname = `${test_dir}/fs_promises_αβγ_08/`; var path = 'one/two/three/αβγ'; var wipePath = (root, path, nofail) => { path .split('/') .map((x, i, a) => { return root + a.slice(0, i + 1).join('/'); }) .reverse() .map((dir) => { try { fs.rmdirSync(dir); } catch (e) { if (!nofail) { throw e; } } }); }; let stages = []; var testSync = () => new Promise((resolve, reject) => { try { wipePath(dname, path + '/' + path, true); fs.rmdirSync(dname); } catch (e) { } try { fs.mkdirSync(dname); fs.mkdirSync(dname, { recursive: true }); fs.mkdirSync(dname + '/', { recursive: true }); fs.mkdirSync(dname + '////', { recursive: true }); fs.mkdirSync(dname + path, { recursive: true }); wipePath(dname, path); fs.mkdirSync(dname + '////' + path + '////' + path + '////', { recursive: true }); wipePath(dname, path + '/' + path); try { fs.mkdirSync(dname + path, { recursive: true, mode: 0 }); } catch (e) { if (e.code != 'EACCES') { reject(e); } } wipePath(dname, path, true); try { fs.mkdirSync(dname + path, { recursive: true }); fs.writeFileSync(dname + path + '/one', 'not dir'); fs.mkdirSync(dname + path + '/' + path, { recursive: true }); } catch (e) { if (e.code != 'ENOTDIR') { reject(e); } } fs.unlinkSync(dname + path + '/one'); wipePath(dname, path); fs.rmdirSync(dname); stages.push("mkdirSync") resolve(); } catch (e) { reject(e); } }); let p = Promise.resolve() .then(testSync) .then(() => assert.compareArray(stages, ["mkdirSync"])) p.then($DONE, $DONE); njs-0.8.9/test/fs/non_utf8000066400000000000000000000000021474132077100153720ustar00rootroot00000000000000njs-0.8.9/test/fs/read_write.t.mjs000066400000000000000000000021461474132077100170240ustar00rootroot00000000000000/*--- includes: [compareArray.js, compatFs.js] flags: [async] ---*/ var fname = `${test_dir}/fs_promises_01`; let stages = []; let test = () => Promise.resolve() .then(() => { return fsp.writeFile(fname, fname); }) .then(data => { stages.push('init'); assert.sameValue(data, undefined, 'init'); }) .then(() => { return fsp.readFile(fname).then(fsp.readFile); }) .then(data => { stages.push('short circut'); assert.sameValue(data.toString(), fname, 'short circut'); }) .then(() => { var read = fsp.readFile.bind(fsp, fname, 'utf8'); var write = fsp.writeFile.bind(fsp, fname); var append = fsp.appendFile.bind(fsp, fname); return write(fname).then(read).then(append).then(read); }) .then(data => { stages.push('chain'); assert.sameValue(data, fname + fname, 'chain'); }) .then(() => { stages.push('errors ok'); }) .then(() => { assert.compareArray(stages, ["init", "short circut", "chain", "errors ok"]); }) let p = Promise.resolve() .then(test) .then(() => assert.compareArray(stages, ["init", "short circut", "chain", "errors ok"])) p.then($DONE, $DONE); njs-0.8.9/test/fs/readdir.t.mjs000066400000000000000000000157001474132077100163110ustar00rootroot00000000000000/*--- includes: [compareArray.js, compatFs.js] flags: [async] ---*/ var dname = `${test_dir}/fs_promises_07`; var dname_utf8 = `${test_dir}/fs_promises_αβγ_07`; var fname = (d) => d + '/fs_promises_07_file'; var lname = (d) => d + '/fs_promises_07_link'; var cname = (d) => d + '/fs_promises_αβγ_07_dir'; var dir_test = [cname(''), lname(''), fname('')].map((x) => x.substring(1)); var match = (entry) => { var idx = dir_test.indexOf(entry.name); try { switch (idx) { case 0: return entry.isDirectory(); case 1: return entry.isSymbolicLink(); case 2: return entry.isFile(); default: return false; } } catch (e) { if (e instanceof InternalError) { return true; } throw e; } }; let stages = []; var testSync = () => new Promise((resolve, reject) => { try { try { fs.rmdirSync(cname(dname)); } catch (e) {} try { fs.rmdirSync(cname(dname_utf8)); } catch (e) {} try { fs.unlinkSync(lname(dname)); } catch (e) {} try { fs.unlinkSync(lname(dname_utf8)); } catch (e) {} try { fs.unlinkSync(fname(dname)); } catch (e) {} try { fs.unlinkSync(fname(dname_utf8)); } catch (e) {} try { fs.rmdirSync(dname); } catch (e) {} try { fs.rmdirSync(dname_utf8); } catch (e) {} try { fs.readdirSync(dname); throw new Error('fs.readdirSync - error 0'); } catch (e) { if (e.code != 'ENOENT') { // njs: e.syscall == 'opendir' // node: e.syscall == 'scandir' throw e; } } fs.mkdirSync(dname); fs.mkdirSync(dname_utf8); fs.writeFileSync(fname(dname), fname(dname)); fs.writeFileSync(fname(dname_utf8), fname(dname_utf8)); fs.symlinkSync(fname('.'), lname(dname)); fs.symlinkSync(fname('.'), lname(dname_utf8)); fs.mkdirSync(cname(dname)); fs.mkdirSync(cname(dname_utf8)); var dir = fs.readdirSync(dname); var dir_utf8 = fs.readdirSync(dname_utf8); if (dir.length != dir_utf8.length || dir.length != 3) { throw new Error('fs.readdirSync - error 1'); } var test = dir.filter((x) => !dir_test.includes(x)); if (test.length != 0) { throw new Error('fs.readdirSync - error 2'); } var test = dir_utf8.filter((x) => !dir_test.includes(x)); if (test.length != 0) { throw new Error('fs.readdirSync - error 3'); } var dir = fs.readdirSync(dname, { withFileTypes: true }); var dir_utf8 = fs.readdirSync(dname_utf8, { withFileTypes: true }); if (dir.length != dir_utf8.length || dir.length != 3) { throw new Error('fs.readdirSync - error 4'); } var test = dir.filter((x) => !match(x)); if (test.length != 0) { throw new Error('fs.readdirSync - error 5'); } var test = dir_utf8.filter((x) => !match(x)); if (test.length != 0) { throw new Error('fs.readdirSync - error 6'); } var dir_buffer = fs.readdirSync(dname, {encoding:'buffer'}); if (dir_buffer.length != 3 || !(dir_buffer[0] instanceof Buffer)) { throw new Error('fs.readdirSync - error 7'); } var dir_buffer_types = fs.readdirSync(dname, {encoding:'buffer', withFileTypes: true}); if (dir_buffer_types.length != 3 || !(dir_buffer_types[0].name instanceof Buffer)) { throw new Error('fs.readdirSync - error 8'); } stages.push("readdirSync"); resolve(); } catch (e) { reject(e); } }); var testCallback = () => new Promise((resolve, reject) => { try { try { fs.rmdirSync(cname(dname)); } catch (e) {} try { fs.unlinkSync(lname(dname)); } catch (e) {} try { fs.unlinkSync(fname(dname)); } catch (e) {} try { fs.rmdirSync(dname); } catch (e) {} fs.readdir(dname, (err, files) => { if (!err || err.code != 'ENOENT') { reject(new Error('fs.readdir - error 1')); } try { fs.mkdirSync(dname); fs.writeFileSync(fname(dname), fname(dname)); fs.symlinkSync(fname('.'), lname(dname)); fs.mkdirSync(cname(dname)); } catch (e) { reject(e); } fs.readdir(dname, (err, dir) => { if (err) { reject(err); } if (dir.length != 3) { reject(new Error('fs.readdir - error 2')); } var test = dir.filter((x) => !dir_test.includes(x)); if (test.length != 0) { reject(new Error('fs.readdir - error 3')); } fs.readdir(dname, { withFileTypes: true }, (err, dir) => { if (err) { reject(err); } if (dir.length != 3) { reject(new Error('fs.readdir - error 4')); } var test = dir.filter((x) => !match(x)); if (test.length != 0) { reject(new Error('fs.readdir - error 5')); } stages.push("readdir"); resolve(); }); }); }); } catch (e) { reject(e); } }); let testFsp = () => Promise.resolve() .then(() => { try { fs.rmdirSync(cname(dname)); } catch (e) {} try { fs.unlinkSync(lname(dname)); } catch (e) {} try { fs.unlinkSync(fname(dname)); } catch (e) {} try { fs.rmdirSync(dname); } catch (e) {} }) .then(() => fsp.readdir(dname) .then(() => { throw new Error('fsp.readdir - error 1'); })) .catch((e) => { if (e.code != 'ENOENT') { throw e; } }) .then(() => { fs.mkdirSync(dname); fs.writeFileSync(fname(dname), fname(dname)); fs.symlinkSync(fname('.'), lname(dname)); fs.mkdirSync(cname(dname)); }) .then(() => fsp.readdir(dname)) .then((dir) => { if (dir.length != 3) { throw new Error('fsp.readdir - error 2'); } var test = dir.filter((x) => !dir_test.includes(x)); if (test.length != 0) { throw new Error('fsp.readdir - error 3'); } }) .then(() => fsp.readdir(dname, { withFileTypes: true })) .then((dir) => { if (dir.length != 3) { throw new Error('fsp.readdir - error 4'); } var test = dir.filter((x) => !match(x)); if (test.length != 0) { throw new Error('fsp.readdir - error 5'); } }) .then(() => { stages.push("fsp.readdir"); }) let p = Promise.resolve() if (has_fs_symbolic_link()) { p = p .then(testSync) .then(testCallback) .then(testFsp) .then(() => assert.compareArray(stages, ['readdirSync', 'readdir', 'fsp.readdir'])) } p.then($DONE, $DONE); njs-0.8.9/test/fs/realpath.t.mjs000066400000000000000000000113501474132077100164740ustar00rootroot00000000000000/*--- includes: [compareArray.js, compatFs.js] flags: [async] ---*/ var fname = `${test_dir}/fs_promises_04`; var fname_utf8 = `${test_dir}/fs_promises_αβγ_04`; var lname = `${test_dir}/fs_promises_lnk_04`; let stages = []; var testSync = () => new Promise((resolve, reject) => { try { try { fs.unlinkSync(fname); } catch (e) {} try { fs.unlinkSync(lname); } catch (e) {} try { fs.realpathSync(fname); throw new Error('fs.realpathSync error 1'); } catch (e) { if (e.code != 'ENOENT') { throw new Error('fs.realpathSync error 2'); } } fs.writeFileSync(fname, fname); fs.writeFileSync(fname_utf8, fname_utf8); var rname = fs.realpathSync(fname); fs.symlinkSync(rname, lname); if (fs.realpathSync(lname) != rname) { throw new Error('fs.symlinkSync error 2'); } if (fs.readFileSync(lname) != fname) { throw new Error('fs.symlinkSync error 3'); } var rname_utf8 = fs.realpathSync(fname_utf8); if (rname_utf8.slice(-6,-3) != 'αβγ') { throw new Error('fs.realpathSync error 3'); } fs.unlinkSync(lname); fs.accessSync(fname); fs.unlinkSync(fname); fs.unlinkSync(fname_utf8); stages.push("symlinkSync"); resolve(); } catch (e) { reject(e); } }); var testCallback = () => new Promise((resolve, reject) => { try { try { fs.unlinkSync(fname); } catch (e) { void e; } try { fs.unlinkSync(lname); } catch (e) { void e; } fs.realpath(fname, (err) => { if (!err) { reject(new Error('fs.realpath error 1')); return; } if (err.code != 'ENOENT') { reject(err); return; } try { fs.writeFileSync(fname, fname); } catch (e) { reject(e); return; } fs.realpath(fname, (err, rname) => { if (err) { reject(err); return; } fs.symlink(rname, lname, (err) => { if (err) { reject(err); return; } fs.realpath(lname, undefined, (err, xname) => { if (err) { reject(err); return; } if (rname != xname) { reject(new Error('fs.symlink error 1')); return; } try { if (fs.readFileSync(lname) != fname) { reject(new Error('fs.symlink error 2')); return; } fs.unlinkSync(lname); fs.accessSync(fname); fs.unlinkSync(fname); } catch (e) { reject(e); return; } stages.push("symlink"); resolve(); }); }); }); }); } catch (e) { reject(e); } }); let testFsp = () => Promise.resolve() .then(() => fsp.unlink(fname) .catch(() => {})) .then(() => fsp.unlink(lname) .catch(() => {})) .then(() => fsp.realpath(fname) .then(() => { throw new Error('fsp.realpath error 1') })) .catch((e) => { if (e.syscall != 'realpath' || e.code != 'ENOENT') { throw e; } }) .then(() => { fs.writeFileSync(fname, fname); return fsp.realpath(fname); }) .then((rname) => fsp.symlink(rname, lname) .then(() => rname)) .then((rname) => fsp.realpath(lname) .then((xname) => { if (rname != xname) { throw new Error(`fsp.symlink error 2`); } })) .then(() => { if (fs.readFileSync(lname) != fname) { throw new Error('fsp.symlink error 3'); } fs.unlinkSync(lname); fs.accessSync(fname); fs.unlinkSync(fname); stages.push("fsp.symlink"); }) let p = Promise.resolve() if (has_fs_symbolic_link()) { p = p .then(testSync) .then(testCallback) .then(testFsp) .then(() => assert.compareArray(stages, ['symlinkSync', 'symlink', 'fsp.symlink'])) } p.then($DONE, $DONE); njs-0.8.9/test/fs/rename.t.mjs000066400000000000000000000050321474132077100161430ustar00rootroot00000000000000/*--- includes: [compareArray.js, compatFs.js] flags: [async] ---*/ var dname = `${test_dir}/`; var fname = (d) => d + '/fs_promises_06_file'; var fname_utf8 = (d) => d + '/fs_promises_αβγ_06'; let stages = []; var testSync = () => new Promise((resolve, reject) => { try { try { fs.unlinkSync(fname(dname)); } catch (e) {} try { fs.unlinkSync(fname_utf8(dname)); } catch (e) {} fs.writeFileSync(fname(dname), fname(dname)); fs.renameSync(fname(dname), fname_utf8(dname)); fs.accessSync(fname_utf8(dname)); try { fs.renameSync(fname_utf8(dname), dname); } catch (e) { if (e.syscall != 'rename' || (e.code != 'ENOTDIR' && e.code != 'EISDIR')) { reject(new Error('fs.unlinkSync error 1')); } } stages.push("renameSync"); resolve(); } catch (e) { reject(e); } }); var testCallback = () => new Promise((resolve, reject) => { try { try { fs.unlinkSync(fname(dname)); } catch (e) {} try { fs.unlinkSync(fname_utf8(dname)); } catch (e) {} fs.writeFileSync(fname(dname), fname(dname)); fs.rename(fname(dname), fname_utf8(dname), err => { if (err) { reject(new Error('fs.unlink error 1')); } fs.accessSync(fname_utf8(dname)); fs.rename(fname_utf8(dname), dname, err => { if (err.syscall != 'rename' || (err.code != 'ENOTDIR' && err.code != 'EISDIR')) { reject(new Error('fs.unlink error 2')); } }); stages.push("rename"); resolve(); }); } catch (e) { reject(e); } }); let testFsp = () => Promise.resolve() .then(() => { try { fs.unlinkSync(fname(dname)); } catch (e) {} try { fs.unlinkSync(fname_utf8(dname)); } catch (e) {} fs.writeFileSync(fname(dname), fname(dname)); }) .then(() => fsp.rename(fname(dname), fname_utf8(dname))) .then(() => fsp.access(fname_utf8(dname))) .then(() => fsp.rename(fname_utf8(dname), dname)) .catch(e => { if (e.syscall != 'rename' || (e.code != 'ENOTDIR' && e.code != 'EISDIR')) { throw new Error('fsp.rename error 1'); } }) .then(() => { stages.push("fsp.rename"); }) let p = Promise.resolve() .then(testSync) .then(testCallback) .then(testFsp) .then(() => assert.compareArray(stages, ["renameSync", "rename", "fsp.rename"])) p.then($DONE, $DONE); njs-0.8.9/test/fs/rmdir.t.mjs000066400000000000000000000052461474132077100160200ustar00rootroot00000000000000/*--- includes: [compareArray.js, compatFs.js] flags: [async] ---*/ var root = test_dir; var dname = 'fs_promises_αβγ_09/'; var lname = 'fs_promises_αβγ_09_lnk'; var path = 'one/two/three/αβγ'; var setContent = (root, path) => { fs.mkdirSync(root + path, { recursive: true }); path .split('/') .forEach((x, i, a) => { for (var j = 1; j < 10; ++j) { var path = root + a.slice(0, i + 1).join('/') + '_file' + j; fs.writeFileSync(path, path); } }); }; var isNode = () => process.argv[0].includes('node'); let stages = []; var testSync = () => new Promise((resolve, reject) => { try { fs.unlinkSync(root + lname); } catch (e) { } try { fs.rmdirSync(root + dname, { recursive: true }); } catch (e) { } try { fs.mkdirSync(root + dname); fs.symlinkSync(dname, root + lname); try { fs.rmdirSync(root + lname); throw new Error('fs.rmdirSync() - error 0'); } catch (e) { if (e.code != "ENOTDIR") { throw e; } } fs.rmdirSync(root + dname); fs.unlinkSync(root + lname); if (!isNode()) { fs.mkdirSync(root + dname); fs.symlinkSync(dname, root + lname); try { fs.rmdirSync(root + lname, { recursive: true }); throw new Error('fs.rmdirSync() - error 1'); } catch (e) { if (e.code != "ENOTDIR") { throw e; } } fs.rmdirSync(root + dname); fs.unlinkSync(root + lname); } fs.mkdirSync(root + dname, { mode: 0 }); fs.rmdirSync(root + dname, { recursive: true }); setContent(root + dname, path); fs.rmdirSync(root + dname, { recursive: true }); try { fs.accessSync(root + dname); throw new Error('fs.rmdirSync() - error 2'); } catch (e) { if (e.code != "ENOENT") { throw e; } } if (!isNode()) { try { fs.rmdirSync(root + dname, { recursive: true }); throw new Error('fs.rmdirSync() - error 3'); } catch (e) { if (e.code != "ENOENT") { throw e; } } } stages.push("rmdirSync"); resolve(); } catch (e) { reject(e); } }); let p = Promise.resolve() if (has_fs_symbolic_link()) { p = p .then(testSync) .then(() => assert.compareArray(stages, ['rmdirSync'])) } p.then($DONE, $DONE); njs-0.8.9/test/fs/unlink.t.mjs000066400000000000000000000046601474132077100162020ustar00rootroot00000000000000/*--- includes: [compareArray.js, compatFs.js] flags: [async] ---*/ var fname = `${test_dir}/fs_promises_03`; let stages = []; var testSync = () => new Promise((resolve, reject) => { try { try { fs.unlinkSync(fname); } catch (e) { void e; } try { fs.unlinkSync(fname); throw new Error('unlinkSync error 1'); } catch (e) { if (e.syscall != 'unlink') { throw e; } } fs.writeFileSync(fname, fname); fs.unlinkSync(fname); try { fs.accessSync(fname); reject(new Error('unlinkSync error 2')); return; } catch (e) { void e; } stages.push("unlinkSync"); resolve(); } catch (e) { reject(e); } }); var testCallback = () => new Promise((resolve, reject) => { fs.unlink(fname, () => { fs.unlink(fname, (err) => { if (!err) { reject(new Error('fs.unlink error 1')); return; } if (err.syscall != 'unlink') { reject(err); return; } fs.writeFileSync(fname, fname); fs.unlink(fname, (err) => { if (err) { reject(err); return; } try { fs.accessSync(fname); reject(new Error('fs.unlink error 2')); return; } catch (e) { void e; } stages.push("unlink"); resolve(); }); }); }); }); let testFsp = () => Promise.resolve() .then(() => fsp.unlink(fname) .catch(() => {})) .then(() => fsp.unlink(fname)) .then(() => { throw new Error('fsp.unlink error 1'); }) .catch((e) => { if (e.syscall != 'unlink') { throw e; } }) .then(() => { fs.writeFileSync(fname, fname); return fsp.unlink(fname); }) .then(() => fsp.access(fname)) .then(() => { throw new Error('fsp.unlink error 2'); }) .catch((e) => { if (e.syscall != 'access') { throw e; } }) .then(() => { stages.push("fsp.unlink"); }) let p = Promise.resolve() .then(testSync) .then(testCallback) .then(testFsp) .then(() => assert.compareArray(stages, ['unlinkSync', 'unlink', 'fsp.unlink'])) p.then($DONE, $DONE); njs-0.8.9/test/fs/utf8000066400000000000000000000000071474132077100145250ustar00rootroot00000000000000αβZγnjs-0.8.9/test/harness/000077500000000000000000000000001474132077100147525ustar00rootroot00000000000000njs-0.8.9/test/harness/assert.js000066400000000000000000000056771474132077100166300ustar00rootroot00000000000000// Copyright (C) 2017 Ecma International. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- description: | Collection of assertion functions used throughout test262 defines: [assert] ---*/ function assert(mustBeTrue, message) { if (mustBeTrue === true) { return; } if (message === undefined) { message = 'Expected true but got ' + assert._toString(mustBeTrue); } throw new Test262Error(message); } assert._isSameValue = function (a, b) { if (a === b) { // Handle +/-0 vs. -/+0 return a !== 0 || 1 / a === 1 / b; } // Handle NaN vs. NaN return a !== a && b !== b; }; assert.sameValue = function (actual, expected, message) { try { if (assert._isSameValue(actual, expected)) { return; } } catch (error) { throw new Test262Error(message + ' (_isSameValue operation threw) ' + error); return; } if (message === undefined) { message = ''; } else { message += ' '; } message += 'Expected SameValue(«' + assert._toString(actual) + '», «' + assert._toString(expected) + '») to be true'; throw new Test262Error(message); }; assert.notSameValue = function (actual, unexpected, message) { if (!assert._isSameValue(actual, unexpected)) { return; } if (message === undefined) { message = ''; } else { message += ' '; } message += 'Expected SameValue(«' + assert._toString(actual) + '», «' + assert._toString(unexpected) + '») to be false'; throw new Test262Error(message); }; assert.throws = function (expectedErrorConstructor, func, message) { var expectedName, actualName; if (typeof func !== "function") { throw new Test262Error('assert.throws requires two arguments: the error constructor ' + 'and a function to run'); return; } if (message === undefined) { message = ''; } else { message += ' '; } try { func(); } catch (thrown) { if (typeof thrown !== 'object' || thrown === null) { message += 'Thrown value was not an object!'; throw new Test262Error(message); } else if (thrown.constructor !== expectedErrorConstructor) { expectedName = expectedErrorConstructor.name; actualName = thrown.constructor.name; if (expectedName === actualName) { message += 'Expected a ' + expectedName + ' but got a different error constructor with the same name'; } else { message += 'Expected a ' + expectedName + ' but got a ' + actualName; } throw new Test262Error(message); } return; } message += 'Expected a ' + expectedErrorConstructor.name + ' to be thrown but no exception was thrown at all'; throw new Test262Error(message); }; assert._toString = function (value) { try { if (value === 0 && 1 / value === -Infinity) { return '-0'; } return String(value); } catch (err) { if (err.name === 'TypeError') { return Object.prototype.toString.call(value); } throw err; } }; njs-0.8.9/test/harness/compareArray.js000066400000000000000000000021711474132077100177360ustar00rootroot00000000000000// Copyright (C) 2017 Ecma International. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- description: | Compare the contents of two arrays defines: [compareArray] ---*/ function compareArray(a, b) { if (b.length !== a.length) { return false; } for (var i = 0; i < a.length; i++) { if (!compareArray.isSameValue(b[i], a[i])) { return false; } } return true; } compareArray.isSameValue = function(a, b) { if (a === 0 && b === 0) return 1 / a === 1 / b; if (a !== a && b !== b) return true; return a === b; }; compareArray.format = function(array) { return `[${array.map(String).join(', ')}]`; }; assert.compareArray = function(actual, expected, message) { message = message === undefined ? '' : message; assert(actual != null, `First argument shouldn't be nullish. ${message}`); assert(expected != null, `Second argument shouldn't be nullish. ${message}`); var format = compareArray.format; assert( compareArray(actual, expected), `Expected ${format(actual)} and ${format(expected)} to have the same contents. ${message}` ); }; njs-0.8.9/test/harness/compareObjects.js000066400000000000000000000005311474132077100202470ustar00rootroot00000000000000function compareObjects(ref, obj) { if (!isObject(ref) || !isObject(obj)) { return ref === obj; } for (const key in ref) { if (!compareObjects(ref[key], obj[key])) { return false; } } return true; } function isObject(object) { return object != null && typeof object === 'object'; } njs-0.8.9/test/harness/compatBuffer.js000066400000000000000000000001241474132077100177220ustar00rootroot00000000000000function has_buffer() { return (typeof Buffer === 'function') && Buffer.from; } njs-0.8.9/test/harness/compatFs.js000066400000000000000000000011011474132077100170550ustar00rootroot00000000000000import fs from 'fs'; let fsp = fs.promises; if (typeof process == 'undefined') { globalThis.process = {}; } let test_dir = process.env && process.env['NJS_TEST_DIR'] || 'build'; test_dir = `${test_dir}/test`; function has_fs_symbolic_link() { let fname = test_dir + '/a'; let lname = test_dir + '/b'; try { fs.unlinkSync(fname); fs.unlinkSync(lname); } catch (e) {} fs.writeFileSync(fname, fname); fname = fs.realpathSync(fname); try { fs.symlinkSync(fname, lname); } catch (e) { return false; } return true; } njs-0.8.9/test/harness/compatNjs.js000066400000000000000000000006651474132077100172550ustar00rootroot00000000000000function has_njs(required_version) { if (typeof njs != 'object' || typeof njs.version != 'string' || typeof njs.version_number != 'number') { return false; } if (required_version) { let req_number = required_version.split('.') .map(v => Number(v)).reduce((acc, v) => acc * 256 + v, 0); return njs.version_number <= req_number; } return true; } njs-0.8.9/test/harness/compatPrint.js000066400000000000000000000001111474132077100176010ustar00rootroot00000000000000if (typeof print !== 'function') { globalThis.print = console.log; } njs-0.8.9/test/harness/compatWebcrypto.js000066400000000000000000000003251474132077100204720ustar00rootroot00000000000000if (typeof crypto == 'undefined' && typeof require == 'function') { globalThis.crypto = require('crypto').webcrypto; } function has_webcrypto() { return (typeof crypto != 'undefined') ? crypto : null; } njs-0.8.9/test/harness/compatXml.js000066400000000000000000000003661474132077100172610ustar00rootroot00000000000000let xml = null; if (typeof require == 'function' && typeof njs == 'object' && typeof njs.version == 'string') { try { xml = require('xml'); } catch (e) { // ignore } } function has_xml() { return xml; } njs-0.8.9/test/harness/doneprintHandle.js000066400000000000000000000011511474132077100204240ustar00rootroot00000000000000// Copyright (C) 2017 Ecma International. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- description: | defines: [$DONE] ---*/ function __consolePrintHandle__(msg) { print(msg); } function $DONE(error) { if (error) { if(typeof error === 'object' && error !== null && 'name' in error) { __consolePrintHandle__('Test262:AsyncTestFailure:' + error.name + ': ' + error.message); } else { __consolePrintHandle__('Test262:AsyncTestFailure:Test262Error: ' + error); } } else { __consolePrintHandle__('Test262:AsyncTestComplete'); } } njs-0.8.9/test/harness/runTsuite.js000066400000000000000000000045051474132077100173160ustar00rootroot00000000000000async function run(tlist) { function validate(t, r, i) { if (r.status == "fulfilled" && r.value === "SKIPPED") { return true; } if (r.status == "fulfilled" && !t[i].exception) { return r.value === "SUCCESS"; } if (r.status == "rejected" && t[i].exception) { return true; } if (r.status == "rejected" && t[i].optional) { return r.reason.toString().startsWith("InternalError: not implemented"); } return false; } function map(ts) { return ts.tests.map(t => { try { if (t.skip && t.skip()) { return Promise.resolve("SKIPPED"); } return ts.T(ts.prepare_args(t, ts.opts)); } catch (e) { return Promise.reject(e); } }); } for (let k = 0; k < tlist.length; k++) { let ts = tlist[k]; if (ts.skip && ts.skip()) { continue; } let results = await Promise.allSettled(map(ts)); let r = results.map((r, i) => { r.passed = validate(ts.tests, r, i); return r; }); let passed = r.filter(r => r.passed).length; if (passed != ts.tests.length) { r.forEach((r, i) => { if (!r.passed) { console.log(` ${JSON.stringify(ts.tests[i])}\n with reason: ${r.reason}`); } }) console.log(`${ts.name} FAILED: [${passed}/${ts.tests.length}]`); } } } function merge(to, from) { let r = Object.assign(Array.isArray(to) ? [] : {}, to); Object.keys(from).forEach(v => { if (typeof r[v] == 'object' && typeof from[v] == 'object') { r[v] = merge(r[v], from[v]); } else if (typeof from[v] == 'object') { if (Buffer.isBuffer(from[v])) { r[v] = Buffer.from(from[v]); } else if (from[v] instanceof Uint8Array) { r[v] = new Uint8Array(from[v]); } else if (from[v] instanceof ArrayBuffer) { r[v] = new ArrayBuffer(from[v].byteLength); } else { r[v] = Object.assign(Array.isArray(from[v]) ? [] : {}, from[v]); } } else { r[v] = from[v]; } }) return r; } njs-0.8.9/test/harness/sta.js000066400000000000000000000013111474132077100160730ustar00rootroot00000000000000// Copyright (c) 2012 Ecma International. All rights reserved. // This code is governed by the BSD license found in the LICENSE file. /*--- description: | Provides both: - An error class to avoid false positives when testing for thrown exceptions - A function to explicitly throw an exception using the Test262Error class defines: [Test262Error, $DONOTEVALUATE] ---*/ function Test262Error(message) { this.message = message || ""; } Test262Error.prototype.toString = function () { return "Test262Error: " + this.message; }; Test262Error.thrower = (message) => { throw new Test262Error(message); }; function $DONOTEVALUATE() { throw "Test262: This statement should not be evaluated."; } njs-0.8.9/test/harness/webCryptoUtils.js000066400000000000000000000012221474132077100203040ustar00rootroot00000000000000function pem_to_der(pem, type) { const pemJoined = pem.toString().split('\n').join(''); const pemHeader = `-----BEGIN ${type} KEY-----`; const pemFooter = `-----END ${type} KEY-----`; const pemContents = pemJoined.substring(pemHeader.length, pemJoined.length - pemFooter.length); return Buffer.from(pemContents, 'base64'); } function base64decode(b64) { const joined = b64.toString().split('\n').join(''); return Buffer.from(joined, 'base64'); } function load_jwk(data) { if (typeof data == 'string') { let json = fs.readFileSync(`test/webcrypto/${data}`); return JSON.parse(json); } return data; } njs-0.8.9/test/help000066400000000000000000000007351474132077100141670ustar00rootroot00000000000000#!/bin/sh # Copyright (C) Dmitry Volyntsev # Copyright (C) NGINX, Inc. cat << END test/test262 [options] options: --binary=PATH sets path to tested binary, \ default: "$NJS_TEST_BINARY" --log=PATH sets path to output log, \ default: "$NJS_TEST_LOG" --test-dir=PATH sets directory for test files, \ default: "$NJS_TEST_DIR" --verbose=YES enables verbose output, \ default: "$NJS_TEST_VERBOSE" END njs-0.8.9/test/inputrc000066400000000000000000000002231474132077100147130ustar00rootroot00000000000000set bell-style audible set print-completions-horizontally on set completion-display-width 0 set completion-ignore-case on set page-completions off njs-0.8.9/test/js/000077500000000000000000000000001474132077100137235ustar00rootroot00000000000000njs-0.8.9/test/js/async_add.t.js000066400000000000000000000003361474132077100164520ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ async function af(x) { const y = await new Promise(resolve => {resolve(x + 10)}); return x + y; } af(50).then(v => { assert.sameValue(v, 110); }) .then($DONE, $DONE); njs-0.8.9/test/js/async_bind.t.js000066400000000000000000000003441474132077100166350ustar00rootroot00000000000000/*--- includes: [compareArray.js] flags: [async] ---*/ async function f(a1, a2, a3) { var v = await a1; return [a1, a2, a3]; } f.bind(null,1,2)('a') .then(v => assert.compareArray(v, [1, 2, 'a'])) .then($DONE, $DONE); njs-0.8.9/test/js/async_blank.t.js000066400000000000000000000002361474132077100170100ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ async function af(x) { return x; } af(12345).then(v => { assert.sameValue(v, 12345) }) .then($DONE, $DONE); njs-0.8.9/test/js/async_catch.t.js000066400000000000000000000003371474132077100170050ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ async function add(x) { return await new Promise((resolve, reject) => {reject(x)}).catch(v => v + 1); } add(50).then(v => { assert.sameValue(v, 51); }) .then($DONE, $DONE); njs-0.8.9/test/js/async_exception_in_await.t.js000066400000000000000000000004241474132077100215710ustar00rootroot00000000000000/*--- includes: [] flags: [] ---*/ var p = new Promise(() => 0); Object.defineProperty(p, "constructor", {get: () => ({}).a.a}); async function g() { try { await p; } catch (e) { } } function f() { g(); return 42; } assert.sameValue(f(), 42); njs-0.8.9/test/js/async_finally.t.js000066400000000000000000000005311474132077100173550ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ let called = false; async function add(x) { return await new Promise((resolve, reject) => {reject(x + 1)}) .finally(() => {called = true}); } add(50).catch(e => { assert.sameValue(e, 51); assert.sameValue(called, true, "finally was not invoked"); }).then($DONE, $DONE); njs-0.8.9/test/js/async_for.t.js000066400000000000000000000007561474132077100165160ustar00rootroot00000000000000/*--- includes: [compareArray.js] flags: [async] ---*/ let stage = []; async function f() { let sum = 0; stage.push(2); for (let x = 4; x < 14; x++) { sum += await new Promise((resolve, reject) => {resolve(x)}); stage.push(x); } stage.push("end"); return sum; } stage.push(1); f().then(v => { assert.sameValue(v, 85); assert.compareArray(stage, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, "end"]); }).then($DONE, $DONE); stage.push(3); njs-0.8.9/test/js/async_inline.t.js000066400000000000000000000004301474132077100171730ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ function pr(x) { return new Promise(resolve => {resolve(x)}); } async function add() { const a = pr(20); const b = pr(50); return await a + await b; } add().then(v => { assert.sameValue(v, 70); }).then($DONE, $DONE); njs-0.8.9/test/js/async_many_call.t.js000066400000000000000000000014551474132077100176640ustar00rootroot00000000000000/*--- includes: [compareArray.js] flags: [async] ---*/ async function test(name) { let k1, k2; switch (name) { case "First": k1 = await Promise.resolve("SUN"); k2 = await Promise.resolve("MOON"); break; case "Second": k1 = await Promise.resolve("CAT"); k2 = await Promise.resolve("MOUSE"); break; case "Third": k1 = await Promise.resolve("MAN"); k2 = await Promise.resolve("WOMAN"); break; default: break; } return `${name}: ${k1} ${k2}`; }; Promise.all(['First', 'Second', 'Third'].map(v => test(v))) .then(results => { assert.compareArray(results, ['First: SUN MOON','Second: CAT MOUSE','Third: MAN WOMAN']); }).then($DONE, $DONE); njs-0.8.9/test/js/async_promise.t.js000066400000000000000000000007051474132077100174000ustar00rootroot00000000000000/*--- includes: [compareArray.js] flags: [async] ---*/ let stages = []; function pr(x) { return new Promise(resolve => {resolve(x)}); } pr(10) .then(async (v) => { stages.push("then before"); let y = await pr(22); stages.push(`then ${v} ${y}`); return v + y; }) .then(v => stages.push(`then2 ${v}`)) .catch(e => $DONOTEVALUATE()) .then(v => assert.compareArray(stages, ['then before', 'then 10 22', 'then2 32'])) .then($DONE, $DONE); njs-0.8.9/test/js/async_recursive_large.t.js000066400000000000000000000005001474132077100210740ustar00rootroot00000000000000/*--- includes: [compareArray.js] flags: [async] ---*/ let stages = []; async function f(v) { if (v == 1000) { return; } stages.push(`f>${v}`); await "X"; await f(v + 1); stages.push(`f<${v}`); } f(0) .then(v => { assert.sameValue(stages.length, 2000); }) .then($DONE, $DONE); njs-0.8.9/test/js/async_recursive_last.t.js000066400000000000000000000005311474132077100207510ustar00rootroot00000000000000/*--- includes: [compareArray.js] flags: [async] ---*/ let stages = []; async function f(v) { if (v == 3) { return; } stages.push(`f>${v}`); f(v + 1); stages.push(`f<${v}`); await "X"; } f(0) .then(v => { assert.compareArray(stages, ['f>0', 'f>1', 'f>2', 'f<2', 'f<1', 'f<0']); }) .then($DONE, $DONE); njs-0.8.9/test/js/async_recursive_mid.t.js000066400000000000000000000005211474132077100205560ustar00rootroot00000000000000/*--- includes: [compareArray.js] flags: [async] ---*/ let stages = []; async function f(v) { if (v == 1000) { return; } stages.push(`f>${v}`); await "X"; f(v + 1); stages.push(`f<${v}`); } f(0) .then(v => { assert.compareArray(stages, ['f>0','f>1','f<0','f>2','f<1']); }) .then($DONE, $DONE); njs-0.8.9/test/js/async_reject.t.js000066400000000000000000000003541474132077100171760ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ async function add(x) { return await new Promise((resolve, reject) => {reject(x)}); } add(50) .then(v => $DONOTEVALUATE()) .catch(v => { assert.sameValue(v, 50); }) .then($DONE, $DONE); njs-0.8.9/test/js/async_stages.t.js000066400000000000000000000007621474132077100172130ustar00rootroot00000000000000/*--- includes: [compareArray.js] flags: [async] ---*/ function pr(x) { return new Promise(resolve => {resolve(x)}) .then(v => v).then(v => v); } let stage = []; async function f() { let sum = 0; stage.push(2); const a1 = await pr(10); stage.push(4); const a2 = await pr(20); stage.push(5); return a1 + a2; } stage.push(1); f().then(v => { stage.push(v); assert.compareArray(stage, [1, 2, 3, 4, 5, 30]); }).then($DONE, $DONE); stage.push(3); njs-0.8.9/test/js/async_throw.t.js000066400000000000000000000005171474132077100170660ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ function pr(x) { return new Promise(resolve => {resolve(x)}).then(v => {throw v}); } async function add(x) { const a = await pr(x); const b = await pr(x); return a + b; } add(50) .then(v => $DONOTEVALUATE()) .catch(v => { assert.sameValue(v, 50); }) .then($DONE, $DONE); njs-0.8.9/test/js/async_throw_async.t.js000066400000000000000000000005221474132077100202570ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ function pr(x) { return new Promise(resolve => {resolve(x)}); } async function add(x) { const a = await pr(x); throw a + 1; const b = await pr(x + 10); return a + b; } add(50) .then(v => $DONOTEVALUATE()) .catch(v => { assert.sameValue(v, 51); }) .then($DONE, $DONE); njs-0.8.9/test/js/async_throw_catch.t.js000066400000000000000000000005041474132077100202240ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ function pr(x) { return new Promise(resolve => {resolve(x)}).then(v => {throw v}).catch(v => v); } async function add(x) { const a = await pr(x); const b = await pr(x + 10); return a + b; } add(50).then(v => { assert.sameValue(v, 110); }) .then($DONE, $DONE); njs-0.8.9/test/js/async_throw_catch_async.t.js000066400000000000000000000005221474132077100214210ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ function pr(x) { return new Promise(resolve => {resolve(x)}); } async function add(x) { const a = await pr(x); throw a + 1; const b = await pr(x + 10); return a + b; } add(50) .then(v => $DONOTEVALUATE()) .catch(v => { assert.sameValue(v, 51); }) .then($DONE, $DONE); njs-0.8.9/test/js/async_throw_in_try_after_await.t.js000066400000000000000000000006151474132077100230170ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ function pr(x) { return new Promise(resolve => {resolve(x)}); } async function add(x) { try { const a = await pr(x); throw 'Oops'; return a + b; } catch (e) { return `catch: ${e.toString()}`; } } add(50) .then( v => assert.sameValue(v, 'catch: Oops'), v => $DONOTEVALUATE(), ).then($DONE, $DONE); njs-0.8.9/test/js/async_try_catch.t.js000066400000000000000000000007371474132077100177070ustar00rootroot00000000000000/*--- includes: [compareArray.js] flags: [async] ---*/ let stages = []; async function af() { try { await new Promise(function(resolve, reject) { reject("reject"); }); $DONOTEVALUATE(); } catch (v) { stages.push(v); } finally { stages.push('finally'); } return "end"; }; af().then(v => { stages.push(v); assert.compareArray(stages, ['reject', 'finally', 'end']); }) .then($DONE, $DONE) njs-0.8.9/test/js/async_try_catch_call.t.js000066400000000000000000000007351474132077100207000ustar00rootroot00000000000000/*--- includes: [compareArray.js] flags: [async] ---*/ let stages = []; const fn = async () => { throw new Error('Oops') }; async function af() { try { await fn(); $DONOTEVALUATE(); } catch (v) { stages.push(`catch:${v}`); } finally { stages.push('finally'); } return "end"; }; af().then(v => { stages.push(v); assert.compareArray(stages, ['catch:Error: Oops', 'finally', 'end']); }) .then($DONE, $DONE) njs-0.8.9/test/js/async_try_catch_expression.t.js000066400000000000000000000006361474132077100221640ustar00rootroot00000000000000/*--- includes: [compareArray.js] flags: [async] ---*/ let stages = []; async function af() { try { await ({}).a.a(); $DONOTEVALUATE(); } catch (v) { stages.push('catch'); } finally { stages.push('finally'); } return "end"; }; af().then(v => { stages.push(v); assert.compareArray(stages, ['catch', 'finally', 'end']); }) .then($DONE, $DONE) njs-0.8.9/test/js/async_try_finally.t.js000066400000000000000000000010171474132077100202530ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ let stages = []; async function af() { try { await new Promise(function(resolve, reject) { reject("reject"); }); $DONOTEVALUATE(); } finally { await new Promise(function(resolve, reject) { reject("finally reject"); }); $DONOTEVALUATE(); } return "shouldn't happen: end"; }; af() .then(v => $DONOTEVALUATE()) .catch(v => { assert.sameValue(v, "finally reject"); }) .then($DONE, $DONE); njs-0.8.9/test/js/async_try_resolve.t.js000066400000000000000000000004751474132077100203030ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ async function af() { let key; try { key = await Promise.resolve("key"); key += ": resolve"; } catch (e) { key += ": exception"; } return key; }; af().then(v => { assert.sameValue(v, "key: resolve"); }) .then($DONE, $DONE); njs-0.8.9/test/js/async_try_throw.t.js000066400000000000000000000006151474132077100177630ustar00rootroot00000000000000/*--- includes: [compareArray.js] flags: [async] ---*/ let stages = []; async function af() { try { throw "try"; $DONOTEVALUATE(); } finally { stages.push("finally"); } return "shouldn't happen: end"; }; af() .then(v => $DONOTEVALUATE()) .catch(v => { stages.push(v); assert.compareArray(stages, ['finally', 'try']); }) .then($DONE, $DONE) njs-0.8.9/test/js/async_try_throw_catch.t.js000066400000000000000000000006131474132077100211230ustar00rootroot00000000000000/*--- includes: [compareArray.js] flags: [async] ---*/ let stage = []; async function af() { try { throw "try"; $DONOTEVALUATE(); } catch (v) { stage.push(v); } finally { stage.push("finally"); } return "end"; }; af().then(v => { stage.push(v); assert.compareArray(stage, ['try', 'finally', 'end']) }).then($DONE, $DONE); njs-0.8.9/test/js/import_as_default.t.js000066400000000000000000000001641474132077100202250ustar00rootroot00000000000000/*--- includes: [] flags: [] paths: [test/js/module/] ---*/ import imp from 'lib4.js'; assert.sameValue(imp, 10); njs-0.8.9/test/js/import_as_default_compare.t.js000066400000000000000000000003361474132077100217340ustar00rootroot00000000000000/*--- includes: [] flags: [] paths: [test/js/module] ---*/ import a from 'lib6.js'; import b from 'lib6-1.js'; assert.sameValue(a.a, 1); assert.sameValue(a.b, 2); assert.sameValue(a.a, b.a); assert.sameValue(a.b, b.b); njs-0.8.9/test/js/import_as_default_double.t.js000066400000000000000000000001601474132077100215530ustar00rootroot00000000000000/*--- includes: [] flags: [] paths: [test/js/module] negative: phase: runtime ---*/ import m from 'lib5.js'; njs-0.8.9/test/js/import_chain.t.js000066400000000000000000000002521474132077100171760ustar00rootroot00000000000000/*--- includes: [] flags: [] paths: [test/js/module/, test/js/module/libs/, test/js/module/sub] ---*/ import lib2 from 'lib2.js'; assert.sameValue(lib2.hash(), "XXX"); njs-0.8.9/test/js/import_comma_expression.t.js000066400000000000000000000002141474132077100214650ustar00rootroot00000000000000/*--- includes: [] flags: [] paths: [test/js/module] ---*/ import m from 'export_comma_expression.js'; assert.sameValue(m.prod(3,5), 15); njs-0.8.9/test/js/import_cyclic.t.js000066400000000000000000000002521474132077100173620ustar00rootroot00000000000000/*--- includes: [] flags: [] paths: [test/js/module/] ---*/ import a from 'cyclic_a.js'; import b from 'cyclic_b.js'; assert.sameValue(a, 10); assert.sameValue(b, 11); njs-0.8.9/test/js/import_declaration_exception.t.js000066400000000000000000000002261474132077100224600ustar00rootroot00000000000000/*--- includes: [] flags: [] paths: [test/js/module, test/js/module/libs] negative: phase: runtime ---*/ import m from 'declaration_exception.js'; njs-0.8.9/test/js/import_empty.t.js000066400000000000000000000002061474132077100172510ustar00rootroot00000000000000/*--- includes: [] flags: [] paths: [test/js/module, test/js/module/libs] negative: phase: runtime ---*/ import m from 'empty.js'; njs-0.8.9/test/js/import_exception.t.js000066400000000000000000000002311474132077100201070ustar00rootroot00000000000000/*--- includes: [] flags: [] paths: [test/js/module, test/js/module/libs] negative: phase: runtime ---*/ import lib from 'lib3.js'; lib.exception(); njs-0.8.9/test/js/import_expression.t.js000066400000000000000000000002521474132077100203130ustar00rootroot00000000000000/*--- includes: [] flags: [] paths: [test/js/module] ---*/ import m from 'export_expression.js'; assert.sameValue(typeof m, 'object'); assert.sameValue(m.sum(3,4), 7); njs-0.8.9/test/js/import_function_expression.t.js000066400000000000000000000001451474132077100222210ustar00rootroot00000000000000/*--- includes: [] flags: [] paths: [test/js/module/] ---*/ import _ from 'function_expression.js'; njs-0.8.9/test/js/import_global_ref.t.js000066400000000000000000000003161474132077100202110ustar00rootroot00000000000000/*--- includes: [] flags: [] paths: [test/js/module/] ---*/ import http from 'http.js'; import jwt from 'jwt.js'; globalThis.http = http; globalThis.jwt = jwt; assert.sameValue(http.check(), "JWT-OK"); njs-0.8.9/test/js/import_global_ref_var.t.js000066400000000000000000000002221474132077100210550ustar00rootroot00000000000000/*--- includes: [] flags: [] paths: [test/js/module/] ---*/ globalThis.a = 42; import m from 'export_global_a.js'; assert.sameValue(m.f(), 42); njs-0.8.9/test/js/import_loading_exception.t.js000066400000000000000000000002221474132077100216040ustar00rootroot00000000000000/*--- includes: [] flags: [] paths: [test/js/module, test/js/module/libs] negative: phase: runtime ---*/ import m from 'loading_exception.js'; njs-0.8.9/test/js/import_multi_default.t.js000066400000000000000000000002071474132077100207520ustar00rootroot00000000000000/*--- includes: [] flags: [] paths: [test/js/module, test/js/module/libs] negative: phase: runtime ---*/ import m from 'export.js'; njs-0.8.9/test/js/import_native_module_exception.t.js000066400000000000000000000002661474132077100230320ustar00rootroot00000000000000/*--- includes: [] flags: [] paths: [test/js/module, test/js/module/libs] negative: phase: runtime ---*/ import fs from 'fs'; import lib from 'lib3.js'; fs.readFileSync({}.a.a); njs-0.8.9/test/js/import_non_assignment.t.js000066400000000000000000000002261474132077100211370ustar00rootroot00000000000000/*--- includes: [] flags: [] paths: [test/js/module, test/js/module/libs] negative: phase: runtime ---*/ import m from 'export_non_assignment.js'; njs-0.8.9/test/js/import_non_default.t.js000066400000000000000000000002231474132077100204100ustar00rootroot00000000000000/*--- includes: [] flags: [] paths: [test/js/module, test/js/module/libs] negative: phase: runtime ---*/ import m from 'export_non_default.js'; njs-0.8.9/test/js/import_not_enough.t.js000066400000000000000000000001471474132077100202640ustar00rootroot00000000000000/*--- includes: [] flags: [] paths: [] negative: phase: runtime ---*/ import name from 'name.js'; njs-0.8.9/test/js/import_not_found.t.js000066400000000000000000000001561474132077100201120ustar00rootroot00000000000000/*--- includes: [] flags: [] njs_cmd_args: [] negative: phase: runtime ---*/ import name from 'name.js'; njs-0.8.9/test/js/import_object.t.js000066400000000000000000000002001474132077100173530ustar00rootroot00000000000000/*--- includes: [] flags: [] paths: [test/js/module] ---*/ import m from 'export_name.js'; assert.sameValue(m.prod(3,4), 12); njs-0.8.9/test/js/import_order.t.js000066400000000000000000000005171474132077100172330ustar00rootroot00000000000000/*--- includes: [compareArray.js] flags: [] paths: [test/js/module/] ---*/ if (!globalThis.stages) { globalThis.stages = []; } globalThis.stages.push('main1'); import _ from 'order.js'; import __ from 'order2.js'; globalThis.stages.push('main2'); assert.compareArray(globalThis.stages, ["order", "order2", "main1", "main2"]); njs-0.8.9/test/js/import_recursive.t.js000066400000000000000000000001651474132077100201260ustar00rootroot00000000000000/*--- includes: [] flags: [] paths: [test/js/module/] ---*/ import m from 'recursive.js'; assert.sameValue(m, 42); njs-0.8.9/test/js/import_recursive_early_access.t.js000066400000000000000000000002031474132077100226340ustar00rootroot00000000000000/*--- includes: [] flags: [] paths: [test/js/module/] negative: phase: runtime ---*/ import _ from 'recursive_early_access.js'; njs-0.8.9/test/js/import_recursive_relative.t.js000066400000000000000000000002001474132077100220070ustar00rootroot00000000000000/*--- includes: [] flags: [] paths: [test/js/module/] ---*/ import m from './recursive_relative.js'; assert.sameValue(m, 42); njs-0.8.9/test/js/import_ref_exception.t.js000066400000000000000000000002161474132077100207460ustar00rootroot00000000000000/*--- includes: [] flags: [] paths: [test/js/module, test/js/module/libs] negative: phase: runtime ---*/ import m from 'ref_exception.js'; njs-0.8.9/test/js/import_relative_path.t.js000066400000000000000000000002111474132077100207360ustar00rootroot00000000000000/*--- includes: [] flags: [] paths: [test/js/module/] ---*/ import hash from 'libs/hash.js'; assert.sameValue(hash.name, "libs.name"); njs-0.8.9/test/js/import_return.t.js000066400000000000000000000002061474132077100174320ustar00rootroot00000000000000/*--- includes: [] flags: [] paths: [test/js/module, test/js/module/libs] negative: phase: runtime ---*/ import m from 'return.js' njs-0.8.9/test/js/import_scalar.t.js000066400000000000000000000001721474132077100173620ustar00rootroot00000000000000/*--- includes: [] flags: [] paths: [test/js/module/] ---*/ import name from 'name.js'; assert.sameValue(name, "name"); njs-0.8.9/test/js/import_singleton.t.js000066400000000000000000000004461474132077100201230ustar00rootroot00000000000000/*--- includes: [] flags: [] paths: [test/js/module/, test/js/module/libs/] ---*/ import lib1 from 'lib1.js'; import lib1_2 from 'lib1.js'; assert.sameValue(lib1.get(), 0); assert.sameValue(lib1_2.get(), 0); lib1.inc(); assert.sameValue(lib1.get(), 1); assert.sameValue(lib1_2.get(), 1); njs-0.8.9/test/js/import_sinking_export_default.t.js000066400000000000000000000002511474132077100226620ustar00rootroot00000000000000/*--- includes: [] flags: [] paths: [test/js/module] ---*/ import m from 'sinking_export_default.js'; assert.sameValue(typeof m, 'object'); assert.sameValue(m.a, 42); njs-0.8.9/test/js/import_very_long_path.t.js000066400000000000000000000101361474132077100211360ustar00rootroot00000000000000/*--- : [] paths: [test/js/module/] negative: phase: runtime ---*/ import name from 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'; njs-0.8.9/test/js/module/000077500000000000000000000000001474132077100152105ustar00rootroot00000000000000njs-0.8.9/test/js/module/cyclic_a.js000066400000000000000000000000601474132077100173100ustar00rootroot00000000000000import _ from 'cyclic_b.js'; export default 10; njs-0.8.9/test/js/module/cyclic_b.js000066400000000000000000000000601474132077100173110ustar00rootroot00000000000000import _ from 'cyclic_a.js'; export default 11; njs-0.8.9/test/js/module/declaration_exception.js000066400000000000000000000001231474132077100221050ustar00rootroot00000000000000 function f() { return 1; } function f() { return 2; } export default f; njs-0.8.9/test/js/module/empty.js000066400000000000000000000000001474132077100166720ustar00rootroot00000000000000njs-0.8.9/test/js/module/exception.js000066400000000000000000000000561474132077100175450ustar00rootroot00000000000000import lib from 'lib3.js'; lib.exception(); njs-0.8.9/test/js/module/export.js000066400000000000000000000000621474132077100170650ustar00rootroot00000000000000var a = 1; export default {a} export default {a} njs-0.8.9/test/js/module/export_comma_expression.js000066400000000000000000000002561474132077100225250ustar00rootroot00000000000000var _export = {}; export default (_export.sum = function(a, b) { return a + b; }, _export.prod = function(a, b) { return a * b; }, _export); njs-0.8.9/test/js/module/export_expression.js000066400000000000000000000003131474132077100213430ustar00rootroot00000000000000function gen_export() { var _export = {}; _export.sum = function(a, b) { return a + b; } _export.prod = function(a, b) { return a * b; } return _export; } export default gen_export(); njs-0.8.9/test/js/module/export_global_a.js000066400000000000000000000000771474132077100207130ustar00rootroot00000000000000function f() { return globalThis.a; } export default {f}; njs-0.8.9/test/js/module/export_name.js000066400000000000000000000002131474132077100200630ustar00rootroot00000000000000var _export = {}; _export.sum = function(a, b) { return a + b; } _export.prod = function(a, b) { return a * b; } export default _export; njs-0.8.9/test/js/module/export_non_assignment.js000066400000000000000000000000271474132077100221700ustar00rootroot00000000000000export default 10, 11; njs-0.8.9/test/js/module/export_non_default.js000066400000000000000000000000311474132077100214370ustar00rootroot00000000000000var a = 1; export a {a} njs-0.8.9/test/js/module/function_expression.js000066400000000000000000000001361474132077100216520ustar00rootroot00000000000000var foo = (function(){ return (function f() {}) }); foo()({1:[]}) export default {foo}; njs-0.8.9/test/js/module/http.js000066400000000000000000000001331474132077100165220ustar00rootroot00000000000000var http = {}; http.check = function() { return jwt.verify(); } export default http; njs-0.8.9/test/js/module/jwt.js000066400000000000000000000001251474132077100163500ustar00rootroot00000000000000var jwt = {}; jwt.verify = function() { return "JWT-OK"; } export default jwt; njs-0.8.9/test/js/module/lib1.js000066400000000000000000000003661474132077100164020ustar00rootroot00000000000000var state = {count:0} function inc() { state.count++; } function get() { return state.count; } if (globalThis.lib1_is_loaded) { throw Error("lib1 already loaded"); globalThis.lib1_is_loaded = true; } export default {inc, get} njs-0.8.9/test/js/module/lib2.js000066400000000000000000000001411474132077100163720ustar00rootroot00000000000000import lib3 from 'lib3.js'; function hash() { return lib3.hash(); } export default {hash}; njs-0.8.9/test/js/module/lib3.js000066400000000000000000000002401474132077100163730ustar00rootroot00000000000000function hash() { return sub.hash(); } function exception() { return sub.error(); } import sub from 'sub/sub1.js'; export default {hash, exception}; njs-0.8.9/test/js/module/lib4.js000066400000000000000000000000551474132077100164000ustar00rootroot00000000000000var var10 = 10; export { var10 as default }; njs-0.8.9/test/js/module/lib5.js000066400000000000000000000001221474132077100163740ustar00rootroot00000000000000var uu = 10; var uu1 = 11; export { uu as default }; export { uu1 as default }; njs-0.8.9/test/js/module/lib6-1.js000066400000000000000000000000601474132077100165340ustar00rootroot00000000000000var obj = { a:1, b:2 } export default obj; njs-0.8.9/test/js/module/lib6.js000066400000000000000000000000651474132077100164030ustar00rootroot00000000000000var obj = { a:1, b:2 } export {obj as default}; njs-0.8.9/test/js/module/libs/000077500000000000000000000000001474132077100161415ustar00rootroot00000000000000njs-0.8.9/test/js/module/libs/hash.js000066400000000000000000000002001474132077100174120ustar00rootroot00000000000000function hash() { return "XXX"; } import sub from 'sub/sub3.js'; import name from 'name.js'; export default {hash, name}; njs-0.8.9/test/js/module/libs/name.js000066400000000000000000000000341474132077100174140ustar00rootroot00000000000000export default 'libs.name'; njs-0.8.9/test/js/module/loading_exception.js000066400000000000000000000000661474132077100212430ustar00rootroot00000000000000throw Error('loading exception'); export default {}; njs-0.8.9/test/js/module/name.js000066400000000000000000000000271474132077100164650ustar00rootroot00000000000000export default 'name'; njs-0.8.9/test/js/module/order.js000066400000000000000000000001551474132077100166620ustar00rootroot00000000000000if (!globalThis.stages) { globalThis.stages = []; } globalThis.stages.push('order'); export default 1; njs-0.8.9/test/js/module/order2.js000066400000000000000000000001561474132077100167450ustar00rootroot00000000000000if (!globalThis.stages) { globalThis.stages = []; } globalThis.stages.push('order2'); export default 1; njs-0.8.9/test/js/module/recursive.js000066400000000000000000000000611474132077100175520ustar00rootroot00000000000000import _ from 'recursive.js'; export default 42; njs-0.8.9/test/js/module/recursive_early_access.js000066400000000000000000000001051474132077100222660ustar00rootroot00000000000000import m from 'recursive_early_access.js'; m.a; export default 42; njs-0.8.9/test/js/module/recursive_relative.js000066400000000000000000000000741474132077100214510ustar00rootroot00000000000000import _ from './recursive_relative.js'; export default 42; njs-0.8.9/test/js/module/ref_exception.js000066400000000000000000000000651474132077100204010ustar00rootroot00000000000000export default {type:typeof undeclared, undeclared}; njs-0.8.9/test/js/module/return.js000066400000000000000000000000121474132077100170560ustar00rootroot00000000000000return 1; njs-0.8.9/test/js/module/sinking_export_default.js000066400000000000000000000000521474132077100223120ustar00rootroot00000000000000let o = {}; export default o; o.a = 42; njs-0.8.9/test/js/module/sub/000077500000000000000000000000001474132077100160015ustar00rootroot00000000000000njs-0.8.9/test/js/module/sub/sub1.js000066400000000000000000000002211474132077100172040ustar00rootroot00000000000000function hash() { return sub2.hash(); } function error() { return {}.a.a; } import sub2 from 'sub2.js'; export default {hash, error}; njs-0.8.9/test/js/module/sub/sub2.js000066400000000000000000000001551474132077100172130ustar00rootroot00000000000000function hash(crypto) { return hashlib.hash(); } import hashlib from 'hash.js'; export default {hash}; njs-0.8.9/test/js/module/sub/sub3.js000066400000000000000000000000411474132077100172060ustar00rootroot00000000000000export default { name: "SUB3" }; njs-0.8.9/test/js/promise_all.t.js000066400000000000000000000006131474132077100170310ustar00rootroot00000000000000/*--- includes: [compareArray.js] flags: [async] ---*/ function resolve(value) { return new Promise(resolve => setTimeout(() => resolve(value), 0)); } Promise.all([resolve(['one', 'two']), resolve(['three', 'four'])]) .then( v => { assert.compareArray(v[0], ['one', 'two']); assert.compareArray(v[1], ['three', 'four']); }, v => $DONOTEVALUATE(), ) .then($DONE, $DONE); njs-0.8.9/test/js/promise_allSettled.t.js000066400000000000000000000020011474132077100203470ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ var p0 = Promise.resolve(2).then(v => v + 1); var p1 = Promise.reject(21).catch(v => v * 2); var p2 = Promise.resolve('nope').then(() => { throw 'foo' }); var p3 = Promise.reject('yes').then(() => { throw 'nope'; }); var p4 = Promise.resolve('here').finally(() => 'nope'); var p5 = Promise.reject('here too').finally(() => 'nope'); var p6 = Promise.resolve('nope').finally(() => { throw 'finally'; }); var p7 = Promise.reject('nope').finally(() => { throw 'finally after rejected'; }); var p8 = Promise.reject(1).then(() => 'nope', () => 0); function dump(v) { var fulfilled = v.filter(v=>v.status == 'fulfilled').map(v=>v.value).sort(); var rejected = v.filter(v=>v.status == 'rejected').map(v=>v.reason).sort(); return `F:${fulfilled}|R:${rejected}` } Promise.allSettled([p0, p1, p2, p3, p4, p5, p6, p7, p8]).then( v => assert.sameValue(dump(v), "F:0,3,42,here|R:finally,finally after rejected,foo,here too,yes"), v => $DONOTEVALUATE(), ) .then($DONE, $DONE); njs-0.8.9/test/js/promise_allSettled_string.t.js000066400000000000000000000006171474132077100217500ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ function dump(v) { var fulfilled = v.filter(v=>v.status == 'fulfilled').map(v=>v.value).sort(); var rejected = v.filter(v=>v.status == 'rejected').map(v=>v.reason).sort(); return `F:${fulfilled}|R:${rejected}`; } Promise.allSettled("abc").then( v => assert.sameValue(dump(v), "F:a,b,c|R:"), v => $DONOTEVALUATE(), ) .then($DONE, $DONE); njs-0.8.9/test/js/promise_all_throw.t.js000066400000000000000000000005601474132077100202550ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ var p0 = Promise.resolve(1).then(v => v + 1); var p1 = Promise.reject(2).catch(v => v * 2); var p2 = Promise.resolve().then(() => { throw 'foo' }); var p3 = Promise.reject().then(() => { throw 'oof'; }); Promise.all([p0, p1, p2, p3]).then( v => $DONOTEVALUATE(), v => assert.sameValue(v, 'foo') ) .then($DONE, $DONE); njs-0.8.9/test/js/promise_any.t.js000066400000000000000000000004611474132077100170510ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ var p0 = Promise.resolve().then(() => { throw 'foo' }); var p1 = Promise.reject(2).catch(v => v * 2); var p2 = Promise.resolve(1).then(v => v + 1); Promise.any([p0, p1, p2]).then( v => assert.sameValue(v, 4), v => $DONOTEVALUATE(), ) .then($DONE, $DONE); njs-0.8.9/test/js/promise_any_all_rejected.t.js000066400000000000000000000003521474132077100215450ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ var p0 = Promise.reject(1); var p1 = Promise.reject(2); Promise.any([p0, p1]).then( v => $DONOTEVALUATE(), v => assert.sameValue(v.constructor, AggregateError), ) .then($DONE, $DONE); njs-0.8.9/test/js/promise_catch_then_throw_catch.t.js000066400000000000000000000003611474132077100227460ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ let called = false; Promise.resolve() .then(() => {}) .catch(() => {}) .then(() => {nonExsisting()}) .catch(() => { called = true; }) .then(() => assert.sameValue(called, true)) .then($DONE, $DONE); njs-0.8.9/test/js/promise_catch_throw.t.js000066400000000000000000000003211474132077100205620ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ Promise.resolve() .then(() => {nonExsisting()}) .catch(() => {nonExsistingInCatch()}) .catch(v => assert.sameValue(v.constructor, ReferenceError)) .then($DONE, $DONE); njs-0.8.9/test/js/promise_finally.t.js000066400000000000000000000004261474132077100177210ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ Promise.resolve('here') .finally(() => {'nope'}) .then(v => assert.sameValue(v, 'here')); Promise.resolve('here') .finally(() => {throw 'nope'}) .then(v => $DONOTEVALUATE()) .catch(v => assert.sameValue(v, 'nope')) .then($DONE, $DONE); njs-0.8.9/test/js/promise_finally_throw.t.js000066400000000000000000000001741474132077100211440ustar00rootroot00000000000000/*--- includes: [] flags: [] negative: phase: runtime ---*/ Promise.resolve() .finally(() => {nonExsistingInFinally()}); njs-0.8.9/test/js/promise_finally_throw_catch.t.js000066400000000000000000000002121474132077100222770ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ Promise.resolve() .finally(() => {nonExsistingInFinally()}) .catch(e => {}) .then($DONE, $DONE); njs-0.8.9/test/js/promise_prototype_reject_type_confusion.t.js000066400000000000000000000003171474132077100250070ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ Symbol.__proto__ = new Promise(()=>{}); Promise.reject(Symbol) .then(v => $DONOTEVALUATE()) .catch(err => assert.sameValue(err.name, 'Symbol')) .then($DONE, $DONE); njs-0.8.9/test/js/promise_prototype_then_type_confusion.t.js000066400000000000000000000003231474132077100244660ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ Symbol.__proto__ = new Promise(()=>{}); Promise.resolve(Symbol) .then(v => $DONOTEVALUATE()) .catch(err => assert.sameValue(err.name, 'TypeError')) .then($DONE, $DONE); njs-0.8.9/test/js/promise_race.t.js000066400000000000000000000005051474132077100171730ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ var p1 = new Promise((resolve, reject) => { setTimeout(resolve, 0, 'one'); }); var p2 = new Promise((resolve, reject) => { setTimeout(resolve, 0, 'two'); }); Promise.race([p1, p2]).then( v => assert.sameValue(v, 'one'), v => $DONOTEVALUATE(), ) .then($DONE, $DONE); njs-0.8.9/test/js/promise_race_throw.t.js000066400000000000000000000004631474132077100204210ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ var p1 = new Promise((resolve, reject) => { throw 'one'; }); var p2 = new Promise((resolve, reject) => { setTimeout(resolve, 0, 'two'); }); Promise.race([p1, p2]).then( v => $DONOTEVALUATE(), v => assert.sameValue(v, 'one'), ) .then($DONE, $DONE); njs-0.8.9/test/js/promise_reject_catch.t.js000066400000000000000000000001761474132077100207030ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ Promise.reject("test") .catch(v => assert.sameValue(v, "test")) .then($DONE, $DONE); njs-0.8.9/test/js/promise_rejection_tracker.t.js000066400000000000000000000001231474132077100217520ustar00rootroot00000000000000/*--- includes: [] flags: [] negative: phase: runtime ---*/ Promise.reject(1); njs-0.8.9/test/js/promise_rejection_tracker_recursive.t.js000066400000000000000000000003661474132077100240520ustar00rootroot00000000000000/*--- includes: [] flags: [] negative: phase: runtime ---*/ String.toString = async () => { String.prototype.concat([String, {toString(){ throw String; }}]); throw 1; }; String.valueOf = String; (async function() { throw String; })() njs-0.8.9/test/js/promise_s01.t.js000066400000000000000000000010151474132077100166610ustar00rootroot00000000000000/*--- includes: [compareArray.js] flags: [async] ---*/ let stages = []; var promise = new Promise(function(resolve, reject) { stages.push('One'); reject(123); }).catch((v) => {stages.push(v)}); stages.push('Two'); promise.then(() => {stages.push('Four'); return {num: 'Five'}}) .then(obj => {stages.push(obj.num); return {num: 'Six'}}) .then(obj => {stages.push(obj.num)}) .then(() => assert.compareArray(stages, ["One", "Two", "Three", 123, "Four", "Five", "Six"])) .then($DONE, $DONE); stages.push('Three'); njs-0.8.9/test/js/promise_s02.t.js000066400000000000000000000007671474132077100166770ustar00rootroot00000000000000/*--- includes: [compareArray.js] flags: [async] ---*/ let stages = []; var promise = new Promise(function(resolve, reject) { stages.push('One'); reject(123); }) stages.push('Two'); promise.then(() => {stages.push('Four'); return {num: 'Five'}}) .then(obj => {stages.push(obj.num); return {num: 'Six'}}) .then(obj => {stages.push(obj.num)}) .catch(v => assert.sameValue(v, 123)) .then(() => assert.compareArray(stages, ["One", "Two", "Three"])) .then($DONE, $DONE); stages.push('Three'); njs-0.8.9/test/js/promise_s03.t.js000066400000000000000000000005731474132077100166730ustar00rootroot00000000000000/*--- includes: [compareArray.js] flags: [async] ---*/ let stages = []; var promise = new Promise(function(resolve, reject) { stages.push('One'); reject(42); }); stages.push('Two'); promise.then( v => $DONOTEVALUATE(), v => assert.sameValue(v, 42)) .then(() => assert.compareArray(stages, ['One', 'Two', 'Three'])) .then($DONE, $DONE); stages.push('Three'); njs-0.8.9/test/js/promise_s04.t.js000066400000000000000000000002521474132077100166660ustar00rootroot00000000000000/*--- includes: [] flags: [] negative: phase: runtime ---*/ Promise.reject(new Error('Oh my')) .then( function(success) {}, function(error) { throw error;} ); njs-0.8.9/test/js/promise_s05.t.js000066400000000000000000000004631474132077100166730ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ let called = false; Promise.resolve('Success') .then( function(value) { assert.sameValue(value, 'Success'); called = true; }, function(value) { $DONOTEVALUATE() }) .then(() => assert.sameValue(called, true)) .then($DONE, $DONE); njs-0.8.9/test/js/promise_s06.t.js000066400000000000000000000002271474132077100166720ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ var p = Promise.resolve([1,2,3]); p.then(function(v) { assert.sameValue(v[0], 1); }) .then($DONE, $DONE); njs-0.8.9/test/js/promise_s07.t.js000066400000000000000000000005071474132077100166740ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ var p1 = Promise.resolve({ then: function(onFulfill, onReject) { onFulfill('fulfilled!'); } }); p1.then( function(v) { assert.sameValue(v, 'fulfilled!'); }, function(e) { $DONOTEVALUATE() }, ) .then(() => assert.sameValue(p1 instanceof Promise, true)) .then($DONE, $DONE); njs-0.8.9/test/js/promise_s08.t.js000066400000000000000000000005271474132077100166770ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ let called = false; var thenable = { then: function(resolve) { called = true; resolve(); } }; function executor(resolve, reject) { resolve(thenable); throw new Error('ignored'); } new Promise(executor) .then(() => assert.sameValue(called, true)) .then($DONE, $DONE); njs-0.8.9/test/js/promise_s09.t.js000066400000000000000000000004561474132077100167010ustar00rootroot00000000000000/*--- includes: [compareArray.js] flags: [] ---*/ let calls = []; function NotPromise(executor) { assert.sameValue(Object.isExtensible(executor), true); executor(function() {calls.push('S')}, function() {calls.push('R')}); } Promise.resolve.call(NotPromise); assert.compareArray(calls, ['S']); njs-0.8.9/test/js/promise_s10.t.js000066400000000000000000000006111474132077100166620ustar00rootroot00000000000000/*--- includes: [compareArray.js] flags: [async] ---*/ let stages = []; var promise = new Promise(function(resolve, reject) { stages.push('One'); reject('Oh no'); }); stages.push('Two'); promise.then(() => {stages.push('Three')}) .catch((v) => {stages.push(v)}) .then(() => assert.compareArray(stages, ['One', 'Two', 'Three', 'Oh no'])) .then($DONE, $DONE); stages.push('Three'); njs-0.8.9/test/js/promise_s11.t.js000066400000000000000000000006571474132077100166750ustar00rootroot00000000000000/*--- includes: [compareArray.js] flags: [async] ---*/ let stages = []; var promise = new Promise((resolve, reject) => resolve('all')); promise.then( function f1(result) { stages.push('S: ' + result); return 'f1'; }); promise.then( function f2(result) { stages.push('R: ' + result); return 'f2'; }) .then(() => assert.compareArray(stages, ['end', 'S: all', 'R: all'])) .then($DONE, $DONE); stages.push('end'); njs-0.8.9/test/js/promise_s12.t.js000066400000000000000000000004451474132077100166710ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ let called = false; var thenable = new Promise(function() {}); var p = new Promise(function(resolve) { resolve(); throw thenable; }); p.then(function() { called = true; }) .then(() => assert.sameValue(called, true)) .then($DONE, $DONE); njs-0.8.9/test/js/promise_s13.t.js000066400000000000000000000006641474132077100166750ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ let called = false; var thenable = Promise.resolve(); var p = new Promise(function(a,b) { throw thenable; }); p .then(v => $DONOTEVALUATE()) .then( v => $DONOTEVALUATE(), function(x) { assert.sameValue(x, thenable, 'The promise should be rejected with the resolution value.'); called = true; } ) .then(() => assert.sameValue(called, true)) .then($DONE, $DONE); njs-0.8.9/test/js/promise_s14.t.js000066400000000000000000000006521474132077100166730ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ let stages = []; var isLoading = true; var promise = new Promise((a, b) => {a()} ); promise.then(function(response) { throw new TypeError('oops'); }) .then(function(json) { }) .catch(e => stages.push(e)) .finally(() => stages.push('Done')) .then(() => { assert.sameValue(stages[0] instanceof TypeError, true); assert.sameValue(stages[1], 'Done'); }) .then($DONE, $DONE); njs-0.8.9/test/js/promise_s15.t.js000066400000000000000000000004641474132077100166750ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ var obj = {}; var p = Promise.resolve(obj); p.then(undefined, undefined) .then( v => { assert.sameValue(v, obj, 'Expected resolution object to be passed through, got ' + v); }, e => $DONOTEVALUATE()) .then($DONE, $DONE); njs-0.8.9/test/js/promise_s16.t.js000066400000000000000000000004561474132077100166770ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ var obj = {}; var p = Promise.reject(obj); p.then(undefined, undefined) .then( v => DONOTEVALUATE(), e => { assert.sameValue(e, obj, 'Expected resolution object to be passed through, got ' + e); }) .then($DONE, $DONE); njs-0.8.9/test/js/promise_s17.t.js000066400000000000000000000005071474132077100166750ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ /* Duplicate of s16. */ var obj = {}; var p = Promise.reject(obj); p.then(undefined, undefined) .then( v => DONOTEVALUATE(), e => { assert.sameValue(e, obj, 'Expected resolution object to be passed through, got ' + e); }) .then($DONE, $DONE); njs-0.8.9/test/js/promise_s18.t.js000066400000000000000000000010761474132077100167000ustar00rootroot00000000000000/*--- includes: [compareArray.js] flags: [async] ---*/ let stages = []; var thenable = { then: function(resolve) { resolve(); stages.push(5); } }; var thenableWithError = { then: function(resolve) { stages.push(3); resolve(thenable); stages.push(4); throw new Error('ignored exception'); } }; function executor(resolve, reject) { stages.push(1); resolve(thenableWithError); stages.push(2); } new Promise(executor) .then(() => assert.compareArray(stages, [1, 2, 3, 4, 5])) .then($DONE, $DONE); njs-0.8.9/test/js/promise_s19.t.js000066400000000000000000000012601474132077100166740ustar00rootroot00000000000000/*--- includes: [compareArray.js] flags: [async] ---*/ let stages = []; var returnValue = null; var value = {}; var resolve; var poisonedThen = Object.defineProperty({}, 'then', { get: function() { stages.push('Throw!'); throw value; } }); var promise = new Promise(function(_resolve) { resolve = _resolve; }); promise.then( v => $DONOTEVALUATE(), e => { stages.push('Reject!'); assert.sameValue(e, value, 'The promise should be fulfilled with the provided value.'); }) .then(() => assert.compareArray(stages, ['Throw!', undefined, 'Reject!'])) .then($DONE, $DONE); returnValue = resolve(poisonedThen); stages.push(returnValue); njs-0.8.9/test/js/promise_s20.t.js000066400000000000000000000007611474132077100166710ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ var returnValue = null; var value = {}; var poisonedThen = Object.defineProperty({}, 'then', { get: function() { throw value; } }); var promise = new Promise(function(resolve) { returnValue = resolve(poisonedThen); }); assert.sameValue(returnValue, undefined); promise .then( v => $DONOTEVALUATE(), e => { assert.sameValue(e, value, 'The promise should be fulfilled with the provided value.'); }) .then($DONE, $DONE); njs-0.8.9/test/js/promise_s21.t.js000066400000000000000000000007611474132077100166720ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ var value = {}; var resolve; var poisonedThen = Object.defineProperty({}, 'then', { get: function() { throw value; } }); var p1 = new Promise(function(_resolve) { resolve = _resolve; }); var p2; p2 = p1.then(function() { return poisonedThen; }); p2 .then( v => $DONOTEVALUATE(), e => { assert.sameValue(e, value, 'The promise should be rejected with the thrown exception.'); }) .then($DONE, $DONE); resolve(); njs-0.8.9/test/js/promise_s22.t.js000066400000000000000000000010461474132077100166700ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ var returnValue = null; var value = {}; var resolve; var thenable = new Promise(function(resolve) { resolve(); }); var promise = new Promise(function(_resolve) { resolve = _resolve; }); thenable.then = function(resolve) { resolve(value); }; promise .then( v => { assert.sameValue(v, value, 'The promise should be fulfilled with the provided value.'); }, e => $DONOTEVALUATE) .then($DONE, $DONE); returnValue = resolve(thenable); assert.sameValue(returnValue, undefined); njs-0.8.9/test/js/promise_s23.t.js000066400000000000000000000006771474132077100167020ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ var returnValue = null; var resolve; var promise = new Promise(function(_resolve) { resolve = _resolve; }); promise .then( v => $DONOTEVALUATE(), e => { assert.sameValue(e.constructor, TypeError, 'The promise should be rejected with a TypeError instance.'); }) .then($DONE, $DONE); returnValue = resolve(promise); assert.sameValue(returnValue, undefined); njs-0.8.9/test/js/promise_s24.t.js000066400000000000000000000004241474132077100166710ustar00rootroot00000000000000/*--- includes: [] flags: [] ---*/ let checkPoint = ''; Promise.reject.call(function(executor) { checkPoint += 'a'; executor(); checkPoint += 'b'; executor(function() {}, function() {}); checkPoint += 'c'; }, {}); assert.sameValue(checkPoint, 'abc'); njs-0.8.9/test/js/promise_s25.t.js000066400000000000000000000007551474132077100167010ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ var resolve, reject; var promise = new Promise(function(_resolve, _reject) { resolve = _resolve; reject = _reject; }); var P = function(executor) { executor(resolve, reject); return promise; }; Promise.resolve.call(P, promise) .then( v => $DONOTEVALUATE(), e => { assert.sameValue(e.constructor, TypeError, 'The promise should be rejected with a TypeError instance.'); }) .then($DONE, $DONE); njs-0.8.9/test/js/promise_s26.t.js000066400000000000000000000131021474132077100166700ustar00rootroot00000000000000/*--- includes: [compareArray.js] flags: [async] ---*/ let stages = []; var inherits = (child, parent) => { child.prototype = Object.create(parent.prototype, { constructor: { value: child, enumerable: false, writable: true, configurable: true } }); Object.setPrototypeOf(child, parent); }; var BoxedPromise = (() => { function BoxedPromise(executor) { stages.push('BoxedPromise.constructor'); if (!(this instanceof BoxedPromise)) { return Promise(executor); } if (typeof executor !== 'function') { return new Promise(executor); } var context, args; var promise = new Promise(wrappedExecutor); this.boxed = promise; try { executor.apply(context, args); } catch (e) { args[1](e); } function wrappedExecutor(resolve, reject) { context = this; args = [wrappedResolve, wrappedReject]; function wrappedResolve(val) { return resolve(val); } function wrappedReject(val) { return reject(val); } } } inherits(BoxedPromise, Promise); BoxedPromise.prototype.then = function(res, rej) { stages.push('BoxedPromise.prototype.then'); var rs = Object.create(Object.getPrototypeOf(this)); rs.boxed = this.boxed.then(res, rej); return rs; }; return BoxedPromise; })(); var PatchedPromise = (() => { function PatchedPromise(executor) { stages.push('PatchedPromise.constructor'); if (!(this instanceof PatchedPromise)) { return Promise(executor); } if (typeof executor !== 'function') { return new Promise(executor); } var context, args; var promise = new Promise(wrappedExecutor); Object.setPrototypeOf(promise, PatchedPromise.prototype); try { executor.apply(context, args); } catch (e) { args[1](e); } return promise; function wrappedExecutor(resolve, reject) { context = this; args = [wrappedResolve, wrappedReject]; function wrappedResolve(val) { return resolve(val); } function wrappedReject(val) { return reject(val); } } } inherits(PatchedPromise, Promise); return PatchedPromise; })(); var testSubclass = (Class, name) => { return new Promise((resolve) => { var resolved = Class.resolve(name) .then((x) => stages.push(`resolved ${name}`)); stages.push(`${name} resolve ${resolved instanceof Class ? 'OK' : 'failed'}`); var rejected = Class.reject(name) .catch((x) => stages.push(`rejected ${name}`)); stages.push(`${name} reject ${rejected instanceof Class ? 'OK' : 'failed'}`); var instance = new Class((resolve) => { setImmediate(() => resolve(name)); }); var chain = instance .then((x) => { stages.push(`then ${x}`); return x; }) .then((x) => { stages.push(`then ${x}`); return x; }); stages.push(`${name} chain ${chain instanceof Class ? 'OK' : 'failed'}`); var fin = chain .finally(() => stages.push(`finally ${name}`)); stages.push(`${name} finally ${fin instanceof Class ? 'OK' : 'failed'}`); stages.push(`${name} sync done`); fin .then(() => stages.push(`${name} async done`)) .then(resolve); }); }; Promise.resolve() .then(() => testSubclass(BoxedPromise, 'BoxedPromise')) .then(() => { assert.compareArray(stages, [ "BoxedPromise.constructor", "BoxedPromise.prototype.then", "BoxedPromise resolve OK", "BoxedPromise.constructor", "BoxedPromise.prototype.then", "BoxedPromise reject OK", "BoxedPromise.constructor", "BoxedPromise.prototype.then", "BoxedPromise.prototype.then", "BoxedPromise chain OK", "BoxedPromise.prototype.then", "BoxedPromise finally OK", "BoxedPromise sync done", "BoxedPromise.prototype.then", "BoxedPromise.prototype.then", "resolved BoxedPromise", "rejected BoxedPromise", "then BoxedPromise", "then BoxedPromise", "finally BoxedPromise", "BoxedPromise.constructor", "BoxedPromise.prototype.then", "BoxedPromise.prototype.then", "BoxedPromise async done", ]); stages = []; }) .then(() => testSubclass(PatchedPromise, 'PatchedPromise')) .then(() => { assert.compareArray(stages, [ "PatchedPromise.constructor", "PatchedPromise.constructor", "PatchedPromise resolve OK", "PatchedPromise.constructor", "PatchedPromise.constructor", "PatchedPromise reject OK", "PatchedPromise.constructor", "PatchedPromise.constructor", "PatchedPromise.constructor", "PatchedPromise chain OK", "PatchedPromise.constructor", "PatchedPromise finally OK", "PatchedPromise sync done", "PatchedPromise.constructor", "PatchedPromise.constructor", "resolved PatchedPromise", "rejected PatchedPromise", "then PatchedPromise", "then PatchedPromise", "finally PatchedPromise", "PatchedPromise.constructor", "PatchedPromise.constructor", "PatchedPromise.constructor", "PatchedPromise async done", ]); stages = []; }) .then($DONE, $DONE); njs-0.8.9/test/js/promise_s27.t.js000066400000000000000000000013461474132077100167000ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ var inherits = (child, parent) => { child.prototype = Object.create(parent.prototype, { constructor: { value: child, enumerable: false, writable: true, configurable: true } }); Object.setPrototypeOf(child, parent); }; function BoxedPromise(executor) { var context, args; new Promise(wrappedExecutor); executor.apply(context, args); function wrappedExecutor(resolve, reject) { context = this; args = [v => resolve(v),v => reject(v)]; } } inherits(BoxedPromise, Promise); Promise.resolve() .then(() => BoxedPromise.resolve()) .catch(e => assert.sameValue(e.constructor, TypeError)) .then($DONE, $DONE); njs-0.8.9/test/js/promise_set_timeout.t.js000066400000000000000000000010601474132077100206170ustar00rootroot00000000000000/*--- includes: [compareArray.js] flags: [] ---*/ var res = []; function abc() { var promise = new Promise(function(resolve, reject) { res.push('One'); resolve(); }); res.push('Two'); promise.then(() => {res.push('Four'); return {num: 'Five'}}) .then((obj) => {res.push(obj.num); return {num: 'Six'}}) .then((obj) => {res.push(obj.num)}); res.push('Three'); } abc(); assert.compareArray(res, ['One', 'Two', 'Three']); setTimeout(() => assert.compareArray(res, ['One', 'Two', 'Three', 'Four', 'Five', 'Six']), 0); njs-0.8.9/test/js/promise_then_throw.t.js000066400000000000000000000001601474132077100204370ustar00rootroot00000000000000/*--- includes: [] flags: [] negative: phase: runtime ---*/ Promise.resolve() .then(() => {nonExsisting()}); njs-0.8.9/test/js/promise_then_throw_catch.t.js000066400000000000000000000003151474132077100216030ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ let called = false; Promise.resolve() .then(() => {nonExsisting()}) .catch(() => {called = true;}) .then(v => assert.sameValue(called, true)) .then($DONE, $DONE); njs-0.8.9/test/js/promise_then_throw_finally_catch.t.js000066400000000000000000000003401474132077100233170ustar00rootroot00000000000000/*--- includes: [] flags: [async] ---*/ let called = false; Promise.resolve() .then(() => {nonExsisting()}) .finally(() => {}) .catch(() => {called = true;}) .then(v => assert.sameValue(called, true)) .then($DONE, $DONE); njs-0.8.9/test/js/promise_two_first_then_throw.t.js000066400000000000000000000002711474132077100225420ustar00rootroot00000000000000/*--- includes: [] flags: [] negative: phase: runtime ---*/ Promise.resolve() .then(() => {nonExsistingOne()}); Promise.resolve() .then(() => {nonExsistingTwo()}) .catch(() => {}); njs-0.8.9/test/js/promise_two_then_throw.t.js000066400000000000000000000002501474132077100213300ustar00rootroot00000000000000/*--- includes: [] flags: [] negative: phase: runtime ---*/ Promise.resolve() .then(() => {nonExsistingOne()}); Promise.resolve() .then(() => {nonExsistingTwo()}); njs-0.8.9/test/options000066400000000000000000000023771474132077100147360ustar00rootroot00000000000000#!/bin/sh # Copyright (C) Dmitry Volyntsev # Copyright (C) NGINX, Inc. NJS_TEST_DIR=`mktemp -d /tmp/njs_test.XXX` NJS_TEST_LOG_DEFAULT="$NJS_TEST_DIR/log.log" NJS_TEST_VERBOSE=${NJS_TEST_VERBOSE:-} NJS_TEST_BINARY=${NJS_TEST_BINARY:-build/njs} NJS_TEST_LOG=${NJS_TEST_LOG:-${NJS_TEST_LOG_DEFAULT}} for njs_option do case "$njs_option" in -*=*) value=`echo "$njs_option" | sed -e 's/[-_a-zA-Z0-9]*=//'` ;; *) value="" ;; esac case "$njs_option" in --binary=*) NJS_TEST_BINARY="$value" shift ;; --log=*) NJS_TEST_LOG="$value" shift ;; --test-dir=*) NJS_TEST_DIR="$value" shift ;; --verbose=*) NJS_TEST_VERBOSE="$value" shift ;; --help) . test/help exit 0 ;; --*) echo echo $0: error: invalid option \"$njs_option\". echo Run \"$0 --help\" to see available options. echo exit 1 ;; *) break ;; esac done NJS_TEST_PATHS=${@:-test} NJS_TEST_EXIT_CODE=1 if echo $NJS_TEST_BINARY | grep -q njs; then NJS_TEST_EXIT_CODE=23 fi njs-0.8.9/test/prepare000066400000000000000000000020221474132077100146640ustar00rootroot00000000000000#!/bin/sh # Copyright (C) Dmitry Volyntsev # Copyright (C) NGINX, Inc. njs_includes=`grep 'includes: \[[^]]*]' $njs_test \ | sed -e 's/includes: \[//' | sed -e 's/,/ /g' | sed -e 's/\]//'` njs_includes="assert.js sta.js $njs_includes" njs_paths=`grep 'paths: \[[^]]*]' $njs_test \ | sed -e 's/paths: \[//' | sed -e 's/ *, */:/g' | sed -e 's/\]//'` njs_flags=`grep 'flags: \[[^]]*]' $njs_test \ | sed -e 's/flags: \[//' | sed -e 's/,/ /g' | sed -e 's/\]//'` njs_negative=`grep 'negative:' $njs_test > /dev/null \ && grep 'phase: ' $njs_test | sed -e 's/ *phase: //' || true` njs_async=no for flag in $njs_flags; do case $flag in async) njs_async=yes njs_includes="compatPrint.js doneprintHandle.js $njs_includes" ;; esac done njs_inc= for file in $njs_includes; do njs_inc="$njs_inc test/harness/$file" done mkdir -p `dirname $NJS_TEST_DIR/$njs_test` cat $njs_inc $njs_test > $NJS_TEST_DIR/$njs_test njs_total=$((njs_total+1)) njs-0.8.9/test/report000066400000000000000000000005731474132077100145520ustar00rootroot00000000000000#!/bin/sh # Copyright (C) Dmitry Volyntsev # Copyright (C) NGINX, Inc. NJS_STATUS="PASSED" if [ $njs_passed -ne $njs_total ]; then NJS_STATUS="FAILED" fi if [ -n "$njs_failed_list" ]; then printf "\n" for t in $njs_failed_list; do printf "$t FAILED\n" done fi printf "TOTAL: $NJS_STATUS [$njs_passed/$njs_total]\n" exit $(($njs_passed != $njs_total)) njs-0.8.9/test/script_args.js000066400000000000000000000001011474132077100161550ustar00rootroot00000000000000var argv = process.argv.slice(2); console.log(argv[0] + argv[1]) njs-0.8.9/test/setup000066400000000000000000000014471474132077100144000ustar00rootroot00000000000000#!/bin/sh # Copyright (C) Dmitry Volyntsev # Copyright (C) NGINX, Inc. verbose() { if [ -n "$NJS_TEST_VERBOSE" ]; then printf "$1" fi } passed() { verbose " PASSED\n" njs_passed=$((njs_passed+1)) } skip() { verbose " SKIPPED\n" njs_passed=$((njs_passed+1)) } failed() { njs_failed_list="$njs_failed_list $1" verbose " FAILED\n" printf "$1:\n" cat $2 printf "\n" } verbose "Test dir: $NJS_TEST_DIR\n" verbose "Log file: $NJS_TEST_LOG\n" verbose "\n" njs_passed=0 njs_total=0 njs_failed_list="" NJS_TESTS="" for arg in $NJS_TEST_PATHS; do if [ -d $arg ]; then NJS_TESTS="$NJS_TESTS $(find $arg -name '*\.t\.js' -o -name '*\.t\.mjs')" else NJS_TESTS="$NJS_TESTS $arg" fi done NJS_TESTS=`printf "%s\n" $NJS_TESTS | sort` njs-0.8.9/test/shell_test.exp000066400000000000000000000240041474132077100161730ustar00rootroot00000000000000# # Copyright (C) Dmitry Volyntsev # Copyright (C) NGINX, Inc. # proc njs_test {body {opts ""}} { if {$opts eq ""} { spawn -nottycopy njs } else { eval spawn -nottycopy njs $opts } expect -re "interactive njs \\((njs|QuickJS):.*\r\n\r\n>> " set len [llength $body] for {set i 0} {$i < $len} {incr i} { set pair [lindex $body $i] send [lindex $pair 0] expect [lindex $pair 1] } send "\n" send ".exit\r\n" expect eof } proc njs_run {opts expected_re} { catch {exec njs {*}$opts} out if {[regexp $expected_re $out match] == 0} { return -code error "njs_run: unexpected output '$out' vs '$expected_re'" } } njs_test { {"njs.version\r\n" "njs.version\r\n\*\.\*\.\*"} } # simple multi line interaction njs_test { {"var a = 1\r\n" "var a = 1\r\nundefined\r\n>> "} {"a *= 2\r\n" "a *= 2\r\n2\r\n>> "} } # function declarations in interactive mode njs_test { {"function a() { return 1; }\r\n" "undefined\r\n>> "} {"a();\r\n" "1\r\n>> "} {"function a() { return 2; }\r\n" "undefined\r\n>> "} {"a();\r\n" "2\r\n>> "} } # Global completions njs_test { {"\t\t" "$262*"} } # Global completions, single partial match njs_test { {"O\t" "O\a*bject"} } njs_test { {"Mat\t" "Mat\a*h"} } njs_test { {"conso\t" "conso\a*le"} } # Global completions, multiple partial match njs_test { {"cons\t\t" "console*const"} } njs_test { {"Type\t" "Type\a*Error"} {".\t\t" "TypeError.__proto__"} } njs_test { {"TypeError.\t\t" "TypeError.__proto__*TypeError.prototype"} } njs_test { {"Object.ge\t" "Object.ge\a*t"} {"\t\t" "Object.getOwnPropertyDescriptor*Object.getPrototypeOf"} } njs_test { {"JS\t" "JS\a*ON"} {".\t\t" "JSON.__*JSON.stringify"} } njs_test { {"decodeURI\t\t" "decodeURI*decodeURIComponent"} {"C\t" "decodeURIC\a*omponent"} } # Global completions, no matches njs_test { {"1.\t\t" "1."} } njs_test { {"1..\t\t" "1.."} } njs_test { {"'abc'.\t\t" "'abc'."} } # Global completions, global vars njs_test { {"var AA = 1; var AAA = 2\r\n" "var AA = 1; var AAA = 2\r\nundefined\r\n>> "} {"AA\t\t" "AA*AAA*"} } njs_test { {"var zz = 1\r\n" "var zz = 1\r\nundefined\r\n>> "} {"1 + z\t\r\n" "1 + z*z*\r\n2"} } njs_test { {"unknown_var\t\t" "unknown_var"} } njs_test { {"unknown_var.\t\t" "unknown_var."} } # An object's level completions njs_test { {"var o = {zz:1, zb:2}\r\n" "var o = {zz:1, zb:2}\r\nundefined\r\n>> "} {"o.z\t\t" "o.zb*o.zz"} } njs_test { {"var d = new Date()\r\n" "var d = new Date()\r\nundefined\r\n>> "} {"d.to\t\t" "d.toDateString*d.toLocaleDateString*d.toString"} } njs_test { {"var o = {a:new Date()}\r\n" "var o = {a:new Date()}\r\nundefined\r\n>> "} {"o.a.to\t\t" "o.a.toDateString*o.a.toLocaleDateString*o.a.toString"} } njs_test { {"var o = {a:1,b:2,333:'t'}\r\n" "var o = {a:1,b:2,333:'t'}\r\nundefined\r\n>> "} {"o.3\t\t" "o.3"} } njs_test { {"var a = Array(5000000); a.aab = 1; a.aac = 2\r\n" "var a = Array(5000000); a.aab = 1; a.aac = 2\r\n2\r\n>> "} {"a.\t\t" "a.aab*"} } njs_test { {"var a = new Uint8Array([5,6,7,8,8]); a.aab = 1; a.aac = 2\r\n" "var a = new Uint8Array(\\\[5,6,7,8,8]); a.aab = 1; a.aac = 2\r\n2\r\n>> "} {"a.\t\t" "a.aab*"} } # console object njs_test { {"console[Symbol.toStringTag]\r\n" "console\\\[Symbol.toStringTag]\r\n'Console'\r\n>> "} {"Object.prototype.toString.call(console)\r\n" "Object.prototype.toString.call(console)\r\n'\\\[object Console]'\r\n>> "} {"console.toString()\r\n" "console.toString()\r\n'\\\[object Console]'\r\n>> "} } # console log functions njs_test { {"console.log.length\r\n" "console.log.length\r\n0"} {"console.log()\r\n" "console.log()\r\nundefined\r\n>> "} {"console.log('')\r\n" "console.log('')\r\n\r\nundefined\r\n>> "} {"console.log(1)\r\n" "console.log(1)\r\n1\r\nundefined\r\n>> "} {"console.log(1, 'a')\r\n" "console.log(1, 'a')\r\n1\r\na\r\nundefined\r\n>> "} {"print(1, 'a')\r\n" "print(1, 'a')\r\n1\r\na\r\nundefined\r\n>> "} {"console.log('\\tабв\\nгд')\r\n" "console.log('\\\\tабв\\\\nгд')\r\n\tабв\r\nгд\r\nundefined\r\n>> "} {"console.error(42)\r\n" "console.error(42)\r\nE: 42\r\nundefined\r\n>> "} {"console.info(23)\r\n" "console.info(23)\r\n23\r\nundefined\r\n>> "} {"console.warn(37)\r\n" "console.warn(37)\r\nW: 37\r\nundefined\r\n>> "} } # console.time* functions njs_test { {"console.time()\r\n" "console.time()\r\nundefined\r\n>> "} {"console.timeEnd()\r\n" "console.timeEnd()\r\ndefault: *.*ms\r\nundefined\r\n>> "} {"console.time(undefined)\r\n" "console.time(undefined)\r\nundefined\r\n>> "} {"console.timeEnd(undefined)\r\n" "console.timeEnd(undefined)\r\ndefault: *.*ms\r\nundefined\r\n>> "} {"console.time('abc')\r\n" "console.time('abc')\r\nundefined\r\n>> "} {"console.time('abc')\r\n" "console.time('abc')\r\nTimer \"abc\" already exists.\r\nundefined\r\n>> "} {"console.timeEnd('abc')\r\n" "console.timeEnd('abc')\r\nabc: *.*ms\r\nundefined\r\n>> "} {"console.time(true)\r\n" "console.time(true)\r\nundefined\r\n>> "} {"console.timeEnd(true)\r\n" "console.timeEnd(true)\r\ntrue: *.*ms\r\nundefined\r\n>> "} {"console.time(42)\r\n" "console.time(42)\r\nundefined\r\n>> "} {"console.timeEnd(42)\r\n" "console.timeEnd(42)\r\n42: *.*ms\r\nundefined\r\n>> "} {"console.timeEnd()\r\n" "console.timeEnd()\r\nTimer \"default\" doesn’t exist."} {"console.timeEnd('abc')\r\n" "console.timeEnd('abc')\r\nTimer \"abc\" doesn’t exist."} {"console.time('abc')\r\n" "console.time('abc')\r\nundefined\r\n>> "} } njs_test { {"var print = console.log.bind(console); print(1, 'a', [1, 2])\r\n" "1\r\na\r\n*1,2*\r\nundefined\r\n>> "} {"var print = console.log.bind(console); print(console.a.a)\r\n" "TypeError: cannot * property *a* of undefined*"} {"print(console.a.a)\r\n" "TypeError: cannot * property *a* of undefined*"} } # Backtraces are reset between invocations njs_test { {"JSON.parse(Error())\r\n" "JSON.parse(Error())\r\nThrown:\r\nSyntaxError: *nexpected token"} {"console.a.a\r\n" "TypeError: cannot * property *a* of undefined*"} } njs_test { {"try { console.log({ toString: function() { throw 'test'; } }) } catch (e) {}\r\n" "undefined"} {"function f() { throw 't' }; try { console.log({ toString: function() { return f() } }) } catch (e) {}\r\n" "undefined"} } njs_test { {"(function() { throw 'test' })()\r\n" "Thrown:\r\n*test"} } njs_test { {"function f() { return ({}.a.a); }\r\n" "undefined"} {"var e; try {f()} catch (ee) {e = ee}\r\n" "undefined"} {"Object.keys(null)\r\n" "Thrown:\r\nTypeError: cannot convert*to object"} {"e\r\n" "TypeError: cannot * property *a* of undefined"} } # Non-ASCII characters njs_test { {"'絵文字'\r\n" "'絵文字'"} {"var v = 'абвгдеёжзийкл';v[10]\r\n" "'й'"} } # Immediate events njs_test { {"var t = setImmediate(console.log, 'a', 'aa')\r\n" "undefined\r\na\r\naa"} } njs_test { {"var a = 1 + 1; setTimeout(function (x) {a = x}, 0, 'a'); a\r\n" "2"} {"a\r\n" "a\r\n'a'"} } njs_test { {"setTimeout(function () {}, 1, 'a')\r\n" "InternalError: njs_set_timer(): async timers unsupported"} } njs_test { {"var a = 1 + 1; setTimeout(function (x) { setTimeout(function (y) {a = y}, 0, x)}, 0, 'a'); a\r\n" "2"} {"a\r\n" "a\r\n'a'"} } njs_test { {"var a = 1 + 1; setImmediate(function (x) { setImmediate(function (y) {a = y}, x)}, 'a'); a\r\n" "2"} {"a\r\n" "a\r\n'a'"} } njs_test { {"var i = 0; (function x() { if (i < 10) setImmediate(x); i++; })()\r\n" "undefined"} {"i\r\n" "i\r\n11"} } njs_test { {"var a = 0, t = setImmediate(function() {a = 1}); clearTimeout(t)\r\n" "undefined"} {"a\r\n" "a\r\n0"} } njs_test { {"var i = 0; (function x() { if (i < 3) setImmediate(x); i++; throw 'Oops';})()\r\n" "Oops"} {"i\r\n" "i\r\n4"} } njs_test { {"var i = 0, queue = []; (function x() { if (i < 5) setImmediate(x); queue.push(i++); })()\r\n" "undefined"} {"queue.toString()\r\n" "queue.toString()\r\n'0,1,2,3,4,5'"} } njs_run {"-c" "setTimeout(() => {console.log('A'.repeat(1024))}, 0); ref"} \ "^Thrown: ReferenceError: \['\"\]ref\['\"\] is not defined.*" njs_run {"-c" "setTimeout(() => {ref}, 0); setTimeout(() => {console.log('A'.repeat(1024))}, 0)"} \ "^Thrown: ReferenceError: \['\"\]ref\['\"\] is not defined.*" njs_test { {"setImmediate(() => { console.log('x'); return Promise.reject('xx'); })\r\n" "0\r\nx\r\nThrown:\r\n*Error: unhandled promise rejection: xx\r\n"} {"setImmediate(() => { console.log('x'); return Promise.reject('xx'); })\r\n" "1\r\nx\r\nThrown:\r\n*Error: unhandled promise rejection: xx\r\n"} {"42\r\n" "42\r\n"} } # CLI OPTIONS # help njs_run {"-h"} "Options" # command njs_run {"-c" "console.log(\"a b c\")"} "a b c" njs_run {"-c" "console.log("} "SyntaxError: \[Uu\]nexpected" # process njs_run {"-c" "console.log(typeof process.argv)"} "object" njs_run {"-c" "console.log(process.argv.slice(2))" "AAA"} "AAA" njs_run {"-c" "console.log(typeof process.env)"} "object" njs_run {"-c" "console.log(process.env.HOME != undefined)"} "true" njs_run {"-c" "console.log(process.env.___UNDECLARED != undefined)"} "false" njs_run {"-c" "console.log(process.pid)"} "\\d+" njs_run {"-c" "console.log(process.ppid)"} "\\d+" njs_run {"-c" "console.log(process.kill(process.pid, 0))"} "true" njs_run {"-c" "console.log(process.kill(process.pid, 'SIGCHLD'))"} "true" # script args njs_run {"test/script_args.js" "A" "B"} "AB" # version njs_run {"-v"} "\\d+\.\\d+\.\\d+" njs-0.8.9/test/shell_test_njs.exp000066400000000000000000000132221474132077100170450ustar00rootroot00000000000000# # Copyright (C) Dmitry Volyntsev # Copyright (C) NGINX, Inc. # proc njs_test {body {opts ""}} { if {$opts eq ""} { spawn -nottycopy njs } else { eval spawn -nottycopy njs $opts } expect -re "interactive njs \\(njs:\\d+\.\\d+\.\\d+\\)\r\n\r\n>> " set len [llength $body] for {set i 0} {$i < $len} {incr i} { set pair [lindex $body $i] send [lindex $pair 0] expect [lindex $pair 1] } send "\n" send ".exit\r\n" expect eof } proc njs_run {opts expected_re} { catch {exec njs {*}$opts} out if {[regexp $expected_re $out match] == 0} { return -code error "njs_run: unexpected output '$out' vs '$expected_re'" } } njs_test { {"njs.version\r\n" "njs.version\r\n\*\.\*\.\*"} } # console dump njs_test { {"console.dump()\r\n" "console.dump()\r\nundefined\r\n>> "} {"console.dump(1)\r\n" "console.dump(1)\r\n1\r\nundefined\r\n>> "} {"console.dump(1, 'a')\r\n" "console.dump(1, 'a')\r\n1\r\na\r\nundefined\r\n>> "} {"var print = console.dump.bind(console); print(1, 'a', [1, 2])\r\n" "1\r\na\r\n\\\[\r\n 1,\r\n 2\r\n]\r\nundefined\r\n>> "} {"var print = console.log.bind(console); print(console.a.a)\r\n" "TypeError: cannot get property \"a\" of undefined"} {"print(console.a.a)\r\n" "TypeError: cannot get property \"a\" of undefined"} } njs_test { {"console.ll()\r\n" "console.ll()\r\nThrown:\r\nTypeError: (intermediate value)\\\[\"ll\"] is not a function"} } njs_test { {"console.log.length\r\n" "console.log.length\r\n0"} } # Backtraces for external objects njs_test { {"console.info(console.a.a)\r\n" "console.info(console.a.a)\r\nThrown:\r\nTypeError:*at console.info (native)"} } # dumper njs_test { {"var o = {toString: function(){}, log: console.log}\r\n" "undefined\r\n>> "} {"o\r\n" "o\r\n{\r\n toString: \\\[Function: toString],\r\n log: \\\[Function: log]\r\n}"} } njs_test { {"[1, new Number(2), 'a', new String('αβZγ'), true, new Boolean(false)]\r\n" "\\\[\r\n 1,\r\n \\\[Number: 2],\r\n 'a',\r\n \\\[String: 'αβZγ'],\r\n true,\r\n \\\[Boolean: false]\r\n]"} } njs_test { {"[undefined,,null]\r\n" "\\\[\r\n undefined,\r\n ,\r\n null\r\n]"} } njs_test { {"[InternalError(),TypeError('msg'), new RegExp(), /^undef$/m, new Date(0)]\r\n" "\\\[\r\n InternalError,\r\n TypeError: msg,\r\n /(?:)/,\r\n /^undef$/m,\r\n 1970-01-01T00:00:00.000Z\r\n]"} } # dumper excapes special characters as JSON.stringify() # except '\"' njs_test { {"\"\\r\\0\\\"\"\r\n" "\\\\r\\\\u0000\""} } njs_test { {"[{a:1}]\r\n" "\r\n\\\[\r\n {\r\n a: 1\r\n }\r\n]"} } # CLI OPTIONS # ast njs_run {"-a" "-c" "console.log(1*2)"} "{\"name\": \"END\"" # process njs_run {"-c" "console.log(typeof process.argv)"} "object" njs_run {"-c" "console.log(process.argv.slice(2))" "AAA"} "AAA" njs_run {"-c" "console.log(typeof process.env)"} "object" njs_run {"-c" "console.log(process.env.HOME != undefined)"} "true" njs_run {"-c" "console.log(process.env.___UNDECLARED != undefined)"} "false" njs_run {"-c" "console.log(process.pid)"} "\\d+" njs_run {"-c" "console.log(process.ppid)"} "\\d+" njs_run {"-c" "console.log(process.kill(process.pid, 0))"} "true" # script args njs_run {"test/script_args.js" "A" "B"} "AB" # disassemble njs_test { {"1+1\r\n" " 1 | 00000 ADD*\r\n*2"} {"__unknown\r\n" " 1 | 00000 GLOBAL GET*\r\n*REFERENCE ERROR*"} {"for (var n in [1]) {try {break} finally{}}\r\n" " 1 | 00000 ARRAY*\r\n*TRY BREAK*PROP NEXT*-*\r\n\r\nundefined"} {"(function() {try {return} finally{}})()\r\n" " 1 | 00000 TRY START*\r\n*TRY RETURN*\r\n\r\nundefined"} } "-d" # modules # FIXME: # During import, the variable is declared regardless of the result of the import. # Because of this, in the console mode, checking the variable after the import # error may give an incorrect result. # # For example: # {"import ref from 'ref_exception.js'\r\n" # "ReferenceError: \"undeclared\" is not defined"} # {"ref\r\n" # "ReferenceError: \"ref\" is not defined\r\n"} njs_test { {"import lib1 from 'lib1.js'; import lib2 from 'lib1.js'\r\n" "undefined\r\n"} {"lib2.inc()\r\n" "undefined\r\n"} {"lib1.get()\r\n" "1\r\n"} {"import ref from 'ref_exception.js'\r\n" "ReferenceError: \"undeclared\" is not defined"} {"var ref\r\n" "SyntaxError: \"ref\" has already been declared"} {"import ref from 'ref_exception.js'\r\n" "SyntaxError: \"ref\" has already been declared"} } "-p test/js/module/ -p test/js/module/libs/" # quiet mode njs_run {"-q" "test/js/import_chain.t.js"} \ "ReferenceError: Cannot load module \"lib2.js\" in 7" # sandboxing njs_test { {"var fs = require('fs')\r\n" "Error: Cannot load module \"fs\"\r\n"} } "-s" njs_test { {"var crypto = require('crypto')\r\n" "undefined\r\n"} } "-s" # safe mode njs_test { {"new Function()\r\n" "TypeError: function constructor is disabled in \"safe\" mode\r\n"} {"(new Function('return this'))() === globalThis\r\n" "true\r\n"} {"new Function('return this;')\r\n" "[Function]"} {"new Function('return thi')\r\n" "TypeError: function constructor is disabled in \"safe\" mode\r\n"} } "-u" # source type njs_test { {"typeof this\r\n" "'undefined'"} {"(() => typeof this)()\r\n" "'undefined'"} {"this.NaN\r\n" "TypeError: cannot get property \"NaN\" of undefined"} } "-m" njs_test { {"typeof this\r\n" "'object'"} {"(() => typeof this)()\r\n" "'object'"} {"this.NaN\r\n" "this.NaN\r\nNaN"} } # version njs_run {"-v"} "\\d+\.\\d+\.\\d+" njs-0.8.9/test/test262000077500000000000000000000031011474132077100144410ustar00rootroot00000000000000#!/bin/sh # Copyright (C) Dmitry Volyntsev # Copyright (C) NGINX, Inc. set -e . test/options . test/setup export NJS_TEST_DIR for njs_test in $NJS_TESTS; do . test/prepare njs_log="$NJS_TEST_DIR/${njs_test%.*}.log" verbose "$njs_test $njs_log" cat << END >> $NJS_TEST_LOG ---------------------------------------- running $njs_test $njs_log END if [ "$NJS_SKIP_LIST" != "${NJS_SKIP_LIST#*$njs_test*}" ]; then skip $njs_test continue fi status=0 NJS_PATH=$njs_paths \ NJS_EXIT_CODE=$NJS_TEST_EXIT_CODE \ $NJS_TEST_BINARY $NJS_TEST_DIR/$njs_test > $njs_log 2>&1 || status=$? cat $njs_log >> $NJS_TEST_LOG njs_out=`cat $njs_log` if [ "$status" -eq 0 ]; then if [ -n "$njs_negative" ]; then failed $njs_test $njs_log elif [ $njs_async = yes ]; then if [ "$njs_out" != 'Test262:AsyncTestComplete' ]; then failed $njs_test $njs_log else passed $njs_test fi else if [ -n "$njs_out" ]; then failed $njs_test $njs_log else passed $njs_test fi fi else if [ -n "$njs_negative" ]; then if [ "$status" = "$NJS_TEST_EXIT_CODE" ]; then passed $njs_test else echo "negative test exited with unexpected exit code:$status" failed $njs_test $njs_log fi else failed $njs_test $njs_log fi fi done . test/finalize . test/report njs-0.8.9/test/ts/000077500000000000000000000000001474132077100137355ustar00rootroot00000000000000njs-0.8.9/test/ts/package.json000066400000000000000000000004761474132077100162320ustar00rootroot00000000000000{ "private": true, "name": "njs-types-test", "version": "0.0.0", "description": "Tests for njs TypeScript type definitions.", "scripts": { "test": "tsc" }, "author": "NGINX, Inc.", "license": "BSD-2-Clause", "devDependencies": { "njs-types": "file:../../ts", "typescript": "~4.9.5" } } njs-0.8.9/test/ts/test.ts000066400000000000000000000222071474132077100152670ustar00rootroot00000000000000import fs from 'fs'; import qs from 'querystring'; import cr from 'crypto'; import xml from 'xml'; import zlib from 'zlib'; async function http_module(r: NginxHTTPRequest) { var s: string; var vod: void; // r.uri if (r.uri == '/') { } // r.args s = r.args.x; s = r.args[1]; s = r.args.x + ''; // r.headersIn r.headersIn['Accept'] == 'dddd'; // r.headersOut r.headersOut['Content-Type'] = 'text/plain'; // Warning: r.headersOut['Content-Type'] = ['a', 'b']; r.headersOut['Connection'] = undefined; delete r.headersOut['Bar']; r.headersOut['Set-Cookie'] = ['aaa', 'bbb']; r.headersOut['Foo'] = ['aaa', 'bbb']; let values: Array = r.rawHeadersIn.filter(v=>v[0].toLowerCase() == 'foo').map(v=>v[1]); // r.log r.log(s); r.log(Buffer.from("abc")); r.log(r.headersOut['Connection'] ?? ''); // r.variables r.variables.a == 'a'; r.variables.cookie_a = 'b'; // r.rawVariables r.rawVariables.a?.equals(Buffer.from([1])); // r.subrequest r.subrequest('/uri', reply => r.return(200, reply.headersOut["Location"] ?? '')); r.subrequest('/p/sub1').then(reply => r.return(reply.status)); r.subrequest('/p/sub2', {method:'POST'}).then(reply => r.return(reply.status)); vod = r.subrequest('/p/sub3', reply => r.return(reply.status)); vod = r.subrequest('/p/sub4', {method:'POST'}, reply => r.return(reply.status)); vod = r.subrequest(Buffer.from('/p/sub5'), {detached:true}); // Warning: vod = r.subrequest('/p/sub9', {detached:true}, reply => r.return(reply.status)); r.subrequest('/p/sub6', 'a=1&b=2').then(reply => r.return(reply.status, JSON.stringify(JSON.parse(reply.responseBody ?? '')))); let body = await r.subrequest('/p/sub7'); // r.requestText r.requestText == 'a'; r.requestText?.startsWith('a'); // r.requestBuffer r.requestBuffer?.equals(Buffer.from([1])); // r.responseText r.responseText == 'a'; r.responseText?.startsWith('a'); // r.responseBuffer r.responseBuffer?.equals(Buffer.from([1])); ngx.fetch('http://nginx.org/', {method:'POST', headers:{Foo:'bar'}}) .then(reply => { if (reply.headers.get('foo')) { throw 'oops' }; let out: Array = reply.headers.getAll("foo"); let has: boolean = reply.headers.has("foo"); reply.headers.append("foo", "xxx"); reply.headers.delete("xxx"); reply.headers.forEach((name, value) => { /* do something. */ }); return reply.text() }) .then(body => r.return(200, body)) .catch(e => r.return(501, e.message)) let response = await ngx.fetch('http://nginx.org/'); let response2 = new Response("xxx", {headers: {"Content-Type": "text/plain"}, status: 404}); let req = new Request("http://nginx.org", {method: "POST", headers: new Headers(["Foo", "bar"])}); let response3 = await ngx.fetch(req); // js_body_filter r.sendBuffer(Buffer.from("xxx"), {last:true}); r.sendBuffer("xxx", {flush: true}); r.done(); } async function fs_module() { var s:string; s = fs.readFileSync('/path', 'utf8'); s = fs.readFileSync(Buffer.from('/path'), {encoding:'hex'}); fs.writeFileSync('/path', Buffer.from('abc')); let fh = await fs.promises.open('/path', 'r+'); let bw = await fh.write(Buffer.from('abc'), 0, 1, 3); let bytes = bw.bytesWritten; bw = await fh.write(Buffer.from('abc'), 2, 2, null); let stat = fh.stat(); let buffer = Buffer.alloc(16); let br = await fh.read(buffer, 0, 16, null); bytes = br.bytesRead; await fh.close(); let fd = fs.openSync('/path', 'r+'); let stat2 = fs.fstatSync(fd); fs.readSync(fd, buffer, 0, 16, 4); buffer[1] += 2; fs.writeSync(fd, buffer, 0, 16, 4); fs.closeSync(fd); fs.mkdirSync('a/b/c', {recursive: true}); await fs.promises.mkdir('d/e/f', {recursive: false}); fs.rmdirSync('a/b/c', {recursive: true}); await fs.promises.rmdir('d/e/f', {recursive: false}); } function qs_module(str: string) { var o; var s:string; o = qs.parse(str); s = qs.stringify(o); } function xml_module(str: string) { let doc; let node; let children, selectedChildren; doc = xml.parse(str); node = doc.$root; node.$ns; children = node.$tags; selectedChildren = node.$tags$xxx; node?.$tag$xxx?.$tag$yyy?.$attr$zzz; let buf:Buffer = xml.exclusiveC14n(node); buf = xml.exclusiveC14n(doc, node.$tag$xxx, false); buf = xml.exclusiveC14n(node, null, true, "aa bb"); node.setText("xxx"); node.removeText(); node.setText(null); node.addChild(node); node.removeChildren('xx'); node.removeAttribute('xx'); node.removeAllAttributes(); node.setAttribute('xx', 'yy'); node.setAttribute('xx', null); node.$tags = [node, node]; } function zlib_module(str: string) { zlib.deflateRawSync(str, {level: zlib.constants.Z_BEST_COMPRESSION, memLevel: 9}); zlib.deflateSync(str, {strategy: zlib.constants.Z_RLE}); zlib.inflateRawSync(str, {windowBits: 14}); zlib.inflateSync(str, {chunkSize: 2048}); } function crypto_module(str: string) { var h; var b:Buffer; var s:string; h = cr.createHash("sha1"); h = h.update(str).update(Buffer.from([0])); h = h.copy(); b = h.digest(); s = cr.createHash("sha256").digest("hex"); } async function crypto_object(keyData: ArrayBuffer, data: ArrayBuffer) { let iv = crypto.getRandomValues(new Uint8Array(16)); let ekey = await crypto.subtle.importKey("pkcs8", keyData, {name: 'RSA-OAEP', hash: "SHA-256"}, false, ['decrypt']); let jkey = await crypto.subtle.importKey("jwk", { kty: "RSA" }, {name: 'RSA-OAEP', hash: "SHA-256"}, true, ['decrypt']); let skey = await crypto.subtle.importKey("raw", keyData, 'AES-CBC', false, ['encrypt']); data = await crypto.subtle.decrypt({name: 'RSA-OAEP'}, ekey, data); data = await crypto.subtle.encrypt({name: 'AES-CBC', iv:iv}, skey, data); let sig = await crypto.subtle.sign({name: 'RSA-PSS', saltLength:32}, skey, data); let r:boolean; r = await crypto.subtle.verify({name: 'RSA-PSS', saltLength:32}, skey, sig, data); let jwk = await crypto.subtle.exportKey('jwk', ekey); let pair = await crypto.subtle.generateKey({name: "RSASSA-PKCS1-v1_5", hash: "SHA-512", modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1])}, true, ['sign', 'verify']); pair.privateKey.extractable; pair.publicKey.algorithm.name; let hkey = await crypto.subtle.generateKey({name: "HMAC", hash: "SHA-384"}, true, ['sign', 'verify']); hkey.algorithm.name; let akey = await crypto.subtle.generateKey({name: "AES-GCM", length: 256}, true, ['encrypt', 'decrypt']); akey.type; } function buffer(b: Buffer) { var s:string; s = b.toString() + b.toString('utf8') + b.toString('hex'); b = Buffer.concat([b, Buffer.from([0,1,2])]); b.equals(b); } function timers() { var handle:TimerHandle; handle = setTimeout(() => {}); handle = setTimeout(() => {}, 100); handle = setTimeout((a:string, b:number) => {}, 100, 'foo', 42); handle = setImmediate(() => {}); handle = setImmediate((a:string, b:number) => {}, 'foo', 42); clearTimeout(handle); // Warning: clearTimeout(123); } function text_decoder() { let b:boolean; let s:string; const d = new TextDecoder("utf-8", {fatal: true}); s = d.encoding; b = d.fatal; b = d.ignoreBOM; s += d.decode(new Uint8Array([1, 2, 3]), {stream: true}); s += d.decode(new Uint8Array([4, 5, 6]), {stream: true}); s += d.decode(); s = new TextDecoder().decode(new Uint8Array([206,177,206,178])); } function text_encoder() { let n:number; let s:string; let uint8a:Uint8Array; const e = new TextEncoder(); s = e.encoding; uint8a = e.encode("text to encode"); const res = e.encodeInto("text to encode", uint8a); n = res.read; n = res.written; } function global_functions() { const encodedData = btoa("text to encode"); const decodedData = atob(encodedData); } function njs_object() { njs.dump('asdf'); njs.version != process.argv[1]; typeof njs.version_number == 'number'; njs.on('exit', ()=> {}); } function ngx_object() { ngx.log(ngx.INFO, ngx.conf_prefix); ngx.log(ngx.WARN, Buffer.from(ngx.error_log_path)); ngx.log(ngx.ERR, ngx.version); } function ngx_shared(dict: NgxSharedDict, numeric: NgxSharedDict) { var s:NgxKeyValuePair = dict.items()[0]; var v:number = numeric.incr('foo', 1); } njs-0.8.9/test/ts/tsconfig.json000066400000000000000000000040441474132077100164460ustar00rootroot00000000000000{ "compilerOptions": { "target": "ES5", "module": "es2015", "lib": [ "ES5", // "ES2015.Collection", "ES2015.Core", // "ES2015.Generator", "ES2015.Iterable", // since 0.5.0 "ES2015.Promise", // since 0.6.2 // "ES2015.Proxy", // "ES2015.Reflect", "ES2015.Symbol", // since 0.7.6 "ES2015.Symbol.WellKnown", "ES2016.Array.Include", // "ES2017.Intl", "ES2017.Object", // since 0.2.7 // "ES2017.SharedMemory", "ES2017.String", "ES2017.TypedArrays", // since 0.3.8 // "ES2018.AsyncGenerator", // "ES2018.AsyncIterable", // "ES2018.Intl", "ES2018.Promise", // since 0.3.8 "ES2018.Regexp", // since 0.3.2 // "ES2019.Array", // "ES2019.Intl", // "ES2019.Object", "ES2019.String", // since 0.3.4 "ES2019.Symbol", // "ES2020.BigInt", "ES2020.Date", // "ES2020.Intl", // "ES2020.Number", "ES2020.Promise", // since 0.6.2 // "ES2020.SharedMemory", // "ES2020.String", // "ES2020.Symbol.WellKnown", // "ES2021.Intl", "ES2021.Promise", // since 0.6.2 "ES2021.String" // since 0.7.10 // "ES2021.WeakRef", ], "noEmit": true, "downlevelIteration": true, "strict": true, "noImplicitAny": true, "strictNullChecks": true, "strictFunctionTypes": true, "strictBindCallApply": true, "strictPropertyInitialization": true, "noImplicitThis": true, "alwaysStrict": true, "moduleResolution": "node", "forceConsistentCasingInFileNames": true }, "include": [ "**/*.ts" ], "exclude": [ "./node_modules/**" ], "files": [ "./node_modules/njs-types/ngx_http_js_module.d.ts" ] } njs-0.8.9/test/webcrypto/000077500000000000000000000000001474132077100153255ustar00rootroot00000000000000njs-0.8.9/test/webcrypto/README.rst000066400000000000000000000107541474132077100170230ustar00rootroot00000000000000=============== WebCrypto tests =============== Intro ===== Tests in this folder are expected to be compatible with node.js Tested versions --------------- node: v16.4.0 openssl: OpenSSL 1.1.1f 31 Mar 2020 Keys generation =============== Generating RSA PKCS8/SPKI key files ----------------------------------- .. code-block:: shell openssl genrsa -out rsa.pem 1024 openssl pkcs8 -inform PEM -in rsa.pem -nocrypt -topk8 -outform PEM -out rsa.pkcs8 openssl rsa -in rsa.pkcs8 -pubout > rsa.spki Generating EC PKCS8/SPKI key files ---------------------------------- .. code-block:: shell openssl ecparam -name prime256v1 -genkey -noout -out ec.pem openssl pkcs8 -inform PEM -in ec.pem -nocrypt -topk8 -outform PEM -out ec.pkcs8 openssl ec -in ec.pkcs8 -pubout > ec.spki Encoding ======== Encoding data using RSA-OAEP ---------------------------- .. code-block:: shell echo -n "WAKAWAKA" > text.txt openssl rsautl -inkey key.spki -pubin -in text.txt -out - -oaep -encrypt | \ base64 > text.base64.rsa-oaep.enc Decoding ciphertext using RSA-OAEP ---------------------------------- .. code-block:: shell base64 -d text.base64.rsa-oaep.enc | openssl rsautl -inkey key.pkcs8 -in - -out - -oaep -decrypt WAKAWAKA Encoding data using AES-GCM --------------------------- .. code-block:: shell echo -n "AES-GCM-SECRET-TEXT" > text.txt node ./test/webcrypto/aes_gcm_enc.js '{"in":"text.txt"}' > text.base64.aes-gcm128.enc echo -n "AES-GCM-96-TAG-LENGTH-SECRET-TEXT" > text.txt node ./test/webcrypto/aes_gcm_enc.js '{"in":"text.txt","tagLength":96}' > text.base64.aes-gcm128-96.enc Encoding data using AES-CTR --------------------------- .. code-block:: shell echo -n "AES-CTR-SECRET-TEXT" | \ openssl enc -aes-128-ctr -K 00112233001122330011223300112233 -iv 44556677445566774455667744556677 | \ base64 > text.base64.aes-ctr128.enc Encoding data using AES-CBC --------------------------- .. code-block:: shell echo -n "AES-CBC-SECRET-TEXT" | \ openssl enc -aes-128-cbc -K 00112233001122330011223300112233 -iv 44556677445566774455667744556677 | \ base64 > text.base64.aes-cbc128.enc Signing ======= Signing data using HMAC ----------------------- .. code-block:: shell echo -n "SigneD-TExt" > text.txt openssl dgst -sha256 -mac hmac -macopt hexkey:aabbcc -binary text.txt | \ base64 > test/webcrypto/text.base64.sha256.hmac.sig Signing data using RSASSA-PKCS1-v1_5 ------------------------------------ .. code-block:: shell echo -n "SigneD-TExt" > text.txt openssl dgst -sha256 -sigopt rsa_padding_mode:pkcs1 -sign test/webcrypto/rsa.pkcs8 text.txt | \ base64 > test/webcrypto/text.base64.sha256.pkcs1.sig base64 -d test/webcrypto/text.base64.sha256.pkcs1.sig > text.sha256.pkcs1.sig openssl dgst -sha256 -sigopt rsa_padding_mode:pkcs1 -verify test/webcrypto/rsa.spki \ -signature text.sha256.pkcs1.sig text.txt Verified OK Signing data using RSA-PSS -------------------------- .. code-block:: shell echo -n "SigneD-TExt" > text.txt openssl dgst -sha256 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:32 -sign test/webcrypto/rsa.pkcs8 text.txt | \ base64 > test/webcrypto/text.base64.sha256.rsa-pss.32.sig base64 -d test/webcrypto/text.base64.sha256.rsa-pss.32.sig > text.sha256.rsa-pss.32.sig openssl dgst -sha256 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:32 \ -verify test/webcrypto/rsa.spki -signature text.sha256.rsa-pss.sig text.txt Verified OK Signing data using ECDSA ------------------------ Note: there are two types of ECDSA signatures: ASN.1 and IEEE P1363 Webcrypto requires IEEE P1363, but OpenSSL outputs only ASN.1 variety. To create P1363, we build an auxilary program asn12IEEEP1336 .. code-block:: shell echo -n "SigneD-TExt" > text.txt openssl dgst -sha256 -binary text.txt > text.sha256 openssl pkeyutl -sign -in text.sha256 -inkey test/webcrypto/ec.pkcs8 | \ base64 > test/webcrypto/text.base64.sha256.ecdsa.asn1.sig base64 -d test/webcrypto/text.base64.sha256.ecdsa.asn1.sig > text.sha256.ecdsa.sig openssl pkeyutl -verify -in text.sha256 -pubin -inkey test/webcrypto/ec.spki -sigfile text.sha256.ecdsa.sig Signature Verified Successfully # convert to IEEE P1363 gcc test/webcrypto/asn12ieeep1336.c -lcrypto -o test/webcrypto/asn12ieeep1336 base64 -d test/webcrypto/text.base64.sha256.ecdsa.asn1.sig | ./test/webcrypto/asn12IEEEP1336 | \ base64 > test/webcrypto/text.base64.sha256.ecdsa.sig njs-0.8.9/test/webcrypto/aes.t.mjs000066400000000000000000000110521474132077100170510ustar00rootroot00000000000000/*--- includes: [compatFs.js, compatBuffer.js, compatWebcrypto.js, runTsuite.js, webCryptoUtils.js] flags: [async] ---*/ async function test(params) { let dkey = await crypto.subtle.importKey("raw", params.key, {name: params.name}, false, ["decrypt"]); let ekey = await crypto.subtle.importKey("raw", params.key, {name: params.name}, false, ["encrypt"]); let enc = await crypto.subtle.encrypt(params, ekey, params.data); let plaintext = await crypto.subtle.decrypt(params, dkey, enc); plaintext = Buffer.from(plaintext); if (params.data.compare(plaintext) != 0) { throw Error(`${params.name} encoding/decoding failed length ${data.length}`); } return 'SUCCESS'; } function p(args, default_opts) { let params = Object.assign({}, default_opts, args); params.key = Buffer.from(params.key, "hex"); params.data = Buffer.from(params.data, "hex"); params.iv = Buffer.from(params.iv, "hex"); params.counter = Buffer.from(params.counter, "hex"); switch (params.name) { case "AES-GCM": if (params.additionalData) { params.additionalData = Buffer.from(params.additionalData, "hex"); } break; } return params; } let aes_tsuite = { name: "AES encoding/decoding", skip: () => (!has_buffer() || !has_webcrypto()), T: test, prepare_args: p, opts: { iv: "44556677445566774455667744556677", key: "00112233001122330011223300112233", counter: "44556677445566774455667744556677", length: 64 }, tests: [ { name: "AES-gcm", data: "aa" }, { name: "aes-gcm", data: "aabbcc" }, { name: "AES-GCM", data: "aabbcc", additionalData: "deafbeef"}, { name: "AES-GCM", data: "aabbccdd".repeat(4) }, { name: "AES-GCM", data: "aa", iv: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" }, { name: "AES-GCM", data: "aabbcc", tagLength: 96 }, { name: "AES-GCM", data: "aabbcc", tagLength: 112 }, { name: "AES-GCM", data: "aabbcc", tagLength: 113, exception: "TypeError: AES-GCM Invalid tagLength" }, { name: "AES-GCM", data: "aabbcc", key: "aabbcc", exception: "TypeError: Invalid key length" }, { name: "AES-GCM", data: "aabbcc", key: "001122330011223300112233001122330011223300112233" }, { name: "AES-GCM", data: "aabbccdd".repeat(4096) }, { name: "AES-CTR", data: "aa" }, { name: "AES-CTR", data: "aabbcc" }, { name: "AES-CTR", data: "aabbccdd".repeat(4) }, { name: "AES-CTR", data: "aabbccdd".repeat(4096) }, { name: "AES-CTR", data: "aa", counter: "ffffffffffffffffffffffffffffffff" }, { name: "AES-CTR", data: "aa", counter: "ffffffff", exception: "TypeError: AES-CTR algorithm.counter must be 16 bytes long" }, { name: "AES-CTR", data: "aabbcc", counter: "ffffffffffffffffffffffffffffffff" }, { name: "AES-CTR", data: "aabbccdd".repeat(5), counter: "ffffffffffffffffffffffffffffffff" }, { name: "AES-CTR", data: "aabbccdd".repeat(4096), counter: "fffffffffffffffffffffffffffffff0" }, { name: "AES-CTR", data: "aabbccdd".repeat(4096), counter: "ffffffffffffffffffffffffffffffff" }, { name: "AES-CTR", data: "aabbccdd".repeat(4096), counter: "ffffffffffffffffffffffffffffffff", length: 7, exception: "TypeError: AES-CTR repeated counter" }, { name: "AES-CTR", data: "aabbccdd".repeat(4096), counter: "ffffffffffffffffffffffffffffffff", length: 11 }, { name: "AES-CTR", data: "aabbccdd".repeat(4096), length: 20 }, { name: "AES-CTR", data: "aabbccdd".repeat(4096), length: 24 }, { name: "AES-CTR", data: "aabbcc", key: "001122330011223300112233001122330011223300112233" }, { name: "AES-CTR", data: "aabbccdd", length: 129, exception: "TypeError: AES-CTR algorithm.length must be between 1 and 128" }, { name: "AES-CTR", data: "aabbcc", key: "aabbcc", exception: "TypeError: Invalid key length" }, { name: "AES-CBC", data: "aa" }, { name: "AES-CBC", data: "aabbccdd".repeat(4) }, { name: "AES-CBC", data: "aabbccdd".repeat(4096) }, { name: "AES-CBC", data: "aabbccdd".repeat(5), iv: "ffffffffffffffffffffffffffffffff" }, { name: "AES-CBC", data: "aabbcc", key: "001122330011223300112233001122330011223300112233" }, { name: "AES-CBC", data: "aabbcc", key: "aabbcc", exception: "TypeError: Invalid key length" }, ]}; run([aes_tsuite]) .then($DONE, $DONE); njs-0.8.9/test/webcrypto/aes_decoding.t.mjs000066400000000000000000000060401474132077100207060ustar00rootroot00000000000000/*--- includes: [compatFs.js, compatBuffer.js, compatWebcrypto.js, runTsuite.js, webCryptoUtils.js] flags: [async] ---*/ async function test(params) { let enc = base64decode(fs.readFileSync(`test/webcrypto/${params.file}`)); let key = await crypto.subtle.importKey("raw", params.key, {name: params.name}, false, ["decrypt"]); let plaintext = await crypto.subtle.decrypt(params, key, enc); plaintext = new TextDecoder().decode(plaintext); if (params.expected != plaintext) { throw Error(`${params.name} decoding failed expected: "${params.expected}" vs "${plaintext}"`); } return 'SUCCESS'; } function p(args, default_opts) { let params = Object.assign({}, default_opts, args); params.key = Buffer.from(params.key, "hex"); params.iv = Buffer.from(params.iv, "hex"); params.counter = Buffer.from(params.counter, "hex"); switch (params.name) { case "AES-GCM": if (params.additionalData) { params.additionalData = Buffer.from(params.additionalData, "hex"); } break; } return params; } let aes_tsuite = { name: "AES decoding", skip: () => (!has_buffer() || !has_webcrypto()), T: test, prepare_args: p, opts: { key: "00112233001122330011223300112233", iv: "44556677445566774455667744556677", counter: "44556677445566774455667744556677", length: 64 }, tests: [ { name: "AES-GCM", file: "text.base64.aes-gcm128.enc", expected: "AES-GCM-SECRET-TEXT" }, { name: "AES-GCM", file: "text.base64.aes-gcm128-96.enc", exception: "Error: EVP_DecryptFinal_ex() failed" }, { name: "AES-GCM", file: "text.base64.aes-gcm128-96.enc", tagLength: 96, expected: "AES-GCM-96-TAG-LENGTH-SECRET-TEXT" }, { name: "AES-GCM", file: "text.base64.aes-gcm128-extra.enc", additionalData: "deadbeef", expected: "AES-GCM-ADDITIONAL-DATA-SECRET-TEXT" }, { name: "AES-GCM", file: "text.base64.aes-gcm256.enc", key: "0011223300112233001122330011223300112233001122330011223300112233", expected: "AES-GCM-256-SECRET-TEXT" }, { name: "AES-GCM", file: "text.base64.aes-gcm256.enc", key: "00112233001122330011223300112233001122330011223300112233001122", exception: "TypeError: AES-GCM Invalid key length" }, { name: "AES-CTR", file: "text.base64.aes-ctr128.enc", expected: "AES-CTR-SECRET-TEXT" }, { name: "AES-CTR", file: "text.base64.aes-ctr256.enc", key: "0011223300112233001122330011223300112233001122330011223300112233", expected: "AES-CTR-256-SECRET-TEXT" }, { name: "AES-CBC", file: "text.base64.aes-cbc128.enc", expected: "AES-CBC-SECRET-TEXT" }, { name: "AES-CBC", file: "text.base64.aes-cbc256.enc", key: "0011223300112233001122330011223300112233001122330011223300112233", expected: "AES-CBC-256-SECRET-TEXT" }, ]}; run([aes_tsuite]) .then($DONE, $DONE); njs-0.8.9/test/webcrypto/aes_gcm_enc.js000066400000000000000000000024211474132077100201050ustar00rootroot00000000000000const fs = require('fs'); if (typeof crypto == 'undefined') { crypto = require('crypto').webcrypto; } function parse_options(argv) { let opts = JSON.parse(argv[2] ? argv[2] : "{}"); if (!opts.key) { opts.key = Buffer.from("00112233001122330011223300112233", "hex"); } else { opts.key = Buffer.from(opts.key, "hex"); } if (!opts.iv) { opts.iv = Buffer.from("44556677445566774455667744556677", "hex"); } else { opts.iv = Buffer.from(opts.iv, "hex"); } if (opts.additionalData) { opts.additionalData = Buffer.from(opts.additionalData, "hex"); } if (!opts['in']) { throw Error("opts.in is expected"); } return opts; } (async function main() { let opts = parse_options(process.argv); let stdin = fs.readFileSync(`test/webcrypto/${opts['in']}`); let key = await crypto.subtle.importKey("raw", opts.key, {name: "AES-GCM"}, false, ["encrypt"]); let params = Object.assign(opts); params.name = "AES-GCM"; let enc = await crypto.subtle.encrypt(params, key, stdin); console.log(Buffer.from(enc).toString("base64")); })() .catch(e => { console.log(`exception:${e.stack}`); }) njs-0.8.9/test/webcrypto/asn12ieeep1336.c000066400000000000000000000021711474132077100177430ustar00rootroot00000000000000#include #include #include #include #include int main(int argc, char * argv[]) { int rbytes, sbytes, len, n; ECDSA_SIG *ecSig; unsigned char *p, *end; const unsigned char *start; unsigned char der[512]; unsigned char out[64]; p = der; end = &der[sizeof(der)]; for ( ;; ) { n = read(STDIN_FILENO, der, end - p); if (n == 0) { break; } if ((end - p) == 0) { printf("too large (> 512) der length in stdin"); return EXIT_FAILURE; } p += n; } start = der; ecSig = d2i_ECDSA_SIG(NULL, &start, p - der); if (ecSig == NULL) { printf("d2i_ECDSA_SIG() failed"); return EXIT_FAILURE; } rbytes = BN_num_bytes(ECDSA_SIG_get0_r(ecSig)); sbytes = BN_num_bytes(ECDSA_SIG_get0_s(ecSig)); BN_bn2binpad(ECDSA_SIG_get0_r(ecSig), out, rbytes); BN_bn2binpad(ECDSA_SIG_get0_s(ecSig), &out[32], sbytes); write(STDOUT_FILENO, out, sizeof(out)); return EXIT_SUCCESS; } njs-0.8.9/test/webcrypto/derive.t.mjs000066400000000000000000000102341474132077100175600ustar00rootroot00000000000000/*--- includes: [compatFs.js, compatBuffer.js, compatWebcrypto.js, runTsuite.js, webCryptoUtils.js] flags: [async] ---*/ async function test(params) { let r; let encoder = new TextEncoder(); let keyMaterial = await crypto.subtle.importKey("raw", encoder.encode(params.pass), params.algorithm.name, false, [ "deriveBits", "deriveKey" ]); if (params.derive === "key") { let key = await crypto.subtle.deriveKey(params.algorithm, keyMaterial, params.derivedAlgorithm, true, [ "encrypt", "decrypt" ]); r = await crypto.subtle.encrypt(params.derivedAlgorithm, key, encoder.encode(params.text)); } else { r = await crypto.subtle.deriveBits(params.algorithm, keyMaterial, params.length); } r = Buffer.from(r).toString("hex"); if (params.expected != r) { throw Error(`${params.algorithm.name} failed expected: "${params.expected}" vs "${r}"`); } return "SUCCESS"; } function p(args, default_opts) { let params = Object.assign({}, default_opts); params = merge(params, args); params.algorithm.salt = Buffer.from(params.algorithm.salt, "hex"); params.algorithm.info = Buffer.from(params.algorithm.info, "hex"); params.derivedAlgorithm.iv = Buffer.from(params.derivedAlgorithm.iv, "hex"); return params; } let derive_tsuite = { name: "derive", skip: () => (!has_buffer() || !has_webcrypto()), T: test, prepare_args: p, opts: { text: "secReT", pass: "passW0rd", derive: "key", optional: false, length: 256, algorithm: { name: "PBKDF2", salt: "00112233001122330011223300112233", hash: "SHA-256", info: "deadbeef", iterations: 100000 }, derivedAlgorithm: { name: "AES-GCM", length: 256, iv: "55667788556677885566778855667788" } }, tests: [ { expected: "e7b55c9f9fda69b87648585f76c58109174aaa400cfa" }, { pass: "pass2", expected: "e87d1787f2807ea0e1f7e1cb265b23004c575cf2ad7e" }, { algorithm: { iterations: 10000 }, expected: "5add0059931ed1db1ca24c26dbe4de5719c43ed18a54" }, { algorithm: { hash: "SHA-512" }, expected: "544d64e5e246fdd2ba290ea932b2d80ef411c76139f4" }, { algorithm: { salt: "aabbccddaabbccddaabbccddaabbccdd" }, expected: "5c1304bedf840b1f6f7d1aa804fe870a8f949d762c32" }, { algorithm: { salt: "aabbccddaabbccddaabbccddaabb" }, exception: "TypeError: PBKDF2 algorithm.salt must be at least 16 bytes long" }, { derivedAlgorithm: { length: 128 }, expected: "9e2d7bcc1f21f30ec3c32af9129b64507d086d129f2a" }, { derivedAlgorithm: { length: 32 }, exception: "TypeError: deriveKey \"AES-GCM\" length must be 128 or 256" }, { derivedAlgorithm: { name: "AES-CBC" }, expected: "3ad6523692d44b6a7a90be7c2721786f" }, { derive: "bits", expected: "6458ed6e16b998d4e646422171087be8a1ee34bed463dfcb3dcd30842b1228fe" }, { derive: "bits", pass: "pass2", expected: "ef8f75073fcadfd504d26610c743873e297ad90340c23ddc0e5f6bdb83cbabb2" }, { derive: "bits", algorithm: { salt: "aabbccddaabbccddaabbccddaabbccdd" }, expected: "22ceb295aa25b59c6bc5b383a089bd6999006c03f273ce3614a4fa0d90bd29ae" }, { derive: "bits", algorithm: { hash: "SHA-1" }, expected: "a2fc83498f7d07b4c8180c7ebfec2af0f3a7d6cb08bf8593d41d3c5c1e1c4d67" }, { derive: "bits", algorithm: { hash: "SHA-1" }, length: 128, expected: "a2fc83498f7d07b4c8180c7ebfec2af0" }, { derive: "bits", algorithm: { hash: "SHA-1" }, length: 64, expected: "a2fc83498f7d07b4" }, { algorithm: { name: "HKDF" }, optional: true, expected: "18ea069ee3317d2db02e02f4a228f50dc80d9a2396e6" }, { derive: "bits", algorithm: { name: "HKDF" }, optional: true, expected: "e089c7491711306c69e077aa19fae6bfd2d4a6d240b0d37317d50472d7291a3e" }, ]}; run([derive_tsuite]) .then($DONE, $DONE); njs-0.8.9/test/webcrypto/digest.t.mjs000066400000000000000000000052541474132077100175670ustar00rootroot00000000000000/*--- includes: [compatFs.js, compatBuffer.js, compatWebcrypto.js, runTsuite.js, webCryptoUtils.js] flags: [async] ---*/ async function test(params) { let digest = await crypto.subtle.digest(params.name, params.data); digest = Buffer.from(digest).toString("hex"); if (params.expected != digest) { throw Error(`${params.name} digest failed expected: "${params.expected}" vs "${digest}"`); } return 'SUCCESS'; } function p(args) { let params = Object.assign({}, args); params.data = Buffer.from(params.data, "hex"); return params; } let digest_tsuite = { name: "SHA digest", skip: () => (!has_buffer() || !has_webcrypto()), T: test, prepare_args: p, opts: { }, tests: [ { name: "XXX", data: "", exception: "TypeError: unknown hash name: \"XXX\"" }, { name: "SHA-256", data: "", expected: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" }, { name: "SHA-256", data: "aabbccdd", expected: "8d70d691c822d55638b6e7fd54cd94170c87d19eb1f628b757506ede5688d297" }, { name: "SHA-256", data: "aabbccdd".repeat(4096), expected: "25077ac2e5ba760f015ef34b93bc2b4682b6b48a94d65e21aaf2c8a3a62f6368" }, { name: "SHA-384", data: "", expected: "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b" }, { name: "SHA-384", data: "aabbccdd", expected: "f9616ef3495efbae2f6af1a754620f3034487e9c60f3a9ef8138b5ed55cdd8d18ad9565653a5d68f678bd34cfa6f4490" }, { name: "SHA-384", data: "aabbccdd".repeat(4096), expected: "50502d6e89bc34ecc826e0d56ccba0e010eff7b2b532e3bd627f4c828f6c741bf518fc834559360ccf7770f1b4d655d8" }, { name: "SHA-512", data: "", expected: "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e" }, { name: "SHA-512", data: "aabbccdd", expected: "48e218b30d4ea16305096fe35e84002a0d262eb3853131309423492228980c60238f9eed238285036f22e37c4662e40c80a461000a7aa9a03fb3cb6e4223e83b" }, { name: "SHA-512", data: "aabbccdd".repeat(4096), expected: "9fcd0bd297646e207a2d655feb4ed4473e07ff24560a1e180a5eb2a67824f68affd9c7b5a8f747b9c39201f5f86a0085bb636c6fc34c216d9c10b4d728be096a" }, { name: "SHA-1", data: "", expected: "da39a3ee5e6b4b0d3255bfef95601890afd80709" }, { name: "SHA-1", data: "aabbccdd", expected: "a7b7e9592daa0896db0517bf8ad53e56b1246923" }, { name: "SHA-1", data: "aabbccdd".repeat(4096), expected: "cdea58919606ea9ae078f7595b192b84446f2189" }, ]}; run([digest_tsuite]) .then($DONE, $DONE); njs-0.8.9/test/webcrypto/ec.jwk000066400000000000000000000003171474132077100164320ustar00rootroot00000000000000{"key_ops":["sign"],"ext":true,"kty":"EC","x":"cUUsZRGuVYReGiTQl9MqR7ir6dZIgw-8pM-F0phJWdw","y":"4Nzn_7uNz0AA3U4fhpfVxSD4U5QciGyEoM4r3jC7bjI","d":"E2sW0_4a3QXaSTJ0JKbSUbieKTD1UFtr7i_2CuetP6A","crv":"P-256"} njs-0.8.9/test/webcrypto/ec.pkcs8000066400000000000000000000003611474132077100166660ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgE2sW0/4a3QXaSTJ0 JKbSUbieKTD1UFtr7i/2CuetP6ChRANCAARxRSxlEa5VhF4aJNCX0ypHuKvp1kiD D7ykz4XSmElZ3ODc5/+7jc9AAN1OH4aX1cUg+FOUHIhshKDOK94wu24y -----END PRIVATE KEY----- njs-0.8.9/test/webcrypto/ec.pub.jwk000066400000000000000000000002371474132077100172200ustar00rootroot00000000000000{"key_ops":["verify"],"ext":true,"kty":"EC","x":"cUUsZRGuVYReGiTQl9MqR7ir6dZIgw-8pM-F0phJWdw","y":"4Nzn_7uNz0AA3U4fhpfVxSD4U5QciGyEoM4r3jC7bjI","crv":"P-256"} njs-0.8.9/test/webcrypto/ec.spki000066400000000000000000000002621474132077100166040ustar00rootroot00000000000000-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEcUUsZRGuVYReGiTQl9MqR7ir6dZI gw+8pM+F0phJWdzg3Of/u43PQADdTh+Gl9XFIPhTlByIbISgziveMLtuMg== -----END PUBLIC KEY----- njs-0.8.9/test/webcrypto/ec2.pkcs8000066400000000000000000000003611474132077100167500ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg56d4aW5UtAvpKMfr E0M8OeCN/6ES0Q1Y+DeymtgvZ2ihRANCAATj283yk3EezOOEF6FRRwfeYNyJ65bj 1jwJ8w9N0zMIedRGg0OJHnNc/uoyu6s1M/BtG/vZJ8IJNHUayiVbqxVL -----END PRIVATE KEY----- njs-0.8.9/test/webcrypto/ec2.spki000066400000000000000000000002621474132077100166660ustar00rootroot00000000000000-----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE49vN8pNxHszjhBehUUcH3mDcieuW 49Y8CfMPTdMzCHnURoNDiR5zXP7qMrurNTPwbRv72SfCCTR1GsolW6sVSw== -----END PUBLIC KEY----- njs-0.8.9/test/webcrypto/export.t.mjs000066400000000000000000000554621474132077100176370ustar00rootroot00000000000000/*--- includes: [compatFs.js, compatWebcrypto.js, runTsuite.js, webCryptoUtils.js, compareObjects.js] flags: [async] ---*/ async function load_key(params) { if (params.generate_keys) { let type = params.generate_keys.type; if (params.generate_keys.keys) { return params.generate_keys.keys[type]; } params.generate_keys.keys = await crypto.subtle.generateKey(params.generate_keys.alg, params.generate_keys.extractable, params.generate_keys.usage); return params.generate_keys.keys[type]; } if (params.generate_key) { return await crypto.subtle.generateKey(params.generate_key.alg, params.generate_key.extractable, params.generate_key.usage); } return await crypto.subtle.importKey(params.key.fmt, params.key.key, params.key.alg, params.key.extractable, params.key.usage); } async function test(params) { let key = await load_key(params); let exp = await crypto.subtle.exportKey(params.export.fmt, key); validate_key(key, params); if (params.check && !params.check(exp, params)) { throw Error(`failed check`); } if (exp[Symbol.toStringTag] == 'ArrayBuffer') { let buf = Buffer.from(exp); exp = "ArrayBuffer:" + buf.toString('base64url'); } if (params.expected && !compareObjects(params.expected, exp)) { throw Error(`unexpected export key: ${JSON.stringify(exp)}\n expected: ${JSON.stringify(params.expected)}`); } if (!params.generate_keys && !params.generate_key && (exp.startsWith && !exp.startsWith("ArrayBuffer:"))) { /* Check that exported key can be imported back. */ let imported = await crypto.subtle.importKey(params.export.fmt, exp, params.key.alg, params.key.extractable, params.key.usage); } return 'SUCCESS'; } function p(args, default_opts) { let key, pem; let params = merge({}, default_opts); params = merge(params, args); switch (params.key.fmt) { case "spki": pem = fs.readFileSync(`test/webcrypto/${params.key.key}`); key = pem_to_der(pem, "PUBLIC"); break; case "pkcs8": pem = fs.readFileSync(`test/webcrypto/${params.key.key}`); key = pem_to_der(pem, "PRIVATE"); break; case "jwk": key = load_jwk(params.key.key); break; case "raw": key = Buffer.from(params.key.key, "base64url"); break; default: throw Error("Unknown encoding key format"); } params.key.key = key; return params; } function validate_key(key, params) { let opts; if (params.generate_keys) { opts = params.generate_keys; } else if (params.generate_key) { opts = params.generate_key; } else { opts = params.key; } if (opts.extractable != key.extractable) { throw Error(`unexpected generated key.extractable: ${key.extractable}`); } switch (key.type) { case "secret": /* AesKeyGenParams */ switch (key.algorithm.name) { case "AES-CBC": case "AES-CTR": case "AES-GCM": let length = key.algorithm.length; switch (length) { case 128: case 192: case 256: break; default: throw Error(`unexpected symmetric generated key.algorithm.length: ${length}`); } /* FALLTHROUGH */ case "HMAC": if (!compareObjects(opts.usage, key.usages)) { throw Error(`unexpected symmetric generated key.usages: ${key.usages}`); } break; default: throw Error(`unexpected symmetric generated key.algorithm.name: ${key.algorithm.name}`); } break; case "private": case "public": if (opts.expected && !compareObjects(opts.expected.key_ops, key.usages)) { throw Error(`unexpected asymmetric generated key.usages: ${key.usages}`); } switch (key.algorithm.name) { case "RSA-OAEP": case "RSA-PSS": case "RSASSA-PKCS1-v1_5": /* RsaHashedKeyGenParams */ let mlength = key.algorithm.modulusLength; let pexp = key.algorithm.publicExponent; let hash = key.algorithm.hash.name; switch (mlength) { case 1024: case 2048: case 4096: break; default: throw Error(`unexpected asymmetric generated key.algorithm.modulusLength: ${mlength}`); } if (!compareObjects(new Uint8Array([1, 0, 1]), pexp)) { throw Error(`unexpected asymmetric generated key.algorithm.publicExponent: ${pexp}`); } switch (hash) { case "SHA-1": case "SHA-256": case "SHA-384": case "SHA-512": break; default: throw Error(`unexpected asymmetric generated key.algorithm.hash.name: ${hash}`); } break; case "ECDSA": case "ECDH": /* EcKeyGenParams */ let crv = key.algorithm.namedCurve; switch (crv) { case "P-256": case "P-384": case "P-521": break; default: throw Error(`unexpected asymmetric generated key.algorithm.namedCurve: ${crv}`); } break; default: throw Error(`unexpected asymmetric generated key.algorithm.name: ${key.algorithm.name}`); } break; default: throw Error(`unexpected generated key.type: ${key.type}`); } } function validate_property(exp, p, exp_len) { if (!exp[p]) { throw Error(`"${p}" is not found in ${JSON.stringify(exp)}`); } if (typeof exp[p] != 'string') { throw Error(`"${p}" is not a string`); } let len = exp[p].length; if (len < exp_len - 4 || len > exp_len + 4) { throw Error(`"${p}":"${exp[p]}" length is out of range [${exp_len - 4}, ${exp_len + 4}]`); } } function validate_rsa_jwk(exp, params) { let expected_len = params.generate_keys.alg.modulusLength / 8 * (4 / 3); expected_len = Math.round(expected_len); validate_property(exp, 'n', expected_len); if (params.generate_keys.type == 'privateKey') { validate_property(exp, 'd', expected_len); validate_property(exp, 'p', expected_len / 2); validate_property(exp, 'q', expected_len / 2); validate_property(exp, 'dq', expected_len / 2); validate_property(exp, 'dp', expected_len / 2); validate_property(exp, 'qi', expected_len / 2); } return true; } let rsa_tsuite = { name: "RSA exporting", skip: () => (!has_webcrypto()), T: test, prepare_args: p, opts: { key: { fmt: "spki", key: "rsa.spki", alg: { name: "RSA-OAEP", hash: "SHA-256" }, extractable: true, usage: [ "encrypt" ] }, export: { fmt: "jwk" }, expected: { ext: true, kty: "RSA", e: "AQAB", }, }, tests: [ { expected: { key_ops: [ "encrypt" ], n: "yUmxoJC8VAM5hyYZa-XUBZg1N1ywFMPUpWsF1kaSGed98P3XUgPzgX80wpyzd5qdGuALqnf2lMc7O8PrGBtO5YrvQlI96NX0jUo5bc5wz220ob3AUCeQnTfx-UFqM4pCwjoDSo2PlphJdWgFYymGBaBCJgnENQL9H1N_8_yNiN8", alg: "RSA-OAEP-256" } }, { export: { fmt: "spki" }, expected: "ArrayBuffer:MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJSbGgkLxUAzmHJhlr5dQFmDU3XLAUw9SlawXWRpIZ533w_ddSA_OBfzTCnLN3mp0a4Auqd_aUxzs7w-sYG07liu9CUj3o1fSNSjltznDPbbShvcBQJ5CdN_H5QWozikLCOgNKjY-WmEl1aAVjKYYFoEImCcQ1Av0fU3_z_I2I3wIDAQAB" }, { key: { fmt: "pkcs8", key: "rsa.pkcs8", usage: [ "decrypt" ], alg: { hash: "SHA-512" } }, expected: { key_ops: [ "decrypt" ], n: "yUmxoJC8VAM5hyYZa-XUBZg1N1ywFMPUpWsF1kaSGed98P3XUgPzgX80wpyzd5qdGuALqnf2lMc7O8PrGBtO5YrvQlI96NX0jUo5bc5wz220ob3AUCeQnTfx-UFqM4pCwjoDSo2PlphJdWgFYymGBaBCJgnENQL9H1N_8_yNiN8", alg: "RSA-OAEP-512" } }, { key: { fmt: "pkcs8", key: "rsa.pkcs8", usage: [ "decrypt" ], alg: { hash: "SHA-512" } }, export: { fmt: "pkcs8" }, expected: "ArrayBuffer:MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAMlJsaCQvFQDOYcmGWvl1AWYNTdcsBTD1KVrBdZGkhnnffD911ID84F_NMKcs3eanRrgC6p39pTHOzvD6xgbTuWK70JSPejV9I1KOW3OcM9ttKG9wFAnkJ038flBajOKQsI6A0qNj5aYSXVoBWMphgWgQiYJxDUC_R9Tf_P8jYjfAgMBAAECgYEAj06DQyCopFujYoASi0oWmGEUSjUYO8BsrdSzVCnsLLsuZBwlZ4Peouyw4Hl2IIoYniCyzYwZJzVtC5Dh2MjgcrJTG5nX3FfheuabGl4in0583C51ZYWlVpDvBWw8kJTfXjiKH4z6ZA9dWdT5Y3aH_kOf-znUc7eTvuzISs61x_kCQQD0BJvbLDlvx3u6esW47LLgQNw9ufMSlu5UYBJ4c-qQ5HAeyp4Zt_AaWENhJitjQcLBSxIFIVw7dIN67RnTNK8VAkEA0yvzzgHo_PGYSlVj-M3965AwQF2wTXz82MZHv6EfcCHKuBfCSecr-igqLHhzfynAQjjf39VrXuPuRL23REF1IwJBAKVFydo0peJTljXDmc-aYb0JsSINo9jfaSS0vU3gFOt2DYqNaW-56WGujlRqadCcZbBNjDL1WWbbj4HevTMT59ECQEWaKgzPolykwN5XUNE0DCp1ZwIAH1kbBjfo-sMVt0f9S1TsN9SmBl-4l1X7CY5zU3RATMH5FR-8ns83fM1ZieMCQQDZEQ-dFAhouzJrnCXAXDTCHA9oBtNmnaN-C6G2DmCi79iu7sLHP9vzdgU-CgjrG4YTU5exaRFNOhLwW4hYKs0F" }, { key: { fmt: "pkcs8", key: "rsa.pkcs8", usage: [ "decrypt" ], alg: { hash: "SHA-512" } }, export: { fmt: "spki" }, exception: "TypeError: private key of \"RSA-OAEP\" cannot be exported as SPKI" }, { generate_keys: { alg: { name: "RSA-OAEP", modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-256" }, extractable: true, type: "publicKey", usage: [ "encrypt", "decrypt" ] }, check: validate_rsa_jwk, expected: { kty: "RSA", ext: true, key_ops: [ "encrypt" ], e: "AQAB", alg: "RSA-OAEP-256" } }, { generate_keys: { alg: { name: "RSA-OAEP", modulusLength: 1024, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-1" }, extractable: true, type: "privateKey", usage: [ "encrypt", "decrypt" ] }, check: validate_rsa_jwk, expected: { kty: "RSA", ext: true, key_ops: [ "decrypt" ], e: "AQAB", alg: "RSA-OAEP" } }, { generate_keys: { alg: { name: "RSA-OAEP", modulusLength: 1024, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-1" }, extractable: false, type: "privateKey", usage: [ "encrypt", "decrypt" ] }, check: validate_rsa_jwk, exception: "TypeError: provided key cannot be extracted" }, { generate_keys: { alg: { name: "RSASSA-PKCS1-v1_5", modulusLength: 1024, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-512" }, extractable: true, type: "publicKey", usage: [ "sign", "verify" ] }, check: validate_rsa_jwk, expected: { kty: "RSA", ext: true, key_ops: [ "verify" ], e: "AQAB", alg: "RS512" } }, { generate_keys: { alg: { name: "RSASSA-PKCS1-v1_5", modulusLength: 1024, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-256" }, extractable: true, type: "privateKey", usage: [ "sign", "verify" ] }, check: validate_rsa_jwk, expected: { kty: "RSA", ext: true, key_ops: [ "sign" ], e: "AQAB", alg: "RS256" } }, { generate_keys: { alg: { name: "RSA-PSS", modulusLength: 1024, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-384" }, extractable: true, type: "publicKey", usage: [ "sign", "verify" ] }, check: validate_rsa_jwk, expected: { kty: "RSA", ext: true, key_ops: [ "verify" ], e: "AQAB", alg: "PS384" } }, { generate_keys: { alg: { name: "RSA-PSS", modulusLength: 1024, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-1" }, extractable: true, type: "privateKey", usage: [ "sign", "verify" ] }, check: validate_rsa_jwk, expected: { kty: "RSA", ext: true, key_ops: [ "sign" ], e: "AQAB", alg: "PS1" } }, ]}; function validate_ec_jwk(exp, params) { let crv = params.generate_keys.alg.namedCurve; let expected_len = Number(crv.slice(2)) / 8 * (4 / 3); expected_len = Math.round(expected_len); validate_property(exp, 'x', expected_len); validate_property(exp, 'y', expected_len); if (params.generate_keys.type == 'privateKey') { validate_property(exp, 'd', expected_len); } return true; } let ec_tsuite = { name: "EC exporting", skip: () => (!has_webcrypto()), T: test, prepare_args: p, opts: { key: { fmt: "spki", key: "ec.spki", alg: { name: "ECDSA", namedCurve: "P-256" }, extractable: true, usage: [ "verify" ] }, export: { fmt: "jwk" }, expected: { ext: true, kty: "EC" }, }, tests: [ { expected: { key_ops: [ "verify" ], x: "cUUsZRGuVYReGiTQl9MqR7ir6dZIgw-8pM-F0phJWdw", y: "4Nzn_7uNz0AA3U4fhpfVxSD4U5QciGyEoM4r3jC7bjI", crv: "P-256" } }, { key: { fmt: "pkcs8", key: "ec.pkcs8", usage: [ "sign" ] }, expected: { key_ops: [ "sign" ], x: "cUUsZRGuVYReGiTQl9MqR7ir6dZIgw-8pM-F0phJWdw", y: "4Nzn_7uNz0AA3U4fhpfVxSD4U5QciGyEoM4r3jC7bjI", d: "E2sW0_4a3QXaSTJ0JKbSUbieKTD1UFtr7i_2CuetP6A", crv: "P-256" } }, { key: { fmt: "pkcs8", key: "ec.pkcs8", usage: [ "sign" ] }, export: { fmt: "pkcs8" }, expected: "ArrayBuffer:MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgE2sW0_4a3QXaSTJ0JKbSUbieKTD1UFtr7i_2CuetP6ChRANCAARxRSxlEa5VhF4aJNCX0ypHuKvp1kiDD7ykz4XSmElZ3ODc5_-7jc9AAN1OH4aX1cUg-FOUHIhshKDOK94wu24y" }, { export: { fmt: "pkcs8" }, exception: "TypeError: public key of \"ECDSA\" cannot be exported as PKCS8" }, { export: { fmt: "raw" }, expected: "ArrayBuffer:BHFFLGURrlWEXhok0JfTKke4q-nWSIMPvKTPhdKYSVnc4Nzn_7uNz0AA3U4fhpfVxSD4U5QciGyEoM4r3jC7bjI" }, { key: { fmt: "pkcs8", key: "ec.pkcs8", usage: [ "sign" ] }, export: { fmt: "raw" }, exception: "TypeError: private key of \"ECDSA\" cannot be exported in \"raw\" format" }, { generate_keys: { alg: { name: "ECDSA", namedCurve: "P-256" }, extractable: true, type: "publicKey", usage: [ "sign", "verify" ] }, check: validate_ec_jwk, expected: { kty: "EC", ext: true, key_ops: [ "verify" ], crv: "P-256" } }, { generate_keys: { alg: { name: "ECDSA", namedCurve: "P-384" }, extractable: true, type: "privateKey", usage: [ "sign", "verify" ] }, check: validate_ec_jwk, expected: { kty: "EC", ext: true, key_ops: [ "sign" ], crv: "P-384" } }, ]}; function validate_hmac_jwk(exp, params) { let hash = params.generate_key.alg.hash; let expected_len = Number(hash.slice(2)) / 8 * (4 / 3); expected_len = Math.round(expected_len); validate_property(exp, 'k', expected_len); return true; } let hmac_tsuite = { name: "HMAC exporting", skip: () => (!has_webcrypto()), T: test, prepare_args: p, opts: { key: { fmt: "raw", key: "c2VjcmV0LUtleTE", alg: { name: "HMAC", hash: "SHA-256" }, extractable: true, usage: [ "sign", "verify" ] }, export: { fmt: "jwk" }, expected: { kty: "oct", ext: true }, }, tests: [ { expected: { key_ops: [ "sign", "verify" ], alg: "HS256", k: "c2VjcmV0LUtleTE" } }, { export: { fmt: "raw" }, expected: "ArrayBuffer:c2VjcmV0LUtleTE" }, { export: { fmt: "spki" }, exception: "TypeError: unsupported key fmt \"spki\" for \"HMAC\"" }, { export: { fmt: "pksc8" }, exception: "TypeError: unsupported key fmt \"pksc8\" for \"HMAC\"" }, { key: { key: "cDBzc3dE", alg: { hash: "SHA-384" } }, expected: { key_ops: [ "sign", "verify" ], alg: "HS384", k: "cDBzc3dE" } }, { key: { extractable: false }, exception: "TypeError: provided key cannot be extracted" }, { key: { fmt: "jwk", key: { kty: "oct", ext: true, k: "c2VjcmV0LUtleTE", alg: "HS256" } }, expected: { key_ops: [ "sign", "verify" ], alg: "HS256", k: "c2VjcmV0LUtleTE" } }, { key: { fmt: "jwk", alg: { hash: "SHA-512" }, key: { kty: "oct", ext: true, k: "c2VjcmV0LUtleTE", alg: "HS512" } }, expected: { key_ops: [ "sign", "verify" ], alg: "HS512", k: "c2VjcmV0LUtleTE" } }, { key: { fmt: "jwk", key: { kty: "oct", ext: true, k: "c2VjcmV0LUtleTE", alg: "HS256" }, alg: { hash: "SHA-384" } }, exception: "TypeError: HMAC JWK hash mismatch" }, { generate_key: { alg: { name: "HMAC", hash: "SHA-256" }, extractable: true, usage: [ "sign", "verify" ] }, check: validate_hmac_jwk, expected: { key_ops: [ "sign", "verify" ], alg: "HS256" } }, { generate_key: { alg: { name: "HMAC", hash: "SHA-1" }, extractable: true, usage: [ "verify" ] }, check: validate_hmac_jwk, expected: { key_ops: [ "verify" ], alg: "HS1" } }, { generate_key: { alg: { name: "HMAC", hash: "SHA-384" }, extractable: true, usage: [ "sign" ] }, check: validate_hmac_jwk, expected: { key_ops: [ "sign" ], alg: "HS384" } }, { generate_key: { alg: { name: "HMAC", hash: "SHA-512" }, extractable: true, usage: [ "sign" ] }, check: validate_hmac_jwk, expected: { key_ops: [ "sign" ], alg: "HS512" } }, ]}; function validate_aes_jwk(exp, params) { let expected_len = params.generate_key.alg.length; expected_len = expected_len / 8 * (4 / 3); expected_len = Math.round(expected_len); validate_property(exp, 'k', expected_len); return true; } let aes_tsuite = { name: "AES exporting", skip: () => (!has_webcrypto()), T: test, prepare_args: p, opts: { key: { fmt: "raw", key: "ABEiMwARIjMAESIzABEiMw", alg: { name: "AES-GCM" }, extractable: true, usage: [ "encrypt" ] }, export: { fmt: "jwk" }, expected: { kty: "oct", ext: true }, }, tests: [ { expected: { key_ops: [ "encrypt" ], alg: "A128GCM", k: "ABEiMwARIjMAESIzABEiMw" } }, { export: { fmt: "raw" }, expected: "ArrayBuffer:ABEiMwARIjMAESIzABEiMw" }, { key: { key: "ABEiMwARIjMAESIzABEiMwARIjMAESIz", alg: { name: "AES-CBC" }, usage: [ "decrypt" ] }, expected: { key_ops: [ "decrypt" ], alg: "A192CBC", k: "ABEiMwARIjMAESIzABEiMwARIjMAESIz" } }, { key: { key: "ABEiMwARIjMAESIzABEiMwARIjMAESIz", alg: { name: "AES-CBC" }, usage: [ "decrypt" ] }, export: { fmt: "raw" }, expected: "ArrayBuffer:ABEiMwARIjMAESIzABEiMwARIjMAESIz" }, { key: { key: "ABEiMwARIjMAESIzABEiMwARIjMAESIzABEiMwARIjM", alg: { name: "AES-CTR" }, usage: [ "decrypt" ] }, expected: { key_ops: [ "decrypt" ], alg: "A256CTR", k: "ABEiMwARIjMAESIzABEiMwARIjMAESIzABEiMwARIjM" } }, { key: { key: "ABEiMwARIjMAESIzABEiMwARIjMAESIzABEiMwARIjM", alg: { name: "AES-CTR" }, usage: [ "decrypt" ] }, export: { fmt: "raw" }, expected: "ArrayBuffer:ABEiMwARIjMAESIzABEiMwARIjMAESIzABEiMwARIjM" }, { generate_key: { alg: { name: "AES-GCM", length: 128 }, extractable: true, usage: [ "encrypt" ] }, check: validate_aes_jwk, expected: { key_ops: [ "encrypt" ], alg: "A128GCM" } }, { generate_key: { alg: { name: "AES-CTR", length: 192 }, extractable: true, usage: [ "decrypt" ] }, check: validate_aes_jwk, expected: { key_ops: [ "decrypt" ], alg: "A192CTR" } }, { generate_key: { alg: { name: "AES-CBC", length: 256 }, extractable: true, usage: [ "decrypt" ] }, check: validate_aes_jwk, expected: { key_ops: [ "decrypt" ], alg: "A256CBC" } }, { generate_key: { alg: { name: "AES-GCM", length: 128 }, extractable: false, usage: [ "decrypt" ] }, exception: "TypeError: provided key cannot be extracted" }, { generate_key: { alg: { name: "AES-GCM" }, extractable: false, usage: [ "decrypt" ] }, exception: "TypeError: length for \"AES-GCM\" key should be one of 128, 192, 256" }, { generate_key: { alg: { name: "AES-GCM", length: 25 }, extractable: false, usage: [ "decrypt" ] }, exception: "TypeError: length for \"AES-GCM\" key should be one of 128, 192, 256" }, ]}; run([ rsa_tsuite, ec_tsuite, hmac_tsuite, aes_tsuite, ]) .then($DONE, $DONE); njs-0.8.9/test/webcrypto/rsa.dec.jwk000066400000000000000000000016001474132077100173560ustar00rootroot00000000000000{"key_ops":["decrypt"],"ext":true,"kty":"RSA","n":"yUmxoJC8VAM5hyYZa-XUBZg1N1ywFMPUpWsF1kaSGed98P3XUgPzgX80wpyzd5qdGuALqnf2lMc7O8PrGBtO5YrvQlI96NX0jUo5bc5wz220ob3AUCeQnTfx-UFqM4pCwjoDSo2PlphJdWgFYymGBaBCJgnENQL9H1N_8_yNiN8","e":"AQAB","d":"j06DQyCopFujYoASi0oWmGEUSjUYO8BsrdSzVCnsLLsuZBwlZ4Peouyw4Hl2IIoYniCyzYwZJzVtC5Dh2MjgcrJTG5nX3FfheuabGl4in0583C51ZYWlVpDvBWw8kJTfXjiKH4z6ZA9dWdT5Y3aH_kOf-znUc7eTvuzISs61x_k","p":"9ASb2yw5b8d7unrFuOyy4EDcPbnzEpbuVGASeHPqkORwHsqeGbfwGlhDYSYrY0HCwUsSBSFcO3SDeu0Z0zSvFQ","q":"0yvzzgHo_PGYSlVj-M3965AwQF2wTXz82MZHv6EfcCHKuBfCSecr-igqLHhzfynAQjjf39VrXuPuRL23REF1Iw","dp":"pUXJ2jSl4lOWNcOZz5phvQmxIg2j2N9pJLS9TeAU63YNio1pb7npYa6OVGpp0JxlsE2MMvVZZtuPgd69MxPn0Q","dq":"RZoqDM-iXKTA3ldQ0TQMKnVnAgAfWRsGN-j6wxW3R_1LVOw31KYGX7iXVfsJjnNTdEBMwfkVH7yezzd8zVmJ4w","qi":"2REPnRQIaLsya5wlwFw0whwPaAbTZp2jfguhtg5gou_Yru7Cxz_b83YFPgoI6xuGE1OXsWkRTToS8FuIWCrNBQ","alg":"RS256"} njs-0.8.9/test/webcrypto/rsa.enc.pub.jwk000066400000000000000000000003721474132077100201620ustar00rootroot00000000000000{"key_ops":["encrypt"],"ext":true,"kty":"RSA","n":"yUmxoJC8VAM5hyYZa-XUBZg1N1ywFMPUpWsF1kaSGed98P3XUgPzgX80wpyzd5qdGuALqnf2lMc7O8PrGBtO5YrvQlI96NX0jUo5bc5wz220ob3AUCeQnTfx-UFqM4pCwjoDSo2PlphJdWgFYymGBaBCJgnENQL9H1N_8_yNiN8","e":"AQAB","alg":"RS256"} njs-0.8.9/test/webcrypto/rsa.jwk000066400000000000000000000015751474132077100166370ustar00rootroot00000000000000{"key_ops":["sign"],"ext":true,"kty":"RSA","n":"yUmxoJC8VAM5hyYZa-XUBZg1N1ywFMPUpWsF1kaSGed98P3XUgPzgX80wpyzd5qdGuALqnf2lMc7O8PrGBtO5YrvQlI96NX0jUo5bc5wz220ob3AUCeQnTfx-UFqM4pCwjoDSo2PlphJdWgFYymGBaBCJgnENQL9H1N_8_yNiN8","e":"AQAB","d":"j06DQyCopFujYoASi0oWmGEUSjUYO8BsrdSzVCnsLLsuZBwlZ4Peouyw4Hl2IIoYniCyzYwZJzVtC5Dh2MjgcrJTG5nX3FfheuabGl4in0583C51ZYWlVpDvBWw8kJTfXjiKH4z6ZA9dWdT5Y3aH_kOf-znUc7eTvuzISs61x_k","p":"9ASb2yw5b8d7unrFuOyy4EDcPbnzEpbuVGASeHPqkORwHsqeGbfwGlhDYSYrY0HCwUsSBSFcO3SDeu0Z0zSvFQ","q":"0yvzzgHo_PGYSlVj-M3965AwQF2wTXz82MZHv6EfcCHKuBfCSecr-igqLHhzfynAQjjf39VrXuPuRL23REF1Iw","dp":"pUXJ2jSl4lOWNcOZz5phvQmxIg2j2N9pJLS9TeAU63YNio1pb7npYa6OVGpp0JxlsE2MMvVZZtuPgd69MxPn0Q","dq":"RZoqDM-iXKTA3ldQ0TQMKnVnAgAfWRsGN-j6wxW3R_1LVOw31KYGX7iXVfsJjnNTdEBMwfkVH7yezzd8zVmJ4w","qi":"2REPnRQIaLsya5wlwFw0whwPaAbTZp2jfguhtg5gou_Yru7Cxz_b83YFPgoI6xuGE1OXsWkRTToS8FuIWCrNBQ","alg":"RS256"} njs-0.8.9/test/webcrypto/rsa.pkcs8000066400000000000000000000016241474132077100170670ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAMlJsaCQvFQDOYcm GWvl1AWYNTdcsBTD1KVrBdZGkhnnffD911ID84F/NMKcs3eanRrgC6p39pTHOzvD 6xgbTuWK70JSPejV9I1KOW3OcM9ttKG9wFAnkJ038flBajOKQsI6A0qNj5aYSXVo BWMphgWgQiYJxDUC/R9Tf/P8jYjfAgMBAAECgYEAj06DQyCopFujYoASi0oWmGEU SjUYO8BsrdSzVCnsLLsuZBwlZ4Peouyw4Hl2IIoYniCyzYwZJzVtC5Dh2MjgcrJT G5nX3FfheuabGl4in0583C51ZYWlVpDvBWw8kJTfXjiKH4z6ZA9dWdT5Y3aH/kOf +znUc7eTvuzISs61x/kCQQD0BJvbLDlvx3u6esW47LLgQNw9ufMSlu5UYBJ4c+qQ 5HAeyp4Zt/AaWENhJitjQcLBSxIFIVw7dIN67RnTNK8VAkEA0yvzzgHo/PGYSlVj +M3965AwQF2wTXz82MZHv6EfcCHKuBfCSecr+igqLHhzfynAQjjf39VrXuPuRL23 REF1IwJBAKVFydo0peJTljXDmc+aYb0JsSINo9jfaSS0vU3gFOt2DYqNaW+56WGu jlRqadCcZbBNjDL1WWbbj4HevTMT59ECQEWaKgzPolykwN5XUNE0DCp1ZwIAH1kb Bjfo+sMVt0f9S1TsN9SmBl+4l1X7CY5zU3RATMH5FR+8ns83fM1ZieMCQQDZEQ+d FAhouzJrnCXAXDTCHA9oBtNmnaN+C6G2DmCi79iu7sLHP9vzdgU+CgjrG4YTU5ex aRFNOhLwW4hYKs0F -----END PRIVATE KEY----- njs-0.8.9/test/webcrypto/rsa.pkcs8.broken000066400000000000000000000016221474132077100203440ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAMlJsaCQvFQDOYcm GWvl1AWYNTdcsBTD1KVrBdZGkhnnffD911ID84F/NMKcs3eanRrgC6p39pTHOzvD 6xgbTuWK70JSPejV9I1KOW3OcM9ttKG9wFAnkJ038flBajOKQsI6A0qNj5aYSXVo BWMphgWgQiYJxDUC/R9Tf/P8jYjfAgMBAAECgYEAj06DQyCopFujYoASi0oWmGEU SjUYO8BsrdSzVCnsLLsuZBwlZ4Peouyw4Hl2IIoYniCyzYwZJzVtC5Dh2MjgcrJT G5nX3FfheuabGl4in0583C51ZYWlVpDvBWw8kJTfXjiKH4z6ZA9dWdT5Y3aH/kOf +znUc7eTvuzISs61x/kCQQD0BJvbLDlvx3u6esW47LLgQNw9ufMSlu5UYBJ4c+qQ 5HAeyp4Zt/AaWENhJitjQcLBSxIFIVw7dIN67RnTNK8VAkEA0yvzzgHo/PGYSlVj +M3965AwQF2wTXz82MZHv6EfcCHKuBfCSecr+igqLHhzfynAQjjf39VrXuPuRL23 REF1IwJBAKVFydo0peJTljXDmc+aYb0JsSINo9jfaSS0vU3gFOt2DYqNaW+56WGu jlRqadCcZbBNjDL1WWbbj4HevTMT59ECQEWaKgzPolykwN5XUNE0DCp1ZwIAH1kb Bjfo+sMVt0f9S1TsN9SmBl+4l1X7CY5zU3RATMH5FR+8ns83fM1ZieMCQQDZEQ+d FAhouzJrnCXAXDTCHA9oBtNmnaN+C6G2DmCi79iu7sLHP9vzdgU+CgjrG4YTU5ex aRFNOhLwW4hYKs -----END PRIVATE KEY----- njs-0.8.9/test/webcrypto/rsa.pub.jwk000066400000000000000000000003711474132077100174150ustar00rootroot00000000000000{"key_ops":["verify"],"ext":true,"kty":"RSA","n":"yUmxoJC8VAM5hyYZa-XUBZg1N1ywFMPUpWsF1kaSGed98P3XUgPzgX80wpyzd5qdGuALqnf2lMc7O8PrGBtO5YrvQlI96NX0jUo5bc5wz220ob3AUCeQnTfx-UFqM4pCwjoDSo2PlphJdWgFYymGBaBCJgnENQL9H1N_8_yNiN8","e":"AQAB","alg":"RS256"} njs-0.8.9/test/webcrypto/rsa.spki000066400000000000000000000004201474132077100167760ustar00rootroot00000000000000-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJSbGgkLxUAzmHJhlr5dQFmDU3 XLAUw9SlawXWRpIZ533w/ddSA/OBfzTCnLN3mp0a4Auqd/aUxzs7w+sYG07liu9C Uj3o1fSNSjltznDPbbShvcBQJ5CdN/H5QWozikLCOgNKjY+WmEl1aAVjKYYFoEIm CcQ1Av0fU3/z/I2I3wIDAQAB -----END PUBLIC KEY----- njs-0.8.9/test/webcrypto/rsa.spki.broken000066400000000000000000000004171474132077100202630ustar00rootroot00000000000000-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJSbGgkLxUAzmHJhlr5dQFmDU3 XLAUw9SlawXWRpIZ533w/ddSA/OBfzTCnLN3mp0a4Auqd/aUxzs7w+sYG07liu9C Uj3o1fSNSjltznDPbbShvcBQJ5CdN/H5QWozikLCOgNKjY+WmEl1aAVjKYYFoEIm CcQ1Av0fU3/z/I2I3IDAQAB -----END PUBLIC KEY----- njs-0.8.9/test/webcrypto/rsa.t.mjs000066400000000000000000000116101474132077100170660ustar00rootroot00000000000000/*--- includes: [compatFs.js, compatBuffer.js, compatWebcrypto.js, runTsuite.js, webCryptoUtils.js] flags: [async] ---*/ async function encrypt_key(params) { if (params.generate_keys) { if (params.generate_keys.publicKey) { return params.generate_keys.publicKey; } params.generate_keys = await crypto.subtle.generateKey(params.generate_keys.alg, params.generate_keys.extractable, params.generate_keys.usage); return params.generate_keys.publicKey; } return await crypto.subtle.importKey(params.enc.fmt, params.enc.key, { name: "RSA-OAEP", hash:params.enc.hash }, false, ["encrypt"]); } async function decrypt_key(params) { if (params.generate_keys) { if (params.generate_keys.privateKey) { return params.generate_keys.privateKey; } params.generate_keys = await crypto.subtle.generateKey(params.generate_keys.alg, params.generate_keys.extractable, params.generate_keys.usage); return params.generate_keys.privateKey; } return await crypto.subtle.importKey(params.dec.fmt, params.dec.key, { name: "RSA-OAEP", hash:params.dec.hash }, false, ["decrypt"]); } async function test(params) { let enc_key = await encrypt_key(params); let dec_key = await decrypt_key(params); let enc = await crypto.subtle.encrypt({name: "RSA-OAEP"}, enc_key, params.data); let plaintext = await crypto.subtle.decrypt({name: "RSA-OAEP"}, dec_key, enc); plaintext = Buffer.from(plaintext); if (params.data.compare(plaintext) != 0) { throw Error(`RSA-OAEP encoding/decoding failed expected: "${params.data}" vs "${plaintext}"`); } return 'SUCCESS'; } function p(args, default_opts) { let key; let params = merge({}, default_opts); params = merge(params, args); switch (params.enc.fmt) { case "spki": let pem = fs.readFileSync(`test/webcrypto/${params.enc.key}`); key = pem_to_der(pem, "PUBLIC"); break; case "jwk": key = load_jwk(params.enc.key); break; default: throw Error("Unknown encoding key format"); } params.enc.key = key; switch (params.dec.fmt) { case "pkcs8": let pem = fs.readFileSync(`test/webcrypto/${params.dec.key}`); key = pem_to_der(pem, "PRIVATE"); break; case "jwk": key = load_jwk(params.dec.key); break; default: throw Error("Unknown decoding key format"); } params.dec.key = key; params.data = Buffer.from(params.data, "hex"); return params; } let rsa_tsuite = { name: "RSA-OAEP encoding/decoding", skip: () => (!has_buffer() || !has_webcrypto()), T: test, prepare_args: p, opts: { enc: { fmt: "spki", key: "rsa.spki", hash: "SHA-256" }, dec: { fmt: "pkcs8", key: "rsa.pkcs8", hash: "SHA-256" }, }, tests: [ { data: "aabbcc" }, { data: "aabbccdd".repeat(4) }, { data: "aabbccdd".repeat(7) }, { data: "aabbcc", generate_keys: { alg: { name: "RSA-OAEP", modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-256" }, extractable: true, usage: [ "encrypt", "decrypt" ] }, expected: true }, { data: "aabbcc", enc: { hash: "SHA-1" }, dec: { hash: "SHA-1" } }, { data: "aabbccdd".repeat(4), enc: { hash: "SHA-1" }, dec: { hash: "SHA-1" } }, { data: "aabbccdd".repeat(7), enc: { hash: "SHA-1" }, dec: { hash: "SHA-1" } }, { data: "aabbcc", enc: { hash: "SHA-384" }, dec: { hash: "SHA-384" } }, { data: "aabbccdd".repeat(4), enc: { hash: "SHA-384" }, dec: { hash: "SHA-384" } }, { data: "aabbccdd".repeat(7), enc: { hash: "SHA-384" }, dec: { hash: "SHA-384" } }, { data: "aabbcc", enc: { hash: "SHA-256" }, dec: { hash: "SHA-384" }, exception: "Error: EVP_PKEY_decrypt() failed" }, { data: "aabbcc", enc: { hash: "XXX" }, exception: "TypeError: unknown hash name: \"XXX\"" }, { data: "aabbcc", dec: { key: "rsa.spki.broken" }, exception: "Error: d2i_PUBKEY() failed" }, { data: "aabbcc", dec: { key: "rsa2.spki" }, exception: "Error: EVP_PKEY_decrypt() failed" }, { data: "aabbcc", enc: { fmt: "jwk", key: "rsa.enc.pub.jwk" }, dec: { fmt: "jwk", key: "rsa.dec.jwk" } }, ]}; run([rsa_tsuite]) .then($DONE, $DONE); njs-0.8.9/test/webcrypto/rsa2.pkcs8000066400000000000000000000016241474132077100171510ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANUukQQW3CVlMsS6 VaRnBhExg2gTCB6tvevFupCpq6sEuX5X6+4/QUTt4/wyWeMn5amaNAjGmDtsmeKX 85MGHPuKceLhjToJT5JGFFDwVB6pNyqBuRzEHYsvQz1TlqcSdo7U/YaQtqkEZsA/ ZAjBSR4lP9ggOBpEq+bBs+2GaZZFAgMBAAECgYB8HMdK1TBICTncdQtlUqGiouv5 TJM+oTJgMNbkYBPU1kRUPUXbiDIsuj8wVfQlHtZDvsYqkcyRVDHnTUX+w+FctIox OU+3bKEZ/winaO3znvPVdy59/evpvQ0rnAGsxYBmyfZTqQgCxX+nMqAsLIplzORM 5zAOawEQfRyHGETHQQJBAPe6YoMHMM/ZVpQ0xwQasbQahcL2GCD4Hwv3neGEKZVz Aos89/qkA+78hg6OxChxSJFxK0p35lu5TqF0QFX3MNUCQQDcTOBFZaGMHnnL+2uc tTmjKMjt47Es5G/NLg5z4cLBeeaz2St8ISbtvuVtl3K9cjeNy4J30zvF5puZrTvw /wexAkEAsfzRcNEGyh+erCdrYlCHox53QsesOGvtapzDa9eYRQ94IXBxvzx+swPu kaET4Pbbq9wCvaN9+CMhErHC08Eh7QJAAQWaRLgj97JsfjW8Wg29JrSZugDEYaDt o9YC2ybA8ITQPSVUvk6pD5FDHy8EqTxOZan8APJJ5LEdJ6lWDdghAQJBANKmYYmk OcQtU29dwuzPkwZWFdl6mhwZdcOrFcjq2pSfxKjBfygXXykscF8pHeQsjPrKK3u6 HNED24fqNlbYHi8= -----END PRIVATE KEY----- njs-0.8.9/test/webcrypto/rsa2.spki000066400000000000000000000004201474132077100170600ustar00rootroot00000000000000-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVLpEEFtwlZTLEulWkZwYRMYNo Ewgerb3rxbqQqaurBLl+V+vuP0FE7eP8MlnjJ+WpmjQIxpg7bJnil/OTBhz7inHi 4Y06CU+SRhRQ8FQeqTcqgbkcxB2LL0M9U5anEnaO1P2GkLapBGbAP2QIwUkeJT/Y IDgaRKvmwbPthmmWRQIDAQAB -----END PUBLIC KEY----- njs-0.8.9/test/webcrypto/rsa_decoding.t.mjs000066400000000000000000000025431474132077100207270ustar00rootroot00000000000000/*--- includes: [compatFs.js, compatBuffer.js, compatWebcrypto.js, runTsuite.js, webCryptoUtils.js] flags: [async] ---*/ async function test(params) { if (!has_buffer() || !has_webcrypto()) { return 'SKIPPED'; } let pem = fs.readFileSync(`test/webcrypto/${params.pem}`); let enc = base64decode(fs.readFileSync(`test/webcrypto/${params.src}`)); let key = await crypto.subtle.importKey("pkcs8", pem_to_der(pem, "PRIVATE"), {name:"RSA-OAEP", hash:"SHA-1"}, false, ["decrypt"]); let plaintext = await crypto.subtle.decrypt({name: "RSA-OAEP"}, key, enc); plaintext = new TextDecoder().decode(plaintext); if (params.expected != plaintext) { throw Error(`RSA-OAEP decoding failed expected: "${params.expected}" vs "${plaintext}"`); } return "SUCCESS"; } let rsa_tsuite = { name: "RSA-OAEP decoding", T: test, prepare_args: (v) => v, opts: { }, tests: [ { pem: "rsa.pkcs8", src: "text.base64.rsa-oaep.enc", expected: "WAKAWAKA" }, { pem: "ec.pkcs8", src: "text.base64.rsa-oaep.enc", exception: "Error: RSA key is not found" }, { pem: "rsa.pkcs8.broken", src: "text.base64.rsa-oaep.enc", exception: "Error: d2i_PKCS8_PRIV_KEY_INFO_bio() failed" }, ]}; run([rsa_tsuite]) .then($DONE, $DONE); njs-0.8.9/test/webcrypto/sign.t.mjs000066400000000000000000000655241474132077100172560ustar00rootroot00000000000000/*--- includes: [compatFs.js, compatBuffer.js, compatWebcrypto.js, runTsuite.js, webCryptoUtils.js] flags: [async] ---*/ async function sign_key(params) { if (params.generate_keys) { if (params.generate_keys.privateKey) { return params.generate_keys.privateKey; } params.generate_keys = await crypto.subtle.generateKey(params.generate_keys.alg, params.generate_keys.extractable, params.generate_keys.usage); return params.generate_keys.privateKey; } return await crypto.subtle.importKey(params.sign_key.fmt, params.sign_key.key, params.import_alg, params.sign_key.extractable, params.sign_key.key_ops); } async function verify_key(params) { if (params.generate_keys) { if (params.generate_keys.publicKey) { return params.generate_keys.publicKey; } params.generate_keys = await crypto.subtle.generateKey(params.generate_keys.alg, params.generate_keys.extractable, params.generate_keys.usage); return params.generate_keys.publicKey; } return await crypto.subtle.importKey(params.verify_key.fmt, params.verify_key.key, params.import_alg, false, [ "verify" ]); } async function test(params) { let encoder = new TextEncoder(); let skey = await sign_key(params); let sig = await crypto.subtle.sign(params.sign_alg, skey, encoder.encode(params.text)) .catch (e => { if (e.toString().startsWith("Error: EVP_PKEY_CTX_set_signature_md() failed")) { /* Red Hat Enterprise Linux: SHA-1 is disabled */ return "SKIPPED"; } }); if (sig == "SKIPPED") { return sig; } if (params.verify) { let vkey = await verify_key(params); let r = await crypto.subtle.verify(params.sign_alg, vkey, sig, encoder.encode(params.text)); if (params.expected !== r) { throw Error(`${params.sign_alg.name} failed expected: "${params.expected}" vs "${r}"`); } if (params.expected === true) { let broken_sig = Buffer.concat([Buffer.from(sig)]); broken_sig[8] = 255 - broken_sig[8]; r = await crypto.subtle.verify(params.sign_alg, vkey, broken_sig, encoder.encode(params.text)); if (r !== false) { throw Error(`${params.sign_alg.name} BROKEN SIG failed expected: "false" vs "${r}"`); } let broken_text = encoder.encode(params.text); broken_text[0] = 255 - broken_text[0]; r = await crypto.subtle.verify(params.sign_alg, vkey, sig, broken_text); if (r !== false) { throw Error(`${params.sign_alg.name} BROKEN TEXT failed expected: "false" vs "${r}"`); } } } else { sig = Buffer.from(sig).toString("hex"); if (params.expected !== sig) { throw Error(`${params.sign_alg.name} failed expected: "${params.expected}" vs "${sig}"`); } } return "SUCCESS"; } function p(args, default_opts) { let key; let encoder = new TextEncoder(); let params = merge({}, default_opts); params = merge(params, args); switch (params.sign_key.fmt) { case "pkcs8": let pem = fs.readFileSync(`test/webcrypto/${params.sign_key.key}`); key = pem_to_der(pem, "PRIVATE"); break; case "jwk": key = load_jwk(params.sign_key.key); break; case "raw": key = Buffer.from(params.sign_key.key, "base64url"); break; default: throw Error("Unknown sign key format"); } params.sign_key.key = key; params.sign_key.extractable = Boolean(params.sign_key.extractable); if (!params.sign_key.key_ops) { params.sign_key.key_ops = [ "sign" ]; } switch (params.verify_key.fmt) { case "spki": let pem = fs.readFileSync(`test/webcrypto/${params.verify_key.key}`); key = pem_to_der(pem, "PUBLIC"); break; case "jwk": key = load_jwk(params.verify_key.key); break; case "raw": key = Buffer.from(params.verify_key.key, "base64url"); break; default: throw Error("Unknown verify key format"); } params.verify_key.key = key; return params; } let hmac_tsuite = { name: "HMAC sign", skip: () => (!has_buffer() || !has_webcrypto()), T: test, prepare_args: p, opts: { text: "TExt-T0-SiGN", sign_key: { key: "c2VjcmV0S0VZ", fmt: "raw" }, verify_key: { key: "c2VjcmV0S0VZ", fmt: "raw" }, verify: false, import_alg: { name: "HMAC", hash: "SHA-256", }, sign_alg: { name: "HMAC", }, }, tests: [ { expected: "76d4f1b22d7544c34e86380c9ab7c756311810dc31e4af3b705045d263db1212" }, { import_alg: { hash: "SHA-384" }, expected: "4bdaa7e80868a9cda35ad78ae5d88c29f1ff97680317c5bc3df1deccf2dad0cf3edce945ed90ec53fa48d887a04d4963" }, { import_alg: { hash: "SHA-512" }, expected: "9dd589ae5e75b6fb8d453c072cc05e6f5eb3d29034d3a0df2559ffe158f3f99fef98a9d1ab2fca459cceea0be3cb7aa3269d77fc9382b56a9cd0571851339938" }, { import_alg: { hash: "SHA-1" }, expected: "0540c587e7ee607fb4fd5e814438ed50f261c244" }, { sign_alg: { name: "ECDSA" }, exception: "TypeError: cannot sign using \"HMAC\" with \"ECDSA\" key" }, { sign_key: { fmt: "jwk", key: { kty: "oct", alg: "HS256", k: "c2VjcmV0S0VZ" } }, expected: "76d4f1b22d7544c34e86380c9ab7c756311810dc31e4af3b705045d263db1212" }, { sign_key: { fmt: "jwk", key: { kty: "oct", alg: "HS256", key_ops: [ "sign" ], k: "c2VjcmV0S0VZ" } }, verify: true, expected: true }, { sign_key: { fmt: "jwk", key: { kty: "oct", alg: "HS256", key_ops: [ "verify" ], k: "c2VjcmV0S0VZ" } }, exception: "TypeError: Key operations and usage mismatch" }, { verify: true, expected: true }, { verify: true, import_alg: { hash: "SHA-384" }, expected: true }, { verify: true, import_alg: { hash: "SHA-512" }, expected: true }, { verify: true, import_alg: { hash: "SHA-1" }, expected: true }, { verify: true, verify_key: { key: "c2VjcmV0S0VZMg" }, expected: false }, ]}; let rsassa_pkcs1_v1_5_tsuite = { name: "RSASSA-PKCS1-v1_5 sign", skip: () => (!has_buffer() || !has_webcrypto()), T: test, prepare_args: p, opts: { text: "TExt-T0-SiGN", sign_key: { key: "rsa.pkcs8", fmt: "pkcs8" }, verify_key: { key: "rsa.spki", fmt: "spki" }, import_alg: { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256", }, sign_alg: { name: "RSASSA-PKCS1-v1_5", }, }, tests: [ { expected: "b126c528abd305dc2b7234de44ffa2190bd55f57087f75620196e8bdb05ba205e52ceca03e4799f30a6d61a6610878b1038a5dd869ab8c04ffe80d49d14407b2c2fe52ca78c9c409fcf7fee26188941f5072179c2bf2de43e637b089c32cf04f14ca01e7b9c33bbbec603b2815de0180b12a3269b0453aba158642e00303890d" }, { import_alg: { hash: "SHA-512" }, expected: "174adca014132f5b9871e1bda2c23fc50f57673c6915b9170d601c626022a03d66c1b8c2a4b8efa08edee83ad27cc05c0d33c7a52a9125fa5be0f99be40483d8123570f91d53f2af51ef0f2b43987182fd114db242f146ea0d7c4ead5d4a11043f83e67d5400fc66dc2b08d7d63122fcd11b495fb4115ecf57c51994f6c516b9" }, { import_alg: { hash: "SHA-1" }, expected: "0cc6377ae31a1b09a7c0a18d12e785e9734565bdeb808b3e41d8bc03adab9ffbd8b1764830fea8f1d8f327034f24296f3aad6112cc3a380db6ef01989f8f9cb608f75b1d9558c36785b6f932ee06729b139b5f02bb886fd1d4fb0f06246064993a421e55579c490c77c27a44c7cc0ea7dd6579cc69402177712ba0f69cac967d" }, { verify: true, expected: true }, { verify: true, import_alg: { hash: "SHA-512" }, expected: true }, { verify: true, import_alg: { hash: "SHA-1" }, expected: true }, { verify: true, verify_key: { key: "rsa2.spki" }, expected: false }, { verify: true, sign_key: { key: "rsa.jwk", fmt: "jwk" }, expected: true }, { sign_key: { key: "rsa.jwk", fmt: "jwk" }, expected: "b126c528abd305dc2b7234de44ffa2190bd55f57087f75620196e8bdb05ba205e52ceca03e4799f30a6d61a6610878b1038a5dd869ab8c04ffe80d49d14407b2c2fe52ca78c9c409fcf7fee26188941f5072179c2bf2de43e637b089c32cf04f14ca01e7b9c33bbbec603b2815de0180b12a3269b0453aba158642e00303890d" }, { verify: true, sign_key: { key: "rsa.jwk", fmt: "jwk" }, verify_key: { key: "rsa.pub.jwk", fmt: "jwk" }, expected: true }, { verify: true, generate_keys: { alg: { name: "RSASSA-PKCS1-v1_5", modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-256" }, extractable: true, usage: [ "sign", "verify" ] }, expected: true }, { sign_key: { key: 1, fmt: "jwk" }, exception: "TypeError: invalid JWK key data" }, { sign_key: { key: { kty: "RSA" }, fmt: "jwk" }, exception: "TypeError: Invalid JWK RSA key" }, { sign_key: { key: { kty: "RSA", n: "yUmxoJC8VAM5hyYZa-XUBZg1N1ywFMPUpWsF1kaSGed98P3XUgPzgX80wpyzd5qdGuALqnf2lMc7O8PrGBtO5YrvQlI96NX0jUo5bc5wz220ob3AUCeQnTfx-UFqM4pCwjoDSo2PlphJdWgFYymGBaBCJgnENQL9H1N_8_yNiN8" }, fmt: "jwk" }, exception: "TypeError: Invalid JWK RSA key" }, { sign_key: { key: { kty: "RSA", n: "yUmxoJC8VAM5hyYZa-XUBZg1N1ywFMPUpWsF1kaSGed98P3XUgPzgX80wpyzd5qdGuALqnf2lMc7O8PrGBtO5YrvQlI96NX0jUo5bc5wz220ob3AUCeQnTfx-UFqM4pCwjoDSo2PlphJdWgFYymGBaBCJgnENQL9H1N_8_yNiN8", e: "AQAB" }, fmt: "jwk" }, exception: "TypeError: key usage mismatch for a RSASSA-PKCS1-v1_5" }, { sign_key: { key: { kty: "RSA", n: "yUmxoJC8VAM5hyYZa-XUBZg1N1ywFMPUpWsF1kaSGed98P3XUgPzgX80wpyzd5qdGuALqnf2lMc7O8PrGBtO5YrvQlI96NX0jUo5bc5wz220ob3AUCeQnTfx-UFqM4pCwjoDSo2PlphJdWgFYymGBaBCJgnENQL9H1N_8_yNiN8", e: "AQAB", d: "j06DQyCopFujYoASi0oWmGEUSjUYO8BsrdSzVCnsLLsuZBwlZ4Peouyw4Hl2IIoYniCyzYwZJzVtC5Dh2MjgcrJTG5nX3FfheuabGl4in0583C51ZYWlVpDvBWw8kJTfXjiKH4z6ZA9dWdT5Y3aH_kOf-znUc7eTvuzISs61x_k" }, fmt: "jwk" }, exception: "TypeError: Invalid JWK RSA key" }, { sign_key: { key: { kty: "RSA", n: "yUmxoJC8VAM5hyYZa-XUBZg1N1ywFMPUpWsF1kaSGed98P3XUgPzgX80wpyzd5qdGuALqnf2lMc7O8PrGBtO5YrvQlI96NX0jUo5bc5wz220ob3AUCeQnTfx-UFqM4pCwjoDSo2PlphJdWgFYymGBaBCJgnENQL9H1N_8_yNiN8", e: "AQAB", d: "j06DQyCopFujYoASi0oWmGEUSjUYO8BsrdSzVCnsLLsuZBwlZ4Peouyw4Hl2IIoYniCyzYwZJzVtC5Dh2MjgcrJTG5nX3FfheuabGl4in0583C51ZYWlVpDvBWw8kJTfXjiKH4z6ZA9dWdT5Y3aH_kOf-znUc7eTvuzISs61x_k", p: "9ASb2yw5b8d7unrFuOyy4EDcPbnzEpbuVGASeHPqkORwHsqeGbfwGlhDYSYrY0HCwUsSBSFcO3SDeu0Z0zSvFQ" }, fmt: "jwk" }, exception: "TypeError: Invalid JWK RSA key" }, { sign_key: { key: { kty: "RSA", n: "yUmxoJC8VAM5hyYZa-XUBZg1N1ywFMPUpWsF1kaSGed98P3XUgPzgX80wpyzd5qdGuALqnf2lMc7O8PrGBtO5YrvQlI96NX0jUo5bc5wz220ob3AUCeQnTfx-UFqM4pCwjoDSo2PlphJdWgFYymGBaBCJgnENQL9H1N_8_yNiN8", e: "AQAB", d: "j06DQyCopFujYoASi0oWmGEUSjUYO8BsrdSzVCnsLLsuZBwlZ4Peouyw4Hl2IIoYniCyzYwZJzVtC5Dh2MjgcrJTG5nX3FfheuabGl4in0583C51ZYWlVpDvBWw8kJTfXjiKH4z6ZA9dWdT5Y3aH_kOf-znUc7eTvuzISs61x_k", p: "9ASb2yw5b8d7unrFuOyy4EDcPbnzEpbuVGASeHPqkORwHsqeGbfwGlhDYSYrY0HCwUsSBSFcO3SDeu0Z0zSvFQ", q: "0yvzzgHo_PGYSlVj-M3965AwQF2wTXz82MZHv6EfcCHKuBfCSecr-igqLHhzfynAQjjf39VrXuPuRL23REF1Iw" }, fmt: "jwk" }, exception: "TypeError: Invalid JWK RSA key" }, { sign_key: { key: { kty: "RSA", n: "yUmxoJC8VAM5hyYZa-XUBZg1N1ywFMPUpWsF1kaSGed98P3XUgPzgX80wpyzd5qdGuALqnf2lMc7O8PrGBtO5YrvQlI96NX0jUo5bc5wz220ob3AUCeQnTfx-UFqM4pCwjoDSo2PlphJdWgFYymGBaBCJgnENQL9H1N_8_yNiN8", e: "AQAB", d: "j06DQyCopFujYoASi0oWmGEUSjUYO8BsrdSzVCnsLLsuZBwlZ4Peouyw4Hl2IIoYniCyzYwZJzVtC5Dh2MjgcrJTG5nX3FfheuabGl4in0583C51ZYWlVpDvBWw8kJTfXjiKH4z6ZA9dWdT5Y3aH_kOf-znUc7eTvuzISs61x_k", p: "9ASb2yw5b8d7unrFuOyy4EDcPbnzEpbuVGASeHPqkORwHsqeGbfwGlhDYSYrY0HCwUsSBSFcO3SDeu0Z0zSvFQ", q: "0yvzzgHo_PGYSlVj-M3965AwQF2wTXz82MZHv6EfcCHKuBfCSecr-igqLHhzfynAQjjf39VrXuPuRL23REF1Iw", dp: "pUXJ2jSl4lOWNcOZz5phvQmxIg2j2N9pJLS9TeAU63YNio1pb7npYa6OVGpp0JxlsE2MMvVZZtuPgd69MxPn0Q" }, fmt: "jwk" }, exception: "TypeError: Invalid JWK RSA key" }, { sign_key: { key: { kty: "RSA", n: "yUmxoJC8VAM5hyYZa-XUBZg1N1ywFMPUpWsF1kaSGed98P3XUgPzgX80wpyzd5qdGuALqnf2lMc7O8PrGBtO5YrvQlI96NX0jUo5bc5wz220ob3AUCeQnTfx-UFqM4pCwjoDSo2PlphJdWgFYymGBaBCJgnENQL9H1N_8_yNiN8", e: "AQAB", d: "j06DQyCopFujYoASi0oWmGEUSjUYO8BsrdSzVCnsLLsuZBwlZ4Peouyw4Hl2IIoYniCyzYwZJzVtC5Dh2MjgcrJTG5nX3FfheuabGl4in0583C51ZYWlVpDvBWw8kJTfXjiKH4z6ZA9dWdT5Y3aH_kOf-znUc7eTvuzISs61x_k", p: "9ASb2yw5b8d7unrFuOyy4EDcPbnzEpbuVGASeHPqkORwHsqeGbfwGlhDYSYrY0HCwUsSBSFcO3SDeu0Z0zSvFQ", q: "0yvzzgHo_PGYSlVj-M3965AwQF2wTXz82MZHv6EfcCHKuBfCSecr-igqLHhzfynAQjjf39VrXuPuRL23REF1Iw", dp: "pUXJ2jSl4lOWNcOZz5phvQmxIg2j2N9pJLS9TeAU63YNio1pb7npYa6OVGpp0JxlsE2MMvVZZtuPgd69MxPn0Q", dq: "RZoqDM-iXKTA3ldQ0TQMKnVnAgAfWRsGN-j6wxW3R_1LVOw31KYGX7iXVfsJjnNTdEBMwfkVH7yezzd8zVmJ4w" }, fmt: "jwk" }, exception: "TypeError: Invalid JWK RSA key" }, { verify: true, sign_key: { key: { kty: "RSA", n: "yUmxoJC8VAM5hyYZa-XUBZg1N1ywFMPUpWsF1kaSGed98P3XUgPzgX80wpyzd5qdGuALqnf2lMc7O8PrGBtO5YrvQlI96NX0jUo5bc5wz220ob3AUCeQnTfx-UFqM4pCwjoDSo2PlphJdWgFYymGBaBCJgnENQL9H1N_8_yNiN8", e: "AQAB", d: "j06DQyCopFujYoASi0oWmGEUSjUYO8BsrdSzVCnsLLsuZBwlZ4Peouyw4Hl2IIoYniCyzYwZJzVtC5Dh2MjgcrJTG5nX3FfheuabGl4in0583C51ZYWlVpDvBWw8kJTfXjiKH4z6ZA9dWdT5Y3aH_kOf-znUc7eTvuzISs61x_k", p: "9ASb2yw5b8d7unrFuOyy4EDcPbnzEpbuVGASeHPqkORwHsqeGbfwGlhDYSYrY0HCwUsSBSFcO3SDeu0Z0zSvFQ", q: "0yvzzgHo_PGYSlVj-M3965AwQF2wTXz82MZHv6EfcCHKuBfCSecr-igqLHhzfynAQjjf39VrXuPuRL23REF1Iw", dp: "pUXJ2jSl4lOWNcOZz5phvQmxIg2j2N9pJLS9TeAU63YNio1pb7npYa6OVGpp0JxlsE2MMvVZZtuPgd69MxPn0Q", dq: "RZoqDM-iXKTA3ldQ0TQMKnVnAgAfWRsGN-j6wxW3R_1LVOw31KYGX7iXVfsJjnNTdEBMwfkVH7yezzd8zVmJ4w", qi: "2REPnRQIaLsya5wlwFw0whwPaAbTZp2jfguhtg5gou_Yru7Cxz_b83YFPgoI6xuGE1OXsWkRTToS8FuIWCrNBQ" }, fmt: "jwk" }, expected: true }, { sign_key: { key: { kty: "RSA", n: "yUmxoJC8VAM5hyYZa-XUBZg1N1ywFMPUpWsF1kaSGed98P3XUgPzgX80wpyzd5qdGuALqnf2lMc7O8PrGBtO5YrvQlI96NX0jUo5bc5wz220ob3AUCeQnTfx-UFqM4pCwjoDSo2PlphJdWgFYymGBaBCJgnENQL9H1N_8_yNiN8", e: "AQAB", d: "j06DQyCopFujYoASi0oWmGEUSjUYO8BsrdSzVCnsLLsuZBwlZ4Peouyw4Hl2IIoYniCyzYwZJzVtC5Dh2MjgcrJTG5nX3FfheuabGl4in0583C51ZYWlVpDvBWw8kJTfXjiKH4z6ZA9dWdT5Y3aH_kOf-znUc7eTvuzISs61x_k", p: "9ASb2yw5b8d7unrFuOyy4EDcPbnzEpbuVGASeHPqkORwHsqeGbfwGlhDYSYrY0HCwUsSBSFcO3SDeu0Z0zSvFQ", q: "0yvzzgHo_PGYSlVj-M3965AwQF2wTXz82MZHv6EfcCHKuBfCSecr-igqLHhzfynAQjjf39VrXuPuRL23REF1Iw", dp: "pUXJ2jSl4lOWNcOZz5phvQmxIg2j2N9pJLS9TeAU63YNio1pb7npYa6OVGpp0JxlsE2MMvVZZtuPgd69MxPn0Q", dq: "RZoqDM-iXKTA3ldQ0TQMKnVnAgAfWRsGN-j6wxW3R_1LVOw31KYGX7iXVfsJjnNTdEBMwfkVH7yezzd8zVmJ4w", qi: "2REPnRQIaLsya5wlwFw0whwPaAbTZp2jfguhtg5gou_Yru7Cxz_b83YFPgoI6xuGE1OXsWkRTToS8FuIWCrNBQ", key_ops: [ "verify" ] }, fmt: "jwk" }, exception: "TypeError: Key operations and usage mismatch" }, { sign_key: { key: { kty: "RSA", n: "yUmxoJC8VAM5hyYZa-XUBZg1N1ywFMPUpWsF1kaSGed98P3XUgPzgX80wpyzd5qdGuALqnf2lMc7O8PrGBtO5YrvQlI96NX0jUo5bc5wz220ob3AUCeQnTfx-UFqM4pCwjoDSo2PlphJdWgFYymGBaBCJgnENQL9H1N_8_yNiN8", e: "AQAB", d: "j06DQyCopFujYoASi0oWmGEUSjUYO8BsrdSzVCnsLLsuZBwlZ4Peouyw4Hl2IIoYniCyzYwZJzVtC5Dh2MjgcrJTG5nX3FfheuabGl4in0583C51ZYWlVpDvBWw8kJTfXjiKH4z6ZA9dWdT5Y3aH_kOf-znUc7eTvuzISs61x_k", p: "9ASb2yw5b8d7unrFuOyy4EDcPbnzEpbuVGASeHPqkORwHsqeGbfwGlhDYSYrY0HCwUsSBSFcO3SDeu0Z0zSvFQ", q: "0yvzzgHo_PGYSlVj-M3965AwQF2wTXz82MZHv6EfcCHKuBfCSecr-igqLHhzfynAQjjf39VrXuPuRL23REF1Iw", dp: "pUXJ2jSl4lOWNcOZz5phvQmxIg2j2N9pJLS9TeAU63YNio1pb7npYa6OVGpp0JxlsE2MMvVZZtuPgd69MxPn0Q", dq: "RZoqDM-iXKTA3ldQ0TQMKnVnAgAfWRsGN-j6wxW3R_1LVOw31KYGX7iXVfsJjnNTdEBMwfkVH7yezzd8zVmJ4w", qi: "2REPnRQIaLsya5wlwFw0whwPaAbTZp2jfguhtg5gou_Yru7Cxz_b83YFPgoI6xuGE1OXsWkRTToS8FuIWCrNBQ", ext: false }, fmt: "jwk", extractable: true }, exception: "TypeError: JWK RSA is not extractable" }, { sign_key: { key: { kty: "RSA", alg: "RS384", n: "yUmxoJC8VAM5hyYZa-XUBZg1N1ywFMPUpWsF1kaSGed98P3XUgPzgX80wpyzd5qdGuALqnf2lMc7O8PrGBtO5YrvQlI96NX0jUo5bc5wz220ob3AUCeQnTfx-UFqM4pCwjoDSo2PlphJdWgFYymGBaBCJgnENQL9H1N_8_yNiN8", e: "AQAB", d: "j06DQyCopFujYoASi0oWmGEUSjUYO8BsrdSzVCnsLLsuZBwlZ4Peouyw4Hl2IIoYniCyzYwZJzVtC5Dh2MjgcrJTG5nX3FfheuabGl4in0583C51ZYWlVpDvBWw8kJTfXjiKH4z6ZA9dWdT5Y3aH_kOf-znUc7eTvuzISs61x_k", p: "9ASb2yw5b8d7unrFuOyy4EDcPbnzEpbuVGASeHPqkORwHsqeGbfwGlhDYSYrY0HCwUsSBSFcO3SDeu0Z0zSvFQ", q: "0yvzzgHo_PGYSlVj-M3965AwQF2wTXz82MZHv6EfcCHKuBfCSecr-igqLHhzfynAQjjf39VrXuPuRL23REF1Iw", dp: "pUXJ2jSl4lOWNcOZz5phvQmxIg2j2N9pJLS9TeAU63YNio1pb7npYa6OVGpp0JxlsE2MMvVZZtuPgd69MxPn0Q", dq: "RZoqDM-iXKTA3ldQ0TQMKnVnAgAfWRsGN-j6wxW3R_1LVOw31KYGX7iXVfsJjnNTdEBMwfkVH7yezzd8zVmJ4w", qi: "2REPnRQIaLsya5wlwFw0whwPaAbTZp2jfguhtg5gou_Yru7Cxz_b83YFPgoI6xuGE1OXsWkRTToS8FuIWCrNBQ" }, fmt: "jwk" }, exception: "TypeError: JWK hash mismatch" }, ]}; let rsa_pss_tsuite = { name: "RSA-PSS sign", skip: () => (!has_buffer() || !has_webcrypto()), T: test, prepare_args: p, opts: { text: "TExt-T0-SiGN", sign_key: { key: "rsa.pkcs8", fmt: "pkcs8" }, verify_key: { key: "rsa.spki", fmt: "spki" }, import_alg: { name: "RSA-PSS", hash: "SHA-256", }, sign_alg: { name: "RSA-PSS", saltLength: 0, }, }, tests: [ { expected: "c126f05ea6e13b3208540bd833f5886d95fe2c89f9b3102b564c9da3bc0c00d224e6ed9be664dee61dfcc0eee790f816c5cf6a0ffc320112d818b72d57de9adbb31d239c225d42395c906bde719bf4ad21c18c679d70186d2efc044fc4995773c5085c64c6d9b7a5fc96dd28176e2cd702a9f35fe64b960f21523ec19bb44408" }, { import_alg: { hash: "SHA-512" }, expected: "3764287839843d25cb8ad109d0ffffd54a8f47fae02e9d2fa8a9363a7b0f98d0ede417c57c0d99a8c11cd502bbc95767a5f437b99cb30341c7af840889633e08cfdaae472bed3e68d451c67182ccd583457c6a9cf81c7e17fb391606f1bc02a83253975f153582ca1c31e9ba9b89dec4bf1d2a9b7b5024dd4dde317432ff26b1" }, { import_alg: { hash: "SHA-1" }, expected: "73d39d22b028b13142b257d405a4a09d0622b97ef7b74e0953274744a76fedee0f283b678cfcaa8e4c38ef84033259f84c59ae987f9d049adea4379a9b0addb9f8b53ee6b64a4e32d8165d057444a1056706da648b88c6a4613022e03be5b6b9e8948d9527a95478f871bfe88dbc67127b038520af3400b942c85e0733bcad27" }, { verify: true, expected: true }, { verify: true, import_alg: { hash: "SHA-512" }, expected: true }, { verify: true, sign_alg: { saltLength: 32 }, expected: true }, { verify: true, import_alg: { hash: "SHA-512" }, sign_alg: { saltLength: 32 }, expected: true }, { verify: true, verify_key: { key: "rsa2.spki" }, expected: false }, { verify: true, sign_key: { key: "rsa.jwk", fmt: "jwk" }, verify_key: { key: "rsa.pub.jwk", fmt: "jwk" }, expected: true }, { verify: true, generate_keys: { alg: { name: "RSA-PSS", modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-256" }, extractable: true, usage: [ "sign", "verify" ] }, expected: true }, ]}; let ecdsa_tsuite = { name: "ECDSA sign", skip: () => (!has_buffer() || !has_webcrypto()), T: test, prepare_args: p, opts: { text: "TExt-T0-SiGN", sign_key: { key: "ec.pkcs8", fmt: "pkcs8" }, verify_key: { key: "ec.spki", fmt: "spki" }, import_alg: { name: "ECDSA", namedCurve: "P-256", }, sign_alg: { name: "ECDSA", hash: "SHA-256", }, }, tests: [ { verify: true, expected: true }, { verify: true, import_alg: { hash: "SHA-384" }, expected: true }, { verify: true, import_alg: { hash: "SHA-512" }, expected: true }, { verify: true, import_alg: { hash: "SHA-1" }, expected: true }, { verify: true, verify_key: { key: "ec2.spki" }, expected: false }, { verify: true, verify_key: { key: "rsa.spki" }, exception: "Error: EC key is not found" }, { verify: true, import_alg: { namedCurve: "P-384" }, exception: "Error: name curve mismatch" }, { verify: true, verify_key: { key: "BHFFLGURrlWEXhok0JfTKke4q-nWSIMPvKTPhdKYSVnc4Nzn_7uNz0AA3U4fhpfVxSD4U5QciGyEoM4r3jC7bjI", fmt: "raw"}, expected: true }, { verify: true, sign_key: { key: "ec.jwk", fmt: "jwk" }, expected: true }, { verify: true, sign_key: { key: "ec.jwk", fmt: "jwk" }, verify_key: { key: "ec.pub.jwk", fmt: "jwk" }, expected: true }, { verify: true, sign_key: { key: "ec.jwk", fmt: "jwk" }, import_alg: { namedCurve: "P-384" }, exception: "Error: JWK EC curve mismatch" }, { sign_key: { key: 1, fmt: "jwk" }, exception: "TypeError: Invalid JWK EC key" }, { sign_key: { key: { kty: "EC" }, fmt: "jwk" }, exception: "TypeError: Invalid JWK EC key" }, { sign_key: { key: { kty: "EC", x: "cUUsZRGuVYReGiTQl9MqR7ir6dZIgw-8pM-F0phJWdw"}, fmt: "jwk" }, exception: "TypeError: Invalid JWK EC key" }, { sign_key: { key: { kty: "EC", x: "cUUsZRGuVYReGiTQl9MqR7ir6dZIgw-8pM-F0phJWdw", y: "4Nzn_7uNz0AA3U4fhpfVxSD4U5QciGyEoM4r3jC7bjI" }, fmt: "jwk" }, exception: "TypeError: Invalid JWK EC key" }, { sign_key: { key: { kty: "EC", x: "cUUsZRGuVYReGiTQl9MqR7ir6dZIgw-8pM-F0phJWdw", y: "4Nzn_7uNz0AA3U4fhpfVxSD4U5QciGyEoM4r3jC7bjI", d: "E2sW0_4a3QXaSTJ0JKbSUbieKTD1UFtr7i_2CuetP6A" }, fmt: "jwk" }, exception: "TypeError: JWK EC curve mismatch" }, { sign_key: { key: { kty: "EC", x: "cUUsZRGuVYReGiTQl9MqR7ir6dZIgw-8pM-F0phJWdw", y: "4Nzn_7uNz0AA3U4fhpfVxSD4U5QciGyEoM4r3jC7bjI", d: "E2sW0_4a3QXaSTJ0JKbSUbieKTD1UFtr7i_2CuetP6A", crv: "P-384" }, fmt: "jwk" }, exception: "TypeError: JWK EC curve mismatch" }, { verify: true, sign_key: { key: { kty: "EC", x: "cUUsZRGuVYReGiTQl9MqR7ir6dZIgw-8pM-F0phJWdw", y: "4Nzn_7uNz0AA3U4fhpfVxSD4U5QciGyEoM4r3jC7bjI", d: "E2sW0_4a3QXaSTJ0JKbSUbieKTD1UFtr7i_2CuetP6A", crv: "P-256" }, fmt: "jwk" }, expected: true }, { sign_key: { key: { kty: "EC", x: "_BROKEN_", y: "4Nzn_7uNz0AA3U4fhpfVxSD4U5QciGyEoM4r3jC7bjI", d: "E2sW0_4a3QXaSTJ0JKbSUbieKTD1UFtr7i_2CuetP6A", crv: "P-256" }, fmt: "jwk" }, exception: "Error: EC_KEY_set_public_key_affine_coordinates() failed" }, { sign_key: { key: { kty: "EC", x: "cUUsZRGuVYReGiTQl9MqR7ir6dZIgw-8pM-F0phJWdw", y: "4Nzn_7uNz0AA3U4fhpfVxSD4U5QciGyEoM4r3jC7bjI", d: "E2sW0_4a3QXaSTJ0JKbSUbieKTD1UFtr7i_2CuetP6A", crv: "P-256", key_ops: [ "verify" ]}, fmt: "jwk" }, exception: "TypeError: Key operations and usage mismatch" }, { sign_key: { key: { kty: "EC", x: "cUUsZRGuVYReGiTQl9MqR7ir6dZIgw-8pM-F0phJWdw", y: "4Nzn_7uNz0AA3U4fhpfVxSD4U5QciGyEoM4r3jC7bjI", ext: false, d: "E2sW0_4a3QXaSTJ0JKbSUbieKTD1UFtr7i_2CuetP6A", crv: "P-256" }, extractable: true, fmt: "jwk" }, exception: "TypeError: JWK is not extractable" }, { sign_key: { key: { kty: "EC", x: "cUUsZRGuVYReGiTQl9MqR7ir6dZIgw-8pM-F0phJWdw", y: "4Nzn_7uNz0AA3U4fhpfVxSD4U5QciGyEoM4r3jC7bjI", d: "E2sW0_4a3QXaSTJ0JKbSUbieKTD1UFtr7i_2CuetP6A", crv: "P-256" }, key_ops: [ 'verify', 'sign' ], fmt: "jwk" }, exception: "TypeError: Unsupported key usage for a ECDSA key" }, ]}; run([ hmac_tsuite, rsassa_pkcs1_v1_5_tsuite, rsa_pss_tsuite, ecdsa_tsuite ]) .then($DONE, $DONE); njs-0.8.9/test/webcrypto/text.base64.aes-cbc128.enc000066400000000000000000000000551474132077100216120ustar00rootroot00000000000000pKzTDFjJuyyWxBpM0++pVETg9638AXJwa9yXCL3Av0c= njs-0.8.9/test/webcrypto/text.base64.aes-cbc256.enc000066400000000000000000000000551474132077100216140ustar00rootroot000000000000003gnuDWCYtwPW5TMPtj1LM/uJxnKknbvPn9gURBEcegE= njs-0.8.9/test/webcrypto/text.base64.aes-ctr128.enc000066400000000000000000000000351474132077100216510ustar00rootroot00000000000000UsVG2TjNHGbXaTZ3fG67MsxXPw== njs-0.8.9/test/webcrypto/text.base64.aes-ctr256.enc000066400000000000000000000000411474132077100216500ustar00rootroot00000000000000jnIqHDDRajcKkHwo4IormMSsBSDEI40= njs-0.8.9/test/webcrypto/text.base64.aes-gcm128-96.enc000066400000000000000000000000751474132077100220670ustar00rootroot00000000000000z4NZNzf3eauJvuFQTopsTxPSERfT4lpbnK1ILuqw81OBxqw8cpheqTfXi7U5 njs-0.8.9/test/webcrypto/text.base64.aes-gcm128-extra.enc000066400000000000000000000001051474132077100227460ustar00rootroot00000000000000z4NZNzf3eavxzIhNW4QOTRfQewfam0gzjLpOKIKwm1+QJfR0ElIvNEPnKHx4d+OxJMpT njs-0.8.9/test/webcrypto/text.base64.aes-gcm128.enc000066400000000000000000000000611474132077100216260ustar00rootroot00000000000000z4NZNzf3eavjzY9WSplsVxPEAuftE7KpHQoIoS+yI/lPxDk= njs-0.8.9/test/webcrypto/text.base64.aes-gcm256.enc000066400000000000000000000000651474132077100216340ustar00rootroot00000000000000JCRgSCD1e7h0ogveLrzbaUBby151RIajzxFhHyD4JpD36kBOL2Kz njs-0.8.9/test/webcrypto/text.base64.rsa-oaep.enc000066400000000000000000000002571474132077100215750ustar00rootroot00000000000000lRLk3t6LYvQBJkOYqWYSWYcHaPmsskb+vgcV3bwnfHF2MNp5oALe14mn/4m759oWCQIgXA/3kC1E kHXOBERS2+wKOXD2hS68kZnnrfWq6//7yw7Fvzv9OjG5mYSUaKcO/p+zaJmorsgvOy+nyZJs+BPD dKU3ohuz1MJ0wrGkki4= njs-0.8.9/test/webcrypto/text.base64.sha1.ecdsa.sig000066400000000000000000000001321474132077100220050ustar00rootroot00000000000000Bn+wY9+5iK++blSypmCJHaO1Pzx3aagiCcCHqHWMp1ULKiu3myhI046rhEThSLyReuTueIEgeCPB a2xGZnFXEg== njs-0.8.9/test/webcrypto/text.base64.sha1.hmac.sig000066400000000000000000000000351474132077100216400ustar00rootroot00000000000000eVw25ESkzl+mDQs7z5VGkxqneZ4= njs-0.8.9/test/webcrypto/text.base64.sha1.pkcs1.sig000066400000000000000000000002571474132077100217570ustar00rootroot00000000000000H4lVoQebJkYFFJyXwBT2C6QDJ2OUQhQ153WjnOzaXLtlUtHdI7EOv8/hJ84ojDRJ4IyLXtGO8up9 3WUIPw1tfwAI3X36MbMN04+HKzVabg4cTy0HnFu3k7D2hq+1vn6rT1Q7xT9C2SJBFmR/HxC2oHKz NcpELOP8crsoqu0c3QY= njs-0.8.9/test/webcrypto/text.base64.sha1.rsa-pss.16.sig000066400000000000000000000002571474132077100225530ustar00rootroot00000000000000aHWhiEOYGTRZyJNeNoEELFFyN+ZYgFLI+rNyuuaQpLEWDHqbUY0tdGenvIiiUxN0GKq/72g6CvyH RXq9VUL4Q+qkDbmROzBC0/P+RqgxpcNVJQx04RyGRVnw+l+GzE3rwbDCQG+95okBOxnac21thk/k GBJQGXhimg6XkCK3vVo= njs-0.8.9/test/webcrypto/text.base64.sha256.ecdsa.sig000066400000000000000000000001321474132077100221610ustar00rootroot00000000000000UTDXV68RaiEqzBF7dlrQ/S7ua8aPiRKceLH8Q4qHECmrZC3A28WDxF1jq3TV9/yZRM+dPWUR8xJF ma25XFC5AA== njs-0.8.9/test/webcrypto/text.base64.sha256.hmac.sig000066400000000000000000000000551474132077100220160ustar00rootroot00000000000000UbsmYPe0ek53QMpDPP/Y4V50bRXQQGNrYCTuzg1tznE= njs-0.8.9/test/webcrypto/text.base64.sha256.hmac.sig.broken000066400000000000000000000000551474132077100232750ustar00rootroot00000000000000UbsmYPe0ek53QMpDPP/Y4V50bRXQQGNrYCAuzg1tznE= njs-0.8.9/test/webcrypto/text.base64.sha256.pkcs1.sig000066400000000000000000000002571474132077100221330ustar00rootroot00000000000000IyGM/e2IMJCrJmE91GkddTyCble3554d8KvpbL1k10QrRlDE1afCab7iwmz4j1yl8pNMbSTKIb0y RfMQ3YlsKxzoJm2+pIrXErb2jF3emnVkUxNuSY3/ROK3rU8YirPbKhvkjulVwVlh4b6YpiXwuKTL HDmHp7AOr7yzjS3VZrs= njs-0.8.9/test/webcrypto/text.base64.sha256.rsa-pss.0.sig000066400000000000000000000002571474132077100226400ustar00rootroot00000000000000DNQrIYaW4opZG1OdyFujH2rxgk06HB2eTUuCGyiN971pAVxCqYn0NhL7iMBrUrgsxnqBH+nNC1jg AMFGe0rtJE/9blWb9QiNz/kwitFI4oztXkcCHcQYwatbQTGQgqeA2rY9N6w6QwMAYJeEd4Jm0lTE oJx9N+C5QoArKBPgXxw= njs-0.8.9/test/webcrypto/text.base64.sha256.rsa-pss.32.sig000066400000000000000000000002571474132077100227250ustar00rootroot00000000000000B+69Fvykn5ncPxA91DQv7K5r5D25f0LuEK60h4WNWev8Hl/Bzseq315o/Ja7RhlgtYltBZTETqUk hkaWEWExCG1kw+xUbsz1HsdHJOwF+e52zirKGifondHVqTOl95aU5msRcuKtCEifnvgKqNE9c+Dz l1hoPlgGrhtnyI0DlcM= njs-0.8.9/test/webcrypto/verify.t.mjs000066400000000000000000000127251474132077100176150ustar00rootroot00000000000000/*--- includes: [compatFs.js, compatBuffer.js, compatWebcrypto.js, runTsuite.js, webCryptoUtils.js] flags: [async] ---*/ async function test(params) { let key = await crypto.subtle.importKey(params.key.fmt, params.key.file, params.import_alg, false, ["verify"]); let r = await crypto.subtle.verify(params.verify_alg, key, params.signature, params.text) .catch (e => { if (e.toString().startsWith("Error: EVP_PKEY_CTX_set_signature_md() failed")) { /* Red Hat Enterprise Linux: SHA-1 is disabled */ return "SKIPPED"; } }); if (r == "SKIPPED") { return r; } if (params.expected !== r) { throw Error(`${params.import_alg.name} failed expected: "${params.expected}" vs "${r}"`); } return 'SUCCESS'; } function p(args, default_opts) { let encoder = new TextEncoder(); let params = merge({}, default_opts); params = merge(params, args); switch (params.key.fmt) { case "spki": let pem = fs.readFileSync(`test/webcrypto/${params.key.file}`); params.key.file = pem_to_der(pem, "PUBLIC"); break; case "raw": params.key.file = Buffer.from(params.key.file, "hex"); break; } params.signature = base64decode(fs.readFileSync(`test/webcrypto/${params.signature}`)); params.text = encoder.encode(params.text); return params; } let hmac_tsuite = { name: "HMAC verify", skip: () => (!has_buffer() || !has_webcrypto()), T: test, prepare_args: p, opts: { text: "SigneD-TExt", key: { fmt: "raw", file: "aabbcc" }, import_alg: { name: "HMAC", hash: "SHA-256", }, verify_alg: { name: "HMAC", }, }, tests: [ { signature: "text.base64.sha256.hmac.sig", expected: true }, { signature: "text.base64.sha256.hmac.sig.broken", expected: false }, { import_alg: { hash: "SHA-1" }, signature: "text.base64.sha1.hmac.sig", expected: true }, { import_alg: { hash: "SHA-1" }, signature: "text.base64.sha256.hmac.sig", expected: false }, { key: { file: "aabbccdd" }, signature: "text.base64.sha256.hmac.sig", expected: false }, ]}; let rsassa_pkcs1_v1_5_tsuite = { name: "RSASSA-PKCS1-v1_5 verify", skip: () => (!has_buffer() || !has_webcrypto()), T: test, prepare_args: p, opts: { text: "SigneD-TExt", key: { fmt: "spki", file: "rsa.spki" }, import_alg: { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256", }, verify_alg: { name: "RSASSA-PKCS1-v1_5", }, }, tests: [ { signature: "text.base64.sha256.pkcs1.sig", expected: true }, { text: "SigneD-TExt2", signature: "text.base64.sha256.pkcs1.sig", expected: false }, { signature: "text.base64.sha1.pkcs1.sig", expected: false }, { import_alg: { hash: "SHA-1" }, signature: "text.base64.sha1.pkcs1.sig", expected: true }, { key: { file: "rsa2.spki"}, signature: "text.base64.sha256.pkcs1.sig", expected: false }, ]}; let rsa_pss_tsuite = { name: "RSA-PSS verify", skip: () => (!has_buffer() || !has_webcrypto()), T: test, prepare_args: p, opts: { text: "SigneD-TExt", key: { fmt: "spki", file: "rsa.spki" }, import_alg: { name: "RSA-PSS", hash: "SHA-256", }, verify_alg: { name: "RSA-PSS", saltLength: 32, }, }, tests: [ { signature: "text.base64.sha256.rsa-pss.32.sig", expected: true }, { text: "SigneD-TExt2", signature: "text.base64.sha256.rsa-pss.32.sig", expected: false }, { key: { file: "rsa2.spki"}, signature: "text.base64.sha256.rsa-pss.32.sig", expected: false }, { verify_alg: { saltLength: 0 }, signature: "text.base64.sha256.rsa-pss.0.sig", expected: true }, { verify_alg: { saltLength: 0 }, signature: "text.base64.sha256.rsa-pss.0.sig", expected: true }, { import_alg: { hash: "SHA-1" }, signature: "text.base64.sha256.rsa-pss.32.sig", expected: false }, { import_alg: { hash: "SHA-1" }, verify_alg: { saltLength: 16 }, signature: "text.base64.sha1.rsa-pss.16.sig", expected: true }, { verify_alg: { saltLength: 16 }, signature: "text.base64.sha256.rsa-pss.32.sig", expected: false }, ]}; let ecdsa_tsuite = { name: "ECDSA verify", skip: () => (!has_buffer() || !has_webcrypto()), T: test, prepare_args: p, opts: { text: "SigneD-TExt", key: { fmt: "spki", file: "ec.spki" }, import_alg: { name: "ECDSA", namedCurve: "P-256", }, verify_alg: { name: "ECDSA", hash: "SHA-256", }, }, tests: [ { signature: "text.base64.sha256.ecdsa.sig", expected: true }, { signature: "text.base64.sha1.ecdsa.sig", expected: false }, { verify_alg: { hash: "SHA-1"}, signature: "text.base64.sha1.ecdsa.sig", expected: true }, { key: { file: "ec2.spki" }, signature: "text.base64.sha256.ecdsa.sig", expected: false }, ]}; run([ hmac_tsuite, rsassa_pkcs1_v1_5_tsuite, rsa_pss_tsuite, ecdsa_tsuite, ]) .then($DONE, $DONE); njs-0.8.9/test/xml/000077500000000000000000000000001474132077100141075ustar00rootroot00000000000000njs-0.8.9/test/xml/README.rst000066400000000000000000000014531474132077100156010ustar00rootroot00000000000000========== XML tests ========== SAML signing ============ Generating SAML signed AuthnRequest ----------------------------------- Note: XMLSec library is used (https://www.aleksey.com/xmlsec/). .. code-block:: shell xmlsec1 --sign --pkcs8-pem test/webcrypto/rsa.pkcs8 \ --output test/xml/