diff --git a/.eslintrc.js b/.eslintrc.js index 8bdf2ad09ae88c..8acba223d986eb 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -118,12 +118,14 @@ module.exports = { 'no-class-assign': 'error', 'no-confusing-arrow': 'error', 'no-const-assign': 'error', + 'no-constructor-return': 'error', 'no-control-regex': 'error', 'no-debugger': 'error', 'no-delete-var': 'error', 'no-dupe-args': 'error', 'no-dupe-class-members': 'error', 'no-dupe-keys': 'error', + 'no-dupe-else-if': 'error', 'no-duplicate-case': 'error', 'no-duplicate-imports': 'error', 'no-empty-character-class': 'error', @@ -247,6 +249,7 @@ module.exports = { 'no-return-await': 'error', 'no-self-assign': 'error', 'no-self-compare': 'error', + 'no-setter-return': 'error', 'no-shadow-restricted-names': 'error', 'no-tabs': 'error', 'no-template-curly-in-string': 'error', @@ -279,6 +282,10 @@ module.exports = { 'one-var': ['error', { initialized: 'never' }], 'one-var-declaration-per-line': 'error', 'operator-linebreak': ['error', 'after'], + 'padding-line-between-statements': [ + 'error', + { blankLine: 'always', prev: 'function', next: 'function' }, + ], 'prefer-const': ['error', { ignoreReadBeforeAssign: true }], 'quotes': ['error', 'single', { avoidEscape: true }], 'quote-props': ['error', 'consistent'], diff --git a/AUTHORS b/AUTHORS index a3d620f2f9c392..adb8aef88a44d5 100644 --- a/AUTHORS +++ b/AUTHORS @@ -2866,5 +2866,84 @@ akitsu-sanae Minuk Park Jim Schlight Theotime Poisseau +Alex Zherdev +dev-313 +Michael Perrotte +Alexandre Ferrando +Loris Zinsou +Jizu Sun +AshCripps +garygsc +Patrick Housley +Artem Maksimov +Nolik +palmires +Vadim Gorbachev +galina.prokofeva +Nadya +PerfectPan +peze +Vladislav Botvin +Ilia Safronov +Dmitriy Kikinskiy +telenord +alexahdp +Daniil Pletnev +Grigoriy Levanov +Simon Schick +Albert Wang +Kenza Houmani +mkdorff +xefimx +garygsc +Susana Ferreira +Xavier Redondo +Duncan Healy +SoulMonk +Kerry Mahne +nathias +Nikolay Krashnikov <1090219@mail.ru> +daern91 +JL Phillips +Jure Stepisnik +Raoul Jaeckel +Kyriakos Markakis +Guilherme Goncalves +Jesse O'Connor +VinceOPS +David OLIVIER +Maria Stogova +Taylor Gagne +Kirlat +Lucas Recknagel +Oliver Belaifa +guzhizhou +Paolo Ceschi Berrini +Jing Lin +poutch +Tembrechts +Jon Church +Àlvar Pérez +Daniel Schuech +Dimitris Ktistakis +Dries Stelten +Vladimir Adamic +Shubham Chaturvedi <19shubham11@gmail.com> +Jamar Torres +Luis Camargo +Herrmann, Rene R. (656) +Semir Ajruli +matijagaspar +Aldo Ambrosioni +Nazar Malyy +Dennis Saenger +ryan jarvinen +Maria Emmanouil +Tijl Claessens +EmaSuriano +Chris Oyler +Jesper Ek +Luciano +jens-cappelle # Generated by tools/update-authors.js diff --git a/BUILDING.md b/BUILDING.md index 08b75855ea6ee0..14206976e48a12 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -116,7 +116,7 @@ platforms. This is true regardless of entries in the table below. | macOS | x64 | >= 10.11 | Tier 1 | | | SmartOS | x64 | >= 18 | Tier 2 | | | AIX | ppc64be >=power7 | >= 7.2 TL02 | Tier 2 | | -| FreeBSD | x64 | >= 11 | Experimental | Downgraded as of Node.js 12 | +| FreeBSD | x64 | >= 11 | Experimental | Downgraded as of Node.js 12 [7](#fn7) | 1: GCC 6 is not provided on the base platform, users will need the @@ -150,6 +150,10 @@ are provided. However, tests in our infrastructure only run on WoW64. Furthermore, compiling on x86 Windows is currently considered Experimental and may not be possible. +7: The default FreeBSD 12.0 compiler is Clang 6.0.1, but +FreeBSD 12.1 upgrades to 8.0.1. Other Clang/LLVM versions are provided +via the system's package manager, including Clang 9.0. + ### Supported toolchains Depending on the host platform, the selection of toolchains may vary. @@ -214,9 +218,10 @@ Supported platforms and toolchains change with each major version of Node.js. This document is only valid for the current major version of Node.js. Consult previous versions of this document for older versions of Node.js: +* [Node.js 13](https://github.com/nodejs/node/blob/v13.x/BUILDING.md) +* [Node.js 12](https://github.com/nodejs/node/blob/v12.x/BUILDING.md) * [Node.js 10](https://github.com/nodejs/node/blob/v10.x/BUILDING.md) * [Node.js 8](https://github.com/nodejs/node/blob/v8.x/BUILDING.md) -* [Node.js 6](https://github.com/nodejs/node/blob/v6.x/BUILDING.md) ## Building Node.js on supported platforms diff --git a/CHANGELOG.md b/CHANGELOG.md index b5c8f757eb7f80..39c3d092a4ba98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,7 +30,8 @@ release. -13.2.0
+13.3.0
+13.2.0
13.1.0
13.0.1
13.0.0
diff --git a/COLLABORATOR_GUIDE.md b/COLLABORATOR_GUIDE.md index 0c5711fe8a85c5..a42c4c8b35f4fa 100644 --- a/COLLABORATOR_GUIDE.md +++ b/COLLABORATOR_GUIDE.md @@ -187,7 +187,9 @@ Do not land any pull requests without passing (green or yellow) CI runs. If there are CI failures unrelated to the change in the pull request, try "Resume Build". It is in the left navigation of the relevant `node-test-pull-request` job. It will preserve all the green results from the current job but re-run -everything else. +everything else. Start a fresh CI if more than seven days have elapsed since +the original failing CI as the compiled binaries for the Windows and ARM +platforms are only kept for seven days. Some of the CI Jobs may require `GIT_REMOTE_REF` which is the remote portion of Git refspec. To specify the branch this way `refs/heads/BRANCH` is used diff --git a/CPP_STYLE_GUIDE.md b/CPP_STYLE_GUIDE.md index 8808405b2c6553..f29c8df3210caa 100644 --- a/CPP_STYLE_GUIDE.md +++ b/CPP_STYLE_GUIDE.md @@ -1,5 +1,8 @@ # C++ Style Guide +See also the [C++ codebase README](src/README.md) for C++ idioms in the Node.js +codebase not related to stylistic issues. + ## Table of Contents * [Guides and References](#guides-and-references) diff --git a/LICENSE b/LICENSE index d09611738be467..9d65fc76cb949e 100644 --- a/LICENSE +++ b/LICENSE @@ -1503,3 +1503,28 @@ The externally maintained libraries used by Node.js are: ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """ + +- uvwasi, located at deps/uvwasi, is licensed as follows: + """ + MIT License + + Copyright (c) 2019 Colin Ihrig and Contributors + + 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. + """ diff --git a/Makefile b/Makefile index d46b4a0c18a3d6..6b678889ca8986 100644 --- a/Makefile +++ b/Makefile @@ -489,6 +489,7 @@ test-all-valgrind: test-build test-all-suites: | clear-stalled test-build bench-addons-build doc-only ## Run all test suites. $(PYTHON) tools/test.py $(PARALLEL_ARGS) --mode=$(BUILDTYPE_LOWER) test/* +# CI_* variables should be kept synchronized with the ones in vcbuild.bat CI_NATIVE_SUITES ?= addons js-native-api node-api CI_JS_SUITES ?= default ifeq ($(node_use_openssl), false) @@ -745,7 +746,7 @@ $(LINK_DATA): $(wildcard lib/*.js) tools/doc/apilinks.js $(call available-node, $(gen-apilink)) out/doc/api/%.json out/doc/api/%.html: doc/api/%.md tools/doc/generate.js \ - tools/doc/html.js tools/doc/json.js tools/doc/apilinks.js | $(LINK_DATA) + tools/doc/markdown.js tools/doc/html.js tools/doc/json.js tools/doc/apilinks.js | $(LINK_DATA) $(call available-node, $(gen-api)) out/doc/api/all.html: $(apidocs_html) tools/doc/allhtml.js \ diff --git a/README.md b/README.md index e200d25eb133d6..7b3f7b69742dfc 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ Looking for help? Check out the each October. * **LTS**: Releases that receive Long-term Support, with a focus on stability and security. Every even-numbered major version will become an LTS release. - LTS releases receive 18 months of _Active LTS_ support and a further 12 months + LTS releases receive 12 months of _Active LTS_ support and a further 18 months of _Maintenance_. LTS release lines have alphabetically-ordered codenames, beginning with v4 Argon. There are no breaking changes or feature additions, except in some special circumstances. diff --git a/benchmark/process/next-tick-breadth-args.js b/benchmark/process/next-tick-breadth-args.js index f7f9882c87e34b..03651dbdbb4eea 100644 --- a/benchmark/process/next-tick-breadth-args.js +++ b/benchmark/process/next-tick-breadth-args.js @@ -13,16 +13,19 @@ function main({ n }) { if (j === n) bench.end(n); } + function cb2(arg1, arg2) { j++; if (j === n) bench.end(n); } + function cb3(arg1, arg2, arg3) { j++; if (j === n) bench.end(n); } + function cb4(arg1, arg2, arg3, arg4) { j++; if (j === n) diff --git a/benchmark/process/next-tick-depth-args.js b/benchmark/process/next-tick-depth-args.js index d9fc37887ec5a3..7c6b2346fd5fad 100644 --- a/benchmark/process/next-tick-depth-args.js +++ b/benchmark/process/next-tick-depth-args.js @@ -20,6 +20,7 @@ function main({ n }) { } else bench.end(n); } + function cb3(arg1, arg2, arg3) { if (--counter) { if (counter % 4 === 0) @@ -33,6 +34,7 @@ function main({ n }) { } else bench.end(n); } + function cb2(arg1, arg2) { if (--counter) { if (counter % 4 === 0) @@ -46,6 +48,7 @@ function main({ n }) { } else bench.end(n); } + function cb1(arg1) { if (--counter) { if (counter % 4 === 0) diff --git a/benchmark/streams/writable-manywrites.js b/benchmark/streams/writable-manywrites.js index 6fcb07e849d615..0ed38d0357a438 100644 --- a/benchmark/streams/writable-manywrites.js +++ b/benchmark/streams/writable-manywrites.js @@ -4,14 +4,19 @@ const common = require('../common'); const Writable = require('stream').Writable; const bench = common.createBenchmark(main, { - n: [2e6] + n: [2e6], + sync: ['yes', 'no'] }); -function main({ n }) { +function main({ n, sync }) { const b = Buffer.allocUnsafe(1024); const s = new Writable(); + sync = sync === 'yes'; s._write = function(chunk, encoding, cb) { - cb(); + if (sync) + cb(); + else + process.nextTick(cb); }; bench.start(); diff --git a/benchmark/timers/set-immediate-breadth-args.js b/benchmark/timers/set-immediate-breadth-args.js index 518ed90e58f476..6d556f9186b7c7 100644 --- a/benchmark/timers/set-immediate-breadth-args.js +++ b/benchmark/timers/set-immediate-breadth-args.js @@ -12,7 +12,9 @@ function main({ n }) { }); function cb1(arg1) {} + function cb2(arg1, arg2) {} + function cb3(arg1, arg2, arg3) {} bench.start(); diff --git a/benchmark/timers/set-immediate-depth-args.js b/benchmark/timers/set-immediate-depth-args.js index d8eb5c409a64e4..a01a400d5f4bc5 100644 --- a/benchmark/timers/set-immediate-depth-args.js +++ b/benchmark/timers/set-immediate-depth-args.js @@ -21,6 +21,7 @@ function main({ n }) { setImmediate(cb1, n); } } + function cb2(n, arg2) { if (--n) { if (n % 3 === 0) @@ -31,6 +32,7 @@ function main({ n }) { setImmediate(cb1, n); } } + function cb1(n) { if (--n) { if (n % 3 === 0) diff --git a/benchmark/timers/timers-breadth-args.js b/benchmark/timers/timers-breadth-args.js index 5f91603caa0a5c..bdb3adbbec6b7a 100644 --- a/benchmark/timers/timers-breadth-args.js +++ b/benchmark/timers/timers-breadth-args.js @@ -12,16 +12,19 @@ function main({ n }) { if (j === n) bench.end(n); } + function cb2(arg1, arg2) { j++; if (j === n) bench.end(n); } + function cb3(arg1, arg2, arg3) { j++; if (j === n) bench.end(n); } + function cb4(arg1, arg2, arg3, arg4) { j++; if (j === n) diff --git a/benchmark/timers/timers-timeout-nexttick.js b/benchmark/timers/timers-timeout-nexttick.js index 781e505ae41ea2..638d8aec149cd5 100644 --- a/benchmark/timers/timers-timeout-nexttick.js +++ b/benchmark/timers/timers-timeout-nexttick.js @@ -19,9 +19,11 @@ function main({ n }) { function cb() { process.nextTick(counter); } + function cb2() { process.nextTick(counter); } + function counter() { count++; if (count === n) diff --git a/benchmark/timers/timers-timeout-pooled.js b/benchmark/timers/timers-timeout-pooled.js index d34080fa668422..1cdae07dc98bdc 100644 --- a/benchmark/timers/timers-timeout-pooled.js +++ b/benchmark/timers/timers-timeout-pooled.js @@ -20,6 +20,7 @@ function main({ n }) { if (count === n) bench.end(n); } + function cb2() { count++; if (count === n) diff --git a/benchmark/timers/timers-timeout-unpooled.js b/benchmark/timers/timers-timeout-unpooled.js index 19f0f6a4af4d0d..fecb73a36013b0 100644 --- a/benchmark/timers/timers-timeout-unpooled.js +++ b/benchmark/timers/timers-timeout-unpooled.js @@ -20,6 +20,7 @@ function main({ n }) { if (count === n) bench.end(n); } + function cb2() { count++; if (count === n) diff --git a/common.gypi b/common.gypi index 2e59636b1718f1..54a8d930c4c062 100644 --- a/common.gypi +++ b/common.gypi @@ -39,7 +39,7 @@ # Reset this number to 0 on major V8 upgrades. # Increment by one for each non-official patch applied to deps/v8. - 'v8_embedder_string': '-node.20', + 'v8_embedder_string': '-node.23', ##### V8 defaults for Node.js ##### diff --git a/configure b/configure index f199a353482bec..39decd9a55ccc7 100755 --- a/configure +++ b/configure @@ -20,7 +20,7 @@ del _ import sys from distutils.spawn import find_executable -print('Node configure: Found Python {0}.{1}.{2}...'.format(*sys.version_info)) +print('Node.js configure: Found Python {0}.{1}.{2}...'.format(*sys.version_info)) acceptable_pythons = ((3, 8), (3, 7), (3, 6), (3, 5), (2, 7)) if sys.version_info[:2] in acceptable_pythons: import configure diff --git a/configure.py b/configure.py index 959c1db6518fdb..48624aba92bf19 100755 --- a/configure.py +++ b/configure.py @@ -455,6 +455,11 @@ dest='without_node_snapshot', help='Turn off V8 snapshot integration. Currently experimental.') +parser.add_option('--without-node-code-cache', + action='store_true', + dest='without_node_code_cache', + help='Turn off V8 Code cache integration.') + intl_optgroup.add_option('--download', action='store', dest='download_list', @@ -976,10 +981,18 @@ def configure_node(o): o['variables']['want_separate_host_toolset'] = int(cross_compiling) if not options.without_node_snapshot: - o['variables']['node_use_node_snapshot'] = b(not cross_compiling) + o['variables']['node_use_node_snapshot'] = b( + not cross_compiling and not options.shared) else: o['variables']['node_use_node_snapshot'] = 'false' + if not options.without_node_code_cache: + # TODO(refack): fix this when implementing embedded code-cache when cross-compiling. + o['variables']['node_use_node_code_cache'] = b( + not cross_compiling and not options.shared) + else: + o['variables']['node_use_node_code_cache'] = 'false' + if target_arch == 'arm': configure_arm(o) elif target_arch in ('mips', 'mipsel', 'mips64el'): @@ -1100,9 +1113,7 @@ def configure_node(o): o['variables']['debug_nghttp2'] = 'false' o['variables']['node_no_browser_globals'] = b(options.no_browser_globals) - # TODO(refack): fix this when implementing embedded code-cache when cross-compiling. - if o['variables']['want_separate_host_toolset'] == 0: - o['variables']['node_code_cache'] = 'yes' # For testing + o['variables']['node_shared'] = b(options.shared) node_module_version = getmoduleversion.get_version() diff --git a/deps/llhttp/README.md b/deps/llhttp/README.md index 7010b90f7ad9e5..c6c061238127f3 100644 --- a/deps/llhttp/README.md +++ b/deps/llhttp/README.md @@ -14,6 +14,8 @@ This project aims to: * Verifiable * Improving benchmarks where possible +More details in [Fedor Indutny's talk at JSConf EU 2019](https://youtu.be/x3k_5Mi66sY) + ## How? Over time, different approaches for improving [http_parser][0]'s code base @@ -30,11 +32,10 @@ So far llhttp outperforms http_parser: | | input size | bandwidth | reqs/sec | time | |:----------------|-----------:|-------------:|-----------:|--------:| -| **llhttp** _(C)_ | 8192.00 mb | 1497.88 mb/s | 3020458.87 ops/sec | 5.47 s | -| **llhttp** _(bitcode)_ | 8192.00 mb | 1131.75 mb/s | 2282171.24 ops/sec | 7.24 s | +| **llhttp** _(C)_ | 8192.00 mb | 1777.24 mb/s | 3583799.39 ops/sec | 4.61 s | | **http_parser** | 8192.00 mb | 694.66 mb/s | 1406180.33 req/sec | 11.79 s | -llhttp is faster by approximately **116%**. +llhttp is faster by approximately **156%**. ## Maintenance @@ -77,8 +78,6 @@ settings.on_message_complete = handle_on_message_complete; */ llhttp_init(&parser, HTTP_BOTH, &settings); -/* Use `llhttp_set_type(&parser, HTTP_REQUEST);` to override the mode */ - /* Parse request! */ const char* request = "GET / HTTP/1.1\r\n\r\n"; int request_len = strlen(request); diff --git a/deps/llhttp/include/llhttp.h b/deps/llhttp/include/llhttp.h index 1671af4d088d26..719abe8aed2ba5 100644 --- a/deps/llhttp/include/llhttp.h +++ b/deps/llhttp/include/llhttp.h @@ -1,9 +1,9 @@ #ifndef INCLUDE_LLHTTP_H_ #define INCLUDE_LLHTTP_H_ -#define LLHTTP_VERSION_MAJOR 1 -#define LLHTTP_VERSION_MINOR 1 -#define LLHTTP_VERSION_PATCH 4 +#define LLHTTP_VERSION_MAJOR 2 +#define LLHTTP_VERSION_MINOR 0 +#define LLHTTP_VERSION_PATCH 1 #ifndef INCLUDE_LLHTTP_ITSELF_H_ #define INCLUDE_LLHTTP_ITSELF_H_ @@ -29,7 +29,7 @@ struct llhttp__internal_s { uint8_t http_major; uint8_t http_minor; uint8_t header_state; - uint8_t flags; + uint16_t flags; uint8_t upgrade; uint16_t status_code; uint8_t finish; @@ -85,7 +85,8 @@ enum llhttp_flags { F_UPGRADE = 0x10, F_CONTENT_LENGTH = 0x20, F_SKIPBODY = 0x40, - F_TRAILING = 0x80 + F_TRAILING = 0x80, + F_LENIENT = 0x100 }; typedef enum llhttp_flags llhttp_flags_t; @@ -297,7 +298,7 @@ llhttp_errno_t llhttp_finish(llhttp_t* parser); int llhttp_message_needs_eof(const llhttp_t* parser); /* Returns `1` if there might be any other messages following the last that was - * successfuly parsed. + * successfully parsed. */ int llhttp_should_keep_alive(const llhttp_t* parser); @@ -353,6 +354,18 @@ const char* llhttp_errno_name(llhttp_errno_t err); /* Returns textual name of HTTP method */ const char* llhttp_method_name(llhttp_method_t method); + +/* Enables/disables lenient header value parsing (disabled by default). + * + * Lenient parsing disables header value token checks, extending llhttp's + * protocol support to highly non-compliant clients/server. No + * `HPE_INVALID_HEADER_TOKEN` will be raised for incorrect header values when + * lenient parsing is "on". + * + * **(USE AT YOUR OWN RISK)** + */ +void llhttp_set_lenient(llhttp_t* parser, int enabled); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/deps/llhttp/src/api.c b/deps/llhttp/src/api.c index 45227b35afb209..6f7246546dfe1a 100644 --- a/deps/llhttp/src/api.c +++ b/deps/llhttp/src/api.c @@ -127,6 +127,15 @@ const char* llhttp_method_name(llhttp_method_t method) { } +void llhttp_set_lenient(llhttp_t* parser, int enabled) { + if (enabled) { + parser->flags |= F_LENIENT; + } else { + parser->flags &= ~F_LENIENT; + } +} + + /* Callbacks */ diff --git a/deps/llhttp/src/http.c b/deps/llhttp/src/http.c index 67834c2d377c49..65d2ee677e4d33 100644 --- a/deps/llhttp/src/http.c +++ b/deps/llhttp/src/http.c @@ -74,9 +74,11 @@ int llhttp__after_message_complete(llhttp_t* parser, const char* p, int should_keep_alive; should_keep_alive = llhttp_should_keep_alive(parser); - parser->flags = 0; parser->finish = HTTP_FINISH_SAFE; + /* Keep `F_LENIENT` flag between messages, but reset every other flag */ + parser->flags &= F_LENIENT; + /* NOTE: this is ignored in loose parsing mode */ return should_keep_alive; } diff --git a/deps/llhttp/src/llhttp.c b/deps/llhttp/src/llhttp.c index 2786638f3ed6d5..698230f93fe08f 100644 --- a/deps/llhttp/src/llhttp.c +++ b/deps/llhttp/src/llhttp.c @@ -2,6 +2,20 @@ #include #include +#ifdef __SSE4_2__ + #ifdef _MSC_VER + #include + #else /* !_MSC_VER */ + #include + #endif /* _MSC_VER */ +#endif /* __SSE4_2__ */ + +#ifdef _MSC_VER + #define ALIGN(n) _declspec(align(n)) +#else /* !_MSC_VER */ + #define ALIGN(n) __attribute__((aligned(n))) +#endif /* _MSC_VER */ + #include "llhttp.h" typedef int (*llhttp__internal__span_cb)( @@ -10,147 +24,161 @@ typedef int (*llhttp__internal__span_cb)( static const unsigned char llparse_blob0[] = { 'C', 'L' }; -static const unsigned char llparse_blob1[] = { - 'o', 'n' +static const unsigned char ALIGN(16) llparse_blob1[] = { + 0x9, 0x9, 0xc, 0xc, '!', '"', '$', '>', '@', '~', 0x80, + 0xff }; static const unsigned char llparse_blob2[] = { - 'e', 'c', 't', 'i', 'o', 'n' + 'o', 'n' }; static const unsigned char llparse_blob3[] = { - 'l', 'o', 's', 'e' + 'e', 'c', 't', 'i', 'o', 'n' }; static const unsigned char llparse_blob4[] = { - 'e', 'e', 'p', '-', 'a', 'l', 'i', 'v', 'e' + 'l', 'o', 's', 'e' }; static const unsigned char llparse_blob5[] = { - 'p', 'g', 'r', 'a', 'd', 'e' + 'e', 'e', 'p', '-', 'a', 'l', 'i', 'v', 'e' }; static const unsigned char llparse_blob6[] = { + 'p', 'g', 'r', 'a', 'd', 'e' +}; +static const unsigned char ALIGN(16) llparse_blob7[] = { + 0x9, 0x9, ' ', '~', 0x80, 0xfe +}; +static const unsigned char llparse_blob8[] = { 'h', 'u', 'n', 'k', 'e', 'd' }; -static const unsigned char llparse_blob7[] = { +static const unsigned char ALIGN(16) llparse_blob9[] = { + ' ', '!', '#', '\'', '*', '+', '-', '.', '0', '9', 'A', + 'Z', '^', 'z', '|', '|' +}; +static const unsigned char ALIGN(16) llparse_blob10[] = { + '~', '~' +}; +static const unsigned char llparse_blob11[] = { 'e', 'n', 't', '-', 'l', 'e', 'n', 'g', 't', 'h' }; -static const unsigned char llparse_blob8[] = { +static const unsigned char llparse_blob12[] = { 'r', 'o', 'x', 'y', '-', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n' }; -static const unsigned char llparse_blob9[] = { +static const unsigned char llparse_blob13[] = { 'r', 'a', 'n', 's', 'f', 'e', 'r', '-', 'e', 'n', 'c', 'o', 'd', 'i', 'n', 'g' }; -static const unsigned char llparse_blob10[] = { +static const unsigned char llparse_blob14[] = { 'p', 'g', 'r', 'a', 'd', 'e' }; -static const unsigned char llparse_blob11[] = { +static const unsigned char llparse_blob15[] = { 0xd, 0xa }; -static const unsigned char llparse_blob12[] = { +static const unsigned char llparse_blob16[] = { 'T', 'T', 'P', '/' }; -static const unsigned char llparse_blob13[] = { +static const unsigned char llparse_blob17[] = { 'C', 'E', '/' }; -static const unsigned char llparse_blob14[] = { +static const unsigned char llparse_blob18[] = { 'I', 'N', 'D' }; -static const unsigned char llparse_blob15[] = { +static const unsigned char llparse_blob19[] = { 'E', 'C', 'K', 'O', 'U', 'T' }; -static const unsigned char llparse_blob16[] = { +static const unsigned char llparse_blob20[] = { 'N', 'E', 'C', 'T' }; -static const unsigned char llparse_blob17[] = { +static const unsigned char llparse_blob21[] = { 'E', 'L', 'E', 'T', 'E' }; -static const unsigned char llparse_blob18[] = { +static const unsigned char llparse_blob22[] = { 'E', 'T' }; -static const unsigned char llparse_blob19[] = { +static const unsigned char llparse_blob23[] = { 'E', 'A', 'D' }; -static const unsigned char llparse_blob20[] = { +static const unsigned char llparse_blob24[] = { 'N', 'K' }; -static const unsigned char llparse_blob21[] = { +static const unsigned char llparse_blob25[] = { 'C', 'K' }; -static const unsigned char llparse_blob22[] = { +static const unsigned char llparse_blob26[] = { 'S', 'E', 'A', 'R', 'C', 'H' }; -static const unsigned char llparse_blob23[] = { +static const unsigned char llparse_blob27[] = { 'R', 'G', 'E' }; -static const unsigned char llparse_blob24[] = { +static const unsigned char llparse_blob28[] = { 'C', 'T', 'I', 'V', 'I', 'T', 'Y' }; -static const unsigned char llparse_blob25[] = { +static const unsigned char llparse_blob29[] = { 'L', 'E', 'N', 'D', 'A', 'R' }; -static const unsigned char llparse_blob26[] = { +static const unsigned char llparse_blob30[] = { 'V', 'E' }; -static const unsigned char llparse_blob27[] = { +static const unsigned char llparse_blob31[] = { 'O', 'T', 'I', 'F', 'Y' }; -static const unsigned char llparse_blob28[] = { +static const unsigned char llparse_blob32[] = { 'P', 'T', 'I', 'O', 'N', 'S' }; -static const unsigned char llparse_blob29[] = { +static const unsigned char llparse_blob33[] = { 'T', 'C', 'H' }; -static const unsigned char llparse_blob30[] = { +static const unsigned char llparse_blob34[] = { 'S', 'T' }; -static const unsigned char llparse_blob31[] = { +static const unsigned char llparse_blob35[] = { 'O', 'P' }; -static const unsigned char llparse_blob32[] = { +static const unsigned char llparse_blob36[] = { 'I', 'N', 'D' }; -static const unsigned char llparse_blob33[] = { +static const unsigned char llparse_blob37[] = { 'A', 'T', 'C', 'H' }; -static const unsigned char llparse_blob34[] = { +static const unsigned char llparse_blob38[] = { 'G', 'E' }; -static const unsigned char llparse_blob35[] = { +static const unsigned char llparse_blob39[] = { 'I', 'N', 'D' }; -static const unsigned char llparse_blob36[] = { +static const unsigned char llparse_blob40[] = { 'O', 'R', 'T' }; -static const unsigned char llparse_blob37[] = { +static const unsigned char llparse_blob41[] = { 'A', 'R', 'C', 'H' }; -static const unsigned char llparse_blob38[] = { +static const unsigned char llparse_blob42[] = { 'U', 'R', 'C', 'E' }; -static const unsigned char llparse_blob39[] = { +static const unsigned char llparse_blob43[] = { 'B', 'S', 'C', 'R', 'I', 'B', 'E' }; -static const unsigned char llparse_blob40[] = { +static const unsigned char llparse_blob44[] = { 'R', 'A', 'C', 'E' }; -static const unsigned char llparse_blob41[] = { +static const unsigned char llparse_blob45[] = { 'I', 'N', 'D' }; -static const unsigned char llparse_blob42[] = { +static const unsigned char llparse_blob46[] = { 'N', 'K' }; -static const unsigned char llparse_blob43[] = { +static const unsigned char llparse_blob47[] = { 'C', 'K' }; -static const unsigned char llparse_blob44[] = { +static const unsigned char llparse_blob48[] = { 'U', 'B', 'S', 'C', 'R', 'I', 'B', 'E' }; -static const unsigned char llparse_blob45[] = { +static const unsigned char llparse_blob49[] = { 'H', 'T', 'T', 'P', '/' }; -static const unsigned char llparse_blob46[] = { +static const unsigned char llparse_blob50[] = { 'A', 'D' }; -static const unsigned char llparse_blob47[] = { +static const unsigned char llparse_blob51[] = { 'T', 'P', '/' }; @@ -259,6 +287,7 @@ enum llparse_state_e { s_n_llhttp__internal__n_header_value_discard_ws_almost_done, s_n_llhttp__internal__n_header_value_lws, s_n_llhttp__internal__n_header_value_almost_done, + s_n_llhttp__internal__n_header_value_lenient, s_n_llhttp__internal__n_header_value_otherwise, s_n_llhttp__internal__n_header_value_connection_token, s_n_llhttp__internal__n_header_value_connection_ws, @@ -655,6 +684,13 @@ int llhttp__internal__c_update_header_state_2( return 0; } +int llhttp__internal__c_test_flags_2( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->flags & 256) == 256; +} + int llhttp__internal__c_update_header_state_4( llhttp__internal_t* state, const unsigned char* p, @@ -679,7 +715,7 @@ int llhttp__internal__c_update_header_state_6( return 0; } -int llhttp__internal__c_test_flags_2( +int llhttp__internal__c_test_flags_3( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { @@ -1394,6 +1430,26 @@ static llparse_state_t llhttp__internal__run( /* UNREACHABLE */; abort(); } + case s_n_llhttp__internal__n_header_value_lenient: + s_n_llhttp__internal__n_header_value_lenient: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_lenient; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_1; + } + case 13: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_3; + } + default: { + p++; + goto s_n_llhttp__internal__n_header_value_lenient; + } + } + /* UNREACHABLE */; + abort(); + } case s_n_llhttp__internal__n_header_value_otherwise: s_n_llhttp__internal__n_header_value_otherwise: { if (p == endp) { @@ -1407,7 +1463,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_2; } default: { - goto s_n_llhttp__internal__n_error_13; + goto s_n_llhttp__internal__n_invoke_test_flags_2; } } /* UNREACHABLE */; @@ -1486,7 +1542,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_header_value_connection_1; } - match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob3, 4); + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob4, 4); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -1510,7 +1566,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_header_value_connection_2; } - match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob4, 9); + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob5, 9); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -1534,7 +1590,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_header_value_connection_3; } - match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob5, 6); + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob6, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -1617,6 +1673,30 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_header_value; } + #ifdef __SSE4_2__ + if (endp - p >= 16) { + __m128i ranges; + __m128i input; + int avail; + int match_len; + + /* Load input */ + input = _mm_loadu_si128((__m128i const*) p); + ranges = _mm_loadu_si128((__m128i const*) llparse_blob7); + + /* Find first character that does not match `ranges` */ + match_len = _mm_cmpestri(ranges, 6, + input, 16, + _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | + _SIDD_NEGATIVE_POLARITY); + + if (match_len != 0) { + p += match_len; + goto s_n_llhttp__internal__n_header_value; + } + goto s_n_llhttp__internal__n_header_value_otherwise; + } + #endif /* __SSE4_2__ */ switch (lookup_table[(uint8_t) *p]) { case 1: { p++; @@ -1679,7 +1759,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_header_value_content_length_ws; } default: { - goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_4; + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_5; } } /* UNREACHABLE */; @@ -1755,7 +1835,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_header_value_te_chunked_1; } - match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob6, 6); + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob8, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -1877,6 +1957,42 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_header_field_general; } + #ifdef __SSE4_2__ + if (endp - p >= 16) { + __m128i ranges; + __m128i input; + int avail; + int match_len; + + /* Load input */ + input = _mm_loadu_si128((__m128i const*) p); + ranges = _mm_loadu_si128((__m128i const*) llparse_blob9); + + /* Find first character that does not match `ranges` */ + match_len = _mm_cmpestri(ranges, 16, + input, 16, + _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | + _SIDD_NEGATIVE_POLARITY); + + if (match_len != 0) { + p += match_len; + goto s_n_llhttp__internal__n_header_field_general; + } + ranges = _mm_loadu_si128((__m128i const*) llparse_blob10); + + /* Find first character that does not match `ranges` */ + match_len = _mm_cmpestri(ranges, 2, + input, 16, + _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | + _SIDD_NEGATIVE_POLARITY); + + if (match_len != 0) { + p += match_len; + goto s_n_llhttp__internal__n_header_field_general; + } + goto s_n_llhttp__internal__n_header_field_general_otherwise; + } + #endif /* __SSE4_2__ */ switch (lookup_table[(uint8_t) *p]) { case 1: { p++; @@ -1916,7 +2032,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_header_field_3; } - match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob2, 6); + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob3, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -1941,7 +2057,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_header_field_4; } - match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob7, 10); + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob11, 10); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -1987,7 +2103,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_header_field_1; } - match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob1, 2); + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob2, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -2011,7 +2127,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_header_field_5; } - match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob8, 15); + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob12, 15); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -2036,7 +2152,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_header_field_6; } - match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob9, 16); + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob13, 16); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -2061,7 +2177,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_header_field_7; } - match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob10, 6); + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob14, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -2156,7 +2272,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_url_skip_lf_to_http09; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob11, 2); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob15, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -2361,7 +2477,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_req_http_start_1; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob12, 4); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob16, 4); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -2385,7 +2501,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_req_http_start_2; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob13, 3); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob17, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -2591,6 +2707,30 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_url_path; } + #ifdef __SSE4_2__ + if (endp - p >= 16) { + __m128i ranges; + __m128i input; + int avail; + int match_len; + + /* Load input */ + input = _mm_loadu_si128((__m128i const*) p); + ranges = _mm_loadu_si128((__m128i const*) llparse_blob1); + + /* Find first character that does not match `ranges` */ + match_len = _mm_cmpestri(ranges, 12, + input, 16, + _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | + _SIDD_NEGATIVE_POLARITY); + + if (match_len != 0) { + p += match_len; + goto s_n_llhttp__internal__n_url_path; + } + goto s_n_llhttp__internal__n_url_query_or_fragment; + } + #endif /* __SSE4_2__ */ switch (lookup_table[(uint8_t) *p]) { case 1: { p++; @@ -2970,7 +3110,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_2; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob14, 3); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob18, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -2995,7 +3135,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_4; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob15, 6); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob19, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3020,7 +3160,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_6; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob16, 4); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob20, 4); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3105,7 +3245,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_8; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob17, 5); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob21, 5); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3130,7 +3270,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_9; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob18, 2); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob22, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3155,7 +3295,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_10; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob19, 3); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob23, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3180,7 +3320,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_12; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob20, 2); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob24, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3205,7 +3345,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_13; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob21, 2); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob25, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3251,7 +3391,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_15; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob22, 6); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob26, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3276,7 +3416,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_16; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob23, 3); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob27, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3301,7 +3441,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_18; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob24, 7); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob28, 7); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3326,7 +3466,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_20; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob25, 6); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob29, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3411,7 +3551,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_22; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob26, 2); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob30, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3465,7 +3605,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_23; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob27, 5); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob31, 5); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3490,7 +3630,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_24; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob28, 6); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob32, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3515,7 +3655,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_26; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob29, 3); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob33, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3540,7 +3680,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_27; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob30, 2); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob34, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3565,7 +3705,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_30; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob32, 3); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob36, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3590,7 +3730,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_31; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob33, 4); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob37, 4); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3636,7 +3776,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_28; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob31, 2); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob35, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3660,7 +3800,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_33; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob34, 2); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob38, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3736,7 +3876,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_36; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob35, 3); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob39, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3761,7 +3901,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_37; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob36, 3); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob40, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3824,7 +3964,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_39; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob37, 4); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob41, 4); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3849,7 +3989,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_40; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob38, 4); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob42, 4); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3874,7 +4014,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_41; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob39, 7); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob43, 7); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3924,7 +4064,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_42; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob40, 4); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob44, 4); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3949,7 +4089,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_45; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob41, 3); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob45, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3974,7 +4114,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_47; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob42, 2); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob46, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3999,7 +4139,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_48; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob43, 2); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob47, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -4045,7 +4185,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_49; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob44, 8); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob48, 8); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -4493,7 +4633,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_res; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob45, 5); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob49, 5); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -4517,7 +4657,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_req_or_res_method_2; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob46, 2); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob50, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -4542,7 +4682,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_req_or_res_method_3; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob47, 3); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob51, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -5191,6 +5331,24 @@ static llparse_state_t llhttp__internal__run( /* UNREACHABLE */; abort(); } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_3: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_header_value_almost_done; + /* UNREACHABLE */; + abort(); + } s_n_llhttp__internal__n_error_13: { state->error = 0xa; state->reason = "Invalid header value char"; @@ -5200,6 +5358,16 @@ static llparse_state_t llhttp__internal__run( /* UNREACHABLE */; abort(); } + s_n_llhttp__internal__n_invoke_test_flags_2: { + switch (llhttp__internal__c_test_flags_2(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_header_value_lenient; + default: + goto s_n_llhttp__internal__n_error_13; + } + /* UNREACHABLE */; + abort(); + } s_n_llhttp__internal__n_invoke_update_header_state_3: { switch (llhttp__internal__c_update_header_state(state, p, endp)) { default: @@ -5288,7 +5456,7 @@ static llparse_state_t llhttp__internal__run( /* UNREACHABLE */; abort(); } - s_n_llhttp__internal__n_span_end_llhttp__on_header_value_3: { + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_4: { const unsigned char* start; int err; @@ -5308,7 +5476,7 @@ static llparse_state_t llhttp__internal__run( s_n_llhttp__internal__n_invoke_mul_add_content_length_1: { switch (llhttp__internal__c_mul_add_content_length_1(state, p, endp, match)) { case 1: - goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_3; + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_4; default: goto s_n_llhttp__internal__n_header_value_content_length; } @@ -5331,7 +5499,7 @@ static llparse_state_t llhttp__internal__run( /* UNREACHABLE */; abort(); } - s_n_llhttp__internal__n_span_end_llhttp__on_header_value_4: { + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_5: { const unsigned char* start; int err; @@ -5357,8 +5525,8 @@ static llparse_state_t llhttp__internal__run( /* UNREACHABLE */; abort(); } - s_n_llhttp__internal__n_invoke_test_flags_2: { - switch (llhttp__internal__c_test_flags_2(state, p, endp)) { + s_n_llhttp__internal__n_invoke_test_flags_3: { + switch (llhttp__internal__c_test_flags_3(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_header_value_content_length; default: @@ -5388,7 +5556,7 @@ static llparse_state_t llhttp__internal__run( case 1: goto s_n_llhttp__internal__n_header_value_connection; case 2: - goto s_n_llhttp__internal__n_invoke_test_flags_2; + goto s_n_llhttp__internal__n_invoke_test_flags_3; case 3: goto s_n_llhttp__internal__n_header_value_te_chunked; case 4: diff --git a/deps/uvwasi/LICENSE b/deps/uvwasi/LICENSE new file mode 100644 index 00000000000000..dfb8546af52b03 --- /dev/null +++ b/deps/uvwasi/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Colin Ihrig and Contributors + +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. diff --git a/deps/uvwasi/include/clocks.h b/deps/uvwasi/include/clocks.h new file mode 100644 index 00000000000000..7437d03df6bfa7 --- /dev/null +++ b/deps/uvwasi/include/clocks.h @@ -0,0 +1,13 @@ +#ifndef __UVWASI_CLOCKS_H__ +#define __UVWASI_CLOCKS_H__ + +#include "wasi_types.h" + +uvwasi_errno_t uvwasi__clock_gettime_realtime(uvwasi_timestamp_t* time); +uvwasi_errno_t uvwasi__clock_gettime_process_cputime(uvwasi_timestamp_t* time); +uvwasi_errno_t uvwasi__clock_gettime_thread_cputime(uvwasi_timestamp_t* time); + +uvwasi_errno_t uvwasi__clock_getres_process_cputime(uvwasi_timestamp_t* time); +uvwasi_errno_t uvwasi__clock_getres_thread_cputime(uvwasi_timestamp_t* time); + +#endif /* __UVWASI_CLOCKS_H__ */ diff --git a/deps/uvwasi/include/fd_table.h b/deps/uvwasi/include/fd_table.h new file mode 100644 index 00000000000000..ef87a3ff8f93c5 --- /dev/null +++ b/deps/uvwasi/include/fd_table.h @@ -0,0 +1,60 @@ +#ifndef __UVWASI_FD_TABLE_H__ +#define __UVWASI_FD_TABLE_H__ + +#include +#include "uv.h" +#include "wasi_types.h" +#include "uv_mapping.h" + +/* TODO(cjihrig): PATH_MAX_BYTES shouldn't be stack allocated. On Windows, paths + can be 32k long, and this PATH_MAX_BYTES is an artificial limitation. */ +#ifdef _WIN32 +/* MAX_PATH is in characters, not bytes. Make sure we have enough headroom. */ +# define PATH_MAX_BYTES (MAX_PATH * 4) +#else +# include +# define PATH_MAX_BYTES (PATH_MAX) +#endif + + +struct uvwasi_fd_wrap_t { + uvwasi_fd_t id; + uv_file fd; + char path[PATH_MAX_BYTES]; + char real_path[PATH_MAX_BYTES]; + uvwasi_filetype_t type; + uvwasi_rights_t rights_base; + uvwasi_rights_t rights_inheriting; + int preopen; + int valid; +}; + +struct uvwasi_fd_table_t { + struct uvwasi_fd_wrap_t* fds; + uint32_t size; + uint32_t used; +}; + +uvwasi_errno_t uvwasi_fd_table_init(struct uvwasi_fd_table_t* table, + uint32_t init_size); +void uvwasi_fd_table_free(struct uvwasi_fd_table_t* table); +uvwasi_errno_t uvwasi_fd_table_insert_preopen(struct uvwasi_fd_table_t* table, + const uv_file fd, + const char* path, + const char* real_path); +uvwasi_errno_t uvwasi_fd_table_insert_fd(struct uvwasi_fd_table_t* table, + const uv_file fd, + const int flags, + const char* path, + uvwasi_rights_t rights_base, + uvwasi_rights_t rights_inheriting, + struct uvwasi_fd_wrap_t* wrap); +uvwasi_errno_t uvwasi_fd_table_get(const struct uvwasi_fd_table_t* table, + const uvwasi_fd_t id, + struct uvwasi_fd_wrap_t** wrap, + uvwasi_rights_t rights_base, + uvwasi_rights_t rights_inheriting); +uvwasi_errno_t uvwasi_fd_table_remove(struct uvwasi_fd_table_t* table, + const uvwasi_fd_t id); + +#endif /* __UVWASI_FD_TABLE_H__ */ diff --git a/deps/uvwasi/include/uv_mapping.h b/deps/uvwasi/include/uv_mapping.h new file mode 100644 index 00000000000000..d835ca507a4856 --- /dev/null +++ b/deps/uvwasi/include/uv_mapping.h @@ -0,0 +1,15 @@ +#ifndef __UVWASI_UV_MAPPING_H__ +#define __UVWASI_UV_MAPPING_H__ + +#include "uv.h" +#include "wasi_types.h" + +#define NANOS_PER_SEC 1000000000 + +uvwasi_errno_t uvwasi__translate_uv_error(int err); +int uvwasi__translate_to_uv_signal(uvwasi_signal_t sig); +uvwasi_timestamp_t uvwasi__timespec_to_timestamp(const uv_timespec_t* ts); +uvwasi_filetype_t uvwasi__stat_to_filetype(const uv_stat_t* stat); +void uvwasi__stat_to_filestat(const uv_stat_t* stat, uvwasi_filestat_t* fs); + +#endif /* __UVWASI_UV_MAPPING_H__ */ diff --git a/deps/uvwasi/include/uvwasi.h b/deps/uvwasi/include/uvwasi.h new file mode 100644 index 00000000000000..a7695734ac2ac6 --- /dev/null +++ b/deps/uvwasi/include/uvwasi.h @@ -0,0 +1,252 @@ +#ifndef __UVWASI_H__ +#define __UVWASI_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "wasi_types.h" +#include "uv_mapping.h" +#include "fd_table.h" + +#define UVWASI_VERSION_MAJOR 0 +#define UVWASI_VERSION_MINOR 0 +#define UVWASI_VERSION_PATCH 1 +#define UVWASI_VERSION_HEX ((UVWASI_VERSION_MAJOR << 16) | \ + (UVWASI_VERSION_MINOR << 8) | \ + (UVWASI_VERSION_PATCH)) +#define UVWASI_STRINGIFY(v) UVWASI_STRINGIFY_HELPER(v) +#define UVWASI_STRINGIFY_HELPER(v) #v +#define UVWASI_VERSION_STRING UVWASI_STRINGIFY(UVWASI_VERSION_MAJOR) "." \ + UVWASI_STRINGIFY(UVWASI_VERSION_MINOR) "." \ + UVWASI_STRINGIFY(UVWASI_VERSION_PATCH) + + +typedef struct uvwasi_s { + struct uvwasi_fd_table_t fds; + size_t argc; + char** argv; + char* argv_buf; + size_t argv_buf_size; + size_t envc; + char** env; + char* env_buf; + size_t env_buf_size; +} uvwasi_t; + +typedef struct uvwasi_preopen_s { + char* mapped_path; + char* real_path; +} uvwasi_preopen_t; + +typedef struct uvwasi_options_s { + size_t fd_table_size; + size_t preopenc; + uvwasi_preopen_t* preopens; + size_t argc; + char** argv; + char** envp; +} uvwasi_options_t; + + +// Embedder API. +uvwasi_errno_t uvwasi_init(uvwasi_t* uvwasi, uvwasi_options_t* options); +void uvwasi_destroy(uvwasi_t* uvwasi); +uvwasi_errno_t uvwasi_embedder_remap_fd(uvwasi_t* uvwasi, + const uvwasi_fd_t fd, + uv_file new_host_fd); + + +// WASI system call API. +uvwasi_errno_t uvwasi_args_get(uvwasi_t* uvwasi, char** argv, char* argv_buf); +uvwasi_errno_t uvwasi_args_sizes_get(uvwasi_t* uvwasi, + size_t* argc, + size_t* argv_buf_size); +uvwasi_errno_t uvwasi_clock_res_get(uvwasi_t* uvwasi, + uvwasi_clockid_t clock_id, + uvwasi_timestamp_t* resolution); +uvwasi_errno_t uvwasi_clock_time_get(uvwasi_t* uvwasi, + uvwasi_clockid_t clock_id, + uvwasi_timestamp_t precision, + uvwasi_timestamp_t* time); +uvwasi_errno_t uvwasi_environ_get(uvwasi_t* uvwasi, + char** environment, + char* environ_buf); +uvwasi_errno_t uvwasi_environ_sizes_get(uvwasi_t* uvwasi, + size_t* environ_count, + size_t* environ_buf_size); +uvwasi_errno_t uvwasi_fd_advise(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_filesize_t offset, + uvwasi_filesize_t len, + uvwasi_advice_t advice); +uvwasi_errno_t uvwasi_fd_allocate(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_filesize_t offset, + uvwasi_filesize_t len); +uvwasi_errno_t uvwasi_fd_close(uvwasi_t* uvwasi, uvwasi_fd_t fd); +uvwasi_errno_t uvwasi_fd_datasync(uvwasi_t* uvwasi, uvwasi_fd_t fd); +uvwasi_errno_t uvwasi_fd_fdstat_get(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_fdstat_t* buf); +uvwasi_errno_t uvwasi_fd_fdstat_set_flags(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_fdflags_t flags); +uvwasi_errno_t uvwasi_fd_fdstat_set_rights(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_rights_t fs_rights_base, + uvwasi_rights_t fs_rights_inheriting + ); +uvwasi_errno_t uvwasi_fd_filestat_get(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_filestat_t* buf); +uvwasi_errno_t uvwasi_fd_filestat_set_size(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_filesize_t st_size); +uvwasi_errno_t uvwasi_fd_filestat_set_times(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_timestamp_t st_atim, + uvwasi_timestamp_t st_mtim, + uvwasi_fstflags_t fst_flags); +uvwasi_errno_t uvwasi_fd_pread(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + const uvwasi_iovec_t* iovs, + size_t iovs_len, + uvwasi_filesize_t offset, + size_t* nread); +uvwasi_errno_t uvwasi_fd_prestat_get(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_prestat_t* buf); +uvwasi_errno_t uvwasi_fd_prestat_dir_name(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + char* path, + size_t path_len); +uvwasi_errno_t uvwasi_fd_pwrite(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + const uvwasi_ciovec_t* iovs, + size_t iovs_len, + uvwasi_filesize_t offset, + size_t* nwritten); +uvwasi_errno_t uvwasi_fd_read(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + const uvwasi_iovec_t* iovs, + size_t iovs_len, + size_t* nread); +uvwasi_errno_t uvwasi_fd_readdir(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + void* buf, + size_t buf_len, + uvwasi_dircookie_t cookie, + size_t* bufused); +uvwasi_errno_t uvwasi_fd_renumber(uvwasi_t* uvwasi, + uvwasi_fd_t from, + uvwasi_fd_t to); +uvwasi_errno_t uvwasi_fd_seek(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_filedelta_t offset, + uvwasi_whence_t whence, + uvwasi_filesize_t* newoffset); +uvwasi_errno_t uvwasi_fd_sync(uvwasi_t* uvwasi, uvwasi_fd_t fd); +uvwasi_errno_t uvwasi_fd_tell(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_filesize_t* offset); +uvwasi_errno_t uvwasi_fd_write(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + const uvwasi_ciovec_t* iovs, + size_t iovs_len, + size_t* nwritten); +uvwasi_errno_t uvwasi_path_create_directory(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + const char* path, + size_t path_len); +uvwasi_errno_t uvwasi_path_filestat_get(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_lookupflags_t flags, + const char* path, + size_t path_len, + uvwasi_filestat_t* buf); +uvwasi_errno_t uvwasi_path_filestat_set_times(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_lookupflags_t flags, + const char* path, + size_t path_len, + uvwasi_timestamp_t st_atim, + uvwasi_timestamp_t st_mtim, + uvwasi_fstflags_t fst_flags); +uvwasi_errno_t uvwasi_path_link(uvwasi_t* uvwasi, + uvwasi_fd_t old_fd, + uvwasi_lookupflags_t old_flags, + const char* old_path, + size_t old_path_len, + uvwasi_fd_t new_fd, + const char* new_path, + size_t new_path_len); +uvwasi_errno_t uvwasi_path_open(uvwasi_t* uvwasi, + uvwasi_fd_t dirfd, + uvwasi_lookupflags_t dirflags, + const char* path, + size_t path_len, + uvwasi_oflags_t o_flags, + uvwasi_rights_t fs_rights_base, + uvwasi_rights_t fs_rights_inheriting, + uvwasi_fdflags_t fs_flags, + uvwasi_fd_t* fd); +uvwasi_errno_t uvwasi_path_readlink(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + const char* path, + size_t path_len, + char* buf, + size_t buf_len, + size_t* bufused); +uvwasi_errno_t uvwasi_path_remove_directory(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + const char* path, + size_t path_len); +uvwasi_errno_t uvwasi_path_rename(uvwasi_t* uvwasi, + uvwasi_fd_t old_fd, + const char* old_path, + size_t old_path_len, + uvwasi_fd_t new_fd, + const char* new_path, + size_t new_path_len); +uvwasi_errno_t uvwasi_path_symlink(uvwasi_t* uvwasi, + const char* old_path, + size_t old_path_len, + uvwasi_fd_t fd, + const char* new_path, + size_t new_path_len); +uvwasi_errno_t uvwasi_path_unlink_file(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + const char* path, + size_t path_len); +uvwasi_errno_t uvwasi_poll_oneoff(uvwasi_t* uvwasi, + const uvwasi_subscription_t* in, + uvwasi_event_t* out, + size_t nsubscriptions, + size_t* nevents); +uvwasi_errno_t uvwasi_proc_exit(uvwasi_t* uvwasi, uvwasi_exitcode_t rval); +uvwasi_errno_t uvwasi_proc_raise(uvwasi_t* uvwasi, uvwasi_signal_t sig); +uvwasi_errno_t uvwasi_random_get(uvwasi_t* uvwasi, void* buf, size_t buf_len); +uvwasi_errno_t uvwasi_sched_yield(uvwasi_t* uvwasi); +uvwasi_errno_t uvwasi_sock_recv(uvwasi_t* uvwasi, + uvwasi_fd_t sock, + const uvwasi_iovec_t* ri_data, + size_t ri_data_len, + uvwasi_riflags_t ri_flags, + size_t* ro_datalen, + uvwasi_roflags_t* ro_flags); +uvwasi_errno_t uvwasi_sock_send(uvwasi_t* uvwasi, + uvwasi_fd_t sock, + const uvwasi_ciovec_t* si_data, + size_t si_data_len, + uvwasi_siflags_t si_flags, + size_t* so_datalen); +uvwasi_errno_t uvwasi_sock_shutdown(uvwasi_t* uvwasi, + uvwasi_fd_t sock, + uvwasi_sdflags_t how); + +#ifdef __cplusplus +} +#endif + +#endif /* __UVWASI_H__ */ diff --git a/deps/uvwasi/include/wasi_types.h b/deps/uvwasi/include/wasi_types.h new file mode 100644 index 00000000000000..34b5291c577627 --- /dev/null +++ b/deps/uvwasi/include/wasi_types.h @@ -0,0 +1,323 @@ +#ifndef __UVWASI_WASI_TYPES_H__ +#define __UVWASI_WASI_TYPES_H__ + +#include +#include + +/* API: https://github.com/WebAssembly/WASI/blob/master/phases/unstable/docs/wasi_unstable_preview0.md */ + +typedef uint8_t uvwasi_advice_t; +#define UVWASI_ADVICE_NORMAL 0 +#define UVWASI_ADVICE_SEQUENTIAL 1 +#define UVWASI_ADVICE_RANDOM 2 +#define UVWASI_ADVICE_WILLNEED 3 +#define UVWASI_ADVICE_DONTNEED 4 +#define UVWASI_ADVICE_NOREUSE 5 + +typedef struct uvwasi_ciovec_s { + const void* buf; + size_t buf_len; +} uvwasi_ciovec_t; + +typedef uint32_t uvwasi_clockid_t; +#define UVWASI_CLOCK_REALTIME 0 +#define UVWASI_CLOCK_MONOTONIC 1 +#define UVWASI_CLOCK_PROCESS_CPUTIME_ID 2 +#define UVWASI_CLOCK_THREAD_CPUTIME_ID 3 + +typedef uint64_t uvwasi_device_t; + +typedef uint64_t uvwasi_dircookie_t; +#define UVWASI_DIRCOOKIE_START 0 + +typedef uint16_t uvwasi_errno_t; +#define UVWASI_ESUCCESS 0 +#define UVWASI_E2BIG 1 +#define UVWASI_EACCES 2 +#define UVWASI_EADDRINUSE 3 +#define UVWASI_EADDRNOTAVAIL 4 +#define UVWASI_EAFNOSUPPORT 5 +#define UVWASI_EAGAIN 6 +#define UVWASI_EALREADY 7 +#define UVWASI_EBADF 8 +#define UVWASI_EBADMSG 9 +#define UVWASI_EBUSY 10 +#define UVWASI_ECANCELED 11 +#define UVWASI_ECHILD 12 +#define UVWASI_ECONNABORTED 13 +#define UVWASI_ECONNREFUSED 14 +#define UVWASI_ECONNRESET 15 +#define UVWASI_EDEADLK 16 +#define UVWASI_EDESTADDRREQ 17 +#define UVWASI_EDOM 18 +#define UVWASI_EDQUOT 19 +#define UVWASI_EEXIST 20 +#define UVWASI_EFAULT 21 +#define UVWASI_EFBIG 22 +#define UVWASI_EHOSTUNREACH 23 +#define UVWASI_EIDRM 24 +#define UVWASI_EILSEQ 25 +#define UVWASI_EINPROGRESS 26 +#define UVWASI_EINTR 27 +#define UVWASI_EINVAL 28 +#define UVWASI_EIO 29 +#define UVWASI_EISCONN 30 +#define UVWASI_EISDIR 31 +#define UVWASI_ELOOP 32 +#define UVWASI_EMFILE 33 +#define UVWASI_EMLINK 34 +#define UVWASI_EMSGSIZE 35 +#define UVWASI_EMULTIHOP 36 +#define UVWASI_ENAMETOOLONG 37 +#define UVWASI_ENETDOWN 38 +#define UVWASI_ENETRESET 39 +#define UVWASI_ENETUNREACH 40 +#define UVWASI_ENFILE 41 +#define UVWASI_ENOBUFS 42 +#define UVWASI_ENODEV 43 +#define UVWASI_ENOENT 44 +#define UVWASI_ENOEXEC 45 +#define UVWASI_ENOLCK 46 +#define UVWASI_ENOLINK 47 +#define UVWASI_ENOMEM 48 +#define UVWASI_ENOMSG 49 +#define UVWASI_ENOPROTOOPT 50 +#define UVWASI_ENOSPC 51 +#define UVWASI_ENOSYS 52 +#define UVWASI_ENOTCONN 53 +#define UVWASI_ENOTDIR 54 +#define UVWASI_ENOTEMPTY 55 +#define UVWASI_ENOTRECOVERABLE 56 +#define UVWASI_ENOTSOCK 57 +#define UVWASI_ENOTSUP 58 +#define UVWASI_ENOTTY 59 +#define UVWASI_ENXIO 60 +#define UVWASI_EOVERFLOW 61 +#define UVWASI_EOWNERDEAD 62 +#define UVWASI_EPERM 63 +#define UVWASI_EPIPE 64 +#define UVWASI_EPROTO 65 +#define UVWASI_EPROTONOSUPPORT 66 +#define UVWASI_EPROTOTYPE 67 +#define UVWASI_ERANGE 68 +#define UVWASI_EROFS 69 +#define UVWASI_ESPIPE 70 +#define UVWASI_ESRCH 71 +#define UVWASI_ESTALE 72 +#define UVWASI_ETIMEDOUT 73 +#define UVWASI_ETXTBSY 74 +#define UVWASI_EXDEV 75 +#define UVWASI_ENOTCAPABLE 76 + +typedef uint16_t uvwasi_eventrwflags_t; /* Bitfield */ +#define UVWASI_EVENT_FD_READWRITE_HANGUP (1 << 0) + +typedef uint8_t uvwasi_eventtype_t; +#define UVWASI_EVENTTYPE_CLOCK 0 +#define UVWASI_EVENTTYPE_FD_READ 1 +#define UVWASI_EVENTTYPE_FD_WRITE 2 + +typedef uint32_t uvwasi_exitcode_t; + +typedef uint32_t uvwasi_fd_t; + +typedef uint16_t uvwasi_fdflags_t; /* Bitfield */ +#define UVWASI_FDFLAG_APPEND (1 << 0) +#define UVWASI_FDFLAG_DSYNC (1 << 1) +#define UVWASI_FDFLAG_NONBLOCK (1 << 2) +#define UVWASI_FDFLAG_RSYNC (1 << 3) +#define UVWASI_FDFLAG_SYNC (1 << 4) + +typedef int64_t uvwasi_filedelta_t; + +typedef uint64_t uvwasi_filesize_t; + +typedef uint8_t uvwasi_filetype_t; +#define UVWASI_FILETYPE_UNKNOWN 0 +#define UVWASI_FILETYPE_BLOCK_DEVICE 1 +#define UVWASI_FILETYPE_CHARACTER_DEVICE 2 +#define UVWASI_FILETYPE_DIRECTORY 3 +#define UVWASI_FILETYPE_REGULAR_FILE 4 +#define UVWASI_FILETYPE_SOCKET_DGRAM 5 +#define UVWASI_FILETYPE_SOCKET_STREAM 6 +#define UVWASI_FILETYPE_SYMBOLIC_LINK 7 + +typedef uint16_t uvwasi_fstflags_t; /* Bitfield */ +#define UVWASI_FILESTAT_SET_ATIM (1 << 0) +#define UVWASI_FILESTAT_SET_ATIM_NOW (1 << 1) +#define UVWASI_FILESTAT_SET_MTIM (1 << 2) +#define UVWASI_FILESTAT_SET_MTIM_NOW (1 << 3) + +typedef uint64_t uvwasi_inode_t; + +typedef struct uvwasi_iovec_s { + void* buf; + size_t buf_len; +} uvwasi_iovec_t; + +typedef uint32_t uvwasi_linkcount_t; + +typedef uint32_t uvwasi_lookupflags_t; /* Bitfield */ +#define UVWASI_LOOKUP_SYMLINK_FOLLOW (1 << 0) + +typedef uint16_t uvwasi_oflags_t; /* Bitfield */ +#define UVWASI_O_CREAT (1 << 0) +#define UVWASI_O_DIRECTORY (1 << 1) +#define UVWASI_O_EXCL (1 << 2) +#define UVWASI_O_TRUNC (1 << 3) + +typedef uint8_t uvwasi_preopentype_t; +#define UVWASI_PREOPENTYPE_DIR 0 + +typedef struct uvwasi_prestat_s { + uvwasi_preopentype_t pr_type; + union uvwasi_prestat_u { + struct uvwasi_prestat_dir_t { + size_t pr_name_len; + } dir; + } u; +} uvwasi_prestat_t; + +typedef uint16_t uvwasi_riflags_t; /* Bitfield */ +#define UVWASI_SOCK_RECV_PEEK (1 << 0) +#define UVWASI_SOCK_RECV_WAITALL (1 << 1) + +typedef uint64_t uvwasi_rights_t; /* Bitfield */ +#define UVWASI_RIGHT_FD_DATASYNC (1 << 0) +#define UVWASI_RIGHT_FD_READ (1 << 1) +#define UVWASI_RIGHT_FD_SEEK (1 << 2) +#define UVWASI_RIGHT_FD_FDSTAT_SET_FLAGS (1 << 3) +#define UVWASI_RIGHT_FD_SYNC (1 << 4) +#define UVWASI_RIGHT_FD_TELL (1 << 5) +#define UVWASI_RIGHT_FD_WRITE (1 << 6) +#define UVWASI_RIGHT_FD_ADVISE (1 << 7) +#define UVWASI_RIGHT_FD_ALLOCATE (1 << 8) +#define UVWASI_RIGHT_PATH_CREATE_DIRECTORY (1 << 9) +#define UVWASI_RIGHT_PATH_CREATE_FILE (1 << 10) +#define UVWASI_RIGHT_PATH_LINK_SOURCE (1 << 11) +#define UVWASI_RIGHT_PATH_LINK_TARGET (1 << 12) +#define UVWASI_RIGHT_PATH_OPEN (1 << 13) +#define UVWASI_RIGHT_FD_READDIR (1 << 14) +#define UVWASI_RIGHT_PATH_READLINK (1 << 15) +#define UVWASI_RIGHT_PATH_RENAME_SOURCE (1 << 16) +#define UVWASI_RIGHT_PATH_RENAME_TARGET (1 << 17) +#define UVWASI_RIGHT_PATH_FILESTAT_GET (1 << 18) +#define UVWASI_RIGHT_PATH_FILESTAT_SET_SIZE (1 << 19) +#define UVWASI_RIGHT_PATH_FILESTAT_SET_TIMES (1 << 20) +#define UVWASI_RIGHT_FD_FILESTAT_GET (1 << 21) +#define UVWASI_RIGHT_FD_FILESTAT_SET_SIZE (1 << 22) +#define UVWASI_RIGHT_FD_FILESTAT_SET_TIMES (1 << 23) +#define UVWASI_RIGHT_PATH_SYMLINK (1 << 24) +#define UVWASI_RIGHT_PATH_REMOVE_DIRECTORY (1 << 25) +#define UVWASI_RIGHT_PATH_UNLINK_FILE (1 << 26) +#define UVWASI_RIGHT_POLL_FD_READWRITE (1 << 27) +#define UVWASI_RIGHT_SOCK_SHUTDOWN (1 << 28) + +typedef uint16_t uvwasi_roflags_t; /* Bitfield */ +#define UVWASI_SOCK_RECV_DATA_TRUNCATED (1 << 0) + +typedef uint8_t uvwasi_sdflags_t; /* Bitfield */ +#define UVWASI_SHUT_RD (1 << 0) +#define UVWASI_SHUT_WR (1 << 1) + +typedef uint16_t uvwasi_siflags_t; /* Bitfield */ + +typedef uint8_t uvwasi_signal_t; +#define UVWASI_SIGHUP 1 +#define UVWASI_SIGINT 2 +#define UVWASI_SIGQUIT 3 +#define UVWASI_SIGILL 4 +#define UVWASI_SIGTRAP 5 +#define UVWASI_SIGABRT 6 +#define UVWASI_SIGBUS 7 +#define UVWASI_SIGFPE 8 +#define UVWASI_SIGKILL 9 +#define UVWASI_SIGUSR1 10 +#define UVWASI_SIGSEGV 11 +#define UVWASI_SIGUSR2 12 +#define UVWASI_SIGPIPE 13 +#define UVWASI_SIGALRM 14 +#define UVWASI_SIGTERM 15 +#define UVWASI_SIGCHLD 16 +#define UVWASI_SIGCONT 17 +#define UVWASI_SIGSTOP 18 +#define UVWASI_SIGTSTP 19 +#define UVWASI_SIGTTIN 20 +#define UVWASI_SIGTTOU 21 +#define UVWASI_SIGURG 22 +#define UVWASI_SIGXCPU 23 +#define UVWASI_SIGXFSZ 24 +#define UVWASI_SIGVTALRM 25 +#define UVWASI_SIGPROF 26 +#define UVWASI_SIGWINCH 27 +#define UVWASI_SIGPOLL 28 +#define UVWASI_SIGPWR 29 +#define UVWASI_SIGSYS 30 + +typedef uint16_t uvwasi_subclockflags_t; /* Bitfield */ +#define UVWASI_SUBSCRIPTION_CLOCK_ABSTIME (1 << 0) + +typedef uint64_t uvwasi_timestamp_t; + +typedef uint64_t uvwasi_userdata_t; + +typedef struct uvwasi_subscription_s { + uvwasi_userdata_t userdata; + uvwasi_eventtype_t type; + union { + struct { + uvwasi_userdata_t identifier; + uvwasi_clockid_t clock_id; + uvwasi_timestamp_t timeout; + uvwasi_timestamp_t precision; + uvwasi_subclockflags_t flags; + } clock; + struct { + uvwasi_fd_t fd; + } fd_readwrite; + } u; +} uvwasi_subscription_t; + +typedef struct uvwasi_dirent_s { + uvwasi_dircookie_t d_next; + uvwasi_inode_t d_ino; + uint32_t d_namlen; + uvwasi_filetype_t d_type; +} uvwasi_dirent_t; + +typedef struct uvwasi_fdstat_s { + uvwasi_filetype_t fs_filetype; + uvwasi_fdflags_t fs_flags; + uvwasi_rights_t fs_rights_base; + uvwasi_rights_t fs_rights_inheriting; +} uvwasi_fdstat_t; + +typedef struct uvwasi_filestat_s { + uvwasi_device_t st_dev; + uvwasi_inode_t st_ino; + uvwasi_filetype_t st_filetype; + uvwasi_linkcount_t st_nlink; + uvwasi_filesize_t st_size; + uvwasi_timestamp_t st_atim; + uvwasi_timestamp_t st_mtim; + uvwasi_timestamp_t st_ctim; +} uvwasi_filestat_t; + +typedef struct uvwasi_event_s { + uvwasi_userdata_t userdata; + uvwasi_errno_t error; + uvwasi_eventtype_t type; + union { + struct { + uvwasi_filesize_t nbytes; + uvwasi_eventrwflags_t flags; + } fd_readwrite; + } u; +} uvwasi_event_t; + +typedef uint8_t uvwasi_whence_t; +#define UVWASI_WHENCE_CUR 0 +#define UVWASI_WHENCE_END 1 +#define UVWASI_WHENCE_SET 2 + +#endif /* __UVWASI_WASI_TYPES_H__ */ diff --git a/deps/uvwasi/src/clocks.c b/deps/uvwasi/src/clocks.c new file mode 100644 index 00000000000000..e1fbc696b62f05 --- /dev/null +++ b/deps/uvwasi/src/clocks.c @@ -0,0 +1,194 @@ +#ifndef _WIN32 +# include +# include +# include +# include +#endif /* _WIN32 */ + +#include "uv.h" +#include "wasi_types.h" +#include "uv_mapping.h" + + +#define UVWASI__WIN_TIME_AND_RETURN(handle, time) \ + do { \ + FILETIME create; \ + FILETIME exit; \ + FILETIME system; \ + FILETIME user; \ + SYSTEMTIME sys_system; \ + SYSTEMTIME sys_user; \ + if (0 == GetProcessTimes((handle), &create, &exit, &system, &user)) { \ + return uvwasi__translate_uv_error( \ + uv_translate_sys_error(GetLastError()) \ + ); \ + } \ + \ + if (0 == FileTimeToSystemTime(&system, &sys_system)) { \ + return uvwasi__translate_uv_error( \ + uv_translate_sys_error(GetLastError()) \ + ); \ + } \ + \ + if (0 == FileTimeToSystemTime(&user, &sys_user)) { \ + return uvwasi__translate_uv_error( \ + uv_translate_sys_error(GetLastError()) \ + ); \ + } \ + \ + (time) = (((sys_system.wHour * 3600) + (sys_system.wMinute * 60) + \ + sys_system.wSecond) * NANOS_PER_SEC) + \ + (sys_system.wMilliseconds * 1000000) + \ + (((sys_user.wHour * 3600) + (sys_user.wMinute * 60) + \ + sys_user.wSecond) * NANOS_PER_SEC) + \ + (sys_user.wMilliseconds * 1000000); \ + return UVWASI_ESUCCESS; \ + } while (0) + + +#define UVWASI__CLOCK_GETTIME_AND_RETURN(clk, time) \ + do { \ + struct timespec ts; \ + if (0 != clock_gettime((clk), &ts)) \ + return uvwasi__translate_uv_error(uv_translate_sys_error(errno)); \ + (time) = (ts.tv_sec * NANOS_PER_SEC) + ts.tv_nsec; \ + return UVWASI_ESUCCESS; \ + } while (0) + + +#define UVWASI__GETRUSAGE_AND_RETURN(who, time) \ + do { \ + struct rusage ru; \ + if (0 != getrusage((who), &ru)) \ + return uvwasi__translate_uv_error(uv_translate_sys_error(errno)); \ + (time) = (ru.ru_utime.tv_sec * NANOS_PER_SEC) + \ + (ru.ru_utime.tv_usec * 1000) + \ + (ru.ru_stime.tv_sec * NANOS_PER_SEC) + \ + (ru.ru_stime.tv_usec * 1000); \ + return UVWASI_ESUCCESS; \ + } while (0) + + +#define UVWASI__OSX_THREADTIME_AND_RETURN(time) \ + do { \ + mach_port_t thread; \ + thread_basic_info_data_t info; \ + mach_msg_type_number_t count; \ + count = THREAD_BASIC_INFO_COUNT; \ + thread = pthread_mach_thread_np(pthread_self()); \ + if (KERN_SUCCESS != thread_info(thread, \ + THREAD_BASIC_INFO, \ + (thread_info_t) &info, \ + &count)) { \ + return UVWASI_ENOSYS; \ + } \ + (time) = (info.user_time.seconds * NANOS_PER_SEC) + \ + (info.user_time.microseconds * 1000) + \ + (info.system_time.seconds * NANOS_PER_SEC) + \ + (info.system_time.microseconds * 1000); \ + return UVWASI_ESUCCESS; \ + } while (0) + + +#define UVWASI__WIN_GETRES_AND_RETURN(time) \ + do { \ + /* The GetProcessTimes() docs claim a resolution of 100 ns. */ \ + (time) = 100; \ + return UVWASI_ESUCCESS; \ + } while (0) + + +#define UVWASI__CLOCK_GETRES_AND_RETURN(clk, time) \ + do { \ + struct timespec ts; \ + /* Try calling clock_getres(). If it doesn't succeed, then default to \ + 1000000. We implement all of the clocks, and some platforms (such as \ + SmartOS) don't support all of the clocks, even though they define \ + the constants for them. */ \ + if (0 != clock_getres((clk), &ts)) \ + (time) = 1000000; \ + else \ + (time) = (ts.tv_sec * NANOS_PER_SEC) + ts.tv_nsec; \ + return UVWASI_ESUCCESS; \ + } while (0) + + +#define UVWASI__SLOW_GETRES_AND_RETURN(time) \ + do { \ + /* Assume a "worst case" of 1000000 ns resolution. */ \ + (time) = 1000000; \ + return UVWASI_ESUCCESS; \ + } while (0) + + +uvwasi_errno_t uvwasi__clock_gettime_realtime(uvwasi_timestamp_t* time) { + uv_timeval64_t tv; + int r; + + r = uv_gettimeofday(&tv); + if (r != 0) + return uvwasi__translate_uv_error(r); + + *time = (tv.tv_sec * NANOS_PER_SEC) + (tv.tv_usec * 1000); + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi__clock_gettime_process_cputime(uvwasi_timestamp_t* time) { +#if defined(_WIN32) + UVWASI__WIN_TIME_AND_RETURN(GetCurrentProcess(), *time); +#elif defined(CLOCK_PROCESS_CPUTIME_ID) && \ + !defined(__APPLE__) && \ + !defined(__sun) + UVWASI__CLOCK_GETTIME_AND_RETURN(CLOCK_PROCESS_CPUTIME_ID, *time); +#else + UVWASI__GETRUSAGE_AND_RETURN(RUSAGE_SELF, *time); +#endif +} + + +uvwasi_errno_t uvwasi__clock_gettime_thread_cputime(uvwasi_timestamp_t* time) { +#if defined(_WIN32) + UVWASI__WIN_TIME_AND_RETURN(GetCurrentThread(), *time); +#elif defined(__APPLE__) + UVWASI__OSX_THREADTIME_AND_RETURN(*time); +#elif defined(CLOCK_THREAD_CPUTIME_ID) && !defined(__sun) + UVWASI__CLOCK_GETTIME_AND_RETURN(CLOCK_THREAD_CPUTIME_ID, *time); +#else +# if defined(RUSAGE_LWP) + UVWASI__GETRUSAGE_AND_RETURN(RUSAGE_LWP, *time); +# elif defined(RUSAGE_THREAD) + UVWASI__GETRUSAGE_AND_RETURN(RUSAGE_THREAD, *time); +# else + return UVWASI_ENOSYS; +# endif /* RUSAGE_LWP */ +#endif +} + + +uvwasi_errno_t uvwasi__clock_getres_process_cputime(uvwasi_timestamp_t* time) { +#if defined(_WIN32) + UVWASI__WIN_GETRES_AND_RETURN(*time); +#elif defined(CLOCK_PROCESS_CPUTIME_ID) && \ + !defined(__APPLE__) && \ + !defined(__sun) + UVWASI__CLOCK_GETRES_AND_RETURN(CLOCK_PROCESS_CPUTIME_ID, *time); +#else + UVWASI__SLOW_GETRES_AND_RETURN(*time); +#endif +} + + +uvwasi_errno_t uvwasi__clock_getres_thread_cputime(uvwasi_timestamp_t* time) { +#if defined(_WIN32) + UVWASI__WIN_GETRES_AND_RETURN(*time); +#elif defined(__APPLE__) + UVWASI__SLOW_GETRES_AND_RETURN(*time); +#elif defined(CLOCK_THREAD_CPUTIME_ID) && !defined(__sun) + UVWASI__CLOCK_GETTIME_AND_RETURN(CLOCK_THREAD_CPUTIME_ID, *time); +#elif defined(RUSAGE_THREAD) || defined(RUSAGE_LWP) + UVWASI__SLOW_GETRES_AND_RETURN(*time); +#else + return UVWASI_ENOSYS; +#endif +} diff --git a/deps/uvwasi/src/fd_table.c b/deps/uvwasi/src/fd_table.c new file mode 100644 index 00000000000000..9343868074d10c --- /dev/null +++ b/deps/uvwasi/src/fd_table.c @@ -0,0 +1,422 @@ +#include +#include +#include + +#ifndef _WIN32 +# include +#endif /* _WIN32 */ + +#include "uv.h" +#include "fd_table.h" +#include "wasi_types.h" +#include "uv_mapping.h" + + +#define UVWASI__RIGHTS_ALL (UVWASI_RIGHT_FD_DATASYNC | \ + UVWASI_RIGHT_FD_READ | \ + UVWASI_RIGHT_FD_SEEK | \ + UVWASI_RIGHT_FD_FDSTAT_SET_FLAGS | \ + UVWASI_RIGHT_FD_SYNC | \ + UVWASI_RIGHT_FD_TELL | \ + UVWASI_RIGHT_FD_WRITE | \ + UVWASI_RIGHT_FD_ADVISE | \ + UVWASI_RIGHT_FD_ALLOCATE | \ + UVWASI_RIGHT_PATH_CREATE_DIRECTORY | \ + UVWASI_RIGHT_PATH_CREATE_FILE | \ + UVWASI_RIGHT_PATH_LINK_SOURCE | \ + UVWASI_RIGHT_PATH_LINK_TARGET | \ + UVWASI_RIGHT_PATH_OPEN | \ + UVWASI_RIGHT_FD_READDIR | \ + UVWASI_RIGHT_PATH_READLINK | \ + UVWASI_RIGHT_PATH_RENAME_SOURCE | \ + UVWASI_RIGHT_PATH_RENAME_TARGET | \ + UVWASI_RIGHT_PATH_FILESTAT_GET | \ + UVWASI_RIGHT_PATH_FILESTAT_SET_SIZE | \ + UVWASI_RIGHT_PATH_FILESTAT_SET_TIMES | \ + UVWASI_RIGHT_FD_FILESTAT_GET | \ + UVWASI_RIGHT_FD_FILESTAT_SET_TIMES | \ + UVWASI_RIGHT_FD_FILESTAT_SET_SIZE | \ + UVWASI_RIGHT_PATH_SYMLINK | \ + UVWASI_RIGHT_PATH_UNLINK_FILE | \ + UVWASI_RIGHT_PATH_REMOVE_DIRECTORY | \ + UVWASI_RIGHT_POLL_FD_READWRITE | \ + UVWASI_RIGHT_SOCK_SHUTDOWN) + +#define UVWASI__RIGHTS_BLOCK_DEVICE_BASE UVWASI__RIGHTS_ALL +#define UVWASI__RIGHTS_BLOCK_DEVICE_INHERITING UVWASI__RIGHTS_ALL + +#define UVWASI__RIGHTS_CHARACTER_DEVICE_BASE UVWASI__RIGHTS_ALL +#define UVWASI__RIGHTS_CHARACTER_DEVICE_INHERITING UVWASI__RIGHTS_ALL + +#define UVWASI__RIGHTS_REGULAR_FILE_BASE (UVWASI_RIGHT_FD_DATASYNC | \ + UVWASI_RIGHT_FD_READ | \ + UVWASI_RIGHT_FD_SEEK | \ + UVWASI_RIGHT_FD_FDSTAT_SET_FLAGS | \ + UVWASI_RIGHT_FD_SYNC | \ + UVWASI_RIGHT_FD_TELL | \ + UVWASI_RIGHT_FD_WRITE | \ + UVWASI_RIGHT_FD_ADVISE | \ + UVWASI_RIGHT_FD_ALLOCATE | \ + UVWASI_RIGHT_FD_FILESTAT_GET | \ + UVWASI_RIGHT_FD_FILESTAT_SET_SIZE | \ + UVWASI_RIGHT_FD_FILESTAT_SET_TIMES |\ + UVWASI_RIGHT_POLL_FD_READWRITE) +#define UVWASI__RIGHTS_REGULAR_FILE_INHERITING 0 + +#define UVWASI__RIGHTS_DIRECTORY_BASE (UVWASI_RIGHT_FD_FDSTAT_SET_FLAGS | \ + UVWASI_RIGHT_FD_SYNC | \ + UVWASI_RIGHT_FD_ADVISE | \ + UVWASI_RIGHT_PATH_CREATE_DIRECTORY | \ + UVWASI_RIGHT_PATH_CREATE_FILE | \ + UVWASI_RIGHT_PATH_LINK_SOURCE | \ + UVWASI_RIGHT_PATH_LINK_TARGET | \ + UVWASI_RIGHT_PATH_OPEN | \ + UVWASI_RIGHT_FD_READDIR | \ + UVWASI_RIGHT_PATH_READLINK | \ + UVWASI_RIGHT_PATH_RENAME_SOURCE | \ + UVWASI_RIGHT_PATH_RENAME_TARGET | \ + UVWASI_RIGHT_PATH_FILESTAT_GET | \ + UVWASI_RIGHT_PATH_FILESTAT_SET_SIZE | \ + UVWASI_RIGHT_PATH_FILESTAT_SET_TIMES | \ + UVWASI_RIGHT_FD_FILESTAT_GET | \ + UVWASI_RIGHT_FD_FILESTAT_SET_TIMES | \ + UVWASI_RIGHT_PATH_SYMLINK | \ + UVWASI_RIGHT_PATH_UNLINK_FILE | \ + UVWASI_RIGHT_PATH_REMOVE_DIRECTORY | \ + UVWASI_RIGHT_POLL_FD_READWRITE) +#define UVWASI__RIGHTS_DIRECTORY_INHERITING (UVWASI__RIGHTS_DIRECTORY_BASE | \ + UVWASI__RIGHTS_REGULAR_FILE_BASE) + +#define UVWASI__RIGHTS_SOCKET_BASE (UVWASI_RIGHT_FD_READ | \ + UVWASI_RIGHT_FD_FDSTAT_SET_FLAGS | \ + UVWASI_RIGHT_FD_WRITE | \ + UVWASI_RIGHT_FD_FILESTAT_GET | \ + UVWASI_RIGHT_POLL_FD_READWRITE | \ + UVWASI_RIGHT_SOCK_SHUTDOWN) +#define UVWASI__RIGHTS_SOCKET_INHERITING UVWASI__RIGHTS_ALL; + +#define UVWASI__RIGHTS_TTY_BASE (UVWASI_RIGHT_FD_READ | \ + UVWASI_RIGHT_FD_FDSTAT_SET_FLAGS | \ + UVWASI_RIGHT_FD_WRITE | \ + UVWASI_RIGHT_FD_FILESTAT_GET | \ + UVWASI_RIGHT_POLL_FD_READWRITE) +#define UVWASI__RIGHTS_TTY_INHERITING 0 + +static uvwasi_errno_t uvwasi__get_type_and_rights(uv_file fd, + int flags, + uvwasi_filetype_t* type, + uvwasi_rights_t* rights_base, + uvwasi_rights_t* rights_inheriting) { + uv_fs_t req; + uvwasi_filetype_t filetype; + int read_or_write_only; + int r; + + r = uv_fs_fstat(NULL, &req, fd, NULL); + filetype = uvwasi__stat_to_filetype(&req.statbuf); + uv_fs_req_cleanup(&req); + if (r != 0) + return uvwasi__translate_uv_error(r); + + *type = filetype; + switch (filetype) { + case UVWASI_FILETYPE_REGULAR_FILE: + *rights_base = UVWASI__RIGHTS_REGULAR_FILE_BASE; + *rights_inheriting = UVWASI__RIGHTS_REGULAR_FILE_INHERITING; + break; + + case UVWASI_FILETYPE_DIRECTORY: + *rights_base = UVWASI__RIGHTS_DIRECTORY_BASE; + *rights_inheriting = UVWASI__RIGHTS_DIRECTORY_INHERITING; + break; + + /* uvwasi__stat_to_filetype() cannot differentiate socket types. It only + returns UVWASI_FILETYPE_SOCKET_STREAM. */ + case UVWASI_FILETYPE_SOCKET_STREAM: + if (uv_guess_handle(fd) == UV_UDP) + *type = UVWASI_FILETYPE_SOCKET_DGRAM; + + *rights_base = UVWASI__RIGHTS_SOCKET_BASE; + *rights_inheriting = UVWASI__RIGHTS_SOCKET_INHERITING; + break; + + case UVWASI_FILETYPE_CHARACTER_DEVICE: + if (uv_guess_handle(fd) == UV_TTY) { + *rights_base = UVWASI__RIGHTS_TTY_BASE; + *rights_inheriting = UVWASI__RIGHTS_TTY_INHERITING; + } else { + *rights_base = UVWASI__RIGHTS_CHARACTER_DEVICE_BASE; + *rights_inheriting = UVWASI__RIGHTS_CHARACTER_DEVICE_INHERITING; + } + break; + + case UVWASI_FILETYPE_BLOCK_DEVICE: + *rights_base = UVWASI__RIGHTS_BLOCK_DEVICE_BASE; + *rights_inheriting = UVWASI__RIGHTS_BLOCK_DEVICE_INHERITING; + break; + + default: + *rights_base = 0; + *rights_inheriting = 0; + } + + if (*type == UVWASI_FILETYPE_UNKNOWN) + return UVWASI_EINVAL; + + /* Disable read/write bits depending on access mode. */ + read_or_write_only = flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR); + + if (read_or_write_only == UV_FS_O_RDONLY) + *rights_base &= ~UVWASI_RIGHT_FD_WRITE; + else if (read_or_write_only == UV_FS_O_WRONLY) + *rights_base &= ~UVWASI_RIGHT_FD_READ; + + return UVWASI_ESUCCESS; +} + + +static uvwasi_errno_t uvwasi__fd_table_insert(struct uvwasi_fd_table_t* table, + uv_file fd, + const char* mapped_path, + const char* real_path, + uvwasi_filetype_t type, + uvwasi_rights_t rights_base, + uvwasi_rights_t rights_inheriting, + int preopen, + struct uvwasi_fd_wrap_t** wrap) { + struct uvwasi_fd_wrap_t* entry; + struct uvwasi_fd_wrap_t* new_fds; + uint32_t new_size; + int index; + uint32_t i; + + /* Check that there is room for a new item. If there isn't, grow the table. */ + if (table->used >= table->size) { + new_size = table->size * 2; + new_fds = realloc(table->fds, new_size * sizeof(*new_fds)); + if (new_fds == NULL) + return UVWASI_ENOMEM; + + for (i = table->size; i < new_size; ++i) + new_fds[i].valid = 0; + + index = table->size; + table->fds = new_fds; + table->size = new_size; + } else { + /* The table is big enough, so find an empty slot for the new data. */ + index = -1; + for (i = 0; i < table->size; ++i) { + if (table->fds[i].valid != 1) { + index = i; + break; + } + } + + /* index should never be -1. */ + if (index == -1) + return UVWASI_ENOSPC; + } + + entry = &table->fds[index]; + entry->id = index; + entry->fd = fd; + strcpy(entry->path, mapped_path); + strcpy(entry->real_path, real_path); + entry->type = type; + entry->rights_base = rights_base; + entry->rights_inheriting = rights_inheriting; + entry->preopen = preopen; + entry->valid = 1; + table->used++; + + if (wrap != NULL) + *wrap = entry; + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_table_init(struct uvwasi_fd_table_t* table, + uint32_t init_size) { + struct uvwasi_fd_wrap_t* wrap; + uvwasi_filetype_t type; + uvwasi_rights_t base; + uvwasi_rights_t inheriting; + uvwasi_errno_t err; + uvwasi_fd_t i; + + /* Require an initial size of at least three to store the stdio FDs. */ + if (table == NULL || init_size < 3) + return UVWASI_EINVAL; + + table->used = 0; + table->size = init_size; + table->fds = calloc(init_size, sizeof(struct uvwasi_fd_wrap_t)); + + if (table->fds == NULL) + return UVWASI_ENOMEM; + + /* Create the stdio FDs. */ + for (i = 0; i < 3; ++i) { + err = uvwasi__get_type_and_rights(i, + UV_FS_O_RDWR, + &type, + &base, + &inheriting); + if (err != UVWASI_ESUCCESS) + goto error_exit; + + err = uvwasi__fd_table_insert(table, + i, + "", + "", + type, + base, + inheriting, + 0, + &wrap); + if (err != UVWASI_ESUCCESS) + goto error_exit; + + if (wrap->id != i || wrap->id != (uvwasi_fd_t) wrap->fd) { + err = UVWASI_EBADF; + goto error_exit; + } + } + + return UVWASI_ESUCCESS; +error_exit: + uvwasi_fd_table_free(table); + return err; +} + + +void uvwasi_fd_table_free(struct uvwasi_fd_table_t* table) { + if (table == NULL) + return; + + free(table->fds); + table->fds = NULL; + table->size = 0; + table->used = 0; +} + + +uvwasi_errno_t uvwasi_fd_table_insert_preopen(struct uvwasi_fd_table_t* table, + const uv_file fd, + const char* path, + const char* real_path) { + uvwasi_filetype_t type; + uvwasi_rights_t base; + uvwasi_rights_t inheriting; + uvwasi_errno_t err; + + if (table == NULL || path == NULL || real_path == NULL) + return UVWASI_EINVAL; + + err = uvwasi__get_type_and_rights(fd, 0, &type, &base, &inheriting); + if (err != UVWASI_ESUCCESS) + return err; + + if (type != UVWASI_FILETYPE_DIRECTORY) + return UVWASI_ENOTDIR; + + err = uvwasi__fd_table_insert(table, + fd, + path, + real_path, + UVWASI_FILETYPE_DIRECTORY, + UVWASI__RIGHTS_DIRECTORY_BASE, + UVWASI__RIGHTS_DIRECTORY_INHERITING, + 1, + NULL); + if (err != UVWASI_ESUCCESS) + return err; + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_table_insert_fd(struct uvwasi_fd_table_t* table, + const uv_file fd, + const int flags, + const char* path, + uvwasi_rights_t rights_base, + uvwasi_rights_t rights_inheriting, + struct uvwasi_fd_wrap_t* wrap) { + struct uvwasi_fd_wrap_t* fd_wrap; + uvwasi_filetype_t type; + uvwasi_rights_t max_base; + uvwasi_rights_t max_inheriting; + uvwasi_errno_t r; + + if (table == NULL || path == NULL || wrap == NULL) + return UVWASI_EINVAL; + + r = uvwasi__get_type_and_rights(fd, flags, &type, &max_base, &max_inheriting); + if (r != UVWASI_ESUCCESS) + return r; + + r = uvwasi__fd_table_insert(table, + fd, + path, + path, + type, + rights_base & max_base, + rights_inheriting & max_inheriting, + 0, + &fd_wrap); + if (r != UVWASI_ESUCCESS) + return r; + + *wrap = *fd_wrap; + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_table_get(const struct uvwasi_fd_table_t* table, + const uvwasi_fd_t id, + struct uvwasi_fd_wrap_t** wrap, + uvwasi_rights_t rights_base, + uvwasi_rights_t rights_inheriting) { + struct uvwasi_fd_wrap_t* entry; + + if (table == NULL || wrap == NULL) + return UVWASI_EINVAL; + if (id >= table->size) + return UVWASI_EBADF; + + entry = &table->fds[id]; + + if (entry->valid != 1 || entry->id != id) + return UVWASI_EBADF; + + /* Validate that the fd has the necessary rights. */ + if ((~entry->rights_base & rights_base) != 0 || + (~entry->rights_inheriting & rights_inheriting) != 0) + return UVWASI_ENOTCAPABLE; + + *wrap = entry; + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_table_remove(struct uvwasi_fd_table_t* table, + const uvwasi_fd_t id) { + struct uvwasi_fd_wrap_t* entry; + + if (table == NULL) + return UVWASI_EINVAL; + if (id >= table->size) + return UVWASI_EBADF; + + entry = &table->fds[id]; + + if (entry->valid != 1 || entry->id != id) + return UVWASI_EBADF; + + entry->valid = 0; + table->used--; + return UVWASI_ESUCCESS; +} diff --git a/deps/uvwasi/src/uv_mapping.c b/deps/uvwasi/src/uv_mapping.c new file mode 100644 index 00000000000000..846dcedbeb6b4f --- /dev/null +++ b/deps/uvwasi/src/uv_mapping.c @@ -0,0 +1,243 @@ +#include + +#ifndef _WIN32 +# include +#endif /* _WIN32 */ + +#include "uv.h" +#include "wasi_types.h" +#include "uv_mapping.h" + +#if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG) +# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#endif + +#if !defined(S_ISDIR) && defined(S_IFMT) && defined(S_IFDIR) +# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif + +#if !defined(S_ISCHR) && defined(S_IFMT) && defined(S_IFCHR) +# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +#endif + +#if !defined(S_ISLNK) && defined(S_IFMT) && defined(S_IFLNK) +# define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#endif + + +uvwasi_errno_t uvwasi__translate_uv_error(int err) { + switch (err) { + case UV_E2BIG: return UVWASI_E2BIG; + case UV_EACCES: return UVWASI_EACCES; + case UV_EADDRINUSE: return UVWASI_EADDRINUSE; + case UV_EADDRNOTAVAIL: return UVWASI_EADDRNOTAVAIL; + case UV_EAFNOSUPPORT: return UVWASI_EAFNOSUPPORT; + case UV_EAGAIN: return UVWASI_EAGAIN; + case UV_EALREADY: return UVWASI_EALREADY; + case UV_EBADF: return UVWASI_EBADF; + case UV_EBUSY: return UVWASI_EBUSY; + case UV_ECANCELED: return UVWASI_ECANCELED; + case UV_ECONNABORTED: return UVWASI_ECONNABORTED; + case UV_ECONNREFUSED: return UVWASI_ECONNREFUSED; + case UV_ECONNRESET: return UVWASI_ECONNRESET; + case UV_EDESTADDRREQ: return UVWASI_EDESTADDRREQ; + case UV_EEXIST: return UVWASI_EEXIST; + case UV_EFAULT: return UVWASI_EFAULT; + case UV_EFBIG: return UVWASI_EFBIG; + case UV_EHOSTUNREACH: return UVWASI_EHOSTUNREACH; + case UV_EINTR: return UVWASI_EINTR; + case UV_EINVAL: return UVWASI_EINVAL; + case UV_EIO: return UVWASI_EIO; + case UV_EISCONN: return UVWASI_EISCONN; + case UV_EISDIR: return UVWASI_EISDIR; + case UV_ELOOP: return UVWASI_ELOOP; + case UV_EMFILE: return UVWASI_EMFILE; + case UV_EMLINK: return UVWASI_EMLINK; + case UV_EMSGSIZE: return UVWASI_EMSGSIZE; + case UV_ENAMETOOLONG: return UVWASI_ENAMETOOLONG; + case UV_ENETDOWN: return UVWASI_ENETDOWN; + case UV_ENETUNREACH: return UVWASI_ENETUNREACH; + case UV_ENFILE: return UVWASI_ENFILE; + case UV_ENOBUFS: return UVWASI_ENOBUFS; + case UV_ENODEV: return UVWASI_ENODEV; + case UV_ENOENT: return UVWASI_ENOENT; + case UV_ENOMEM: return UVWASI_ENOMEM; + case UV_ENOPROTOOPT: return UVWASI_ENOPROTOOPT; + case UV_ENOSPC: return UVWASI_ENOSPC; + case UV_ENOSYS: return UVWASI_ENOSYS; + case UV_ENOTCONN: return UVWASI_ENOTCONN; + case UV_ENOTDIR: return UVWASI_ENOTDIR; + /* On at least some AIX machines, ENOTEMPTY and EEXIST are equivalent. */ +#if ENOTEMPTY != EEXIST + case UV_ENOTEMPTY: return UVWASI_ENOTEMPTY; +#endif /* ENOTEMPTY != EEXIST */ + case UV_ENOTSOCK: return UVWASI_ENOTSOCK; + case UV_ENOTSUP: return UVWASI_ENOTSUP; + case UV_ENXIO: return UVWASI_ENXIO; + case UV_EPERM: return UVWASI_EPERM; + case UV_EPIPE: return UVWASI_EPIPE; + case UV_EPROTO: return UVWASI_EPROTO; + case UV_EPROTONOSUPPORT: return UVWASI_EPROTONOSUPPORT; + case UV_EPROTOTYPE: return UVWASI_EPROTOTYPE; + case UV_ERANGE: return UVWASI_ERANGE; + case UV_EROFS: return UVWASI_EROFS; + case UV_ESPIPE: return UVWASI_ESPIPE; + case UV_ESRCH: return UVWASI_ESRCH; + case UV_ETIMEDOUT: return UVWASI_ETIMEDOUT; + case UV_ETXTBSY: return UVWASI_ETXTBSY; + case UV_EXDEV: return UVWASI_EXDEV; + case 0: return UVWASI_ESUCCESS; + /* The following libuv error codes have no corresponding WASI error code: + UV_EAI_ADDRFAMILY, UV_EAI_AGAIN, UV_EAI_BADFLAGS, UV_EAI_BADHINTS, + UV_EAI_CANCELED, UV_EAI_FAIL, UV_EAI_FAMILY, UV_EAI_MEMORY, + UV_EAI_NODATA, UV_EAI_NONAME, UV_EAI_OVERFLOW, UV_EAI_PROTOCOL, + UV_EAI_SERVICE, UV_EAI_SOCKTYPE, UV_ECHARSET, UV_ENONET, UV_EOF, + UV_ESHUTDOWN, UV_UNKNOWN + */ + default: + /* libuv errors are < 0 */ + if (err > 0) + return err; + + return UVWASI_ENOSYS; + } +} + + +int uvwasi__translate_to_uv_signal(uvwasi_signal_t sig) { + switch (sig) { +#ifdef SIGABRT + case UVWASI_SIGABRT: return SIGABRT; +#endif +#ifdef SIGALRM + case UVWASI_SIGALRM: return SIGALRM; +#endif +#ifdef SIGBUS + case UVWASI_SIGBUS: return SIGBUS; +#endif +#ifdef SIGCHLD + case UVWASI_SIGCHLD: return SIGCHLD; +#endif +#ifdef SIGCONT + case UVWASI_SIGCONT: return SIGCONT; +#endif +#ifdef SIGFPE + case UVWASI_SIGFPE: return SIGFPE; +#endif +#ifdef SIGHUP + case UVWASI_SIGHUP: return SIGHUP; +#endif +#ifdef SIGILL + case UVWASI_SIGILL: return SIGILL; +#endif +#ifdef SIGINT + case UVWASI_SIGINT: return SIGINT; +#endif +#ifdef SIGKILL + case UVWASI_SIGKILL: return SIGKILL; +#endif +#ifdef SIGPIPE + case UVWASI_SIGPIPE: return SIGPIPE; +#endif +#ifdef SIGQUIT + case UVWASI_SIGQUIT: return SIGQUIT; +#endif +#ifdef SIGSEGV + case UVWASI_SIGSEGV: return SIGSEGV; +#endif +#ifdef SIGSTOP + case UVWASI_SIGSTOP: return SIGSTOP; +#endif +#ifdef SIGSYS + case UVWASI_SIGSYS: return SIGSYS; +#endif +#ifdef SIGTERM + case UVWASI_SIGTERM: return SIGTERM; +#endif +#ifdef SIGTRAP + case UVWASI_SIGTRAP: return SIGTRAP; +#endif +#ifdef SIGTSTP + case UVWASI_SIGTSTP: return SIGTSTP; +#endif +#ifdef SIGTTIN + case UVWASI_SIGTTIN: return SIGTTIN; +#endif +#ifdef SIGTTOU + case UVWASI_SIGTTOU: return SIGTTOU; +#endif +#ifdef SIGURG + case UVWASI_SIGURG: return SIGURG; +#endif +#ifdef SIGUSR1 + case UVWASI_SIGUSR1: return SIGUSR1; +#endif +#ifdef SIGUSR2 + case UVWASI_SIGUSR2: return SIGUSR2; +#endif +#ifdef SIGVTALRM + case UVWASI_SIGVTALRM: return SIGVTALRM; +#endif +#ifdef SIGXCPU + case UVWASI_SIGXCPU: return SIGXCPU; +#endif +#ifdef SIGXFSZ + case UVWASI_SIGXFSZ: return SIGXFSZ; +#endif + default: return -1; + } +} + + +uvwasi_timestamp_t uvwasi__timespec_to_timestamp(const uv_timespec_t* ts) { + /* TODO(cjihrig): Handle overflow. */ + return (uvwasi_timestamp_t) ts->tv_sec * NANOS_PER_SEC + ts->tv_nsec; +} + + +uvwasi_filetype_t uvwasi__stat_to_filetype(const uv_stat_t* stat) { + uint64_t mode; + + mode = stat->st_mode; + + if (S_ISREG(mode)) + return UVWASI_FILETYPE_REGULAR_FILE; + + if (S_ISDIR(mode)) + return UVWASI_FILETYPE_DIRECTORY; + + if (S_ISCHR(mode)) + return UVWASI_FILETYPE_CHARACTER_DEVICE; + + if (S_ISLNK(mode)) + return UVWASI_FILETYPE_SYMBOLIC_LINK; + +#ifdef S_ISSOCK + if (S_ISSOCK(mode)) + return UVWASI_FILETYPE_SOCKET_STREAM; +#endif /* S_ISSOCK */ + +#ifdef S_ISFIFO + if (S_ISFIFO(mode)) + return UVWASI_FILETYPE_SOCKET_STREAM; +#endif /* S_ISFIFO */ + +#ifdef S_ISBLK + if (S_ISBLK(mode)) + return UVWASI_FILETYPE_BLOCK_DEVICE; +#endif /* S_ISBLK */ + + return UVWASI_FILETYPE_UNKNOWN; +} + + +void uvwasi__stat_to_filestat(const uv_stat_t* stat, uvwasi_filestat_t* fs) { + fs->st_dev = stat->st_dev; + fs->st_ino = stat->st_ino; + fs->st_nlink = stat->st_nlink; + fs->st_size = stat->st_size; + fs->st_filetype = uvwasi__stat_to_filetype(stat); + fs->st_atim = uvwasi__timespec_to_timestamp(&stat->st_atim); + fs->st_mtim = uvwasi__timespec_to_timestamp(&stat->st_mtim); + fs->st_ctim = uvwasi__timespec_to_timestamp(&stat->st_ctim); +} diff --git a/deps/uvwasi/src/uvwasi.c b/deps/uvwasi/src/uvwasi.c new file mode 100644 index 00000000000000..39a94b6d217c52 --- /dev/null +++ b/deps/uvwasi/src/uvwasi.c @@ -0,0 +1,1918 @@ +#include +#include + +#ifndef _WIN32 +# include +# include +# include +# include +# include +# define SLASH '/' +# define SLASH_STR "/" +# define IS_SLASH(c) ((c) == '/') +#else +# define SLASH '\\' +# define SLASH_STR "\\" +# define IS_SLASH(c) ((c) == '/' || (c) == '\\') +#endif /* _WIN32 */ + +#define UVWASI__READDIR_NUM_ENTRIES 1 + +#include "uvwasi.h" +#include "uv.h" +#include "uv_mapping.h" +#include "fd_table.h" +#include "clocks.h" + + +static int uvwasi__is_absolute_path(const char* path, size_t path_len) { + /* TODO(cjihrig): Add a Windows implementation. */ + return path != NULL && path_len > 0 && path[0] == SLASH; +} + + +static uvwasi_errno_t uvwasi__resolve_path(const struct uvwasi_fd_wrap_t* fd, + const char* path, + size_t path_len, + char* resolved_path, + uvwasi_lookupflags_t flags) { + /* TODO(cjihrig): path_len is treated as a size. Need to verify if path_len is + really a string length or a size. Also need to verify if it is null + terminated. */ + uv_fs_t realpath_req; + uvwasi_errno_t err; + char* abs_path; + char* tok; + char* ptr; + int realpath_size; + int abs_size; + int input_is_absolute; + int r; +#ifdef _WIN32 + int i; +#endif /* _WIN32 */ + + err = UVWASI_ESUCCESS; + input_is_absolute = uvwasi__is_absolute_path(path, path_len); + + if (1 == input_is_absolute) { + /* TODO(cjihrig): Revisit this. Copying is probably not necessary here. */ + abs_size = path_len; + abs_path = malloc(abs_size); + if (abs_path == NULL) { + err = UVWASI_ENOMEM; + goto exit; + } + + memcpy(abs_path, path, abs_size); + } else { + /* Resolve the relative path to fd's real path. */ + abs_size = path_len + strlen(fd->real_path) + 2; + abs_path = malloc(abs_size); + if (abs_path == NULL) { + err = UVWASI_ENOMEM; + goto exit; + } + + r = snprintf(abs_path, abs_size, "%s/%s", fd->real_path, path); + if (r <= 0) { + err = uvwasi__translate_uv_error(uv_translate_sys_error(errno)); + goto exit; + } + } + +#ifdef _WIN32 + /* On Windows, convert slashes to backslashes. */ + for (i = 0; i < abs_size; ++i) { + if (abs_path[i] == '/') + abs_path[i] = SLASH; + } +#endif /* _WIN32 */ + + ptr = resolved_path; + tok = strtok(abs_path, SLASH_STR); + for (; tok != NULL; tok = strtok(NULL, SLASH_STR)) { + if (0 == strcmp(tok, ".")) + continue; + + if (0 == strcmp(tok, "..")) { + while (*ptr != SLASH && ptr != resolved_path) + ptr--; + *ptr = '\0'; + continue; + } + +#ifdef _WIN32 + /* On Windows, prevent a leading slash in the path. */ + if (ptr == resolved_path) + r = sprintf(ptr, "%s", tok); + else +#endif /* _WIN32 */ + r = sprintf(ptr, "%c%s", SLASH, tok); + + if (r < 1) { /* At least one character should have been written. */ + err = uvwasi__translate_uv_error(uv_translate_sys_error(errno)); + goto exit; + } + + ptr += r; + } + + if ((flags & UVWASI_LOOKUP_SYMLINK_FOLLOW) == UVWASI_LOOKUP_SYMLINK_FOLLOW) { + r = uv_fs_realpath(NULL, &realpath_req, resolved_path, NULL); + if (r == 0) { + realpath_size = strlen(realpath_req.ptr) + 1; + if (realpath_size > PATH_MAX_BYTES) { + err = UVWASI_ENOBUFS; + uv_fs_req_cleanup(&realpath_req); + goto exit; + } + + memcpy(resolved_path, realpath_req.ptr, realpath_size); + } else if (r != UV_ENOENT) { + /* Report errors except ENOENT. */ + err = uvwasi__translate_uv_error(r); + uv_fs_req_cleanup(&realpath_req); + goto exit; + } + + uv_fs_req_cleanup(&realpath_req); + } + + /* Verify that the resolved path is still in the sandbox. */ + if (resolved_path != strstr(resolved_path, fd->real_path)) { + err = UVWASI_ENOTCAPABLE; + goto exit; + } + +exit: + free(abs_path); + return err; +} + + +static uvwasi_errno_t uvwasi__lseek(uv_file fd, + uvwasi_filedelta_t offset, + uvwasi_whence_t whence, + uvwasi_filesize_t* newoffset) { + int real_whence; + + if (whence == UVWASI_WHENCE_CUR) + real_whence = SEEK_CUR; + else if (whence == UVWASI_WHENCE_END) + real_whence = SEEK_END; + else if (whence == UVWASI_WHENCE_SET) + real_whence = SEEK_SET; + else + return UVWASI_EINVAL; + +#ifdef _WIN32 + int64_t r; + + r = _lseeki64(fd, offset, real_whence); + if (-1L == r) + return uvwasi__translate_uv_error(uv_translate_sys_error(errno)); +#else + off_t r; + + r = lseek(fd, offset, real_whence); + if ((off_t) -1 == r) + return uvwasi__translate_uv_error(uv_translate_sys_error(errno)); +#endif /* _WIN32 */ + + *newoffset = r; + return UVWASI_ESUCCESS; +} + + +static uvwasi_errno_t uvwasi__setup_iovs(uv_buf_t** buffers, + const uvwasi_iovec_t* iovs, + size_t iovs_len) { + uv_buf_t* bufs; + size_t i; + + bufs = malloc(iovs_len * sizeof(*bufs)); + if (bufs == NULL) + return UVWASI_ENOMEM; + + for (i = 0; i < iovs_len; ++i) + bufs[i] = uv_buf_init(iovs[i].buf, iovs[i].buf_len); + + *buffers = bufs; + return UVWASI_ESUCCESS; +} + + +static uvwasi_errno_t uvwasi__setup_ciovs(uv_buf_t** buffers, + const uvwasi_ciovec_t* iovs, + size_t iovs_len) { + uv_buf_t* bufs; + size_t i; + + bufs = malloc(iovs_len * sizeof(*bufs)); + if (bufs == NULL) + return UVWASI_ENOMEM; + + for (i = 0; i < iovs_len; ++i) + bufs[i] = uv_buf_init((char*)iovs[i].buf, iovs[i].buf_len); + + *buffers = bufs; + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_init(uvwasi_t* uvwasi, uvwasi_options_t* options) { + uv_fs_t realpath_req; + uv_fs_t open_req; + uvwasi_errno_t err; + size_t args_size; + size_t size; + size_t offset; + size_t env_count; + size_t env_buf_size; + size_t i; + int r; + + if (uvwasi == NULL || options == NULL || options->fd_table_size == 0) + return UVWASI_EINVAL; + + uvwasi->argv_buf = NULL; + uvwasi->argv = NULL; + uvwasi->env_buf = NULL; + uvwasi->env = NULL; + uvwasi->fds.fds = NULL; + + args_size = 0; + for (i = 0; i < options->argc; ++i) + args_size += strlen(options->argv[i]) + 1; + + uvwasi->argc = options->argc; + uvwasi->argv_buf_size = args_size; + + if (args_size > 0) { + uvwasi->argv_buf = malloc(args_size); + if (uvwasi->argv_buf == NULL) { + err = UVWASI_ENOMEM; + goto exit; + } + + uvwasi->argv = calloc(options->argc, sizeof(char*)); + if (uvwasi->argv == NULL) { + err = UVWASI_ENOMEM; + goto exit; + } + + offset = 0; + for (i = 0; i < options->argc; ++i) { + size = strlen(options->argv[i]) + 1; + memcpy(uvwasi->argv_buf + offset, options->argv[i], size); + uvwasi->argv[i] = uvwasi->argv_buf + offset; + offset += size; + } + } + + env_count = 0; + env_buf_size = 0; + if (options->envp != NULL) { + while (options->envp[env_count] != NULL) { + env_buf_size += strlen(options->envp[env_count]) + 1; + env_count++; + } + } + + uvwasi->envc = env_count; + uvwasi->env_buf_size = env_buf_size; + + if (env_buf_size > 0) { + uvwasi->env_buf = malloc(env_buf_size); + if (uvwasi->env_buf == NULL) { + err = UVWASI_ENOMEM; + goto exit; + } + + uvwasi->env = calloc(env_count, sizeof(char*)); + if (uvwasi->env == NULL) { + err = UVWASI_ENOMEM; + goto exit; + } + + offset = 0; + for (i = 0; i < env_count; ++i) { + size = strlen(options->envp[i]) + 1; + memcpy(uvwasi->env_buf + offset, options->envp[i], size); + uvwasi->env[i] = uvwasi->env_buf + offset; + offset += size; + } + } + + for (i = 0; i < options->preopenc; ++i) { + if (options->preopens[i].real_path == NULL || + options->preopens[i].mapped_path == NULL) { + err = UVWASI_EINVAL; + goto exit; + } + } + + err = uvwasi_fd_table_init(&uvwasi->fds, options->fd_table_size); + if (err != UVWASI_ESUCCESS) + goto exit; + + for (i = 0; i < options->preopenc; ++i) { + r = uv_fs_realpath(NULL, + &realpath_req, + options->preopens[i].real_path, + NULL); + if (r != 0) { + err = uvwasi__translate_uv_error(r); + uv_fs_req_cleanup(&realpath_req); + goto exit; + } + + r = uv_fs_open(NULL, &open_req, realpath_req.ptr, 0, 0666, NULL); + if (r < 0) { + err = uvwasi__translate_uv_error(r); + uv_fs_req_cleanup(&realpath_req); + uv_fs_req_cleanup(&open_req); + goto exit; + } + + err = uvwasi_fd_table_insert_preopen(&uvwasi->fds, + open_req.result, + options->preopens[i].mapped_path, + realpath_req.ptr); + uv_fs_req_cleanup(&realpath_req); + uv_fs_req_cleanup(&open_req); + + if (err != UVWASI_ESUCCESS) + goto exit; + } + + return UVWASI_ESUCCESS; + +exit: + uvwasi_destroy(uvwasi); + return err; +} + + +void uvwasi_destroy(uvwasi_t* uvwasi) { + if (uvwasi == NULL) + return; + + uvwasi_fd_table_free(&uvwasi->fds); + free(uvwasi->argv_buf); + free(uvwasi->argv); + free(uvwasi->env_buf); + free(uvwasi->env); + uvwasi->argv_buf = NULL; + uvwasi->argv = NULL; + uvwasi->env_buf = NULL; + uvwasi->env = NULL; +} + + +uvwasi_errno_t uvwasi_embedder_remap_fd(uvwasi_t* uvwasi, + const uvwasi_fd_t fd, + uv_file new_host_fd) { + struct uvwasi_fd_wrap_t* wrap; + uvwasi_errno_t err; + + if (uvwasi == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, fd, &wrap, 0, 0); + if (err != UVWASI_ESUCCESS) + return err; + + wrap->fd = new_host_fd; + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_args_get(uvwasi_t* uvwasi, char** argv, char* argv_buf) { + size_t i; + + if (uvwasi == NULL || argv == NULL || argv_buf == NULL) + return UVWASI_EINVAL; + + for (i = 0; i < uvwasi->argc; ++i) { + argv[i] = argv_buf + (uvwasi->argv[i] - uvwasi->argv_buf); + } + + memcpy(argv_buf, uvwasi->argv_buf, uvwasi->argv_buf_size); + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_args_sizes_get(uvwasi_t* uvwasi, + size_t* argc, + size_t* argv_buf_size) { + if (uvwasi == NULL || argc == NULL || argv_buf_size == NULL) + return UVWASI_EINVAL; + + *argc = uvwasi->argc; + *argv_buf_size = uvwasi->argv_buf_size; + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_clock_res_get(uvwasi_t* uvwasi, + uvwasi_clockid_t clock_id, + uvwasi_timestamp_t* resolution) { + if (uvwasi == NULL || resolution == NULL) + return UVWASI_EINVAL; + + switch (clock_id) { + case UVWASI_CLOCK_MONOTONIC: + case UVWASI_CLOCK_REALTIME: + *resolution = 1; /* Nanosecond precision. */ + return UVWASI_ESUCCESS; + case UVWASI_CLOCK_PROCESS_CPUTIME_ID: + return uvwasi__clock_getres_process_cputime(resolution); + case UVWASI_CLOCK_THREAD_CPUTIME_ID: + return uvwasi__clock_getres_thread_cputime(resolution); + default: + return UVWASI_EINVAL; + } +} + + +uvwasi_errno_t uvwasi_clock_time_get(uvwasi_t* uvwasi, + uvwasi_clockid_t clock_id, + uvwasi_timestamp_t precision, + uvwasi_timestamp_t* time) { + if (uvwasi == NULL || time == NULL) + return UVWASI_EINVAL; + + switch (clock_id) { + case UVWASI_CLOCK_MONOTONIC: + *time = uv_hrtime(); + return UVWASI_ESUCCESS; + case UVWASI_CLOCK_REALTIME: + return uvwasi__clock_gettime_realtime(time); + case UVWASI_CLOCK_PROCESS_CPUTIME_ID: + return uvwasi__clock_gettime_process_cputime(time); + case UVWASI_CLOCK_THREAD_CPUTIME_ID: + return uvwasi__clock_gettime_thread_cputime(time); + default: + return UVWASI_EINVAL; + } +} + + +uvwasi_errno_t uvwasi_environ_get(uvwasi_t* uvwasi, + char** environment, + char* environ_buf) { + size_t i; + + if (uvwasi == NULL || environment == NULL || environ_buf == NULL) + return UVWASI_EINVAL; + + for (i = 0; i < uvwasi->envc; ++i) { + environment[i] = environ_buf + (uvwasi->env[i] - uvwasi->env_buf); + } + + memcpy(environ_buf, uvwasi->env_buf, uvwasi->env_buf_size); + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_environ_sizes_get(uvwasi_t* uvwasi, + size_t* environ_count, + size_t* environ_buf_size) { + if (uvwasi == NULL || environ_count == NULL || environ_buf_size == NULL) + return UVWASI_EINVAL; + + *environ_count = uvwasi->envc; + *environ_buf_size = uvwasi->env_buf_size; + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_advise(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_filesize_t offset, + uvwasi_filesize_t len, + uvwasi_advice_t advice) { + struct uvwasi_fd_wrap_t* wrap; + uvwasi_errno_t err; +#ifdef POSIX_FADV_NORMAL + int mapped_advice; + int r; +#endif /* POSIX_FADV_NORMAL */ + + if (uvwasi == NULL) + return UVWASI_EINVAL; + + switch (advice) { + case UVWASI_ADVICE_DONTNEED: +#ifdef POSIX_FADV_NORMAL + mapped_advice = POSIX_FADV_DONTNEED; +#endif /* POSIX_FADV_NORMAL */ + break; + case UVWASI_ADVICE_NOREUSE: +#ifdef POSIX_FADV_NORMAL + mapped_advice = POSIX_FADV_NOREUSE; +#endif /* POSIX_FADV_NORMAL */ + break; + case UVWASI_ADVICE_NORMAL: +#ifdef POSIX_FADV_NORMAL + mapped_advice = POSIX_FADV_NORMAL; +#endif /* POSIX_FADV_NORMAL */ + break; + case UVWASI_ADVICE_RANDOM: +#ifdef POSIX_FADV_NORMAL + mapped_advice = POSIX_FADV_RANDOM; +#endif /* POSIX_FADV_NORMAL */ + break; + case UVWASI_ADVICE_SEQUENTIAL: +#ifdef POSIX_FADV_NORMAL + mapped_advice = POSIX_FADV_SEQUENTIAL; +#endif /* POSIX_FADV_NORMAL */ + break; + case UVWASI_ADVICE_WILLNEED: +#ifdef POSIX_FADV_NORMAL + mapped_advice = POSIX_FADV_WILLNEED; +#endif /* POSIX_FADV_NORMAL */ + break; + default: + return UVWASI_EINVAL; + } + + err = uvwasi_fd_table_get(&uvwasi->fds, fd, &wrap, UVWASI_RIGHT_FD_ADVISE, 0); + if (err != UVWASI_ESUCCESS) + return err; + +#ifdef POSIX_FADV_NORMAL + r = posix_fadvise(wrap->fd, offset, len, mapped_advice); + if (r != 0) + return uvwasi__translate_uv_error(uv_translate_sys_error(r)); +#endif /* POSIX_FADV_NORMAL */ + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_allocate(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_filesize_t offset, + uvwasi_filesize_t len) { +#if !defined(__POSIX__) + uv_fs_t req; + uint64_t st_size; +#endif /* !__POSIX__ */ + struct uvwasi_fd_wrap_t* wrap; + uvwasi_errno_t err; + int r; + + if (uvwasi == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_FD_ALLOCATE, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + /* Try to use posix_fallocate(). If that's not an option, fall back to the + race condition prone combination of fstat() + ftruncate(). */ +#if defined(__POSIX__) + r = posix_fallocate(wrap->fd, offset, len); + if (r != 0) + return uvwasi__translate_uv_error(uv_translate_sys_error(r)); +#else + r = uv_fs_fstat(NULL, &req, wrap->fd, NULL); + st_size = req.statbuf.st_size; + uv_fs_req_cleanup(&req); + if (r != 0) + return uvwasi__translate_uv_error(r); + + if (st_size < offset + len) { + r = uv_fs_ftruncate(NULL, &req, wrap->fd, offset + len, NULL); + if (r != 0) + return uvwasi__translate_uv_error(r); + } +#endif /* __POSIX__ */ + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_close(uvwasi_t* uvwasi, uvwasi_fd_t fd) { + struct uvwasi_fd_wrap_t* wrap; + uvwasi_errno_t err; + uv_fs_t req; + int r; + + if (uvwasi == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, fd, &wrap, 0, 0); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_close(NULL, &req, wrap->fd, NULL); + uv_fs_req_cleanup(&req); + + if (r != 0) + return uvwasi__translate_uv_error(r); + + return uvwasi_fd_table_remove(&uvwasi->fds, fd); +} + + +uvwasi_errno_t uvwasi_fd_datasync(uvwasi_t* uvwasi, uvwasi_fd_t fd) { + struct uvwasi_fd_wrap_t* wrap; + uvwasi_errno_t err; + uv_fs_t req; + int r; + + if (uvwasi == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_FD_DATASYNC, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_fdatasync(NULL, &req, wrap->fd, NULL); + uv_fs_req_cleanup(&req); + + if (r != 0) + return uvwasi__translate_uv_error(r); + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_fdstat_get(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_fdstat_t* buf) { + struct uvwasi_fd_wrap_t* wrap; + uvwasi_errno_t err; +#ifndef _WIN32 + int r; +#endif + + if (uvwasi == NULL || buf == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, fd, &wrap, 0, 0); + if (err != UVWASI_ESUCCESS) + return err; + + buf->fs_filetype = wrap->type; + buf->fs_rights_base = wrap->rights_base; + buf->fs_rights_inheriting = wrap->rights_inheriting; +#ifdef _WIN32 + buf->fs_flags = 0; /* TODO(cjihrig): Missing Windows support. */ +#else + r = fcntl(wrap->fd, F_GETFL); + if (r < 0) + return uvwasi__translate_uv_error(uv_translate_sys_error(errno)); + buf->fs_flags = r; +#endif /* _WIN32 */ + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_fdstat_set_flags(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_fdflags_t flags) { +#ifdef _WIN32 + /* TODO(cjihrig): Missing Windows support. */ + return UVWASI_ENOSYS; +#else + struct uvwasi_fd_wrap_t* wrap; + uvwasi_errno_t err; + int mapped_flags; + int r; + + if (uvwasi == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_FD_FDSTAT_SET_FLAGS, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + mapped_flags = 0; + + if ((flags & UVWASI_FDFLAG_APPEND) == UVWASI_FDFLAG_APPEND) + mapped_flags |= O_APPEND; + + if ((flags & UVWASI_FDFLAG_DSYNC) == UVWASI_FDFLAG_DSYNC) +#ifdef O_DSYNC + mapped_flags |= O_DSYNC; +#else + mapped_flags |= O_SYNC; +#endif /* O_DSYNC */ + + if ((flags & UVWASI_FDFLAG_NONBLOCK) == UVWASI_FDFLAG_NONBLOCK) + mapped_flags |= O_NONBLOCK; + + if ((flags & UVWASI_FDFLAG_RSYNC) == UVWASI_FDFLAG_RSYNC) +#ifdef O_RSYNC + mapped_flags |= O_RSYNC; +#else + mapped_flags |= O_SYNC; +#endif /* O_RSYNC */ + + if ((flags & UVWASI_FDFLAG_SYNC) == UVWASI_FDFLAG_SYNC) + mapped_flags |= O_SYNC; + + r = fcntl(wrap->fd, F_SETFL, mapped_flags); + if (r < 0) + return uvwasi__translate_uv_error(uv_translate_sys_error(errno)); + + return UVWASI_ESUCCESS; +#endif /* _WIN32 */ +} + + +uvwasi_errno_t uvwasi_fd_fdstat_set_rights(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_rights_t fs_rights_base, + uvwasi_rights_t fs_rights_inheriting + ) { + struct uvwasi_fd_wrap_t* wrap; + uvwasi_errno_t err; + + if (uvwasi == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, fd, &wrap, 0, 0); + if (err != UVWASI_ESUCCESS) + return err; + + /* Check for attempts to add new permissions. */ + if ((fs_rights_base | wrap->rights_base) > wrap->rights_base) + return UVWASI_ENOTCAPABLE; + if ((fs_rights_inheriting | wrap->rights_inheriting) > + wrap->rights_inheriting) { + return UVWASI_ENOTCAPABLE; + } + + wrap->rights_base = fs_rights_base; + wrap->rights_inheriting = fs_rights_inheriting; + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_filestat_get(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_filestat_t* buf) { + struct uvwasi_fd_wrap_t* wrap; + uv_fs_t req; + uvwasi_errno_t err; + int r; + + if (uvwasi == NULL || buf == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_FD_FILESTAT_GET, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_fstat(NULL, &req, wrap->fd, NULL); + if (r != 0) { + uv_fs_req_cleanup(&req); + return uvwasi__translate_uv_error(r); + } + + uvwasi__stat_to_filestat(&req.statbuf, buf); + uv_fs_req_cleanup(&req); + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_filestat_set_size(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_filesize_t st_size) { + /* TODO(cjihrig): uv_fs_ftruncate() takes an int64_t. st_size is uint64_t. */ + struct uvwasi_fd_wrap_t* wrap; + uv_fs_t req; + uvwasi_errno_t err; + int r; + + if (uvwasi == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_FD_FILESTAT_SET_SIZE, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_ftruncate(NULL, &req, wrap->fd, st_size, NULL); + uv_fs_req_cleanup(&req); + + if (r != 0) + return uvwasi__translate_uv_error(r); + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_filestat_set_times(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_timestamp_t st_atim, + uvwasi_timestamp_t st_mtim, + uvwasi_fstflags_t fst_flags) { + /* TODO(cjihrig): libuv does not currently support nanosecond precision. */ + struct uvwasi_fd_wrap_t* wrap; + uv_fs_t req; + uvwasi_errno_t err; + int r; + + if (uvwasi == NULL) + return UVWASI_EINVAL; + + if (fst_flags & ~(UVWASI_FILESTAT_SET_ATIM | UVWASI_FILESTAT_SET_ATIM_NOW | + UVWASI_FILESTAT_SET_MTIM | UVWASI_FILESTAT_SET_MTIM_NOW)) { + return UVWASI_EINVAL; + } + + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_FD_FILESTAT_SET_TIMES, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + /* TODO(cjihrig): st_atim and st_mtim should not be unconditionally passed. */ + r = uv_fs_futime(NULL, &req, wrap->fd, st_atim, st_mtim, NULL); + uv_fs_req_cleanup(&req); + + if (r != 0) + return uvwasi__translate_uv_error(r); + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_pread(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + const uvwasi_iovec_t* iovs, + size_t iovs_len, + uvwasi_filesize_t offset, + size_t* nread) { + struct uvwasi_fd_wrap_t* wrap; + uv_buf_t* bufs; + uv_fs_t req; + uvwasi_errno_t err; + size_t uvread; + int r; + + if (uvwasi == NULL || iovs == NULL || nread == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_FD_READ | UVWASI_RIGHT_FD_SEEK, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi__setup_iovs(&bufs, iovs, iovs_len); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_read(NULL, &req, wrap->fd, bufs, iovs_len, offset, NULL); + uvread = req.result; + uv_fs_req_cleanup(&req); + free(bufs); + + if (r < 0) + return uvwasi__translate_uv_error(r); + + *nread = uvread; + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_prestat_get(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_prestat_t* buf) { + struct uvwasi_fd_wrap_t* wrap; + uvwasi_errno_t err; + + if (uvwasi == NULL || buf == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, fd, &wrap, 0, 0); + if (err != UVWASI_ESUCCESS) + return err; + if (wrap->preopen != 1) + return UVWASI_EINVAL; + + buf->pr_type = UVWASI_PREOPENTYPE_DIR; + buf->u.dir.pr_name_len = strlen(wrap->path) + 1; + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_prestat_dir_name(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + char* path, + size_t path_len) { + struct uvwasi_fd_wrap_t* wrap; + uvwasi_errno_t err; + size_t size; + + if (uvwasi == NULL || path == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, fd, &wrap, 0, 0); + if (err != UVWASI_ESUCCESS) + return err; + if (wrap->preopen != 1) + return UVWASI_EBADF; + + size = strlen(wrap->path) + 1; + if (size > path_len) + return UVWASI_ENOBUFS; + + memcpy(path, wrap->path, size); + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_pwrite(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + const uvwasi_ciovec_t* iovs, + size_t iovs_len, + uvwasi_filesize_t offset, + size_t* nwritten) { + struct uvwasi_fd_wrap_t* wrap; + uv_buf_t* bufs; + uv_fs_t req; + uvwasi_errno_t err; + size_t uvwritten; + int r; + + if (uvwasi == NULL || iovs == NULL || nwritten == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_FD_WRITE | UVWASI_RIGHT_FD_SEEK, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi__setup_ciovs(&bufs, iovs, iovs_len); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_write(NULL, &req, wrap->fd, bufs, iovs_len, offset, NULL); + uvwritten = req.result; + uv_fs_req_cleanup(&req); + free(bufs); + + if (r < 0) + return uvwasi__translate_uv_error(r); + + *nwritten = uvwritten; + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_read(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + const uvwasi_iovec_t* iovs, + size_t iovs_len, + size_t* nread) { + struct uvwasi_fd_wrap_t* wrap; + uv_buf_t* bufs; + uv_fs_t req; + uvwasi_errno_t err; + size_t uvread; + int r; + + if (uvwasi == NULL || iovs == NULL || nread == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, fd, &wrap, UVWASI_RIGHT_FD_READ, 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi__setup_iovs(&bufs, iovs, iovs_len); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_read(NULL, &req, wrap->fd, bufs, iovs_len, -1, NULL); + uvread = req.result; + uv_fs_req_cleanup(&req); + free(bufs); + + if (r < 0) + return uvwasi__translate_uv_error(r); + + *nread = uvread; + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_readdir(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + void* buf, + size_t buf_len, + uvwasi_dircookie_t cookie, + size_t* bufused) { + /* TODO(cjihrig): Support Windows where seekdir() and telldir() are used. */ + /* TODO(cjihrig): Avoid opening and closing the directory on each call. */ + struct uvwasi_fd_wrap_t* wrap; + uvwasi_dirent_t dirent; + uv_dirent_t dirents[UVWASI__READDIR_NUM_ENTRIES]; + uv_dir_t* dir; + uv_fs_t req; + uvwasi_errno_t err; + size_t name_len; + size_t available; + size_t size_to_cp; + long tell; + int i; + int r; + + if (uvwasi == NULL || buf == NULL || bufused == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_FD_READDIR, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + /* Open the directory. */ + r = uv_fs_opendir(NULL, &req, wrap->real_path, NULL); + if (r != 0) + return uvwasi__translate_uv_error(r); + + /* Setup for reading the directory. */ + dir = req.ptr; + dir->dirents = dirents; + dir->nentries = UVWASI__READDIR_NUM_ENTRIES; + uv_fs_req_cleanup(&req); + +#ifndef _WIN32 + /* TODO(cjihrig): Need a Windows equivalent of this logic. */ + /* Seek to the proper location in the directory. */ + if (cookie != UVWASI_DIRCOOKIE_START) + seekdir(dir->dir, cookie); +#endif + + /* Read the directory entries into the provided buffer. */ + err = UVWASI_ESUCCESS; + *bufused = 0; + while (0 != (r = uv_fs_readdir(NULL, &req, dir, NULL))) { + if (r < 0) { + err = uvwasi__translate_uv_error(r); + uv_fs_req_cleanup(&req); + goto exit; + } + + for (i = 0; i < r; i++) { + /* TODO(cjihrig): This should probably be serialized to the buffer + consistently across platforms. In other words, d_next should always + be 8 bytes, d_ino should always be 8 bytes, d_namlen should always be + 4 bytes, and d_type should always be 1 byte. */ +#ifndef _WIN32 + tell = telldir(dir->dir); + if (tell < 0) { + err = uvwasi__translate_uv_error(uv_translate_sys_error(errno)); + uv_fs_req_cleanup(&req); + goto exit; + } +#else + tell = 0; /* TODO(cjihrig): Need to support Windows. */ +#endif /* _WIN32 */ + + name_len = strlen(dirents[i].name); + dirent.d_next = (uvwasi_dircookie_t) tell; + /* TODO(cjihrig): Missing ino libuv (and Windows) support. fstat()? */ + dirent.d_ino = 0; + dirent.d_namlen = name_len; + + switch (dirents[i].type) { + case UV_DIRENT_FILE: + dirent.d_type = UVWASI_FILETYPE_REGULAR_FILE; + break; + case UV_DIRENT_DIR: + dirent.d_type = UVWASI_FILETYPE_DIRECTORY; + break; + case UV_DIRENT_SOCKET: + dirent.d_type = UVWASI_FILETYPE_SOCKET_STREAM; + break; + case UV_DIRENT_LINK: + dirent.d_type = UVWASI_FILETYPE_SYMBOLIC_LINK; + break; + case UV_DIRENT_CHAR: + dirent.d_type = UVWASI_FILETYPE_CHARACTER_DEVICE; + break; + case UV_DIRENT_BLOCK: + dirent.d_type = UVWASI_FILETYPE_BLOCK_DEVICE; + break; + case UV_DIRENT_FIFO: + case UV_DIRENT_UNKNOWN: + default: + dirent.d_type = UVWASI_FILETYPE_UNKNOWN; + break; + } + + /* Write dirent to the buffer. */ + available = buf_len - *bufused; + size_to_cp = sizeof(dirent) > available ? available : sizeof(dirent); + memcpy((char*)buf + *bufused, &dirent, size_to_cp); + *bufused += size_to_cp; + /* Write the entry name to the buffer. */ + available = buf_len - *bufused; + size_to_cp = name_len > available ? available : name_len; + memcpy((char*)buf + *bufused, &dirents[i].name, size_to_cp); + *bufused += size_to_cp; + } + + uv_fs_req_cleanup(&req); + + if (*bufused >= buf_len) + break; + } + +exit: + /* Close the directory. */ + r = uv_fs_closedir(NULL, &req, dir, NULL); + uv_fs_req_cleanup(&req); + if (r != 0) + return uvwasi__translate_uv_error(r); + + return err; +} + + +uvwasi_errno_t uvwasi_fd_renumber(uvwasi_t* uvwasi, + uvwasi_fd_t from, + uvwasi_fd_t to) { + struct uvwasi_fd_wrap_t* to_wrap; + struct uvwasi_fd_wrap_t* from_wrap; + uv_fs_t req; + uvwasi_errno_t err; + int r; + + if (uvwasi == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, from, &from_wrap, 0, 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi_fd_table_get(&uvwasi->fds, to, &to_wrap, 0, 0); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_close(NULL, &req, to_wrap->fd, NULL); + uv_fs_req_cleanup(&req); + if (r != 0) + return uvwasi__translate_uv_error(r); + + memcpy(to_wrap, from_wrap, sizeof(*to_wrap)); + to_wrap->id = to; + + return uvwasi_fd_table_remove(&uvwasi->fds, from); +} + + +uvwasi_errno_t uvwasi_fd_seek(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_filedelta_t offset, + uvwasi_whence_t whence, + uvwasi_filesize_t* newoffset) { + struct uvwasi_fd_wrap_t* wrap; + uvwasi_errno_t err; + + if (uvwasi == NULL || newoffset == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, fd, &wrap, UVWASI_RIGHT_FD_SEEK, 0); + if (err != UVWASI_ESUCCESS) + return err; + + return uvwasi__lseek(wrap->fd, offset, whence, newoffset); +} + + +uvwasi_errno_t uvwasi_fd_sync(uvwasi_t* uvwasi, uvwasi_fd_t fd) { + struct uvwasi_fd_wrap_t* wrap; + uv_fs_t req; + uvwasi_errno_t err; + int r; + + if (uvwasi == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_FD_SYNC, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_fsync(NULL, &req, wrap->fd, NULL); + uv_fs_req_cleanup(&req); + + if (r != 0) + return uvwasi__translate_uv_error(r); + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_fd_tell(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_filesize_t* offset) { + struct uvwasi_fd_wrap_t* wrap; + uvwasi_errno_t err; + + if (uvwasi == NULL || offset == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, fd, &wrap, UVWASI_RIGHT_FD_TELL, 0); + if (err != UVWASI_ESUCCESS) + return err; + + return uvwasi__lseek(wrap->fd, 0, UVWASI_WHENCE_CUR, offset); +} + + +uvwasi_errno_t uvwasi_fd_write(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + const uvwasi_ciovec_t* iovs, + size_t iovs_len, + size_t* nwritten) { + struct uvwasi_fd_wrap_t* wrap; + uv_buf_t* bufs; + uv_fs_t req; + uvwasi_errno_t err; + size_t uvwritten; + int r; + + if (uvwasi == NULL || iovs == NULL || nwritten == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, fd, &wrap, UVWASI_RIGHT_FD_WRITE, 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi__setup_ciovs(&bufs, iovs, iovs_len); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_write(NULL, &req, wrap->fd, bufs, iovs_len, -1, NULL); + uvwritten = req.result; + uv_fs_req_cleanup(&req); + free(bufs); + + if (r < 0) + return uvwasi__translate_uv_error(r); + + *nwritten = uvwritten; + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_path_create_directory(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + const char* path, + size_t path_len) { + char resolved_path[PATH_MAX_BYTES]; + struct uvwasi_fd_wrap_t* wrap; + uv_fs_t req; + uvwasi_errno_t err; + int r; + + if (uvwasi == NULL || path == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_PATH_CREATE_DIRECTORY, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi__resolve_path(wrap, path, path_len, resolved_path, 0); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_mkdir(NULL, &req, resolved_path, 0777, NULL); + uv_fs_req_cleanup(&req); + + if (r != 0) + return uvwasi__translate_uv_error(r); + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_path_filestat_get(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_lookupflags_t flags, + const char* path, + size_t path_len, + uvwasi_filestat_t* buf) { + char resolved_path[PATH_MAX_BYTES]; + struct uvwasi_fd_wrap_t* wrap; + uv_fs_t req; + uvwasi_errno_t err; + int r; + + if (uvwasi == NULL || path == NULL || buf == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_PATH_FILESTAT_GET, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi__resolve_path(wrap, path, path_len, resolved_path, flags); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_stat(NULL, &req, resolved_path, NULL); + if (r != 0) { + uv_fs_req_cleanup(&req); + return uvwasi__translate_uv_error(r); + } + + uvwasi__stat_to_filestat(&req.statbuf, buf); + uv_fs_req_cleanup(&req); + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_path_filestat_set_times(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_lookupflags_t flags, + const char* path, + size_t path_len, + uvwasi_timestamp_t st_atim, + uvwasi_timestamp_t st_mtim, + uvwasi_fstflags_t fst_flags) { + /* TODO(cjihrig): libuv does not currently support nanosecond precision. */ + char resolved_path[PATH_MAX_BYTES]; + struct uvwasi_fd_wrap_t* wrap; + uv_fs_t req; + uvwasi_errno_t err; + int r; + + if (uvwasi == NULL || path == NULL) + return UVWASI_EINVAL; + + if (fst_flags & ~(UVWASI_FILESTAT_SET_ATIM | UVWASI_FILESTAT_SET_ATIM_NOW | + UVWASI_FILESTAT_SET_MTIM | UVWASI_FILESTAT_SET_MTIM_NOW)) { + return UVWASI_EINVAL; + } + + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_PATH_FILESTAT_SET_TIMES, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi__resolve_path(wrap, path, path_len, resolved_path, flags); + if (err != UVWASI_ESUCCESS) + return err; + + /* TODO(cjihrig): st_atim and st_mtim should not be unconditionally passed. */ + r = uv_fs_utime(NULL, &req, resolved_path, st_atim, st_mtim, NULL); + uv_fs_req_cleanup(&req); + + if (r != 0) + return uvwasi__translate_uv_error(r); + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_path_link(uvwasi_t* uvwasi, + uvwasi_fd_t old_fd, + uvwasi_lookupflags_t old_flags, + const char* old_path, + size_t old_path_len, + uvwasi_fd_t new_fd, + const char* new_path, + size_t new_path_len) { + char resolved_old_path[PATH_MAX_BYTES]; + char resolved_new_path[PATH_MAX_BYTES]; + struct uvwasi_fd_wrap_t* old_wrap; + struct uvwasi_fd_wrap_t* new_wrap; + uvwasi_errno_t err; + uv_fs_t req; + int r; + + if (uvwasi == NULL || old_path == NULL || new_path == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, + old_fd, + &old_wrap, + UVWASI_RIGHT_PATH_LINK_SOURCE, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi_fd_table_get(&uvwasi->fds, + new_fd, + &new_wrap, + UVWASI_RIGHT_PATH_LINK_TARGET, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi__resolve_path(old_wrap, + old_path, + old_path_len, + resolved_old_path, + old_flags); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi__resolve_path(new_wrap, + new_path, + new_path_len, + resolved_new_path, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_link(NULL, &req, resolved_old_path, resolved_new_path, NULL); + uv_fs_req_cleanup(&req); + if (r != 0) + return uvwasi__translate_uv_error(r); + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_path_open(uvwasi_t* uvwasi, + uvwasi_fd_t dirfd, + uvwasi_lookupflags_t dirflags, + const char* path, + size_t path_len, + uvwasi_oflags_t o_flags, + uvwasi_rights_t fs_rights_base, + uvwasi_rights_t fs_rights_inheriting, + uvwasi_fdflags_t fs_flags, + uvwasi_fd_t* fd) { + char resolved_path[PATH_MAX_BYTES]; + uvwasi_rights_t needed_inheriting; + uvwasi_rights_t needed_base; + struct uvwasi_fd_wrap_t* dirfd_wrap; + struct uvwasi_fd_wrap_t wrap; + uvwasi_errno_t err; + uv_fs_t req; + int flags; + int read; + int write; + int r; + + if (uvwasi == NULL || path == NULL || fd == NULL) + return UVWASI_EINVAL; + + read = 0 != (fs_rights_base & (UVWASI_RIGHT_FD_READ | + UVWASI_RIGHT_FD_READDIR)); + write = 0 != (fs_rights_base & (UVWASI_RIGHT_FD_DATASYNC | + UVWASI_RIGHT_FD_WRITE | + UVWASI_RIGHT_FD_ALLOCATE | + UVWASI_RIGHT_FD_FILESTAT_SET_SIZE)); + flags = write ? read ? UV_FS_O_RDWR : UV_FS_O_WRONLY : UV_FS_O_RDONLY; + needed_base = UVWASI_RIGHT_PATH_OPEN; + needed_inheriting = fs_rights_base | fs_rights_inheriting; + + if ((o_flags & UVWASI_O_CREAT) != 0) { + flags |= UV_FS_O_CREAT; + needed_base |= UVWASI_RIGHT_PATH_CREATE_FILE; + } + if ((o_flags & UVWASI_O_DIRECTORY) != 0) + flags |= UV_FS_O_DIRECTORY; + if ((o_flags & UVWASI_O_EXCL) != 0) + flags |= UV_FS_O_EXCL; + if ((o_flags & UVWASI_O_TRUNC) != 0) { + flags |= UV_FS_O_TRUNC; + needed_base |= UVWASI_RIGHT_PATH_FILESTAT_SET_SIZE; + } + + if ((fs_flags & UVWASI_FDFLAG_APPEND) != 0) + flags |= UV_FS_O_APPEND; + if ((fs_flags & UVWASI_FDFLAG_DSYNC) != 0) { + flags |= UV_FS_O_DSYNC; + needed_inheriting |= UVWASI_RIGHT_FD_DATASYNC; + } + if ((fs_flags & UVWASI_FDFLAG_NONBLOCK) != 0) + flags |= UV_FS_O_NONBLOCK; + if ((fs_flags & UVWASI_FDFLAG_RSYNC) != 0) { +#ifdef O_RSYNC + flags |= O_RSYNC; /* libuv has no UV_FS_O_RSYNC. */ +#else + flags |= UV_FS_O_SYNC; +#endif + needed_inheriting |= UVWASI_RIGHT_FD_SYNC; + } + if ((fs_flags & UVWASI_FDFLAG_SYNC) != 0) { + flags |= UV_FS_O_SYNC; + needed_inheriting |= UVWASI_RIGHT_FD_SYNC; + } + if (write && (flags & (UV_FS_O_APPEND | UV_FS_O_TRUNC)) == 0) + needed_inheriting |= UVWASI_RIGHT_FD_SEEK; + + err = uvwasi_fd_table_get(&uvwasi->fds, + dirfd, + &dirfd_wrap, + needed_base, + needed_inheriting); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi__resolve_path(dirfd_wrap, + path, + path_len, + resolved_path, + dirflags); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_open(NULL, &req, resolved_path, flags, 0666, NULL); + uv_fs_req_cleanup(&req); + + if (r < 0) + return uvwasi__translate_uv_error(r); + + err = uvwasi_fd_table_insert_fd(&uvwasi->fds, + r, + flags, + resolved_path, + fs_rights_base, + fs_rights_inheriting, + &wrap); + if (err != UVWASI_ESUCCESS) + goto close_file_and_error_exit; + + /* Not all platforms support UV_FS_O_DIRECTORY, so enforce it here as well. */ + if ((o_flags & UVWASI_O_DIRECTORY) != 0 && + wrap.type != UVWASI_FILETYPE_DIRECTORY) { + uvwasi_fd_table_remove(&uvwasi->fds, wrap.id); + err = UVWASI_ENOTDIR; + goto close_file_and_error_exit; + } + + *fd = wrap.id; + return UVWASI_ESUCCESS; + +close_file_and_error_exit: + uv_fs_close(NULL, &req, r, NULL); + uv_fs_req_cleanup(&req); + return err; +} + + +uvwasi_errno_t uvwasi_path_readlink(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + const char* path, + size_t path_len, + char* buf, + size_t buf_len, + size_t* bufused) { + char resolved_path[PATH_MAX_BYTES]; + struct uvwasi_fd_wrap_t* wrap; + uvwasi_errno_t err; + uv_fs_t req; + size_t len; + int r; + + if (uvwasi == NULL || path == NULL || buf == NULL || bufused == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_PATH_READLINK, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi__resolve_path(wrap, path, path_len, resolved_path, 0); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_readlink(NULL, &req, resolved_path, NULL); + if (r != 0) { + uv_fs_req_cleanup(&req); + return uvwasi__translate_uv_error(r); + } + + len = strnlen(req.ptr, buf_len); + if (len >= buf_len) { + uv_fs_req_cleanup(&req); + return UVWASI_ENOBUFS; + } + + memcpy(buf, req.ptr, len); + buf[len] = '\0'; + *bufused = len + 1; + uv_fs_req_cleanup(&req); + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_path_remove_directory(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + const char* path, + size_t path_len) { + char resolved_path[PATH_MAX_BYTES]; + struct uvwasi_fd_wrap_t* wrap; + uv_fs_t req; + uvwasi_errno_t err; + int r; + + if (uvwasi == NULL || path == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_PATH_REMOVE_DIRECTORY, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi__resolve_path(wrap, path, path_len, resolved_path, 0); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_rmdir(NULL, &req, resolved_path, NULL); + uv_fs_req_cleanup(&req); + + if (r != 0) + return uvwasi__translate_uv_error(r); + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_path_rename(uvwasi_t* uvwasi, + uvwasi_fd_t old_fd, + const char* old_path, + size_t old_path_len, + uvwasi_fd_t new_fd, + const char* new_path, + size_t new_path_len) { + char resolved_old_path[PATH_MAX_BYTES]; + char resolved_new_path[PATH_MAX_BYTES]; + struct uvwasi_fd_wrap_t* old_wrap; + struct uvwasi_fd_wrap_t* new_wrap; + uvwasi_errno_t err; + uv_fs_t req; + int r; + + if (uvwasi == NULL || old_path == NULL || new_path == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, + old_fd, + &old_wrap, + UVWASI_RIGHT_PATH_RENAME_SOURCE, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi_fd_table_get(&uvwasi->fds, + new_fd, + &new_wrap, + UVWASI_RIGHT_PATH_RENAME_TARGET, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi__resolve_path(old_wrap, + old_path, + old_path_len, + resolved_old_path, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi__resolve_path(new_wrap, + new_path, + new_path_len, + resolved_new_path, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_rename(NULL, &req, resolved_old_path, resolved_new_path, NULL); + uv_fs_req_cleanup(&req); + if (r != 0) + return uvwasi__translate_uv_error(r); + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_path_symlink(uvwasi_t* uvwasi, + const char* old_path, + size_t old_path_len, + uvwasi_fd_t fd, + const char* new_path, + size_t new_path_len) { + char resolved_new_path[PATH_MAX_BYTES]; + struct uvwasi_fd_wrap_t* wrap; + uvwasi_errno_t err; + uv_fs_t req; + int r; + + if (uvwasi == NULL || old_path == NULL || new_path == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_PATH_SYMLINK, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi__resolve_path(wrap, + new_path, + new_path_len, + resolved_new_path, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + /* Windows support may require setting the flags option. */ + r = uv_fs_symlink(NULL, &req, old_path, resolved_new_path, 0, NULL); + uv_fs_req_cleanup(&req); + if (r != 0) + return uvwasi__translate_uv_error(r); + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_path_unlink_file(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + const char* path, + size_t path_len) { + char resolved_path[PATH_MAX_BYTES]; + struct uvwasi_fd_wrap_t* wrap; + uv_fs_t req; + uvwasi_errno_t err; + int r; + + if (uvwasi == NULL || path == NULL) + return UVWASI_EINVAL; + + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_PATH_UNLINK_FILE, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi__resolve_path(wrap, path, path_len, resolved_path, 0); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_unlink(NULL, &req, resolved_path, NULL); + uv_fs_req_cleanup(&req); + + if (r != 0) + return uvwasi__translate_uv_error(r); + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_poll_oneoff(uvwasi_t* uvwasi, + const uvwasi_subscription_t* in, + uvwasi_event_t* out, + size_t nsubscriptions, + size_t* nevents) { + /* TODO(cjihrig): Implement this. */ + return UVWASI_ENOTSUP; +} + + +uvwasi_errno_t uvwasi_proc_exit(uvwasi_t* uvwasi, uvwasi_exitcode_t rval) { + exit(rval); + return UVWASI_ESUCCESS; /* This doesn't happen. */ +} + + +uvwasi_errno_t uvwasi_proc_raise(uvwasi_t* uvwasi, uvwasi_signal_t sig) { + int r; + + if (uvwasi == NULL) + return UVWASI_EINVAL; + + r = uvwasi__translate_to_uv_signal(sig); + if (r == -1) + return UVWASI_ENOSYS; + + r = uv_kill(uv_os_getpid(), r); + if (r != 0) + return uvwasi__translate_uv_error(r); + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_random_get(uvwasi_t* uvwasi, void* buf, size_t buf_len) { + int r; + + if (uvwasi == NULL || buf == NULL) + return UVWASI_EINVAL; + + r = uv_random(NULL, NULL, buf, buf_len, 0, NULL); + if (r != 0) + return uvwasi__translate_uv_error(r); + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_sched_yield(uvwasi_t* uvwasi) { + if (uvwasi == NULL) + return UVWASI_EINVAL; + +#ifndef _WIN32 + if (0 != sched_yield()) + return uvwasi__translate_uv_error(uv_translate_sys_error(errno)); +#else + SwitchToThread(); +#endif /* _WIN32 */ + + return UVWASI_ESUCCESS; +} + + +uvwasi_errno_t uvwasi_sock_recv(uvwasi_t* uvwasi, + uvwasi_fd_t sock, + const uvwasi_iovec_t* ri_data, + size_t ri_data_len, + uvwasi_riflags_t ri_flags, + size_t* ro_datalen, + uvwasi_roflags_t* ro_flags) { + /* TODO(cjihrig): Waiting to implement, pending + https://github.com/WebAssembly/WASI/issues/4 */ + return UVWASI_ENOTSUP; +} + + +uvwasi_errno_t uvwasi_sock_send(uvwasi_t* uvwasi, + uvwasi_fd_t sock, + const uvwasi_ciovec_t* si_data, + size_t si_data_len, + uvwasi_siflags_t si_flags, + size_t* so_datalen) { + /* TODO(cjihrig): Waiting to implement, pending + https://github.com/WebAssembly/WASI/issues/4 */ + return UVWASI_ENOTSUP; +} + + +uvwasi_errno_t uvwasi_sock_shutdown(uvwasi_t* uvwasi, + uvwasi_fd_t sock, + uvwasi_sdflags_t how) { + /* TODO(cjihrig): Waiting to implement, pending + https://github.com/WebAssembly/WASI/issues/4 */ + return UVWASI_ENOTSUP; +} diff --git a/deps/uvwasi/uvwasi.gyp b/deps/uvwasi/uvwasi.gyp new file mode 100644 index 00000000000000..c07b07b608b724 --- /dev/null +++ b/deps/uvwasi/uvwasi.gyp @@ -0,0 +1,25 @@ +{ + 'targets': [ + { + 'target_name': 'uvwasi', + 'type': 'static_library', + 'cflags': ['-fvisibility=hidden'], + 'xcode_settings': { + 'GCC_SYMBOLS_PRIVATE_EXTERN': 'YES', # -fvisibility=hidden + }, + 'include_dirs': ['include'], + 'sources': [ + 'src/clocks.c', + 'src/fd_table.c', + 'src/uv_mapping.c', + 'src/uvwasi.c', + ], + 'dependencies': [ + '../uv/uv.gyp:libuv', + ], + 'direct_dependent_settings': { + 'include_dirs': ['include'] + }, + } + ] +} diff --git a/deps/v8/include/v8-version.h b/deps/v8/include/v8-version.h index 8970c573efda6f..e11961895d7f34 100644 --- a/deps/v8/include/v8-version.h +++ b/deps/v8/include/v8-version.h @@ -11,7 +11,7 @@ #define V8_MAJOR_VERSION 7 #define V8_MINOR_VERSION 9 #define V8_BUILD_NUMBER 317 -#define V8_PATCH_LEVEL 23 +#define V8_PATCH_LEVEL 25 // Use 1 for candidates and 0 otherwise. // (Boolean macro values are not supported by all preprocessors.) diff --git a/deps/v8/src/builtins/builtins-wasm-gen.cc b/deps/v8/src/builtins/builtins-wasm-gen.cc index 12270495c133e9..d6346fb9aa439e 100644 --- a/deps/v8/src/builtins/builtins-wasm-gen.cc +++ b/deps/v8/src/builtins/builtins-wasm-gen.cc @@ -121,18 +121,19 @@ TF_BUILTIN(WasmAtomicNotify, WasmBuiltinsAssembler) { TNode centry = LoadCEntryFromInstance(instance); TNode target = LoadBuiltinFromFrame(Builtins::kAllocateHeapNumber); + TNode context = LoadContextFromInstance(instance); // TODO(aseemgarg): Use SMIs if possible for address and count TNode address_heap = UncheckedCast( - CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); + CallStub(AllocateHeapNumberDescriptor(), target, context)); StoreHeapNumberValue(address_heap, ChangeUint32ToFloat64(address)); TNode count_heap = UncheckedCast( - CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); + CallStub(AllocateHeapNumberDescriptor(), target, context)); StoreHeapNumberValue(count_heap, ChangeUint32ToFloat64(count)); TNode result_smi = UncheckedCast(CallRuntimeWithCEntry( - Runtime::kWasmAtomicNotify, centry, NoContextConstant(), instance, + Runtime::kWasmAtomicNotify, centry, context, instance, address_heap, count_heap)); ReturnRaw(SmiToInt32(result_smi)); } @@ -149,23 +150,24 @@ TF_BUILTIN(WasmI32AtomicWait, WasmBuiltinsAssembler) { TNode centry = LoadCEntryFromInstance(instance); TNode target = LoadBuiltinFromFrame(Builtins::kAllocateHeapNumber); + TNode context = LoadContextFromInstance(instance); // TODO(aseemgarg): Use SMIs if possible for address and expected_value TNode address_heap = UncheckedCast( - CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); + CallStub(AllocateHeapNumberDescriptor(), target, context)); StoreHeapNumberValue(address_heap, ChangeUint32ToFloat64(address)); TNode expected_value_heap = UncheckedCast( - CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); + CallStub(AllocateHeapNumberDescriptor(), target, context)); StoreHeapNumberValue(expected_value_heap, ChangeInt32ToFloat64(expected_value)); TNode timeout_heap = UncheckedCast( - CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); + CallStub(AllocateHeapNumberDescriptor(), target, context)); StoreHeapNumberValue(timeout_heap, timeout); TNode result_smi = UncheckedCast(CallRuntimeWithCEntry( - Runtime::kWasmI32AtomicWait, centry, NoContextConstant(), instance, + Runtime::kWasmI32AtomicWait, centry, context, instance, address_heap, expected_value_heap, timeout_heap)); ReturnRaw(SmiToInt32(result_smi)); } @@ -184,28 +186,29 @@ TF_BUILTIN(WasmI64AtomicWait, WasmBuiltinsAssembler) { TNode centry = LoadCEntryFromInstance(instance); TNode target = LoadBuiltinFromFrame(Builtins::kAllocateHeapNumber); + TNode context = LoadContextFromInstance(instance); // TODO(aseemgarg): Use SMIs if possible for address and expected_value TNode address_heap = UncheckedCast( - CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); + CallStub(AllocateHeapNumberDescriptor(), target, context)); StoreHeapNumberValue(address_heap, ChangeUint32ToFloat64(address)); TNode expected_value_high_heap = UncheckedCast( - CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); + CallStub(AllocateHeapNumberDescriptor(), target, context)); StoreHeapNumberValue(expected_value_high_heap, ChangeUint32ToFloat64(expected_value_high)); TNode expected_value_low_heap = UncheckedCast( - CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); + CallStub(AllocateHeapNumberDescriptor(), target, context)); StoreHeapNumberValue(expected_value_low_heap, ChangeUint32ToFloat64(expected_value_low)); TNode timeout_heap = UncheckedCast( - CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); + CallStub(AllocateHeapNumberDescriptor(), target, context)); StoreHeapNumberValue(timeout_heap, timeout); TNode result_smi = UncheckedCast(CallRuntimeWithCEntry( - Runtime::kWasmI64AtomicWait, centry, NoContextConstant(), instance, + Runtime::kWasmI64AtomicWait, centry, context, instance, address_heap, expected_value_high_heap, expected_value_low_heap, timeout_heap)); ReturnRaw(SmiToInt32(result_smi)); diff --git a/deps/v8/src/compiler/heap-refs.h b/deps/v8/src/compiler/heap-refs.h index c6322ebe691936..f08e49832e202e 100644 --- a/deps/v8/src/compiler/heap-refs.h +++ b/deps/v8/src/compiler/heap-refs.h @@ -389,6 +389,7 @@ class ContextRef : public HeapObjectRef { V(JSFunction, object_function) \ V(JSFunction, promise_function) \ V(JSFunction, promise_then) \ + V(JSFunction, regexp_function) \ V(JSFunction, string_function) \ V(JSFunction, symbol_function) \ V(JSGlobalObject, global_object) \ diff --git a/deps/v8/src/compiler/js-call-reducer.cc b/deps/v8/src/compiler/js-call-reducer.cc index d400fa2673ee3d..b86b1e6baffc3e 100644 --- a/deps/v8/src/compiler/js-call-reducer.cc +++ b/deps/v8/src/compiler/js-call-reducer.cc @@ -7098,11 +7098,14 @@ Reduction JSCallReducer::ReduceRegExpPrototypeTest(Node* node) { Node* control = NodeProperties::GetControlInput(node); Node* regexp = NodeProperties::GetValueInput(node, 1); + // Only the initial JSRegExp map is valid here, since the following lastIndex + // check as well as the lowered builtin call rely on a known location of the + // lastIndex field. + Handle regexp_initial_map = + native_context().regexp_function().initial_map().object(); + MapInference inference(broker(), regexp, effect); - if (!inference.HaveMaps() || - !inference.AllOfInstanceTypes(InstanceTypeChecker::IsJSRegExp)) { - return inference.NoChange(); - } + if (!inference.Is(regexp_initial_map)) return inference.NoChange(); MapHandles const& regexp_maps = inference.GetMaps(); ZoneVector access_infos(graph()->zone()); diff --git a/deps/v8/src/compiler/map-inference.cc b/deps/v8/src/compiler/map-inference.cc index 1e2434f4ae67ce..6ce036aa0bd553 100644 --- a/deps/v8/src/compiler/map-inference.cc +++ b/deps/v8/src/compiler/map-inference.cc @@ -91,6 +91,13 @@ MapHandles const& MapInference::GetMaps() { return maps_; } +bool MapInference::Is(Handle expected_map) { + if (!HaveMaps()) return false; + const MapHandles& maps = GetMaps(); + if (maps.size() != 1) return false; + return maps[0].equals(expected_map); +} + void MapInference::InsertMapChecks(JSGraph* jsgraph, Node** effect, Node* control, const FeedbackSource& feedback) { diff --git a/deps/v8/src/compiler/map-inference.h b/deps/v8/src/compiler/map-inference.h index acba2eb0f2ff08..498b6bc15e7f37 100644 --- a/deps/v8/src/compiler/map-inference.h +++ b/deps/v8/src/compiler/map-inference.h @@ -55,6 +55,7 @@ class MapInference { V8_WARN_UNUSED_RESULT MapHandles const& GetMaps(); V8_WARN_UNUSED_RESULT bool AllOfInstanceTypes( std::function f); + V8_WARN_UNUSED_RESULT bool Is(Handle expected_map); // These methods provide a guard. // diff --git a/deps/v8/src/heap/factory.cc b/deps/v8/src/heap/factory.cc index 721682f00f26de..7e434ea041866f 100644 --- a/deps/v8/src/heap/factory.cc +++ b/deps/v8/src/heap/factory.cc @@ -3070,20 +3070,22 @@ Handle Factory::NewSyntheticModule( Handle module_name, Handle export_names, v8::Module::SyntheticModuleEvaluationSteps evaluation_steps) { ReadOnlyRoots roots(isolate()); - Handle module( - SyntheticModule::cast(New(synthetic_module_map(), AllocationType::kOld)), - isolate()); + Handle exports = ObjectHashTable::New(isolate(), static_cast(export_names->length())); Handle evaluation_steps_foreign = NewForeign(reinterpret_cast(evaluation_steps)); - module->set_exports(*exports); + + Handle module( + SyntheticModule::cast(New(synthetic_module_map(), AllocationType::kOld)), + isolate()); module->set_hash(isolate()->GenerateIdentityHash(Smi::kMaxValue)); module->set_module_namespace(roots.undefined_value()); module->set_status(Module::kUninstantiated); module->set_exception(roots.the_hole_value()); module->set_name(*module_name); module->set_export_names(*export_names); + module->set_exports(*exports); module->set_evaluation_steps(*evaluation_steps_foreign); return module; } diff --git a/deps/v8/src/ic/accessor-assembler.cc b/deps/v8/src/ic/accessor-assembler.cc index 99cbd3c3c892b1..c9f86ed3282fbe 100644 --- a/deps/v8/src/ic/accessor-assembler.cc +++ b/deps/v8/src/ic/accessor-assembler.cc @@ -1053,8 +1053,7 @@ void AccessorAssembler::HandleStoreICHandlerCase( { Comment("store_interceptor"); TailCallRuntime(Runtime::kStorePropertyWithInterceptor, p->context(), - p->value(), p->slot(), p->vector(), p->receiver(), - p->name()); + p->value(), p->receiver(), p->name()); } BIND(&if_slow); @@ -1516,8 +1515,7 @@ void AccessorAssembler::HandleStoreICProtoHandler( { Label if_add_normal(this), if_store_global_proxy(this), if_api_setter(this), - if_accessor(this), if_native_data_property(this), if_slow(this), - if_interceptor(this); + if_accessor(this), if_native_data_property(this), if_slow(this); CSA_ASSERT(this, TaggedIsSmi(smi_handler)); TNode handler_word = SmiToInt32(CAST(smi_handler)); @@ -1547,9 +1545,6 @@ void AccessorAssembler::HandleStoreICProtoHandler( GotoIf(Word32Equal(handler_kind, Int32Constant(StoreHandler::kSlow)), &if_slow); - GotoIf(Word32Equal(handler_kind, Int32Constant(StoreHandler::kInterceptor)), - &if_interceptor); - GotoIf( Word32Equal(handler_kind, Int32Constant(StoreHandler::kApiSetterHolderIsPrototype)), @@ -1574,14 +1569,6 @@ void AccessorAssembler::HandleStoreICProtoHandler( } } - BIND(&if_interceptor); - { - Comment("store_interceptor"); - TailCallRuntime(Runtime::kStorePropertyWithInterceptor, p->context(), - p->value(), p->slot(), p->vector(), p->receiver(), - p->name()); - } - BIND(&if_add_normal); { // This is a case of "transitioning store" to a dictionary mode object diff --git a/deps/v8/src/ic/ic.cc b/deps/v8/src/ic/ic.cc index 4ac5fd7abefaa7..1b481cd8172b6e 100644 --- a/deps/v8/src/ic/ic.cc +++ b/deps/v8/src/ic/ic.cc @@ -1308,8 +1308,7 @@ bool StoreIC::LookupForWrite(LookupIterator* it, Handle value, case LookupIterator::INTERCEPTOR: { Handle holder = it->GetHolder(); InterceptorInfo info = holder->GetNamedInterceptor(); - if ((it->HolderIsReceiverOrHiddenPrototype() && - !info.non_masking()) || + if (it->HolderIsReceiverOrHiddenPrototype() || !info.getter().IsUndefined(isolate()) || !info.query().IsUndefined(isolate())) { return true; @@ -2718,23 +2717,20 @@ RUNTIME_FUNCTION(Runtime_LoadPropertyWithInterceptor) { RUNTIME_FUNCTION(Runtime_StorePropertyWithInterceptor) { HandleScope scope(isolate); - DCHECK_EQ(5, args.length()); + DCHECK_EQ(3, args.length()); // Runtime functions don't follow the IC's calling convention. Handle value = args.at(0); - Handle slot = args.at(1); - Handle vector = args.at(2); - Handle receiver = args.at(3); - Handle name = args.at(4); - FeedbackSlot vector_slot = FeedbackVector::ToSlot(slot->value()); + Handle receiver = args.at(1); + Handle name = args.at(2); // TODO(ishell): Cache interceptor_holder in the store handler like we do // for LoadHandler::kInterceptor case. Handle interceptor_holder = receiver; - if (receiver->IsJSGlobalProxy()) { - FeedbackSlotKind kind = vector->GetKind(vector_slot); - if (IsStoreGlobalICKind(kind)) { - interceptor_holder = Handle::cast(isolate->global_object()); - } + if (receiver->IsJSGlobalProxy() && + (!receiver->HasNamedInterceptor() || + receiver->GetNamedInterceptor().non_masking())) { + interceptor_holder = + handle(JSObject::cast(receiver->map().prototype()), isolate); } DCHECK(interceptor_holder->HasNamedInterceptor()); Handle interceptor(interceptor_holder->GetNamedInterceptor(), diff --git a/deps/v8/src/parsing/parser-base.h b/deps/v8/src/parsing/parser-base.h index 847774910a94a1..b12a27142c22a7 100644 --- a/deps/v8/src/parsing/parser-base.h +++ b/deps/v8/src/parsing/parser-base.h @@ -4108,6 +4108,7 @@ void ParserBase::ParseFunctionBody( inner_body.Rewind(); inner_body.Add(inner_block); inner_block->set_scope(inner_scope); + impl()->RecordBlockSourceRange(inner_block, scope()->end_position()); if (!impl()->HasCheckedSyntax()) { const AstRawString* conflict = inner_scope->FindVariableDeclaredIn( function_scope, VariableMode::kLastLexicalVariableMode); diff --git a/deps/v8/test/cctest/test-api.cc b/deps/v8/test/cctest/test-api.cc index 12faaff39cbfcb..bafdd6ce3b7194 100644 --- a/deps/v8/test/cctest/test-api.cc +++ b/deps/v8/test/cctest/test-api.cc @@ -23918,6 +23918,31 @@ TEST(CreateSyntheticModule) { CHECK_EQ(i_module->status(), i::Module::kInstantiated); } +TEST(CreateSyntheticModuleGC) { + // Try to make sure that CreateSyntheticModule() deals well with a GC + // happening during its execution. + i::FLAG_gc_interval = 10; + i::FLAG_inline_new = false; + + LocalContext env; + v8::Isolate* isolate = env->GetIsolate(); + v8::Isolate::Scope iscope(isolate); + v8::HandleScope scope(isolate); + v8::Local context = v8::Context::New(isolate); + v8::Context::Scope cscope(context); + + std::vector> export_names{v8_str("default")}; + v8::Local module_name = + v8_str("CreateSyntheticModule-TestSyntheticModuleGC"); + + for (int i = 0; i < 200; i++) { + Local module = v8::Module::CreateSyntheticModule( + isolate, module_name, export_names, + UnexpectedSyntheticModuleEvaluationStepsCallback); + USE(module); + } +} + TEST(SyntheticModuleSetExports) { LocalContext env; v8::Isolate* isolate = env->GetIsolate(); diff --git a/deps/v8/test/mjsunit/code-coverage-block-async.js b/deps/v8/test/mjsunit/code-coverage-block-async.js index 111be213b6837c..56845aac0127a3 100644 --- a/deps/v8/test/mjsunit/code-coverage-block-async.js +++ b/deps/v8/test/mjsunit/code-coverage-block-async.js @@ -119,4 +119,21 @@ new Foo().timeout().next(); // 0400 {"start":184,"end":302,"count":0}, {"start":158,"end":182,"count":1}] ); +TestCoverage( +"https://crbug.com/v8/9952", +` +async function test(foo) { // 0000 + return {bar}; // 0050 + // 0100 + function bar() { // 0150 + console.log("test"); // 0200 + } // 0250 +} // 0300 +test().then(r => r.bar()); // 0350 +%PerformMicrotaskCheckpoint(); // 0400`, +[{"start":0,"end":449,"count":1}, + {"start":0,"end":301,"count":1}, + {"start":152,"end":253,"count":1}, + {"start":362,"end":374,"count":1}]); + %DebugToggleBlockCoverage(false); diff --git a/deps/v8/test/mjsunit/code-coverage-block.js b/deps/v8/test/mjsunit/code-coverage-block.js index 6cf81bcce0a311..4b9221604ad8d0 100644 --- a/deps/v8/test/mjsunit/code-coverage-block.js +++ b/deps/v8/test/mjsunit/code-coverage-block.js @@ -1083,4 +1083,33 @@ TestCoverage( {"start":16,"end":33,"count":0}] ); +TestCoverage( +"https://crbug.com/v8/9952", +` +function test(foo = "foodef") { // 0000 + return {bar}; // 0050 + // 0100 + function bar() { // 0150 + console.log("test"); // 0200 + } // 0250 +} // 0300 +test().bar(); // 0350`, +[{"start":0,"end":399,"count":1}, + {"start":0,"end":301,"count":1}, + {"start":152,"end":253,"count":1}]); + +TestCoverage( +"https://crbug.com/v8/9952", +` +function test(foo = (()=>{})) { // 0000 + return {foo}; // 0050 +} // 0100 + // 0150 +test(()=>{}).foo(); // 0200`, +[{"start":0,"end":249,"count":1}, + {"start":0,"end":101,"count":1}, + {"start":21,"end":27,"count":0}, + {"start":205,"end":211,"count":1}] +); + %DebugToggleBlockCoverage(false); diff --git a/deps/v8/test/mjsunit/regress/regress-crbug-1024758.js b/deps/v8/test/mjsunit/regress/regress-crbug-1024758.js new file mode 100644 index 00000000000000..d6f77ee0f0b145 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-crbug-1024758.js @@ -0,0 +1,37 @@ +// Copyright 2019 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. +// +// Flags: --allow-natives-syntax + +function f() { + return r.test("abc"); +} + +function to_dict(o) { + r.a = 42; + r.b = 42; + delete r.a; +} + +function to_fast(o) { + const obj = {}; + const obj2 = {}; + delete o.a; + obj.__proto__ = o; + obj[0] = 1; + obj.__proto__ = obj2; + delete obj[0]; + return o; +} + +// Shrink the instance size by first transitioning to dictionary properties, +// then back to fast properties. +const r = /./; +to_dict(r); +to_fast(r); + +%PrepareFunctionForOptimization(f); +assertTrue(f()); +%OptimizeFunctionOnNextCall(f); +assertTrue(f()); diff --git a/doc/api/assert.md b/doc/api/assert.md index d27094fec53425..b0c8c378e64b9b 100644 --- a/doc/api/assert.md +++ b/doc/api/assert.md @@ -1243,11 +1243,11 @@ assert.throws( (err) => { assert(err instanceof Error); assert(/value/.test(err)); - // Returning anything from validation functions besides `true` is not - // recommended. By doing that, it's not clear what part of the validation - // failed. Instead, throw an error about the specific validation that failed - // (as done in this example) and add as much helpful debugging information - // to that error as possible. + // Avoid returning anything from validation functions besides `true`. + // Otherwise, it's not clear what part of the validation failed. Instead, + // throw an error about the specific validation that failed (as done in this + // example) and add as much helpful debugging information to that error as + // possible. return true; }, 'unexpected error' @@ -1266,9 +1266,11 @@ a string as the second argument gets considered: function throwingFirst() { throw new Error('First'); } + function throwingSecond() { throw new Error('Second'); } + function notThrowing() {} // The second argument is a string and the input function threw an Error. @@ -1294,8 +1296,8 @@ assert.throws(throwingFirst, /Second$/); // AssertionError [ERR_ASSERTION] ``` -Due to the confusing notation, it is recommended not to use a string as the -second argument. This might lead to difficult-to-spot errors. +Due to the confusing error-prone notation, avoid a string as the second +argument. [`AssertionError`]: #assert_class_assert_assertionerror [`Class`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes diff --git a/doc/api/buffer.md b/doc/api/buffer.md index e46e7f0994cff3..a97ab6c2a6314b 100644 --- a/doc/api/buffer.md +++ b/doc/api/buffer.md @@ -1025,6 +1025,9 @@ console.log(buf.toString('ascii')); * {ArrayBuffer} The underlying `ArrayBuffer` object based on which this `Buffer` object is created. +This `ArrayBuffer` is not guaranteed to correspond exactly to the original +`Buffer`. See the notes on `buf.byteOffset` for details. + ```js const arrayBuffer = new ArrayBuffer(16); const buffer = Buffer.from(arrayBuffer); diff --git a/doc/api/child_process.md b/doc/api/child_process.md index 7a025f94fade93..746de9bd2c8b7b 100644 --- a/doc/api/child_process.md +++ b/doc/api/child_process.md @@ -1061,10 +1061,12 @@ added: v0.1.90 --> * `signal` {number|string} +* Returns: {boolean} The `subprocess.kill()` method sends a signal to the child process. If no argument is given, the process will be sent the `'SIGTERM'` signal. See -signal(7) for a list of available signals. +signal(7) for a list of available signals. This function returns `true` if +kill(2) succeeds, and `false` otherwise. ```js const { spawn } = require('child_process'); diff --git a/doc/api/cli.md b/doc/api/cli.md index c2bf666c9288d4..ad8f86f86121b4 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -192,7 +192,7 @@ added: v8.5.0 --> Enable latest experimental modules features (currently -`--experimental-conditional-exports` and `--experimental-self-resolve`). +`--experimental-conditional-exports` and `--experimental-resolve-self`). ### `--experimental-policy` + +Enable experimental WebAssembly System Interface (WASI) support. + ### `--experimental-wasm-modules` + +The context must be a `SecureContext`. + ### ERR_TLS_INVALID_PROTOCOL_METHOD @@ -1992,6 +2000,11 @@ The fulfilled value of a linking promise is not a `vm.Module` object. The current module's status does not allow for this operation. The specific meaning of the error depends on the specific function. + +### ERR_WASI_ALREADY_STARTED + +The WASI instance has already started. + ### ERR_WORKER_INVALID_EXEC_ARGV diff --git a/doc/api/esm.md b/doc/api/esm.md index caaecaa0b41498..93b89adec12a98 100644 --- a/doc/api/esm.md +++ b/doc/api/esm.md @@ -654,7 +654,10 @@ CommonJS and ES module instances of the package: ```js // ./node_modules/pkg/index.mjs - export state from './state.cjs'; + import state from './state.cjs'; + export { + state + }; ``` Even if `pkg` is used via both `require` and `import` in an application (for diff --git a/doc/api/fs.md b/doc/api/fs.md index de0acff8eac34e..9a91f9f22082e0 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -3220,6 +3220,13 @@ Synchronous rename(2). Returns `undefined`. * {string[]} diff --git a/doc/api/n-api.md b/doc/api/n-api.md index 28ed048689e2ff..3b6fb9a97bfba0 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -3239,6 +3239,8 @@ defined in [Section 7.2.14][] of the ECMAScript Language Specification. added: v13.0.0 --> +> Stability: 1 - Experimental + ```C napi_status napi_detach_arraybuffer(napi_env env, napi_value arraybuffer) @@ -3258,6 +3260,31 @@ that is, created with [`napi_create_external_arraybuffer`][]. This API represents the invocation of the `ArrayBuffer` detach operation as defined in [Section 24.1.1.3][] of the ECMAScript Language Specification. +### napi_is_detached_arraybuffer + + +> Stability: 1 - Experimental + +```C +napi_status napi_is_detached_arraybuffer(napi_env env, + napi_value arraybuffer, + bool* result) +``` + +* `[in] env`: The environment that the API is invoked under. +* `[in] arraybuffer`: The JavaScript `ArrayBuffer` to be checked. +* `[out] result`: Whether the `arraybuffer` is detached. + +Returns `napi_ok` if the API succeeded. + +The `ArrayBuffer` is considered detached if its internal data is `null`. + +This API represents the invocation of the `ArrayBuffer` `IsDetachedBuffer` +operation as defined in [Section 24.1.1.2][] of the ECMAScript Language +Specification. + ## Working with JavaScript Properties N-API exposes a set of APIs to get and set properties on JavaScript @@ -5259,6 +5286,7 @@ This API may only be called from the main thread. [Section 7]: https://tc39.github.io/ecma262/#sec-abstract-operations [Section 8.7]: https://tc39.es/ecma262/#sec-agents [Section 9.1.6]: https://tc39.github.io/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-defineownproperty-p-desc +[Section 24.1.1.2]: https://tc39.es/ecma262/#sec-isdetachedbuffer [Travis CI]: https://travis-ci.org [Visual Studio]: https://visualstudio.microsoft.com [Working with JavaScript Properties]: #n_api_working_with_javascript_properties diff --git a/doc/api/net.md b/doc/api/net.md index 419bc8677fabd8..7ec75e358f396a 100644 --- a/doc/api/net.md +++ b/doc/api/net.md @@ -548,19 +548,17 @@ added: v0.3.8 * {integer} +This property shows the number of characters buffered for writing. The buffer +may contain strings whose length after encoding is not yet known. So this number +is only an approximation of the number of bytes in the buffer. + `net.Socket` has the property that `socket.write()` always works. This is to help users get up and running quickly. The computer cannot always keep up with the amount of data that is written to a socket. The network connection simply might be too slow. Node.js will internally queue up the data written to a -socket and send it out over the wire when it is possible. (Internally it is -polling on the socket's file descriptor for being writable). - -The consequence of this internal buffering is that memory may grow. This -property shows the number of characters currently buffered to be written. -(Number of characters is approximately equal to the number of bytes to be -written, but the buffer may contain strings, and the strings are lazily -encoded, so the exact number of bytes is not known.) +socket and send it out over the wire when it is possible. +The consequence of this internal buffering is that memory may grow. Users who experience large or growing `bufferSize` should attempt to "throttle" the data flows in their program with [`socket.pause()`][] and [`socket.resume()`][]. @@ -650,9 +648,9 @@ For both types, available `options` include: * `onread` {Object} If specified, incoming data is stored in a single `buffer` and passed to the supplied `callback` when data arrives on the socket. - Note: this will cause the streaming functionality to not provide any data, - however events like `'error'`, `'end'`, and `'close'` will still be emitted - as normal and methods like `pause()` and `resume()` will also behave as + This will cause the streaming functionality to not provide any data. + The socket will emit events like `'error'`, `'end'`, and `'close'` + as usual. Methods like `pause()` and `resume()` will also behave as expected. * `buffer` {Buffer|Uint8Array|Function} Either a reusable chunk of memory to use for storing incoming data or a function that returns such. diff --git a/doc/api/perf_hooks.md b/doc/api/perf_hooks.md index 3fcdd99509ca0f..1ccf3ee37578e3 100644 --- a/doc/api/perf_hooks.md +++ b/doc/api/perf_hooks.md @@ -547,7 +547,7 @@ The standard deviation of the recorded event loop delays. ### Measuring the duration of async operations The following example uses the [Async Hooks][] and Performance APIs to measure -the actual duration of a Timeout operation (including the amount of time it +the actual duration of a Timeout operation (including the amount of time it took to execute the callback). ```js diff --git a/doc/api/process.md b/doc/api/process.md index a9b5c4af09215b..217fbb66bc0f9b 100644 --- a/doc/api/process.md +++ b/doc/api/process.md @@ -1957,7 +1957,7 @@ If Node.js is spawned with an IPC channel, the `process.send()` method can be used to send messages to the parent process. Messages will be received as a [`'message'`][] event on the parent's [`ChildProcess`][] object. -If Node.js was not spawned with an IPC channel, `process.send()` will be +If Node.js was not spawned with an IPC channel, `process.send` will be `undefined`. The message goes through serialization and parsing. The resulting message might diff --git a/doc/api/repl.md b/doc/api/repl.md index cd7edb59ddf0a1..967336710ce3a1 100644 --- a/doc/api/repl.md +++ b/doc/api/repl.md @@ -151,10 +151,9 @@ REPL session. This use of the [`domain`][] module in the REPL has these side effects: -* Uncaught exceptions only emit the [`'uncaughtException'`][] event if the - `repl` is used as standalone program. If the `repl` is included anywhere in - another application, adding a listener for this event will throw an - [`ERR_INVALID_REPL_INPUT`][] exception. +* Uncaught exceptions only emit the [`'uncaughtException'`][] event in the + standalone REPL. Adding a listener for this event in a REPL within + another Node.js program throws [`ERR_INVALID_REPL_INPUT`][]. * Trying to use [`process.setUncaughtExceptionCaptureCallback()`][] throws an [`ERR_DOMAIN_CANNOT_SET_UNCAUGHT_EXCEPTION_CAPTURE`][] error. diff --git a/doc/api/stream.md b/doc/api/stream.md index 2143a84926c8db..4e3efde0752261 100644 --- a/doc/api/stream.md +++ b/doc/api/stream.md @@ -1692,6 +1692,13 @@ of a stream that are intended for use by consumers (as described in the [API for Stream Consumers][] section). Doing so may lead to adverse side effects in application code consuming the stream. +Avoid overriding public methods such as `write()`, `end()`, `cork()`, +`uncork()`, `read()` and `destroy()`, or emitting internal events such +as `'error'`, `'data'`, `'end'`, `'finish'` and `'close'` through `.emit()`. +Doing so can break current and future stream invariants leading to behavior +and/or compatibility issues with other streams, stream utilities, and user +expectations. + ### Simplified Construction + +> Stability: 1 - Experimental + +The WASI API provides an implementation of the [WebAssembly System Interface][] +specification. WASI gives sandboxed WebAssembly applications access to the +underlying operating system via a collection of POSIX-like functions. + +```js +'use strict'; +const fs = require('fs'); +const { WASI } = require('wasi'); +const wasi = new WASI({ + args: process.argv, + env: process.env, + preopens: { + '/sandbox': '/some/real/path/that/wasm/can/access' + } +}); +const importObject = { wasi_unstable: wasi.wasiImport }; + +(async () => { + const wasm = await WebAssembly.compile(fs.readFileSync('./binary.wasm')); + const instance = await WebAssembly.instantiate(wasm, importObject); + + wasi.start(instance); +})(); +``` + +The `--experimental-wasi-unstable-preview0` and `--experimental-wasm-bigint` +CLI arguments are needed for the previous example to run. + +## Class: WASI + + +The `WASI` class provides the WASI system call API and additional convenience +methods for working with WASI-based applications. Each `WASI` instance +represents a distinct sandbox environment. For security purposes, each `WASI` +instance must have its command line arguments, environment variables, and +sandbox directory structure configured explicitly. + +### new WASI(\[options\]) + + +* `options` {Object} + * `args` {Array} An array of strings that the WebAssembly application will + see as command line arguments. The first argument is the virtual path to the + WASI command itself. **Default:** `[]`. + * `env` {Object} An object similar to `process.env` that the WebAssembly + application will see as its environment. **Default:** `{}`. + * `preopens` {Object} This object represents the WebAssembly application's + sandbox directory structure. The string keys of `preopens` are treated as + directories within the sandbox. The corresponding values in `preopens` are + the real paths to those directories on the host machine. + +### wasi.start(instance) + + +* `instance` {WebAssembly.Instance} + +Attempt to begin execution of `instance` by invoking its `_start()` export. +If `instance` does not contain a `_start()` export, then `start()` attempts to +invoke the `__wasi_unstable_reactor_start()` export. If neither of those exports +is present on `instance`, then `start()` does nothing. + +`start()` requires that `instance` exports a [`WebAssembly.Memory`][] named +`memory`. If `instance` does not have a `memory` export an exception is thrown. + +### wasi.wasiImport + + +* {Object} + +`wasiImport` is an object that implements the WASI system call API. This object +should be passed as the `wasi_unstable` import during the instantiation of a +[`WebAssembly.Instance`][]. + +[`WebAssembly.Instance`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Instance +[`WebAssembly.Memory`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Memory +[WebAssembly System Interface]: https://wasi.dev/ diff --git a/doc/api/worker_threads.md b/doc/api/worker_threads.md index af01ed7f7fba0b..b334a1091b47fd 100644 --- a/doc/api/worker_threads.md +++ b/doc/api/worker_threads.md @@ -157,12 +157,12 @@ console.log(receiveMessageOnPort(port2)); When this function is used, no `'message'` event will be emitted and the `onmessage` listener will not be invoked. -### worker.resourceLimits +## worker.resourceLimits -* {Object|undefined} +* {Object} * `maxYoungGenerationSizeMb` {number} * `maxOldGenerationSizeMb` {number} * `codeRangeSizeMb` {number} diff --git a/doc/changelogs/CHANGELOG_V13.md b/doc/changelogs/CHANGELOG_V13.md index ec3a37fbc2d380..4cc76438459d23 100644 --- a/doc/changelogs/CHANGELOG_V13.md +++ b/doc/changelogs/CHANGELOG_V13.md @@ -9,6 +9,7 @@ +13.3.0
13.2.0
13.1.0
13.0.1
@@ -32,8 +33,209 @@ * [io.js](CHANGELOG_IOJS.md) * [Archive](CHANGELOG_ARCHIVE.md) + +## 2019-12-03, Version 13.3.0 (Current), @BridgeAR + +### Notable Changes + +* **fs**: + * Reworked experimental recursive `rmdir()` (cjihrig) [#30644](https://github.com/nodejs/node/pull/30644) + * The `maxBusyTries` option is renamed to `maxRetries`, and its default is + set to 0. The `emfileWait` option has been removed, and `EMFILE` errors + use the same retry logic as other errors. The `retryDelay` option is now + supported. `ENFILE` errors are now retried. +* **http**: + * Make maximum header size configurable per-stream or per-server (Anna Henningsen) [#30570](https://github.com/nodejs/node/pull/30570) +* **http2**: + * Make maximum tolerated rejected streams configurable (Denys Otrishko) [#30534](https://github.com/nodejs/node/pull/30534) + * Allow to configure maximum tolerated invalid frames (Denys Otrishko) [#30534](https://github.com/nodejs/node/pull/30534) +* **wasi**: + * Introduce initial WASI support (cjihrig) [#30258](https://github.com/nodejs/node/pull/30258) + +### Commits + +* [[`4cd4e7c17a`](https://github.com/nodejs/node/commit/4cd4e7c17a)] - **benchmark,doc,lib,test**: prepare for padding lint rule (Rich Trott) [#30696](https://github.com/nodejs/node/pull/30696) +* [[`63eb4fee46`](https://github.com/nodejs/node/commit/63eb4fee46)] - **buffer**: fix 6-byte writeUIntBE() range check (Brian White) [#30459](https://github.com/nodejs/node/pull/30459) +* [[`e8af569200`](https://github.com/nodejs/node/commit/e8af569200)] - **buffer**: release buffers with free callbacks on env exit (Anna Henningsen) [#30551](https://github.com/nodejs/node/pull/30551) +* [[`648766bccf`](https://github.com/nodejs/node/commit/648766bccf)] - **build**: do not build mksnapshot and mkcodecache for --shared (Joyee Cheung) [#30647](https://github.com/nodejs/node/pull/30647) +* [[`6545314a4f`](https://github.com/nodejs/node/commit/6545314a4f)] - **build**: add --without-node-code-cache configure option (Joyee Cheung) [#30647](https://github.com/nodejs/node/pull/30647) +* [[`80ada94cd3`](https://github.com/nodejs/node/commit/80ada94cd3)] - **build**: use Node.js instead of Node in configure (Tobias Nießen) [#30642](https://github.com/nodejs/node/pull/30642) +* [[`0aae502c67`](https://github.com/nodejs/node/commit/0aae502c67)] - **build,win**: propagate error codes in vcbuild (João Reis) [#30724](https://github.com/nodejs/node/pull/30724) +* [[`6a53152b42`](https://github.com/nodejs/node/commit/6a53152b42)] - **build,win**: add test-ci-native and test-ci-js (João Reis) [#30724](https://github.com/nodejs/node/pull/30724) +* [[`30a4f68a15`](https://github.com/nodejs/node/commit/30a4f68a15)] - **child_process**: document kill() return value (cjihrig) [#30669](https://github.com/nodejs/node/pull/30669) +* [[`dae36a9692`](https://github.com/nodejs/node/commit/dae36a9692)] - **child_process**: replace var with let/const (dnlup) [#30389](https://github.com/nodejs/node/pull/30389) +* [[`4b13bca31a`](https://github.com/nodejs/node/commit/4b13bca31a)] - **child_process**: replace var with const/let in internal/child\_process.js (Luis Camargo) [#30414](https://github.com/nodejs/node/pull/30414) +* [[`378c54fe97`](https://github.com/nodejs/node/commit/378c54fe97)] - **cluster**: replace vars in child.js (EmaSuriano) [#30383](https://github.com/nodejs/node/pull/30383) +* [[`708e67a732`](https://github.com/nodejs/node/commit/708e67a732)] - **cluster**: replace var with let (Herrmann, Rene R. (656)) [#30425](https://github.com/nodejs/node/pull/30425) +* [[`55fbe45f69`](https://github.com/nodejs/node/commit/55fbe45f69)] - **cluster**: replace var by let in shared\_handle.js (poutch) [#30402](https://github.com/nodejs/node/pull/30402) +* [[`4affc30a12`](https://github.com/nodejs/node/commit/4affc30a12)] - **crypto**: automatically manage memory for ECDSA\_SIG (Tobias Nießen) [#30641](https://github.com/nodejs/node/pull/30641) +* [[`55c2ac70b7`](https://github.com/nodejs/node/commit/55c2ac70b7)] - **crypto**: remove redundant validateUint32 argument (Tobias Nießen) [#30579](https://github.com/nodejs/node/pull/30579) +* [[`0ba877a541`](https://github.com/nodejs/node/commit/0ba877a541)] - **deps**: V8: cherry-pick 0dfd9ea51241 (bcoe) [#30713](https://github.com/nodejs/node/pull/30713) +* [[`b470354057`](https://github.com/nodejs/node/commit/b470354057)] - **deps**: patch V8 to 7.9.317.25 (Myles Borins) [#30679](https://github.com/nodejs/node/pull/30679) +* [[`d257448bca`](https://github.com/nodejs/node/commit/d257448bca)] - **deps**: update llhttp to 2.0.1 (Fedor Indutny) [#30553](https://github.com/nodejs/node/pull/30553) +* [[`456d250d2d`](https://github.com/nodejs/node/commit/456d250d2d)] - **deps**: V8: backport 93f189f19a03 (Michaël Zasso) [#30681](https://github.com/nodejs/node/pull/30681) +* [[`aa01ebdbca`](https://github.com/nodejs/node/commit/aa01ebdbca)] - **deps**: V8: cherry-pick ca5b0ec (Anna Henningsen) [#30708](https://github.com/nodejs/node/pull/30708) +* [[`f37450f580`](https://github.com/nodejs/node/commit/f37450f580)] - **dns**: use length for building TXT string (Anna Henningsen) [#30690](https://github.com/nodejs/node/pull/30690) +* [[`3d302ff276`](https://github.com/nodejs/node/commit/3d302ff276)] - **doc**: fix typographical error (Rich Trott) [#30735](https://github.com/nodejs/node/pull/30735) +* [[`19b31c1bc5`](https://github.com/nodejs/node/commit/19b31c1bc5)] - **doc**: revise REPL uncaught exception text (Rich Trott) [#30729](https://github.com/nodejs/node/pull/30729) +* [[`61af1fcaa1`](https://github.com/nodejs/node/commit/61af1fcaa1)] - **doc**: update signature algorithm in release doc (Myles Borins) [#30673](https://github.com/nodejs/node/pull/30673) +* [[`a8002d92ab`](https://github.com/nodejs/node/commit/a8002d92ab)] - **doc**: update README.md to fix active/maint times (Michael Dawson) [#30707](https://github.com/nodejs/node/pull/30707) +* [[`f46df0b496`](https://github.com/nodejs/node/commit/f46df0b496)] - **doc**: update socket.bufferSize text (Rich Trott) [#30723](https://github.com/nodejs/node/pull/30723) +* [[`cbd50262c0`](https://github.com/nodejs/node/commit/cbd50262c0)] - **doc**: note that buf.buffer's contents might differ (AJ Jordan) [#29651](https://github.com/nodejs/node/pull/29651) +* [[`a25626c1ed`](https://github.com/nodejs/node/commit/a25626c1ed)] - **doc**: clarify IncomingMessage.destroy() description (Sam Foxman) [#30255](https://github.com/nodejs/node/pull/30255) +* [[`8fcb450934`](https://github.com/nodejs/node/commit/8fcb450934)] - **doc**: fixed a typo in process.md (Harendra Singh) [#30277](https://github.com/nodejs/node/pull/30277) +* [[`ad9f737e44`](https://github.com/nodejs/node/commit/ad9f737e44)] - **doc**: documenting a bit more FreeBSD case (David Carlier) [#30325](https://github.com/nodejs/node/pull/30325) +* [[`40b762177f`](https://github.com/nodejs/node/commit/40b762177f)] - **doc**: add missing 'added' versions to module.builtinModules (Thomas Watson) [#30562](https://github.com/nodejs/node/pull/30562) +* [[`aca0119089`](https://github.com/nodejs/node/commit/aca0119089)] - **doc**: fix worker.resourceLimits indentation (Daniel Nalborczyk) [#30663](https://github.com/nodejs/node/pull/30663) +* [[`43e78578a6`](https://github.com/nodejs/node/commit/43e78578a6)] - **doc**: fix worker.resourceLimits type (Daniel Nalborczyk) [#30664](https://github.com/nodejs/node/pull/30664) +* [[`20dbce17d5`](https://github.com/nodejs/node/commit/20dbce17d5)] - **doc**: avoid proposal syntax in code example (Alex Zherdev) [#30685](https://github.com/nodejs/node/pull/30685) +* [[`1e7c567734`](https://github.com/nodejs/node/commit/1e7c567734)] - **doc**: address nits for src/README.md (Anna Henningsen) [#30693](https://github.com/nodejs/node/pull/30693) +* [[`87136c9bde`](https://github.com/nodejs/node/commit/87136c9bde)] - **doc**: revise socket.connect() note (Rich Trott) [#30691](https://github.com/nodejs/node/pull/30691) +* [[`fcde49700c`](https://github.com/nodejs/node/commit/fcde49700c)] - **doc**: remove "this API is unstable" note for v8 serdes API (bruce-one) [#30631](https://github.com/nodejs/node/pull/30631) +* [[`809a2b056b`](https://github.com/nodejs/node/commit/809a2b056b)] - **doc**: fixup incorrect flag name reference (Guy Bedford) [#30651](https://github.com/nodejs/node/pull/30651) +* [[`3d978839c1`](https://github.com/nodejs/node/commit/3d978839c1)] - **doc**: minor updates to releases.md (Beth Griggs) [#30636](https://github.com/nodejs/node/pull/30636) +* [[`e9f031c741`](https://github.com/nodejs/node/commit/e9f031c741)] - **doc**: add 13 and 12 to previous versions (Andrew Hughes) [#30590](https://github.com/nodejs/node/pull/30590) +* [[`8ab18b6b6f`](https://github.com/nodejs/node/commit/8ab18b6b6f)] - **doc**: update AUTHORS list (Gus Caplan) [#30672](https://github.com/nodejs/node/pull/30672) +* [[`329a821d25`](https://github.com/nodejs/node/commit/329a821d25)] - **doc**: add explanation why keep var with for loop (Lucas Recknagel) [#30380](https://github.com/nodejs/node/pull/30380) +* [[`426ca263c8`](https://github.com/nodejs/node/commit/426ca263c8)] - **doc**: document "Resume Build" limitation (Richard Lau) [#30604](https://github.com/nodejs/node/pull/30604) +* [[`00f7cc65a1`](https://github.com/nodejs/node/commit/00f7cc65a1)] - **doc**: add note of caution about non-conforming streams (Robert Nagy) [#29895](https://github.com/nodejs/node/pull/29895) +* [[`7d98a59c39`](https://github.com/nodejs/node/commit/7d98a59c39)] - **doc**: add note about debugging worker\_threads (Denys Otrishko) [#30594](https://github.com/nodejs/node/pull/30594) +* [[`8ef629a78a`](https://github.com/nodejs/node/commit/8ef629a78a)] - **doc**: simplify "is recommended" language in assert documentation (Rich Trott) [#30558](https://github.com/nodejs/node/pull/30558) +* [[`19d192d1f0`](https://github.com/nodejs/node/commit/19d192d1f0)] - **doc**: fix a typo in a date for version 13.2.0 (Kirlat) [#30587](https://github.com/nodejs/node/pull/30587) +* [[`b67759a93c`](https://github.com/nodejs/node/commit/b67759a93c)] - **doc,deps**: document how to maintain ICU in Node.js (Steven R. Loomis) [#30607](https://github.com/nodejs/node/pull/30607) +* [[`bfcc9142f3`](https://github.com/nodejs/node/commit/bfcc9142f3)] - **doc,n-api**: mark napi\_detach\_arraybuffer as experimental (legendecas) [#30703](https://github.com/nodejs/node/pull/30703) +* [[`365f0ab09b`](https://github.com/nodejs/node/commit/365f0ab09b)] - **esm**: data URLs should ignore unknown parameters (Bradley Farias) [#30593](https://github.com/nodejs/node/pull/30593) +* [[`0285aa0967`](https://github.com/nodejs/node/commit/0285aa0967)] - **events**: improve performance caused by primordials (guzhizhou) [#30577](https://github.com/nodejs/node/pull/30577) +* [[`3475f9b82c`](https://github.com/nodejs/node/commit/3475f9b82c)] - **fs**: add ENFILE to rimraf retry logic (cjihrig) [#30644](https://github.com/nodejs/node/pull/30644) +* [[`f725953433`](https://github.com/nodejs/node/commit/f725953433)] - **fs**: add retryDelay option to rimraf (cjihrig) [#30644](https://github.com/nodejs/node/pull/30644) +* [[`51bc379243`](https://github.com/nodejs/node/commit/51bc379243)] - **fs**: remove rimraf's emfileWait option (cjihrig) [#30644](https://github.com/nodejs/node/pull/30644) +* [[`612a3a2e6c`](https://github.com/nodejs/node/commit/612a3a2e6c)] - **fs**: make rimraf default to 0 retries (cjihrig) [#30644](https://github.com/nodejs/node/pull/30644) +* [[`fa1f87b199`](https://github.com/nodejs/node/commit/fa1f87b199)] - **fs**: rename rimraf's maxBusyTries to maxRetries (cjihrig) [#30644](https://github.com/nodejs/node/pull/30644) +* [[`8ee27ffe77`](https://github.com/nodejs/node/commit/8ee27ffe77)] - **fs**: change var to let (Àlvar Pérez) [#30407](https://github.com/nodejs/node/pull/30407) +* [[`850c2a72ea`](https://github.com/nodejs/node/commit/850c2a72ea)] - **fs**: cover fs.opendir ERR\_INVALID\_CALLBACK (Vladislav Botvin) [#30307](https://github.com/nodejs/node/pull/30307) +* [[`62574087ea`](https://github.com/nodejs/node/commit/62574087ea)] - **(SEMVER-MINOR)** **http**: make maximum header size configurable per-stream or per-server (Anna Henningsen) [#30570](https://github.com/nodejs/node/pull/30570) +* [[`1d1d136806`](https://github.com/nodejs/node/commit/1d1d136806)] - **http**: set socket.server unconditionally (Anna Henningsen) [#30571](https://github.com/nodejs/node/pull/30571) +* [[`6848bfbf65`](https://github.com/nodejs/node/commit/6848bfbf65)] - **http**: replace var with let (Guilherme Goncalves) [#30421](https://github.com/nodejs/node/pull/30421) +* [[`8256d38349`](https://github.com/nodejs/node/commit/8256d38349)] - **http**: destructure primordials in lib/\_http\_server.js (Artem Maksimov) [#30315](https://github.com/nodejs/node/pull/30315) +* [[`3b169f1dbd`](https://github.com/nodejs/node/commit/3b169f1dbd)] - **http**: improve performance caused by primordials (Lucas Recknagel) [#30416](https://github.com/nodejs/node/pull/30416) +* [[`6f313f9ab0`](https://github.com/nodejs/node/commit/6f313f9ab0)] - **http2**: fix session memory accounting after pausing (Michael Lehenbauer) [#30684](https://github.com/nodejs/node/pull/30684) +* [[`7d37bcebea`](https://github.com/nodejs/node/commit/7d37bcebea)] - **(SEMVER-MINOR)** **http2**: make maximum tolerated rejected streams configurable (Denys Otrishko) [#30534](https://github.com/nodejs/node/pull/30534) +* [[`092a3c28aa`](https://github.com/nodejs/node/commit/092a3c28aa)] - **(SEMVER-MINOR)** **http2**: allow to configure maximum tolerated invalid frames (Denys Otrishko) [#30534](https://github.com/nodejs/node/pull/30534) +* [[`e92afd998f`](https://github.com/nodejs/node/commit/e92afd998f)] - **(SEMVER-MINOR)** **http2**: replace direct array usage with struct for js\_fields\_ (Denys Otrishko) [#30534](https://github.com/nodejs/node/pull/30534) +* [[`30ef8e4cbd`](https://github.com/nodejs/node/commit/30ef8e4cbd)] - **http2**: change var to let compact.js (Maria Emmanouil) [#30392](https://github.com/nodejs/node/pull/30392) +* [[`1a2ed4a5f4`](https://github.com/nodejs/node/commit/1a2ed4a5f4)] - **http2**: core.js replace var with let (Daniel Schuech) [#30403](https://github.com/nodejs/node/pull/30403) +* [[`f7ca7e6677`](https://github.com/nodejs/node/commit/f7ca7e6677)] - **http2**: replace var with let/const (Paolo Ceschi Berrini) [#30417](https://github.com/nodejs/node/pull/30417) +* [[`6322611077`](https://github.com/nodejs/node/commit/6322611077)] - **inspector**: properly shut down uv\_async\_t (Anna Henningsen) [#30612](https://github.com/nodejs/node/pull/30612) +* [[`de3a1c3019`](https://github.com/nodejs/node/commit/de3a1c3019)] - **lib**: enforce use of primordial Number (Sebastien Ahkrin) [#30700](https://github.com/nodejs/node/pull/30700) +* [[`5a9340d723`](https://github.com/nodejs/node/commit/5a9340d723)] - **lib**: use static Number properties from primordials (Michaël Zasso) [#30686](https://github.com/nodejs/node/pull/30686) +* [[`892bde635e`](https://github.com/nodejs/node/commit/892bde635e)] - **lib**: enforce use of Boolean from primordials (Michaël Zasso) [#30698](https://github.com/nodejs/node/pull/30698) +* [[`ae2c7d0b02`](https://github.com/nodejs/node/commit/ae2c7d0b02)] - **lib**: replace Date.now function by primordial DateNow (Tchoupinax) [#30689](https://github.com/nodejs/node/pull/30689) +* [[`c09e3deac5`](https://github.com/nodejs/node/commit/c09e3deac5)] - **lib**: replace ArrayBuffer.isView by primordial ArrayBuffer (Vincent Dhennin) [#30692](https://github.com/nodejs/node/pull/30692) +* [[`5ef4dceb95`](https://github.com/nodejs/node/commit/5ef4dceb95)] - **lib**: enforce use of Array from primordials (Michaël Zasso) [#30635](https://github.com/nodejs/node/pull/30635) +* [[`a4dfe3b7dc`](https://github.com/nodejs/node/commit/a4dfe3b7dc)] - **lib**: flatten access to primordials (Michaël Zasso) [#30610](https://github.com/nodejs/node/pull/30610) +* [[`b545b91de5`](https://github.com/nodejs/node/commit/b545b91de5)] - **lib**: use let instead of var (Shubham Chaturvedi) [#30375](https://github.com/nodejs/node/pull/30375) +* [[`5120926337`](https://github.com/nodejs/node/commit/5120926337)] - **lib**: replace var with let/const (jens-cappelle) [#30391](https://github.com/nodejs/node/pull/30391) +* [[`b18b056d64`](https://github.com/nodejs/node/commit/b18b056d64)] - **lib**: replace var w/ let (Chris Oyler) [#30386](https://github.com/nodejs/node/pull/30386) +* [[`3796885096`](https://github.com/nodejs/node/commit/3796885096)] - **lib**: replace var with let/const (Tijl Claessens) [#30390](https://github.com/nodejs/node/pull/30390) +* [[`ffe3040659`](https://github.com/nodejs/node/commit/ffe3040659)] - **lib**: adding perf notes js\_stream\_socket.js (ryan jarvinen) [#30415](https://github.com/nodejs/node/pull/30415) +* [[`797b938c49`](https://github.com/nodejs/node/commit/797b938c49)] - **lib**: replace var with let (Dennis Saenger) [#30396](https://github.com/nodejs/node/pull/30396) +* [[`0b64e45e41`](https://github.com/nodejs/node/commit/0b64e45e41)] - **lib**: main\_thread\_only change var to let (matijagaspar) [#30398](https://github.com/nodejs/node/pull/30398) +* [[`d024630f44`](https://github.com/nodejs/node/commit/d024630f44)] - **lib**: change var to let in stream\_base\_commons (Kyriakos Markakis) [#30426](https://github.com/nodejs/node/pull/30426) +* [[`3c041edbe7`](https://github.com/nodejs/node/commit/3c041edbe7)] - **lib**: use let instead of var (Semir Ajruli) [#30424](https://github.com/nodejs/node/pull/30424) +* [[`d277c375fd`](https://github.com/nodejs/node/commit/d277c375fd)] - **lib**: changed var to let (Oliver Belaifa) [#30427](https://github.com/nodejs/node/pull/30427) +* [[`0fd89cc0f1`](https://github.com/nodejs/node/commit/0fd89cc0f1)] - **lib**: replace var with let/const (Dries Stelten) [#30409](https://github.com/nodejs/node/pull/30409) +* [[`bdba03e3ed`](https://github.com/nodejs/node/commit/bdba03e3ed)] - **lib**: change var to let (Dimitris Ktistakis) [#30408](https://github.com/nodejs/node/pull/30408) +* [[`48fef42ca9`](https://github.com/nodejs/node/commit/48fef42ca9)] - **lib**: replace var with let/const (Tembrechts) [#30404](https://github.com/nodejs/node/pull/30404) +* [[`502173b54e`](https://github.com/nodejs/node/commit/502173b54e)] - **lib**: replace var to let in cli\_table.js (Jing Lin) [#30400](https://github.com/nodejs/node/pull/30400) +* [[`2cf8a7f117`](https://github.com/nodejs/node/commit/2cf8a7f117)] - **module**: fix specifier resolution algorithm (Rongjian Zhang) [#30574](https://github.com/nodejs/node/pull/30574) +* [[`be9788bf20`](https://github.com/nodejs/node/commit/be9788bf20)] - **n-api**: detach external ArrayBuffers on env exit (Anna Henningsen) [#30551](https://github.com/nodejs/node/pull/30551) +* [[`8171cef921`](https://github.com/nodejs/node/commit/8171cef921)] - **(SEMVER-MINOR)** **n-api**: implement napi\_is\_detached\_arraybuffer (Denys Otrishko) [#30613](https://github.com/nodejs/node/pull/30613) +* [[`cc5875b2e6`](https://github.com/nodejs/node/commit/cc5875b2e6)] - **n-api**: add missed nullptr check in napi\_has\_own\_property (Denys Otrishko) [#30626](https://github.com/nodejs/node/pull/30626) +* [[`017280e6e2`](https://github.com/nodejs/node/commit/017280e6e2)] - **net**: replaced vars to lets and consts (nathias) [#30401](https://github.com/nodejs/node/pull/30401) +* [[`56248a827a`](https://github.com/nodejs/node/commit/56248a827a)] - **process**: replace var with let/const (Jesper Ek) [#30382](https://github.com/nodejs/node/pull/30382) +* [[`5c40b2f9ac`](https://github.com/nodejs/node/commit/5c40b2f9ac)] - **process**: replace vars in per\_thread.js (EmaSuriano) [#30385](https://github.com/nodejs/node/pull/30385) +* [[`c50bbf58da`](https://github.com/nodejs/node/commit/c50bbf58da)] - **readline**: change var to let (dnlup) [#30435](https://github.com/nodejs/node/pull/30435) +* [[`b91d22cc8d`](https://github.com/nodejs/node/commit/b91d22cc8d)] - **repl**: fix referrer for dynamic import (Corey Farrell) [#30609](https://github.com/nodejs/node/pull/30609) +* [[`4e5818a456`](https://github.com/nodejs/node/commit/4e5818a456)] - **repl**: change var to let (Oliver Belaifa) [#30428](https://github.com/nodejs/node/pull/30428) +* [[`e65ad865c6`](https://github.com/nodejs/node/commit/e65ad865c6)] - **src**: change header file in node\_stat\_watcher.cc (Reza Fatahi) [#29976](https://github.com/nodejs/node/pull/29976) +* [[`be84ceefb8`](https://github.com/nodejs/node/commit/be84ceefb8)] - **src**: clean up node\_file.h (Anna Henningsen) [#30530](https://github.com/nodejs/node/pull/30530) +* [[`bccfd124b0`](https://github.com/nodejs/node/commit/bccfd124b0)] - **src**: remove unused variable in node\_dir.cc (gengjiawen) [#30267](https://github.com/nodejs/node/pull/30267) +* [[`fc11db18fe`](https://github.com/nodejs/node/commit/fc11db18fe)] - **src**: inline SetSNICallback (Anna Henningsen) [#30548](https://github.com/nodejs/node/pull/30548) +* [[`7bd587ef0c`](https://github.com/nodejs/node/commit/7bd587ef0c)] - **src**: use BaseObjectPtr to store SNI context (Anna Henningsen) [#30548](https://github.com/nodejs/node/pull/30548) +* [[`8ec0d75de7`](https://github.com/nodejs/node/commit/8ec0d75de7)] - **src**: cleanup unused headers (Alexandre Ferrando) [#30328](https://github.com/nodejs/node/pull/30328) +* [[`6c249c0982`](https://github.com/nodejs/node/commit/6c249c0982)] - **src**: run native immediates during Environment cleanup (Anna Henningsen) [#30666](https://github.com/nodejs/node/pull/30666) +* [[`bea25016d1`](https://github.com/nodejs/node/commit/bea25016d1)] - **src**: no SetImmediate from destructor in stream\_pipe code (Anna Henningsen) [#30666](https://github.com/nodejs/node/pull/30666) +* [[`94357db815`](https://github.com/nodejs/node/commit/94357db815)] - **src**: add more `can_call_into_js()` guards (Anna Henningsen) [#30666](https://github.com/nodejs/node/pull/30666) +* [[`d54432f974`](https://github.com/nodejs/node/commit/d54432f974)] - **src**: keep object alive in stream\_pipe code (Anna Henningsen) [#30666](https://github.com/nodejs/node/pull/30666) +* [[`d194c0ff37`](https://github.com/nodejs/node/commit/d194c0ff37)] - **src**: replaced var with let (Aldo Ambrosioni) [#30397](https://github.com/nodejs/node/pull/30397) +* [[`44f28ea155`](https://github.com/nodejs/node/commit/44f28ea155)] - **src**: fix -Wsign-compare warnings (cjihrig) [#30565](https://github.com/nodejs/node/pull/30565) +* [[`1916acb3cb`](https://github.com/nodejs/node/commit/1916acb3cb)] - **src**: fix signal handler crash on close (Shelley Vohr) [#30582](https://github.com/nodejs/node/pull/30582) +* [[`9e9e48bf7e`](https://github.com/nodejs/node/commit/9e9e48bf7e)] - **src**: use uv\_async\_t for WeakRefs (Anna Henningsen) [#30616](https://github.com/nodejs/node/pull/30616) +* [[`9d8d2e1f45`](https://github.com/nodejs/node/commit/9d8d2e1f45)] - **src,doc**: fix broken links (cjihrig) [#30662](https://github.com/nodejs/node/pull/30662) +* [[`f135c38796`](https://github.com/nodejs/node/commit/f135c38796)] - **src,doc**: add C++ internals documentation (Anna Henningsen) [#30552](https://github.com/nodejs/node/pull/30552) +* [[`e968e26dbd`](https://github.com/nodejs/node/commit/e968e26dbd)] - **stream**: improve performance for sync write finishes (Anna Henningsen) [#30710](https://github.com/nodejs/node/pull/30710) +* [[`49e047f7a1`](https://github.com/nodejs/node/commit/49e047f7a1)] - **test**: add coverage for ERR\_TLS\_INVALID\_PROTOCOL\_VERSION (Rich Trott) [#30741](https://github.com/nodejs/node/pull/30741) +* [[`81d81a5904`](https://github.com/nodejs/node/commit/81d81a5904)] - **test**: add an indicator `isIBMi` (Xu Meng) [#30714](https://github.com/nodejs/node/pull/30714) +* [[`37c70ee198`](https://github.com/nodejs/node/commit/37c70ee198)] - **test**: use arrow functions in async-hooks tests (garygsc) [#30137](https://github.com/nodejs/node/pull/30137) +* [[`b5c7dad95a`](https://github.com/nodejs/node/commit/b5c7dad95a)] - **test**: fix test-benchmark-streams (Rich Trott) [#30757](https://github.com/nodejs/node/pull/30757) +* [[`1e199ceb71`](https://github.com/nodejs/node/commit/1e199ceb71)] - **test**: move test-http-max-http-headers to parallel (Rich Trott) [#30712](https://github.com/nodejs/node/pull/30712) +* [[`1918b4e84f`](https://github.com/nodejs/node/commit/1918b4e84f)] - **test**: correct header length subtraction (Rich Trott) [#30712](https://github.com/nodejs/node/pull/30712) +* [[`1222be81e3`](https://github.com/nodejs/node/commit/1222be81e3)] - **test**: remove unused callback argument (Rich Trott) [#30712](https://github.com/nodejs/node/pull/30712) +* [[`d69b9b753a`](https://github.com/nodejs/node/commit/d69b9b753a)] - **test**: simplify forEach() usage (Rich Trott) [#30712](https://github.com/nodejs/node/pull/30712) +* [[`01ab031cca`](https://github.com/nodejs/node/commit/01ab031cca)] - **test**: remove unused callback argument (Rich Trott) [#30712](https://github.com/nodejs/node/pull/30712) +* [[`93707c4916`](https://github.com/nodejs/node/commit/93707c4916)] - **test**: increase coverage for trace\_events.js (Rich Trott) [#30705](https://github.com/nodejs/node/pull/30705) +* [[`4800b623ed`](https://github.com/nodejs/node/commit/4800b623ed)] - **test**: use arrow functions in addons tests (garygsc) [#30131](https://github.com/nodejs/node/pull/30131) +* [[`ba0115fe6f`](https://github.com/nodejs/node/commit/ba0115fe6f)] - **test**: refactor createHook test (Jeny) [#30568](https://github.com/nodejs/node/pull/30568) +* [[`099d3fdf87`](https://github.com/nodejs/node/commit/099d3fdf87)] - **test**: port worker + buffer test to N-API (Anna Henningsen) [#30551](https://github.com/nodejs/node/pull/30551) +* [[`83861fb333`](https://github.com/nodejs/node/commit/83861fb333)] - **test**: revert 6d022c13 (Anna Henningsen) [#30708](https://github.com/nodejs/node/pull/30708) +* [[`a3b758d634`](https://github.com/nodejs/node/commit/a3b758d634)] - **test**: move test-https-server-consumed-timeout to parallel (Rich Trott) [#30677](https://github.com/nodejs/node/pull/30677) +* [[`00f532f15e`](https://github.com/nodejs/node/commit/00f532f15e)] - **test**: remove unnecessary common.platformTimeout() call (Rich Trott) [#30677](https://github.com/nodejs/node/pull/30677) +* [[`ecb902f33c`](https://github.com/nodejs/node/commit/ecb902f33c)] - **test**: do not skip test-http-server-consumed-timeout (Rich Trott) [#30677](https://github.com/nodejs/node/pull/30677) +* [[`49458deb4f`](https://github.com/nodejs/node/commit/49458deb4f)] - **test**: remove unused function argument from http test (Rich Trott) [#30677](https://github.com/nodejs/node/pull/30677) +* [[`a2f440d326`](https://github.com/nodejs/node/commit/a2f440d326)] - **test**: add logging in case of infinite loop (Rich Trott) [#30649](https://github.com/nodejs/node/pull/30649) +* [[`3e3ad396bd`](https://github.com/nodejs/node/commit/3e3ad396bd)] - **test**: remove destructuring from test-inspector-contexts (Rich Trott) [#30649](https://github.com/nodejs/node/pull/30649) +* [[`3571e132a7`](https://github.com/nodejs/node/commit/3571e132a7)] - **test**: check for session.post() errors in test-insepctor-context (Rich Trott) [#30649](https://github.com/nodejs/node/pull/30649) +* [[`37696320a2`](https://github.com/nodejs/node/commit/37696320a2)] - **test**: add mustCall() to test-inspector-contexts (Rich Trott) [#30649](https://github.com/nodejs/node/pull/30649) +* [[`0972fa3c16`](https://github.com/nodejs/node/commit/0972fa3c16)] - **test**: add regression test for signal handler removal in exit (Anna Henningsen) [#30589](https://github.com/nodejs/node/pull/30589) +* [[`5ecfd947e2`](https://github.com/nodejs/node/commit/5ecfd947e2)] - **(SEMVER-MINOR)** **test**: update and harden http2-reset-flood (Denys Otrishko) [#30534](https://github.com/nodejs/node/pull/30534) +* [[`70d6fa122a`](https://github.com/nodejs/node/commit/70d6fa122a)] - **test**: skip test-domain-error-types in debug mode temporariliy (Rich Trott) [#30629](https://github.com/nodejs/node/pull/30629) +* [[`949f2ad528`](https://github.com/nodejs/node/commit/949f2ad528)] - **test**: move test-worker-prof to sequential (Rich Trott) [#30628](https://github.com/nodejs/node/pull/30628) +* [[`d4b61709f1`](https://github.com/nodejs/node/commit/d4b61709f1)] - **test**: dir class initialisation w/o handler (Dmitriy Kikinskiy) [#30313](https://github.com/nodejs/node/pull/30313) +* [[`60b17b4fe6`](https://github.com/nodejs/node/commit/60b17b4fe6)] - **test**: change object assign by spread operator (poutch) [#30438](https://github.com/nodejs/node/pull/30438) +* [[`97e627335f`](https://github.com/nodejs/node/commit/97e627335f)] - **test**: use useful message argument in test function (Rich Trott) [#30618](https://github.com/nodejs/node/pull/30618) +* [[`d651c7dd6b`](https://github.com/nodejs/node/commit/d651c7dd6b)] - **test**: test for minimum ICU version consistency (Richard Lau) [#30608](https://github.com/nodejs/node/pull/30608) +* [[`dade9069c3`](https://github.com/nodejs/node/commit/dade9069c3)] - **test**: code&learn var to let update (Nazar Malyy) [#30436](https://github.com/nodejs/node/pull/30436) +* [[`e401e8c8ed`](https://github.com/nodejs/node/commit/e401e8c8ed)] - **test**: change object assign to spread object (poutch) [#30422](https://github.com/nodejs/node/pull/30422) +* [[`2ecc735c48`](https://github.com/nodejs/node/commit/2ecc735c48)] - **test**: use spread instead of Object.assign (dnlup) [#30419](https://github.com/nodejs/node/pull/30419) +* [[`d8da9dacab`](https://github.com/nodejs/node/commit/d8da9dacab)] - **test**: changed var to let in module-errors (Jamar Torres) [#30413](https://github.com/nodejs/node/pull/30413) +* [[`9dab32f340`](https://github.com/nodejs/node/commit/9dab32f340)] - **test**: use spread instead of object.assign (Shubham Chaturvedi) [#30412](https://github.com/nodejs/node/pull/30412) +* [[`7e7a8165a8`](https://github.com/nodejs/node/commit/7e7a8165a8)] - **test**: replace var with let in pre\_execution.js (Vladimir Adamic) [#30411](https://github.com/nodejs/node/pull/30411) +* [[`8a9ee48797`](https://github.com/nodejs/node/commit/8a9ee48797)] - **test**: change var to let in test-trace-events (Jon Church) [#30406](https://github.com/nodejs/node/pull/30406) +* [[`d6a448825c`](https://github.com/nodejs/node/commit/d6a448825c)] - **test**: dns utils replace var (Osmond van Hemert) [#30405](https://github.com/nodejs/node/pull/30405) +* [[`01e0571e94`](https://github.com/nodejs/node/commit/01e0571e94)] - **test**: test cover cases when trace is empty (telenord) [#30311](https://github.com/nodejs/node/pull/30311) +* [[`f8dfa2d704`](https://github.com/nodejs/node/commit/f8dfa2d704)] - **test**: switch to object spread in common/benchmark.js (palmires) [#30309](https://github.com/nodejs/node/pull/30309) +* [[`36671f9bf8`](https://github.com/nodejs/node/commit/36671f9bf8)] - **test**: add common.mustCall() to stream test (Rich Trott) [#30561](https://github.com/nodejs/node/pull/30561) +* [[`106235fe91`](https://github.com/nodejs/node/commit/106235fe91)] - **test**: move explanatory comment to expected location in file (Rich Trott) [#30561](https://github.com/nodejs/node/pull/30561) +* [[`081b4e2496`](https://github.com/nodejs/node/commit/081b4e2496)] - **test**: move stream test to parallel (Rich Trott) [#30561](https://github.com/nodejs/node/pull/30561) +* [[`103d01e057`](https://github.com/nodejs/node/commit/103d01e057)] - **test**: remove string literal as message in strictEqual() in stream test (Rich Trott) [#30561](https://github.com/nodejs/node/pull/30561) +* [[`ebba3228e2`](https://github.com/nodejs/node/commit/ebba3228e2)] - **test**: use arrow function for callback in stream test (Rich Trott) [#30561](https://github.com/nodejs/node/pull/30561) +* [[`e122d397c0`](https://github.com/nodejs/node/commit/e122d397c0)] - **test**: replace setTimeout with setImmediate in stream test (Rich Trott) [#30561](https://github.com/nodejs/node/pull/30561) +* [[`20ee4997f3`](https://github.com/nodejs/node/commit/20ee4997f3)] - **test**: refactor test-dgram-multicast-set-interface-lo.js (Taylor Gagne) [#30536](https://github.com/nodejs/node/pull/30536) +* [[`7aa1df7076`](https://github.com/nodejs/node/commit/7aa1df7076)] - **tls**: introduce ERR\_TLS\_INVALID\_CONTEXT (Rich Trott) [#30718](https://github.com/nodejs/node/pull/30718) +* [[`0b0f0237c1`](https://github.com/nodejs/node/commit/0b0f0237c1)] - **tls**: add memory tracking support to SSLWrap (Anna Henningsen) [#30548](https://github.com/nodejs/node/pull/30548) +* [[`89e2c71b27`](https://github.com/nodejs/node/commit/89e2c71b27)] - **tls**: allow empty subject even with altNames defined (Jason Macgowan) [#22906](https://github.com/nodejs/node/pull/22906) +* [[`941a91daed`](https://github.com/nodejs/node/commit/941a91daed)] - **tools**: enforce blank line between functions (Rich Trott) [#30696](https://github.com/nodejs/node/pull/30696) +* [[`5a6f836a15`](https://github.com/nodejs/node/commit/5a6f836a15)] - **tools**: add unified plugin changing links for html docs (Marek Łabuz) [#29946](https://github.com/nodejs/node/pull/29946) +* [[`84f7b5c752`](https://github.com/nodejs/node/commit/84f7b5c752)] - **tools**: enable more eslint rules (cjihrig) [#30598](https://github.com/nodejs/node/pull/30598) +* [[`5522467cf5`](https://github.com/nodejs/node/commit/5522467cf5)] - **tools**: update ESLint to 6.7.1 (cjihrig) [#30598](https://github.com/nodejs/node/pull/30598) +* [[`1f10681496`](https://github.com/nodejs/node/commit/1f10681496)] - **tty**: truecolor check moved before 256 check (Duncan Healy) [#30474](https://github.com/nodejs/node/pull/30474) +* [[`6a0dd1cbbd`](https://github.com/nodejs/node/commit/6a0dd1cbbd)] - **util**: fix .format() not always calling toString when it should be (Ruben Bridgewater) [#30343](https://github.com/nodejs/node/pull/30343) +* [[`1040e7222f`](https://github.com/nodejs/node/commit/1040e7222f)] - **util**: fix inspection of errors with tampered name or stack property (Ruben Bridgewater) [#30576](https://github.com/nodejs/node/pull/30576) +* [[`18e9b56bf6`](https://github.com/nodejs/node/commit/18e9b56bf6)] - **util**: use let instead of var for util/inspect.js (Luciano) [#30399](https://github.com/nodejs/node/pull/30399) +* [[`9ec53cf5c1`](https://github.com/nodejs/node/commit/9ec53cf5c1)] - **(SEMVER-MINOR)** **wasi**: introduce initial WASI support (cjihrig) [#30258](https://github.com/nodejs/node/pull/30258) + -## 2019-21-19, Version 13.2.0 (Current), @MylesBorins +## 2019-11-21, Version 13.2.0 (Current), @MylesBorins ### Notable Changes diff --git a/doc/guides/contributing/pull-requests.md b/doc/guides/contributing/pull-requests.md index f23c92fa024e63..31e3ba64757c12 100644 --- a/doc/guides/contributing/pull-requests.md +++ b/doc/guides/contributing/pull-requests.md @@ -121,7 +121,9 @@ in the API docs will also be checked when running `make lint` (or use `REPLACEME` for the version number in the documentation YAML. For contributing C++ code, you may want to look at the -[C++ Style Guide](../../../CPP_STYLE_GUIDE.md). +[C++ Style Guide](../../../CPP_STYLE_GUIDE.md), as well as the +[README of `src/`](../../../src/README.md) for an overview over Node.js +C++ internals. ### Step 4: Commit diff --git a/doc/guides/maintaining-V8.md b/doc/guides/maintaining-V8.md index d33b745ac56b8e..733df451b2805f 100644 --- a/doc/guides/maintaining-V8.md +++ b/doc/guides/maintaining-V8.md @@ -313,6 +313,10 @@ Node.js keeps a vendored copy of V8 inside of the deps/ directory. In addition, Node.js may need to float patches that do not exist upstream. This means that some care may need to be taken to update the vendored copy of V8. +V8 builds against the version of ICU supplied by Node.js, +see [maintaining-icu.md](./maintaining-icu.md) for special considerations. +Specifically, a V8 update may necessitate an ICU update. + ### Minor updates (patch level) Because there may be floating patches on the version of V8 in Node.js, it is diff --git a/doc/guides/maintaining-icu.md b/doc/guides/maintaining-icu.md new file mode 100644 index 00000000000000..4add40b7fa2046 --- /dev/null +++ b/doc/guides/maintaining-icu.md @@ -0,0 +1,233 @@ +# Maintaining ICU in Node.js + +## Background + +International Components for Unicode ([ICU4C][ICU]) is used both by V8 +and also by Node.js directly to provide internationalization +functionality. To quote from icu-project.org: + +> ICU is a mature, widely used set of C/C++ and Java libraries providing +> Unicode and Globalization support for software applications. ICU is +> widely portable and gives applications the same results on all platforms +> and between C/C++ and Java software. + +## Data dependencies + +ICU consumes and includes: + +* Extracted locale data from [CLDR][] +* Extracted [Unicode][] data. +* Time zone ([tz][]) data + +The current versions of these items can be viewed for node with `node -p process.versions`: + +```shell +$ node -p process.versions + +{ + … + cldr: '35.1', + icu: '64.2', + tz: '2019a', + unicode: '12.1' +} +``` + +## Release Schedule + +ICU typically has >1 release a year, particularly coinciding with a major +release of [Unicode][]. The current release schedule is available on the [ICU][] +website on the left sidebar. + +### V8 depends on ICU + +V8 will aggressively upgrade to a new ICU version, due to requirements for +features/bugfixes needed for [Ecma402][] support. The minimum required version +of ICU is specified within the V8 source tree. If the ICU version is too old, +V8 will not compile. + +```c +// deps/v8/src/objects/intl-objects.h +#define V8_MINIMUM_ICU_VERSION 64 +``` + +V8 in Node.js depends on the ICU version supplied by Node.js. + +The file `tools/icu/icu_versions.json` contains the current minimum +version of ICU that Node.js is known to work with. This should be +_at least_ the same version as V8, so that users will find out +earlier that their ICU is too old. A test case validates this when +Node.js is built. + +## How to upgrade ICU + +* Make sure your Node.js workspace is clean (`git status` +should be sufficient). +* Configure Node.js with the specific [ICU version](http://icu-project.org/download) + you want to upgrade to, for example: + +```shell +./configure \ + --with-intl=full-icu \ + --with-icu-source=http://download.icu-project.org/files/icu4c/58.1/icu4c-58_1-src.tgz +make +``` + +> _Note_ in theory, the equivalent `vcbuild.bat` commands should work also, +> but the commands below are makefile-centric. + +* If there are ICU version-specific changes needed, you may need to make changes + in `tools/icu/icu-generic.gyp` or add patch files to `tools/icu/patches`. + * Specifically, look for the lists in `sources!` in the `tools/icu/icu-generic.gyp` for + files to exclude. + +* Verify the Node.js build works: + +```shell +make test-ci +``` + +Also running + + + +```js +new Intl.DateTimeFormat('es', { month: 'long' }).format(new Date(9E8)); +``` + +…Should return `enero` not `January`. + +* Now, copy `deps/icu` over to `deps/icu-small` + +> :warning: Do not modify any source code in `deps/icu-small` ! +> See section below about floating patches to ICU. + +```shell +python tools/icu/shrink-icu-src.py +``` + +* Now, do a clean rebuild of Node.js to test: + +```shell +make -k distclean +./configure +make +``` + +* Test this newly default-generated Node.js + + + +```js +process.versions.icu; +new Intl.DateTimeFormat('es', { month: 'long' }).format(new Date(9E8)); +``` + +(This should print your updated ICU version number, and also `January` again.) + +You are ready to check in the updated `deps/icu-small`. This is a big commit, +so make this a separate commit from the smaller changes. + +> :warning: Do not modify any source code in `deps/icu-small` ! +> See section below about floating patches to ICU. + +* Now, rebuild the Node.js license. + +```shell +# clean up - remove deps/icu +make clean +tools/license-builder.sh +``` + +* Update the URL and hash for the full ICU file in `tools/icu/current_ver.dep`. +It should match the ICU URL used in the first step. When this is done, the +following should build with small ICU. + +```shell +# clean up +rm -rf out deps/icu deps/icu4c* +./configure --with-intl=small-icu --download=all +make +make test-ci +``` + +* commit the change to `tools/icu/current_ver.dep` and `LICENSE` files. + + * Note: To simplify review, I often will “pre-land” this patch, meaning that + I run the patch through `curl -L https://github.com/nodejs/node/pull/xxx.patch + | git am -3 --whitespace=fix` per the collaborator’s guide… and then push that + patched branch into my PR's branch. This reduces the whitespace changes that + show up in the PR, since the final land will eliminate those anyway. + +## Floating patches to ICU + +Floating patches are applied at `configure` time. The "patch" files +are used instead of the original source files. The patch files are +complete `.cpp` files replacing the original contents. + +Patches are tied to a specific ICU version. They won’t apply to a +future ICU version. We assume that you filed a bug against [ICU][] and +upstreamed the fix, so the patch won’t be needed in a later ICU +version. + +### Example + +For example, to patch `source/tools/toolutil/pkg_genc.cpp` for +ICU version 63: + +```shell +# go to your Node.js source directory +cd + +# create the floating patch directory +mkdir -p tools/icu/patches/63 + +# create the subdirectory for the file(s) to patch: +mkdir -p tools/icu/patches/63/source/tools/toolutil/ + +# copy the file to patch +cp deps/icu-small/source/tools/toolutil/pkg_genc.cpp \ +tools/icu/patches/63/source/tools/toolutil/pkg_genc.cpp + +# Make any changes to this file: +(edit tools/icu/patches/63/source/tools/toolutil/pkg_genc.cpp ) + +# test +make clean && ./configure && make +``` + +You should see a message such as: + +```shell +INFO: Using floating patch "tools/icu/patches/63/source/tools/toolutil/pkg_genc.cpp" from "tools/icu" +``` + +### Clean Up + +Any patches older than the minimum version given in `tools/icu/icu_versions.json` +ought to be deleted, because they will never be used. + +### Why not just modify the ICU source directly? + +Especially given the V8 dependencies above, there may be times when a floating +patch to ICU is required. Though it seems expedient to simply change a file in +`deps/icu-small`, this is not the right approach for the following reasons: + +1. **Repeatability.** Given the complexity of merging in a new ICU version, +following the steps above in the prior section of this document ought to be +repeatable without concern for overriding a patch. + +2. **Verifiability.** Given the number of files modified in an ICU PR, +a floating patch could easily be missed — or dropped altogether next time +something is landed. + +3. **Compatibility.** There are a number of ways that ICU can be loaded into +Node.js (see the top level README.md). Only modifying `icu-small` would cause +the patch not to be landed in case the user specifies the ICU source code +another way. + +[ICU]: http://icu-project.org +[Unicode]: https://unicode.org +[tz]: https://www.iana.org/time-zones +[CLDR]: https://unicode.org/cldr +[Ecma402]: https://github.com/tc39/ecma402 diff --git a/doc/node.1 b/doc/node.1 index e3628034e832e5..714772336e07c1 100644 --- a/doc/node.1 +++ b/doc/node.1 @@ -141,6 +141,9 @@ Enable experimental support for a package to load itself. .It Fl -experimental-vm-modules Enable experimental ES module support in VM module. . +.It Fl -experimental-wasi-unstable-preview0 +Enable experimental WebAssembly System Interface support. +. .It Fl -experimental-wasm-modules Enable experimental WebAssembly module support. . diff --git a/doc/releases.md b/doc/releases.md index 6e642fd5ed8d33..0fc7fb6220fa70 100644 --- a/doc/releases.md +++ b/doc/releases.md @@ -32,7 +32,7 @@ official release builds for Node.js, hosted on . * [17. Cleanup](#17-cleanup) * [18. Announce](#18-announce) * [19. Celebrate](#19-celebrate) -* [Major Releases](#major-Releases) +* [Major Releases](#major-releases) ## Who can make a release? @@ -81,11 +81,11 @@ access to individuals authorized by the TSC. ### 3. A Publicly Listed GPG Key -A SHASUMS256.txt file is produced for every promoted build, nightly, and +A `SHASUMS256.txt` file is produced for every promoted build, nightly, and releases. Additionally for releases, this file is signed by the individual responsible for that release. In order to be able to verify downloaded binaries, -the public should be able to check that the SHASUMS256.txt file has been signed -by someone who has been authorized to create a release. +the public should be able to check that the `SHASUMS256.txt` file has been +signed by someone who has been authorized to create a release. The GPG keys should be fetchable from a known third-party keyserver. The SKS Keyservers at are recommended. Use the @@ -179,12 +179,13 @@ Carefully review the list of commits: should only be cherry-picked when appropriate for the type of release being made. * If you think it's risky so should wait for a while, add the `baking-for-lts` - tag. +tag. When cherry-picking commits, if there are simple conflicts you can resolve them. Otherwise, add the `backport-requested-vN.x` label to the original PR and post a comment stating that it does not land cleanly and will require a -backport PR. +backport PR. You can refer the owner of the PR to the "[Backporting to Release + Lines](https://github.com/nodejs/node/blob/master/doc/guides/backporting-to-release-lines.md)" guide. If commits were cherry-picked in this step, check that the test still pass and push to the staging branch to keep it up-to-date. @@ -483,7 +484,14 @@ $ npm install -g git-secure-tag Create a tag using the following command: ```console -$ git secure-tag -sm 'YYYY-MM-DD Node.js vx.y.z (Release Type) Release' +$ git secure-tag -sm "YYYY-MM-DD Node.js vx.y.z () Release" +``` + +`release-type` is either "Current" or "LTS". For LTS releases, you should also + include the release codename, for example: + +```txt +2019-10-22 Node.js v10.17.0 'Dubnium' (LTS) Release ``` The tag **must** be signed using the GPG key that's listed for you on the @@ -541,7 +549,7 @@ formatting passes the lint rules on `master`. ### 13. Promote and Sign the Release Builds **The same individual who signed the release tag must be the one -to promote the builds as the SHASUMS256.txt file needs to be signed with the +to promote the builds as the `SHASUMS256.txt` file needs to be signed with the same GPG key!** Use `tools/release.sh` to promote and sign the build. When run, it will perform @@ -549,10 +557,9 @@ the following actions: **a.** Select a GPG key from your private keys. It will use a command similar to: `gpg --list-secret-keys` to list your keys. If you don't have any keys, it -will bail. (Why are you releasing? Your tag should be signed!) If you have only -one key, it will use that. If you have more than one key it will ask you to -select one from the list. Be sure to use the same key that you signed your git -tag with. +will bail. If you have only one key, it will use that. If you have more than +one key it will ask you to select one from the list. Be sure to use the same +key that you signed your git tag with. **b.** Log in to the server via SSH and check for releases that can be promoted, along with the list of artifacts. It will use the `dist-promotable` command on @@ -563,32 +570,32 @@ shouldn't be), be sure to only promote the release you are responsible for. **c.** Log in to the server via SSH and run the promote script for the given release. The command on the server will be similar to: `dist-promote vx.y.z`. After this step, the release artifacts will be available for download and a -SHASUMS256.txt file will be present. The release will still be unsigned, +`SHASUMS256.txt` file will be present. The release will still be unsigned, however. -**d.** Use `scp` to download SHASUMS256.txt to a temporary directory on your +**d.** Use `scp` to download `SHASUMS256.txt` to a temporary directory on your computer. -**e.** Sign the SHASUMS256.txt file using a command similar to: `gpg ---default-key YOURKEY --clearsign /path/to/SHASUMS256.txt`. You will be prompted -by GPG for your password. The signed file will be named SHASUMS256.txt.asc. +**e.** Sign the `SHASUMS256.txt` file using a command similar to: `gpg +--default-key YOURKEY --digest-algo SHA256 --clearsign /path/to/SHASUMS256.txt`. +You will be prompted by GPG for your password. The signed file will be named +SHASUMS256.txt.asc. **f.** Output an ASCII armored version of your public GPG key using a command -similar to: `gpg --default-key YOURKEY --armor --export --output -/path/to/SHASUMS256.txt.gpg`. This does not require your password and is mainly -a convenience for users, although not the recommended way to get a copy of your -key. +similar to: `gpg --default-key YOURKEY --digest-algo SHA256 --detach-sign /path/to/SHASUMS256.txt`. +You will be prompted by GPG for your password. The signed file will be named +SHASUMS256.txt.sig. -**g.** Upload the SHASUMS256.txt files back to the server into the release +**g.** Upload the `SHASUMS256.txt` files back to the server into the release directory. If you didn't wait for ARM builds in the previous step before promoting the release, you should re-run `tools/release.sh` after the ARM builds have finished. That will move the ARM artifacts into the correct location. You will -be prompted to re-sign SHASUMS256.txt. +be prompted to re-sign `SHASUMS256.txt`. -It is possible to only sign a release by running `./tools/release.sh -s -vX.Y.Z`. +**It is possible to only sign a release by running `./tools/release.sh -s +vX.Y.Z`.** ### 14. Check the Release @@ -608,7 +615,7 @@ release. However, the blog post is not yet fully automatic. Create a new blog post by running the [nodejs.org release-post.js script][]. This script will use the promoted builds and changelog to generate the post. Run `npm run serve` to preview the post locally before pushing to the -[nodejs.org](https://github.com/nodejs/nodejs.org) repo. +[nodejs.org repository][]. * You can add a short blurb just under the main heading if you want to say something important, otherwise the text should be publication ready. @@ -616,13 +623,12 @@ This script will use the promoted builds and changelog to generate the post. Run ARMv6 builds. Any downloads that are missing will have `*Coming soon*` next to them. It's your responsibility to manually update these later when you have the outstanding builds. -* The SHASUMS256.txt.asc content is at the bottom of the post. When you update +* The `SHASUMS256.txt.asc` content is at the bottom of the post. When you update the list of tarballs you'll need to copy/paste the new contents of this file to reflect those changes. -* Always use pull-requests on the nodejs.org repo. Be respectful of that working - group, but you shouldn't have to wait for PR sign-off. Opening a PR and - merging it immediately _should_ be fine. However, please follow the following - commit message format: +* Always use pull-requests on the [nodejs.org repository][]. Be respectful + of the website team, but you do not have to wait for PR sign-off. Please + use the following commit message format: ```console Blog: vX.Y.Z release post @@ -630,8 +636,8 @@ This script will use the promoted builds and changelog to generate the post. Run Refs: ``` -* Changes to `master` on the nodejs.org repo will trigger a new build of - nodejs.org so your changes should appear in a few minutes after pushing. +* Changes to `master` on the [nodejs.org repository][] will trigger a new build + of nodejs.org so your changes should appear a few minutes after pushing. ### 16. Create the release on GitHub @@ -681,7 +687,7 @@ New Node.js Major releases happen twice per year: Major releases should be targeted for the third Tuesday of the release month. A major release must not slip beyond the release month. In other words, major - releases must not slip into May or November. +releases must not slip into May or November. The release date for the next major release should be announced immediately following the current release (e.g. the release date for 13.0.0 should be @@ -709,6 +715,10 @@ separate `vN.x-proposal` branch should be created that tracks the `vN.x` branch. This branch will contain the draft release commit (with the draft changelog). +Notify the `@nodejs/npm` team in the release proposal PR to inform them of the +upcoming release. `npm` maintains a list of [supported versions](https://github.com/npm/cli/blob/latest/lib/utils/unsupported.js#L3) +that will need updating to include the new major release. + ### Test Releases and Release Candidates Test builds should be generated from the `vN.x-proposal` branch starting at @@ -761,5 +771,6 @@ judgment there. [CI lockdown procedure]: https://github.com/nodejs/build/blob/master/doc/jenkins-guide.md#restricting-access-for-security-releases [Build issue tracker]: https://github.com/nodejs/build/issues/new [nodejs.org release-post.js script]: https://github.com/nodejs/nodejs.org/blob/master/scripts/release-post.js +[nodejs.org repository]: https://github.com/nodejs/nodejs.org [Partner Communities]: https://github.com/nodejs/community-committee/blob/master/governance/PARTNER_COMMUNITIES.md [webchat.freenode.net]: https://webchat.freenode.net/ diff --git a/lib/.eslintrc.yaml b/lib/.eslintrc.yaml index f66e15e6d963e8..2823b7aa0d1def 100644 --- a/lib/.eslintrc.yaml +++ b/lib/.eslintrc.yaml @@ -9,10 +9,16 @@ rules: - groups: [[ "&&", "||" ]] no-restricted-globals: - error + - name: Array + message: "Use `const { Array } = primordials;` instead of the global." + - name: Boolean + message: "Use `const { Boolean } = primordials;` instead of the global." - name: JSON message: "Use `const { JSON } = primordials;` instead of the global." - name: Math message: "Use `const { Math } = primordials;` instead of the global." + - name: Number + message: "Use `const { Number } = primordials;` instead of the global." - name: Object message: "Use `const { Object } = primordials;` instead of the global." - name: Reflect diff --git a/lib/_http_agent.js b/lib/_http_agent.js index f8aa395aefdb38..af25b2bba75615 100644 --- a/lib/_http_agent.js +++ b/lib/_http_agent.js @@ -21,7 +21,11 @@ 'use strict'; -const { Object } = primordials; +const { + ObjectKeys, + ObjectSetPrototypeOf, + ObjectValues, +} = primordials; const net = require('net'); const EventEmitter = require('events'); @@ -129,8 +133,8 @@ function Agent(options) { // Don't emit keylog events unless there is a listener for them. this.on('newListener', maybeEnableKeylog); } -Object.setPrototypeOf(Agent.prototype, EventEmitter.prototype); -Object.setPrototypeOf(Agent, EventEmitter); +ObjectSetPrototypeOf(Agent.prototype, EventEmitter.prototype); +ObjectSetPrototypeOf(Agent, EventEmitter); function maybeEnableKeylog(eventName) { if (eventName === 'keylog') { @@ -141,7 +145,7 @@ function maybeEnableKeylog(eventName) { agent.emit('keylog', keylog, this); }; // Existing sockets will start listening on keylog now. - const sockets = Object.values(this.sockets); + const sockets = ObjectValues(this.sockets); for (let i = 0; i < sockets.length; i++) { sockets[i].on('keylog', this[kOnKeylog]); } @@ -381,7 +385,7 @@ Agent.prototype.destroy = function destroy() { const sets = [this.freeSockets, this.sockets]; for (let s = 0; s < sets.length; s++) { const set = sets[s]; - const keys = Object.keys(set); + const keys = ObjectKeys(set); for (let v = 0; v < keys.length; v++) { const setName = set[keys[v]]; for (let n = 0; n < setName.length; n++) { diff --git a/lib/_http_client.js b/lib/_http_client.js index 4e2b8e9883bfc7..6e59fa07c65be8 100644 --- a/lib/_http_client.js +++ b/lib/_http_client.js @@ -21,7 +21,14 @@ 'use strict'; -const { Object } = primordials; +const { + ArrayIsArray, + Boolean, + NumberIsFinite, + ObjectAssign, + ObjectKeys, + ObjectSetPrototypeOf, +} = primordials; const net = require('net'); const url = require('url'); @@ -48,6 +55,7 @@ const { ERR_INVALID_PROTOCOL, ERR_UNESCAPED_CHARACTERS } = codes; +const { validateInteger } = require('internal/validators'); const { getTimerDuration } = require('internal/timers'); const { DTRACE_HTTP_CLIENT_REQUEST, @@ -107,7 +115,7 @@ function ClientRequest(input, options, cb) { cb = options; options = input || {}; } else { - options = Object.assign(input || {}, options); + options = ObjectAssign(input || {}, options); } let agent = options.agent; @@ -172,6 +180,11 @@ function ClientRequest(input, options, cb) { method = this.method = 'GET'; } + const maxHeaderSize = options.maxHeaderSize; + if (maxHeaderSize !== undefined) + validateInteger(maxHeaderSize, 'maxHeaderSize', 0); + this.maxHeaderSize = maxHeaderSize; + this.path = options.path || '/'; if (cb) { this.once('response', cb); @@ -204,7 +217,7 @@ function ClientRequest(input, options, cb) { // but only if the Agent will actually reuse the connection! // If it's not a keepAlive agent, and the maxSockets==Infinity, then // there's never a case where this socket will actually be reused - if (!this.agent.keepAlive && !Number.isFinite(this.agent.maxSockets)) { + if (!this.agent.keepAlive && !NumberIsFinite(this.agent.maxSockets)) { this._last = true; this.shouldKeepAlive = false; } else { @@ -213,10 +226,10 @@ function ClientRequest(input, options, cb) { } } - const headersArray = Array.isArray(options.headers); + const headersArray = ArrayIsArray(options.headers); if (!headersArray) { if (options.headers) { - const keys = Object.keys(options.headers); + const keys = ObjectKeys(options.headers); for (let i = 0; i < keys.length; i++) { const key = keys[i]; this.setHeader(key, options.headers[key]); @@ -295,8 +308,8 @@ function ClientRequest(input, options, cb) { this._deferToConnect(null, null, () => this._flush()); } -Object.setPrototypeOf(ClientRequest.prototype, OutgoingMessage.prototype); -Object.setPrototypeOf(ClientRequest, OutgoingMessage); +ObjectSetPrototypeOf(ClientRequest.prototype, OutgoingMessage.prototype); +ObjectSetPrototypeOf(ClientRequest, OutgoingMessage); ClientRequest.prototype._finish = function _finish() { DTRACE_HTTP_CLIENT_REQUEST(this, this.socket); @@ -655,7 +668,8 @@ function tickOnSocket(req, socket) { const parser = parsers.alloc(); req.socket = socket; parser.initialize(HTTPParser.RESPONSE, - new HTTPClientAsyncResource('HTTPINCOMINGMESSAGE', req)); + new HTTPClientAsyncResource('HTTPINCOMINGMESSAGE', req), + req.maxHeaderSize || 0); parser.socket = socket; parser.outgoing = req; req.parser = parser; diff --git a/lib/_http_common.js b/lib/_http_common.js index 8c88a90568be4c..d0f07d3b69e5f5 100644 --- a/lib/_http_common.js +++ b/lib/_http_common.js @@ -21,7 +21,9 @@ 'use strict'; -const { Math } = primordials; +const { + MathMin, +} = primordials; const { setImmediate } = require('timers'); const { methods, HTTPParser } = internalBinding('http_parser'); @@ -95,7 +97,7 @@ function parserOnHeadersComplete(versionMajor, versionMinor, headers, method, // If parser.maxHeaderPairs <= 0 assume that there's no limit. if (parser.maxHeaderPairs > 0) - n = Math.min(n, parser.maxHeaderPairs); + n = MathMin(n, parser.maxHeaderPairs); incoming._addHeaderLines(headers, n); diff --git a/lib/_http_incoming.js b/lib/_http_incoming.js index 36d81153fe7e8f..bc04e8779ff75c 100644 --- a/lib/_http_incoming.js +++ b/lib/_http_incoming.js @@ -21,7 +21,10 @@ 'use strict'; -const { Object } = primordials; +const { + ObjectDefineProperty, + ObjectSetPrototypeOf, +} = primordials; const Stream = require('stream'); @@ -80,10 +83,10 @@ function IncomingMessage(socket) { // read by the user, so there's no point continuing to handle it. this._dumped = false; } -Object.setPrototypeOf(IncomingMessage.prototype, Stream.Readable.prototype); -Object.setPrototypeOf(IncomingMessage, Stream.Readable); +ObjectSetPrototypeOf(IncomingMessage.prototype, Stream.Readable.prototype); +ObjectSetPrototypeOf(IncomingMessage, Stream.Readable); -Object.defineProperty(IncomingMessage.prototype, 'connection', { +ObjectDefineProperty(IncomingMessage.prototype, 'connection', { get: function() { return this.socket; }, diff --git a/lib/_http_outgoing.js b/lib/_http_outgoing.js index 0dc13f19fe918c..e331d073b57067 100644 --- a/lib/_http_outgoing.js +++ b/lib/_http_outgoing.js @@ -21,7 +21,14 @@ 'use strict'; -const { Object, ObjectPrototype } = primordials; +const { + ArrayIsArray, + ObjectCreate, + ObjectDefineProperty, + ObjectKeys, + ObjectPrototypeHasOwnProperty, + ObjectSetPrototypeOf, +} = primordials; const { getDefaultHighWaterMark } = require('internal/streams/state'); const assert = require('internal/assert'); @@ -109,10 +116,10 @@ function OutgoingMessage() { this._onPendingData = noopPendingOutput; } -Object.setPrototypeOf(OutgoingMessage.prototype, Stream.prototype); -Object.setPrototypeOf(OutgoingMessage, Stream); +ObjectSetPrototypeOf(OutgoingMessage.prototype, Stream.prototype); +ObjectSetPrototypeOf(OutgoingMessage, Stream); -Object.defineProperty(OutgoingMessage.prototype, 'writableFinished', { +ObjectDefineProperty(OutgoingMessage.prototype, 'writableFinished', { get() { return ( this.finished && @@ -122,32 +129,32 @@ Object.defineProperty(OutgoingMessage.prototype, 'writableFinished', { } }); -Object.defineProperty(OutgoingMessage.prototype, 'writableObjectMode', { +ObjectDefineProperty(OutgoingMessage.prototype, 'writableObjectMode', { get() { return false; } }); -Object.defineProperty(OutgoingMessage.prototype, 'writableLength', { +ObjectDefineProperty(OutgoingMessage.prototype, 'writableLength', { get() { return this.outputSize + (this.socket ? this.socket.writableLength : 0); } }); -Object.defineProperty(OutgoingMessage.prototype, 'writableHighWaterMark', { +ObjectDefineProperty(OutgoingMessage.prototype, 'writableHighWaterMark', { get() { return this.socket ? this.socket.writableHighWaterMark : HIGH_WATER_MARK; } }); -Object.defineProperty(OutgoingMessage.prototype, 'writableCorked', { +ObjectDefineProperty(OutgoingMessage.prototype, 'writableCorked', { get() { const corked = this.socket ? this.socket.writableCorked : 0; return corked + this[kCorked]; } }); -Object.defineProperty(OutgoingMessage.prototype, '_headers', { +ObjectDefineProperty(OutgoingMessage.prototype, '_headers', { get: internalUtil.deprecate(function() { return this.getHeaders(); }, 'OutgoingMessage.prototype._headers is deprecated', 'DEP0066'), @@ -155,8 +162,8 @@ Object.defineProperty(OutgoingMessage.prototype, '_headers', { if (val == null) { this[kOutHeaders] = null; } else if (typeof val === 'object') { - const headers = this[kOutHeaders] = Object.create(null); - const keys = Object.keys(val); + const headers = this[kOutHeaders] = ObjectCreate(null); + const keys = ObjectKeys(val); for (var i = 0; i < keys.length; ++i) { const name = keys[i]; headers[name.toLowerCase()] = [name, val[name]]; @@ -165,7 +172,7 @@ Object.defineProperty(OutgoingMessage.prototype, '_headers', { }, 'OutgoingMessage.prototype._headers is deprecated', 'DEP0066') }); -Object.defineProperty(OutgoingMessage.prototype, 'connection', { +ObjectDefineProperty(OutgoingMessage.prototype, 'connection', { get: function() { return this.socket; }, @@ -174,12 +181,12 @@ Object.defineProperty(OutgoingMessage.prototype, 'connection', { } }); -Object.defineProperty(OutgoingMessage.prototype, '_headerNames', { +ObjectDefineProperty(OutgoingMessage.prototype, '_headerNames', { get: internalUtil.deprecate(function() { const headers = this[kOutHeaders]; if (headers !== null) { - const out = Object.create(null); - const keys = Object.keys(headers); + const out = ObjectCreate(null); + const keys = ObjectKeys(headers); for (var i = 0; i < keys.length; ++i) { const key = keys[i]; const val = headers[key][0]; @@ -194,7 +201,7 @@ Object.defineProperty(OutgoingMessage.prototype, '_headerNames', { const headers = this[kOutHeaders]; if (!headers) return; - const keys = Object.keys(val); + const keys = ObjectKeys(val); for (var i = 0; i < keys.length; ++i) { const header = headers[keys[i]]; if (header) @@ -214,7 +221,7 @@ OutgoingMessage.prototype._renderHeaders = function _renderHeaders() { const headers = {}; if (headersMap !== null) { - const keys = Object.keys(headersMap); + const keys = ObjectKeys(headersMap); for (var i = 0, l = keys.length; i < l; i++) { const key = keys[i]; headers[headersMap[key][0]] = headersMap[key][1]; @@ -353,13 +360,13 @@ function _storeHeader(firstLine, headers) { const entry = headers[key]; processHeader(this, state, entry[0], entry[1], false); } - } else if (Array.isArray(headers)) { + } else if (ArrayIsArray(headers)) { for (const entry of headers) { processHeader(this, state, entry[0], entry[1], true); } } else { for (const key in headers) { - if (ObjectPrototype.hasOwnProperty(headers, key)) { + if (ObjectPrototypeHasOwnProperty(headers, key)) { processHeader(this, state, key, headers[key], true); } } @@ -447,7 +454,7 @@ function _storeHeader(firstLine, headers) { function processHeader(self, state, key, value, validate) { if (validate) validateHeaderName(key); - if (Array.isArray(value)) { + if (ArrayIsArray(value)) { if (value.length < 2 || !isCookieField(key)) { for (var i = 0; i < value.length; i++) storeHeader(self, state, key, value[i], validate); @@ -520,7 +527,7 @@ OutgoingMessage.prototype.setHeader = function setHeader(name, value) { let headers = this[kOutHeaders]; if (headers === null) - this[kOutHeaders] = headers = Object.create(null); + this[kOutHeaders] = headers = ObjectCreate(null); headers[name.toLowerCase()] = [name, value]; }; @@ -540,16 +547,16 @@ OutgoingMessage.prototype.getHeader = function getHeader(name) { // Returns an array of the names of the current outgoing headers. OutgoingMessage.prototype.getHeaderNames = function getHeaderNames() { - return this[kOutHeaders] !== null ? Object.keys(this[kOutHeaders]) : []; + return this[kOutHeaders] !== null ? ObjectKeys(this[kOutHeaders]) : []; }; // Returns a shallow copy of the current outgoing headers. OutgoingMessage.prototype.getHeaders = function getHeaders() { const headers = this[kOutHeaders]; - const ret = Object.create(null); + const ret = ObjectCreate(null); if (headers) { - const keys = Object.keys(headers); + const keys = ObjectKeys(headers); for (var i = 0; i < keys.length; ++i) { const key = keys[i]; const val = headers[key][1]; @@ -601,13 +608,13 @@ OutgoingMessage.prototype._implicitHeader = function _implicitHeader() { this.emit('error', new ERR_METHOD_NOT_IMPLEMENTED('_implicitHeader()')); }; -Object.defineProperty(OutgoingMessage.prototype, 'headersSent', { +ObjectDefineProperty(OutgoingMessage.prototype, 'headersSent', { configurable: true, enumerable: true, get: function() { return !!this._header; } }); -Object.defineProperty(OutgoingMessage.prototype, 'writableEnded', { +ObjectDefineProperty(OutgoingMessage.prototype, 'writableEnded', { get: function() { return this.finished; } }); @@ -688,8 +695,8 @@ function connectionCorkNT(conn) { OutgoingMessage.prototype.addTrailers = function addTrailers(headers) { this._trailer = ''; - const keys = Object.keys(headers); - const isArray = Array.isArray(headers); + const keys = ObjectKeys(headers); + const isArray = ArrayIsArray(headers); var field, value; for (var i = 0, l = keys.length; i < l; i++) { var key = keys[i]; diff --git a/lib/_http_server.js b/lib/_http_server.js index bc956a01147ad1..6c52f7adbc919d 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -21,7 +21,10 @@ 'use strict'; -const { Object } = primordials; +const { + ObjectKeys, + ObjectSetPrototypeOf, +} = primordials; const net = require('net'); const assert = require('internal/assert'); @@ -55,6 +58,7 @@ const { ERR_INVALID_ARG_TYPE, ERR_INVALID_CHAR } = require('internal/errors').codes; +const { validateInteger } = require('internal/validators'); const Buffer = require('buffer').Buffer; const { DTRACE_HTTP_SERVER_REQUEST, @@ -162,8 +166,8 @@ function ServerResponse(req) { }; } } -Object.setPrototypeOf(ServerResponse.prototype, OutgoingMessage.prototype); -Object.setPrototypeOf(ServerResponse, OutgoingMessage); +ObjectSetPrototypeOf(ServerResponse.prototype, OutgoingMessage.prototype); +ObjectSetPrototypeOf(ServerResponse, OutgoingMessage); ServerResponse.prototype._finish = function _finish() { DTRACE_HTTP_SERVER_RESPONSE(this.socket); @@ -254,7 +258,7 @@ function writeHead(statusCode, reason, obj) { // Slow-case: when progressive API and header fields are passed. let k; if (obj) { - const keys = Object.keys(obj); + const keys = ObjectKeys(obj); for (let i = 0; i < keys.length; i++) { k = keys[i]; if (k) this.setHeader(k, obj[k]); @@ -319,6 +323,11 @@ function Server(options, requestListener) { this[kIncomingMessage] = options.IncomingMessage || IncomingMessage; this[kServerResponse] = options.ServerResponse || ServerResponse; + const maxHeaderSize = options.maxHeaderSize; + if (maxHeaderSize !== undefined) + validateInteger(maxHeaderSize, 'maxHeaderSize', 0); + this.maxHeaderSize = maxHeaderSize; + net.Server.call(this, { allowHalfOpen: true }); if (requestListener) { @@ -337,8 +346,8 @@ function Server(options, requestListener) { this.maxHeadersCount = null; this.headersTimeout = 40 * 1000; // 40 seconds } -Object.setPrototypeOf(Server.prototype, net.Server.prototype); -Object.setPrototypeOf(Server, net.Server); +ObjectSetPrototypeOf(Server.prototype, net.Server.prototype); +ObjectSetPrototypeOf(Server, net.Server); Server.prototype.setTimeout = function setTimeout(msecs, callback) { @@ -360,8 +369,7 @@ function connectionListenerInternal(server, socket) { // Ensure that the server property of the socket is correctly set. // See https://github.com/nodejs/node/issues/13435 - if (socket.server === null) - socket.server = server; + socket.server = server; // If the user has added a listener to the server, // request, or response, then it's their responsibility. @@ -377,7 +385,8 @@ function connectionListenerInternal(server, socket) { // https://github.com/nodejs/node/pull/21313 parser.initialize( HTTPParser.REQUEST, - new HTTPServerAsyncResource('HTTPINCOMINGMESSAGE', socket) + new HTTPServerAsyncResource('HTTPINCOMINGMESSAGE', socket), + server.maxHeaderSize || 0 ); parser.socket = socket; diff --git a/lib/_stream_duplex.js b/lib/_stream_duplex.js index 858dc938f01cd1..572e6da94d8a6a 100644 --- a/lib/_stream_duplex.js +++ b/lib/_stream_duplex.js @@ -26,19 +26,23 @@ 'use strict'; -const { Object } = primordials; +const { + ObjectDefineProperty, + ObjectKeys, + ObjectSetPrototypeOf, +} = primordials; module.exports = Duplex; const Readable = require('_stream_readable'); const Writable = require('_stream_writable'); -Object.setPrototypeOf(Duplex.prototype, Readable.prototype); -Object.setPrototypeOf(Duplex, Readable); +ObjectSetPrototypeOf(Duplex.prototype, Readable.prototype); +ObjectSetPrototypeOf(Duplex, Readable); { // Allow the keys array to be GC'ed. - const keys = Object.keys(Writable.prototype); + const keys = ObjectKeys(Writable.prototype); for (let v = 0; v < keys.length; v++) { const method = keys[v]; if (!Duplex.prototype[method]) @@ -68,7 +72,7 @@ function Duplex(options) { } } -Object.defineProperty(Duplex.prototype, 'writableHighWaterMark', { +ObjectDefineProperty(Duplex.prototype, 'writableHighWaterMark', { // Making it explicit this property is not enumerable // because otherwise some prototype manipulation in // userland will fail @@ -78,7 +82,7 @@ Object.defineProperty(Duplex.prototype, 'writableHighWaterMark', { } }); -Object.defineProperty(Duplex.prototype, 'writableBuffer', { +ObjectDefineProperty(Duplex.prototype, 'writableBuffer', { // Making it explicit this property is not enumerable // because otherwise some prototype manipulation in // userland will fail @@ -88,7 +92,7 @@ Object.defineProperty(Duplex.prototype, 'writableBuffer', { } }); -Object.defineProperty(Duplex.prototype, 'writableLength', { +ObjectDefineProperty(Duplex.prototype, 'writableLength', { // Making it explicit this property is not enumerable // because otherwise some prototype manipulation in // userland will fail @@ -98,7 +102,7 @@ Object.defineProperty(Duplex.prototype, 'writableLength', { } }); -Object.defineProperty(Duplex.prototype, 'writableFinished', { +ObjectDefineProperty(Duplex.prototype, 'writableFinished', { // Making it explicit this property is not enumerable // because otherwise some prototype manipulation in // userland will fail @@ -108,7 +112,7 @@ Object.defineProperty(Duplex.prototype, 'writableFinished', { } }); -Object.defineProperty(Duplex.prototype, 'writableCorked', { +ObjectDefineProperty(Duplex.prototype, 'writableCorked', { // Making it explicit this property is not enumerable // because otherwise some prototype manipulation in // userland will fail @@ -118,7 +122,7 @@ Object.defineProperty(Duplex.prototype, 'writableCorked', { } }); -Object.defineProperty(Duplex.prototype, 'writableEnded', { +ObjectDefineProperty(Duplex.prototype, 'writableEnded', { // Making it explicit this property is not enumerable // because otherwise some prototype manipulation in // userland will fail @@ -143,7 +147,7 @@ function onEndNT(self) { self.end(); } -Object.defineProperty(Duplex.prototype, 'destroyed', { +ObjectDefineProperty(Duplex.prototype, 'destroyed', { // Making it explicit this property is not enumerable // because otherwise some prototype manipulation in // userland will fail diff --git a/lib/_stream_passthrough.js b/lib/_stream_passthrough.js index 205a14b65fe243..279df5ac2d68ca 100644 --- a/lib/_stream_passthrough.js +++ b/lib/_stream_passthrough.js @@ -25,13 +25,15 @@ 'use strict'; -const { Object } = primordials; +const { + ObjectSetPrototypeOf, +} = primordials; module.exports = PassThrough; const Transform = require('_stream_transform'); -Object.setPrototypeOf(PassThrough.prototype, Transform.prototype); -Object.setPrototypeOf(PassThrough, Transform); +ObjectSetPrototypeOf(PassThrough.prototype, Transform.prototype); +ObjectSetPrototypeOf(PassThrough, Transform); function PassThrough(options) { if (!(this instanceof PassThrough)) diff --git a/lib/_stream_readable.js b/lib/_stream_readable.js index 71fd74b07bea70..0d83ada205b835 100644 --- a/lib/_stream_readable.js +++ b/lib/_stream_readable.js @@ -21,7 +21,13 @@ 'use strict'; -const { Object } = primordials; +const { + ArrayIsArray, + NumberIsInteger, + NumberIsNaN, + ObjectDefineProperty, + ObjectSetPrototypeOf, +} = primordials; module.exports = Readable; Readable.ReadableState = ReadableState; @@ -49,8 +55,8 @@ let StringDecoder; let createReadableStreamAsyncIterator; let from; -Object.setPrototypeOf(Readable.prototype, Stream.prototype); -Object.setPrototypeOf(Readable, Stream); +ObjectSetPrototypeOf(Readable.prototype, Stream.prototype); +ObjectSetPrototypeOf(Readable, Stream); const { errorOrDestroy } = destroyImpl; const kProxyEvents = ['error', 'close', 'destroy', 'pause', 'resume']; @@ -67,7 +73,7 @@ function prependListener(emitter, event, fn) { // the prependListener() method. The goal is to eventually remove this hack. if (!emitter._events || !emitter._events[event]) emitter.on(event, fn); - else if (Array.isArray(emitter._events[event])) + else if (ArrayIsArray(emitter._events[event])) emitter._events[event].unshift(fn); else emitter._events[event] = [fn, emitter._events[event]]; @@ -157,7 +163,7 @@ function ReadableState(options, stream, isDuplex) { } // Legacy getter for `pipesCount` -Object.defineProperty(ReadableState.prototype, 'pipesCount', { +ObjectDefineProperty(ReadableState.prototype, 'pipesCount', { get() { return this.pipes.length; } @@ -187,7 +193,7 @@ function Readable(options) { Stream.call(this); } -Object.defineProperty(Readable.prototype, 'destroyed', { +ObjectDefineProperty(Readable.prototype, 'destroyed', { // Making it explicit this property is not enumerable // because otherwise some prototype manipulation in // userland will fail @@ -211,7 +217,7 @@ Object.defineProperty(Readable.prototype, 'destroyed', { } }); -Object.defineProperty(Readable.prototype, 'readableEnded', { +ObjectDefineProperty(Readable.prototype, 'readableEnded', { // Making it explicit this property is not enumerable // because otherwise some prototype manipulation in // userland will fail @@ -402,7 +408,7 @@ function howMuchToRead(n, state) { return 0; if (state.objectMode) return 1; - if (Number.isNaN(n)) { + if (NumberIsNaN(n)) { // Only flow one buffer at a time if (state.flowing && state.length) return state.buffer.first().length; @@ -421,7 +427,7 @@ Readable.prototype.read = function(n) { // in this scenario, so we are doing it manually. if (n === undefined) { n = NaN; - } else if (!Number.isInteger(n)) { + } else if (!NumberIsInteger(n)) { n = parseInt(n, 10); } const state = this._readableState; @@ -1089,7 +1095,7 @@ Readable.prototype[Symbol.asyncIterator] = function() { return createReadableStreamAsyncIterator(this); }; -Object.defineProperty(Readable.prototype, 'readableHighWaterMark', { +ObjectDefineProperty(Readable.prototype, 'readableHighWaterMark', { // Making it explicit this property is not enumerable // because otherwise some prototype manipulation in // userland will fail @@ -1099,7 +1105,7 @@ Object.defineProperty(Readable.prototype, 'readableHighWaterMark', { } }); -Object.defineProperty(Readable.prototype, 'readableBuffer', { +ObjectDefineProperty(Readable.prototype, 'readableBuffer', { // Making it explicit this property is not enumerable // because otherwise some prototype manipulation in // userland will fail @@ -1109,7 +1115,7 @@ Object.defineProperty(Readable.prototype, 'readableBuffer', { } }); -Object.defineProperty(Readable.prototype, 'readableFlowing', { +ObjectDefineProperty(Readable.prototype, 'readableFlowing', { // Making it explicit this property is not enumerable // because otherwise some prototype manipulation in // userland will fail @@ -1127,7 +1133,7 @@ Object.defineProperty(Readable.prototype, 'readableFlowing', { // Exposed for testing purposes only. Readable._fromList = fromList; -Object.defineProperty(Readable.prototype, 'readableLength', { +ObjectDefineProperty(Readable.prototype, 'readableLength', { // Making it explicit this property is not enumerable // because otherwise some prototype manipulation in // userland will fail @@ -1137,14 +1143,14 @@ Object.defineProperty(Readable.prototype, 'readableLength', { } }); -Object.defineProperty(Readable.prototype, 'readableObjectMode', { +ObjectDefineProperty(Readable.prototype, 'readableObjectMode', { enumerable: false, get() { return this._readableState ? this._readableState.objectMode : false; } }); -Object.defineProperty(Readable.prototype, 'readableEncoding', { +ObjectDefineProperty(Readable.prototype, 'readableEncoding', { enumerable: false, get() { return this._readableState ? this._readableState.encoding : null; diff --git a/lib/_stream_transform.js b/lib/_stream_transform.js index b4fffaa98891cd..cb4aae2e6d18f4 100644 --- a/lib/_stream_transform.js +++ b/lib/_stream_transform.js @@ -63,7 +63,9 @@ 'use strict'; -const { Object } = primordials; +const { + ObjectSetPrototypeOf, +} = primordials; module.exports = Transform; const { @@ -73,8 +75,8 @@ const { ERR_TRANSFORM_WITH_LENGTH_0 } = require('internal/errors').codes; const Duplex = require('_stream_duplex'); -Object.setPrototypeOf(Transform.prototype, Duplex.prototype); -Object.setPrototypeOf(Transform, Duplex); +ObjectSetPrototypeOf(Transform.prototype, Duplex.prototype); +ObjectSetPrototypeOf(Transform, Duplex); function afterTransform(er, data) { diff --git a/lib/_stream_writable.js b/lib/_stream_writable.js index 9b4036e4764418..9bea209e257d2a 100644 --- a/lib/_stream_writable.js +++ b/lib/_stream_writable.js @@ -25,7 +25,11 @@ 'use strict'; -const { Object } = primordials; +const { + Array, + ObjectDefineProperty, + ObjectSetPrototypeOf, +} = primordials; module.exports = Writable; Writable.WritableState = WritableState; @@ -52,8 +56,8 @@ const { const { errorOrDestroy } = destroyImpl; -Object.setPrototypeOf(Writable.prototype, Stream.prototype); -Object.setPrototypeOf(Writable, Stream); +ObjectSetPrototypeOf(Writable.prototype, Stream.prototype); +ObjectSetPrototypeOf(Writable, Stream); function nop() {} @@ -138,6 +142,10 @@ function WritableState(options, stream, isDuplex) { // The amount that is being written when _write is called. this.writelen = 0; + // Storage for data passed to the afterWrite() callback in case of + // synchronous _write() completion. + this.afterWriteTickInfo = null; + this.bufferedRequest = null; this.lastBufferedRequest = null; @@ -183,7 +191,7 @@ WritableState.prototype.getBuffer = function getBuffer() { return out; }; -Object.defineProperty(WritableState.prototype, 'buffer', { +ObjectDefineProperty(WritableState.prototype, 'buffer', { get: internalUtil.deprecate(function writableStateBufferGetter() { return this.getBuffer(); }, '_writableState.buffer is deprecated. Use _writableState.getBuffer ' + @@ -195,7 +203,7 @@ Object.defineProperty(WritableState.prototype, 'buffer', { var realHasInstance; if (typeof Symbol === 'function' && Symbol.hasInstance) { realHasInstance = Function.prototype[Symbol.hasInstance]; - Object.defineProperty(Writable, Symbol.hasInstance, { + ObjectDefineProperty(Writable, Symbol.hasInstance, { value: function(object) { if (realHasInstance.call(this, object)) return true; @@ -346,7 +354,7 @@ Writable.prototype.setDefaultEncoding = function setDefaultEncoding(encoding) { return this; }; -Object.defineProperty(Writable.prototype, 'writableBuffer', { +ObjectDefineProperty(Writable.prototype, 'writableBuffer', { // Making it explicit this property is not enumerable // because otherwise some prototype manipulation in // userland will fail @@ -365,7 +373,7 @@ function decodeChunk(state, chunk, encoding) { return chunk; } -Object.defineProperty(Writable.prototype, 'writableEnded', { +ObjectDefineProperty(Writable.prototype, 'writableEnded', { // Making it explicit this property is not enumerable // because otherwise some prototype manipulation in // userland will fail @@ -375,7 +383,7 @@ Object.defineProperty(Writable.prototype, 'writableEnded', { } }); -Object.defineProperty(Writable.prototype, 'writableHighWaterMark', { +ObjectDefineProperty(Writable.prototype, 'writableHighWaterMark', { // Making it explicit this property is not enumerable // because otherwise some prototype manipulation in // userland will fail @@ -385,7 +393,7 @@ Object.defineProperty(Writable.prototype, 'writableHighWaterMark', { } }); -Object.defineProperty(Writable.prototype, 'writableCorked', { +ObjectDefineProperty(Writable.prototype, 'writableCorked', { // Making it explicit this property is not enumerable // because otherwise some prototype manipulation in // userland will fail @@ -494,22 +502,41 @@ function onwrite(stream, er) { } if (sync) { - process.nextTick(afterWrite, stream, state, cb); + // It is a common case that the callback passed to .write() is always + // the same. In that case, we do not schedule a new nextTick(), but rather + // just increase a counter, to improve performance and avoid memory + // allocations. + if (state.afterWriteTickInfo !== null && + state.afterWriteTickInfo.cb === cb) { + state.afterWriteTickInfo.count++; + } else { + state.afterWriteTickInfo = { count: 1, cb, stream, state }; + process.nextTick(afterWriteTick, state.afterWriteTickInfo); + } } else { - afterWrite(stream, state, cb); + afterWrite(stream, state, 1, cb); } } } -function afterWrite(stream, state, cb) { +function afterWriteTick({ stream, state, count, cb }) { + state.afterWriteTickInfo = null; + return afterWrite(stream, state, count, cb); +} + +function afterWrite(stream, state, count, cb) { const needDrain = !state.ending && !stream.destroyed && state.length === 0 && state.needDrain; if (needDrain) { state.needDrain = false; stream.emit('drain'); } - state.pendingcb--; - cb(); + + while (count-- > 0) { + state.pendingcb--; + cb(); + } + finishMaybe(stream, state); } @@ -624,7 +651,7 @@ Writable.prototype.end = function(chunk, encoding, cb) { return this; }; -Object.defineProperty(Writable.prototype, 'writableLength', { +ObjectDefineProperty(Writable.prototype, 'writableLength', { // Making it explicit this property is not enumerable // because otherwise some prototype manipulation in // userland will fail @@ -642,6 +669,7 @@ function needFinish(state) { !state.finished && !state.writing); } + function callFinal(stream, state) { stream._final((err) => { state.pendingcb--; @@ -654,6 +682,7 @@ function callFinal(stream, state) { } }); } + function prefinish(stream, state) { if (!state.prefinished && !state.finalCalled) { if (typeof stream._final === 'function' && !state.destroyed) { @@ -715,7 +744,7 @@ function onCorkedFinish(corkReq, state, err) { state.corkedRequestsFree.next = corkReq; } -Object.defineProperty(Writable.prototype, 'destroyed', { +ObjectDefineProperty(Writable.prototype, 'destroyed', { // Making it explicit this property is not enumerable // because otherwise some prototype manipulation in // userland will fail @@ -739,14 +768,14 @@ Object.defineProperty(Writable.prototype, 'destroyed', { } }); -Object.defineProperty(Writable.prototype, 'writableObjectMode', { +ObjectDefineProperty(Writable.prototype, 'writableObjectMode', { enumerable: false, get() { return this._writableState ? this._writableState.objectMode : false; } }); -Object.defineProperty(Writable.prototype, 'writableFinished', { +ObjectDefineProperty(Writable.prototype, 'writableFinished', { // Making it explicit this property is not enumerable // because otherwise some prototype manipulation in // userland will fail diff --git a/lib/_tls_common.js b/lib/_tls_common.js index 981503e6e96658..e4180a2f4dc7a3 100644 --- a/lib/_tls_common.js +++ b/lib/_tls_common.js @@ -21,7 +21,10 @@ 'use strict'; -const { Object } = primordials; +const { + ArrayIsArray, + ObjectCreate, +} = primordials; const { parseCertString } = require('internal/tls'); const { isArrayBufferView } = require('internal/util/types'); @@ -103,7 +106,7 @@ exports.createSecureContext = function createSecureContext(options) { // Add CA before the cert to be able to load cert's issuer in C++ code. const { ca } = options; if (ca) { - if (Array.isArray(ca)) { + if (ArrayIsArray(ca)) { for (i = 0; i < ca.length; ++i) { val = ca[i]; validateKeyOrCertOption('ca', val); @@ -119,7 +122,7 @@ exports.createSecureContext = function createSecureContext(options) { const { cert } = options; if (cert) { - if (Array.isArray(cert)) { + if (ArrayIsArray(cert)) { for (i = 0; i < cert.length; ++i) { val = cert[i]; validateKeyOrCertOption('cert', val); @@ -138,7 +141,7 @@ exports.createSecureContext = function createSecureContext(options) { const key = options.key; const passphrase = options.passphrase; if (key) { - if (Array.isArray(key)) { + if (ArrayIsArray(key)) { for (i = 0; i < key.length; ++i) { val = key[i]; // eslint-disable-next-line eqeqeq @@ -238,7 +241,7 @@ exports.createSecureContext = function createSecureContext(options) { } if (options.crl) { - if (Array.isArray(options.crl)) { + if (ArrayIsArray(options.crl)) { for (i = 0; i < options.crl.length; i++) { c.context.addCRL(options.crl[i]); } @@ -255,7 +258,7 @@ exports.createSecureContext = function createSecureContext(options) { if (!toBuf) toBuf = require('internal/crypto/util').toBuf; - if (Array.isArray(options.pfx)) { + if (ArrayIsArray(options.pfx)) { for (i = 0; i < options.pfx.length; i++) { const pfx = options.pfx[i]; const raw = pfx.buf ? pfx.buf : pfx; @@ -314,7 +317,7 @@ exports.translatePeerCertificate = function translatePeerCertificate(c) { if (c.subject != null) c.subject = parseCertString(c.subject); if (c.infoAccess != null) { const info = c.infoAccess; - c.infoAccess = Object.create(null); + c.infoAccess = ObjectCreate(null); // XXX: More key validation? info.replace(/([^\n:]*):([^\n]*)(?:\n|$)/g, (all, key, val) => { diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js index 69fc05475896a1..530a41a1e855fd 100644 --- a/lib/_tls_wrap.js +++ b/lib/_tls_wrap.js @@ -21,7 +21,11 @@ 'use strict'; -const { Object } = primordials; +const { + ObjectAssign, + ObjectDefineProperty, + ObjectSetPrototypeOf, +} = primordials; const { assertCrypto, @@ -52,6 +56,7 @@ const { ERR_SOCKET_CLOSED, ERR_TLS_DH_PARAM_SIZE, ERR_TLS_HANDSHAKE_TIMEOUT, + ERR_TLS_INVALID_CONTEXT, ERR_TLS_RENEGOTIATION_DISABLED, ERR_TLS_REQUIRED_SERVER_NAME, ERR_TLS_SESSION_ATTACK, @@ -437,8 +442,8 @@ function TLSSocket(socket, opts) { // Read on next tick so the caller has a chance to setup listeners process.nextTick(initRead, this, socket); } -Object.setPrototypeOf(TLSSocket.prototype, net.Socket.prototype); -Object.setPrototypeOf(TLSSocket, net.Socket); +ObjectSetPrototypeOf(TLSSocket.prototype, net.Socket.prototype); +ObjectSetPrototypeOf(TLSSocket, net.Socket); exports.TLSSocket = TLSSocket; const proxiedMethods = [ @@ -513,8 +518,9 @@ TLSSocket.prototype._wrapHandle = function(wrap) { options.credentials || tls.createSecureContext(options); assert(handle.isStreamBase, 'handle must be a StreamBase'); - assert(context.context instanceof NativeSecureContext, - 'context.context must be a NativeSecureContext'); + if (!(context.context instanceof NativeSecureContext)) { + throw new ERR_TLS_INVALID_CONTEXT('context'); + } const res = tls_wrap.wrap(handle, context.context, !!options.isServer); res._parent = handle; // C++ "wrap" object: TCPWrap, JSStream, ... res._parentWrap = wrap; // JS object: net.Socket, JSStreamSocket, ... @@ -531,7 +537,7 @@ TLSSocket.prototype._wrapHandle = function(wrap) { // This eliminates a cyclic reference to TLSWrap // Ref: https://github.com/nodejs/node/commit/f7620fb96d339f704932f9bb9a0dceb9952df2d4 function defineHandleReading(socket, handle) { - Object.defineProperty(handle, 'reading', { + ObjectDefineProperty(handle, 'reading', { get: () => { return socket[kRes].reading; }, @@ -1077,8 +1083,8 @@ function Server(options, listener) { this[kEnableTrace] = options.enableTrace; } -Object.setPrototypeOf(Server.prototype, net.Server.prototype); -Object.setPrototypeOf(Server, net.Server); +ObjectSetPrototypeOf(Server.prototype, net.Server.prototype); +ObjectSetPrototypeOf(Server, net.Server); exports.Server = Server; exports.createServer = function createServer(options, listener) { return new Server(options, listener); @@ -1318,9 +1324,9 @@ function normalizeConnectArgs(listArgs) { // the host/port/path args that it knows about, not the tls options. // This means that options.host overrides a host arg. if (listArgs[1] !== null && typeof listArgs[1] === 'object') { - Object.assign(options, listArgs[1]); + ObjectAssign(options, listArgs[1]); } else if (listArgs[2] !== null && typeof listArgs[2] === 'object') { - Object.assign(options, listArgs[2]); + ObjectAssign(options, listArgs[2]); } return cb ? [options, cb] : [options]; diff --git a/lib/assert.js b/lib/assert.js index 4ce3c3bfdeff03..c3faba09053474 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -20,7 +20,12 @@ 'use strict'; -const { Object, ObjectPrototype } = primordials; +const { + ObjectAssign, + ObjectIs, + ObjectKeys, + ObjectPrototypeIsPrototypeOf, +} = primordials; const { Buffer } = require('buffer'); const { @@ -494,7 +499,7 @@ assert.strictEqual = function strictEqual(actual, expected, message) { if (arguments.length < 2) { throw new ERR_MISSING_ARGS('actual', 'expected'); } - if (!Object.is(actual, expected)) { + if (!ObjectIs(actual, expected)) { innerFail({ actual, expected, @@ -509,7 +514,7 @@ assert.notStrictEqual = function notStrictEqual(actual, expected, message) { if (arguments.length < 2) { throw new ERR_MISSING_ARGS('actual', 'expected'); } - if (Object.is(actual, expected)) { + if (ObjectIs(actual, expected)) { innerFail({ actual, expected, @@ -595,7 +600,7 @@ function expectedException(actual, expected, message, fn) { throw err; } else { // Handle validation objects. - const keys = Object.keys(expected); + const keys = ObjectKeys(expected); // Special handle errors to make sure the name and the message are // compared as well. if (expected instanceof Error) { @@ -619,7 +624,7 @@ function expectedException(actual, expected, message, fn) { // Check for matching Error classes. } else if (expected.prototype !== undefined && actual instanceof expected) { return; - } else if (ObjectPrototype.isPrototypeOf(Error, expected)) { + } else if (ObjectPrototypeIsPrototypeOf(Error, expected)) { if (!message) { generatedMessage = true; message = 'The error is expected to be an instance of ' + @@ -883,7 +888,7 @@ assert.ifError = function ifError(err) { function strict(...args) { innerOk(strict, args.length, ...args); } -assert.strict = Object.assign(strict, assert, { +assert.strict = ObjectAssign(strict, assert, { equal: assert.strictEqual, deepEqual: assert.deepStrictEqual, notEqual: assert.notStrictEqual, diff --git a/lib/async_hooks.js b/lib/async_hooks.js index c18a902c2ace84..a7ca5af7ef8105 100644 --- a/lib/async_hooks.js +++ b/lib/async_hooks.js @@ -1,6 +1,9 @@ 'use strict'; -const { Reflect } = primordials; +const { + NumberIsSafeInteger, + ReflectApply, +} = primordials; const { ERR_ASYNC_CALLBACK, @@ -145,7 +148,7 @@ class AsyncResource { // Unlike emitInitScript, AsyncResource doesn't supports null as the // triggerAsyncId. - if (!Number.isSafeInteger(triggerAsyncId) || triggerAsyncId < -1) { + if (!NumberIsSafeInteger(triggerAsyncId) || triggerAsyncId < -1) { throw new ERR_INVALID_ASYNC_ID('triggerAsyncId', triggerAsyncId); } @@ -171,7 +174,7 @@ class AsyncResource { try { if (thisArg === undefined) return fn(...args); - return Reflect.apply(fn, thisArg, args); + return ReflectApply(fn, thisArg, args); } finally { if (hasAsyncIdStack()) emitAfter(asyncId); diff --git a/lib/buffer.js b/lib/buffer.js index a751677a7344ea..8cbc0af538dbba 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -22,17 +22,18 @@ 'use strict'; const { - Object: { - defineProperties: ObjectDefineProperties, - defineProperty: ObjectDefineProperty, - setPrototypeOf: ObjectSetPrototypeOf, - create: ObjectCreate - }, - Math: { - floor: MathFloor, - trunc: MathTrunc, - min: MathMin - } + Array, + ArrayIsArray, + MathFloor, + MathMin, + MathTrunc, + NumberIsNaN, + NumberMAX_SAFE_INTEGER, + NumberMIN_SAFE_INTEGER, + ObjectCreate, + ObjectDefineProperties, + ObjectDefineProperty, + ObjectSetPrototypeOf, } = primordials; const { @@ -177,9 +178,9 @@ function showFlaggedDeprecation() { function toInteger(n, defaultVal) { n = +n; - if (!Number.isNaN(n) && - n >= Number.MIN_SAFE_INTEGER && - n <= Number.MAX_SAFE_INTEGER) { + if (!NumberIsNaN(n) && + n >= NumberMIN_SAFE_INTEGER && + n <= NumberMAX_SAFE_INTEGER) { return ((n % 1) === 0 ? n : MathFloor(n)); } return defaultVal; @@ -444,7 +445,7 @@ function fromArrayBuffer(obj, byteOffset, length) { byteOffset = 0; } else { byteOffset = +byteOffset; - if (Number.isNaN(byteOffset)) + if (NumberIsNaN(byteOffset)) byteOffset = 0; } @@ -487,7 +488,7 @@ function fromObject(obj) { return fromArrayLike(obj); } - if (obj.type === 'Buffer' && Array.isArray(obj.data)) { + if (obj.type === 'Buffer' && ArrayIsArray(obj.data)) { return fromArrayLike(obj.data); } } @@ -522,7 +523,7 @@ Buffer[kIsEncodingSymbol] = Buffer.isEncoding; Buffer.concat = function concat(list, length) { let i; - if (!Array.isArray(list)) { + if (!ArrayIsArray(list)) { throw new ERR_INVALID_ARG_TYPE('list', 'Array', list); } @@ -892,7 +893,7 @@ function bidirectionalIndexOf(buffer, val, byteOffset, encoding, dir) { // Coerce to Number. Values like null and [] become 0. byteOffset = +byteOffset; // If the offset is undefined, "foo", {}, coerces to NaN, search whole buffer. - if (Number.isNaN(byteOffset)) { + if (NumberIsNaN(byteOffset)) { byteOffset = dir ? 0 : buffer.length; } dir = !!dir; // Cast to bool. @@ -1065,7 +1066,7 @@ function adjustOffset(offset, length) { if (offset < length) { return offset; } - return Number.isNaN(offset) ? 0 : length; + return NumberIsNaN(offset) ? 0 : length; } Buffer.prototype.slice = function slice(start, end) { diff --git a/lib/child_process.js b/lib/child_process.js index 16dc30856a96f5..2a0d471c02a74d 100644 --- a/lib/child_process.js +++ b/lib/child_process.js @@ -21,7 +21,13 @@ 'use strict'; -const { Object, ObjectPrototype } = primordials; +const { + ArrayIsArray, + NumberIsInteger, + ObjectAssign, + ObjectDefineProperty, + ObjectPrototypeHasOwnProperty, +} = primordials; const { promisify, @@ -55,11 +61,11 @@ function fork(modulePath /* , args, options */) { validateString(modulePath, 'modulePath'); // Get options and args arguments. - var execArgv; - var options = {}; - var args = []; - var pos = 1; - if (pos < arguments.length && Array.isArray(arguments[pos])) { + let execArgv; + let options = {}; + let args = []; + let pos = 1; + if (pos < arguments.length && ArrayIsArray(arguments[pos])) { args = arguments[pos++]; } @@ -92,7 +98,7 @@ function fork(modulePath /* , args, options */) { if (typeof options.stdio === 'string') { options.stdio = stdioStringToArray(options.stdio, 'ipc'); - } else if (!Array.isArray(options.stdio)) { + } else if (!ArrayIsArray(options.stdio)) { // Use a separate fd=3 for the IPC channel. Inherit stdin, stdout, // and stderr from the parent if silent isn't set. options.stdio = stdioStringToArray( @@ -170,7 +176,7 @@ const customPromiseExecFunction = (orig) => { }; }; -Object.defineProperty(exec, promisify.custom, { +ObjectDefineProperty(exec, promisify.custom, { enumerable: false, value: customPromiseExecFunction(exec) }); @@ -182,7 +188,7 @@ function execFile(file /* , args, options, callback */) { // Parse the optional positional parameters. let pos = 1; - if (pos < arguments.length && Array.isArray(arguments[pos])) { + if (pos < arguments.length && ArrayIsArray(arguments[pos])) { args = arguments[pos++]; } else if (pos < arguments.length && arguments[pos] == null) { pos++; @@ -231,7 +237,7 @@ function execFile(file /* , args, options, callback */) { windowsVerbatimArguments: !!options.windowsVerbatimArguments }); - var encoding; + let encoding; const _stdout = []; const _stderr = []; if (options.encoding !== 'buffer' && Buffer.isEncoding(options.encoding)) { @@ -239,15 +245,15 @@ function execFile(file /* , args, options, callback */) { } else { encoding = null; } - var stdoutLen = 0; - var stderrLen = 0; - var killed = false; - var exited = false; - var timeoutId; + let stdoutLen = 0; + let stderrLen = 0; + let killed = false; + let exited = false; + let timeoutId; - var ex = null; + let ex = null; - var cmd = file; + let cmd = file; function exithandler(code, signal) { if (exited) return; @@ -261,8 +267,8 @@ function execFile(file /* , args, options, callback */) { if (!callback) return; // merge chunks - var stdout; - var stderr; + let stdout; + let stderr; if (encoding || ( child.stdout && @@ -389,7 +395,7 @@ function execFile(file /* , args, options, callback */) { return child; } -Object.defineProperty(execFile, promisify.custom, { +ObjectDefineProperty(execFile, promisify.custom, { enumerable: false, value: customPromiseExecFunction(execFile) }); @@ -400,7 +406,7 @@ function normalizeSpawnArguments(file, args, options) { if (file.length === 0) throw new ERR_INVALID_ARG_VALUE('file', file, 'cannot be empty'); - if (Array.isArray(args)) { + if (ArrayIsArray(args)) { args = args.slice(0); } else if (args == null) { args = []; @@ -507,7 +513,7 @@ function normalizeSpawnArguments(file, args, options) { // process.env.NODE_V8_COVERAGE always propagates, making it possible to // collect coverage for programs that spawn with white-listed environment. if (process.env.NODE_V8_COVERAGE && - !ObjectPrototype.hasOwnProperty(options.env || {}, 'NODE_V8_COVERAGE')) { + !ObjectPrototypeHasOwnProperty(options.env || {}, 'NODE_V8_COVERAGE')) { env.NODE_V8_COVERAGE = process.env.NODE_V8_COVERAGE; } @@ -562,15 +568,15 @@ function spawnSync(file, args, options) { options.stdio = getValidStdio(options.stdio || 'pipe', true).stdio; if (options.input) { - var stdin = options.stdio[0] = { ...options.stdio[0] }; + const stdin = options.stdio[0] = { ...options.stdio[0] }; stdin.input = options.input; } // We may want to pass data in on any given fd, ensure it is a valid buffer - for (var i = 0; i < options.stdio.length; i++) { - var input = options.stdio[i] && options.stdio[i].input; + for (let i = 0; i < options.stdio.length; i++) { + const input = options.stdio[i] && options.stdio[i].input; if (input != null) { - var pipe = options.stdio[i] = { ...options.stdio[i] }; + const pipe = options.stdio[i] = { ...options.stdio[i] }; if (isArrayBufferView(input)) { pipe.input = input; } else if (typeof input === 'string') { @@ -591,11 +597,11 @@ function spawnSync(file, args, options) { function checkExecSyncError(ret, args, cmd) { - var err; + let err; if (ret.error) { err = ret.error; } else if (ret.status !== 0) { - var msg = 'Command failed: '; + let msg = 'Command failed: '; msg += cmd || args.join(' '); if (ret.stderr && ret.stderr.length > 0) msg += `\n${ret.stderr.toString()}`; @@ -603,7 +609,7 @@ function checkExecSyncError(ret, args, cmd) { err = new Error(msg); } if (err) { - Object.assign(err, ret); + ObjectAssign(err, ret); } return err; } @@ -646,7 +652,7 @@ function execSync(command, options) { function validateTimeout(timeout) { - if (timeout != null && !(Number.isInteger(timeout) && timeout >= 0)) { + if (timeout != null && !(NumberIsInteger(timeout) && timeout >= 0)) { throw new ERR_OUT_OF_RANGE('timeout', 'an unsigned integer', timeout); } } diff --git a/lib/constants.js b/lib/constants.js index 77341c064640f1..cd510f7a37b4ed 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -21,18 +21,21 @@ 'use strict'; -const { Object } = primordials; +const { + ObjectAssign, + ObjectFreeze, +} = primordials; // This module is deprecated in documentation only. Users should be directed // towards using the specific constants exposed by the individual modules on // which they are most relevant. // Deprecation Code: DEP0008 const constants = internalBinding('constants'); -Object.assign(exports, - constants.os.dlopen, - constants.os.errno, - constants.os.priority, - constants.os.signals, - constants.fs, - constants.crypto); -Object.freeze(exports); +ObjectAssign(exports, + constants.os.dlopen, + constants.os.errno, + constants.os.priority, + constants.os.signals, + constants.fs, + constants.crypto); +ObjectFreeze(exports); diff --git a/lib/crypto.js b/lib/crypto.js index 6bfa8319c1b2b5..e2d1875d8d0c8b 100644 --- a/lib/crypto.js +++ b/lib/crypto.js @@ -24,7 +24,9 @@ 'use strict'; -const { Object } = primordials; +const { + ObjectDefineProperties, +} = primordials; const { assertCrypto, @@ -220,7 +222,7 @@ function getFipsForced() { return 1; } -Object.defineProperties(module.exports, { +ObjectDefineProperties(module.exports, { createCipher: { enumerable: false, value: deprecate(createCipher, diff --git a/lib/dgram.js b/lib/dgram.js index 94f44d520220ff..5f7e8f032cf998 100644 --- a/lib/dgram.js +++ b/lib/dgram.js @@ -21,7 +21,12 @@ 'use strict'; -const { Object } = primordials; +const { + Array, + ArrayIsArray, + ObjectDefineProperty, + ObjectSetPrototypeOf, +} = primordials; const errors = require('internal/errors'); const { @@ -121,8 +126,8 @@ function Socket(type, listener) { sendBufferSize }; } -Object.setPrototypeOf(Socket.prototype, EventEmitter.prototype); -Object.setPrototypeOf(Socket, EventEmitter); +ObjectSetPrototypeOf(Socket.prototype, EventEmitter.prototype); +ObjectSetPrototypeOf(Socket, EventEmitter); function createSocket(type, listener) { @@ -589,7 +594,7 @@ Socket.prototype.send = function(buffer, throw new ERR_SOCKET_DGRAM_IS_CONNECTED(); } - if (!Array.isArray(buffer)) { + if (!ArrayIsArray(buffer)) { if (typeof buffer === 'string') { list = [ Buffer.from(buffer) ]; } else if (!isUint8Array(buffer)) { @@ -965,7 +970,7 @@ Socket.prototype.getSendBufferSize = function() { // Deprecated private APIs. -Object.defineProperty(Socket.prototype, '_handle', { +ObjectDefineProperty(Socket.prototype, '_handle', { get: deprecate(function() { return this[kStateSymbol].handle; }, 'Socket.prototype._handle is deprecated', 'DEP0112'), @@ -975,7 +980,7 @@ Object.defineProperty(Socket.prototype, '_handle', { }); -Object.defineProperty(Socket.prototype, '_receiving', { +ObjectDefineProperty(Socket.prototype, '_receiving', { get: deprecate(function() { return this[kStateSymbol].receiving; }, 'Socket.prototype._receiving is deprecated', 'DEP0112'), @@ -985,7 +990,7 @@ Object.defineProperty(Socket.prototype, '_receiving', { }); -Object.defineProperty(Socket.prototype, '_bindState', { +ObjectDefineProperty(Socket.prototype, '_bindState', { get: deprecate(function() { return this[kStateSymbol].bindState; }, 'Socket.prototype._bindState is deprecated', 'DEP0112'), @@ -995,7 +1000,7 @@ Object.defineProperty(Socket.prototype, '_bindState', { }); -Object.defineProperty(Socket.prototype, '_queue', { +ObjectDefineProperty(Socket.prototype, '_queue', { get: deprecate(function() { return this[kStateSymbol].queue; }, 'Socket.prototype._queue is deprecated', 'DEP0112'), @@ -1005,7 +1010,7 @@ Object.defineProperty(Socket.prototype, '_queue', { }); -Object.defineProperty(Socket.prototype, '_reuseAddr', { +ObjectDefineProperty(Socket.prototype, '_reuseAddr', { get: deprecate(function() { return this[kStateSymbol].reuseAddr; }, 'Socket.prototype._reuseAddr is deprecated', 'DEP0112'), @@ -1027,7 +1032,7 @@ Socket.prototype._stopReceiving = deprecate(function() { // Legacy alias on the C++ wrapper object. This is not public API, so we may // want to runtime-deprecate it at some point. There's no hurry, though. -Object.defineProperty(UDP.prototype, 'owner', { +ObjectDefineProperty(UDP.prototype, 'owner', { get() { return this[owner_symbol]; }, set(v) { return this[owner_symbol] = v; } }); diff --git a/lib/dns.js b/lib/dns.js index 2a4dd15c5edeac..8a6c7456babbd0 100644 --- a/lib/dns.js +++ b/lib/dns.js @@ -21,7 +21,11 @@ 'use strict'; -const { Object } = primordials; +const { + ObjectCreate, + ObjectDefineProperties, + ObjectDefineProperty, +} = primordials; const cares = internalBinding('cares_wrap'); const { toASCII } = require('internal/idna'); @@ -152,8 +156,8 @@ function lookup(hostname, options, callback) { return req; } -Object.defineProperty(lookup, customPromisifyArgs, - { value: ['address', 'family'], enumerable: false }); +ObjectDefineProperty(lookup, customPromisifyArgs, + { value: ['address', 'family'], enumerable: false }); function onlookupservice(err, hostname, service) { @@ -190,8 +194,8 @@ function lookupService(address, port, callback) { return req; } -Object.defineProperty(lookupService, customPromisifyArgs, - { value: ['hostname', 'service'], enumerable: false }); +ObjectDefineProperty(lookupService, customPromisifyArgs, + { value: ['hostname', 'service'], enumerable: false }); function onresolve(err, result, ttls) { @@ -227,11 +231,11 @@ function resolver(bindingName) { if (err) throw dnsException(err, bindingName, name); return req; } - Object.defineProperty(query, 'name', { value: bindingName }); + ObjectDefineProperty(query, 'name', { value: bindingName }); return query; } -const resolveMap = Object.create(null); +const resolveMap = ObjectCreate(null); Resolver.prototype.resolveAny = resolveMap.ANY = resolver('queryAny'); Resolver.prototype.resolve4 = resolveMap.A = resolver('queryA'); Resolver.prototype.resolve6 = resolveMap.AAAA = resolver('queryAaaa'); @@ -316,7 +320,7 @@ module.exports = { bindDefaultResolver(module.exports, getDefaultResolver()); -Object.defineProperties(module.exports, { +ObjectDefineProperties(module.exports, { promises: { configurable: true, enumerable: true, diff --git a/lib/domain.js b/lib/domain.js index c3e38ba832c879..322e7eb9586c35 100644 --- a/lib/domain.js +++ b/lib/domain.js @@ -26,7 +26,11 @@ // No new pull requests targeting this module will be accepted // unless they address existing, critical bugs. -const { Object, Reflect } = primordials; +const { + Array, + ObjectDefineProperty, + ReflectApply, +} = primordials; const EventEmitter = require('events'); const { @@ -43,7 +47,7 @@ const { WeakReference } = internalBinding('util'); // Overwrite process.domain with a getter/setter that will allow for more // effective optimizations const _domain = [null]; -Object.defineProperty(process, 'domain', { +ObjectDefineProperty(process, 'domain', { enumerable: true, get: function() { return _domain[0]; @@ -59,7 +63,7 @@ const asyncHook = createHook({ if (process.domain !== null && process.domain !== undefined) { // If this operation is created while in a domain, let's mark it pairing.set(asyncId, process.domain[kWeak]); - Object.defineProperty(resource, 'domain', { + ObjectDefineProperty(resource, 'domain', { configurable: true, enumerable: false, value: process.domain, @@ -127,7 +131,7 @@ function topLevelDomainCallback(cb, ...args) { if (domain) domain.enter(); - const ret = Reflect.apply(cb, this, args); + const ret = ReflectApply(cb, this, args); if (domain) domain.exit(); @@ -207,7 +211,7 @@ Domain.prototype._errorHandler = function(er) { let caught = false; if ((typeof er === 'object' && er !== null) || typeof er === 'function') { - Object.defineProperty(er, 'domain', { + ObjectDefineProperty(er, 'domain', { configurable: true, enumerable: false, value: this, @@ -332,7 +336,7 @@ Domain.prototype.add = function(ee) { } } - Object.defineProperty(ee, 'domain', { + ObjectDefineProperty(ee, 'domain', { configurable: true, enumerable: false, value: this, @@ -376,7 +380,7 @@ function intercepted(_this, self, cb, fnargs) { const er = fnargs[0]; er.domainBound = cb; er.domainThrown = false; - Object.defineProperty(er, 'domain', { + ObjectDefineProperty(er, 'domain', { configurable: true, enumerable: false, value: self, @@ -435,7 +439,7 @@ Domain.prototype.bind = function(cb) { return bound(this, self, cb, arguments); } - Object.defineProperty(runBound, 'domain', { + ObjectDefineProperty(runBound, 'domain', { configurable: true, enumerable: false, value: this, @@ -450,7 +454,7 @@ EventEmitter.usingDomains = true; const eventInit = EventEmitter.init; EventEmitter.init = function() { - Object.defineProperty(this, 'domain', { + ObjectDefineProperty(this, 'domain', { configurable: true, enumerable: false, value: null, @@ -475,7 +479,7 @@ EventEmitter.prototype.emit = function(...args) { // handler, there's no active domain or this is process if (shouldEmitError || domain === null || domain === undefined || this === process) { - return Reflect.apply(eventEmit, this, args); + return ReflectApply(eventEmit, this, args); } if (type === 'error') { @@ -484,7 +488,7 @@ EventEmitter.prototype.emit = function(...args) { if (typeof er === 'object') { er.domainEmitter = this; - Object.defineProperty(er, 'domain', { + ObjectDefineProperty(er, 'domain', { configurable: true, enumerable: false, value: domain, @@ -537,7 +541,7 @@ EventEmitter.prototype.emit = function(...args) { } domain.enter(); - const ret = Reflect.apply(eventEmit, this, args); + const ret = ReflectApply(eventEmit, this, args); domain.exit(); return ret; diff --git a/lib/events.js b/lib/events.js index 1356806f6544c3..a09333af443534 100644 --- a/lib/events.js +++ b/lib/events.js @@ -21,10 +21,19 @@ 'use strict'; -const { Math, Object, Reflect } = primordials; -const apply = Reflect.apply; - -var spliceOne; +const { + Array, + MathMin, + NumberIsNaN, + ObjectCreate, + ObjectDefineProperty, + ObjectGetPrototypeOf, + ObjectKeys, + ReflectApply, + ReflectOwnKeys, +} = primordials; + +let spliceOne; const { kEnhanceStackBeforeInspector, @@ -57,7 +66,7 @@ EventEmitter.prototype._maxListeners = undefined; // By default EventEmitters will print a warning if more than 10 listeners are // added to it. This is a useful default which helps finding memory leaks. -var defaultMaxListeners = 10; +let defaultMaxListeners = 10; function checkListener(listener) { if (typeof listener !== 'function') { @@ -65,13 +74,13 @@ function checkListener(listener) { } } -Object.defineProperty(EventEmitter, 'defaultMaxListeners', { +ObjectDefineProperty(EventEmitter, 'defaultMaxListeners', { enumerable: true, get: function() { return defaultMaxListeners; }, set: function(arg) { - if (typeof arg !== 'number' || arg < 0 || Number.isNaN(arg)) { + if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) { throw new ERR_OUT_OF_RANGE('defaultMaxListeners', 'a non-negative number', arg); @@ -83,8 +92,8 @@ Object.defineProperty(EventEmitter, 'defaultMaxListeners', { EventEmitter.init = function() { if (this._events === undefined || - this._events === Object.getPrototypeOf(this)._events) { - this._events = Object.create(null); + this._events === ObjectGetPrototypeOf(this)._events) { + this._events = ObjectCreate(null); this._eventsCount = 0; } @@ -94,7 +103,7 @@ EventEmitter.init = function() { // Obviously not all Emitters should be limited to 10. This function allows // that to be increased. Set to zero for unlimited. EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) { - if (typeof n !== 'number' || n < 0 || Number.isNaN(n)) { + if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) { throw new ERR_OUT_OF_RANGE('n', 'a non-negative number', n); } this._maxListeners = n; @@ -114,14 +123,14 @@ EventEmitter.prototype.getMaxListeners = function getMaxListeners() { // Returns the length and line number of the first sequence of `a` that fully // appears in `b` with a length of at least 4. function identicalSequenceRange(a, b) { - for (var i = 0; i < a.length - 3; i++) { + for (let i = 0; i < a.length - 3; i++) { // Find the first entry of b that matches the current entry of a. const pos = b.indexOf(a[i]); if (pos !== -1) { const rest = b.length - pos; if (rest > 3) { let len = 1; - const maxLen = Math.min(a.length - i, rest); + const maxLen = MathMin(a.length - i, rest); // Count the number of consecutive entries. while (maxLen > len && a[i + len] === b[pos + len]) { len++; @@ -176,7 +185,7 @@ EventEmitter.prototype.emit = function emit(type, ...args) { const capture = {}; // eslint-disable-next-line no-restricted-syntax Error.captureStackTrace(capture, EventEmitter.prototype.emit); - Object.defineProperty(er, kEnhanceStackBeforeInspector, { + ObjectDefineProperty(er, kEnhanceStackBeforeInspector, { value: enhanceStackTrace.bind(this, er, capture), configurable: true }); @@ -207,27 +216,27 @@ EventEmitter.prototype.emit = function emit(type, ...args) { return false; if (typeof handler === 'function') { - apply(handler, this, args); + ReflectApply(handler, this, args); } else { const len = handler.length; const listeners = arrayClone(handler, len); - for (var i = 0; i < len; ++i) - apply(listeners[i], this, args); + for (let i = 0; i < len; ++i) + ReflectApply(listeners[i], this, args); } return true; }; function _addListener(target, type, listener, prepend) { - var m; - var events; - var existing; + let m; + let events; + let existing; checkListener(listener); events = target._events; if (events === undefined) { - events = target._events = Object.create(null); + events = target._events = ObjectCreate(null); target._eventsCount = 0; } else { // To avoid recursion in the case that type === "newListener"! Before @@ -341,7 +350,7 @@ EventEmitter.prototype.removeListener = if (list === listener || list.listener === listener) { if (--this._eventsCount === 0) - this._events = Object.create(null); + this._events = ObjectCreate(null); else { delete events[type]; if (events.removeListener) @@ -350,7 +359,7 @@ EventEmitter.prototype.removeListener = } else if (typeof list !== 'function') { let position = -1; - for (var i = list.length - 1; i >= 0; i--) { + for (let i = list.length - 1; i >= 0; i--) { if (list[i] === listener || list[i].listener === listener) { originalListener = list[i].listener; position = i; @@ -390,11 +399,11 @@ EventEmitter.prototype.removeAllListeners = // Not listening for removeListener, no need to emit if (events.removeListener === undefined) { if (arguments.length === 0) { - this._events = Object.create(null); + this._events = ObjectCreate(null); this._eventsCount = 0; } else if (events[type] !== undefined) { if (--this._eventsCount === 0) - this._events = Object.create(null); + this._events = ObjectCreate(null); else delete events[type]; } @@ -403,12 +412,12 @@ EventEmitter.prototype.removeAllListeners = // Emit removeListener for all listeners on all events if (arguments.length === 0) { - for (const key of Object.keys(events)) { + for (const key of ObjectKeys(events)) { if (key === 'removeListener') continue; this.removeAllListeners(key); } this.removeAllListeners('removeListener'); - this._events = Object.create(null); + this._events = ObjectCreate(null); this._eventsCount = 0; return this; } @@ -419,7 +428,7 @@ EventEmitter.prototype.removeAllListeners = this.removeListener(type, listeners); } else if (listeners !== undefined) { // LIFO order - for (var i = listeners.length - 1; i >= 0; i--) { + for (let i = listeners.length - 1; i >= 0; i--) { this.removeListener(type, listeners[i]); } } @@ -478,19 +487,19 @@ function listenerCount(type) { } EventEmitter.prototype.eventNames = function eventNames() { - return this._eventsCount > 0 ? Reflect.ownKeys(this._events) : []; + return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : []; }; function arrayClone(arr, n) { const copy = new Array(n); - for (var i = 0; i < n; ++i) + for (let i = 0; i < n; ++i) copy[i] = arr[i]; return copy; } function unwrapListeners(arr) { const ret = new Array(arr.length); - for (var i = 0; i < ret.length; ++i) { + for (let i = 0; i < ret.length; ++i) { ret[i] = arr[i].listener || arr[i]; } return ret; diff --git a/lib/fs.js b/lib/fs.js index 2944a44a317f76..6a5ebb8203af15 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -24,7 +24,13 @@ 'use strict'; -const { Math, Object } = primordials; +const { + MathMax, + NumberIsSafeInteger, + ObjectCreate, + ObjectDefineProperties, + ObjectDefineProperty, +} = primordials; const { fs: constants } = internalBinding('constants'); const { @@ -209,7 +215,7 @@ function exists(path, callback) { } } -Object.defineProperty(exists, internalUtil.promisify.custom, { +ObjectDefineProperty(exists, internalUtil.promisify.custom, { value: (path) => { return new Promise((resolve) => fs.exists(path, resolve)); } @@ -467,7 +473,7 @@ function read(fd, buffer, offset, length, position, callback) { validateOffsetLengthRead(offset, length, buffer.byteLength); - if (!Number.isSafeInteger(position)) + if (!NumberIsSafeInteger(position)) position = -1; function wrapper(err, bytesRead) { @@ -481,8 +487,8 @@ function read(fd, buffer, offset, length, position, callback) { binding.read(fd, buffer, offset, length, position, req); } -Object.defineProperty(read, internalUtil.customPromisifyArgs, - { value: ['bytesRead', 'buffer'], enumerable: false }); +ObjectDefineProperty(read, internalUtil.customPromisifyArgs, + { value: ['bytesRead', 'buffer'], enumerable: false }); function readSync(fd, buffer, offset, length, position) { validateInt32(fd, 'fd', 0); @@ -507,7 +513,7 @@ function readSync(fd, buffer, offset, length, position) { validateOffsetLengthRead(offset, length, buffer.byteLength); - if (!Number.isSafeInteger(position)) + if (!NumberIsSafeInteger(position)) position = -1; const ctx = {}; @@ -562,8 +568,8 @@ function write(fd, buffer, offset, length, position, callback) { return binding.writeString(fd, buffer, offset, length, req); } -Object.defineProperty(write, internalUtil.customPromisifyArgs, - { value: ['bytesWritten', 'buffer'], enumerable: false }); +ObjectDefineProperty(write, internalUtil.customPromisifyArgs, + { value: ['bytesWritten', 'buffer'], enumerable: false }); // Usage: // fs.writeSync(fd, buffer[, offset[, length[, position]]]); @@ -619,7 +625,7 @@ function writev(fd, buffers, position, callback) { return binding.writeBuffers(fd, buffers, position, req); } -Object.defineProperty(writev, internalUtil.customPromisifyArgs, { +ObjectDefineProperty(writev, internalUtil.customPromisifyArgs, { value: ['bytesWritten', 'buffer'], enumerable: false }); @@ -713,7 +719,7 @@ function ftruncate(fd, len = 0, callback) { } validateInt32(fd, 'fd', 0); validateInteger(len, 'len'); - len = Math.max(0, len); + len = MathMax(0, len); const req = new FSReqCallback(); req.oncomplete = makeCallback(callback); binding.ftruncate(fd, len, req); @@ -722,7 +728,7 @@ function ftruncate(fd, len = 0, callback) { function ftruncateSync(fd, len = 0) { validateInt32(fd, 'fd', 0); validateInteger(len, 'len'); - len = Math.max(0, len); + len = MathMax(0, len); const ctx = {}; binding.ftruncate(fd, len, undefined, ctx); handleErrorFromBinding(ctx); @@ -1460,7 +1466,7 @@ if (isWindows) { nextPart = function nextPart(p, i) { return p.indexOf('/', i); }; } -const emptyObj = Object.create(null); +const emptyObj = ObjectCreate(null); function realpathSync(p, options) { if (!options) options = emptyObj; @@ -1479,8 +1485,8 @@ function realpathSync(p, options) { return maybeCachedResult; } - const seenLinks = Object.create(null); - const knownHard = Object.create(null); + const seenLinks = ObjectCreate(null); + const knownHard = ObjectCreate(null); const original = p; // Current character position in p @@ -1617,8 +1623,8 @@ function realpath(p, options, callback) { validatePath(p); p = pathModule.resolve(p); - const seenLinks = Object.create(null); - const knownHard = Object.create(null); + const seenLinks = ObjectCreate(null); + const knownHard = ObjectCreate(null); // Current character position in p let pos; @@ -1949,7 +1955,7 @@ module.exports = fs = { _toUnixTimestamp: toUnixTimestamp }; -Object.defineProperties(fs, { +ObjectDefineProperties(fs, { F_OK: { enumerable: true, value: F_OK || 0 }, R_OK: { enumerable: true, value: R_OK || 0 }, W_OK: { enumerable: true, value: W_OK || 0 }, diff --git a/lib/http.js b/lib/http.js index 8582557446397e..55c655763d2647 100644 --- a/lib/http.js +++ b/lib/http.js @@ -21,7 +21,9 @@ 'use strict'; -const { Object } = primordials; +const { + ObjectDefineProperty, +} = primordials; const httpAgent = require('_http_agent'); const { ClientRequest } = require('_http_client'); @@ -65,7 +67,7 @@ module.exports = { request }; -Object.defineProperty(module.exports, 'maxHeaderSize', { +ObjectDefineProperty(module.exports, 'maxHeaderSize', { configurable: true, enumerable: true, get() { @@ -78,7 +80,7 @@ Object.defineProperty(module.exports, 'maxHeaderSize', { } }); -Object.defineProperty(module.exports, 'globalAgent', { +ObjectDefineProperty(module.exports, 'globalAgent', { configurable: true, enumerable: true, get() { diff --git a/lib/https.js b/lib/https.js index 6d799d0c925f2b..d8383a03052b1b 100644 --- a/lib/https.js +++ b/lib/https.js @@ -21,7 +21,10 @@ 'use strict'; -const { Object } = primordials; +const { + ObjectAssign, + ObjectSetPrototypeOf, +} = primordials; require('internal/util').assertCrypto(); @@ -76,8 +79,8 @@ function Server(opts, requestListener) { this.maxHeadersCount = null; this.headersTimeout = 40 * 1000; // 40 seconds } -Object.setPrototypeOf(Server.prototype, tls.Server.prototype); -Object.setPrototypeOf(Server, tls.Server); +ObjectSetPrototypeOf(Server.prototype, tls.Server.prototype); +ObjectSetPrototypeOf(Server, tls.Server); Server.prototype.setTimeout = HttpServer.prototype.setTimeout; @@ -153,8 +156,8 @@ function Agent(options) { list: [] }; } -Object.setPrototypeOf(Agent.prototype, HttpAgent.prototype); -Object.setPrototypeOf(Agent, HttpAgent); +ObjectSetPrototypeOf(Agent.prototype, HttpAgent.prototype); +ObjectSetPrototypeOf(Agent, HttpAgent); Agent.prototype.createConnection = createConnection; Agent.prototype.getName = function getName(options) { @@ -296,7 +299,7 @@ function request(...args) { } if (args[0] && typeof args[0] !== 'function') { - Object.assign(options, args.shift()); + ObjectAssign(options, args.shift()); } options._defaultAgent = module.exports.globalAgent; diff --git a/lib/inspector.js b/lib/inspector.js index 2f29506b91bc0e..107500938377ce 100644 --- a/lib/inspector.js +++ b/lib/inspector.js @@ -1,6 +1,9 @@ 'use strict'; -const { JSON } = primordials; +const { + JSONParse, + JSONStringify, +} = primordials; const { ERR_INSPECTOR_ALREADY_CONNECTED, @@ -61,7 +64,7 @@ class Session extends EventEmitter { } [onMessageSymbol](message) { - const parsed = JSON.parse(message); + const parsed = JSONParse(message); try { if (parsed.id) { const callback = this[messageCallbacksSymbol].get(parsed.id); @@ -107,7 +110,7 @@ class Session extends EventEmitter { if (callback) { this[messageCallbacksSymbol].set(id, callback); } - this[connectionSymbol].dispatch(JSON.stringify(message)); + this[connectionSymbol].dispatch(JSONStringify(message)); } disconnect() { diff --git a/lib/internal/assert.js b/lib/internal/assert.js index b62b2753c0b907..0f52faab4bcf53 100644 --- a/lib/internal/assert.js +++ b/lib/internal/assert.js @@ -7,6 +7,7 @@ function lazyError() { } return error; } + function assert(value, message) { if (!value) { const ERR_INTERNAL_ASSERTION = lazyError(); diff --git a/lib/internal/assert/assertion_error.js b/lib/internal/assert/assertion_error.js index 0b57bf2cb200c5..69a6d8afac52c4 100644 --- a/lib/internal/assert/assertion_error.js +++ b/lib/internal/assert/assertion_error.js @@ -1,6 +1,12 @@ 'use strict'; -const { Math, Object } = primordials; +const { + MathMax, + ObjectCreate, + ObjectDefineProperty, + ObjectGetPrototypeOf, + ObjectKeys, +} = primordials; const { inspect } = require('internal/util/inspect'); const { codes: { @@ -31,12 +37,12 @@ const kReadableOperator = { const kMaxShortLength = 12; function copyError(source) { - const keys = Object.keys(source); - const target = Object.create(Object.getPrototypeOf(source)); + const keys = ObjectKeys(source); + const target = ObjectCreate(ObjectGetPrototypeOf(source)); for (const key of keys) { target[key] = source[key]; } - Object.defineProperty(target, 'message', { value: source.message }); + ObjectDefineProperty(target, 'message', { value: source.message }); return target; } @@ -135,7 +141,7 @@ function createErrDiff(actual, expected, operator) { b = expectedLines[expectedLines.length - 1]; } - const maxLines = Math.max(actualLines.length, expectedLines.length); + const maxLines = MathMax(actualLines.length, expectedLines.length); // Strict equal with identical objects that are not identical by reference. // E.g., assert.deepStrictEqual({ a: Symbol() }, { a: Symbol() }) if (maxLines === 0) { @@ -404,7 +410,7 @@ class AssertionError extends Error { Error.stackTraceLimit = limit; this.generatedMessage = !message; - Object.defineProperty(this, 'name', { + ObjectDefineProperty(this, 'name', { value: 'AssertionError [ERR_ASSERTION]', enumerable: false, writable: true, diff --git a/lib/internal/async_hooks.js b/lib/internal/async_hooks.js index bf803885cacfa3..b17406c78241cd 100644 --- a/lib/internal/async_hooks.js +++ b/lib/internal/async_hooks.js @@ -1,6 +1,10 @@ 'use strict'; -const { FunctionPrototype, Object } = primordials; +const { + FunctionPrototypeBind, + NumberIsSafeInteger, + ObjectDefineProperty, +} = primordials; const { ERR_ASYNC_TYPE, @@ -115,7 +119,7 @@ function validateAsyncId(asyncId, type) { // Skip validation when async_hooks is disabled if (async_hook_fields[kCheck] <= 0) return; - if (!Number.isSafeInteger(asyncId) || asyncId < -1) { + if (!NumberIsSafeInteger(asyncId) || asyncId < -1) { fatalError(new ERR_INVALID_ASYNC_ID(type, asyncId)); } } @@ -129,6 +133,8 @@ function emitInitNative(asyncId, type, triggerAsyncId, resource) { active_hooks.call_depth += 1; // Use a single try/catch for all hooks to avoid setting up one per iteration. try { + // Using var here instead of let because "for (var ...)" is faster than let. + // Refs: https://github.com/nodejs/node/pull/30380#issuecomment-552948364 for (var i = 0; i < active_hooks.array.length; i++) { if (typeof active_hooks.array[i][init_symbol] === 'function') { active_hooks.array[i][init_symbol]( @@ -159,6 +165,8 @@ function emitHook(symbol, asyncId) { // Use a single try/catch for all hook to avoid setting up one per // iteration. try { + // Using var here instead of let because "for (var ...)" is faster than let. + // Refs: https://github.com/nodejs/node/pull/30380#issuecomment-552948364 for (var i = 0; i < active_hooks.array.length; i++) { if (typeof active_hooks.array[i][symbol] === 'function') { active_hooks.array[i][symbol](asyncId); @@ -180,10 +188,10 @@ function emitHook(symbol, asyncId) { } function emitHookFactory(symbol, name) { - const fn = FunctionPrototype.bind(emitHook, undefined, symbol); + const fn = FunctionPrototypeBind(emitHook, undefined, symbol); // Set the name property of the function as it looks good in the stack trace. - Object.defineProperty(fn, 'name', { + ObjectDefineProperty(fn, 'name', { value: name }); return fn; @@ -292,7 +300,7 @@ function clearDefaultTriggerAsyncId() { function defaultTriggerAsyncIdScope(triggerAsyncId, block, ...args) { if (triggerAsyncId === undefined) return block(...args); - // CHECK(Number.isSafeInteger(triggerAsyncId)) + // CHECK(NumberIsSafeInteger(triggerAsyncId)) // CHECK(triggerAsyncId > 0) const oldDefaultTriggerAsyncId = async_id_fields[kDefaultTriggerAsyncId]; async_id_fields[kDefaultTriggerAsyncId] = triggerAsyncId; diff --git a/lib/internal/bootstrap/loaders.js b/lib/internal/bootstrap/loaders.js index 1915dc667461e3..044bea3114b840 100644 --- a/lib/internal/bootstrap/loaders.js +++ b/lib/internal/bootstrap/loaders.js @@ -44,15 +44,17 @@ /* global process, getLinkedBinding, getInternalBinding, primordials */ const { - Reflect, - Object, - ObjectPrototype, - SafeSet + ReflectGet, + ObjectCreate, + ObjectDefineProperty, + ObjectKeys, + ObjectPrototypeHasOwnProperty, + SafeSet, } = primordials; // Set up process.moduleLoadList. const moduleLoadList = []; -Object.defineProperty(process, 'moduleLoadList', { +ObjectDefineProperty(process, 'moduleLoadList', { value: moduleLoadList, configurable: true, enumerable: true, @@ -98,7 +100,7 @@ const internalBindingWhitelist = new SafeSet([ // Set up process.binding() and process._linkedBinding(). { - const bindingObj = Object.create(null); + const bindingObj = ObjectCreate(null); process.binding = function binding(module) { module = String(module); @@ -123,7 +125,7 @@ const internalBindingWhitelist = new SafeSet([ // Set up internalBinding() in the closure. let internalBinding; { - const bindingObj = Object.create(null); + const bindingObj = ObjectCreate(null); // eslint-disable-next-line no-global-assign internalBinding = function internalBinding(module) { let mod = bindingObj[module]; @@ -155,6 +157,9 @@ function NativeModule(id) { this.loaded = false; this.loading = false; this.canBeRequiredByUsers = !id.startsWith('internal/'); + + if (id === 'wasi') + this.canBeRequiredByUsers = !!internalBinding('config').experimentalWasi; } // To be called during pre-execution when --expose-internals is on. @@ -222,7 +227,7 @@ NativeModule.prototype.compileForPublicLoader = function() { // When using --expose-internals, we do not want to reflect the named // exports from core modules as this can trigger unnecessary getters. const internal = this.id.startsWith('internal/'); - this.exportKeys = internal ? [] : Object.keys(this.exports); + this.exportKeys = internal ? [] : ObjectKeys(this.exports); } this.getESMFacade(); this.syncExports(); @@ -230,8 +235,8 @@ NativeModule.prototype.compileForPublicLoader = function() { }; const getOwn = (target, property, receiver) => { - return ObjectPrototype.hasOwnProperty(target, property) ? - Reflect.get(target, property, receiver) : + return ObjectPrototypeHasOwnProperty(target, property) ? + ReflectGet(target, property, receiver) : undefined; }; diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js index 18acd9d2b64774..c586ddfd5e6f89 100644 --- a/lib/internal/bootstrap/node.js +++ b/lib/internal/bootstrap/node.js @@ -35,7 +35,14 @@ setupPrepareStackTrace(); -const { JSON, Object, Symbol } = primordials; +const { + JSONParse, + ObjectDefineProperties, + ObjectDefineProperty, + ObjectGetPrototypeOf, + ObjectSetPrototypeOf, + SymbolToStringTag, +} = primordials; const config = internalBinding('config'); const { deprecate } = require('internal/util'); @@ -64,7 +71,7 @@ if (ownsProcessState) { } // process.config is serialized config.gypi -process.config = JSON.parse(internalBinding('native_module').config); +process.config = JSONParse(internalBinding('native_module').config); const rawMethods = internalBinding('process_methods'); // Set up methods and events on the process object for the main thread @@ -205,7 +212,7 @@ const { setTraceCategoryStateUpdateHandler } = internalBinding('trace_events'); setTraceCategoryStateUpdateHandler(perThreadSetup.toggleTraceCategoryState); // process.allowedNodeEnvironmentFlags -Object.defineProperty(process, 'allowedNodeEnvironmentFlags', { +ObjectDefineProperty(process, 'allowedNodeEnvironmentFlags', { get() { const flags = perThreadSetup.buildAllowedFlags(); process.allowedNodeEnvironmentFlags = flags; @@ -214,7 +221,7 @@ Object.defineProperty(process, 'allowedNodeEnvironmentFlags', { // If the user tries to set this to another value, override // this completely to that value. set(value) { - Object.defineProperty(this, 'allowedNodeEnvironmentFlags', { + ObjectDefineProperty(this, 'allowedNodeEnvironmentFlags', { value, configurable: true, enumerable: true, @@ -234,7 +241,7 @@ process.assert = deprecate( // TODO(joyeecheung): this property has not been well-maintained, should we // deprecate it in favor of a better API? const { isDebugBuild, hasOpenSSL, hasInspector } = config; -Object.defineProperty(process, 'features', { +ObjectDefineProperty(process, 'features', { enumerable: true, writable: false, configurable: false, @@ -318,17 +325,17 @@ function setupPrepareStackTrace() { function setupProcessObject() { const EventEmitter = require('events'); - const origProcProto = Object.getPrototypeOf(process); - Object.setPrototypeOf(origProcProto, EventEmitter.prototype); + const origProcProto = ObjectGetPrototypeOf(process); + ObjectSetPrototypeOf(origProcProto, EventEmitter.prototype); EventEmitter.call(process); - Object.defineProperty(process, Symbol.toStringTag, { + ObjectDefineProperty(process, SymbolToStringTag, { enumerable: false, writable: true, configurable: false, value: 'process' }); // Make process globally available to users by putting it on the global proxy - Object.defineProperty(global, 'process', { + ObjectDefineProperty(global, 'process', { value: process, enumerable: false, writable: true, @@ -337,19 +344,19 @@ function setupProcessObject() { } function setupProcessStdio(getStdout, getStdin, getStderr) { - Object.defineProperty(process, 'stdout', { + ObjectDefineProperty(process, 'stdout', { configurable: true, enumerable: true, get: getStdout }); - Object.defineProperty(process, 'stderr', { + ObjectDefineProperty(process, 'stderr', { configurable: true, enumerable: true, get: getStderr }); - Object.defineProperty(process, 'stdin', { + ObjectDefineProperty(process, 'stdin', { configurable: true, enumerable: true, get: getStdin @@ -362,7 +369,7 @@ function setupProcessStdio(getStdout, getStdin, getStderr) { } function setupGlobalProxy() { - Object.defineProperty(global, Symbol.toStringTag, { + ObjectDefineProperty(global, SymbolToStringTag, { value: 'global', writable: false, enumerable: false, @@ -377,7 +384,7 @@ function setupGlobalProxy() { function makeSetter(name) { return deprecate(function(value) { - Object.defineProperty(this, name, { + ObjectDefineProperty(this, name, { configurable: true, writable: true, enumerable: true, @@ -386,7 +393,7 @@ function setupGlobalProxy() { }, `'${name}' is deprecated, use 'global'`, 'DEP0016'); } - Object.defineProperties(global, { + ObjectDefineProperties(global, { GLOBAL: { configurable: true, get: makeGetter('GLOBAL'), @@ -409,7 +416,7 @@ function setupBuffer() { delete bufferBinding.setBufferPrototype; delete bufferBinding.zeroFill; - Object.defineProperty(global, 'Buffer', { + ObjectDefineProperty(global, 'Buffer', { value: Buffer, enumerable: false, writable: true, @@ -436,7 +443,7 @@ function createGlobalConsole(consoleFromVM) { // https://heycam.github.io/webidl/#es-namespaces function exposeNamespace(target, name, namespaceObject) { - Object.defineProperty(target, name, { + ObjectDefineProperty(target, name, { writable: true, enumerable: false, configurable: true, @@ -446,7 +453,7 @@ function exposeNamespace(target, name, namespaceObject) { // https://heycam.github.io/webidl/#es-interfaces function exposeInterface(target, name, interfaceObject) { - Object.defineProperty(target, name, { + ObjectDefineProperty(target, name, { writable: true, enumerable: false, configurable: true, @@ -456,7 +463,7 @@ function exposeInterface(target, name, interfaceObject) { // https://heycam.github.io/webidl/#define-the-operations function defineOperation(target, name, method) { - Object.defineProperty(target, name, { + ObjectDefineProperty(target, name, { writable: true, enumerable: true, configurable: true, diff --git a/lib/internal/bootstrap/pre_execution.js b/lib/internal/bootstrap/pre_execution.js index 8edec86a3e3c96..b3738015922a0c 100644 --- a/lib/internal/bootstrap/pre_execution.js +++ b/lib/internal/bootstrap/pre_execution.js @@ -1,6 +1,9 @@ 'use strict'; -const { Object, SafeWeakMap } = primordials; +const { + ObjectDefineProperty, + SafeWeakMap, +} = primordials; const { getOptionValue } = require('internal/options'); const { Buffer } = require('buffer'); @@ -74,7 +77,7 @@ function patchProcessObject(expandArgv1) { patchProcessObjectNative(process); - Object.defineProperty(process, 'argv0', { + ObjectDefineProperty(process, 'argv0', { enumerable: true, configurable: false, value: process.argv[0] @@ -107,7 +110,7 @@ function patchProcessObject(expandArgv1) { function addReadOnlyProcessAlias(name, option, enumerable = true) { const value = getOptionValue(option); if (value) { - Object.defineProperty(process, name, { + ObjectDefineProperty(process, name, { writable: false, configurable: true, enumerable, @@ -153,7 +156,7 @@ function initializeReport() { } const { report } = require('internal/process/report'); const { emitExperimentalWarning } = require('internal/util'); - Object.defineProperty(process, 'report', { + ObjectDefineProperty(process, 'report', { enumerable: false, configurable: true, get() { @@ -273,7 +276,7 @@ function initializeDeprecations() { // process.features. const { noBrowserGlobals } = internalBinding('config'); if (noBrowserGlobals) { - Object.defineProperty(process, '_noBrowserGlobals', { + ObjectDefineProperty(process, '_noBrowserGlobals', { writable: false, enumerable: true, configurable: true, @@ -295,7 +298,7 @@ function initializeDeprecations() { // deprecation path for these in ES Modules. // See https://github.com/nodejs/node/pull/26334. let _process = process; - Object.defineProperty(global, 'process', { + ObjectDefineProperty(global, 'process', { get() { return _process; }, @@ -307,7 +310,7 @@ function initializeDeprecations() { }); let _Buffer = Buffer; - Object.defineProperty(global, 'Buffer', { + ObjectDefineProperty(global, 'Buffer', { get() { return _Buffer; }, @@ -372,7 +375,7 @@ function initializePolicy() { const realIntegrities = new Map(); const integrityEntries = SRI.parse(experimentalPolicyIntegrity); let foundMatch = false; - for (var i = 0; i < integrityEntries.length; i++) { + for (let i = 0; i < integrityEntries.length; i++) { const { algorithm, value: expected diff --git a/lib/internal/buffer.js b/lib/internal/buffer.js index f3bc0e48d1c911..8d55f780fadc2d 100644 --- a/lib/internal/buffer.js +++ b/lib/internal/buffer.js @@ -1,6 +1,9 @@ 'use strict'; -const { Math } = primordials; +const { + MathFloor, + Number, +} = primordials; const { ERR_BUFFER_OUT_OF_BOUNDS, @@ -61,7 +64,7 @@ function checkInt(value, min, max, buf, offset, byteLength) { } function boundsError(value, length, type) { - if (Math.floor(value) !== value) { + if (MathFloor(value) !== value) { validateNumber(value, type); throw new ERR_OUT_OF_RANGE(type || 'offset', 'an integer', value); } @@ -644,7 +647,7 @@ function writeU_Int48LE(buf, value, offset, min, max) { value = +value; checkInt(value, min, max, buf, offset, 5); - const newVal = Math.floor(value * 2 ** -32); + const newVal = MathFloor(value * 2 ** -32); buf[offset++] = value; value = value >>> 8; buf[offset++] = value; @@ -669,7 +672,7 @@ function writeU_Int40LE(buf, value, offset, min, max) { buf[offset++] = value; value = value >>> 8; buf[offset++] = value; - buf[offset++] = Math.floor(newVal * 2 ** -32); + buf[offset++] = MathFloor(newVal * 2 ** -32); return offset; } @@ -736,7 +739,7 @@ function writeUInt8(value, offset = 0) { function writeUIntBE(value, offset, byteLength) { if (byteLength === 6) - return writeU_Int48BE(this, value, offset, 0, 0xffffffffffffff); + return writeU_Int48BE(this, value, offset, 0, 0xffffffffffff); if (byteLength === 5) return writeU_Int40BE(this, value, offset, 0, 0xffffffffff); if (byteLength === 3) @@ -755,7 +758,7 @@ function writeU_Int48BE(buf, value, offset, min, max) { value = +value; checkInt(value, min, max, buf, offset, 5); - const newVal = Math.floor(value * 2 ** -32); + const newVal = MathFloor(value * 2 ** -32); buf[offset++] = (newVal >>> 8); buf[offset++] = newVal; buf[offset + 3] = value; @@ -772,7 +775,7 @@ function writeU_Int40BE(buf, value, offset, min, max) { value = +value; checkInt(value, min, max, buf, offset, 4); - buf[offset++] = Math.floor(value * 2 ** -32); + buf[offset++] = MathFloor(value * 2 ** -32); buf[offset + 3] = value; value = value >>> 8; buf[offset + 2] = value; diff --git a/lib/internal/child_process.js b/lib/internal/child_process.js index 9e13650fa3d56a..3836ee8e3d5ef5 100644 --- a/lib/internal/child_process.js +++ b/lib/internal/child_process.js @@ -1,6 +1,10 @@ 'use strict'; -const { Object } = primordials; +const { + ArrayIsArray, + ObjectDefineProperty, + ObjectSetPrototypeOf, +} = primordials; const { errnoException, @@ -103,8 +107,8 @@ const handleConversion = { // The worker should keep track of the socket message.key = socket.server._connectionKey; - var firstTime = !this.channel.sockets.send[message.key]; - var socketList = getSocketList('send', this, message.key); + const firstTime = !this.channel.sockets.send[message.key]; + const socketList = getSocketList('send', this, message.key); // The server should no longer expose a .connection property // and when asked to close it should query the socket status from @@ -171,7 +175,7 @@ const handleConversion = { if (message.key) { // Add socket to connections list - var socketList = getSocketList('got', this, message.key); + const socketList = getSocketList('got', this, message.key); socketList.add({ socket: socket }); @@ -258,7 +262,7 @@ function ChildProcess() { this._handle = null; if (exitCode < 0) { - var syscall = this.spawnfile ? 'spawn ' + this.spawnfile : 'spawn'; + const syscall = this.spawnfile ? 'spawn ' + this.spawnfile : 'spawn'; const err = errnoException(exitCode, syscall); if (this.spawnfile) @@ -281,8 +285,8 @@ function ChildProcess() { maybeClose(this); }; } -Object.setPrototypeOf(ChildProcess.prototype, EventEmitter.prototype); -Object.setPrototypeOf(ChildProcess, EventEmitter); +ObjectSetPrototypeOf(ChildProcess.prototype, EventEmitter.prototype); +ObjectSetPrototypeOf(ChildProcess, EventEmitter); function flushStdio(subprocess) { @@ -290,7 +294,7 @@ function flushStdio(subprocess) { if (stdio == null) return; - for (var i = 0; i < stdio.length; i++) { + for (let i = 0; i < stdio.length; i++) { const stream = stdio[i]; // TODO(addaleax): This doesn't necessarily account for all the ways in // which data can be read from a stream, e.g. being consumed on the @@ -354,7 +358,7 @@ ChildProcess.prototype.spawn = function(options) { // Let child process know about opened IPC channel if (options.envPairs === undefined) options.envPairs = []; - else if (!Array.isArray(options.envPairs)) { + else if (!ArrayIsArray(options.envPairs)) { throw new ERR_INVALID_ARG_TYPE('options.envPairs', 'Array', options.envPairs); @@ -367,7 +371,7 @@ ChildProcess.prototype.spawn = function(options) { validateString(options.file, 'options.file'); this.spawnfile = options.file; - if (Array.isArray(options.args)) + if (ArrayIsArray(options.args)) this.spawnargs = options.args; else if (options.args === undefined) this.spawnargs = []; @@ -471,7 +475,7 @@ ChildProcess.prototype.kill = function(sig) { convertToValidSignal(sig === undefined ? 'SIGTERM' : sig); if (this._handle) { - var err = this._handle.kill(signal); + const err = this._handle.kill(signal); if (err === 0) { /* Success. */ this.killed = true; @@ -528,7 +532,7 @@ let serialization; function setupChannel(target, channel, serializationMode) { target.channel = channel; - Object.defineProperty(target, '_channel', { + ObjectDefineProperty(target, '_channel', { get: deprecate(() => { return target.channel; }, channelDeprecationMsg, 'DEP0129'), @@ -610,8 +614,8 @@ function setupChannel(target, channel, serializationMode) { } } - assert(Array.isArray(target._handleQueue)); - var queue = target._handleQueue; + assert(ArrayIsArray(target._handleQueue)); + const queue = target._handleQueue; target._handleQueue = null; if (target._pendingMessage) { @@ -621,8 +625,8 @@ function setupChannel(target, channel, serializationMode) { target._pendingMessage.callback); } - for (var i = 0; i < queue.length; i++) { - var args = queue[i]; + for (let i = 0; i < queue.length; i++) { + const args = queue[i]; target._send(args.message, args.handle, args.options, args.callback); } @@ -854,7 +858,7 @@ function setupChannel(target, channel, serializationMode) { if (this._pendingMessage) closePendingHandle(this); - var fired = false; + let fired = false; function finish() { if (fired) return; fired = true; @@ -903,13 +907,13 @@ function isInternal(message) { function nop() { } function getValidStdio(stdio, sync) { - var ipc; - var ipcFd; + let ipc; + let ipcFd; // Replace shortcut with an array if (typeof stdio === 'string') { stdio = stdioStringToArray(stdio); - } else if (!Array.isArray(stdio)) { + } else if (!ArrayIsArray(stdio)) { throw new ERR_INVALID_OPT_VALUE('stdio', inspect(stdio)); } @@ -923,7 +927,7 @@ function getValidStdio(stdio, sync) { // (i.e. PipeWraps or fds) stdio = stdio.reduce((acc, stdio, i) => { function cleanup() { - for (var i = 0; i < acc.length; i++) { + for (let i = 0; i < acc.length; i++) { if ((acc[i].type === 'pipe' || acc[i].type === 'ipc') && acc[i].handle) acc[i].handle.close(); } @@ -937,7 +941,7 @@ function getValidStdio(stdio, sync) { if (stdio === 'ignore') { acc.push({ type: 'ignore' }); } else if (stdio === 'pipe' || (typeof stdio === 'number' && stdio < 0)) { - var a = { + const a = { type: 'pipe', readable: i === 0, writable: i !== 0 @@ -977,7 +981,7 @@ function getValidStdio(stdio, sync) { }); } else if (getHandleWrapType(stdio) || getHandleWrapType(stdio.handle) || getHandleWrapType(stdio._handle)) { - var handle = getHandleWrapType(stdio) ? + const handle = getHandleWrapType(stdio) ? stdio : getHandleWrapType(stdio.handle) ? stdio.handle : stdio._handle; @@ -1007,9 +1011,9 @@ function getValidStdio(stdio, sync) { function getSocketList(type, worker, key) { const sockets = worker.channel.sockets[type]; - var socketList = sockets[key]; + let socketList = sockets[key]; if (!socketList) { - var Construct = type === 'send' ? SocketListSend : SocketListReceive; + const Construct = type === 'send' ? SocketListSend : SocketListReceive; socketList = sockets[key] = new Construct(worker, key); } return socketList; @@ -1028,7 +1032,7 @@ function spawnSync(options) { const result = spawn_sync.spawn(options); if (result.output && options.encoding && options.encoding !== 'buffer') { - for (var i = 0; i < result.output.length; i++) { + for (let i = 0; i < result.output.length; i++) { if (!result.output[i]) continue; result.output[i] = result.output[i].toString(options.encoding); diff --git a/lib/internal/child_process/serialization.js b/lib/internal/child_process/serialization.js index 1381f299265e4c..48b6d7ffe00bf6 100644 --- a/lib/internal/child_process/serialization.js +++ b/lib/internal/child_process/serialization.js @@ -1,6 +1,9 @@ 'use strict'; -const { JSON } = primordials; +const { + JSONParse, + JSONStringify, +} = primordials; const { Buffer } = require('buffer'); const { StringDecoder } = require('string_decoder'); const v8 = require('v8'); @@ -104,14 +107,14 @@ const json = { } else { chunks[0] = channel[kJSONBuffer] + chunks[0]; for (let i = 0; i < numCompleteChunks; i++) - yield JSON.parse(chunks[i]); + yield JSONParse(chunks[i]); channel[kJSONBuffer] = incompleteChunk; } channel.buffering = channel[kJSONBuffer].length !== 0; }, writeChannelMessage(channel, req, message, handle) { - const string = JSON.stringify(message) + '\n'; + const string = JSONStringify(message) + '\n'; return channel.writeUtf8String(req, string, handle); }, }; diff --git a/lib/internal/cli_table.js b/lib/internal/cli_table.js index aaa094e6a5d25f..f0f09b4f8fe870 100644 --- a/lib/internal/cli_table.js +++ b/lib/internal/cli_table.js @@ -1,6 +1,10 @@ 'use strict'; -const { Math, ObjectPrototype } = primordials; +const { + MathCeil, + MathMax, + ObjectPrototypeHasOwnProperty, +} = primordials; const { getStringWidth } = require('internal/readline/utils'); @@ -30,13 +34,13 @@ const tableChars = { const renderRow = (row, columnWidths) => { let out = tableChars.left; - for (var i = 0; i < row.length; i++) { + for (let i = 0; i < row.length; i++) { const cell = row[i]; const len = getStringWidth(cell); const needed = (columnWidths[i] - len) / 2; // round(needed) + ceil(needed) will always add up to the amount // of spaces we need while also left justifying the output. - out += `${' '.repeat(needed)}${cell}${' '.repeat(Math.ceil(needed))}`; + out += `${' '.repeat(needed)}${cell}${' '.repeat(MathCeil(needed))}`; if (i !== row.length - 1) out += tableChars.middle; } @@ -47,18 +51,18 @@ const renderRow = (row, columnWidths) => { const table = (head, columns) => { const rows = []; const columnWidths = head.map((h) => getStringWidth(h)); - const longestColumn = columns.reduce((n, a) => Math.max(n, a.length), 0); + const longestColumn = columns.reduce((n, a) => MathMax(n, a.length), 0); - for (var i = 0; i < head.length; i++) { + for (let i = 0; i < head.length; i++) { const column = columns[i]; - for (var j = 0; j < longestColumn; j++) { + for (let j = 0; j < longestColumn; j++) { if (rows[j] === undefined) rows[j] = []; const value = rows[j][i] = - ObjectPrototype.hasOwnProperty(column, j) ? column[j] : ''; + ObjectPrototypeHasOwnProperty(column, j) ? column[j] : ''; const width = columnWidths[i] || 0; const counted = getStringWidth(value); - columnWidths[i] = Math.max(width, counted); + columnWidths[i] = MathMax(width, counted); } } diff --git a/lib/internal/cluster/child.js b/lib/internal/cluster/child.js index 097c6dafae0172..80db676e6b22a8 100644 --- a/lib/internal/cluster/child.js +++ b/lib/internal/cluster/child.js @@ -1,6 +1,8 @@ 'use strict'; -const { Object } = primordials; +const { + ObjectAssign, +} = primordials; const assert = require('internal/assert'); const path = require('path'); @@ -128,7 +130,7 @@ function rr(message, indexesKey, cb) { if (message.errno) return cb(message.errno, null); - var key = message.key; + let key = message.key; function listen(backlog) { // TODO(bnoordhuis) Send a message to the master that tells it to @@ -154,7 +156,7 @@ function rr(message, indexesKey, cb) { function getsockname(out) { if (key) - Object.assign(out, message.sockname); + ObjectAssign(out, message.sockname); return 0; } diff --git a/lib/internal/cluster/master.js b/lib/internal/cluster/master.js index 005de8aa1b3cbf..bee224a67d9406 100644 --- a/lib/internal/cluster/master.js +++ b/lib/internal/cluster/master.js @@ -1,6 +1,9 @@ 'use strict'; -const { Object } = primordials; +const { + ObjectKeys, + ObjectValues, +} = primordials; const assert = require('internal/assert'); const { fork } = require('child_process'); @@ -29,12 +32,12 @@ cluster.settings = {}; cluster.SCHED_NONE = SCHED_NONE; // Leave it to the operating system. cluster.SCHED_RR = SCHED_RR; // Master distributes connections. -var ids = 0; -var debugPortOffset = 1; -var initialized = false; +let ids = 0; +let debugPortOffset = 1; +let initialized = false; // XXX(bnoordhuis) Fold cluster.schedulingPolicy into cluster.settings? -var schedulingPolicy = { +let schedulingPolicy = { 'none': SCHED_NONE, 'rr': SCHED_RR }[process.env.NODE_CLUSTER_SCHED_POLICY]; @@ -82,7 +85,7 @@ cluster.setupMaster = function(options) { if (message.cmd !== 'NODE_DEBUG_ENABLED') return; - for (const worker of Object.values(cluster.workers)) { + for (const worker of ObjectValues(cluster.workers)) { if (worker.state === 'online' || worker.state === 'listening') { process._debugProcess(worker.process.pid); } else { @@ -144,7 +147,7 @@ function removeWorker(worker) { assert(worker); delete cluster.workers[worker.id]; - if (Object.keys(cluster.workers).length === 0) { + if (ObjectKeys(cluster.workers).length === 0) { assert(handles.size === 0, 'Resource leak detected.'); intercom.emit('disconnect'); } @@ -222,12 +225,12 @@ function emitForkNT(worker) { } cluster.disconnect = function(cb) { - const workers = Object.keys(cluster.workers); + const workers = ObjectKeys(cluster.workers); if (workers.length === 0) { process.nextTick(() => intercom.emit('disconnect')); } else { - for (const worker of Object.values(cluster.workers)) { + for (const worker of ObjectValues(cluster.workers)) { if (worker.isConnected()) { worker.disconnect(); } @@ -271,7 +274,7 @@ function queryServer(worker, message) { const key = `${message.address}:${message.port}:${message.addressType}:` + `${message.fd}:${message.index}`; - var handle = handles.get(key); + let handle = handles.get(key); if (handle === undefined) { let address = message.address; @@ -286,7 +289,7 @@ function queryServer(worker, message) { address = message.address; } - var constructor = RoundRobinHandle; + let constructor = RoundRobinHandle; // UDP is exempt from round-robin connection balancing for what should // be obvious reasons: it's connectionless. There is nothing to send to // the workers except raw datagrams and that's pointless. diff --git a/lib/internal/cluster/round_robin_handle.js b/lib/internal/cluster/round_robin_handle.js index 6794e8d41996f1..61023b83705bcb 100644 --- a/lib/internal/cluster/round_robin_handle.js +++ b/lib/internal/cluster/round_robin_handle.js @@ -1,4 +1,9 @@ 'use strict'; + +const { + Boolean, +} = primordials; + const assert = require('internal/assert'); const net = require('net'); const { sendHelper } = require('internal/cluster/utils'); diff --git a/lib/internal/cluster/shared_handle.js b/lib/internal/cluster/shared_handle.js index 0d0a805709a010..088798597382f8 100644 --- a/lib/internal/cluster/shared_handle.js +++ b/lib/internal/cluster/shared_handle.js @@ -11,7 +11,7 @@ function SharedHandle(key, address, port, addressType, fd, flags) { this.handle = null; this.errno = 0; - var rval; + let rval; if (addressType === 'udp4' || addressType === 'udp6') rval = dgram._createSocketHandle(address, port, addressType, fd, flags); else diff --git a/lib/internal/cluster/utils.js b/lib/internal/cluster/utils.js index f20206c842be96..b6d572fd0ea44c 100644 --- a/lib/internal/cluster/utils.js +++ b/lib/internal/cluster/utils.js @@ -6,7 +6,7 @@ module.exports = { }; const callbacks = new Map(); -var seq = 0; +let seq = 0; function sendHelper(proc, message, handle, cb) { if (!proc.connected) @@ -29,7 +29,7 @@ function internal(worker, cb) { if (message.cmd !== 'NODE_CLUSTER') return; - var fn = cb; + let fn = cb; if (message.ack !== undefined) { const callback = callbacks.get(message.ack); diff --git a/lib/internal/cluster/worker.js b/lib/internal/cluster/worker.js index 4563b2e663b686..516b7a3b73d787 100644 --- a/lib/internal/cluster/worker.js +++ b/lib/internal/cluster/worker.js @@ -1,9 +1,7 @@ 'use strict'; const { - Object: { - setPrototypeOf: ObjectSetPrototypeOf - } + ObjectSetPrototypeOf, } = primordials; const EventEmitter = require('events'); diff --git a/lib/internal/console/constructor.js b/lib/internal/console/constructor.js index c3339241aa7741..76b2066e9b7fd0 100644 --- a/lib/internal/console/constructor.js +++ b/lib/internal/console/constructor.js @@ -3,7 +3,19 @@ // The Console constructor is not actually used to construct the global // console. It's exported for backwards compatibility. -const { Object, ObjectPrototype, Reflect, Math } = primordials; +const { + ArrayFrom, + ArrayIsArray, + Boolean, + MathFloor, + Number, + ObjectDefineProperties, + ObjectDefineProperty, + ObjectKeys, + ObjectPrototypeHasOwnProperty, + ObjectValues, + ReflectOwnKeys, +} = primordials; const { trace } = internalBinding('trace_events'); const { @@ -36,11 +48,6 @@ const kSecond = 1000; const kMinute = 60 * kSecond; const kHour = 60 * kMinute; -const { - isArray: ArrayIsArray, - from: ArrayFrom, -} = Array; - // Lazy loaded for startup performance. let cliTable; @@ -106,7 +113,7 @@ function Console(options /* or: stdout, stderr, ignoreErrors = true */) { } // Bind the prototype functions to this Console instance - const keys = Object.keys(Console.prototype); + const keys = ObjectKeys(Console.prototype); for (var v = 0; v < keys.length; v++) { var k = keys[v]; // We have to bind the methods grabbed from the instance instead of from @@ -126,7 +133,7 @@ const consolePropAttributes = { }; // Fixup global.console instanceof global.console.Console -Object.defineProperty(Console, Symbol.hasInstance, { +ObjectDefineProperty(Console, Symbol.hasInstance, { value(instance) { return instance[kIsConsole]; } @@ -134,7 +141,7 @@ Object.defineProperty(Console, Symbol.hasInstance, { // Eager version for the Console constructor Console.prototype[kBindStreamsEager] = function(stdout, stderr) { - Object.defineProperties(this, { + ObjectDefineProperties(this, { '_stdout': { ...consolePropAttributes, value: stdout }, '_stderr': { ...consolePropAttributes, value: stderr } }); @@ -145,7 +152,7 @@ Console.prototype[kBindStreamsEager] = function(stdout, stderr) { Console.prototype[kBindStreamsLazy] = function(object) { let stdout; let stderr; - Object.defineProperties(this, { + ObjectDefineProperties(this, { '_stdout': { enumerable: false, configurable: true, @@ -168,7 +175,7 @@ Console.prototype[kBindStreamsLazy] = function(object) { }; Console.prototype[kBindProperties] = function(ignoreErrors, colorMode) { - Object.defineProperties(this, { + ObjectDefineProperties(this, { '_stdoutErrorHandler': { ...consolePropAttributes, value: createWriteErrorHandler(this, kUseStdout) @@ -412,7 +419,7 @@ const consoleMethods = { const depth = v !== null && typeof v === 'object' && !isArray(v) && - Object.keys(v).length > 2 ? -1 : 0; + ObjectKeys(v).length > 2 ? -1 : 0; const opt = { depth, maxArrayLength: 3, @@ -477,7 +484,7 @@ const consoleMethods = { const map = {}; let hasPrimitives = false; const valuesKeyArray = []; - const indexKeyArray = Object.keys(tabularData); + const indexKeyArray = ObjectKeys(tabularData); for (; i < indexKeyArray.length; i++) { const item = tabularData[indexKeyArray[i]]; @@ -487,12 +494,12 @@ const consoleMethods = { hasPrimitives = true; valuesKeyArray[i] = _inspect(item); } else { - const keys = properties || Object.keys(item); + const keys = properties || ObjectKeys(item); for (const key of keys) { if (map[key] === undefined) map[key] = []; if ((primitive && properties) || - !ObjectPrototype.hasOwnProperty(item, key)) + !ObjectPrototypeHasOwnProperty(item, key)) map[key][i] = ''; else map[key][i] = _inspect(item[key]); @@ -500,8 +507,8 @@ const consoleMethods = { } } - const keys = Object.keys(map); - const values = Object.values(map); + const keys = ObjectKeys(map); + const values = ObjectValues(map); if (hasPrimitives) { keys.push(valuesKey); values.push(valuesKeyArray); @@ -545,10 +552,10 @@ function formatTime(ms) { if (ms >= kSecond) { if (ms >= kMinute) { if (ms >= kHour) { - hours = Math.floor(ms / kHour); + hours = MathFloor(ms / kHour); ms = ms % kHour; } - minutes = Math.floor(ms / kMinute); + minutes = MathFloor(ms / kMinute); ms = ms % kMinute; } seconds = ms / kSecond; @@ -576,7 +583,7 @@ const isArray = (v) => ArrayIsArray(v) || isTypedArray(v) || isBuffer(v); function noop() {} -for (const method of Reflect.ownKeys(consoleMethods)) +for (const method of ReflectOwnKeys(consoleMethods)) Console.prototype[method] = consoleMethods[method]; Console.prototype.debug = Console.prototype.log; diff --git a/lib/internal/console/global.js b/lib/internal/console/global.js index bad7478f1e1131..6a1dc3806fdb0b 100644 --- a/lib/internal/console/global.js +++ b/lib/internal/console/global.js @@ -12,7 +12,12 @@ // Therefore, the console.Console.prototype is not // in the global console prototype chain anymore. -const { Object, Reflect } = primordials; +const { + ObjectCreate, + ReflectDefineProperty, + ReflectGetOwnPropertyDescriptor, + ReflectOwnKeys, +} = primordials; const { Console, @@ -20,20 +25,20 @@ const { kBindProperties } = require('internal/console/constructor'); -const globalConsole = Object.create({}); +const globalConsole = ObjectCreate({}); // Since Console is not on the prototype chain of the global console, // the symbol properties on Console.prototype have to be looked up from // the global console itself. In addition, we need to make the global // console a namespace by binding the console methods directly onto // the global console with the receiver fixed. -for (const prop of Reflect.ownKeys(Console.prototype)) { +for (const prop of ReflectOwnKeys(Console.prototype)) { if (prop === 'constructor') { continue; } - const desc = Reflect.getOwnPropertyDescriptor(Console.prototype, prop); + const desc = ReflectGetOwnPropertyDescriptor(Console.prototype, prop); if (typeof desc.value === 'function') { // fix the receiver desc.value = desc.value.bind(globalConsole); } - Reflect.defineProperty(globalConsole, prop, desc); + ReflectDefineProperty(globalConsole, prop, desc); } globalConsole[kBindStreamsLazy](process); diff --git a/lib/internal/crypto/cipher.js b/lib/internal/crypto/cipher.js index 133b1e51532fc7..add56eae680ced 100644 --- a/lib/internal/crypto/cipher.js +++ b/lib/internal/crypto/cipher.js @@ -1,6 +1,8 @@ 'use strict'; -const { Object } = primordials; +const { + ObjectSetPrototypeOf, +} = primordials; const { RSA_PKCS1_OAEP_PADDING, @@ -126,8 +128,8 @@ function Cipher(cipher, password, options) { createCipher.call(this, cipher, password, options, true); } -Object.setPrototypeOf(Cipher.prototype, LazyTransform.prototype); -Object.setPrototypeOf(Cipher, LazyTransform); +ObjectSetPrototypeOf(Cipher.prototype, LazyTransform.prototype); +ObjectSetPrototypeOf(Cipher, LazyTransform); Cipher.prototype._transform = function _transform(chunk, encoding, callback) { this.push(this[kHandle].update(chunk, encoding)); @@ -239,8 +241,8 @@ function addCipherPrototypeFunctions(constructor) { constructor.prototype.setAAD = Cipher.prototype.setAAD; } -Object.setPrototypeOf(Cipheriv.prototype, LazyTransform.prototype); -Object.setPrototypeOf(Cipheriv, LazyTransform); +ObjectSetPrototypeOf(Cipheriv.prototype, LazyTransform.prototype); +ObjectSetPrototypeOf(Cipheriv, LazyTransform); addCipherPrototypeFunctions(Cipheriv); function Decipher(cipher, password, options) { @@ -250,8 +252,8 @@ function Decipher(cipher, password, options) { createCipher.call(this, cipher, password, options, false); } -Object.setPrototypeOf(Decipher.prototype, LazyTransform.prototype); -Object.setPrototypeOf(Decipher, LazyTransform); +ObjectSetPrototypeOf(Decipher.prototype, LazyTransform.prototype); +ObjectSetPrototypeOf(Decipher, LazyTransform); addCipherPrototypeFunctions(Decipher); @@ -262,8 +264,8 @@ function Decipheriv(cipher, key, iv, options) { createCipherWithIV.call(this, cipher, key, options, false, iv); } -Object.setPrototypeOf(Decipheriv.prototype, LazyTransform.prototype); -Object.setPrototypeOf(Decipheriv, LazyTransform); +ObjectSetPrototypeOf(Decipheriv.prototype, LazyTransform.prototype); +ObjectSetPrototypeOf(Decipheriv, LazyTransform); addCipherPrototypeFunctions(Decipheriv); module.exports = { diff --git a/lib/internal/crypto/diffiehellman.js b/lib/internal/crypto/diffiehellman.js index 7e0fde38bcbe10..da8f87bf16d4f6 100644 --- a/lib/internal/crypto/diffiehellman.js +++ b/lib/internal/crypto/diffiehellman.js @@ -1,6 +1,8 @@ 'use strict'; -const { Object } = primordials; +const { + ObjectDefineProperty, +} = primordials; const { Buffer } = require('buffer'); const { @@ -63,7 +65,7 @@ function DiffieHellman(sizeOrKey, keyEncoding, generator, genEncoding) { generator = toBuf(generator, genEncoding); this[kHandle] = new _DiffieHellman(sizeOrKey, generator); - Object.defineProperty(this, 'verifyError', { + ObjectDefineProperty(this, 'verifyError', { enumerable: true, value: this[kHandle].verifyError, writable: false @@ -75,7 +77,7 @@ function DiffieHellmanGroup(name) { if (!(this instanceof DiffieHellmanGroup)) return new DiffieHellmanGroup(name); this[kHandle] = new _DiffieHellmanGroup(name); - Object.defineProperty(this, 'verifyError', { + ObjectDefineProperty(this, 'verifyError', { enumerable: true, value: this[kHandle].verifyError, writable: false diff --git a/lib/internal/crypto/hash.js b/lib/internal/crypto/hash.js index b28f9fca13365c..56784275f9c0d2 100644 --- a/lib/internal/crypto/hash.js +++ b/lib/internal/crypto/hash.js @@ -1,6 +1,8 @@ 'use strict'; -const { Object } = primordials; +const { + ObjectSetPrototypeOf, +} = primordials; const { Hash: _Hash, @@ -47,8 +49,8 @@ function Hash(algorithm, options) { LazyTransform.call(this, options); } -Object.setPrototypeOf(Hash.prototype, LazyTransform.prototype); -Object.setPrototypeOf(Hash, LazyTransform); +ObjectSetPrototypeOf(Hash.prototype, LazyTransform.prototype); +ObjectSetPrototypeOf(Hash, LazyTransform); Hash.prototype.copy = function copy(options) { const state = this[kState]; @@ -118,8 +120,8 @@ function Hmac(hmac, key, options) { LazyTransform.call(this, options); } -Object.setPrototypeOf(Hmac.prototype, LazyTransform.prototype); -Object.setPrototypeOf(Hmac, LazyTransform); +ObjectSetPrototypeOf(Hmac.prototype, LazyTransform.prototype); +ObjectSetPrototypeOf(Hmac, LazyTransform); Hmac.prototype.update = Hash.prototype.update; diff --git a/lib/internal/crypto/keygen.js b/lib/internal/crypto/keygen.js index 0a4bde77fa369b..88d2822fa6fad0 100644 --- a/lib/internal/crypto/keygen.js +++ b/lib/internal/crypto/keygen.js @@ -1,6 +1,8 @@ 'use strict'; -const { Object } = primordials; +const { + ObjectDefineProperty, +} = primordials; const { AsyncWrap, Providers } = internalBinding('async_wrap'); const { @@ -63,7 +65,7 @@ function generateKeyPair(type, options, callback) { handleError(impl(wrap)); } -Object.defineProperty(generateKeyPair, customPromisifyArgs, { +ObjectDefineProperty(generateKeyPair, customPromisifyArgs, { value: ['publicKey', 'privateKey'], enumerable: false }); diff --git a/lib/internal/crypto/keys.js b/lib/internal/crypto/keys.js index 250147d7529439..25ef8884953bc9 100644 --- a/lib/internal/crypto/keys.js +++ b/lib/internal/crypto/keys.js @@ -1,6 +1,8 @@ 'use strict'; -const { Object } = primordials; +const { + ObjectDefineProperty, +} = primordials; const { KeyObject: KeyObjectHandle, @@ -48,7 +50,7 @@ class KeyObject { this[kKeyType] = type; - Object.defineProperty(this, kHandle, { + ObjectDefineProperty(this, kHandle, { value: handle, enumerable: false, configurable: false, diff --git a/lib/internal/crypto/pbkdf2.js b/lib/internal/crypto/pbkdf2.js index 1c2288b65f1a25..20f6f682254036 100644 --- a/lib/internal/crypto/pbkdf2.js +++ b/lib/internal/crypto/pbkdf2.js @@ -66,8 +66,8 @@ function check(password, salt, iterations, keylen, digest) { password = getArrayBufferView(password, 'password'); salt = getArrayBufferView(salt, 'salt'); - validateUint32(iterations, 'iterations', 0); - validateUint32(keylen, 'keylen', 0); + validateUint32(iterations, 'iterations'); + validateUint32(keylen, 'keylen'); return { password, salt, iterations, keylen, digest }; } diff --git a/lib/internal/crypto/random.js b/lib/internal/crypto/random.js index f42458aa788985..85855a709e89fd 100644 --- a/lib/internal/crypto/random.js +++ b/lib/internal/crypto/random.js @@ -1,6 +1,9 @@ 'use strict'; -const { Math } = primordials; +const { + MathMin, + NumberIsNaN, +} = primordials; const { AsyncWrap, Providers } = internalBinding('async_wrap'); const { Buffer, kMaxLength } = require('buffer'); @@ -14,14 +17,14 @@ const { validateNumber } = require('internal/validators'); const { isArrayBufferView } = require('internal/util/types'); const kMaxUint32 = 2 ** 32 - 1; -const kMaxPossibleLength = Math.min(kMaxLength, kMaxUint32); +const kMaxPossibleLength = MathMin(kMaxLength, kMaxUint32); function assertOffset(offset, elementSize, length) { validateNumber(offset, 'offset'); offset *= elementSize; - const maxLength = Math.min(length, kMaxPossibleLength); - if (Number.isNaN(offset) || offset > maxLength || offset < 0) { + const maxLength = MathMin(length, kMaxPossibleLength); + if (NumberIsNaN(offset) || offset > maxLength || offset < 0) { throw new ERR_OUT_OF_RANGE('offset', `>= 0 && <= ${maxLength}`, offset); } @@ -32,7 +35,7 @@ function assertSize(size, elementSize, offset, length) { validateNumber(size, 'size'); size *= elementSize; - if (Number.isNaN(size) || size > kMaxPossibleLength || size < 0) { + if (NumberIsNaN(size) || size > kMaxPossibleLength || size < 0) { throw new ERR_OUT_OF_RANGE('size', `>= 0 && <= ${kMaxPossibleLength}`, size); } diff --git a/lib/internal/crypto/sig.js b/lib/internal/crypto/sig.js index 6eda8455643848..27930ce1acf793 100644 --- a/lib/internal/crypto/sig.js +++ b/lib/internal/crypto/sig.js @@ -1,6 +1,8 @@ 'use strict'; -const { Object } = primordials; +const { + ObjectSetPrototypeOf, +} = primordials; const { ERR_CRYPTO_SIGN_KEY_REQUIRED, @@ -38,8 +40,8 @@ function Sign(algorithm, options) { Writable.call(this, options); } -Object.setPrototypeOf(Sign.prototype, Writable.prototype); -Object.setPrototypeOf(Sign, Writable); +ObjectSetPrototypeOf(Sign.prototype, Writable.prototype); +ObjectSetPrototypeOf(Sign, Writable); Sign.prototype._write = function _write(chunk, encoding, callback) { this.update(chunk, encoding); @@ -153,8 +155,8 @@ function Verify(algorithm, options) { Writable.call(this, options); } -Object.setPrototypeOf(Verify.prototype, Writable.prototype); -Object.setPrototypeOf(Verify, Writable); +ObjectSetPrototypeOf(Verify.prototype, Writable.prototype); +ObjectSetPrototypeOf(Verify, Writable); Verify.prototype._write = Sign.prototype._write; Verify.prototype.update = Sign.prototype.update; diff --git a/lib/internal/dns/promises.js b/lib/internal/dns/promises.js index 306d5eee477343..c4bbc21ae5528c 100644 --- a/lib/internal/dns/promises.js +++ b/lib/internal/dns/promises.js @@ -1,6 +1,9 @@ 'use strict'; -const { Object } = primordials; +const { + ObjectCreate, + ObjectDefineProperty, +} = primordials; const { bindDefaultResolver, @@ -203,12 +206,12 @@ function resolver(bindingName) { return createResolverPromise(this, bindingName, name, ttl); } - Object.defineProperty(query, 'name', { value: bindingName }); + ObjectDefineProperty(query, 'name', { value: bindingName }); return query; } -const resolveMap = Object.create(null); +const resolveMap = ObjectCreate(null); // Resolver instances correspond 1:1 to c-ares channels. class Resolver { diff --git a/lib/internal/dns/utils.js b/lib/internal/dns/utils.js index 70097663546a1a..18ad6bfad7a192 100644 --- a/lib/internal/dns/utils.js +++ b/lib/internal/dns/utils.js @@ -1,4 +1,9 @@ 'use strict'; + +const { + ArrayIsArray, +} = primordials; + const errors = require('internal/errors'); const { isIP } = require('internal/net'); const { @@ -38,7 +43,7 @@ class Resolver { } setServers(servers) { - if (!Array.isArray(servers)) { + if (!ArrayIsArray(servers)) { throw new ERR_INVALID_ARG_TYPE('servers', 'Array', servers); } @@ -52,7 +57,7 @@ class Resolver { if (typeof serv !== 'string') { throw new ERR_INVALID_ARG_TYPE(`servers[${index}]`, 'string', serv); } - var ipVersion = isIP(serv); + let ipVersion = isIP(serv); if (ipVersion !== 0) return newSet.push([ipVersion, serv, IANA_DNS_PORT]); diff --git a/lib/internal/encoding.js b/lib/internal/encoding.js index 16de0c986e6cb1..fe940f6e860497 100644 --- a/lib/internal/encoding.js +++ b/lib/internal/encoding.js @@ -3,7 +3,11 @@ // An implementation of the WHATWG Encoding Standard // https://encoding.spec.whatwg.org -const { Object } = primordials; +const { + ObjectCreate, + ObjectDefineProperties, + ObjectGetOwnPropertyDescriptors, +} = primordials; const { ERR_ENCODING_INVALID_ENCODED_DATA, @@ -36,7 +40,7 @@ const { encodeUtf8String } = internalBinding('buffer'); -var Buffer; +let Buffer; function lazyBuffer() { if (Buffer === undefined) Buffer = require('buffer').Buffer; @@ -281,8 +285,8 @@ const encodings = new Map([ // Unfortunately, String.prototype.trim also removes non-ascii whitespace, // so we have to do this manually function trimAsciiWhitespace(label) { - var s = 0; - var e = label.length; + let s = 0; + let e = label.length; while (s < e && ( label[s] === '\u0009' || label[s] === '\u000a' || @@ -339,7 +343,7 @@ class TextEncoder { if (typeof depth === 'number' && depth < 0) return this; const ctor = getConstructorOf(this); - const obj = Object.create({ + const obj = ObjectCreate({ constructor: ctor === null ? TextEncoder : ctor }); obj.encoding = this.encoding; @@ -348,7 +352,7 @@ class TextEncoder { } } -Object.defineProperties( +ObjectDefineProperties( TextEncoder.prototype, { 'encode': { enumerable: true }, 'encodeInto': { enumerable: true }, @@ -378,7 +382,7 @@ function makeTextDecoderICU() { if (enc === undefined) throw new ERR_ENCODING_NOT_SUPPORTED(encoding); - var flags = 0; + let flags = 0; if (options !== null) { flags |= options.fatal ? CONVERTER_FLAGS_FATAL : 0; flags |= options.ignoreBOM ? CONVERTER_FLAGS_IGNORE_BOM : 0; @@ -406,7 +410,7 @@ function makeTextDecoderICU() { } validateArgument(options, 'object', 'options', 'Object'); - var flags = 0; + let flags = 0; if (options !== null) flags |= options.stream ? 0 : CONVERTER_FLAGS_FLUSH; @@ -422,7 +426,7 @@ function makeTextDecoderICU() { } function makeTextDecoderJS() { - var StringDecoder; + let StringDecoder; function lazyStringDecoder() { if (StringDecoder === undefined) ({ StringDecoder } = require('string_decoder')); @@ -444,7 +448,7 @@ function makeTextDecoderJS() { if (enc === undefined || !hasConverter(enc)) throw new ERR_ENCODING_NOT_SUPPORTED(encoding); - var flags = 0; + let flags = 0; if (options !== null) { if (options.fatal) { throw new ERR_NO_ICU('"fatal" option'); @@ -508,9 +512,9 @@ function makeTextDecoderJS() { // Mix in some shared properties. { - Object.defineProperties( + ObjectDefineProperties( TextDecoder.prototype, - Object.getOwnPropertyDescriptors({ + ObjectGetOwnPropertyDescriptors({ get encoding() { validateDecoder(this); return this[kEncoding]; @@ -532,7 +536,7 @@ function makeTextDecoderJS() { if (typeof depth === 'number' && depth < 0) return this; const ctor = getConstructorOf(this); - const obj = Object.create({ + const obj = ObjectCreate({ constructor: ctor === null ? TextDecoder : ctor }); obj.encoding = this.encoding; @@ -546,7 +550,7 @@ function makeTextDecoderJS() { return require('internal/util/inspect').inspect(obj, opts); } })); - Object.defineProperties(TextDecoder.prototype, { + ObjectDefineProperties(TextDecoder.prototype, { decode: { enumerable: true }, [inspect]: { enumerable: false }, [Symbol.toStringTag]: { diff --git a/lib/internal/error-serdes.js b/lib/internal/error-serdes.js index 8647e3b78d19ec..1216c56bffe5c2 100644 --- a/lib/internal/error-serdes.js +++ b/lib/internal/error-serdes.js @@ -2,10 +2,16 @@ const Buffer = require('buffer').Buffer; const { - ArrayPrototype, - FunctionPrototype, - Object, - ObjectPrototype, + ArrayPrototypeForEach, + FunctionPrototypeCall, + ObjectAssign, + ObjectCreate, + ObjectDefineProperty, + ObjectGetOwnPropertyDescriptor, + ObjectGetOwnPropertyNames, + ObjectGetPrototypeOf, + ObjectKeys, + ObjectPrototypeToString, SafeSet, } = primordials; @@ -16,24 +22,24 @@ const kInspectedError = 2; const errors = { Error, TypeError, RangeError, URIError, SyntaxError, ReferenceError, EvalError }; -const errorConstructorNames = new SafeSet(Object.keys(errors)); +const errorConstructorNames = new SafeSet(ObjectKeys(errors)); function TryGetAllProperties(object, target = object) { - const all = Object.create(null); + const all = ObjectCreate(null); if (object === null) return all; - Object.assign(all, - TryGetAllProperties(Object.getPrototypeOf(object), target)); - const keys = Object.getOwnPropertyNames(object); - ArrayPrototype.forEach(keys, (key) => { + ObjectAssign(all, + TryGetAllProperties(ObjectGetPrototypeOf(object), target)); + const keys = ObjectGetOwnPropertyNames(object); + ArrayPrototypeForEach(keys, (key) => { let descriptor; try { - descriptor = Object.getOwnPropertyDescriptor(object, key); + descriptor = ObjectGetOwnPropertyDescriptor(object, key); } catch { return; } const getter = descriptor.get; if (getter && key !== '__proto__') { try { - descriptor.value = FunctionPrototype.call(getter, target); + descriptor.value = FunctionPrototypeCall(getter, target); } catch {} } if ('value' in descriptor && typeof descriptor.value !== 'function') { @@ -48,12 +54,12 @@ function TryGetAllProperties(object, target = object) { function GetConstructors(object) { const constructors = []; - for (var current = object; + for (let current = object; current !== null; - current = Object.getPrototypeOf(current)) { - const desc = Object.getOwnPropertyDescriptor(current, 'constructor'); + current = ObjectGetPrototypeOf(current)) { + const desc = ObjectGetOwnPropertyDescriptor(current, 'constructor'); if (desc && desc.value) { - Object.defineProperty(constructors, constructors.length, { + ObjectDefineProperty(constructors, constructors.length, { value: desc.value, enumerable: true }); } @@ -63,7 +69,7 @@ function GetConstructors(object) { } function GetName(object) { - const desc = Object.getOwnPropertyDescriptor(object, 'name'); + const desc = ObjectGetOwnPropertyDescriptor(object, 'name'); return desc && desc.value; } @@ -80,9 +86,9 @@ function serializeError(error) { if (!serialize) serialize = require('v8').serialize; try { if (typeof error === 'object' && - ObjectPrototype.toString(error) === '[object Error]') { + ObjectPrototypeToString(error) === '[object Error]') { const constructors = GetConstructors(error); - for (var i = 0; i < constructors.length; i++) { + for (let i = 0; i < constructors.length; i++) { const name = GetName(constructors[i]); if (errorConstructorNames.has(name)) { const serialized = serialize({ @@ -109,7 +115,7 @@ function deserializeError(error) { case kSerializedError: const { constructor, properties } = deserialize(error.subarray(1)); const ctor = errors[constructor]; - return Object.create(ctor.prototype, properties); + return ObjectCreate(ctor.prototype, properties); case kSerializedObject: return deserialize(error.subarray(1)); case kInspectedError: diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 6d4a582631810c..f85253ec444256 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -10,7 +10,13 @@ // value statically and permanently identifies the error. While the error // message may change, the code should not. -const { Object, Math } = primordials; +const { + ArrayIsArray, + MathAbs, + NumberIsInteger, + ObjectDefineProperty, + ObjectKeys, +} = primordials; const messages = new Map(); const codes = {}; @@ -113,7 +119,7 @@ class SystemError extends Error { if (context.dest !== undefined) message += ` => ${context.dest}`; - Object.defineProperty(this, 'message', { + ObjectDefineProperty(this, 'message', { value: message, enumerable: false, writable: true, @@ -123,14 +129,14 @@ class SystemError extends Error { this.code = key; - Object.defineProperty(this, 'info', { + ObjectDefineProperty(this, 'info', { value: context, enumerable: true, configurable: true, writable: false }); - Object.defineProperty(this, 'errno', { + ObjectDefineProperty(this, 'errno', { get() { return context.errno; }, @@ -141,7 +147,7 @@ class SystemError extends Error { configurable: true }); - Object.defineProperty(this, 'syscall', { + ObjectDefineProperty(this, 'syscall', { get() { return context.syscall; }, @@ -158,7 +164,7 @@ class SystemError extends Error { // always be of type string. We should probably just remove the // `.toString()` and `Buffer.from()` operations and set the value on the // context as the user did. - Object.defineProperty(this, 'path', { + ObjectDefineProperty(this, 'path', { get() { return context.path != null ? context.path.toString() : context.path; @@ -173,7 +179,7 @@ class SystemError extends Error { } if (context.dest !== undefined) { - Object.defineProperty(this, 'dest', { + ObjectDefineProperty(this, 'dest', { get() { return context.dest != null ? context.dest.toString() : context.dest; @@ -222,7 +228,7 @@ function makeNodeErrorWithCode(Base, key) { Error.stackTraceLimit = limit; } const message = getMessage(key, args, this); - Object.defineProperty(this, 'message', { + ObjectDefineProperty(this, 'message', { value: message, enumerable: false, writable: true, @@ -270,7 +276,7 @@ function addCodeToName(err, name, code) { err.stack; // Reset the name to the actual name. if (name === 'SystemError') { - Object.defineProperty(err, 'name', { + ObjectDefineProperty(err, 'name', { value: name, enumerable: false, writable: true, @@ -383,7 +389,7 @@ function uvException(ctx) { const err = new Error(message); Error.stackTraceLimit = tmpLimit; - for (const prop of Object.keys(ctx)) { + for (const prop of ObjectKeys(ctx)) { if (prop === 'message' || prop === 'path' || prop === 'dest') { continue; } @@ -604,7 +610,7 @@ function isStackOverflowError(err) { function oneOf(expected, thing) { assert(typeof thing === 'string', '`thing` has to be of type string'); - if (Array.isArray(expected)) { + if (ArrayIsArray(expected)) { const len = expected.length; assert(len > 0, 'At least one expected value needs to be specified'); @@ -1103,7 +1109,7 @@ E('ERR_OUT_OF_RANGE', let msg = replaceDefaultBoolean ? str : `The value of "${str}" is out of range.`; let received; - if (Number.isInteger(input) && Math.abs(input) > 2 ** 32) { + if (NumberIsInteger(input) && MathAbs(input) > 2 ** 32) { received = addNumericalSeparator(String(input)); } else if (typeof input === 'bigint') { received = String(input); @@ -1163,6 +1169,7 @@ E('ERR_TLS_CERT_ALTNAME_INVALID', function(reason, host, cert) { }, Error); E('ERR_TLS_DH_PARAM_SIZE', 'DH parameter size %s is less than 2048', Error); E('ERR_TLS_HANDSHAKE_TIMEOUT', 'TLS handshake timeout', Error); +E('ERR_TLS_INVALID_CONTEXT', '%s must be a SecureContext', TypeError), E('ERR_TLS_INVALID_PROTOCOL_VERSION', '%j is not a valid %s TLS protocol version', TypeError); E('ERR_TLS_PROTOCOL_VERSION_CONFLICT', @@ -1223,6 +1230,7 @@ E('ERR_VM_MODULE_LINKING_ERRORED', E('ERR_VM_MODULE_NOT_MODULE', 'Provided module is not an instance of Module', Error); E('ERR_VM_MODULE_STATUS', 'Module status %s', Error); +E('ERR_WASI_ALREADY_STARTED', 'WASI instance has already started', Error); E('ERR_WORKER_INVALID_EXEC_ARGV', (errors) => `Initiated Worker with invalid execArgv flags: ${errors.join(', ')}`, Error); diff --git a/lib/internal/fixed_queue.js b/lib/internal/fixed_queue.js index a073ab7fc327af..d3ffbc2a1e154e 100644 --- a/lib/internal/fixed_queue.js +++ b/lib/internal/fixed_queue.js @@ -1,5 +1,9 @@ 'use strict'; +const { + Array, +} = primordials; + // Currently optimal queue size, tested on V8 6.0 - 6.6. Must be power of two. const kSize = 2048; const kMask = kSize - 1; diff --git a/lib/internal/freelist.js b/lib/internal/freelist.js index 16437b2b8a7a6d..3e93e04f8dae38 100644 --- a/lib/internal/freelist.js +++ b/lib/internal/freelist.js @@ -1,6 +1,8 @@ 'use strict'; -const { Reflect } = primordials; +const { + ReflectApply, +} = primordials; class FreeList { constructor(name, max, ctor) { @@ -17,7 +19,7 @@ class FreeList { alloc() { return this.list.length > 0 ? this.list.pop() : - Reflect.apply(this.ctor, this, arguments); + ReflectApply(this.ctor, this, arguments); } free(obj) { diff --git a/lib/internal/fs/dir.js b/lib/internal/fs/dir.js index 90ab31fe5abc73..c4e0a3746c125a 100644 --- a/lib/internal/fs/dir.js +++ b/lib/internal/fs/dir.js @@ -1,6 +1,8 @@ 'use strict'; -const { Object } = primordials; +const { + ObjectDefineProperty, +} = primordials; const pathModule = require('path'); const binding = internalBinding('fs'); @@ -172,7 +174,7 @@ class Dir { } } -Object.defineProperty(Dir.prototype, Symbol.asyncIterator, { +ObjectDefineProperty(Dir.prototype, Symbol.asyncIterator, { value: Dir.prototype.entries, enumerable: false, writable: true, diff --git a/lib/internal/fs/promises.js b/lib/internal/fs/promises.js index 31613780a78e42..daf18e19d1c784 100644 --- a/lib/internal/fs/promises.js +++ b/lib/internal/fs/promises.js @@ -1,6 +1,10 @@ 'use strict'; -const { Math } = primordials; +const { + MathMax, + MathMin, + NumberIsSafeInteger, +} = primordials; const { F_OK, @@ -134,7 +138,7 @@ async function writeFileHandle(filehandle, data, options) { do { const { bytesWritten } = await write(filehandle, buffer, 0, - Math.min(16384, buffer.length)); + MathMin(16384, buffer.length)); remaining -= bytesWritten; buffer = buffer.slice(bytesWritten); } while (remaining > 0); @@ -160,7 +164,7 @@ async function readFileHandle(filehandle, options) { const chunks = []; const chunkSize = size === 0 ? kReadFileMaxChunkSize : - Math.min(size, kReadFileMaxChunkSize); + MathMin(size, kReadFileMaxChunkSize); let endOfFile = false; do { const buf = Buffer.alloc(chunkSize); @@ -232,7 +236,7 @@ async function read(handle, buffer, offset, length, position) { validateOffsetLengthRead(offset, length, buffer.length); - if (!Number.isSafeInteger(position)) + if (!NumberIsSafeInteger(position)) position = -1; const bytesRead = (await binding.read(handle.fd, buffer, offset, length, @@ -299,7 +303,7 @@ async function truncate(path, len = 0) { async function ftruncate(handle, len = 0) { validateFileHandle(handle); validateInteger(len, 'len'); - len = Math.max(0, len); + len = MathMax(0, len); return binding.ftruncate(handle.fd, len, kUsePromises); } diff --git a/lib/internal/fs/read_file_context.js b/lib/internal/fs/read_file_context.js index e1de8fc7cda2c3..d7b0e368006f05 100644 --- a/lib/internal/fs/read_file_context.js +++ b/lib/internal/fs/read_file_context.js @@ -1,6 +1,8 @@ 'use strict'; -const { Math } = primordials; +const { + MathMin, +} = primordials; const { Buffer } = require('buffer'); @@ -87,7 +89,7 @@ class ReadFileContext { } else { buffer = this.buffer; offset = this.pos; - length = Math.min(kReadFileBufferLength, this.size - this.pos); + length = MathMin(kReadFileBufferLength, this.size - this.pos); } const req = new FSReqCallback(); diff --git a/lib/internal/fs/rimraf.js b/lib/internal/fs/rimraf.js index 73f783d1d231f2..60310e1cf9427e 100644 --- a/lib/internal/fs/rimraf.js +++ b/lib/internal/fs/rimraf.js @@ -22,27 +22,24 @@ const { const { join } = require('path'); const { setTimeout } = require('timers'); const notEmptyErrorCodes = new Set(['ENOTEMPTY', 'EEXIST', 'EPERM']); +const retryErrorCodes = new Set( + ['EBUSY', 'EMFILE', 'ENFILE', 'ENOTEMPTY', 'EPERM']); const isWindows = process.platform === 'win32'; const epermHandler = isWindows ? fixWinEPERM : _rmdir; const epermHandlerSync = isWindows ? fixWinEPERMSync : _rmdirSync; -const numRetries = isWindows ? 100 : 1; function rimraf(path, options, callback) { - let timeout = 0; // For EMFILE handling. - let busyTries = 0; + let retries = 0; _rimraf(path, options, function CB(err) { if (err) { - if ((err.code === 'EBUSY' || err.code === 'ENOTEMPTY' || - err.code === 'EPERM') && busyTries < options.maxBusyTries) { - busyTries++; - return setTimeout(_rimraf, busyTries * 100, path, options, CB); + if (retryErrorCodes.has(err.code) && retries < options.maxRetries) { + retries++; + const delay = retries * options.retryDelay; + return setTimeout(_rimraf, delay, path, options, CB); } - if (err.code === 'EMFILE' && timeout < options.emfileWait) - return setTimeout(_rimraf, timeout++, path, options, CB); - // The file is already gone. if (err.code === 'ENOENT') err = null; @@ -211,7 +208,7 @@ function _rmdirSync(path, options, originalErr) { rimrafSync(join(path, child), options); }); - for (let i = 0; i < numRetries; i++) { + for (let i = 0; i < options.maxRetries + 1; i++) { try { return rmdirSync(path, options); } catch {} // Ignore errors. diff --git a/lib/internal/fs/streams.js b/lib/internal/fs/streams.js index 0d292c8a536efb..7b17eae338aa46 100644 --- a/lib/internal/fs/streams.js +++ b/lib/internal/fs/streams.js @@ -1,6 +1,13 @@ 'use strict'; -const { Math, Object } = primordials; +const { + Array, + MathMin, + NumberIsInteger, + NumberIsSafeInteger, + ObjectDefineProperty, + ObjectSetPrototypeOf, +} = primordials; const { ERR_OUT_OF_RANGE @@ -36,9 +43,9 @@ function allocNewPool(poolSize) { // Check the `this.start` and `this.end` of stream. function checkPosition(pos, name) { - if (!Number.isSafeInteger(pos)) { + if (!NumberIsSafeInteger(pos)) { validateNumber(pos, name); - if (!Number.isInteger(pos)) + if (!NumberIsInteger(pos)) throw new ERR_OUT_OF_RANGE(name, 'an integer', pos); throw new ERR_OUT_OF_RANGE(name, '>= 0 and <= 2 ** 53 - 1', pos); } @@ -109,8 +116,8 @@ function ReadStream(path, options) { } }); } -Object.setPrototypeOf(ReadStream.prototype, Readable.prototype); -Object.setPrototypeOf(ReadStream, Readable); +ObjectSetPrototypeOf(ReadStream.prototype, Readable.prototype); +ObjectSetPrototypeOf(ReadStream, Readable); const openReadFs = internalUtil.deprecate(function() { _openReadFs(this); @@ -157,13 +164,13 @@ ReadStream.prototype._read = function(n) { // in the thread pool another read() finishes up the pool, and // allocates a new one. const thisPool = pool; - let toRead = Math.min(pool.length - pool.used, n); + let toRead = MathMin(pool.length - pool.used, n); const start = pool.used; if (this.pos !== undefined) - toRead = Math.min(this.end - this.pos + 1, toRead); + toRead = MathMin(this.end - this.pos + 1, toRead); else - toRead = Math.min(this.end - this.bytesRead + 1, toRead); + toRead = MathMin(this.end - this.bytesRead + 1, toRead); // Already read everything we were supposed to read! // treat as EOF. @@ -235,7 +242,7 @@ ReadStream.prototype.close = function(cb) { this.destroy(null, cb); }; -Object.defineProperty(ReadStream.prototype, 'pending', { +ObjectDefineProperty(ReadStream.prototype, 'pending', { get() { return this.fd === null; }, configurable: true }); @@ -280,8 +287,8 @@ function WriteStream(path, options) { if (typeof this.fd !== 'number') _openWriteFs(this); } -Object.setPrototypeOf(WriteStream.prototype, Writable.prototype); -Object.setPrototypeOf(WriteStream, Writable); +ObjectSetPrototypeOf(WriteStream.prototype, Writable.prototype); +ObjectSetPrototypeOf(WriteStream, Writable); WriteStream.prototype._final = function(callback) { if (typeof this.fd !== 'number') { @@ -360,7 +367,7 @@ WriteStream.prototype._writev = function(data, cb) { const chunks = new Array(len); let size = 0; - for (var i = 0; i < len; i++) { + for (let i = 0; i < len; i++) { const chunk = data[i].chunk; chunks[i] = chunk; @@ -406,7 +413,7 @@ WriteStream.prototype.close = function(cb) { // There is no shutdown() for files. WriteStream.prototype.destroySoon = WriteStream.prototype.end; -Object.defineProperty(WriteStream.prototype, 'pending', { +ObjectDefineProperty(WriteStream.prototype, 'pending', { get() { return this.fd === null; }, configurable: true }); diff --git a/lib/internal/fs/sync_write_stream.js b/lib/internal/fs/sync_write_stream.js index 522bfc829df303..7d1209ba2decb4 100644 --- a/lib/internal/fs/sync_write_stream.js +++ b/lib/internal/fs/sync_write_stream.js @@ -1,6 +1,8 @@ 'use strict'; -const { Object } = primordials; +const { + ObjectSetPrototypeOf, +} = primordials; const { Writable } = require('stream'); const { closeSync, writeSync } = require('fs'); @@ -15,8 +17,8 @@ function SyncWriteStream(fd, options) { this.autoClose = options.autoClose === undefined ? true : options.autoClose; } -Object.setPrototypeOf(SyncWriteStream.prototype, Writable.prototype); -Object.setPrototypeOf(SyncWriteStream, Writable); +ObjectSetPrototypeOf(SyncWriteStream.prototype, Writable.prototype); +ObjectSetPrototypeOf(SyncWriteStream, Writable); SyncWriteStream.prototype._write = function(chunk, encoding, cb) { writeSync(this.fd, chunk, 0, chunk.length); diff --git a/lib/internal/fs/utils.js b/lib/internal/fs/utils.js index 3324ec5080e178..951030c5a50bd1 100644 --- a/lib/internal/fs/utils.js +++ b/lib/internal/fs/utils.js @@ -1,6 +1,13 @@ 'use strict'; -const { Object, Reflect } = primordials; +const { + ArrayIsArray, + DateNow, + Number, + NumberIsFinite, + ObjectSetPrototypeOf, + ReflectOwnKeys, +} = primordials; const { Buffer } = require('buffer'); const { @@ -118,7 +125,7 @@ class DirentFromStats extends Dirent { } } -for (const name of Reflect.ownKeys(Dirent.prototype)) { +for (const name of ReflectOwnKeys(Dirent.prototype)) { if (name === 'constructor') { continue; } @@ -353,8 +360,8 @@ function BigIntStats(dev, mode, nlink, uid, gid, rdev, blksize, this.birthtime = dateFromMs(this.birthtimeMs); } -Object.setPrototypeOf(BigIntStats.prototype, StatsBase.prototype); -Object.setPrototypeOf(BigIntStats, StatsBase); +ObjectSetPrototypeOf(BigIntStats.prototype, StatsBase.prototype); +ObjectSetPrototypeOf(BigIntStats, StatsBase); BigIntStats.prototype._checkModeProperty = function(property) { if (isWindows && (property === S_IFIFO || property === S_IFBLK || @@ -379,8 +386,8 @@ function Stats(dev, mode, nlink, uid, gid, rdev, blksize, this.birthtime = dateFromMs(birthtimeMs); } -Object.setPrototypeOf(Stats.prototype, StatsBase.prototype); -Object.setPrototypeOf(Stats, StatsBase); +ObjectSetPrototypeOf(Stats.prototype, StatsBase.prototype); +ObjectSetPrototypeOf(Stats, StatsBase); // HACK: Workaround for https://github.com/standard-things/esm/issues/821. // TODO(ronag): Remove this as soon as `esm` publishes a fixed version. @@ -481,9 +488,9 @@ function toUnixTimestamp(time, name = 'time') { if (typeof time === 'string' && +time == time) { return +time; } - if (Number.isFinite(time)) { + if (NumberIsFinite(time)) { if (time < 0) { - return Date.now() / 1000; + return DateNow() / 1000; } return time; } @@ -538,7 +545,7 @@ const getValidatedPath = hideStackFrames((fileURLOrPath, propName = 'path') => { }); const validateBufferArray = hideStackFrames((buffers, propName = 'buffers') => { - if (!Array.isArray(buffers)) + if (!ArrayIsArray(buffers)) throw new ERR_INVALID_ARG_TYPE(propName, 'ArrayBufferView[]', buffers); for (let i = 0; i < buffers.length; i++) { @@ -562,8 +569,8 @@ function warnOnNonPortableTemplate(template) { } const defaultRmdirOptions = { - emfileWait: 1000, - maxBusyTries: 3, + retryDelay: 100, + maxRetries: 0, recursive: false, }; @@ -578,8 +585,8 @@ const validateRmdirOptions = hideStackFrames((options) => { if (typeof options.recursive !== 'boolean') throw new ERR_INVALID_ARG_TYPE('recursive', 'boolean', options.recursive); - validateInt32(options.emfileWait, 'emfileWait', 0); - validateUint32(options.maxBusyTries, 'maxBusyTries'); + validateInt32(options.retryDelay, 'retryDelay', 0); + validateUint32(options.maxRetries, 'maxRetries'); return options; }); diff --git a/lib/internal/fs/watchers.js b/lib/internal/fs/watchers.js index c974f40aae104f..7454ada7671140 100644 --- a/lib/internal/fs/watchers.js +++ b/lib/internal/fs/watchers.js @@ -1,6 +1,9 @@ 'use strict'; -const { Object } = primordials; +const { + ObjectDefineProperty, + ObjectSetPrototypeOf, +} = primordials; const errors = require('internal/errors'); const { @@ -38,8 +41,8 @@ function StatWatcher(bigint) { this[kOldStatus] = -1; this[kUseBigint] = bigint; } -Object.setPrototypeOf(StatWatcher.prototype, EventEmitter.prototype); -Object.setPrototypeOf(StatWatcher, EventEmitter); +ObjectSetPrototypeOf(StatWatcher.prototype, EventEmitter.prototype); +ObjectSetPrototypeOf(StatWatcher, EventEmitter); function onchange(newStatus, stats) { const self = this[owner_symbol]; @@ -134,8 +137,8 @@ function FSWatcher() { } }; } -Object.setPrototypeOf(FSWatcher.prototype, EventEmitter.prototype); -Object.setPrototypeOf(FSWatcher, EventEmitter); +ObjectSetPrototypeOf(FSWatcher.prototype, EventEmitter.prototype); +ObjectSetPrototypeOf(FSWatcher, EventEmitter); // At the moment if filename is undefined, we // 1. Throw an Error if it's the first time Symbol('kFSWatchStart') is called @@ -201,7 +204,7 @@ function emitCloseNT(self) { // Legacy alias on the C++ wrapper object. This is not public API, so we may // want to runtime-deprecate it at some point. There's no hurry, though. -Object.defineProperty(FSEvent.prototype, 'owner', { +ObjectDefineProperty(FSEvent.prototype, 'owner', { get() { return this[owner_symbol]; }, set(v) { return this[owner_symbol] = v; } }); diff --git a/lib/internal/http.js b/lib/internal/http.js index fcfd2c91f22d33..09294888be5e70 100644 --- a/lib/internal/http.js +++ b/lib/internal/http.js @@ -3,8 +3,8 @@ const { setUnrefTimeout } = require('internal/timers'); const { PerformanceEntry, notify } = internalBinding('performance'); -var nowCache; -var utcCache; +let nowCache; +let utcCache; function nowDate() { if (!nowCache) cache(); diff --git a/lib/internal/http2/compat.js b/lib/internal/http2/compat.js index 5bc64504cd3d74..6a55b4edd19311 100644 --- a/lib/internal/http2/compat.js +++ b/lib/internal/http2/compat.js @@ -1,6 +1,14 @@ 'use strict'; -const { Object, ObjectPrototype, Reflect } = primordials; +const { + ArrayIsArray, + Boolean, + ObjectAssign, + ObjectCreate, + ObjectKeys, + ObjectPrototypeHasOwnProperty, + ReflectGetPrototypeOf, +} = primordials; const assert = require('internal/assert'); const Stream = require('stream'); @@ -120,7 +128,7 @@ function onStreamData(chunk) { function onStreamTrailers(trailers, flags, rawTrailers) { const request = this[kRequest]; if (request !== undefined) { - Object.assign(request[kTrailers], trailers); + ObjectAssign(request[kTrailers], trailers); request[kRawTrailers].push(...rawTrailers); } } @@ -206,8 +214,8 @@ const proxySocketHandler = { }, getPrototypeOf(stream) { if (stream.session !== undefined) - return Reflect.getPrototypeOf(stream.session[kSocket]); - return Reflect.getPrototypeOf(stream); + return ReflectGetPrototypeOf(stream.session[kSocket]); + return ReflectGetPrototypeOf(stream); }, set(stream, prop, value) { switch (prop) { @@ -434,8 +442,8 @@ class Http2ServerResponse extends Stream { sendDate: true, statusCode: HTTP_STATUS_OK, }; - this[kHeaders] = Object.create(null); - this[kTrailers] = Object.create(null); + this[kHeaders] = ObjectCreate(null); + this[kTrailers] = ObjectCreate(null); this[kStream] = stream; stream[kProxySocket] = null; stream[kResponse] = this; @@ -524,9 +532,9 @@ class Http2ServerResponse extends Stream { } addTrailers(headers) { - const keys = Object.keys(headers); + const keys = ObjectKeys(headers); let key = ''; - for (var i = 0; i < keys.length; i++) { + for (let i = 0; i < keys.length; i++) { key = keys[i]; this.setTrailer(key, headers[key]); } @@ -539,7 +547,7 @@ class Http2ServerResponse extends Stream { } getHeaderNames() { - return Object.keys(this[kHeaders]); + return ObjectKeys(this[kHeaders]); } getHeaders() { @@ -549,7 +557,7 @@ class Http2ServerResponse extends Stream { hasHeader(name) { validateString(name, 'name'); name = name.trim().toLowerCase(); - return ObjectPrototype.hasOwnProperty(this[kHeaders], name); + return ObjectPrototypeHasOwnProperty(this[kHeaders], name); } removeHeader(name) { @@ -610,14 +618,14 @@ class Http2ServerResponse extends Stream { if (headers === undefined && typeof statusMessage === 'object') headers = statusMessage; - var i; - if (Array.isArray(headers)) { + let i; + if (ArrayIsArray(headers)) { for (i = 0; i < headers.length; i++) { const header = headers[i]; this[kSetHeader](header[0], header[1]); } } else if (typeof headers === 'object') { - const keys = Object.keys(headers); + const keys = ObjectKeys(headers); let key = ''; for (i = 0; i < keys.length; i++) { key = keys[i]; diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js index fded7067b5b3dd..c4d12f9205151c 100644 --- a/lib/internal/http2/core.js +++ b/lib/internal/http2/core.js @@ -2,7 +2,16 @@ /* eslint-disable no-use-before-define */ -const { Math, Object, Reflect } = primordials; +const { + ArrayFrom, + ArrayIsArray, + MathMin, + ObjectAssign, + ObjectCreate, + ObjectDefineProperty, + ObjectPrototypeHasOwnProperty, + ReflectGetPrototypeOf, +} = primordials; const { assertCrypto, @@ -81,7 +90,11 @@ const { }, hideStackFrames } = require('internal/errors'); -const { validateNumber, validateString } = require('internal/validators'); +const { validateNumber, + validateString, + validateUint32, + isUint32, +} = require('internal/validators'); const fsPromisesInternal = require('internal/fs/promises'); const { utcDate } = require('internal/http'); const { onServerStream, @@ -124,8 +137,6 @@ const { kTimeout } = require('internal/timers'); const { isArrayBufferView } = require('internal/util/types'); const { format } = require('internal/util/inspect'); -const hasOwnProperty = Object.prototype.hasOwnProperty; - const { FileHandle } = internalBinding('fs'); const binding = internalBinding('http2'); const { @@ -199,6 +210,8 @@ const { kBitfield, kSessionPriorityListenerCount, kSessionFrameErrorListenerCount, + kSessionMaxInvalidFrames, + kSessionMaxRejectedStreams, kSessionUint8FieldCount, kSessionHasRemoteSettingsListeners, kSessionRemoteSettingsIsUpToDate, @@ -582,7 +595,7 @@ function onOrigin(origins) { if (!session.encrypted || session.destroyed) return undefined; const originSet = initOriginSet(session); - for (var n = 0; n < origins.length; n++) + for (let n = 0; n < origins.length; n++) originSet.add(origins[n]); session.emit('origin', origins); } @@ -813,7 +826,7 @@ const proxySocketHandler = { const socket = session[kSocket]; if (socket === undefined) throw new ERR_HTTP2_SOCKET_UNBOUND(); - return Reflect.getPrototypeOf(socket); + return ReflectGetPrototypeOf(socket); }, set(session, prop, value) { switch (prop) { @@ -937,13 +950,25 @@ function setupHandle(socket, type, options) { this[kEncrypted] = false; } + if (isUint32(options.maxSessionInvalidFrames)) { + const uint32 = new Uint32Array( + this[kNativeFields].buffer, kSessionMaxInvalidFrames, 1); + uint32[0] = options.maxSessionInvalidFrames; + } + + if (isUint32(options.maxSessionRejectedStreams)) { + const uint32 = new Uint32Array( + this[kNativeFields].buffer, kSessionMaxRejectedStreams, 1); + uint32[0] = options.maxSessionRejectedStreams; + } + const settings = typeof options.settings === 'object' ? options.settings : {}; this.settings(settings); if (type === NGHTTP2_SESSION_SERVER && - Array.isArray(options.origins)) { + ArrayIsArray(options.origins)) { this.origin(...options.origins); } @@ -1100,7 +1125,7 @@ class Http2Session extends EventEmitter { get originSet() { if (!this.encrypted || this.destroyed) return undefined; - return Array.from(initOriginSet(this)); + return ArrayFrom(initOriginSet(this)); } // True if the Http2Session is still waiting for the socket to connect @@ -1496,7 +1521,7 @@ class ServerHttp2Session extends Http2Session { let arr = ''; let len = 0; const count = origins.length; - for (var i = 0; i < count; i++) { + for (let i = 0; i < count; i++) { let origin = origins[i]; if (typeof origin === 'string') { origin = (new URL(origin)).origin; @@ -1545,7 +1570,7 @@ class ClientHttp2Session extends Http2Session { assertIsObject(headers, 'headers'); assertIsObject(options, 'options'); - headers = Object.assign(Object.create(null), headers); + headers = ObjectAssign(ObjectCreate(null), headers); options = { ...options }; if (headers[HTTP2_HEADER_METHOD] === undefined) @@ -1992,7 +2017,7 @@ class Http2Stream extends Duplex { throw new ERR_HTTP2_TRAILERS_NOT_READY(); assertIsObject(headers, 'headers'); - headers = Object.assign(Object.create(null), headers); + headers = ObjectAssign(ObjectCreate(null), headers); debugStreamObj(this, 'sending trailers'); @@ -2123,13 +2148,12 @@ class Http2Stream extends Duplex { function processHeaders(oldHeaders) { assertIsObject(oldHeaders, 'headers'); - const headers = Object.create(null); + const headers = ObjectCreate(null); if (oldHeaders !== null && oldHeaders !== undefined) { - const hop = hasOwnProperty.bind(oldHeaders); // This loop is here for performance reason. Do not change. - for (var key in oldHeaders) { - if (hop(key)) { + for (const key in oldHeaders) { + if (ObjectPrototypeHasOwnProperty(oldHeaders, key)) { headers[key] = oldHeaders[key]; } } @@ -2309,8 +2333,8 @@ function doSendFileFD(session, options, fd, headers, streamOptions, err, stat) { if (stat.isFile()) { statOptions.length = statOptions.length < 0 ? stat.size - (+statOptions.offset) : - Math.min(stat.size - (+statOptions.offset), - statOptions.length); + MathMin(stat.size - (+statOptions.offset), + statOptions.length); headers[HTTP2_HEADER_CONTENT_LENGTH] = statOptions.length; } @@ -2387,7 +2411,7 @@ class ServerHttp2Stream extends Http2Stream { options.endStream = !!options.endStream; assertIsObject(headers, 'headers'); - headers = Object.assign(Object.create(null), headers); + headers = ObjectAssign(ObjectCreate(null), headers); if (headers[HTTP2_HEADER_METHOD] === undefined) headers[HTTP2_HEADER_METHOD] = HTTP2_METHOD_GET; @@ -2619,7 +2643,7 @@ class ServerHttp2Stream extends Http2Stream { throw new ERR_HTTP2_HEADERS_AFTER_RESPOND(); assertIsObject(headers, 'headers'); - headers = Object.assign(Object.create(null), headers); + headers = ObjectAssign(ObjectCreate(null), headers); debugStreamObj(this, 'sending additional headers'); @@ -2669,8 +2693,8 @@ const setTimeout = { writable: true, value: setStreamTimeout }; -Object.defineProperty(Http2Stream.prototype, 'setTimeout', setTimeout); -Object.defineProperty(Http2Session.prototype, 'setTimeout', setTimeout); +ObjectDefineProperty(Http2Stream.prototype, 'setTimeout', setTimeout); +ObjectDefineProperty(Http2Session.prototype, 'setTimeout', setTimeout); // When the socket emits an error, destroy the associated Http2Session and @@ -2768,6 +2792,16 @@ function initializeOptions(options) { assertIsObject(options.settings, 'options.settings'); options.settings = { ...options.settings }; + if (options.maxSessionInvalidFrames !== undefined) + validateUint32(options.maxSessionInvalidFrames, 'maxSessionInvalidFrames'); + + if (options.maxSessionRejectedStreams !== undefined) { + validateUint32( + options.maxSessionRejectedStreams, + 'maxSessionRejectedStreams' + ); + } + // Used only with allowHTTP1 options.Http1IncomingMessage = options.Http1IncomingMessage || http.IncomingMessage; @@ -2923,7 +2957,7 @@ function connect(authority, options, listener) { } // Support util.promisify -Object.defineProperty(connect, promisify.custom, { +ObjectDefineProperty(connect, promisify.custom, { value: (authority, options) => { return new Promise((resolve) => { const server = connect(authority, options, () => resolve(server)); diff --git a/lib/internal/http2/util.js b/lib/internal/http2/util.js index 9cc2a30897b6ae..d97ce2f13f0306 100644 --- a/lib/internal/http2/util.js +++ b/lib/internal/http2/util.js @@ -1,6 +1,12 @@ 'use strict'; -const { Math, Object } = primordials; +const { + ArrayIsArray, + MathMax, + Number, + ObjectCreate, + ObjectKeys, +} = primordials; const binding = internalBinding('http2'); const { @@ -197,7 +203,7 @@ const IDX_OPTIONS_MAX_SESSION_MEMORY = 8; const IDX_OPTIONS_FLAGS = 9; function updateOptionsBuffer(options) { - var flags = 0; + let flags = 0; if (typeof options.maxDeflateDynamicTableSize === 'number') { flags |= (1 << IDX_OPTIONS_MAX_DEFLATE_DYNAMIC_TABLE_SIZE); optionsBuffer[IDX_OPTIONS_MAX_DEFLATE_DYNAMIC_TABLE_SIZE] = @@ -236,12 +242,12 @@ function updateOptionsBuffer(options) { if (typeof options.maxOutstandingSettings === 'number') { flags |= (1 << IDX_OPTIONS_MAX_OUTSTANDING_SETTINGS); optionsBuffer[IDX_OPTIONS_MAX_OUTSTANDING_SETTINGS] = - Math.max(1, options.maxOutstandingSettings); + MathMax(1, options.maxOutstandingSettings); } if (typeof options.maxSessionMemory === 'number') { flags |= (1 << IDX_OPTIONS_MAX_SESSION_MEMORY); optionsBuffer[IDX_OPTIONS_MAX_SESSION_MEMORY] = - Math.max(1, options.maxSessionMemory); + MathMax(1, options.maxSessionMemory); } optionsBuffer[IDX_OPTIONS_FLAGS] = flags; } @@ -249,7 +255,7 @@ function updateOptionsBuffer(options) { function getDefaultSettings() { settingsBuffer[IDX_SETTINGS_FLAGS] = 0; binding.refreshDefaultSettings(); - const holder = Object.create(null); + const holder = ObjectCreate(null); const flags = settingsBuffer[IDX_SETTINGS_FLAGS]; @@ -318,7 +324,7 @@ function getSettings(session, remote) { } function updateSettingsBuffer(settings) { - var flags = 0; + let flags = 0; if (typeof settings.headerTableSize === 'number') { flags |= (1 << IDX_SETTINGS_HEADER_TABLE_SIZE); settingsBuffer[IDX_SETTINGS_HEADER_TABLE_SIZE] = @@ -430,7 +436,7 @@ function mapToHeaders(map, assertValuePseudoHeader = assertValidPseudoHeader) { let ret = ''; let count = 0; - const keys = Object.keys(map); + const keys = ObjectKeys(map); const singles = new Set(); let i; let isArray; @@ -445,7 +451,7 @@ function mapToHeaders(map, continue; key = key.toLowerCase(); isSingleValueHeader = kSingleValueHeaders.has(key); - isArray = Array.isArray(value); + isArray = ArrayIsArray(value); if (isArray) { switch (value.length) { case 0: @@ -509,7 +515,7 @@ const assertIsObject = hideStackFrames((value, name, types) => { if (value !== undefined && (value === null || typeof value !== 'object' || - Array.isArray(value))) { + ArrayIsArray(value))) { throw new ERR_INVALID_ARG_TYPE(name, types || 'Object', value); } }); @@ -525,13 +531,13 @@ const assertWithinRange = hideStackFrames( ); function toHeaderObject(headers) { - const obj = Object.create(null); + const obj = ObjectCreate(null); for (var n = 0; n < headers.length; n = n + 2) { - var name = headers[n]; - var value = headers[n + 1]; + const name = headers[n]; + let value = headers[n + 1]; if (name === HTTP2_HEADER_STATUS) value |= 0; - var existing = obj[name]; + const existing = obj[name]; if (existing === undefined) { obj[name] = name === HTTP2_HEADER_SET_COOKIE ? [value] : value; } else if (!kSingleValueHeaders.has(name)) { diff --git a/lib/internal/js_stream_socket.js b/lib/internal/js_stream_socket.js index ebe90cccc75f45..7c35a55b1c9455 100644 --- a/lib/internal/js_stream_socket.js +++ b/lib/internal/js_stream_socket.js @@ -14,9 +14,13 @@ const kCurrentShutdownRequest = Symbol('kCurrentShutdownRequest'); const kPendingShutdownRequest = Symbol('kPendingShutdownRequest'); function isClosing() { return this[owner_symbol].isClosing(); } + function onreadstart() { return this[owner_symbol].readStart(); } + function onreadstop() { return this[owner_symbol].readStop(); } + function onshutdown(req) { return this[owner_symbol].doShutdown(req); } + function onwrite(req, bufs) { return this[owner_symbol].doWrite(req, bufs); } /* This class serves as a wrapper for when the C++ side of Node wants access @@ -157,6 +161,7 @@ class JSStreamSocket extends Socket { let pending = bufs.length; this.stream.cork(); + // Use `var` over `let` for performance optimization. for (var i = 0; i < bufs.length; ++i) this.stream.write(bufs[i], done); this.stream.uncork(); diff --git a/lib/internal/main/worker_thread.js b/lib/internal/main/worker_thread.js index 13d7e8de0ec75c..95fa3026fda90b 100644 --- a/lib/internal/main/worker_thread.js +++ b/lib/internal/main/worker_thread.js @@ -3,7 +3,9 @@ // In worker threads, execute the script sent through the // message port. -const { Object } = primordials; +const { + ObjectDefineProperty, +} = primordials; const { patchProcessObject, @@ -72,12 +74,12 @@ const port = getEnvMessagePort(); // related IPC properties as unavailable. if (process.env.NODE_CHANNEL_FD) { const workerThreadSetup = require('internal/process/worker_thread_only'); - Object.defineProperty(process, 'channel', { + ObjectDefineProperty(process, 'channel', { enumerable: false, get: workerThreadSetup.unavailable('process.channel') }); - Object.defineProperty(process, 'connected', { + ObjectDefineProperty(process, 'connected', { enumerable: false, get: workerThreadSetup.unavailable('process.connected') }); diff --git a/lib/internal/modules/cjs/helpers.js b/lib/internal/modules/cjs/helpers.js index b75f67c0a4ddad..2d73219a77772c 100644 --- a/lib/internal/modules/cjs/helpers.js +++ b/lib/internal/modules/cjs/helpers.js @@ -1,6 +1,9 @@ 'use strict'; -const { Object, SafeMap } = primordials; +const { + ObjectDefineProperty, + SafeMap, +} = primordials; const { ERR_MANIFEST_DEPENDENCY_MISSING, ERR_UNKNOWN_BUILTIN_MODULE @@ -114,6 +117,11 @@ const builtinLibs = [ 'v8', 'vm', 'worker_threads', 'zlib' ]; +if (internalBinding('config').experimentalWasi) { + builtinLibs.push('wasi'); + builtinLibs.sort(); +} + if (typeof internalBinding('inspector').open === 'function') { builtinLibs.push('inspector'); builtinLibs.sort(); @@ -135,14 +143,14 @@ function addBuiltinLibsToObject(object) { object[name] = val; }; - Object.defineProperty(object, name, { + ObjectDefineProperty(object, name, { get: () => { const lib = require(name); // Disable the current getter/setter and set up a new // non-enumerable property. delete object[name]; - Object.defineProperty(object, name, { + ObjectDefineProperty(object, name, { get: () => lib, set: setReal, configurable: true, diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js index ae9e839523b754..d962bc2a4cec30 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js @@ -22,12 +22,19 @@ 'use strict'; const { - JSON, - Object, - ObjectPrototype, - Reflect, + ArrayIsArray, + JSONParse, + ObjectCreate, + ObjectDefineProperty, + ObjectFreeze, + ObjectKeys, + ObjectPrototypeHasOwnProperty, + ReflectSet, SafeMap, - StringPrototype, + StringPrototypeIndexOf, + StringPrototypeMatch, + StringPrototypeSlice, + StringPrototypeStartsWith, } = primordials; const { NativeModule } = require('internal/bootstrap/loaders'); @@ -93,7 +100,7 @@ const { const isWindows = process.platform === 'win32'; -const relativeResolveCache = Object.create(null); +const relativeResolveCache = ObjectCreate(null); let requireDepth = 0; let statCache = null; @@ -155,12 +162,12 @@ for (const [id, mod] of NativeModule.map) { } } -Object.freeze(builtinModules); +ObjectFreeze(builtinModules); Module.builtinModules = builtinModules; -Module._cache = Object.create(null); -Module._pathCache = Object.create(null); -Module._extensions = Object.create(null); +Module._cache = ObjectCreate(null); +Module._pathCache = ObjectCreate(null); +Module._extensions = ObjectCreate(null); let modulePaths = []; Module.globalPaths = []; @@ -179,16 +186,16 @@ const wrapper = [ let wrapperProxy = new Proxy(wrapper, { set(target, property, value, receiver) { patched = true; - return Reflect.set(target, property, value, receiver); + return ReflectSet(target, property, value, receiver); }, defineProperty(target, property, descriptor) { patched = true; - return Object.defineProperty(target, property, descriptor); + return ObjectDefineProperty(target, property, descriptor); } }); -Object.defineProperty(Module, 'wrap', { +ObjectDefineProperty(Module, 'wrap', { get() { return wrap; }, @@ -199,7 +206,7 @@ Object.defineProperty(Module, 'wrap', { } }); -Object.defineProperty(Module, 'wrapper', { +ObjectDefineProperty(Module, 'wrapper', { get() { return wrapperProxy; }, @@ -244,7 +251,7 @@ function readPackage(requestPath) { } try { - const parsed = JSON.parse(json); + const parsed = JSONParse(json); const filtered = { name: parsed.name, main: parsed.main, @@ -406,7 +413,7 @@ function resolveBasePath(basePath, exts, isMain, trailingSlash, request) { if (!filename) { // Try it with each of the extensions if (exts === undefined) - exts = Object.keys(Module._extensions); + exts = ObjectKeys(Module._extensions); filename = tryExtensions(basePath, exts, isMain); } } @@ -414,7 +421,7 @@ function resolveBasePath(basePath, exts, isMain, trailingSlash, request) { if (!filename && rc === 1) { // Directory. // try it with each of the extensions at "index" if (exts === undefined) - exts = Object.keys(Module._extensions); + exts = ObjectKeys(Module._extensions); filename = tryPackage(basePath, exts, isMain, request); } @@ -433,14 +440,14 @@ function trySelf(paths, exts, isMain, trailingSlash, request) { let expansion; if (request === pkg.name) { expansion = ''; - } else if (StringPrototype.startsWith(request, `${pkg.name}/`)) { - expansion = StringPrototype.slice(request, pkg.name.length); + } else if (StringPrototypeStartsWith(request, `${pkg.name}/`)) { + expansion = StringPrototypeSlice(request, pkg.name.length); } else { return false; } if (exts === undefined) - exts = Object.keys(Module._extensions); + exts = ObjectKeys(Module._extensions); if (expansion) { // Use exports @@ -455,13 +462,13 @@ function trySelf(paths, exts, isMain, trailingSlash, request) { function isConditionalDotExportSugar(exports, basePath) { if (typeof exports === 'string') return true; - if (Array.isArray(exports)) + if (ArrayIsArray(exports)) return true; if (typeof exports !== 'object') return false; let isConditional = false; let firstCheck = true; - for (const key of Object.keys(exports)) { + for (const key of ObjectKeys(exports)) { const curIsConditional = key[0] !== '.'; if (firstCheck) { firstCheck = false; @@ -487,7 +494,7 @@ function applyExports(basePath, expansion) { pkgExports = { '.': pkgExports }; if (typeof pkgExports === 'object') { - if (ObjectPrototype.hasOwnProperty(pkgExports, mappingKey)) { + if (ObjectPrototypeHasOwnProperty(pkgExports, mappingKey)) { const mapping = pkgExports[mappingKey]; return resolveExportsTarget(pathToFileURL(basePath + '/'), mapping, '', basePath, mappingKey); @@ -498,17 +505,17 @@ function applyExports(basePath, expansion) { return basePath; let dirMatch = ''; - for (const candidateKey of Object.keys(pkgExports)) { + for (const candidateKey of ObjectKeys(pkgExports)) { if (candidateKey[candidateKey.length - 1] !== '/') continue; if (candidateKey.length > dirMatch.length && - StringPrototype.startsWith(mappingKey, candidateKey)) { + StringPrototypeStartsWith(mappingKey, candidateKey)) { dirMatch = candidateKey; } } if (dirMatch !== '') { const mapping = pkgExports[dirMatch]; - const subpath = StringPrototype.slice(mappingKey, dirMatch.length); + const subpath = StringPrototypeSlice(mappingKey, dirMatch.length); return resolveExportsTarget(pathToFileURL(basePath + '/'), mapping, subpath, basePath, mappingKey); } @@ -532,7 +539,7 @@ function resolveExports(nmPath, request, absoluteRequest) { // The implementation's behavior is meant to mirror resolution in ESM. if (!absoluteRequest) { const [, name, expansion = ''] = - StringPrototype.match(request, EXPORTS_PATTERN) || []; + StringPrototypeMatch(request, EXPORTS_PATTERN) || []; if (!name) { return path.resolve(nmPath, request); } @@ -551,21 +558,21 @@ function resolveExportsTarget(pkgPath, target, subpath, basePath, mappingKey) { const resolvedTarget = new URL(target, pkgPath); const pkgPathPath = pkgPath.pathname; const resolvedTargetPath = resolvedTarget.pathname; - if (StringPrototype.startsWith(resolvedTargetPath, pkgPathPath) && - StringPrototype.indexOf(resolvedTargetPath, '/node_modules/', - pkgPathPath.length - 1) === -1) { + if (StringPrototypeStartsWith(resolvedTargetPath, pkgPathPath) && + StringPrototypeIndexOf(resolvedTargetPath, '/node_modules/', + pkgPathPath.length - 1) === -1) { const resolved = new URL(subpath, resolvedTarget); const resolvedPath = resolved.pathname; - if (StringPrototype.startsWith(resolvedPath, resolvedTargetPath) && - StringPrototype.indexOf(resolvedPath, '/node_modules/', - pkgPathPath.length - 1) === -1) { + if (StringPrototypeStartsWith(resolvedPath, resolvedTargetPath) && + StringPrototypeIndexOf(resolvedPath, '/node_modules/', + pkgPathPath.length - 1) === -1) { return fileURLToPath(resolved); } } } - } else if (Array.isArray(target)) { + } else if (ArrayIsArray(target)) { for (const targetValue of target) { - if (Array.isArray(targetValue)) continue; + if (ArrayIsArray(targetValue)) continue; try { return resolveExportsTarget(pkgPath, targetValue, subpath, basePath, mappingKey); @@ -575,7 +582,7 @@ function resolveExportsTarget(pkgPath, target, subpath, basePath, mappingKey) { } } else if (typeof target === 'object' && target !== null) { if (experimentalConditionalExports && - ObjectPrototype.hasOwnProperty(target, 'require')) { + ObjectPrototypeHasOwnProperty(target, 'require')) { try { return resolveExportsTarget(pkgPath, target.require, subpath, basePath, mappingKey); @@ -584,7 +591,7 @@ function resolveExportsTarget(pkgPath, target, subpath, basePath, mappingKey) { } } if (experimentalConditionalExports && - ObjectPrototype.hasOwnProperty(target, 'node')) { + ObjectPrototypeHasOwnProperty(target, 'node')) { try { return resolveExportsTarget(pkgPath, target.node, subpath, basePath, mappingKey); @@ -592,7 +599,7 @@ function resolveExportsTarget(pkgPath, target, subpath, basePath, mappingKey) { if (e.code !== 'MODULE_NOT_FOUND') throw e; } } - if (ObjectPrototype.hasOwnProperty(target, 'default')) { + if (ObjectPrototypeHasOwnProperty(target, 'default')) { try { return resolveExportsTarget(pkgPath, target.default, subpath, basePath, mappingKey); @@ -670,7 +677,7 @@ Module._findPath = function(request, paths, isMain) { if (!filename) { // Try it with each of the extensions if (exts === undefined) - exts = Object.keys(Module._extensions); + exts = ObjectKeys(Module._extensions); filename = tryExtensions(basePath, exts, isMain); } } @@ -678,7 +685,7 @@ Module._findPath = function(request, paths, isMain) { if (!filename && rc === 1) { // Directory. // try it with each of the extensions at "index" if (exts === undefined) - exts = Object.keys(Module._extensions); + exts = ObjectKeys(Module._extensions); filename = tryPackage(basePath, exts, isMain, request); } @@ -904,7 +911,7 @@ Module._resolveFilename = function(request, parent, isMain, options) { let paths; if (typeof options === 'object' && options !== null) { - if (Array.isArray(options.paths)) { + if (ArrayIsArray(options.paths)) { const isRelative = request.startsWith('./') || request.startsWith('../') || ((isWindows && request.startsWith('.\\')) || @@ -1171,7 +1178,7 @@ Module._extensions['.json'] = function(module, filename) { } try { - module.exports = JSON.parse(stripBOM(content)); + module.exports = JSONParse(stripBOM(content)); } catch (err) { err.message = filename + ': ' + err.message; throw err; @@ -1267,7 +1274,7 @@ Module._initPaths = function() { }; Module._preloadModules = function(requests) { - if (!Array.isArray(requests)) + if (!ArrayIsArray(requests)) return; // Preloaded modules have a dummy parent module which is deemed to exist diff --git a/lib/internal/modules/esm/create_dynamic_module.js b/lib/internal/modules/esm/create_dynamic_module.js index 804420c92010ed..f8e00fd2ad1889 100644 --- a/lib/internal/modules/esm/create_dynamic_module.js +++ b/lib/internal/modules/esm/create_dynamic_module.js @@ -1,11 +1,16 @@ 'use strict'; -const { ArrayPrototype, JSON, Object } = primordials; +const { + ArrayPrototypeJoin, + ArrayPrototypeMap, + JSONStringify, + ObjectCreate, +} = primordials; const debug = require('internal/util/debuglog').debuglog('esm'); function createImport(impt, index) { - const imptPath = JSON.stringify(impt); + const imptPath = JSONStringify(impt); return `import * as $import_${index} from ${imptPath}; import.meta.imports[${imptPath}] = $import_${index};`; } @@ -23,8 +28,8 @@ import.meta.exports.${name} = { const createDynamicModule = (imports, exports, url = '', evaluate) => { debug('creating ESM facade for %s with exports: %j', url, exports); const source = ` -${ArrayPrototype.join(ArrayPrototype.map(imports, createImport), '\n')} -${ArrayPrototype.join(ArrayPrototype.map(exports, createExport), '\n')} +${ArrayPrototypeJoin(ArrayPrototypeMap(imports, createImport), '\n')} +${ArrayPrototypeJoin(ArrayPrototypeMap(exports, createExport), '\n')} import.meta.done(); `; const { ModuleWrap, callbackMap } = internalBinding('module_wrap'); @@ -32,12 +37,12 @@ import.meta.done(); const readyfns = new Set(); const reflect = { - exports: Object.create(null), + exports: ObjectCreate(null), onReady: (cb) => { readyfns.add(cb); }, }; if (imports.length) - reflect.imports = Object.create(null); + reflect.imports = ObjectCreate(null); callbackMap.set(m, { initializeImportMeta: (meta, wrap) => { diff --git a/lib/internal/modules/esm/default_resolve.js b/lib/internal/modules/esm/default_resolve.js index 5271c6a0fe0a02..169c6f35694f24 100644 --- a/lib/internal/modules/esm/default_resolve.js +++ b/lib/internal/modules/esm/default_resolve.js @@ -1,5 +1,9 @@ 'use strict'; +const { + SafeMap, +} = primordials; + const internalFS = require('internal/fs/utils'); const { NativeModule } = require('internal/bootstrap/loaders'); const { extname } = require('path'); @@ -19,8 +23,6 @@ const { URL, pathToFileURL, fileURLToPath } = require('internal/url'); const { ERR_INPUT_TYPE_NOT_ALLOWED, ERR_UNKNOWN_FILE_EXTENSION } = require('internal/errors').codes; -const { SafeMap } = primordials; - const realpathCache = new SafeMap(); // const TYPE_NONE = 0; @@ -53,7 +55,7 @@ function resolve(specifier, parentURL) { try { const parsed = new URL(specifier); if (parsed.protocol === 'data:') { - const [ , mime ] = /^([^/]+\/[^;,]+)(;base64)?,/.exec(parsed.pathname) || [ null, null, null ]; + const [ , mime ] = /^([^/]+\/[^;,]+)(?:[^,]*?)(;base64)?,/.exec(parsed.pathname) || [ null, null, null ]; const format = ({ '__proto__': null, 'text/javascript': 'module', diff --git a/lib/internal/modules/esm/loader.js b/lib/internal/modules/esm/loader.js index 875fb97f912a85..97f57935292396 100644 --- a/lib/internal/modules/esm/loader.js +++ b/lib/internal/modules/esm/loader.js @@ -1,6 +1,10 @@ 'use strict'; -const { FunctionPrototype } = primordials; +const { + FunctionPrototypeBind, + ObjectSetPrototypeOf, + SafeMap, +} = primordials; const { ERR_INVALID_RETURN_PROPERTY, @@ -26,11 +30,6 @@ const { getOptionValue } = require('internal/options'); const debug = require('internal/util/debuglog').debuglog('esm'); -const { - Object, - SafeMap -} = primordials; - /* A Loader instance is used as the main entry point for loading ES modules. * Currently, this is a singleton -- there is only one used for loading * the main module and everything in its dependency graph. */ @@ -137,10 +136,10 @@ class Loader { hook({ resolve, dynamicInstantiate }) { // Use .bind() to avoid giving access to the Loader instance when called. if (resolve !== undefined) - this._resolve = FunctionPrototype.bind(resolve, null); + this._resolve = FunctionPrototypeBind(resolve, null); if (dynamicInstantiate !== undefined) { this._dynamicInstantiate = - FunctionPrototype.bind(dynamicInstantiate, null); + FunctionPrototypeBind(dynamicInstantiate, null); } } @@ -182,6 +181,6 @@ class Loader { } } -Object.setPrototypeOf(Loader.prototype, null); +ObjectSetPrototypeOf(Loader.prototype, null); exports.Loader = Loader; diff --git a/lib/internal/modules/esm/module_job.js b/lib/internal/modules/esm/module_job.js index df1edc3810c080..e4a9040217689d 100644 --- a/lib/internal/modules/esm/module_job.js +++ b/lib/internal/modules/esm/module_job.js @@ -1,9 +1,9 @@ 'use strict'; const { - Object, + ObjectSetPrototypeOf, SafeSet, - SafePromise + SafePromise, } = primordials; const { ModuleWrap } = internalBinding('module_wrap'); @@ -109,5 +109,5 @@ class ModuleJob { return { module, result: module.evaluate(timeout, breakOnSigint) }; } } -Object.setPrototypeOf(ModuleJob.prototype, null); +ObjectSetPrototypeOf(ModuleJob.prototype, null); module.exports = ModuleJob; diff --git a/lib/internal/modules/esm/module_map.js b/lib/internal/modules/esm/module_map.js index 41adc0079ada31..9b7d86581e4159 100644 --- a/lib/internal/modules/esm/module_map.js +++ b/lib/internal/modules/esm/module_map.js @@ -2,7 +2,7 @@ const ModuleJob = require('internal/modules/esm/module_job'); const { - SafeMap + SafeMap, } = primordials; const debug = require('internal/util/debuglog').debuglog('esm'); const { ERR_INVALID_ARG_TYPE } = require('internal/errors').codes; diff --git a/lib/internal/modules/esm/translators.js b/lib/internal/modules/esm/translators.js index 34a9a140dd7732..dafc1b02f03112 100644 --- a/lib/internal/modules/esm/translators.js +++ b/lib/internal/modules/esm/translators.js @@ -3,10 +3,10 @@ /* global WebAssembly */ const { - JSON, - Object, + JSONParse, + ObjectKeys, SafeMap, - StringPrototype + StringPrototypeReplace, } = primordials; const { Buffer } = require('buffer'); @@ -29,7 +29,6 @@ const { ERR_UNKNOWN_BUILTIN_MODULE } = require('internal/errors').codes; const readFileAsync = promisify(fs.readFile); -const JsonParse = JSON.parse; const { maybeCacheSourceMap } = require('internal/source_map/source_map_cache'); const moduleWrap = internalBinding('module_wrap'); const { ModuleWrap } = moduleWrap; @@ -39,7 +38,7 @@ const debug = debuglog('esm'); const translators = new SafeMap(); exports.translators = translators; -const DATA_URL_PATTERN = /^[^/]+\/[^,;]+(;base64)?,([\s\S]*)$/; +const DATA_URL_PATTERN = /^[^/]+\/[^,;]+(?:[^,]*?)(;base64)?,([\s\S]*)$/; function getSource(url) { const parsed = new URL(url); if (parsed.protocol === 'file:') { @@ -101,7 +100,7 @@ translators.set('commonjs', function commonjsStrategy(url, isMain) { return cached; } const module = CJSModule._cache[ - isWindows ? StringPrototype.replace(pathname, winSepRegEx, '\\') : pathname + isWindows ? StringPrototypeReplace(pathname, winSepRegEx, '\\') : pathname ]; if (module && module.loaded) { const exports = module.exports; @@ -141,7 +140,7 @@ translators.set('json', async function jsonStrategy(url) { let module; if (pathname) { modulePath = isWindows ? - StringPrototype.replace(pathname, winSepRegEx, '\\') : pathname; + StringPrototypeReplace(pathname, winSepRegEx, '\\') : pathname; module = CJSModule._cache[modulePath]; if (module && module.loaded) { const exports = module.exports; @@ -164,7 +163,7 @@ translators.set('json', async function jsonStrategy(url) { } } try { - const exports = JsonParse(stripBOM(content)); + const exports = JSONParse(stripBOM(content)); module = { exports, loaded: true @@ -204,7 +203,7 @@ translators.set('wasm', async function(url) { return createDynamicModule(imports, exports, url, (reflect) => { const { exports } = new WebAssembly.Instance(compiled, reflect.imports); - for (const expt of Object.keys(exports)) + for (const expt of ObjectKeys(exports)) reflect.exports[expt].set(exports[expt]); }).module; }); diff --git a/lib/internal/modules/run_main.js b/lib/internal/modules/run_main.js index 1061727c78269c..5f8b1f53d33768 100644 --- a/lib/internal/modules/run_main.js +++ b/lib/internal/modules/run_main.js @@ -24,6 +24,10 @@ function shouldUseESMLoader(mainPath) { const userLoader = getOptionValue('--experimental-loader'); if (userLoader) return true; + const esModuleSpecifierResolution = + getOptionValue('--es-module-specifier-resolution'); + if (esModuleSpecifierResolution === 'node') + return true; // Determine the module format of the main if (mainPath && mainPath.endsWith('.mjs')) return true; diff --git a/lib/internal/per_context/domexception.js b/lib/internal/per_context/domexception.js index 3852567ac6bdba..9b26631904284a 100644 --- a/lib/internal/per_context/domexception.js +++ b/lib/internal/per_context/domexception.js @@ -1,10 +1,11 @@ 'use strict'; const { + ObjectDefineProperties, + ObjectDefineProperty, SafeWeakMap, SafeMap, - Object, - Symbol + SymbolToStringTag, } = primordials; class ERR_INVALID_THIS extends TypeError { @@ -73,8 +74,8 @@ class DOMException extends Error { } } -Object.defineProperties(DOMException.prototype, { - [Symbol.toStringTag]: { configurable: true, value: 'DOMException' }, +ObjectDefineProperties(DOMException.prototype, { + [SymbolToStringTag]: { configurable: true, value: 'DOMException' }, name: { enumerable: true, configurable: true }, message: { enumerable: true, configurable: true }, code: { enumerable: true, configurable: true } @@ -112,8 +113,8 @@ function forEachCode(fn) { forEachCode((name, codeName, value) => { const desc = { enumerable: true, value }; - Object.defineProperty(DOMException, codeName, desc); - Object.defineProperty(DOMException.prototype, codeName, desc); + ObjectDefineProperty(DOMException, codeName, desc); + ObjectDefineProperty(DOMException.prototype, codeName, desc); }); exports.DOMException = DOMException; diff --git a/lib/internal/per_context/primordials.js b/lib/internal/per_context/primordials.js index 7e02a9e3162d9c..9e17b3541c78af 100644 --- a/lib/internal/per_context/primordials.js +++ b/lib/internal/per_context/primordials.js @@ -13,7 +13,6 @@ // by the native module compiler. const ReflectApply = Reflect.apply; -const ReflectConstruct = Reflect.construct; // This function is borrowed from the function with the same name on V8 Extras' // `utils` object. V8 implements Reflect.apply very efficiently in conjunction @@ -37,14 +36,28 @@ function copyProps(src, dest) { } } -function copyPrototype(src, dest) { +function copyPropsRenamed(src, dest, prefix) { for (const key of Reflect.ownKeys(src)) { - if (!Reflect.getOwnPropertyDescriptor(dest, key)) { + if (typeof key === 'string') { + Reflect.defineProperty( + dest, + `${prefix}${key[0].toUpperCase()}${key.slice(1)}`, + Reflect.getOwnPropertyDescriptor(src, key)); + } + } +} + +function copyPrototype(src, dest, prefix) { + for (const key of Reflect.ownKeys(src)) { + if (typeof key === 'string') { const desc = Reflect.getOwnPropertyDescriptor(src, key); if (typeof desc.value === 'function') { desc.value = uncurryThis(desc.value); } - Reflect.defineProperty(dest, key, desc); + Reflect.defineProperty( + dest, + `${prefix}${key[0].toUpperCase()}${key.slice(1)}`, + desc); } } } @@ -83,13 +96,13 @@ primordials.SafePromise = makeSafe( 'Math', 'Reflect' ].forEach((name) => { - const target = primordials[name] = Object.create(null); - copyProps(global[name], target); + copyPropsRenamed(global[name], primordials, name); }); // Create copies of intrinsic objects [ 'Array', + 'ArrayBuffer', 'BigInt', 'Boolean', 'Date', @@ -102,18 +115,13 @@ primordials.SafePromise = makeSafe( 'Set', 'String', 'Symbol', + 'WeakMap', + 'WeakSet', ].forEach((name) => { const original = global[name]; - const target = primordials[name] = Object.setPrototypeOf({ - [name]: function(...args) { - return new.target ? - ReflectConstruct(original, args, new.target) : - ReflectApply(original, this, args); - } - }[name], null); - copyProps(original, target); - const proto = primordials[name + 'Prototype'] = Object.create(null); - copyPrototype(original.prototype, proto); + primordials[name] = original; + copyPropsRenamed(original, primordials, name); + copyPrototype(original.prototype, primordials, `${name}Prototype`); }); Object.setPrototypeOf(primordials, null); diff --git a/lib/internal/policy/manifest.js b/lib/internal/policy/manifest.js index 118a89efa6f88a..02cf0743d728cd 100644 --- a/lib/internal/policy/manifest.js +++ b/lib/internal/policy/manifest.js @@ -1,12 +1,15 @@ 'use strict'; const { + ArrayIsArray, Map, - MapPrototype, - Object, - RegExpPrototype, + MapPrototypeSet, + ObjectEntries, + ObjectFreeze, + ObjectSetPrototypeOf, + RegExpPrototypeTest, SafeMap, - uncurryThis + uncurryThis, } = primordials; const { canBeRequiredByUsers @@ -28,7 +31,6 @@ const HashUpdate = uncurryThis(crypto.Hash.prototype.update); const HashDigest = uncurryThis(crypto.Hash.prototype.digest); const BufferEquals = uncurryThis(Buffer.prototype.equals); const BufferToString = uncurryThis(Buffer.prototype.toString); -const { entries } = Object; const kRelativeURLStringPattern = /^\.{0,2}\//; const { getOptionValue } = require('internal/options'); const shouldAbortOnUncaughtException = @@ -73,10 +75,10 @@ class Manifest { } this.#reaction = reaction; - const manifestEntries = entries(obj.resources); + const manifestEntries = ObjectEntries(obj.resources); const parsedURLs = new SafeMap(); - for (var i = 0; i < manifestEntries.length; i++) { + for (let i = 0; i < manifestEntries.length; i++) { let resourceHREF = manifestEntries[i][0]; const originalHREF = resourceHREF; let resourceURL; @@ -84,7 +86,7 @@ class Manifest { resourceURL = parsedURLs.get(resourceHREF); resourceHREF = resourceURL.href; } else if ( - RegExpPrototype.test(kRelativeURLStringPattern, resourceHREF) + RegExpPrototypeTest(kRelativeURLStringPattern, resourceHREF) ) { resourceURL = new URL(resourceHREF, manifestURL); resourceHREF = resourceURL.href; @@ -96,7 +98,7 @@ class Manifest { if (integrity != null) { debug(`Manifest contains integrity for url ${originalHREF}`); if (typeof integrity === 'string') { - const sri = Object.freeze(SRI.parse(integrity)); + const sri = ObjectFreeze(SRI.parse(integrity)); if (integrities.has(resourceHREF)) { const old = integrities.get(resourceHREF); let mismatch = false; @@ -105,8 +107,8 @@ class Manifest { mismatch = true; } else { compare: - for (var sriI = 0; sriI < sri.length; sriI++) { - for (var oldI = 0; oldI < old.length; oldI++) { + for (let sriI = 0; sriI < sri.length; sriI++) { + for (let oldI = 0; oldI < old.length; oldI++) { if (sri[sriI].algorithm === old[oldI].algorithm && BufferEquals(sri[sriI].value, old[oldI].value) && sri[sriI].options === old[oldI].options) { @@ -136,7 +138,7 @@ class Manifest { if (dependencyMap === null || dependencyMap === undefined) { dependencyMap = {}; } - if (typeof dependencyMap === 'object' && !Array.isArray(dependencyMap)) { + if (typeof dependencyMap === 'object' && !ArrayIsArray(dependencyMap)) { /** * @returns {true | URL} */ @@ -156,7 +158,7 @@ class Manifest { parsedURLs.set(to, resolvedURL); parsedURLs.set(href, resolvedURL); return resolvedURL; - } else if (RegExpPrototype.test(kRelativeURLStringPattern, to)) { + } else if (RegExpPrototypeTest(kRelativeURLStringPattern, to)) { const resolvedURL = new URL(to, manifestURL); const href = resourceURL.href; parsedURLs.set(to, resolvedURL); @@ -180,7 +182,7 @@ class Manifest { 'dependencies'); } } - Object.freeze(this); + ObjectFreeze(this); } getRedirector(requester) { @@ -205,7 +207,7 @@ class Manifest { const integrityEntries = integrities.get(href); if (integrityEntries === true) return true; // Avoid clobbered Symbol.iterator - for (var i = 0; i < integrityEntries.length; i++) { + for (let i = 0; i < integrityEntries.length; i++) { const { algorithm, value: expected @@ -217,7 +219,7 @@ class Manifest { timingSafeEqual(digest, expected)) { return true; } - MapPrototype.set( + MapPrototypeSet( realIntegrities, algorithm, BufferToString(digest, 'base64') @@ -230,8 +232,8 @@ class Manifest { } // Lock everything down to avoid problems even if reference is leaked somehow -Object.setPrototypeOf(Manifest, null); -Object.setPrototypeOf(Manifest.prototype, null); -Object.freeze(Manifest); -Object.freeze(Manifest.prototype); -module.exports = Object.freeze({ Manifest }); +ObjectSetPrototypeOf(Manifest, null); +ObjectSetPrototypeOf(Manifest.prototype, null); +ObjectFreeze(Manifest); +ObjectFreeze(Manifest.prototype); +module.exports = ObjectFreeze({ Manifest }); diff --git a/lib/internal/policy/sri.js b/lib/internal/policy/sri.js index 877c96a6f7b7d6..38d7fc9e07d741 100644 --- a/lib/internal/policy/sri.js +++ b/lib/internal/policy/sri.js @@ -2,9 +2,12 @@ // Value of https://w3c.github.io/webappsec-subresource-integrity/#the-integrity-attribute const { - Object, - RegExpPrototype, - StringPrototype + ObjectDefineProperty, + ObjectFreeze, + ObjectSeal, + RegExpPrototypeExec, + RegExpPrototypeTest, + StringPrototypeSlice, } = primordials; // Returns [{algorithm, value (in base64 string), options,}] @@ -20,10 +23,9 @@ const kHASH_EXPRESSION = `(${kHASH_ALGO})-(${kHASH_VALUE})`; const kOPTION_EXPRESSION = `(${kVCHAR}*)`; const kHASH_WITH_OPTIONS = `${kHASH_EXPRESSION}(?:[?](${kOPTION_EXPRESSION}))?`; const kSRIPattern = RegExp(`(${kWSP}*)(?:${kHASH_WITH_OPTIONS})`, 'g'); -const { freeze } = Object; -Object.seal(kSRIPattern); +ObjectSeal(kSRIPattern); const kAllWSP = RegExp(`^${kWSP}*$`); -Object.seal(kAllWSP); +ObjectSeal(kAllWSP); const BufferFrom = require('buffer').Buffer.from; @@ -32,7 +34,7 @@ const parse = (str) => { let prevIndex = 0; let match; const entries = []; - while (match = RegExpPrototype.exec(kSRIPattern, str)) { + while (match = RegExpPrototypeExec(kSRIPattern, str)) { if (match.index !== prevIndex) { throw new ERR_SRI_PARSE(str, str.charAt(prevIndex), prevIndex); } @@ -41,10 +43,10 @@ const parse = (str) => { } // Avoid setters being fired - Object.defineProperty(entries, entries.length, { + ObjectDefineProperty(entries, entries.length, { enumerable: true, configurable: true, - value: freeze({ + value: ObjectFreeze({ __proto__: null, algorithm: match[2], value: BufferFrom(match[3], 'base64'), @@ -55,7 +57,7 @@ const parse = (str) => { } if (prevIndex !== str.length) { - if (!RegExpPrototype.test(kAllWSP, StringPrototype.slice(str, prevIndex))) { + if (!RegExpPrototypeTest(kAllWSP, StringPrototypeSlice(str, prevIndex))) { throw new ERR_SRI_PARSE(str, str.charAt(prevIndex), prevIndex); } } diff --git a/lib/internal/priority_queue.js b/lib/internal/priority_queue.js index ec8bbaea412761..afd2cef643b9e5 100644 --- a/lib/internal/priority_queue.js +++ b/lib/internal/priority_queue.js @@ -1,5 +1,9 @@ 'use strict'; +const { + Array, +} = primordials; + const kCompare = Symbol('compare'); const kHeap = Symbol('heap'); const kSetPosition = Symbol('setPosition'); diff --git a/lib/internal/process/execution.js b/lib/internal/process/execution.js index eed554263c213c..44c67452f5a01f 100644 --- a/lib/internal/process/execution.js +++ b/lib/internal/process/execution.js @@ -1,6 +1,8 @@ 'use strict'; -const { JSON } = primordials; +const { + JSONStringify, +} = primordials; const path = require('path'); @@ -64,7 +66,7 @@ function evalScript(name, body, breakFirstLine, print) { module.paths = CJSModule._nodeModulePaths(cwd); global.kVmBreakFirstLineSymbol = kVmBreakFirstLineSymbol; const script = ` - global.__filename = ${JSON.stringify(name)}; + global.__filename = ${JSONStringify(name)}; global.exports = exports; global.module = module; global.__dirname = __dirname; @@ -72,8 +74,8 @@ function evalScript(name, body, breakFirstLine, print) { const { kVmBreakFirstLineSymbol } = global; delete global.kVmBreakFirstLineSymbol; return require("vm").runInThisContext( - ${JSON.stringify(body)}, { - filename: ${JSON.stringify(name)}, + ${JSONStringify(body)}, { + filename: ${JSONStringify(name)}, displayErrors: true, [kVmBreakFirstLineSymbol]: ${!!breakFirstLine} });\n`; diff --git a/lib/internal/process/main_thread_only.js b/lib/internal/process/main_thread_only.js index 0cb3edbf9ad7b4..ab56500744ba0d 100644 --- a/lib/internal/process/main_thread_only.js +++ b/lib/internal/process/main_thread_only.js @@ -3,6 +3,10 @@ // This file contains process bootstrappers that can only be // run in the main thread +const { + ArrayIsArray, +} = primordials; + const { errnoException, codes: { @@ -74,10 +78,10 @@ function wrapPosixCredentialSetters(credentials) { } function setgroups(groups) { - if (!Array.isArray(groups)) { + if (!ArrayIsArray(groups)) { throw new ERR_INVALID_ARG_TYPE('groups', 'Array', groups); } - for (var i = 0; i < groups.length; i++) { + for (let i = 0; i < groups.length; i++) { validateId(groups[i], `groups[${i}]`); } // Result is 0 on success. A positive integer indicates that the diff --git a/lib/internal/process/per_thread.js b/lib/internal/process/per_thread.js index 63ebf2afb6ffee..2ead72781d53f0 100644 --- a/lib/internal/process/per_thread.js +++ b/lib/internal/process/per_thread.js @@ -5,10 +5,15 @@ // thread and the worker threads. const { - Object, - RegExpPrototype, - SetPrototype, - StringPrototype + ArrayIsArray, + NumberMAX_SAFE_INTEGER, + ObjectDefineProperties, + ObjectDefineProperty, + ObjectFreeze, + ObjectGetOwnPropertyDescriptors, + RegExpPrototypeTest, + SetPrototypeHas, + StringPrototypeReplace, } = primordials; const { @@ -98,7 +103,7 @@ function wrapProcessMethods(binding) { // implementation always returns numbers <= Number.MAX_SAFE_INTEGER. function previousValueIsValid(num) { return typeof num === 'number' && - num <= Number.MAX_SAFE_INTEGER && + num <= NumberMAX_SAFE_INTEGER && num >= 0; } @@ -111,7 +116,7 @@ function wrapProcessMethods(binding) { _hrtime(hrValues); if (time !== undefined) { - if (!Array.isArray(time)) { + if (!ArrayIsArray(time)) { throw new ERR_INVALID_ARG_TYPE('time', 'Array', time); } if (time.length !== 2) { @@ -164,7 +169,7 @@ function wrapProcessMethods(binding) { } function kill(pid, sig) { - var err; + let err; // eslint-disable-next-line eqeqeq if (pid != (pid | 0)) { @@ -271,15 +276,15 @@ function buildAllowedFlags() { } const trimLeadingDashes = - (flag) => StringPrototype.replace(flag, leadingDashesRegex, ''); + (flag) => StringPrototypeReplace(flag, leadingDashesRegex, ''); // Save these for comparison against flags provided to // process.allowedNodeEnvironmentFlags.has() which lack leading dashes. // Avoid interference w/ user code by flattening `Set.prototype` into // each object. - const nodeFlags = Object.defineProperties( + const nodeFlags = ObjectDefineProperties( new Set(allowedNodeEnvironmentFlags.map(trimLeadingDashes)), - Object.getOwnPropertyDescriptors(Set.prototype) + ObjectGetOwnPropertyDescriptors(Set.prototype) ); class NodeEnvironmentFlagsSet extends Set { @@ -288,7 +293,7 @@ function buildAllowedFlags() { // The super constructor consumes `add`, but // disallow any future adds. - Object.defineProperty(this, 'add', { + ObjectDefineProperty(this, 'add', { value: () => this }); } @@ -311,21 +316,21 @@ function buildAllowedFlags() { // on a dummy option set and see whether it rejects the argument or // not. if (typeof key === 'string') { - key = StringPrototype.replace(key, replaceUnderscoresRegex, '-'); - if (RegExpPrototype.test(leadingDashesRegex, key)) { - key = StringPrototype.replace(key, trailingValuesRegex, ''); - return SetPrototype.has(this, key); + key = StringPrototypeReplace(key, replaceUnderscoresRegex, '-'); + if (RegExpPrototypeTest(leadingDashesRegex, key)) { + key = StringPrototypeReplace(key, trailingValuesRegex, ''); + return SetPrototypeHas(this, key); } - return SetPrototype.has(nodeFlags, key); + return SetPrototypeHas(nodeFlags, key); } return false; } } - Object.freeze(NodeEnvironmentFlagsSet.prototype.constructor); - Object.freeze(NodeEnvironmentFlagsSet.prototype); + ObjectFreeze(NodeEnvironmentFlagsSet.prototype.constructor); + ObjectFreeze(NodeEnvironmentFlagsSet.prototype); - return Object.freeze(new NodeEnvironmentFlagsSet( + return ObjectFreeze(new NodeEnvironmentFlagsSet( allowedNodeEnvironmentFlags )); } diff --git a/lib/internal/process/policy.js b/lib/internal/process/policy.js index d2501403c39951..ea283a449742fc 100644 --- a/lib/internal/process/policy.js +++ b/lib/internal/process/policy.js @@ -1,6 +1,10 @@ 'use strict'; -const { JSON, Object, Reflect } = primordials; +const { + JSONParse, + ObjectFreeze, + ReflectSetPrototypeOf, +} = primordials; const { ERR_MANIFEST_TDZ, @@ -10,7 +14,7 @@ let manifest; let manifestSrc; let manifestURL; -module.exports = Object.freeze({ +module.exports = ObjectFreeze({ __proto__: null, setup(src, url) { manifestSrc = src; @@ -20,10 +24,10 @@ module.exports = Object.freeze({ return; } - const json = JSON.parse(src, (_, o) => { + const json = JSONParse(src, (_, o) => { if (o && typeof o === 'object') { - Reflect.setPrototypeOf(o, null); - Object.freeze(o); + ReflectSetPrototypeOf(o, null); + ObjectFreeze(o); } return o; }); diff --git a/lib/internal/process/promises.js b/lib/internal/process/promises.js index ab833776070449..35ebb39115f3be 100644 --- a/lib/internal/process/promises.js +++ b/lib/internal/process/promises.js @@ -1,6 +1,8 @@ 'use strict'; -const { Object } = primordials; +const { + ObjectDefineProperty, +} = primordials; const { tickInfo, @@ -222,7 +224,7 @@ function getErrorWithoutStack(name, message) { // eslint-disable-next-line no-restricted-syntax const err = new Error(message); Error.stackTraceLimit = tmp; - Object.defineProperty(err, 'name', { + ObjectDefineProperty(err, 'name', { value: name, enumerable: false, writable: true, diff --git a/lib/internal/process/report.js b/lib/internal/process/report.js index cc78aebbdaa358..407cc12cb04f76 100644 --- a/lib/internal/process/report.js +++ b/lib/internal/process/report.js @@ -5,7 +5,9 @@ const { } = require('internal/errors').codes; const { validateSignalName, validateString } = require('internal/validators'); const nr = internalBinding('report'); -const { JSON } = primordials; +const { + JSONParse, +} = primordials; const report = { writeReport(file, err) { if (typeof file === 'object' && file !== null) { @@ -27,7 +29,7 @@ const report = { else if (err === null || typeof err !== 'object') throw new ERR_INVALID_ARG_TYPE('err', 'Object', err); - return JSON.parse(nr.getReport(err.stack)); + return JSONParse(nr.getReport(err.stack)); }, get directory() { return nr.getDirectory(); diff --git a/lib/internal/process/stdio.js b/lib/internal/process/stdio.js index 08781547c48952..2491f14cdf0140 100644 --- a/lib/internal/process/stdio.js +++ b/lib/internal/process/stdio.js @@ -19,9 +19,9 @@ function dummyDestroy(err, cb) { } function getMainThreadStdio() { - var stdin; - var stdout; - var stderr; + let stdin; + let stdout; + let stderr; function getStdout() { if (stdout) return stdout; @@ -53,7 +53,7 @@ function getMainThreadStdio() { switch (guessHandleType(fd)) { case 'TTY': - var tty = require('tty'); + const tty = require('tty'); stdin = new tty.ReadStream(fd, { highWaterMark: 0, readable: true, @@ -62,13 +62,13 @@ function getMainThreadStdio() { break; case 'FILE': - var fs = require('fs'); + const fs = require('fs'); stdin = new fs.ReadStream(null, { fd: fd, autoClose: false }); break; case 'PIPE': case 'TCP': - var net = require('net'); + const net = require('net'); // It could be that process has been started with an IPC channel // sitting on fd=0, in such case the pipe for this fd is already @@ -147,11 +147,11 @@ function getMainThreadStdio() { } function createWritableStdioStream(fd) { - var stream; + let stream; // Note stream._type is used for test-module-load-list.js switch (guessHandleType(fd)) { case 'TTY': - var tty = require('tty'); + const tty = require('tty'); stream = new tty.WriteStream(fd); stream._type = 'tty'; break; @@ -164,7 +164,7 @@ function createWritableStdioStream(fd) { case 'PIPE': case 'TCP': - var net = require('net'); + const net = require('net'); // If fd is already being used for the IPC channel, libuv will return // an error when trying to use it again. In that case, create the socket diff --git a/lib/internal/process/task_queues.js b/lib/internal/process/task_queues.js index 8906b4aa1e2426..8b2d2d808af854 100644 --- a/lib/internal/process/task_queues.js +++ b/lib/internal/process/task_queues.js @@ -1,6 +1,9 @@ 'use strict'; -const { FunctionPrototype } = primordials; +const { + Array, + FunctionPrototypeBind, +} = primordials; const { // For easy access to the nextTick state in the C++ land, @@ -46,6 +49,7 @@ const kHasTickScheduled = 0; function hasTickScheduled() { return tickInfo[kHasTickScheduled] === 1; } + function setHasTickScheduled(value) { tickInfo[kHasTickScheduled] = value ? 1 : 0; } @@ -105,7 +109,7 @@ function nextTick(callback) { if (process._exiting) return; - var args; + let args; switch (arguments.length) { case 1: break; case 2: args = [arguments[1]]; break; @@ -113,7 +117,7 @@ function nextTick(callback) { case 4: args = [arguments[1], arguments[2], arguments[3]]; break; default: args = new Array(arguments.length - 1); - for (var i = 1; i < arguments.length; i++) + for (let i = 1; i < arguments.length; i++) args[i - 1] = arguments[i]; } @@ -165,7 +169,7 @@ function queueMicrotask(callback) { const asyncResource = createMicrotaskResource(); asyncResource.callback = callback; - enqueueMicrotask(FunctionPrototype.bind(runMicrotask, asyncResource)); + enqueueMicrotask(FunctionPrototypeBind(runMicrotask, asyncResource)); } module.exports = { diff --git a/lib/internal/process/warning.js b/lib/internal/process/warning.js index 3ad3d4b9fbaa04..cf744cf0d591b1 100644 --- a/lib/internal/process/warning.js +++ b/lib/internal/process/warning.js @@ -1,5 +1,9 @@ 'use strict'; +const { + ArrayIsArray, +} = primordials; + const { ERR_INVALID_ARG_TYPE } = require('internal/errors').codes; // Lazily loaded @@ -58,7 +62,7 @@ function onWarning(warning) { if (isDeprecation && process.noDeprecation) return; const trace = process.traceProcessWarnings || (isDeprecation && process.traceDeprecation); - var msg = `(${process.release.name}:${process.pid}) `; + let msg = `(${process.release.name}:${process.pid}) `; if (warning.code) msg += `[${warning.code}] `; if (trace && warning.stack) { @@ -84,7 +88,7 @@ function onWarning(warning) { // process.emitWarning(str[, options]) function emitWarning(warning, type, code, ctor, now) { let detail; - if (type !== null && typeof type === 'object' && !Array.isArray(type)) { + if (type !== null && typeof type === 'object' && !ArrayIsArray(type)) { ctor = type.ctor; code = type.code; if (typeof type.detail === 'string') diff --git a/lib/internal/process/worker_thread_only.js b/lib/internal/process/worker_thread_only.js index b6529e43441679..920f0f77261bab 100644 --- a/lib/internal/process/worker_thread_only.js +++ b/lib/internal/process/worker_thread_only.js @@ -16,6 +16,7 @@ function lazyWorkerStdio() { if (!workerStdio) workerStdio = createWorkerStdio(); return workerStdio; } + function createStdioGetters() { return { getStdout() { return lazyWorkerStdio().stdout; }, diff --git a/lib/internal/querystring.js b/lib/internal/querystring.js index ecb4e072d83873..7df1c495c6e5c5 100644 --- a/lib/internal/querystring.js +++ b/lib/internal/querystring.js @@ -1,5 +1,9 @@ 'use strict'; +const { + Array, +} = primordials; + const { ERR_INVALID_URI } = require('internal/errors').codes; const hexTable = new Array(256); diff --git a/lib/internal/readline/utils.js b/lib/internal/readline/utils.js index 31de512f74ba85..510acf221831a0 100644 --- a/lib/internal/readline/utils.js +++ b/lib/internal/readline/utils.js @@ -1,5 +1,10 @@ 'use strict'; +const { + Boolean, + NumberIsInteger, +} = primordials; + // Regex used for ansi escape code splitting // Adopted from https://github.com/chalk/ansi-regex/blob/master/index.js // License: MIT, authors: @sindresorhus, Qix-, and arjunmehta @@ -16,7 +21,7 @@ let isFullWidthCodePoint; function CSI(strings, ...args) { let ret = `${kEscape}[`; - for (var n = 0; n < strings.length; n++) { + for (let n = 0; n < strings.length; n++) { ret += strings[n]; if (n < args.length) ret += args[n]; @@ -34,7 +39,7 @@ if (internalBinding('config').hasIntl) { const icu = internalBinding('icu'); getStringWidth = function getStringWidth(str, options) { options = options || {}; - if (Number.isInteger(str)) { + if (NumberIsInteger(str)) { // Provide information about the character with code point 'str'. return icu.getStringWidth( str, @@ -72,14 +77,14 @@ if (internalBinding('config').hasIntl) { * Returns the number of columns required to display the given string. */ getStringWidth = function getStringWidth(str) { - if (Number.isInteger(str)) + if (NumberIsInteger(str)) return isFullWidthCodePoint(str) ? 2 : 1; let width = 0; str = stripVTControlCharacters(String(str)); - for (var i = 0; i < str.length; i++) { + for (let i = 0; i < str.length; i++) { const code = str.codePointAt(i); if (code >= kUTF16SurrogateThreshold) { // Surrogates. @@ -103,7 +108,7 @@ if (internalBinding('config').hasIntl) { isFullWidthCodePoint = function isFullWidthCodePoint(code) { // Code points are derived from: // http://www.unicode.org/Public/UNIDATA/EastAsianWidth.txt - return Number.isInteger(code) && code >= 0x1100 && ( + return NumberIsInteger(code) && code >= 0x1100 && ( code <= 0x115f || // Hangul Jamo code === 0x2329 || // LEFT-POINTING ANGLE BRACKET code === 0x232a || // RIGHT-POINTING ANGLE BRACKET diff --git a/lib/internal/repl.js b/lib/internal/repl.js index cb368ea6121ec3..565ab049c71487 100644 --- a/lib/internal/repl.js +++ b/lib/internal/repl.js @@ -1,11 +1,15 @@ 'use strict'; -const { Object } = primordials; +const { + Number, + NumberIsNaN, + ObjectCreate, +} = primordials; const REPL = require('repl'); const { kStandaloneREPL } = require('internal/repl/utils'); -module.exports = Object.create(REPL); +module.exports = ObjectCreate(REPL); module.exports.createInternalRepl = createRepl; function createRepl(env, opts, cb) { @@ -37,7 +41,7 @@ function createRepl(env, opts, cb) { } const historySize = Number(env.NODE_REPL_HISTORY_SIZE); - if (!Number.isNaN(historySize) && historySize > 0) { + if (!NumberIsNaN(historySize) && historySize > 0) { opts.historySize = historySize; } else { opts.historySize = 1000; diff --git a/lib/internal/repl/await.js b/lib/internal/repl/await.js index ed58e89529ad3b..b437221ca22e77 100644 --- a/lib/internal/repl/await.js +++ b/lib/internal/repl/await.js @@ -1,6 +1,8 @@ 'use strict'; -const { Object } = primordials; +const { + ObjectKeys, +} = primordials; const acorn = require('internal/deps/acorn/acorn/dist/acorn'); const walk = require('internal/deps/acorn/acorn-walk/dist/walk'); @@ -72,7 +74,7 @@ const visitorsWithoutAncestors = { }; const visitors = {}; -for (const nodeType of Object.keys(walk.base)) { +for (const nodeType of ObjectKeys(walk.base)) { const callback = visitorsWithoutAncestors[nodeType] || walk.base[nodeType]; visitors[nodeType] = (node, state, c) => { const isNew = node !== state.ancestors[state.ancestors.length - 1]; @@ -100,7 +102,7 @@ function processTopLevelAwait(src) { body, ancestors: [], replace(from, to, str) { - for (var i = from; i < to; i++) { + for (let i = from; i < to; i++) { wrappedArray[i] = ''; } if (from === to) str += wrappedArray[from]; diff --git a/lib/internal/repl/history.js b/lib/internal/repl/history.js index 5d90b40e0041c7..6404e821b13e0d 100644 --- a/lib/internal/repl/history.js +++ b/lib/internal/repl/history.js @@ -1,5 +1,9 @@ 'use strict'; +const { + Boolean, +} = primordials; + const { Interface } = require('readline'); const path = require('path'); const fs = require('fs'); @@ -41,9 +45,9 @@ function setupHistory(repl, historyPath, ready) { } } - var timer = null; - var writing = false; - var pending = false; + let timer = null; + let writing = false; + let pending = false; repl.pause(); // History files are conventionally not readable by others: // https://github.com/nodejs/node/issues/3392 diff --git a/lib/internal/source_map/source_map_cache.js b/lib/internal/source_map/source_map_cache.js index 340615eb6cffcd..8211e51e3ec413 100644 --- a/lib/internal/source_map/source_map_cache.js +++ b/lib/internal/source_map/source_map_cache.js @@ -1,26 +1,21 @@ 'use strict'; const { - JSON, - Object: { - create: ObjectCreate, - keys: ObjectKeys, - getOwnPropertyDescriptor: ObjectGetOwnPropertyDescriptor, - }, - ObjectPrototype: { - hasOwnProperty: ObjectHasOwnProperty - }, - MapPrototype: { - entries: MapEntries - }, uncurryThis + JSONParse, + ObjectCreate, + ObjectKeys, + ObjectGetOwnPropertyDescriptor, + ObjectPrototypeHasOwnProperty, + MapPrototypeEntries, + WeakMapPrototypeGet, + uncurryThis, } = primordials; -const MapIteratorNext = uncurryThis(MapEntries(new Map()).next); -const WeakMapGet = uncurryThis(WeakMap.prototype.get); +const MapIteratorNext = uncurryThis(MapPrototypeEntries(new Map()).next); function ObjectGetValueSafe(obj, key) { const desc = ObjectGetOwnPropertyDescriptor(obj, key); - return ObjectHasOwnProperty(desc, 'value') ? desc.value : undefined; + return ObjectPrototypeHasOwnProperty(desc, 'value') ? desc.value : undefined; } // See https://sourcemaps.info/spec.html for SourceMap V3 specification. @@ -115,7 +110,7 @@ function lineLengths(content) { function sourceMapFromFile(sourceMapFile) { try { const content = fs.readFileSync(sourceMapFile, 'utf8'); - const data = JSON.parse(content); + const data = JSONParse(content); return sourcesToAbsolute(dirname(sourceMapFile), data); } catch (err) { debug(err.stack); @@ -134,7 +129,7 @@ function sourceMapFromDataUrl(basePath, url) { const decodedData = base64 ? Buffer.from(data, 'base64').toString('utf8') : data; try { - const parsedData = JSON.parse(decodedData); + const parsedData = JSONParse(decodedData); return sourcesToAbsolute(basePath, parsedData); } catch (err) { debug(err.stack); @@ -182,7 +177,7 @@ function rekeySourceMap(cjsModuleInstance, newInstance) { function sourceMapCacheToObject() { const obj = ObjectCreate(null); - const it = MapEntries(esmSourceMapCache); + const it = MapPrototypeEntries(esmSourceMapCache); let entry; while (!(entry = MapIteratorNext(it)).done) { const k = entry.value[0]; @@ -211,7 +206,7 @@ function appendCJSCache(obj) { for (let i = 0; i < cjsModules.length; i++) { const key = cjsModules[i]; const module = ObjectGetValueSafe(cjsModuleCache, key); - const value = WeakMapGet(cjsSourceMapCache, module); + const value = WeakMapPrototypeGet(cjsSourceMapCache, module); if (value) { // This is okay because `obj` has a null prototype. obj[`file://${key}`] = { diff --git a/lib/internal/stream_base_commons.js b/lib/internal/stream_base_commons.js index eb2e53963d82b3..504de5fc57f9ab 100644 --- a/lib/internal/stream_base_commons.js +++ b/lib/internal/stream_base_commons.js @@ -1,5 +1,9 @@ 'use strict'; +const { + Array, +} = primordials; + const { Buffer } = require('buffer'); const { FastBuffer } = require('internal/buffer'); const { @@ -111,16 +115,15 @@ function createWriteWrap(handle) { function writevGeneric(self, data, cb) { const req = createWriteWrap(self[kHandle]); const allBuffers = data.allBuffers; - var chunks; - var i; + let chunks; if (allBuffers) { chunks = data; - for (i = 0; i < data.length; i++) + for (let i = 0; i < data.length; i++) data[i] = data[i].chunk; } else { chunks = new Array(data.length << 1); - for (i = 0; i < data.length; i++) { - var entry = data[i]; + for (let i = 0; i < data.length; i++) { + const entry = data[i]; chunks[i * 2] = entry.chunk; chunks[i * 2 + 1] = entry.encoding; } diff --git a/lib/internal/streams/async_iterator.js b/lib/internal/streams/async_iterator.js index 07f2191e7134ce..e05813e5dfd947 100644 --- a/lib/internal/streams/async_iterator.js +++ b/lib/internal/streams/async_iterator.js @@ -1,6 +1,10 @@ 'use strict'; -const { Object } = primordials; +const { + ObjectCreate, + ObjectGetPrototypeOf, + ObjectSetPrototypeOf, +} = primordials; const finished = require('internal/streams/end-of-stream'); @@ -49,10 +53,10 @@ function wrapForNext(lastPromise, iter) { }; } -const AsyncIteratorPrototype = Object.getPrototypeOf( - Object.getPrototypeOf(async function* () {}).prototype); +const AsyncIteratorPrototype = ObjectGetPrototypeOf( + ObjectGetPrototypeOf(async function* () {}).prototype); -const ReadableStreamAsyncIteratorPrototype = Object.setPrototypeOf({ +const ReadableStreamAsyncIteratorPrototype = ObjectSetPrototypeOf({ get stream() { return this[kStream]; }, @@ -135,7 +139,7 @@ const ReadableStreamAsyncIteratorPrototype = Object.setPrototypeOf({ }, AsyncIteratorPrototype); const createReadableStreamAsyncIterator = (stream) => { - const iterator = Object.create(ReadableStreamAsyncIteratorPrototype, { + const iterator = ObjectCreate(ReadableStreamAsyncIteratorPrototype, { [kStream]: { value: stream, writable: true }, [kLastResolve]: { value: null, writable: true }, [kLastReject]: { value: null, writable: true }, diff --git a/lib/internal/streams/lazy_transform.js b/lib/internal/streams/lazy_transform.js index 0036bf306c3193..6584159095af39 100644 --- a/lib/internal/streams/lazy_transform.js +++ b/lib/internal/streams/lazy_transform.js @@ -3,7 +3,11 @@ // for the stream, one conventional and one non-conventional. 'use strict'; -const { Object } = primordials; +const { + ObjectDefineProperties, + ObjectDefineProperty, + ObjectSetPrototypeOf, +} = primordials; const stream = require('stream'); @@ -18,8 +22,8 @@ function LazyTransform(options) { this.writable = true; this.readable = true; } -Object.setPrototypeOf(LazyTransform.prototype, stream.Transform.prototype); -Object.setPrototypeOf(LazyTransform, stream.Transform); +ObjectSetPrototypeOf(LazyTransform.prototype, stream.Transform.prototype); +ObjectSetPrototypeOf(LazyTransform, stream.Transform); function makeGetter(name) { return function() { @@ -36,7 +40,7 @@ function makeGetter(name) { function makeSetter(name) { return function(val) { - Object.defineProperty(this, name, { + ObjectDefineProperty(this, name, { value: val, enumerable: true, configurable: true, @@ -45,7 +49,7 @@ function makeSetter(name) { }; } -Object.defineProperties(LazyTransform.prototype, { +ObjectDefineProperties(LazyTransform.prototype, { _readableState: { get: makeGetter('_readableState'), set: makeSetter('_readableState'), diff --git a/lib/internal/streams/legacy.js b/lib/internal/streams/legacy.js index 062eabec3890c0..702e3c56ba6376 100644 --- a/lib/internal/streams/legacy.js +++ b/lib/internal/streams/legacy.js @@ -1,14 +1,16 @@ 'use strict'; -const { Object } = primordials; +const { + ObjectSetPrototypeOf, +} = primordials; const EE = require('events'); function Stream() { EE.call(this); } -Object.setPrototypeOf(Stream.prototype, EE.prototype); -Object.setPrototypeOf(Stream, EE); +ObjectSetPrototypeOf(Stream.prototype, EE.prototype); +ObjectSetPrototypeOf(Stream, EE); Stream.prototype.pipe = function(dest, options) { const source = this; diff --git a/lib/internal/streams/pipeline.js b/lib/internal/streams/pipeline.js index 798745a110d51b..0c464605106630 100644 --- a/lib/internal/streams/pipeline.js +++ b/lib/internal/streams/pipeline.js @@ -3,6 +3,10 @@ 'use strict'; +const { + ArrayIsArray, +} = primordials; + let eos; const { once } = require('internal/util'); @@ -65,7 +69,7 @@ function popCallback(streams) { function pipeline(...streams) { const callback = popCallback(streams); - if (Array.isArray(streams[0])) streams = streams[0]; + if (ArrayIsArray(streams[0])) streams = streams[0]; if (streams.length < 2) { throw new ERR_MISSING_ARGS('streams'); diff --git a/lib/internal/streams/state.js b/lib/internal/streams/state.js index a3f5e672862452..fa902ef429b553 100644 --- a/lib/internal/streams/state.js +++ b/lib/internal/streams/state.js @@ -1,6 +1,9 @@ 'use strict'; -const { Math } = primordials; +const { + MathFloor, + NumberIsInteger, +} = primordials; const { ERR_INVALID_OPT_VALUE } = require('internal/errors').codes; @@ -16,11 +19,11 @@ function getDefaultHighWaterMark(objectMode) { function getHighWaterMark(state, options, duplexKey, isDuplex) { const hwm = highWaterMarkFrom(options, isDuplex, duplexKey); if (hwm != null) { - if (!Number.isInteger(hwm) || hwm < 0) { + if (!NumberIsInteger(hwm) || hwm < 0) { const name = isDuplex ? duplexKey : 'highWaterMark'; throw new ERR_INVALID_OPT_VALUE(name, hwm); } - return Math.floor(hwm); + return MathFloor(hwm); } // Default value diff --git a/lib/internal/timers.js b/lib/internal/timers.js index 4067c2ee0b3165..84b3c34d87cfaf 100644 --- a/lib/internal/timers.js +++ b/lib/internal/timers.js @@ -72,7 +72,12 @@ // timers within (or creation of a new list). However, these operations combined // have shown to be trivial in comparison to other timers architectures. -const { Math, Object } = primordials; +const { + MathMax, + MathTrunc, + NumberMIN_SAFE_INTEGER, + ObjectCreate, +} = primordials; const { scheduleTimer, @@ -117,7 +122,7 @@ const kHasOutstanding = 2; // Timeout values > TIMEOUT_MAX are set to 1. const TIMEOUT_MAX = 2 ** 31 - 1; -let timerListId = Number.MIN_SAFE_INTEGER; +let timerListId = NumberMIN_SAFE_INTEGER; const kRefed = Symbol('refed'); @@ -137,7 +142,7 @@ const timerListQueue = new PriorityQueue(compareTimersLists, setPosition); // // - key = time in milliseconds // - value = linked list -const timerListMap = Object.create(null); +const timerListMap = ObjectCreate(null); function initAsyncResource(resource, type) { const asyncId = resource[async_id_symbol] = newAsyncId(); @@ -308,7 +313,7 @@ function insert(item, refed, start) { return; // Truncate so that accuracy of sub-millisecond timers is not assumed. - msecs = Math.trunc(msecs); + msecs = MathTrunc(msecs); item._idleStart = start; @@ -490,7 +495,7 @@ function getTimerCallbacks(runNextTicks) { // Check if this loop iteration is too early for the next timer. // This happens if there are more timers scheduled for later in the list. if (diff < msecs) { - list.expiry = Math.max(timer._idleStart + msecs, now + 1); + list.expiry = MathMax(timer._idleStart + msecs, now + 1); list.id = timerListId++; timerListQueue.percolateDown(1); debug('%d list wait because diff is %d', msecs, diff); diff --git a/lib/internal/tls.js b/lib/internal/tls.js index 8bf9330352bd20..d7370ad52c0f8e 100644 --- a/lib/internal/tls.js +++ b/lib/internal/tls.js @@ -1,11 +1,14 @@ 'use strict'; -const { Object } = primordials; +const { + ArrayIsArray, + ObjectCreate, +} = primordials; // Example: // C=US\nST=CA\nL=SF\nO=Joyent\nOU=Node.js\nCN=ca1\nemailAddress=ry@clouds.org function parseCertString(s) { - const out = Object.create(null); + const out = ObjectCreate(null); const parts = s.split('\n'); for (let i = 0, len = parts.length; i < len; i++) { const sepIndex = parts[i].indexOf('='); @@ -13,7 +16,7 @@ function parseCertString(s) { const key = parts[i].slice(0, sepIndex); const value = parts[i].slice(sepIndex + 1); if (key in out) { - if (!Array.isArray(out[key])) { + if (!ArrayIsArray(out[key])) { out[key] = [out[key]]; } out[key].push(value); diff --git a/lib/internal/trace_events_async_hooks.js b/lib/internal/trace_events_async_hooks.js index 4485b7a8c820a0..87ee63d8ba7426 100644 --- a/lib/internal/trace_events_async_hooks.js +++ b/lib/internal/trace_events_async_hooks.js @@ -1,6 +1,10 @@ 'use strict'; -const { Object, SafeMap, SafeSet } = primordials; +const { + ObjectKeys, + SafeMap, + SafeSet, +} = primordials; const { trace } = internalBinding('trace_events'); const async_wrap = internalBinding('async_wrap'); @@ -20,7 +24,7 @@ const kEnabled = Symbol('enabled'); // Embedder C++ API can't be emitted from async_wrap.cc. Thus they are // emitted using the JavaScript API. To prevent emitting the same event // twice the async_wrap.Providers list is used to filter the events. -const nativeProviders = new SafeSet(Object.keys(async_wrap.Providers)); +const nativeProviders = new SafeSet(ObjectKeys(async_wrap.Providers)); const typeMemory = new SafeMap(); function createHook() { diff --git a/lib/internal/tty.js b/lib/internal/tty.js index faf5df9b42e6b2..98975fa68a436f 100644 --- a/lib/internal/tty.js +++ b/lib/internal/tty.js @@ -173,6 +173,10 @@ function getColorDepth(env = process.env) { return COLORS_256; } + if (env.COLORTERM === 'truecolor' || env.COLORTERM === '24bit') { + return COLORS_16m; + } + if (env.TERM) { if (/^xterm-256/.test(env.TERM)) return COLORS_256; @@ -188,13 +192,10 @@ function getColorDepth(env = process.env) { } } } - + // Move 16 color COLORTERM below 16m and 256 if (env.COLORTERM) { - if (env.COLORTERM === 'truecolor' || env.COLORTERM === '24bit') - return COLORS_16m; return COLORS_16; } - return COLORS_2; } diff --git a/lib/internal/url.js b/lib/internal/url.js index 860fa4d7ad01b3..3c75baf27b0973 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -1,6 +1,17 @@ 'use strict'; -const { Object, Reflect } = primordials; +const { + Array, + Number, + ObjectCreate, + ObjectDefineProperties, + ObjectDefineProperty, + ObjectGetOwnPropertySymbols, + ObjectGetPrototypeOf, + ObjectKeys, + ReflectGetOwnPropertyDescriptor, + ReflectOwnKeys, +} = primordials; const { inspect } = require('internal/util/inspect'); const { @@ -74,8 +85,8 @@ const searchParams = Symbol('query'); const kFormat = Symbol('format'); // https://tc39.github.io/ecma262/#sec-%iteratorprototype%-object -const IteratorPrototype = Object.getPrototypeOf( - Object.getPrototypeOf([][Symbol.iterator]()) +const IteratorPrototype = ObjectGetPrototypeOf( + ObjectGetPrototypeOf([][Symbol.iterator]()) ); const unpairedSurrogateRe = @@ -164,10 +175,10 @@ class URLSearchParams { // Record // Need to use reflection APIs for full spec compliance. this[searchParams] = []; - const keys = Reflect.ownKeys(init); + const keys = ReflectOwnKeys(init); for (let i = 0; i < keys.length; i++) { const key = keys[i]; - const desc = Reflect.getOwnPropertyDescriptor(init, key); + const desc = ReflectGetOwnPropertyDescriptor(init, key); if (desc !== undefined && desc.enumerable) { const typedKey = toUSVString(key); const typedValue = toUSVString(init[key]); @@ -338,7 +349,7 @@ class URL { [inspect.custom](depth, opts) { if (this == null || - Object.getPrototypeOf(this[context]) !== URLContext.prototype) { + ObjectGetPrototypeOf(this[context]) !== URLContext.prototype) { throw new ERR_INVALID_THIS('URL'); } @@ -347,7 +358,7 @@ class URL { const ctor = getConstructorOf(this); - const obj = Object.create({ + const obj = ObjectCreate({ constructor: ctor === null ? URL : ctor }); @@ -374,7 +385,7 @@ class URL { } } -Object.defineProperties(URL.prototype, { +ObjectDefineProperties(URL.prototype, { [kFormat]: { enumerable: false, configurable: false, @@ -846,7 +857,7 @@ function serializeParams(array) { // Mainly to mitigate func-name-matching ESLint rule function defineIDLClass(proto, classStr, obj) { // https://heycam.github.io/webidl/#dfn-class-string - Object.defineProperty(proto, Symbol.toStringTag, { + ObjectDefineProperty(proto, Symbol.toStringTag, { writable: false, enumerable: false, configurable: true, @@ -854,16 +865,16 @@ function defineIDLClass(proto, classStr, obj) { }); // https://heycam.github.io/webidl/#es-operations - for (const key of Object.keys(obj)) { - Object.defineProperty(proto, key, { + for (const key of ObjectKeys(obj)) { + ObjectDefineProperty(proto, key, { writable: true, enumerable: true, configurable: true, value: obj[key] }); } - for (const key of Object.getOwnPropertySymbols(obj)) { - Object.defineProperty(proto, key, { + for (const key of ObjectGetOwnPropertySymbols(obj)) { + ObjectDefineProperty(proto, key, { writable: true, enumerable: false, configurable: true, @@ -1137,7 +1148,7 @@ defineIDLClass(URLSearchParams.prototype, 'URLSearchParams', { }); // https://heycam.github.io/webidl/#es-iterable-entries -Object.defineProperty(URLSearchParams.prototype, Symbol.iterator, { +ObjectDefineProperty(URLSearchParams.prototype, Symbol.iterator, { writable: true, configurable: true, value: URLSearchParams.prototype.entries @@ -1145,7 +1156,7 @@ Object.defineProperty(URLSearchParams.prototype, Symbol.iterator, { // https://heycam.github.io/webidl/#dfn-default-iterator-object function createSearchParamsIterator(target, kind) { - const iterator = Object.create(URLSearchParamsIteratorPrototype); + const iterator = ObjectCreate(URLSearchParamsIteratorPrototype); iterator[context] = { target, kind, @@ -1155,12 +1166,12 @@ function createSearchParamsIterator(target, kind) { } // https://heycam.github.io/webidl/#dfn-iterator-prototype-object -const URLSearchParamsIteratorPrototype = Object.create(IteratorPrototype); +const URLSearchParamsIteratorPrototype = ObjectCreate(IteratorPrototype); defineIDLClass(URLSearchParamsIteratorPrototype, 'URLSearchParams Iterator', { next() { if (!this || - Object.getPrototypeOf(this) !== URLSearchParamsIteratorPrototype) { + ObjectGetPrototypeOf(this) !== URLSearchParamsIteratorPrototype) { throw new ERR_INVALID_THIS('URLSearchParamsIterator'); } @@ -1402,7 +1413,7 @@ function constructUrl(flags, protocol, username, password, ctx.fragment = fragment; ctx.host = host; - const url = Object.create(URL.prototype); + const url = ObjectCreate(URL.prototype); url[context] = ctx; const params = new URLSearchParams(); url[searchParams] = params; diff --git a/lib/internal/util.js b/lib/internal/util.js index 58502f3b7a7a93..1ff25dbc7d2a86 100644 --- a/lib/internal/util.js +++ b/lib/internal/util.js @@ -1,6 +1,17 @@ 'use strict'; -const { Object, Reflect } = primordials; +const { + ArrayFrom, + ArrayIsArray, + ObjectCreate, + ObjectDefineProperties, + ObjectDefineProperty, + ObjectGetOwnPropertyDescriptor, + ObjectGetOwnPropertyDescriptors, + ObjectGetPrototypeOf, + ObjectSetPrototypeOf, + ReflectConstruct, +} = primordials; const { codes: { ERR_INVALID_ARG_TYPE, @@ -65,13 +76,13 @@ function deprecate(fn, msg, code) { } } if (new.target) { - return Reflect.construct(fn, args, new.target); + return ReflectConstruct(fn, args, new.target); } return fn.apply(this, args); } // The wrapper will keep the same prototype as fn to maintain prototype chain - Object.setPrototypeOf(deprecated, fn); + ObjectSetPrototypeOf(deprecated, fn); if (fn.prototype) { // Setting this (rather than using Object.setPrototype, as above) ensures // that calling the unwrapped constructor gives an instanceof the wrapped @@ -166,7 +177,7 @@ function emitExperimentalWarning(feature) { function filterDuplicateStrings(items, low) { const map = new Map(); - for (var i = 0; i < items.length; i++) { + for (let i = 0; i < items.length; i++) { const item = items[i]; const key = item.toLowerCase(); if (low) { @@ -175,7 +186,7 @@ function filterDuplicateStrings(items, low) { map.set(key, item); } } - return Array.from(map.values()).sort(); + return ArrayFrom(map.values()).sort(); } function cachedResult(fn) { @@ -195,14 +206,14 @@ function cachedResult(fn) { // B() instanceof B // true function createClassWrapper(type) { function fn(...args) { - return Reflect.construct(type, args, new.target || type); + return ReflectConstruct(type, args, new.target || type); } // Mask the wrapper function name and length values - Object.defineProperties(fn, { + ObjectDefineProperties(fn, { name: { value: type.name }, length: { value: type.length } }); - Object.setPrototypeOf(fn, type); + ObjectSetPrototypeOf(fn, type); fn.prototype = type.prototype; return fn; } @@ -212,7 +223,7 @@ function getSignalsToNamesMapping() { if (signalsToNamesMapping !== undefined) return signalsToNamesMapping; - signalsToNamesMapping = Object.create(null); + signalsToNamesMapping = ObjectCreate(null); for (const key in signals) { signalsToNamesMapping[signals[key]] = key; } @@ -234,14 +245,14 @@ function convertToValidSignal(signal) { function getConstructorOf(obj) { while (obj) { - const descriptor = Object.getOwnPropertyDescriptor(obj, 'constructor'); + const descriptor = ObjectGetOwnPropertyDescriptor(obj, 'constructor'); if (descriptor !== undefined && typeof descriptor.value === 'function' && descriptor.value.name !== '') { return descriptor.value; } - obj = Object.getPrototypeOf(obj); + obj = ObjectGetPrototypeOf(obj); } return null; @@ -264,7 +275,7 @@ function promisify(original) { if (typeof fn !== 'function') { throw new ERR_INVALID_ARG_TYPE('util.promisify.custom', 'Function', fn); } - return Object.defineProperty(fn, kCustomPromisifiedSymbol, { + return ObjectDefineProperty(fn, kCustomPromisifiedSymbol, { value: fn, enumerable: false, writable: false, configurable: true }); } @@ -281,7 +292,7 @@ function promisify(original) { } if (argumentNames !== undefined && values.length > 1) { const obj = {}; - for (var i = 0; i < argumentNames.length; i++) + for (let i = 0; i < argumentNames.length; i++) obj[argumentNames[i]] = values[i]; resolve(obj); } else { @@ -291,14 +302,14 @@ function promisify(original) { }); } - Object.setPrototypeOf(fn, Object.getPrototypeOf(original)); + ObjectSetPrototypeOf(fn, ObjectGetPrototypeOf(original)); - Object.defineProperty(fn, kCustomPromisifiedSymbol, { + ObjectDefineProperty(fn, kCustomPromisifiedSymbol, { value: fn, enumerable: false, writable: false, configurable: true }); - return Object.defineProperties( + return ObjectDefineProperties( fn, - Object.getOwnPropertyDescriptors(original) + ObjectGetOwnPropertyDescriptors(original) ); } @@ -352,7 +363,7 @@ function isInsideNodeModules() { // Iterate over all stack frames and look for the first one not coming // from inside Node.js itself: - if (Array.isArray(stack)) { + if (ArrayIsArray(stack)) { for (const frame of stack) { const filename = frame.getFileName(); // If a filename does not start with / or contain \, diff --git a/lib/internal/util/comparisons.js b/lib/internal/util/comparisons.js index 52cd11429e6ac6..b2784217171410 100644 --- a/lib/internal/util/comparisons.js +++ b/lib/internal/util/comparisons.js @@ -1,19 +1,21 @@ 'use strict'; const { - BigIntPrototype, - BooleanPrototype, - DatePrototype, - Number, - NumberPrototype, - Object, - ObjectPrototype: { - hasOwnProperty, - propertyIsEnumerable, - toString: objectToString - }, - StringPrototype, - SymbolPrototype + ArrayIsArray, + BigIntPrototypeValueOf, + BooleanPrototypeValueOf, + DatePrototypeGetTime, + NumberIsNaN, + NumberPrototypeValueOf, + ObjectGetOwnPropertySymbols, + ObjectGetPrototypeOf, + ObjectIs, + ObjectKeys, + ObjectPrototypeHasOwnProperty, + ObjectPrototypePropertyIsEnumerable, + ObjectPrototypeToString, + StringPrototypeValueOf, + SymbolPrototypeValueOf, } = primordials; const { compare } = internalBinding('buffer'); @@ -83,24 +85,24 @@ function areEqualArrayBuffers(buf1, buf2) { function isEqualBoxedPrimitive(val1, val2) { if (isNumberObject(val1)) { return isNumberObject(val2) && - Object.is(NumberPrototype.valueOf(val1), - NumberPrototype.valueOf(val2)); + ObjectIs(NumberPrototypeValueOf(val1), + NumberPrototypeValueOf(val2)); } if (isStringObject(val1)) { return isStringObject(val2) && - StringPrototype.valueOf(val1) === StringPrototype.valueOf(val2); + StringPrototypeValueOf(val1) === StringPrototypeValueOf(val2); } if (isBooleanObject(val1)) { return isBooleanObject(val2) && - BooleanPrototype.valueOf(val1) === BooleanPrototype.valueOf(val2); + BooleanPrototypeValueOf(val1) === BooleanPrototypeValueOf(val2); } if (isBigIntObject(val1)) { return isBigIntObject(val2) && - BigIntPrototype.valueOf(val1) === BigIntPrototype.valueOf(val2); + BigIntPrototypeValueOf(val1) === BigIntPrototypeValueOf(val2); } if (isSymbolObject(val1)) { return isSymbolObject(val2) && - SymbolPrototype.valueOf(val1) === SymbolPrototype.valueOf(val2); + SymbolPrototypeValueOf(val1) === SymbolPrototypeValueOf(val2); } return false; } @@ -129,19 +131,19 @@ function innerDeepEqual(val1, val2, strict, memos) { if (val1 === val2) { if (val1 !== 0) return true; - return strict ? Object.is(val1, val2) : true; + return strict ? ObjectIs(val1, val2) : true; } // Check more closely if val1 and val2 are equal. if (strict) { if (typeof val1 !== 'object') { - return typeof val1 === 'number' && Number.isNaN(val1) && - Number.isNaN(val2); + return typeof val1 === 'number' && NumberIsNaN(val1) && + NumberIsNaN(val2); } if (typeof val2 !== 'object' || val1 === null || val2 === null) { return false; } - if (Object.getPrototypeOf(val1) !== Object.getPrototypeOf(val2)) { + if (ObjectGetPrototypeOf(val1) !== ObjectGetPrototypeOf(val2)) { return false; } } else { @@ -156,13 +158,13 @@ function innerDeepEqual(val1, val2, strict, memos) { return false; } } - const val1Tag = objectToString(val1); - const val2Tag = objectToString(val2); + const val1Tag = ObjectPrototypeToString(val1); + const val2Tag = ObjectPrototypeToString(val2); if (val1Tag !== val2Tag) { return false; } - if (Array.isArray(val1)) { + if (ArrayIsArray(val1)) { // Check for sparse arrays and general fast path if (val1.length !== val2.length) { return false; @@ -179,7 +181,7 @@ function innerDeepEqual(val1, val2, strict, memos) { return keyCheck(val1, val2, strict, memos, kNoIterator); } if (isDate(val1)) { - if (DatePrototype.getTime(val1) !== DatePrototype.getTime(val2)) { + if (DatePrototypeGetTime(val1) !== DatePrototypeGetTime(val2)) { return false; } } else if (isRegExp(val1)) { @@ -233,7 +235,7 @@ function innerDeepEqual(val1, val2, strict, memos) { } function getEnumerables(val, keys) { - return keys.filter((k) => propertyIsEnumerable(val, k)); + return keys.filter((k) => ObjectPrototypePropertyIsEnumerable(val, k)); } function keyCheck(val1, val2, strict, memos, iterationType, aKeys) { @@ -245,8 +247,8 @@ function keyCheck(val1, val2, strict, memos, iterationType, aKeys) { // d) For Sets and Maps, equal contents // Note: this accounts for both named and indexed properties on Arrays. if (arguments.length === 5) { - aKeys = Object.keys(val1); - const bKeys = Object.keys(val2); + aKeys = ObjectKeys(val1); + const bKeys = ObjectKeys(val2); // The pair must have the same number of owned properties. if (aKeys.length !== bKeys.length) { @@ -257,34 +259,34 @@ function keyCheck(val1, val2, strict, memos, iterationType, aKeys) { // Cheap key test let i = 0; for (; i < aKeys.length; i++) { - if (!hasOwnProperty(val2, aKeys[i])) { + if (!ObjectPrototypeHasOwnProperty(val2, aKeys[i])) { return false; } } if (strict && arguments.length === 5) { - const symbolKeysA = Object.getOwnPropertySymbols(val1); + const symbolKeysA = ObjectGetOwnPropertySymbols(val1); if (symbolKeysA.length !== 0) { let count = 0; for (i = 0; i < symbolKeysA.length; i++) { const key = symbolKeysA[i]; - if (propertyIsEnumerable(val1, key)) { - if (!propertyIsEnumerable(val2, key)) { + if (ObjectPrototypePropertyIsEnumerable(val1, key)) { + if (!ObjectPrototypePropertyIsEnumerable(val2, key)) { return false; } aKeys.push(key); count++; - } else if (propertyIsEnumerable(val2, key)) { + } else if (ObjectPrototypePropertyIsEnumerable(val2, key)) { return false; } } - const symbolKeysB = Object.getOwnPropertySymbols(val2); + const symbolKeysB = ObjectGetOwnPropertySymbols(val2); if (symbolKeysA.length !== symbolKeysB.length && getEnumerables(val2, symbolKeysB).length !== count) { return false; } } else { - const symbolKeysB = Object.getOwnPropertySymbols(val2); + const symbolKeysB = ObjectGetOwnPropertySymbols(val2); if (symbolKeysB.length !== 0 && getEnumerables(val2, symbolKeysB).length !== 0) { return false; @@ -362,7 +364,7 @@ function findLooseMatchingPrimitives(prim) { // a regular number and not NaN. // Fall through case 'number': - if (Number.isNaN(prim)) { + if (NumberIsNaN(prim)) { return false; } } @@ -519,24 +521,24 @@ function objEquiv(a, b, strict, keys, memos, iterationType) { } } else if (iterationType === kIsArray) { for (; i < a.length; i++) { - if (hasOwnProperty(a, i)) { - if (!hasOwnProperty(b, i) || + if (ObjectPrototypeHasOwnProperty(a, i)) { + if (!ObjectPrototypeHasOwnProperty(b, i) || !innerDeepEqual(a[i], b[i], strict, memos)) { return false; } - } else if (hasOwnProperty(b, i)) { + } else if (ObjectPrototypeHasOwnProperty(b, i)) { return false; } else { // Array is sparse. - const keysA = Object.keys(a); + const keysA = ObjectKeys(a); for (; i < keysA.length; i++) { const key = keysA[i]; - if (!hasOwnProperty(b, key) || + if (!ObjectPrototypeHasOwnProperty(b, key) || !innerDeepEqual(a[key], b[key], strict, memos)) { return false; } } - if (keysA.length !== Object.keys(b).length) { + if (keysA.length !== ObjectKeys(b).length) { return false; } return true; diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index 2f922f6659931c..f0888dc2a2dac9 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -1,24 +1,46 @@ 'use strict'; const { - BigIntPrototype, - BooleanPrototype, - DatePrototype, - ErrorPrototype, - JSON, - MapPrototype, - Math, - NumberPrototype, - Object, - ObjectPrototype: { - hasOwnProperty, - propertyIsEnumerable - }, - RegExpPrototype, - SetPrototype, - StringPrototype, - SymbolPrototype, - uncurryThis + Array, + ArrayIsArray, + BigIntPrototypeValueOf, + BooleanPrototypeValueOf, + DatePrototypeGetTime, + DatePrototypeToISOString, + DatePrototypeToString, + ErrorPrototypeToString, + JSONStringify, + MapPrototypeEntries, + MathFloor, + MathMax, + MathMin, + MathRound, + MathSqrt, + Number, + NumberIsNaN, + NumberPrototypeValueOf, + ObjectAssign, + ObjectCreate, + ObjectDefineProperties, + ObjectDefineProperty, + ObjectGetOwnPropertyDescriptor, + ObjectGetOwnPropertyDescriptors, + ObjectGetOwnPropertyNames, + ObjectGetOwnPropertySymbols, + ObjectGetPrototypeOf, + ObjectIs, + ObjectKeys, + ObjectPrototypeHasOwnProperty, + ObjectPrototypePropertyIsEnumerable, + ObjectSeal, + RegExpPrototypeToString, + SetPrototypeValues, + StringPrototypeValueOf, + SymbolPrototypeToString, + SymbolPrototypeValueOf, + SymbolIterator, + SymbolToStringTag, + uncurryThis, } = primordials; const { @@ -94,10 +116,10 @@ const { NativeModule } = require('internal/bootstrap/loaders'); let hexSlice; const builtInObjects = new Set( - Object.getOwnPropertyNames(global).filter((e) => /^([A-Z][a-z]+)+$/.test(e)) + ObjectGetOwnPropertyNames(global).filter((e) => /^([A-Z][a-z]+)+$/.test(e)) ); -const inspectDefaultOptions = Object.seal({ +const inspectDefaultOptions = ObjectSeal({ showHidden: false, depth: 2, colors: false, @@ -153,7 +175,7 @@ const meta = [ function getUserOptions(ctx) { const obj = { stylize: ctx.stylize }; - for (const key of Object.keys(inspectDefaultOptions)) { + for (const key of ObjectKeys(inspectDefaultOptions)) { obj[key] = ctx[key]; } if (ctx.userOptions === undefined) @@ -202,12 +224,14 @@ function inspect(value, opts) { if (typeof opts === 'boolean') { ctx.showHidden = opts; } else if (opts) { - const optKeys = Object.keys(opts); + const optKeys = ObjectKeys(opts); for (const key of optKeys) { // TODO(BridgeAR): Find a solution what to do about stylize. Either make // this function public or add a new API with a similar or better // functionality. - if (hasOwnProperty(inspectDefaultOptions, key) || key === 'stylize') { + if ( + ObjectPrototypeHasOwnProperty(inspectDefaultOptions, key) || + key === 'stylize') { ctx[key] = opts[key]; } else if (ctx.userOptions === undefined) { // This is required to pass through the actual user input. @@ -222,7 +246,7 @@ function inspect(value, opts) { } inspect.custom = customInspectSymbol; -Object.defineProperty(inspect, 'defaultOptions', { +ObjectDefineProperty(inspect, 'defaultOptions', { get() { return inspectDefaultOptions; }, @@ -230,12 +254,12 @@ Object.defineProperty(inspect, 'defaultOptions', { if (options === null || typeof options !== 'object') { throw new ERR_INVALID_ARG_TYPE('options', 'Object', options); } - return Object.assign(inspectDefaultOptions, options); + return ObjectAssign(inspectDefaultOptions, options); } }); // http://en.wikipedia.org/wiki/ANSI_escape_code#graphics -inspect.colors = Object.assign(Object.create(null), { +inspect.colors = ObjectAssign(ObjectCreate(null), { bold: [1, 22], italic: [3, 23], underline: [4, 24], @@ -252,7 +276,7 @@ inspect.colors = Object.assign(Object.create(null), { }); // Don't use 'blue' not visible on cmd.exe -inspect.styles = Object.assign(Object.create(null), { +inspect.styles = ObjectAssign(ObjectCreate(null), { special: 'cyan', number: 'yellow', bigint: 'yellow', @@ -355,14 +379,14 @@ function getConstructorName(obj, ctx, recurseTimes) { let firstProto; const tmp = obj; while (obj) { - const descriptor = Object.getOwnPropertyDescriptor(obj, 'constructor'); + const descriptor = ObjectGetOwnPropertyDescriptor(obj, 'constructor'); if (descriptor !== undefined && typeof descriptor.value === 'function' && descriptor.value.name !== '') { return descriptor.value.name; } - obj = Object.getPrototypeOf(obj); + obj = ObjectGetPrototypeOf(obj); if (firstProto === undefined) { firstProto = obj; } @@ -408,9 +432,9 @@ function getPrefix(constructor, tag, fallback) { // Look up the keys of the object. function getKeys(value, showHidden) { let keys; - const symbols = Object.getOwnPropertySymbols(value); + const symbols = ObjectGetOwnPropertySymbols(value); if (showHidden) { - keys = Object.getOwnPropertyNames(value); + keys = ObjectGetOwnPropertyNames(value); if (symbols.length !== 0) keys.push(...symbols); } else { @@ -420,14 +444,15 @@ function getKeys(value, showHidden) { // TODO(devsnek): track https://github.com/tc39/ecma262/issues/1209 // and modify this logic as needed. try { - keys = Object.keys(value); + keys = ObjectKeys(value); } catch (err) { assert(isNativeError(err) && err.name === 'ReferenceError' && isModuleNamespaceObject(value)); - keys = Object.getOwnPropertyNames(value); + keys = ObjectGetOwnPropertyNames(value); } if (symbols.length !== 0) { - keys.push(...symbols.filter((key) => propertyIsEnumerable(value, key))); + const filter = (key) => ObjectPrototypePropertyIsEnumerable(value, key); + keys.push(...symbols.filter(filter)); } } return keys; @@ -492,12 +517,12 @@ function clazzWithNullPrototype(clazz, name) { } } class NullPrototype extends clazz { - get [Symbol.toStringTag]() { + get [SymbolToStringTag]() { return ''; } } - Object.defineProperty(NullPrototype.prototype.constructor, 'name', - { value: `[${name}: null prototype]` }); + ObjectDefineProperty(NullPrototype.prototype.constructor, 'name', + { value: `[${name}: null prototype]` }); lazyNullPrototypeCache.set(clazz, NullPrototype); return NullPrototype; } @@ -506,11 +531,11 @@ function noPrototypeIterator(ctx, value, recurseTimes) { let newVal; if (isSet(value)) { const clazz = clazzWithNullPrototype(Set, 'Set'); - newVal = new clazz(SetPrototype.values(value)); + newVal = new clazz(SetPrototypeValues(value)); } else if (isMap(value)) { const clazz = clazzWithNullPrototype(Map, 'Map'); - newVal = new clazz(MapPrototype.entries(value)); - } else if (Array.isArray(value)) { + newVal = new clazz(MapPrototypeEntries(value)); + } else if (ArrayIsArray(value)) { const clazz = clazzWithNullPrototype(Array, 'Array'); newVal = new clazz(value.length); } else if (isTypedArray(value)) { @@ -519,7 +544,7 @@ function noPrototypeIterator(ctx, value, recurseTimes) { newVal = new clazz(value); } if (newVal !== undefined) { - Object.defineProperties(newVal, Object.getOwnPropertyDescriptors(value)); + ObjectDefineProperties(newVal, ObjectGetOwnPropertyDescriptors(value)); return formatRaw(ctx, newVal, recurseTimes); } } @@ -595,13 +620,15 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { let keys; const constructor = getConstructorName(value, ctx, recurseTimes); - let tag = value[Symbol.toStringTag]; + let tag = value[SymbolToStringTag]; // Only list the tag in case it's non-enumerable / not an own property. // Otherwise we'd print this twice. if (typeof tag !== 'string' || (tag !== '' && - (ctx.showHidden ? hasOwnProperty : propertyIsEnumerable)( - value, Symbol.toStringTag + (ctx.showHidden ? + ObjectPrototypeHasOwnProperty : + ObjectPrototypePropertyIsEnumerable)( + value, SymbolToStringTag ))) { tag = ''; } @@ -615,9 +642,9 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { let extrasType = kObjectType; // Iterators and the rest are split to reduce checks. - if (value[Symbol.iterator]) { + if (value[SymbolIterator]) { noIterator = false; - if (Array.isArray(value)) { + if (ArrayIsArray(value)) { keys = getOwnNonIndexProperties(value, filter); // Only set the constructor for non ordinary ("Array [...]") arrays. const prefix = getPrefix(constructor, tag, 'Array'); @@ -680,7 +707,7 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { return ctx.stylize(base, 'special'); } else if (isRegExp(value)) { // Make RegExps say that they are RegExps - base = RegExpPrototype.toString( + base = RegExpPrototypeToString( constructor !== null ? value : new RegExp(value) ); const prefix = getPrefix(constructor, tag, 'RegExp'); @@ -690,9 +717,9 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { return ctx.stylize(base, 'regexp'); } else if (isDate(value)) { // Make dates with properties first say the date - base = Number.isNaN(DatePrototype.getTime(value)) ? - DatePrototype.toString(value) : - DatePrototype.toISOString(value); + base = NumberIsNaN(DatePrototypeGetTime(value)) ? + DatePrototypeToString(value) : + DatePrototypeToISOString(value); const prefix = getPrefix(constructor, tag, 'Date'); if (prefix !== 'Date ') base = `${prefix}${base}`; @@ -843,23 +870,23 @@ function getBoxedBase(value, ctx, keys, constructor, tag) { let fn; let type; if (isNumberObject(value)) { - fn = NumberPrototype; + fn = NumberPrototypeValueOf; type = 'Number'; } else if (isStringObject(value)) { - fn = StringPrototype; + fn = StringPrototypeValueOf; type = 'String'; // For boxed Strings, we have to remove the 0-n indexed entries, // since they just noisy up the output and are redundant // Make boxed primitive Strings look like such keys.splice(0, value.length); } else if (isBooleanObject(value)) { - fn = BooleanPrototype; + fn = BooleanPrototypeValueOf; type = 'Boolean'; } else if (isBigIntObject(value)) { - fn = BigIntPrototype; + fn = BigIntPrototypeValueOf; type = 'BigInt'; } else { - fn = SymbolPrototype; + fn = SymbolPrototypeValueOf; type = 'Symbol'; } let base = `[${type}`; @@ -870,7 +897,7 @@ function getBoxedBase(value, ctx, keys, constructor, tag) { base += ` (${constructor})`; } } - base += `: ${formatPrimitive(stylizeNoColor, fn.valueOf(value), ctx)}]`; + base += `: ${formatPrimitive(stylizeNoColor, fn(value), ctx)}]`; if (tag !== '' && tag !== constructor) { base += ` [${tag}]`; } @@ -907,12 +934,12 @@ function getFunctionBase(value, constructor, tag) { } function formatError(err, constructor, tag, ctx) { - let stack = err.stack || ErrorPrototype.toString(err); + const name = err.name != null ? String(err.name) : 'Error'; + let len = name.length; + let stack = err.stack ? String(err.stack) : ErrorPrototypeToString(err); // A stack trace may contain arbitrary data. Only manipulate the output // for "regular errors" (errors that "look normal") for now. - const name = err.name || 'Error'; - let len = name.length; if (constructor === null || (name.endsWith('Error') && stack.startsWith(name) && @@ -1010,22 +1037,22 @@ function groupArrayElements(ctx, output, value) { (totalLength / actualMax > 5 || maxLength <= 6)) { const approxCharHeights = 2.5; - const averageBias = Math.sqrt(actualMax - totalLength / output.length); - const biasedMax = Math.max(actualMax - 3 - averageBias, 1); + const averageBias = MathSqrt(actualMax - totalLength / output.length); + const biasedMax = MathMax(actualMax - 3 - averageBias, 1); // Dynamically check how many columns seem possible. - const columns = Math.min( + const columns = MathMin( // Ideally a square should be drawn. We expect a character to be about 2.5 // times as high as wide. This is the area formula to calculate a square // which contains n rectangles of size `actualMax * approxCharHeights`. // Divide that by `actualMax` to receive the correct number of columns. // The added bias increases the columns for short entries. - Math.round( - Math.sqrt( + MathRound( + MathSqrt( approxCharHeights * biasedMax * outputLength ) / biasedMax ), // Do not exceed the breakLength. - Math.floor((ctx.breakLength - ctx.indentationLvl) / actualMax), + MathFloor((ctx.breakLength - ctx.indentationLvl) / actualMax), // Limit array grouping for small `compact` modes as the user requested // minimal grouping. ctx.compact * 4, @@ -1059,7 +1086,7 @@ function groupArrayElements(ctx, output, value) { // Each iteration creates a single line of grouped entries. for (let i = 0; i < outputLength; i += columns) { // The last lines may contain less entries than columns. - const max = Math.min(i + columns, outputLength); + const max = MathMin(i + columns, outputLength); let str = ''; let j = i; for (; j < max - 1; j++) { @@ -1103,7 +1130,7 @@ function handleMaxCallStackSize(ctx, err, constructorName, indentationLvl) { function formatNumber(fn, value) { // Format -0 as '-0'. Checking `value === -0` won't distinguish 0 from -0. - return fn(Object.is(value, -0) ? '-0' : `${value}`, 'number'); + return fn(ObjectIs(value, -0) ? '-0' : `${value}`, 'number'); } function formatBigInt(fn, value) { @@ -1131,12 +1158,12 @@ function formatPrimitive(fn, value, ctx) { if (typeof value === 'undefined') return fn('undefined', 'undefined'); // es6 symbol primitive - return fn(SymbolPrototype.toString(value), 'symbol'); + return fn(SymbolPrototypeToString(value), 'symbol'); } function formatNamespaceObject(ctx, value, recurseTimes, keys) { const output = new Array(keys.length); - for (var i = 0; i < keys.length; i++) { + for (let i = 0; i < keys.length; i++) { try { output[i] = formatProperty(ctx, value, recurseTimes, keys[i], kObjectType); @@ -1163,7 +1190,7 @@ function formatNamespaceObject(ctx, value, recurseTimes, keys) { // The array is sparse and/or has extra keys function formatSpecialArray(ctx, value, recurseTimes, maxLength, output, i) { - const keys = Object.keys(value); + const keys = ObjectKeys(value); let index = i; for (; i < keys.length && output.length < maxLength; i++) { const key = keys[i]; @@ -1210,7 +1237,7 @@ function formatArrayBuffer(ctx, value) { } if (hexSlice === undefined) hexSlice = uncurryThis(require('buffer').Buffer.prototype.hexSlice); - let str = hexSlice(buffer, 0, Math.min(ctx.maxArrayLength, buffer.length)) + let str = hexSlice(buffer, 0, MathMin(ctx.maxArrayLength, buffer.length)) .replace(/(.{2})/g, '$1 ').trim(); const remaining = buffer.length - ctx.maxArrayLength; if (remaining > 0) @@ -1220,13 +1247,13 @@ function formatArrayBuffer(ctx, value) { function formatArray(ctx, value, recurseTimes) { const valLen = value.length; - const len = Math.min(Math.max(0, ctx.maxArrayLength), valLen); + const len = MathMin(MathMax(0, ctx.maxArrayLength), valLen); const remaining = valLen - len; const output = []; - for (var i = 0; i < len; i++) { + for (let i = 0; i < len; i++) { // Special handle sparse arrays. - if (!hasOwnProperty(value, i)) { + if (!ObjectPrototypeHasOwnProperty(value, i)) { return formatSpecialArray(ctx, value, recurseTimes, len, output, i); } output.push(formatProperty(ctx, value, recurseTimes, i, kArrayType)); @@ -1237,7 +1264,7 @@ function formatArray(ctx, value, recurseTimes) { } function formatTypedArray(ctx, value, recurseTimes) { - const maxLength = Math.min(Math.max(0, ctx.maxArrayLength), value.length); + const maxLength = MathMin(MathMax(0, ctx.maxArrayLength), value.length); const remaining = value.length - maxLength; const output = new Array(maxLength); const elementFormatter = value.length > 0 && typeof value[0] === 'number' ? @@ -1275,7 +1302,7 @@ function formatSet(ctx, value, recurseTimes) { ctx.indentationLvl -= 2; // With `showHidden`, `length` will display as a hidden property for // arrays. For consistency's sake, do the same for `size`, even though this - // property isn't selected by Object.getOwnPropertyNames(). + // property isn't selected by ObjectGetOwnPropertyNames(). if (ctx.showHidden) output.push(`[size]: ${ctx.stylize(`${value.size}`, 'number')}`); return output; @@ -1296,11 +1323,11 @@ function formatMap(ctx, value, recurseTimes) { } function formatSetIterInner(ctx, recurseTimes, entries, state) { - const maxArrayLength = Math.max(ctx.maxArrayLength, 0); - const maxLength = Math.min(maxArrayLength, entries.length); + const maxArrayLength = MathMax(ctx.maxArrayLength, 0); + const maxLength = MathMin(maxArrayLength, entries.length); let output = new Array(maxLength); ctx.indentationLvl += 2; - for (var i = 0; i < maxLength; i++) { + for (let i = 0; i < maxLength; i++) { output[i] = formatValue(ctx, entries[i], recurseTimes); } ctx.indentationLvl -= 2; @@ -1318,11 +1345,11 @@ function formatSetIterInner(ctx, recurseTimes, entries, state) { } function formatMapIterInner(ctx, recurseTimes, entries, state) { - const maxArrayLength = Math.max(ctx.maxArrayLength, 0); + const maxArrayLength = MathMax(ctx.maxArrayLength, 0); // Entries exist as [key1, val1, key2, val2, ...] const len = entries.length / 2; const remaining = len - maxArrayLength; - const maxLength = Math.min(maxArrayLength, len); + const maxLength = MathMin(maxArrayLength, len); let output = new Array(maxLength); let i = 0; ctx.indentationLvl += 2; @@ -1401,7 +1428,7 @@ function formatPromise(ctx, value, recurseTimes) { function formatProperty(ctx, value, recurseTimes, key, type) { let name, str; let extra = ' '; - const desc = Object.getOwnPropertyDescriptor(value, key) || + const desc = ObjectGetOwnPropertyDescriptor(value, key) || { value: value[key], enumerable: true }; if (desc.value !== undefined) { const diff = (type !== kObjectType || ctx.compact !== true) ? 2 : 3; @@ -1468,7 +1495,7 @@ function isBelowBreakLength(ctx, output, start, base) { let totalLength = output.length + start; if (totalLength + output.length > ctx.breakLength) return false; - for (var i = 0; i < output.length; i++) { + for (let i = 0; i < output.length; i++) { if (ctx.colors) { totalLength += removeColors(output[i]).length; } else { @@ -1542,16 +1569,41 @@ function reduceToSingleString( return `${braces[0]}${ln}${join(output, `,\n${indentation} `)} ${braces[1]}`; } +function hasBuiltInToString(value) { + // Count objects that have no `toString` function as built-in. + if (typeof value.toString !== 'function') { + return true; + } + + // The object has a own `toString` property. Thus it's not not a built-in one. + if (ObjectPrototypeHasOwnProperty(value, 'toString')) { + return false; + } + + // Find the object that has the `toString` property as own property in the + // prototype chain. + let pointer = value; + do { + pointer = ObjectGetPrototypeOf(pointer); + } while (!ObjectPrototypeHasOwnProperty(pointer, 'toString')); + + // Check closer if the object is a built-in. + const descriptor = ObjectGetOwnPropertyDescriptor(pointer, 'constructor'); + return descriptor !== undefined && + typeof descriptor.value === 'function' && + builtInObjects.has(descriptor.value.name); +} + const firstErrorLine = (error) => error.message.split('\n')[0]; let CIRCULAR_ERROR_MESSAGE; function tryStringify(arg) { try { - return JSON.stringify(arg); + return JSONStringify(arg); } catch (err) { // Populate the circular error message lazily if (!CIRCULAR_ERROR_MESSAGE) { try { - const a = {}; a.a = a; JSON.stringify(a); + const a = {}; a.a = a; JSONStringify(a); } catch (err) { CIRCULAR_ERROR_MESSAGE = firstErrorLine(err); } @@ -1589,7 +1641,7 @@ function formatWithOptionsInternal(inspectOptions, ...args) { let tempStr; let lastPos = 0; - for (var i = 0; i < first.length - 1; i++) { + for (let i = 0; i < first.length - 1; i++) { if (first.charCodeAt(i) === 37) { // '%' const nextChar = first.charCodeAt(++i); if (a + 1 !== args.length) { @@ -1600,28 +1652,17 @@ function formatWithOptionsInternal(inspectOptions, ...args) { tempStr = formatNumber(stylizeNoColor, tempArg); } else if (typeof tempArg === 'bigint') { tempStr = `${tempArg}n`; + } else if (typeof tempArg !== 'object' || + tempArg === null || + !hasBuiltInToString(tempArg)) { + tempStr = String(tempArg); } else { - let constr; - if (typeof tempArg !== 'object' || - tempArg === null || - (typeof tempArg.toString === 'function' && - // A direct own property. - (hasOwnProperty(tempArg, 'toString') || - // A direct own property on the constructor prototype in - // case the constructor is not an built-in object. - ((constr = tempArg.constructor) && - !builtInObjects.has(constr.name) && - constr.prototype && - hasOwnProperty(constr.prototype, 'toString'))))) { - tempStr = String(tempArg); - } else { - tempStr = inspect(tempArg, { - ...inspectOptions, - compact: 3, - colors: false, - depth: 0 - }); - } + tempStr = inspect(tempArg, { + ...inspectOptions, + compact: 3, + colors: false, + depth: 0 + }); } break; case 106: // 'j' diff --git a/lib/internal/util/inspector.js b/lib/internal/util/inspector.js index 5230137fce6ef9..5f11eff21a2a15 100644 --- a/lib/internal/util/inspector.js +++ b/lib/internal/util/inspector.js @@ -1,6 +1,8 @@ 'use strict'; -const { Object } = primordials; +const { + ObjectKeys, +} = primordials; let session; function sendInspectorCommand(cb, onError) { @@ -36,7 +38,7 @@ function installConsoleExtensions(commandLineApi) { // Wrap a console implemented by Node.js with features from the VM inspector function wrapConsole(consoleFromNode, consoleFromVM) { const { consoleCall } = internalBinding('inspector'); - for (const key of Object.keys(consoleFromVM)) { + for (const key of ObjectKeys(consoleFromVM)) { // If global console has the same method as inspector console, // then wrap these two methods into one. Native wrapper will preserve // the original stack. diff --git a/lib/internal/util/types.js b/lib/internal/util/types.js index 3f05ad06ac53be..676f386a2458d4 100644 --- a/lib/internal/util/types.js +++ b/lib/internal/util/types.js @@ -1,16 +1,19 @@ 'use strict'; -const { Object, uncurryThis } = primordials; +const { + ArrayBufferIsView, + ObjectGetOwnPropertyDescriptor, + ObjectGetPrototypeOf, + SymbolToStringTag, + uncurryThis, +} = primordials; -const TypedArrayPrototype = Object.getPrototypeOf(Uint8Array.prototype); +const TypedArrayPrototype = ObjectGetPrototypeOf(Uint8Array.prototype); const TypedArrayProto_toStringTag = uncurryThis( - Object.getOwnPropertyDescriptor(TypedArrayPrototype, - Symbol.toStringTag).get); - -// Cached to make sure no userland code can tamper with it. -const isArrayBufferView = ArrayBuffer.isView; + ObjectGetOwnPropertyDescriptor(TypedArrayPrototype, + SymbolToStringTag).get); function isTypedArray(value) { return TypedArrayProto_toStringTag(value) !== undefined; @@ -62,7 +65,7 @@ function isBigUint64Array(value) { module.exports = { ...internalBinding('types'), - isArrayBufferView, + isArrayBufferView: ArrayBufferIsView, isTypedArray, isUint8Array, isUint8ClampedArray, diff --git a/lib/internal/v8_prof_polyfill.js b/lib/internal/v8_prof_polyfill.js index 7d8d3ff0202d20..33e57835666650 100644 --- a/lib/internal/v8_prof_polyfill.js +++ b/lib/internal/v8_prof_polyfill.js @@ -77,7 +77,7 @@ try { const fd = fs.openSync(logFile, 'r'); const buf = Buffer.allocUnsafe(4096); const dec = new (require('string_decoder').StringDecoder)('utf-8'); -var line = ''; +let line = ''; { const message = versionCheck(peekline(), process.versions.v8); @@ -125,7 +125,7 @@ function versionCheck(firstLine, expected) { return 'Unable to read v8-version from log file.'; } // Compare major, minor and build; ignore the patch and candidate fields. - for (var i = 0; i < 3; i++) + for (let i = 0; i < 3; i++) if (curVer[i] !== firstLine[i + 1]) return 'Testing v8 version different from logging version'; } diff --git a/lib/internal/v8_prof_processor.js b/lib/internal/v8_prof_processor.js index 7a942d3225a2a9..d647d4749f9adc 100644 --- a/lib/internal/v8_prof_processor.js +++ b/lib/internal/v8_prof_processor.js @@ -1,6 +1,8 @@ 'use strict'; -const { JSON } = primordials; +const { + JSONStringify, +} = primordials; const vm = require('vm'); @@ -32,7 +34,7 @@ if (process.platform === 'darwin') { } tickArguments.push.apply(tickArguments, process.argv.slice(1)); script = `(function(module, require) { - arguments = ${JSON.stringify(tickArguments)}; + arguments = ${JSONStringify(tickArguments)}; function write (s) { process.stdout.write(s) } function printErr(err) { console.error(err); } ${script} diff --git a/lib/internal/validators.js b/lib/internal/validators.js index 7fd4d5180e9231..079ab3d2f53c20 100644 --- a/lib/internal/validators.js +++ b/lib/internal/validators.js @@ -1,5 +1,11 @@ 'use strict'; +const { + NumberIsInteger, + NumberMAX_SAFE_INTEGER, + NumberMIN_SAFE_INTEGER, +} = primordials; + const { hideStackFrames, codes: { @@ -14,7 +20,6 @@ const { isArrayBufferView } = require('internal/util/types'); const { signals } = internalBinding('constants').os; -const { MAX_SAFE_INTEGER, MIN_SAFE_INTEGER } = Number; function isInt32(value) { return value === (value | 0); @@ -63,10 +68,10 @@ function parseMode(value, name, def) { } const validateInteger = hideStackFrames( - (value, name, min = MIN_SAFE_INTEGER, max = MAX_SAFE_INTEGER) => { + (value, name, min = NumberMIN_SAFE_INTEGER, max = NumberMAX_SAFE_INTEGER) => { if (typeof value !== 'number') throw new ERR_INVALID_ARG_TYPE(name, 'number', value); - if (!Number.isInteger(value)) + if (!NumberIsInteger(value)) throw new ERR_OUT_OF_RANGE(name, 'an integer', value); if (value < min || value > max) throw new ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value); @@ -80,7 +85,7 @@ const validateInt32 = hideStackFrames( if (typeof value !== 'number') { throw new ERR_INVALID_ARG_TYPE(name, 'number', value); } - if (!Number.isInteger(value)) { + if (!NumberIsInteger(value)) { throw new ERR_OUT_OF_RANGE(name, 'an integer', value); } throw new ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value); @@ -96,7 +101,7 @@ const validateUint32 = hideStackFrames((value, name, positive) => { if (typeof value !== 'number') { throw new ERR_INVALID_ARG_TYPE(name, 'number', value); } - if (!Number.isInteger(value)) { + if (!NumberIsInteger(value)) { throw new ERR_OUT_OF_RANGE(name, 'an integer', value); } const min = positive ? 1 : 0; diff --git a/lib/internal/vm/module.js b/lib/internal/vm/module.js index 169313c8dda8cc..88a276e217a7fd 100644 --- a/lib/internal/vm/module.js +++ b/lib/internal/vm/module.js @@ -1,6 +1,12 @@ 'use strict'; -const { Object, Symbol, SafePromise } = primordials; +const { + ArrayIsArray, + ObjectCreate, + ObjectDefineProperty, + Symbol, + SafePromise, +} = primordials; const { isContext } = internalBinding('contextify'); const { isModuleNamespaceObject } = require('internal/util/types'); @@ -89,7 +95,7 @@ class Module { context[kPerContextModuleId] += 1; } else { identifier = `${defaultModuleName}(0)`; - Object.defineProperty(context, kPerContextModuleId, { + ObjectDefineProperty(context, kPerContextModuleId, { value: 1, writable: true, enumerable: false, @@ -217,7 +223,7 @@ class Module { if (typeof depth === 'number' && depth < 0) return options.stylize(`[${ctor.name}]`, 'special'); - const o = Object.create({ constructor: ctor }); + const o = ObjectCreate({ constructor: ctor }); o.status = this.status; o.identifier = this.identifier; o.context = this.context; @@ -345,7 +351,7 @@ class SourceTextModule extends Module { class SyntheticModule extends Module { constructor(exportNames, evaluateCallback, options = {}) { - if (!Array.isArray(exportNames) || + if (!ArrayIsArray(exportNames) || exportNames.some((e) => typeof e !== 'string')) { throw new ERR_INVALID_ARG_TYPE('exportNames', 'Array of strings', exportNames); diff --git a/lib/internal/worker.js b/lib/internal/worker.js index 614f93010515b3..621dfa77693168 100644 --- a/lib/internal/worker.js +++ b/lib/internal/worker.js @@ -2,7 +2,12 @@ /* global SharedArrayBuffer */ -const { Math, Object } = primordials; +const { + ArrayIsArray, + MathMax, + ObjectCreate, + ObjectEntries, +} = primordials; const EventEmitter = require('events'); const assert = require('internal/assert'); @@ -75,7 +80,7 @@ class Worker extends EventEmitter { super(); debug(`[${threadId}] create new worker`, filename, options); validateString(filename, 'filename'); - if (options.execArgv && !Array.isArray(options.execArgv)) { + if (options.execArgv && !ArrayIsArray(options.execArgv)) { throw new ERR_INVALID_ARG_TYPE('options.execArgv', 'array', options.execArgv); @@ -94,8 +99,8 @@ class Worker extends EventEmitter { let env; if (typeof options.env === 'object' && options.env !== null) { - env = Object.create(null); - for (const [ key, value ] of Object.entries(options.env)) + env = ObjectCreate(null); + for (const [ key, value ] of ObjectEntries(options.env)) env[key] = `${value}`; } else if (options.env == null) { env = process.env; @@ -318,7 +323,7 @@ function parseResourceLimits(obj) { if (typeof obj !== 'object' || obj === null) return ret; if (typeof obj.maxOldGenerationSizeMb === 'number') - ret[kMaxOldGenerationSizeMb] = Math.max(obj.maxOldGenerationSizeMb, 2); + ret[kMaxOldGenerationSizeMb] = MathMax(obj.maxOldGenerationSizeMb, 2); if (typeof obj.maxYoungGenerationSizeMb === 'number') ret[kMaxYoungGenerationSizeMb] = obj.maxYoungGenerationSizeMb; if (typeof obj.codeRangeSizeMb === 'number') diff --git a/lib/internal/worker/io.js b/lib/internal/worker/io.js index ba2150e530b5be..a672dac94ce7e5 100644 --- a/lib/internal/worker/io.js +++ b/lib/internal/worker/io.js @@ -1,6 +1,13 @@ 'use strict'; -const { Object } = primordials; +const { + ObjectAssign, + ObjectCreate, + ObjectDefineProperty, + ObjectGetOwnPropertyDescriptors, + ObjectGetPrototypeOf, + ObjectSetPrototypeOf, +} = primordials; const { handle_onclose: handleOnCloseSymbol, @@ -48,12 +55,12 @@ const messageTypes = { // not provide methods that are not present in the Browser and not documented // on our side (e.g. hasRef). // Save a copy of the original set of methods as a shallow clone. -const MessagePortPrototype = Object.create( - Object.getPrototypeOf(MessagePort.prototype), - Object.getOwnPropertyDescriptors(MessagePort.prototype)); +const MessagePortPrototype = ObjectCreate( + ObjectGetPrototypeOf(MessagePort.prototype), + ObjectGetOwnPropertyDescriptors(MessagePort.prototype)); // Set up the new inheritance chain. -Object.setPrototypeOf(MessagePort, EventEmitter); -Object.setPrototypeOf(MessagePort.prototype, EventEmitter.prototype); +ObjectSetPrototypeOf(MessagePort, EventEmitter); +ObjectSetPrototypeOf(MessagePort.prototype, EventEmitter.prototype); // Copy methods that are inherited from HandleWrap, because // changing the prototype of MessagePort.prototype implicitly removed them. MessagePort.prototype.ref = MessagePortPrototype.ref; @@ -73,7 +80,7 @@ MessagePort.prototype[kOnMessageListener] = function onmessage(event) { // This is for compatibility with the Web's MessagePort API. It makes sense to // provide it as an `EventEmitter` in Node.js, but if somebody overrides // `onmessage`, we'll switch over to the Web API model. -Object.defineProperty(MessagePort.prototype, 'onmessage', { +ObjectDefineProperty(MessagePort.prototype, 'onmessage', { enumerable: true, configurable: true, get() { @@ -96,7 +103,7 @@ function oninit() { setupPortReferencing(this, this, 'message'); } -Object.defineProperty(MessagePort.prototype, onInitSymbol, { +ObjectDefineProperty(MessagePort.prototype, onInitSymbol, { enumerable: true, writable: false, value: oninit @@ -107,7 +114,7 @@ function onclose() { this.emit('close'); } -Object.defineProperty(MessagePort.prototype, handleOnCloseSymbol, { +ObjectDefineProperty(MessagePort.prototype, handleOnCloseSymbol, { enumerable: false, writable: false, value: onclose @@ -119,7 +126,7 @@ MessagePort.prototype.close = function(cb) { MessagePortPrototype.close.call(this); }; -Object.defineProperty(MessagePort.prototype, inspect.custom, { +ObjectDefineProperty(MessagePort.prototype, inspect.custom, { enumerable: false, writable: false, value: function inspect() { // eslint-disable-line func-name-matching @@ -129,14 +136,14 @@ Object.defineProperty(MessagePort.prototype, inspect.custom, { // e.g. when accessing the prototype directly. ref = MessagePortPrototype.hasRef.call(this); } catch { return this; } - return Object.assign(Object.create(MessagePort.prototype), - ref === undefined ? { - active: false, - } : { - active: true, - refed: ref - }, - this); + return ObjectAssign(ObjectCreate(MessagePort.prototype), + ref === undefined ? { + active: false, + } : { + active: true, + refed: ref + }, + this); } }); diff --git a/lib/net.js b/lib/net.js index bdb77fdacbd856..02fd18748036a3 100644 --- a/lib/net.js +++ b/lib/net.js @@ -22,10 +22,12 @@ 'use strict'; const { - Object: { - defineProperty: ObjectDefineProperty, - setPrototypeOf: ObjectSetPrototypeOf - } + ArrayIsArray, + Boolean, + Number, + NumberIsNaN, + ObjectDefineProperty, + ObjectSetPrototypeOf, } = primordials; const EventEmitter = require('events'); @@ -155,7 +157,7 @@ function createServer(options, connectionListener) { // Target API: // -// var s = net.connect({port: 80, host: 'google.com'}, function() { +// let s = net.connect({port: 80, host: 'google.com'}, function() { // ... // }); // @@ -190,7 +192,7 @@ function connect(...args) { // For Server.prototype.listen(), the [...] part is [, backlog] // but will not be handled here (handled in listen()) function normalizeArgs(args) { - var arr; + let arr; if (args.length === 0) { arr = [{}, null]; @@ -199,7 +201,7 @@ function normalizeArgs(args) { } const arg0 = args[0]; - var options = {}; + let options = {}; if (typeof arg0 === 'object' && arg0 !== null) { // (options[...][, cb]) options = arg0; @@ -382,7 +384,7 @@ ObjectSetPrototypeOf(Socket, stream.Duplex); // Refresh existing timeouts. Socket.prototype._unrefTimer = function _unrefTimer() { - for (var s = this; s !== null; s = s._parent) { + for (let s = this; s !== null; s = s._parent) { if (s[kTimeout]) s[kTimeout].refresh(); } @@ -558,7 +560,7 @@ function tryReadStart(socket) { // Not already reading, start the flow debug('Socket._handle.readStart'); socket._handle.reading = true; - var err = socket._handle.readStart(); + const err = socket._handle.readStart(); if (err) socket.destroy(errnoException(err, 'read')); } @@ -646,7 +648,7 @@ Socket.prototype._destroy = function(exception, cb) { this.readable = this.writable = false; - for (var s = this; s !== null; s = s._parent) { + for (let s = this; s !== null; s = s._parent) { clearTimeout(s[kTimeout]); } @@ -654,7 +656,7 @@ Socket.prototype._destroy = function(exception, cb) { if (this._handle) { if (this !== process.stderr) debug('close handle'); - var isException = exception ? true : false; + const isException = exception ? true : false; // `bytesRead` and `kBytesWritten` should be accessible after `.destroy()` this[kBytesRead] = this._handle.bytesRead; this[kBytesWritten] = this._handle.bytesWritten; @@ -686,8 +688,8 @@ Socket.prototype._getpeername = function() { if (!this._handle || !this._handle.getpeername) { return {}; } - var out = {}; - var err = this._handle.getpeername(out); + const out = {}; + const err = this._handle.getpeername(out); if (err) return {}; // FIXME(bnoordhuis) Throw? this._peername = out; } @@ -724,8 +726,8 @@ Socket.prototype._getsockname = function() { return {}; } if (!this._sockname) { - var out = {}; - var err = this._handle.getsockname(out); + const out = {}; + const err = this._handle.getsockname(out); if (err) return {}; // FIXME(bnoordhuis) Throw? this._sockname = out; } @@ -796,7 +798,7 @@ protoGetter('_bytesDispatched', function _bytesDispatched() { }); protoGetter('bytesWritten', function bytesWritten() { - var bytes = this._bytesDispatched; + let bytes = this._bytesDispatched; const state = this._writableState; const data = this._pendingData; const encoding = this._pendingEncoding; @@ -811,9 +813,9 @@ protoGetter('bytesWritten', function bytesWritten() { bytes += Buffer.byteLength(el.chunk, el.encoding); }); - if (Array.isArray(data)) { + if (ArrayIsArray(data)) { // Was a writev, iterate over chunks to get total length - for (var i = 0; i < data.length; i++) { + for (let i = 0; i < data.length; i++) { const chunk = data[i]; if (data.allBuffers || chunk instanceof Buffer) @@ -843,7 +845,7 @@ function checkBindError(err, port, handle) { // getsockname() method. Non-issue for now, the cluster module doesn't // really support pipes anyway. if (err === 0 && port > 0 && handle.getsockname) { - var out = {}; + const out = {}; err = handle.getsockname(out); if (err === 0 && port !== out.port) { debug(`checkBindError, bound to ${out.port} instead of ${port}`); @@ -861,7 +863,7 @@ function internalConnect( assert(self.connecting); - var err; + let err; if (localAddress || localPort) { if (addressType === 4) { @@ -903,8 +905,8 @@ function internalConnect( } if (err) { - var sockname = self._getsockname(); - var details; + const sockname = self._getsockname(); + let details; if (sockname) { details = sockname.address + ':' + sockname.port; @@ -922,7 +924,7 @@ Socket.prototype.connect = function(...args) { // already been normalized (so we don't normalize more than once). This has // been solved before in https://github.com/nodejs/node/pull/12342, but was // reverted as it had unintended side effects. - if (Array.isArray(args[0]) && args[0][normalizedArgsSymbol]) { + if (ArrayIsArray(args[0]) && args[0][normalizedArgsSymbol]) { normalized = args[0]; } else { normalized = normalizeArgs(args); @@ -1127,15 +1129,15 @@ function afterConnect(status, handle, req, readable, writable) { } else { self.connecting = false; - var details; + let details; if (req.localAddress && req.localPort) { details = req.localAddress + ':' + req.localPort; } - var ex = exceptionWithHostPort(status, - 'connect', - req.address, - req.port, - details); + const ex = exceptionWithHostPort(status, + 'connect', + req.address, + req.port, + details); if (details) { ex.localAddress = req.localAddress; ex.localPort = req.localPort; @@ -1199,11 +1201,11 @@ function toNumber(x) { return (x = Number(x)) >= 0 ? x : false; } // Returns handle if it can be created, or error code if it can't function createServerHandle(address, port, addressType, fd, flags) { - var err = 0; + let err = 0; // Assign handle in listen, and clean up if bind or listen fails - var handle; + let handle; - var isTCP = false; + let isTCP = false; if (typeof fd === 'number' && fd >= 0) { try { handle = createHandle(fd, true); @@ -1221,8 +1223,8 @@ function createServerHandle(address, port, addressType, fd, flags) { } else if (port === -1 && addressType === -1) { handle = new Pipe(PipeConstants.SERVER); if (process.platform === 'win32') { - var instances = parseInt(process.env.NODE_PENDING_PIPE_INSTANCES); - if (!Number.isNaN(instances)) { + const instances = parseInt(process.env.NODE_PENDING_PIPE_INSTANCES); + if (!NumberIsNaN(instances)) { handle.setPendingInstances(instances); } } @@ -1266,7 +1268,7 @@ function setupListenHandle(address, port, addressType, backlog, fd, flags) { } else { debug('setupListenHandle: create a handle'); - var rval = null; + let rval = null; // Try to bind to the unspecified IPv6 address, see if IPv6 is available if (!address && typeof fd !== 'number') { @@ -1286,7 +1288,7 @@ function setupListenHandle(address, port, addressType, backlog, fd, flags) { rval = createServerHandle(address, port, addressType, fd, flags); if (typeof rval === 'number') { - var error = uvExceptionWithHostPort(rval, 'listen', address, port); + const error = uvExceptionWithHostPort(rval, 'listen', address, port); process.nextTick(emitErrorNT, this, error); return; } @@ -1303,7 +1305,7 @@ function setupListenHandle(address, port, addressType, backlog, fd, flags) { const err = this._handle.listen(backlog || 511); if (err) { - var ex = uvExceptionWithHostPort(err, 'listen', address, port); + const ex = uvExceptionWithHostPort(err, 'listen', address, port); this._handle.close(); this._handle = null; defaultTriggerAsyncIdScope(this[async_id_symbol], @@ -1370,7 +1372,7 @@ function listenInCluster(server, address, port, addressType, err = checkBindError(err, port, handle); if (err) { - var ex = exceptionWithHostPort(err, 'bind', address, port); + const ex = exceptionWithHostPort(err, 'bind', address, port); return server.emit('error', ex); } @@ -1385,7 +1387,7 @@ function listenInCluster(server, address, port, addressType, Server.prototype.listen = function(...args) { const normalized = normalizeArgs(args); - var options = normalized[0]; + let options = normalized[0]; const cb = normalized[1]; if (this._handle) { @@ -1427,7 +1429,7 @@ Server.prototype.listen = function(...args) { // ([port][, host][, backlog][, cb]) where port is specified // or (options[, cb]) where options.port is specified // or if options.port is normalized as 0 before - var backlog; + let backlog; if (typeof options.port === 'number' || typeof options.port === 'string') { if (!isLegalPort(options.port)) { throw new ERR_SOCKET_BAD_PORT(options.port); @@ -1448,7 +1450,7 @@ Server.prototype.listen = function(...args) { // (path[, backlog][, cb]) or (options[, cb]) // where path or options.path is a UNIX domain socket or Windows pipe if (options.path && isPipeName(options.path)) { - var pipeName = this._pipeName = options.path; + const pipeName = this._pipeName = options.path; backlog = options.backlog || backlogFromArgs; listenInCluster(this, pipeName, -1, -1, backlog, undefined, options.exclusive); @@ -1506,8 +1508,8 @@ ObjectDefineProperty(Server.prototype, 'listening', { Server.prototype.address = function() { if (this._handle && this._handle.getsockname) { - var out = {}; - var err = this._handle.getsockname(out); + const out = {}; + const err = this._handle.getsockname(out); if (err) { throw errnoException(err, 'address'); } @@ -1569,8 +1571,8 @@ Server.prototype.getConnections = function(cb) { } // Poll workers - var left = this._workers.length; - var total = this._connections; + let left = this._workers.length; + let total = this._connections; function oncount(err, count) { if (err) { @@ -1582,7 +1584,7 @@ Server.prototype.getConnections = function(cb) { if (--left === 0) return end(null, total); } - for (var n = 0; n < this._workers.length; n++) { + for (let n = 0; n < this._workers.length; n++) { this._workers[n].getConnections(oncount); } @@ -1607,7 +1609,7 @@ Server.prototype.close = function(cb) { } if (this._usingWorkers) { - var left = this._workers.length; + let left = this._workers.length; const onWorkerClose = () => { if (--left !== 0) return; @@ -1620,7 +1622,7 @@ Server.prototype.close = function(cb) { this._connections++; // Poll workers - for (var n = 0; n < this._workers.length; n++) + for (let n = 0; n < this._workers.length; n++) this._workers[n].close(onWorkerClose); } else { this._emitCloseIfDrained(); @@ -1690,11 +1692,11 @@ Server.prototype.unref = function() { return this; }; -var _setSimultaneousAccepts; -var warnSimultaneousAccepts = true; +let _setSimultaneousAccepts; +let warnSimultaneousAccepts = true; if (process.platform === 'win32') { - var simultaneousAccepts; + let simultaneousAccepts; _setSimultaneousAccepts = function(handle) { if (warnSimultaneousAccepts) { diff --git a/lib/os.js b/lib/os.js index d312c7d298e3ec..51520802d8ecf7 100644 --- a/lib/os.js +++ b/lib/os.js @@ -21,7 +21,9 @@ 'use strict'; -const { Object } = primordials; +const { + ObjectDefineProperties, +} = primordials; const { safeGetenv } = internalBinding('credentials'); const constants = internalBinding('constants').os; @@ -287,7 +289,7 @@ module.exports = { tmpDir: deprecate(tmpdir, tmpDirDeprecationMsg, 'DEP0022') }; -Object.defineProperties(module.exports, { +ObjectDefineProperties(module.exports, { constants: { configurable: false, enumerable: true, diff --git a/lib/perf_hooks.js b/lib/perf_hooks.js index 18b0332feaffaa..0b8bab03e48296 100644 --- a/lib/perf_hooks.js +++ b/lib/perf_hooks.js @@ -1,6 +1,13 @@ 'use strict'; -const { Object } = primordials; +const { + ArrayIsArray, + Boolean, + NumberIsSafeInteger, + ObjectDefineProperties, + ObjectDefineProperty, + ObjectKeys, +} = primordials; const { ELDHistogram: _ELDHistogram, @@ -215,7 +222,7 @@ const nodeTiming = new PerformanceNodeTiming(); // Maintains a list of entries as a linked list stored in insertion order. class PerformanceObserverEntryList { constructor() { - Object.defineProperties(this, { + ObjectDefineProperties(this, { [kEntries]: { writable: true, enumerable: false, @@ -280,7 +287,7 @@ class PerformanceObserver extends AsyncResource { throw new ERR_INVALID_CALLBACK(callback); } super('PerformanceObserver'); - Object.defineProperties(this, { + ObjectDefineProperties(this, { [kTypes]: { enumerable: false, writable: true, @@ -312,7 +319,7 @@ class PerformanceObserver extends AsyncResource { disconnect() { const observerCountsGC = observerCounts[NODE_PERFORMANCE_ENTRY_TYPE_GC]; const types = this[kTypes]; - const keys = Object.keys(types); + const keys = ObjectKeys(types); for (var n = 0; n < keys.length; n++) { const item = types[keys[n]]; if (item) { @@ -331,7 +338,7 @@ class PerformanceObserver extends AsyncResource { if (typeof options !== 'object' || options === null) { throw new ERR_INVALID_ARG_TYPE('options', 'Object', options); } - if (!Array.isArray(options.entryTypes)) { + if (!ArrayIsArray(options.entryTypes)) { throw new ERR_INVALID_OPT_VALUE('entryTypes', options); } const entryTypes = options.entryTypes.filter(filterTypes).map(mapTypes); @@ -413,13 +420,13 @@ class Performance { if (fn[kTimerified]) return fn[kTimerified]; const ret = timerify(fn, fn.length); - Object.defineProperty(fn, kTimerified, { + ObjectDefineProperty(fn, kTimerified, { enumerable: false, configurable: true, writable: false, value: ret }); - Object.defineProperties(ret, { + ObjectDefineProperties(ret, { [kTimerified]: { enumerable: false, configurable: true, @@ -598,7 +605,7 @@ function monitorEventLoopDelay(options = {}) { throw new ERR_INVALID_ARG_TYPE('options.resolution', 'number', resolution); } - if (resolution <= 0 || !Number.isSafeInteger(resolution)) { + if (resolution <= 0 || !NumberIsSafeInteger(resolution)) { throw new ERR_INVALID_OPT_VALUE.RangeError('resolution', resolution); } return new ELDHistogram(new _ELDHistogram(resolution)); @@ -610,7 +617,7 @@ module.exports = { monitorEventLoopDelay }; -Object.defineProperty(module.exports, 'constants', { +ObjectDefineProperty(module.exports, 'constants', { configurable: false, enumerable: true, value: constants diff --git a/lib/querystring.js b/lib/querystring.js index 40f6d3dafb2b56..954b35d69e3fbd 100644 --- a/lib/querystring.js +++ b/lib/querystring.js @@ -23,7 +23,12 @@ 'use strict'; -const { Object } = primordials; +const { + Array, + ArrayIsArray, + ObjectCreate, + ObjectKeys, +} = primordials; const { Buffer } = require('buffer'); const { @@ -167,7 +172,7 @@ function stringify(obj, sep, eq, options) { } if (obj !== null && typeof obj === 'object') { - const keys = Object.keys(obj); + const keys = ObjectKeys(obj); const len = keys.length; const flast = len - 1; let fields = ''; @@ -177,7 +182,7 @@ function stringify(obj, sep, eq, options) { let ks = encode(stringifyPrimitive(k)); ks += eq; - if (Array.isArray(v)) { + if (ArrayIsArray(v)) { const vlen = v.length; if (vlen === 0) continue; const vlast = vlen - 1; @@ -233,7 +238,7 @@ function addKeyVal(obj, key, value, keyEncoded, valEncoded, decode) { // Parse a key/val string. function parse(qs, sep, eq, options) { - const obj = Object.create(null); + const obj = ObjectCreate(null); if (typeof qs !== 'string' || qs.length === 0) { return obj; diff --git a/lib/readline.js b/lib/readline.js index 13c70fbb8c45c5..b25cf9ee64b8a7 100644 --- a/lib/readline.js +++ b/lib/readline.js @@ -27,7 +27,16 @@ 'use strict'; -const { Math, Object } = primordials; +const { + DateNow, + MathCeil, + MathFloor, + MathMax, + NumberIsFinite, + NumberIsNaN, + ObjectDefineProperty, + ObjectSetPrototypeOf, +} = primordials; const { ERR_INVALID_CALLBACK, @@ -110,7 +119,7 @@ function Interface(input, output, completer, terminal) { prompt = input.prompt; } if (input.escapeCodeTimeout !== undefined) { - if (Number.isFinite(input.escapeCodeTimeout)) { + if (NumberIsFinite(input.escapeCodeTimeout)) { this.escapeCodeTimeout = input.escapeCodeTimeout; } else { throw new ERR_INVALID_OPT_VALUE( @@ -132,7 +141,7 @@ function Interface(input, output, completer, terminal) { } if (typeof historySize !== 'number' || - Number.isNaN(historySize) || + NumberIsNaN(historySize) || historySize < 0) { throw new ERR_INVALID_OPT_VALUE.RangeError('historySize', historySize); } @@ -150,7 +159,7 @@ function Interface(input, output, completer, terminal) { this.historySize = historySize; this.removeHistoryDuplicates = !!removeHistoryDuplicates; this.crlfDelay = crlfDelay ? - Math.max(kMincrlfDelay, crlfDelay) : kMincrlfDelay; + MathMax(kMincrlfDelay, crlfDelay) : kMincrlfDelay; // Check arity, 2 - for async, 1 for sync if (typeof completer === 'function') { this.completer = completer.length === 2 ? @@ -251,10 +260,10 @@ function Interface(input, output, completer, terminal) { input.resume(); } -Object.setPrototypeOf(Interface.prototype, EventEmitter.prototype); -Object.setPrototypeOf(Interface, EventEmitter); +ObjectSetPrototypeOf(Interface.prototype, EventEmitter.prototype); +ObjectSetPrototypeOf(Interface, EventEmitter); -Object.defineProperty(Interface.prototype, 'columns', { +ObjectDefineProperty(Interface.prototype, 'columns', { configurable: true, enumerable: true, get: function() { @@ -436,7 +445,7 @@ Interface.prototype._normalWrite = function(b) { } let string = this._decoder.write(b); if (this._sawReturnAt && - Date.now() - this._sawReturnAt <= this.crlfDelay) { + DateNow() - this._sawReturnAt <= this.crlfDelay) { string = string.replace(/^\n/, ''); this._sawReturnAt = 0; } @@ -449,7 +458,7 @@ Interface.prototype._normalWrite = function(b) { this._line_buffer = null; } if (newPartContainsEnding) { - this._sawReturnAt = string.endsWith('\r') ? Date.now() : 0; + this._sawReturnAt = string.endsWith('\r') ? DateNow() : 0; // Got one or more newlines; process into "line" events const lines = string.split(lineEnding); @@ -507,7 +516,7 @@ Interface.prototype._tabComplete = function(lastKeypressWasTab) { const width = completions.reduce(function completionReducer(a, b) { return a.length > b.length ? a : b; }).length + 2; // 2 space padding - let maxColumns = Math.floor(self.columns / width); + let maxColumns = MathFloor(self.columns / width); if (!maxColumns || maxColumns === Infinity) { maxColumns = 1; } @@ -541,7 +550,7 @@ function handleGroup(self, group, width, maxColumns) { if (group.length === 0) { return; } - const minRows = Math.ceil(group.length / maxColumns); + const minRows = MathCeil(group.length / maxColumns); for (let row = 0; row < minRows; row++) { for (let col = 0; col < maxColumns; col++) { const idx = row * maxColumns + col; @@ -733,7 +742,7 @@ Interface.prototype._getDisplayPos = function(str) { } if (code === 0x0a) { // new line \n // row must be incremented by 1 even if offset = 0 or col = +Infinity - row += Math.ceil(offset / col) || 1; + row += MathCeil(offset / col) || 1; offset = 0; continue; } @@ -832,14 +841,14 @@ function _ttyWriteDumb(s, key) { switch (key.name) { case 'return': // Carriage return, i.e. \r - this._sawReturnAt = Date.now(); + this._sawReturnAt = DateNow(); this._line(); break; case 'enter': // When key interval > crlfDelay if (this._sawReturnAt === 0 || - Date.now() - this._sawReturnAt > this.crlfDelay) { + DateNow() - this._sawReturnAt > this.crlfDelay) { this._line(); } this._sawReturnAt = 0; @@ -1013,14 +1022,14 @@ Interface.prototype._ttyWrite = function(s, key) { switch (key.name) { case 'return': // Carriage return, i.e. \r - this._sawReturnAt = Date.now(); + this._sawReturnAt = DateNow(); this._line(); break; case 'enter': // When key interval > crlfDelay if (this._sawReturnAt === 0 || - Date.now() - this._sawReturnAt > this.crlfDelay) { + DateNow() - this._sawReturnAt > this.crlfDelay) { this._line(); } this._sawReturnAt = 0; diff --git a/lib/repl.js b/lib/repl.js index 21c0505b85f23d..c1473588d3942c 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -42,7 +42,20 @@ 'use strict'; -const { Math, Object, ObjectPrototype } = primordials; +const { + ArrayIsArray, + MathMax, + NumberIsNaN, + ObjectAssign, + ObjectCreate, + ObjectDefineProperty, + ObjectGetOwnPropertyDescriptor, + ObjectGetOwnPropertyNames, + ObjectGetPrototypeOf, + ObjectKeys, + ObjectPrototypeHasOwnProperty, + ObjectSetPrototypeOf, +} = primordials; const { builtinLibs, @@ -235,7 +248,7 @@ function REPLServer(prompt, domainSet.add(this._domain); let rli = this; - Object.defineProperty(this, 'rli', { + ObjectDefineProperty(this, 'rli', { get: deprecate(() => rli, 'REPLServer.rli is deprecated', 'DEP0124'), set: deprecate((val) => rli = val, @@ -260,6 +273,7 @@ function REPLServer(prompt, function pause() { paused = true; } + function unpause() { if (!paused) return; paused = false; @@ -318,10 +332,12 @@ function REPLServer(prompt, if (code === '\n') return cb(null); - let pwd; + let parentURL; try { const { pathToFileURL } = require('url'); - pwd = pathToFileURL(process.cwd()).href; + // Adding `/repl` prevents dynamic imports from loading relative + // to the parent of `process.cwd()`. + parentURL = pathToFileURL(path.join(process.cwd(), 'repl')).href; } catch { } while (true) { @@ -336,7 +352,7 @@ function REPLServer(prompt, filename: file, displayErrors: true, importModuleDynamically: async (specifier) => { - return asyncESM.ESMLoader.import(specifier, pwd); + return asyncESM.ESMLoader.import(specifier, parentURL); } }); } catch (e) { @@ -552,7 +568,7 @@ function REPLServer(prompt, self.lines.level = []; self.clearBufferedCommand(); - Object.defineProperty(this, 'bufferedCommand', { + ObjectDefineProperty(this, 'bufferedCommand', { get: deprecate(() => self[kBufferedCommandSymbol], 'REPLServer.bufferedCommand is deprecated', 'DEP0074'), @@ -580,7 +596,7 @@ function REPLServer(prompt, prompt }); - this.commands = Object.create(null); + this.commands = ObjectCreate(null); defineDefaultCommands(this); // Figure out which "writer" function to use @@ -591,7 +607,7 @@ function REPLServer(prompt, writer.options.colors = self.useColors; if (options[kStandaloneREPL]) { - Object.defineProperty(inspect, 'replDefaults', { + ObjectDefineProperty(inspect, 'replDefaults', { get() { return writer.options; }, @@ -599,7 +615,7 @@ function REPLServer(prompt, if (options === null || typeof options !== 'object') { throw new ERR_INVALID_ARG_TYPE('options', 'Object', options); } - return Object.assign(writer.options, options); + return ObjectAssign(writer.options, options); }, enumerable: true, configurable: true @@ -689,7 +705,7 @@ function REPLServer(prompt, // display next prompt and return. if (trimmedCmd) { if (trimmedCmd.charAt(0) === '.' && trimmedCmd.charAt(1) !== '.' && - Number.isNaN(parseFloat(trimmedCmd))) { + NumberIsNaN(parseFloat(trimmedCmd))) { const matches = trimmedCmd.match(/^\.([^\s]+)\s*(.*)$/); const keyword = matches && matches[1]; const rest = matches && matches[2]; @@ -820,8 +836,8 @@ function REPLServer(prompt, self.displayPrompt(); } -Object.setPrototypeOf(REPLServer.prototype, Interface.prototype); -Object.setPrototypeOf(REPLServer, Interface); +ObjectSetPrototypeOf(REPLServer.prototype, Interface.prototype); +ObjectSetPrototypeOf(REPLServer, Interface); exports.REPLServer = REPLServer; @@ -884,16 +900,16 @@ REPLServer.prototype.createContext = function() { }, () => { context = vm.createContext(); }); - for (const name of Object.getOwnPropertyNames(global)) { + for (const name of ObjectGetOwnPropertyNames(global)) { // Only set properties on the context that do not exist as primordial. if (!(name in primordials)) { - Object.defineProperty(context, name, - Object.getOwnPropertyDescriptor(global, name)); + ObjectDefineProperty(context, name, + ObjectGetOwnPropertyDescriptor(global, name)); } } context.global = context; const _console = new Console(this.outputStream); - Object.defineProperty(context, 'console', { + ObjectDefineProperty(context, 'console', { configurable: true, writable: true, value: _console @@ -903,12 +919,12 @@ REPLServer.prototype.createContext = function() { const module = new CJSModule(''); module.paths = CJSModule._resolveLookupPaths('', parentModule) || []; - Object.defineProperty(context, 'module', { + ObjectDefineProperty(context, 'module', { configurable: true, writable: true, value: module }); - Object.defineProperty(context, 'require', { + ObjectDefineProperty(context, 'require', { configurable: true, writable: true, value: makeRequireFunction(module) @@ -926,7 +942,7 @@ REPLServer.prototype.resetContext = function() { this.lines = []; this.lines.level = []; - Object.defineProperty(this.context, '_', { + ObjectDefineProperty(this.context, '_', { configurable: true, get: () => this.last, set: (value) => { @@ -938,7 +954,7 @@ REPLServer.prototype.resetContext = function() { } }); - Object.defineProperty(this.context, '_error', { + ObjectDefineProperty(this.context, '_error', { configurable: true, get: () => this.lastError, set: (value) => { @@ -990,8 +1006,8 @@ function ArrayStream() { this.emit('data', `${data[n]}\n`); }; } -Object.setPrototypeOf(ArrayStream.prototype, Stream.prototype); -Object.setPrototypeOf(ArrayStream, Stream); +ObjectSetPrototypeOf(ArrayStream.prototype, Stream.prototype); +ObjectSetPrototypeOf(ArrayStream, Stream); ArrayStream.prototype.readable = true; ArrayStream.prototype.writable = true; ArrayStream.prototype.resume = function() {}; @@ -1092,7 +1108,7 @@ function complete(line, callback) { let filter; let match = line.match(/^\s*\.(\w*)$/); if (match) { - completionGroups.push(Object.keys(this.commands)); + completionGroups.push(ObjectKeys(this.commands)); completeOn = match[1]; if (match[1].length) { filter = match[1]; @@ -1101,7 +1117,7 @@ function complete(line, callback) { completionGroupsLoaded(); } else if (match = line.match(requireRE)) { // require('...') - const exts = Object.keys(this.context.require.extensions); + const exts = ObjectKeys(this.context.require.extensions); const indexRe = new RegExp('^index(?:' + exts.map(regexpEscape).join('|') + ')$'); const versionedFileNamesRe = /-\d+\.\d+/; @@ -1229,7 +1245,7 @@ function complete(line, callback) { if (this.useGlobal || vm.isContext(this.context)) { completionGroups.push(getGlobalLexicalScopeNames(this[kContextId])); let contextProto = this.context; - while (contextProto = Object.getPrototypeOf(contextProto)) { + while (contextProto = ObjectGetPrototypeOf(contextProto)) { completionGroups.push( filteredOwnPropertyNames.call(this, contextProto)); } @@ -1239,9 +1255,9 @@ function complete(line, callback) { completionGroupsLoaded(); } else { this.eval('.scope', this.context, 'repl', function ev(err, globals) { - if (err || !Array.isArray(globals)) { + if (err || !ArrayIsArray(globals)) { if (filter !== '') addCommonWords(completionGroups); - } else if (Array.isArray(globals[0])) { + } else if (ArrayIsArray(globals[0])) { // Add grouped globals for (let n = 0; n < globals.length; n++) completionGroups.push(globals[n]); @@ -1271,13 +1287,13 @@ function complete(line, callback) { let sentinel = 5; let p; if (typeof obj === 'object' || typeof obj === 'function') { - p = Object.getPrototypeOf(obj); + p = ObjectGetPrototypeOf(obj); } else { p = obj.constructor ? obj.constructor.prototype : null; } while (p !== null) { memberGroups.push(filteredOwnPropertyNames.call(this, p)); - p = Object.getPrototypeOf(p); + p = ObjectGetPrototypeOf(p); // Circular refs possible? Let's guard against that. sentinel--; if (sentinel <= 0) { @@ -1336,7 +1352,7 @@ function complete(line, callback) { group.sort(); for (let j = group.length - 1; j >= 0; j--) { const c = group[j]; - if (!ObjectPrototype.hasOwnProperty(uniq, c)) { + if (!ObjectPrototypeHasOwnProperty(uniq, c)) { completions.unshift(c); uniq[c] = true; } @@ -1532,9 +1548,9 @@ function defineDefaultCommands(repl) { repl.defineCommand('help', { help: 'Print this help message', action: function() { - const names = Object.keys(this.commands).sort(); + const names = ObjectKeys(this.commands).sort(); const longestNameLength = names.reduce( - (max, name) => Math.max(max, name.length), + (max, name) => MathMax(max, name.length), 0 ); for (let n = 0; n < names.length; n++) { @@ -1604,6 +1620,6 @@ function regexpEscape(s) { function Recoverable(err) { this.err = err; } -Object.setPrototypeOf(Recoverable.prototype, SyntaxError.prototype); -Object.setPrototypeOf(Recoverable, SyntaxError); +ObjectSetPrototypeOf(Recoverable.prototype, SyntaxError.prototype); +ObjectSetPrototypeOf(Recoverable, SyntaxError); exports.Recoverable = Recoverable; diff --git a/lib/string_decoder.js b/lib/string_decoder.js index 7df50eb0178377..0ab74b772559c4 100644 --- a/lib/string_decoder.js +++ b/lib/string_decoder.js @@ -21,7 +21,10 @@ 'use strict'; -const { Object } = primordials; +const { + ArrayBufferIsView, + ObjectDefineProperties, +} = primordials; const { Buffer } = require('buffer'); const { @@ -72,7 +75,7 @@ function StringDecoder(encoding) { StringDecoder.prototype.write = function write(buf) { if (typeof buf === 'string') return buf; - if (!ArrayBuffer.isView(buf)) + if (!ArrayBufferIsView(buf)) throw new ERR_INVALID_ARG_TYPE('buf', ['Buffer', 'TypedArray', 'DataView'], buf); @@ -95,7 +98,7 @@ StringDecoder.prototype.text = function text(buf, offset) { return this.write(buf.slice(offset)); }; -Object.defineProperties(StringDecoder.prototype, { +ObjectDefineProperties(StringDecoder.prototype, { lastChar: { configurable: true, enumerable: true, diff --git a/lib/timers.js b/lib/timers.js index f7fd157b3284ef..68327e2c3e5947 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -21,7 +21,9 @@ 'use strict'; -const { Math } = primordials; +const { + MathTrunc, +} = primordials; const { immediateInfo, @@ -77,7 +79,7 @@ function unenroll(item) { // That function could then be used by http and other similar modules. if (item[kRefed]) { // Compliment truncation during insert(). - const msecs = Math.trunc(item._idleTimeout); + const msecs = MathTrunc(item._idleTimeout); const list = timerListMap[msecs]; if (list !== undefined && L.isEmpty(list)) { debug('unenroll: list empty'); @@ -118,7 +120,7 @@ function setTimeout(callback, after, arg1, arg2, arg3) { throw new ERR_INVALID_CALLBACK(callback); } - var i, args; + let i, args; switch (arguments.length) { // fast cases case 1: @@ -164,7 +166,7 @@ function setInterval(callback, repeat, arg1, arg2, arg3) { throw new ERR_INVALID_CALLBACK(callback); } - var i, args; + let i, args; switch (arguments.length) { // fast cases case 1: @@ -248,7 +250,7 @@ function setImmediate(callback, arg1, arg2, arg3) { throw new ERR_INVALID_CALLBACK(callback); } - var i, args; + let i, args; switch (arguments.length) { // fast cases case 1: diff --git a/lib/tls.js b/lib/tls.js index 2ac43c01d32dbb..281de073c49574 100644 --- a/lib/tls.js +++ b/lib/tls.js @@ -21,7 +21,12 @@ 'use strict'; -const { Object } = primordials; +const { + Array, + ArrayIsArray, + ObjectDefineProperty, + ObjectFreeze, +} = primordials; const { ERR_TLS_CERT_ALTNAME_INVALID, @@ -82,10 +87,10 @@ exports.getCiphers = internalUtil.cachedResult( let rootCertificates; function cacheRootCertificates() { - rootCertificates = Object.freeze(getRootCertificates()); + rootCertificates = ObjectFreeze(getRootCertificates()); } -Object.defineProperty(exports, 'rootCertificates', { +ObjectDefineProperty(exports, 'rootCertificates', { configurable: false, enumerable: true, get: () => { @@ -109,8 +114,8 @@ function convertProtocols(protocols) { return p + 1 + len; }, 0)); - var offset = 0; - for (var i = 0, c = protocols.length; i < c; i++) { + let offset = 0; + for (let i = 0, c = protocols.length; i < c; i++) { buff[offset++] = lens[i]; buff.write(protocols[i], offset); offset += lens[i]; @@ -121,7 +126,7 @@ function convertProtocols(protocols) { exports.convertALPNProtocols = function convertALPNProtocols(protocols, out) { // If protocols is Array - translate it into buffer - if (Array.isArray(protocols)) { + if (ArrayIsArray(protocols)) { out.ALPNProtocols = convertProtocols(protocols); } else if (isArrayBufferView(protocols)) { // Copy new buffer not to be modified by user. @@ -163,7 +168,7 @@ function check(hostParts, pattern, wildcards) { return false; // Check host parts from right to left first. - for (var i = hostParts.length - 1; i > 0; i -= 1) { + for (let i = hostParts.length - 1; i > 0; i -= 1) { if (hostParts[i] !== patternParts[i]) return false; } @@ -243,33 +248,37 @@ exports.checkServerIdentity = function checkServerIdentity(hostname, cert) { let valid = false; let reason = 'Unknown reason'; + const hasAltNames = + dnsNames.length > 0 || ips.length > 0 || uriNames.length > 0; + + hostname = unfqdn(hostname); // Remove trailing dot for error messages. + if (net.isIP(hostname)) { valid = ips.includes(canonicalizeIP(hostname)); if (!valid) reason = `IP: ${hostname} is not in the cert's list: ${ips.join(', ')}`; // TODO(bnoordhuis) Also check URI SANs that are IP addresses. - } else if (subject) { - hostname = unfqdn(hostname); // Remove trailing dot for error messages. + } else if (hasAltNames || subject) { const hostParts = splitHost(hostname); const wildcard = (pattern) => check(hostParts, pattern, true); - const noWildcard = (pattern) => check(hostParts, pattern, false); - // Match against Common Name only if no supported identifiers are present. - if (dnsNames.length === 0 && ips.length === 0 && uriNames.length === 0) { + if (hasAltNames) { + const noWildcard = (pattern) => check(hostParts, pattern, false); + valid = dnsNames.some(wildcard) || uriNames.some(noWildcard); + if (!valid) + reason = + `Host: ${hostname}. is not in the cert's altnames: ${altNames}`; + } else { + // Match against Common Name only if no supported identifiers exist. const cn = subject.CN; - if (Array.isArray(cn)) + if (ArrayIsArray(cn)) valid = cn.some(wildcard); else if (cn) valid = wildcard(cn); if (!valid) reason = `Host: ${hostname}. is not cert's CN: ${cn}`; - } else { - valid = dnsNames.some(wildcard) || uriNames.some(noWildcard); - if (!valid) - reason = - `Host: ${hostname}. is not in the cert's altnames: ${altNames}`; } } else { reason = 'Cert is empty'; diff --git a/lib/trace_events.js b/lib/trace_events.js index f142afded15d56..5ec50d32c63424 100644 --- a/lib/trace_events.js +++ b/lib/trace_events.js @@ -1,5 +1,9 @@ 'use strict'; +const { + ArrayIsArray, +} = primordials; + const { hasTracing } = internalBinding('config'); const kHandle = Symbol('handle'); const kEnabled = Symbol('enabled'); @@ -76,7 +80,7 @@ function createTracing(options) { if (typeof options !== 'object' || options === null) throw new ERR_INVALID_ARG_TYPE('options', 'object', options); - if (!Array.isArray(options.categories)) { + if (!ArrayIsArray(options.categories)) { throw new ERR_INVALID_ARG_TYPE('options.categories', 'string[]', options.categories); } diff --git a/lib/tty.js b/lib/tty.js index cc22a3b499feca..583cc1329830c9 100644 --- a/lib/tty.js +++ b/lib/tty.js @@ -21,7 +21,11 @@ 'use strict'; -const { Object } = primordials; +const { + Array, + NumberIsInteger, + ObjectSetPrototypeOf, +} = primordials; const net = require('net'); const { TTY, isTTY } = internalBinding('tty_wrap'); @@ -36,7 +40,7 @@ const { let readline; function isatty(fd) { - return Number.isInteger(fd) && fd >= 0 && isTTY(fd); + return NumberIsInteger(fd) && fd >= 0 && isTTY(fd); } function ReadStream(fd, options) { @@ -63,8 +67,8 @@ function ReadStream(fd, options) { this.isTTY = true; } -Object.setPrototypeOf(ReadStream.prototype, net.Socket.prototype); -Object.setPrototypeOf(ReadStream, net.Socket); +ObjectSetPrototypeOf(ReadStream.prototype, net.Socket.prototype); +ObjectSetPrototypeOf(ReadStream, net.Socket); ReadStream.prototype.setRawMode = function(flag) { flag = !!flag; @@ -110,8 +114,8 @@ function WriteStream(fd) { } } -Object.setPrototypeOf(WriteStream.prototype, net.Socket.prototype); -Object.setPrototypeOf(WriteStream, net.Socket); +ObjectSetPrototypeOf(WriteStream.prototype, net.Socket.prototype); +ObjectSetPrototypeOf(WriteStream, net.Socket); WriteStream.prototype.isTTY = true; diff --git a/lib/url.js b/lib/url.js index fc3863f6ef8a4b..316b2a12ee4524 100644 --- a/lib/url.js +++ b/lib/url.js @@ -21,7 +21,11 @@ 'use strict'; -const { Object, SafeSet } = primordials; +const { + ObjectCreate, + ObjectKeys, + SafeSet, +} = primordials; const { toASCII } = require('internal/idna'); const { encodeStr, hexTable } = require('internal/querystring'); @@ -248,7 +252,7 @@ Url.prototype.parse = function parse(url, parseQueryString, slashesDenoteHost) { } } else if (parseQueryString) { this.search = null; - this.query = Object.create(null); + this.query = ObjectCreate(null); } return this; } @@ -437,7 +441,7 @@ Url.prototype.parse = function parse(url, parseQueryString, slashesDenoteHost) { } else if (parseQueryString) { // No query string, but parseQueryString still requested this.search = null; - this.query = Object.create(null); + this.query = ObjectCreate(null); } const useQuestionIdx = @@ -677,7 +681,7 @@ Url.prototype.resolveObject = function resolveObject(relative) { } const result = new Url(); - const tkeys = Object.keys(this); + const tkeys = ObjectKeys(this); for (let tk = 0; tk < tkeys.length; tk++) { const tkey = tkeys[tk]; result[tkey] = this[tkey]; @@ -696,7 +700,7 @@ Url.prototype.resolveObject = function resolveObject(relative) { // Hrefs like //foo/bar always cut to the protocol. if (relative.slashes && !relative.protocol) { // Take everything except the protocol from relative - const rkeys = Object.keys(relative); + const rkeys = ObjectKeys(relative); for (let rk = 0; rk < rkeys.length; rk++) { const rkey = rkeys[rk]; if (rkey !== 'protocol') @@ -723,7 +727,7 @@ Url.prototype.resolveObject = function resolveObject(relative) { // because that's known to be hostless. // anything else is assumed to be absolute. if (!slashedProtocol.has(relative.protocol)) { - const keys = Object.keys(relative); + const keys = ObjectKeys(relative); for (let v = 0; v < keys.length; v++) { const k = keys[v]; result[k] = relative[k]; diff --git a/lib/util.js b/lib/util.js index 5ac2516f738b83..e2b738052daa4d 100644 --- a/lib/util.js +++ b/lib/util.js @@ -21,7 +21,17 @@ 'use strict'; -const { Object, ObjectPrototype, Reflect } = primordials; +const { + ArrayIsArray, + NumberIsSafeInteger, + ObjectDefineProperties, + ObjectDefineProperty, + ObjectGetOwnPropertyDescriptors, + ObjectKeys, + ObjectPrototypeToString, + ObjectSetPrototypeOf, + ReflectApply, +} = primordials; const { codes: { @@ -85,7 +95,7 @@ function isObject(arg) { } function isError(e) { - return ObjectPrototype.toString(e) === '[object Error]' || e instanceof Error; + return ObjectPrototypeToString(e) === '[object Error]' || e instanceof Error; } function isFunction(arg) { @@ -149,19 +159,19 @@ function inherits(ctor, superCtor) { throw new ERR_INVALID_ARG_TYPE('superCtor.prototype', 'Object', superCtor.prototype); } - Object.defineProperty(ctor, 'super_', { + ObjectDefineProperty(ctor, 'super_', { value: superCtor, writable: true, configurable: true }); - Object.setPrototypeOf(ctor.prototype, superCtor.prototype); + ObjectSetPrototypeOf(ctor.prototype, superCtor.prototype); } function _extend(target, source) { // Don't do anything if source isn't an object if (source === null || typeof source !== 'object') return target; - const keys = Object.keys(source); + const keys = ObjectKeys(source); let i = keys.length; while (i--) { target[keys[i]] = source[keys[i]]; @@ -193,15 +203,15 @@ function callbackify(original) { if (typeof maybeCb !== 'function') { throw new ERR_INVALID_ARG_TYPE('last argument', 'Function', maybeCb); } - const cb = (...args) => { Reflect.apply(maybeCb, this, args); }; + const cb = (...args) => { ReflectApply(maybeCb, this, args); }; // In true node style we process the callback on `nextTick` with all the // implications (stack, `uncaughtException`, `async_hooks`) - Reflect.apply(original, this, args) + ReflectApply(original, this, args) .then((ret) => process.nextTick(cb, null, ret), (rej) => process.nextTick(callbackifyOnRejected, rej, cb)); } - const descriptors = Object.getOwnPropertyDescriptors(original); + const descriptors = ObjectGetOwnPropertyDescriptors(original); // It is possible to manipulate a functions `length` or `name` property. This // guards against the manipulation. if (typeof descriptors.length.value === 'number') { @@ -210,13 +220,13 @@ function callbackify(original) { if (typeof descriptors.name.value === 'string') { descriptors.name.value += 'Callbackified'; } - Object.defineProperties(callbackified, descriptors); + ObjectDefineProperties(callbackified, descriptors); return callbackified; } function getSystemErrorName(err) { validateNumber(err, 'err'); - if (err >= 0 || !Number.isSafeInteger(err)) { + if (err >= 0 || !NumberIsSafeInteger(err)) { throw new ERR_OUT_OF_RANGE('err', 'a negative integer', err); } return internalErrorName(err); @@ -235,7 +245,7 @@ module.exports = { getSystemErrorName, inherits, inspect, - isArray: Array.isArray, + isArray: ArrayIsArray, isBoolean, isBuffer, isDeepStrictEqual(a, b) { diff --git a/lib/v8.js b/lib/v8.js index 32c2f3aa1b50ed..1e0ee43f224c8f 100644 --- a/lib/v8.js +++ b/lib/v8.js @@ -14,7 +14,10 @@ 'use strict'; -const { ObjectPrototype } = primordials; +const { + Array, + ObjectPrototypeToString, +} = primordials; const { Buffer } = require('buffer'); const { validateString } = require('internal/validators'); @@ -220,7 +223,7 @@ const arrayBufferViewTypeToIndex = new Map(); { const dummy = new ArrayBuffer(); for (const [i, ctor] of arrayBufferViewTypes.entries()) { - const tag = ObjectPrototype.toString(new ctor(dummy)); + const tag = ObjectPrototypeToString(new ctor(dummy)); arrayBufferViewTypeToIndex.set(tag, i); } } @@ -239,7 +242,7 @@ class DefaultSerializer extends Serializer { if (abView.constructor === Buffer) { i = bufferConstructorIndex; } else { - const tag = ObjectPrototype.toString(abView); + const tag = ObjectPrototypeToString(abView); i = arrayBufferViewTypeToIndex.get(tag); if (i === undefined) { diff --git a/lib/vm.js b/lib/vm.js index 8d44a92c804a21..d12a460df69c39 100644 --- a/lib/vm.js +++ b/lib/vm.js @@ -21,7 +21,10 @@ 'use strict'; -const { Array, ArrayPrototype } = primordials; +const { + ArrayIsArray, + ArrayPrototypeForEach, +} = primordials; const { ContextifyScript, @@ -309,11 +312,11 @@ function runInThisContext(code, options) { function compileFunction(code, params, options = {}) { validateString(code, 'code'); if (params !== undefined) { - if (!Array.isArray(params)) { + if (!ArrayIsArray(params)) { throw new ERR_INVALID_ARG_TYPE('params', 'Array', params); } - ArrayPrototype.forEach(params, - (param, i) => validateString(param, `params[${i}]`)); + ArrayPrototypeForEach(params, + (param, i) => validateString(param, `params[${i}]`)); } const { @@ -356,14 +359,14 @@ function compileFunction(code, params, options = {}) { ); } } - if (!Array.isArray(contextExtensions)) { + if (!ArrayIsArray(contextExtensions)) { throw new ERR_INVALID_ARG_TYPE( 'options.contextExtensions', 'Array', contextExtensions ); } - ArrayPrototype.forEach(contextExtensions, (extension, i) => { + ArrayPrototypeForEach(contextExtensions, (extension, i) => { if (typeof extension !== 'object') { throw new ERR_INVALID_ARG_TYPE( `options.contextExtensions[${i}]`, diff --git a/lib/wasi.js b/lib/wasi.js new file mode 100644 index 00000000000000..65b8473624198c --- /dev/null +++ b/lib/wasi.js @@ -0,0 +1,105 @@ +'use strict'; +/* global WebAssembly */ +const { + ArrayIsArray, + ArrayPrototypeForEach, + ArrayPrototypeMap, + FunctionPrototypeBind, + ObjectKeys +} = primordials; +const { + ERR_INVALID_ARG_TYPE, + ERR_WASI_ALREADY_STARTED +} = require('internal/errors').codes; +const { emitExperimentalWarning } = require('internal/util'); +const { WASI: _WASI } = internalBinding('wasi'); +const kSetMemory = Symbol('setMemory'); +const kStarted = Symbol('started'); + +emitExperimentalWarning('WASI'); + + +class WASI { + constructor(options = {}) { + if (options === null || typeof options !== 'object') + throw new ERR_INVALID_ARG_TYPE('options', 'object', options); + + // eslint-disable-next-line prefer-const + let { args, env, preopens } = options; + + if (ArrayIsArray(args)) + args = ArrayPrototypeMap(args, (arg) => { return String(arg); }); + else if (args === undefined) + args = []; + else + throw new ERR_INVALID_ARG_TYPE('options.args', 'Array', args); + + const envPairs = []; + + if (env !== null && typeof env === 'object') { + for (const key in env) { + const value = env[key]; + if (value !== undefined) + envPairs.push(`${key}=${value}`); + } + } else if (env !== undefined) { + throw new ERR_INVALID_ARG_TYPE('options.env', 'Object', env); + } + + const preopenArray = []; + + if (typeof preopens === 'object' && preopens !== null) { + ArrayPrototypeForEach(ObjectKeys(preopens), (key) => { + preopenArray.push(String(key)); + preopenArray.push(String(preopens[key])); + }); + } else if (preopens !== undefined) { + throw new ERR_INVALID_ARG_TYPE('options.preopens', 'Object', preopens); + } + + const wrap = new _WASI(args, envPairs, preopenArray); + + for (const prop in wrap) { + wrap[prop] = FunctionPrototypeBind(wrap[prop], wrap); + } + + this[kSetMemory] = wrap._setMemory; + delete wrap._setMemory; + this.wasiImport = wrap; + this[kStarted] = false; + } + + start(instance) { + if (!(instance instanceof WebAssembly.Instance)) { + throw new ERR_INVALID_ARG_TYPE( + 'instance', 'WebAssembly.Instance', instance); + } + + const exports = instance.exports; + + if (exports === null || typeof exports !== 'object') + throw new ERR_INVALID_ARG_TYPE('instance.exports', 'Object', exports); + + const { memory } = exports; + + if (!(memory instanceof WebAssembly.Memory)) { + throw new ERR_INVALID_ARG_TYPE( + 'instance.exports.memory', 'WebAssembly.Memory', memory); + } + + if (this[kStarted]) { + throw new ERR_WASI_ALREADY_STARTED(); + } + + this[kStarted] = true; + this[kSetMemory](memory); + + if (exports._start) + exports._start(); + else if (exports.__wasi_unstable_reactor_start) + exports.__wasi_unstable_reactor_start(); + } +} + + +module.exports = { WASI }; diff --git a/lib/zlib.js b/lib/zlib.js index 445a538035a773..375af0fea5a429 100644 --- a/lib/zlib.js +++ b/lib/zlib.js @@ -21,7 +21,17 @@ 'use strict'; -const { Math, Object } = primordials; +const { + MathMax, + NumberIsFinite, + NumberIsNaN, + ObjectDefineProperties, + ObjectDefineProperty, + ObjectFreeze, + ObjectGetPrototypeOf, + ObjectKeys, + ObjectSetPrototypeOf, +} = primordials; const { codes: { @@ -80,7 +90,7 @@ const codes = { Z_VERSION_ERROR: constants.Z_VERSION_ERROR }; -const ckeys = Object.keys(codes); +const ckeys = ObjectKeys(codes); for (var ck = 0; ck < ckeys.length; ck++) { var ckey = ckeys[ck]; codes[codes[ckey]] = ckey; @@ -92,7 +102,7 @@ function zlibBuffer(engine, buffer, callback) { // Streams do not support non-Buffer ArrayBufferViews yet. Convert it to a // Buffer without copying. if (isArrayBufferView(buffer) && - Object.getPrototypeOf(buffer) !== Buffer.prototype) { + ObjectGetPrototypeOf(buffer) !== Buffer.prototype) { buffer = Buffer.from(buffer.buffer, buffer.byteOffset, buffer.byteLength); } else if (isAnyArrayBuffer(buffer)) { buffer = Buffer.from(buffer); @@ -183,11 +193,11 @@ const checkFiniteNumber = hideStackFrames((number, name) => { return false; } - if (Number.isFinite(number)) { + if (NumberIsFinite(number)) { return true; // Is a valid number } - if (Number.isNaN(number)) { + if (NumberIsNaN(number)) { return false; } @@ -267,10 +277,10 @@ function ZlibBase(opts, mode, handle, { flush, finishFlush, fullFlush }) { this.once('end', this.close); this._info = opts && opts.info; } -Object.setPrototypeOf(ZlibBase.prototype, Transform.prototype); -Object.setPrototypeOf(ZlibBase, Transform); +ObjectSetPrototypeOf(ZlibBase.prototype, Transform.prototype); +ObjectSetPrototypeOf(ZlibBase, Transform); -Object.defineProperty(ZlibBase.prototype, '_closed', { +ObjectDefineProperty(ZlibBase.prototype, '_closed', { configurable: true, enumerable: true, get() { @@ -282,7 +292,7 @@ Object.defineProperty(ZlibBase.prototype, '_closed', { // perspective, but it is inconsistent with all other streams exposed by Node.js // that have this concept, where it stands for the number of bytes read // *from* the stream (that is, net.Socket/tls.Socket & file system streams). -Object.defineProperty(ZlibBase.prototype, 'bytesRead', { +ObjectDefineProperty(ZlibBase.prototype, 'bytesRead', { configurable: true, enumerable: true, get: deprecate(function() { @@ -661,8 +671,8 @@ function Zlib(opts, mode) { this._level = level; this._strategy = strategy; } -Object.setPrototypeOf(Zlib.prototype, ZlibBase.prototype); -Object.setPrototypeOf(Zlib, ZlibBase); +ObjectSetPrototypeOf(Zlib.prototype, ZlibBase.prototype); +ObjectSetPrototypeOf(Zlib, ZlibBase); // This callback is used by `.params()` to wait until a full flush happened // before adjusting the parameters. In particular, the call to the native @@ -697,32 +707,32 @@ function Deflate(opts) { return new Deflate(opts); Zlib.call(this, opts, DEFLATE); } -Object.setPrototypeOf(Deflate.prototype, Zlib.prototype); -Object.setPrototypeOf(Deflate, Zlib); +ObjectSetPrototypeOf(Deflate.prototype, Zlib.prototype); +ObjectSetPrototypeOf(Deflate, Zlib); function Inflate(opts) { if (!(this instanceof Inflate)) return new Inflate(opts); Zlib.call(this, opts, INFLATE); } -Object.setPrototypeOf(Inflate.prototype, Zlib.prototype); -Object.setPrototypeOf(Inflate, Zlib); +ObjectSetPrototypeOf(Inflate.prototype, Zlib.prototype); +ObjectSetPrototypeOf(Inflate, Zlib); function Gzip(opts) { if (!(this instanceof Gzip)) return new Gzip(opts); Zlib.call(this, opts, GZIP); } -Object.setPrototypeOf(Gzip.prototype, Zlib.prototype); -Object.setPrototypeOf(Gzip, Zlib); +ObjectSetPrototypeOf(Gzip.prototype, Zlib.prototype); +ObjectSetPrototypeOf(Gzip, Zlib); function Gunzip(opts) { if (!(this instanceof Gunzip)) return new Gunzip(opts); Zlib.call(this, opts, GUNZIP); } -Object.setPrototypeOf(Gunzip.prototype, Zlib.prototype); -Object.setPrototypeOf(Gunzip, Zlib); +ObjectSetPrototypeOf(Gunzip.prototype, Zlib.prototype); +ObjectSetPrototypeOf(Gunzip, Zlib); function DeflateRaw(opts) { if (opts && opts.windowBits === 8) opts.windowBits = 9; @@ -730,24 +740,24 @@ function DeflateRaw(opts) { return new DeflateRaw(opts); Zlib.call(this, opts, DEFLATERAW); } -Object.setPrototypeOf(DeflateRaw.prototype, Zlib.prototype); -Object.setPrototypeOf(DeflateRaw, Zlib); +ObjectSetPrototypeOf(DeflateRaw.prototype, Zlib.prototype); +ObjectSetPrototypeOf(DeflateRaw, Zlib); function InflateRaw(opts) { if (!(this instanceof InflateRaw)) return new InflateRaw(opts); Zlib.call(this, opts, INFLATERAW); } -Object.setPrototypeOf(InflateRaw.prototype, Zlib.prototype); -Object.setPrototypeOf(InflateRaw, Zlib); +ObjectSetPrototypeOf(InflateRaw.prototype, Zlib.prototype); +ObjectSetPrototypeOf(InflateRaw, Zlib); function Unzip(opts) { if (!(this instanceof Unzip)) return new Unzip(opts); Zlib.call(this, opts, UNZIP); } -Object.setPrototypeOf(Unzip.prototype, Zlib.prototype); -Object.setPrototypeOf(Unzip, Zlib); +ObjectSetPrototypeOf(Unzip.prototype, Zlib.prototype); +ObjectSetPrototypeOf(Unzip, Zlib); function createConvenienceMethod(ctor, sync) { if (sync) { @@ -765,7 +775,7 @@ function createConvenienceMethod(ctor, sync) { } } -const kMaxBrotliParam = Math.max(...Object.keys(constants).map((key) => { +const kMaxBrotliParam = MathMax(...ObjectKeys(constants).map((key) => { return key.startsWith('BROTLI_PARAM_') ? constants[key] : 0; })); @@ -781,9 +791,9 @@ function Brotli(opts, mode) { brotliInitParamsArray.fill(-1); if (opts && opts.params) { - for (const origKey of Object.keys(opts.params)) { + for (const origKey of ObjectKeys(opts.params)) { const key = +origKey; - if (Number.isNaN(key) || key < 0 || key > kMaxBrotliParam || + if (NumberIsNaN(key) || key < 0 || key > kMaxBrotliParam || (brotliInitParamsArray[key] | 0) !== -1) { throw new ERR_BROTLI_INVALID_PARAM(origKey); } @@ -809,24 +819,24 @@ function Brotli(opts, mode) { ZlibBase.call(this, opts, mode, handle, brotliDefaultOpts); } -Object.setPrototypeOf(Brotli.prototype, Zlib.prototype); -Object.setPrototypeOf(Brotli, Zlib); +ObjectSetPrototypeOf(Brotli.prototype, Zlib.prototype); +ObjectSetPrototypeOf(Brotli, Zlib); function BrotliCompress(opts) { if (!(this instanceof BrotliCompress)) return new BrotliCompress(opts); Brotli.call(this, opts, BROTLI_ENCODE); } -Object.setPrototypeOf(BrotliCompress.prototype, Brotli.prototype); -Object.setPrototypeOf(BrotliCompress, Brotli); +ObjectSetPrototypeOf(BrotliCompress.prototype, Brotli.prototype); +ObjectSetPrototypeOf(BrotliCompress, Brotli); function BrotliDecompress(opts) { if (!(this instanceof BrotliDecompress)) return new BrotliDecompress(opts); Brotli.call(this, opts, BROTLI_DECODE); } -Object.setPrototypeOf(BrotliDecompress.prototype, Brotli.prototype); -Object.setPrototypeOf(BrotliDecompress, Brotli); +ObjectSetPrototypeOf(BrotliDecompress.prototype, Brotli.prototype); +ObjectSetPrototypeOf(BrotliDecompress, Brotli); function createProperty(ctor) { @@ -841,7 +851,7 @@ function createProperty(ctor) { // Legacy alias on the C++ wrapper object. This is not public API, so we may // want to runtime-deprecate it at some point. There's no hurry, though. -Object.defineProperty(binding.Zlib.prototype, 'jsref', { +ObjectDefineProperty(binding.Zlib.prototype, 'jsref', { get() { return this[owner_symbol]; }, set(v) { return this[owner_symbol] = v; } }); @@ -879,7 +889,7 @@ module.exports = { brotliDecompressSync: createConvenienceMethod(BrotliDecompress, true), }; -Object.defineProperties(module.exports, { +ObjectDefineProperties(module.exports, { createDeflate: createProperty(Deflate), createInflate: createProperty(Inflate), createDeflateRaw: createProperty(DeflateRaw), @@ -897,17 +907,17 @@ Object.defineProperties(module.exports, { codes: { enumerable: true, writable: false, - value: Object.freeze(codes) + value: ObjectFreeze(codes) } }); // These should be considered deprecated // expose all the zlib constants -const bkeys = Object.keys(constants); +const bkeys = ObjectKeys(constants); for (var bk = 0; bk < bkeys.length; bk++) { var bkey = bkeys[bk]; if (bkey.startsWith('BROTLI')) continue; - Object.defineProperty(module.exports, bkey, { + ObjectDefineProperty(module.exports, bkey, { enumerable: false, value: constants[bkey], writable: false }); } diff --git a/node.gyp b/node.gyp index 810cea8c241359..8becce9062edef 100644 --- a/node.gyp +++ b/node.gyp @@ -82,6 +82,7 @@ 'lib/util.js', 'lib/v8.js', 'lib/vm.js', + 'lib/wasi.js', 'lib/worker_threads.js', 'lib/zlib.js', 'lib/internal/assert.js', @@ -321,7 +322,10 @@ 'src/node_main.cc' ], - 'dependencies': [ 'deps/histogram/histogram.gyp:histogram' ], + 'dependencies': [ + 'deps/histogram/histogram.gyp:histogram', + 'deps/uvwasi/uvwasi.gyp:uvwasi', + ], 'msvs_settings': { 'VCLinkerTool': { @@ -432,7 +436,7 @@ }, }, }], - ['want_separate_host_toolset==0', { + ['node_use_node_code_cache=="true"', { 'dependencies': [ 'mkcodecache', ], @@ -495,7 +499,10 @@ 'src', '<(SHARED_INTERMEDIATE_DIR)' # for node_natives.h ], - 'dependencies': [ 'deps/histogram/histogram.gyp:histogram' ], + 'dependencies': [ + 'deps/histogram/histogram.gyp:histogram', + 'deps/uvwasi/uvwasi.gyp:uvwasi', + ], 'sources': [ 'src/api/async_resource.cc', @@ -560,6 +567,7 @@ 'src/node_url.cc', 'src/node_util.cc', 'src/node_v8.cc', + 'src/node_wasi.cc', 'src/node_watchdog.cc', 'src/node_worker.cc', 'src/node_zlib.cc', @@ -613,6 +621,7 @@ 'src/node_dir.h', 'src/node_errors.h', 'src/node_file.h', + 'src/node_file-inl.h', 'src/node_http2.h', 'src/node_http2_state.h', 'src/node_i18n.h', @@ -637,6 +646,7 @@ 'src/node_url.h', 'src/node_version.h', 'src/node_v8_platform-inl.h', + 'src/node_wasi.h', 'src/node_watchdog.h', 'src/node_worker.h', 'src/pipe_wrap.h', @@ -1071,6 +1081,7 @@ 'dependencies': [ '<(node_lib_target_name)', 'deps/histogram/histogram.gyp:histogram', + 'deps/uvwasi/uvwasi.gyp:uvwasi', 'node_dtrace_header', 'node_dtrace_ustack', 'node_dtrace_provider', @@ -1086,6 +1097,7 @@ 'deps/v8/include', 'deps/cares/include', 'deps/uv/include', + 'deps/uvwasi/include', 'test/cctest', ], @@ -1180,6 +1192,7 @@ 'dependencies': [ '<(node_lib_target_name)', 'deps/histogram/histogram.gyp:histogram', + 'deps/uvwasi/uvwasi.gyp:uvwasi', ], 'includes': [ @@ -1192,6 +1205,7 @@ 'deps/v8/include', 'deps/cares/include', 'deps/uv/include', + 'deps/uvwasi/include', ], 'defines': [ @@ -1223,6 +1237,7 @@ 'dependencies': [ '<(node_lib_target_name)', 'deps/histogram/histogram.gyp:histogram', + 'deps/uvwasi/uvwasi.gyp:uvwasi', ], 'includes': [ @@ -1235,6 +1250,7 @@ 'deps/v8/include', 'deps/cares/include', 'deps/uv/include', + 'deps/uvwasi/include', ], 'defines': [ 'NODE_WANT_INTERNALS=1' ], diff --git a/src/README.md b/src/README.md new file mode 100644 index 00000000000000..cfd6cd6266c695 --- /dev/null +++ b/src/README.md @@ -0,0 +1,896 @@ +# Node.js C++ codebase + +Hi! 👋 You’ve found the C++ code backing Node.js. This README aims to help you +get started working on it and document some idioms you may encounter while +doing so. + +## Coding style + +Node.js has a document detailing its [C++ coding style][] +that can be helpful as a reference for stylistic issues. + +## V8 API documentation + +A lot of the Node.js codebase is around what the underlying JavaScript engine, +V8, provides through its API for embedders. Knowledge of this API can also be +useful when working with native addons for Node.js written in C++, although for +new projects [N-API][] is typically the better alternative. + +V8 does not provide much public API documentation beyond what is +available in its C++ header files, most importantly `v8.h`, which can be +accessed online in the following locations: + +* On GitHub: [`v8.h` in Node.js master][] +* On GitHub: [`v8.h` in V8 master][] +* On the Chromium project’s Code Search application: [`v8.h` in Code Search][] + +V8 also provides an [introduction for V8 embedders][], +which can be useful for understanding some of the concepts it uses in its +embedder API. + +Important concepts when using V8 are the ones of [`Isolate`][]s and +[JavaScript value handles][]. + +## libuv API documentation + +The other major dependency of Node.js is [libuv][], providing +the [event loop][] and other operation system abstractions to Node.js. + +There is a [reference documentation for the libuv API][]. + +## Helpful concepts + +A number of concepts are involved in putting together Node.js on top of V8 and +libuv. This section aims to explain some of them and how they work together. + + +### `Isolate` + +The `v8::Isolate` class represents a single JavaScript engine instance, in +particular a set of JavaScript objects that can refer to each other +(the “heap”). + +The `v8::Isolate` is often passed to other V8 API functions, and provides some +APIs for managing the behaviour of the JavaScript engine or querying about its +current state or statistics such as memory usage. + +V8 APIs are not thread-safe unless explicitly specified. In a typical Node.js +application, the main thread and any `Worker` threads each have one `Isolate`, +and JavaScript objects from one `Isolate` cannot refer to objects from +another `Isolate`. + +Garbage collection, as well as other operations that affect the entire heap, +happen on a per-`Isolate` basis. + +Typical ways of accessing the current `Isolate` in the Node.js code are: + +* Given a `FunctionCallbackInfo` for a [binding function][], + using `args.GetIsolate()`. +* Given a [`Context`][], using `context->GetIsolate()`. +* Given a [`Environment`][], using `env->isolate()`. + +### V8 JavaScript values + +V8 provides classes that mostly correspond to JavaScript types; for example, +`v8::Value` is a class representing any kind of JavaScript type, with +subclasses such as `v8::Number` (which in turn has subclasses like `v8::Int32`), +`v8::Boolean` or `v8::Object`. Most types are represented by subclasses +of `v8::Object`, e.g. `v8::Uint8Array` or `v8::Date`. + + +### Internal fields + +V8 provides the ability to store data in so-called “internal fields” inside +`v8::Object`s that were created as instances of C++-backed classes. The number +of fields needs to be defined when creating that class. + +Both JavaScript values and `void*` pointers may be stored in such fields. +In most native Node.js objects, the first internal field is used to store a +pointer to a [`BaseObject`][] subclass, which then contains all relevant +information associated with the JavaScript object. + +The most typical way of working internal fields are: + +* `obj->InternalFieldCount()` to look up the number of internal fields for an + object (`0` for regular JavaScript objects). +* `obj->GetInternalField(i)` to get a JavaScript value from an internal field. +* `obj->SetInternalField(i, v)` to store a JavaScript value in an + internal field. +* `obj->GetAlignedPointerFromInternalField(i)` to get a `void*` pointer from an + internal field. +* `obj->SetAlignedPointerInInternalField(i, p)` to store a `void*` pointer in an + internal field. + +[`Context`][]s provide the same feature under the name “embedder data”. + + +### JavaScript value handles + +All JavaScript values are accessed through the V8 API through so-called handles, +of which there are two types: [`Local`][]s and [`Global`][]s. + + +#### `Local` handles + +A `v8::Local` handle is a temporary pointer to a JavaScript object, where +“temporary” usually means that is no longer needed after the current function +is done executing. `Local` handles can only be allocated on the C++ stack. + +Most of the V8 API uses `Local` handles to work with JavaScript values or return +them from functions. + +Whenever a `Local` handle is created, a `v8::HandleScope` or +`v8::EscapableHandleScope` object must exist on the stack. The `Local` is then +added to that scope and deleted along with it. + +When inside a [binding function][], a `HandleScope` already exists outside of +it, so there is no need to explicitly create one. + +`EscapableHandleScope`s can be used to allow a single `Local` handle to be +passed to the outer scope. This is useful when a function returns a `Local`. + +The following JavaScript and C++ functions are mostly equivalent: + +```js +function getFoo(obj) { + return obj.foo; +} +``` + +```c++ +v8::Local GetFoo(v8::Local context, + v8::Local obj) { + v8::Isolate* isolate = context->GetIsolate(); + v8::EscapableHandleScope handle_scope(isolate); + + // The 'foo_string' handle cannot be returned from this function because + // it is not “escaped” with `.Escape()`. + v8::Local foo_string = + v8::String::NewFromUtf8(isolate, + "foo", + v8::NewStringType::kNormal).ToLocalChecked(); + + v8::Local return_value; + if (obj->Get(context, foo_string).ToLocal(&return_value)) { + return handle_scope.Escape(return_value); + } else { + // There was a JS exception! Handle it somehow. + return v8::Local(); + } +} +``` + +See [exception handling][] for more information about the usage of `.To()`, +`.ToLocalChecked()`, `v8::Maybe` and `v8::MaybeLocal` usage. + +##### Casting local handles + +If it is known that a `Local` refers to a more specific type, it can +be cast to that type using `.As<...>()`: + +```c++ +v8::Local some_value; +// CHECK() is a Node.js utilitity that works similar to assert(). +CHECK(some_value->IsUint8Array()); +v8::Local as_uint8 = some_value.As(); +``` + +Generally, using `val.As()` is only valid if `val->IsX()` is true, and +failing to follow that rule may lead to crashes. + +##### Detecting handle leaks + +If it is expected that no `Local` handles should be created within a given +scope unless explicitly within a `HandleScope`, a `SealHandleScope` can be used. + +For example, there is a `SealHandleScope` around the event loop, forcing +any functions that are called from the event loop and want to run or access +JavaScript code to create `HandleScope`s. + + +#### `Global` handles + +A `v8::Global` handle (sometimes also referred to by the name of its parent +class `Persistent`, although use of that is discouraged in Node.js) is a +reference to a JavaScript object that can remain active as long as the engine +instance is active. + +Global handles can be either strong or weak. Strong global handles are so-called +“GC roots”, meaning that they will keep the JavaScript object they refer to +alive even if no other objects refer to them. Weak global handles do not do +that, and instead optionally call a callback when the object they refer to +is garbage-collected. + +```c++ +v8::Global reference; + +void StoreReference(v8::Isolate* isolate, v8::Local obj) { + // Create a strong reference to `obj`. + reference.Reset(isolate, obj); +} + +// Must be called with a HandleScope around it. +v8::Local LoadReference(v8::Isolate* isolate) { + return reference.Get(isolate); +} +``` + +##### `Eternal` handles + +`v8::Eternal` handles are a special kind of handles similar to `v8::Global` +handles, with the exception that the values they point to are never +garbage-collected while the JavaScript Engine instance is alive, even if +the `v8::Eternal` itself is destroyed at some point. This type of handle +is rarely used. + + +### `Context` + +JavaScript allows multiple global objects and sets of built-in JavaScript +objects (like the `Object` or `Array` functions) to coexist inside the same +heap. Node.js exposes this ability through the [`vm` module][]. + +V8 refers to each of these global objects and their associated builtins as a +`Context`. + +Currently, in Node.js there is one main `Context` associated with an +[`Environment`][] instance, and most Node.js features will only work inside +that context. (The only exception at the time of writing are +[`MessagePort`][] objects.) This restriction is not inherent to the design of +Node.js, and a sufficiently committed person could restructure Node.js to +provide built-in modules inside of `vm.Context`s. + +Often, the `Context` is passed around for [exception handling][]. +Typical ways of accessing the current `Environment` in the Node.js code are: + +* Given an [`Isolate`][], using `isolate->GetCurrentContext()`. +* Given an [`Environment`][], using `env->context()` to get the `Environment`’s + main context. + + +### Event loop + +The main abstraction for an event loop inside Node.js is the `uv_loop_t` struct. +Typically, there is one event loop per thread. This includes not only the main +thread and Workers, but also helper threads that may occasionally be spawned +in the course of running a Node.js program. + +The current event loop can be accessed using `env->event_loop()` given an +[`Environment`][] instance. The restriction of using a single event loop +is not inherent to the design of Node.js, and a sufficiently committed person +could restructure Node.js to provide e.g. the ability to run parts of Node.js +inside an event loop separate from the active thread’s event loop. + + +### `Environment` + +Node.js instances are represented by the `Environment` class. + +Currently, every `Environment` class is associated with: + +* One [event loop][] +* One [`Isolate`][] +* One main [`Context`][] + +The `Environment` class contains a large number of different fields for +different Node.js modules, for example a libuv timer for `setTimeout()` or +the memory for a `Float64Array` that the `fs` module uses for storing data +returned from a `fs.stat()` call. + +It also provides [cleanup hooks][] and maintains a list of [`BaseObject`][] +instances. + +Typical ways of accessing the current `Environment` in the Node.js code are: + +* Given a `FunctionCallbackInfo` for a [binding function][], + using `Environment::GetCurrent(args)`. +* Given a [`BaseObject`][], using `env()` or `self->env()`. +* Given a [`Context`][], using `Environment::GetCurrent(context)`. + This requires that `context` has been associated with the `Environment` + instance, e.g. is the main `Context` for the `Environment` or one of its + `vm.Context`s. +* Given an [`Isolate`][], using `Environment::GetCurrent(isolate)`. This looks + up the current [`Context`][] and then uses that. + + +### `IsolateData` + +Every Node.js instance ([`Environment`][]) is associated with one `IsolateData` +instance that contains information about or associated with a given +[`Isolate`][]. + +#### String table + +`IsolateData` contains a list of strings that can be quickly accessed +inside Node.js code, e.g. given an `Environment` instance `env` the JavaScript +string “name” can be accessed through `env->name_string()` without actually +creating a new JavaScript string. + +### Platform + +Every process that uses V8 has a `v8::Platform` instance that provides some +functionalities to V8, most importantly the ability to schedule work on +background threads. + +Node.js provides a `NodePlatform` class that implements the `v8::Platform` +interface and uses libuv for providing background threading abilities. + +The platform can be accessed through `isolate_data->platform()` given an +[`IsolateData`][] instance, although that only works when: + +* The current Node.js instance was not started by an embedder; or +* The current Node.js instance was started by an embedder whose `v8::Platform` + implementation also implement’s the `node::MultiIsolatePlatform` interface + and who passed this to Node.js. + + +### Binding functions + +C++ functions exposed to JS follow a specific signature. The following example +is from `node_util.cc`: + +```c++ +void ArrayBufferViewHasBuffer(const FunctionCallbackInfo& args) { + CHECK(args[0]->IsArrayBufferView()); + args.GetReturnValue().Set(args[0].As()->HasBuffer()); +} +``` + +(Namespaces are usually omitted through the use of `using` statements in the +Node.js source code.) + +`args[n]` is a `Local` that represents the n-th argument passed to the +function. `args.This()` is the `this` value inside this function call. +`args.Holder()` is equivalent to `args.This()` in all use cases inside of +Node.js. + +`args.GetReturnValue()` is a placeholder for the return value of the function, +and provides a `.Set()` method that can be called with a boolean, integer, +floating-point number or a `Local` to set the return value. + +Node.js provides various helpers for building JS classes in C++ and/or attaching +C++ functions to the exports of a built-in module: + +```c++ +void Initialize(Local target, + Local unused, + Local context, + void* priv) { + Environment* env = Environment::GetCurrent(context); + + env->SetMethod(target, "getaddrinfo", GetAddrInfo); + env->SetMethod(target, "getnameinfo", GetNameInfo); + + // 'SetMethodNoSideEffect' means that debuggers can safely execute this + // function for e.g. previews. + env->SetMethodNoSideEffect(target, "canonicalizeIP", CanonicalizeIP); + + // ... more code ... + + // Building the `ChannelWrap` class for JS: + Local channel_wrap = + env->NewFunctionTemplate(ChannelWrap::New); + // Allow for 1 internal field, see `BaseObject` for details on this: + channel_wrap->InstanceTemplate()->SetInternalFieldCount(1); + channel_wrap->Inherit(AsyncWrap::GetConstructorTemplate(env)); + + // Set various methods on the class (i.e. on the prototype): + env->SetProtoMethod(channel_wrap, "queryAny", Query); + env->SetProtoMethod(channel_wrap, "queryA", Query); + // ... + env->SetProtoMethod(channel_wrap, "querySoa", Query); + env->SetProtoMethod(channel_wrap, "getHostByAddr", Query); + + env->SetProtoMethodNoSideEffect(channel_wrap, "getServers", GetServers); + + Local channel_wrap_string = + FIXED_ONE_BYTE_STRING(env->isolate(), "ChannelWrap"); + channel_wrap->SetClassName(channel_wrap_string); + target->Set(env->context(), channel_wrap_string, + channel_wrap->GetFunction(context).ToLocalChecked()).Check(); +} + +// Run the `Initialize` function when loading this module through +// `internalBinding('cares_wrap')` in Node.js’s built-in JavaScript code: +NODE_MODULE_CONTEXT_AWARE_INTERNAL(cares_wrap, Initialize) +``` + + +### Exception handling + +The V8 engine provides multiple features to work with JavaScript exceptions, +as C++ exceptions are disabled inside of Node.js: + +#### Maybe types + +V8 provides the `v8::Maybe` and `v8::MaybeLocal` types, typically used +as return values from API functions that can run JavaScript code and therefore +can throw exceptions. + +Conceptually, the idea is that every `v8::Maybe` is either empty (checked +through `.IsNothing()`) or holds a value of type `T` (checked through +`.IsJust()`). If the `Maybe` is empty, then a JavaScript exception is pending. +A typical way of accessing the value is using the `.To()` function, which +returns a boolean indicating success of the operation (i.e. the `Maybe` not +being empty) and taking a pointer to a `T` to store the value if there is one. + +##### Checked conversion + +`maybe.Check()` can be used to assert that the maybe is not empty, i.e. crash +the process otherwise. `maybe.FromJust()` (aka `maybe.ToChecked()`) can be used +to access the value and crash the process if it is not set. + +This should only be performed if it is actually sure that the operation has +not failed. A lot of Node.js’s source code does **not** follow this rule, and +can be brought to crash through this. + +##### MaybeLocal + +`v8::MaybeLocal` is a variant of `v8::Maybe` that is either empty or +holds a value of type `Local`. It has methods that perform the same +operations as the methods of `v8::Maybe`, but with different names: + +| `Maybe` | `MaybeLocal` | +| ---------------------- | ------------------------------- | +| `maybe.IsNothing()` | `maybe_local.IsEmpty()` | +| `maybe.IsJust()` | – | +| `maybe.To(&value)` | `maybe_local.ToLocal(&local)` | +| `maybe.ToChecked()` | `maybe_local.ToLocalChecked()` | +| `maybe.FromJust()` | `maybe_local.ToLocalChecked()` | +| `maybe.Check()` | – | +| `v8::Nothing()` | `v8::MaybeLocal()` | +| `v8::Just(value)` | `v8::MaybeLocal(value)` | + +##### Handling empty `Maybe`s + +Usually, the best approach to encountering an empty `Maybe` is to just return +from the current function as soon as possible, and let execution in JavaScript +land resume. If the empty `Maybe` is encountered inside a nested function, +is may be a good idea to use a `Maybe` or `MaybeLocal` for the return type +of that function and pass information about pending JavaScript exceptions along +that way. + +Generally, when an empty `Maybe` is encountered, it is not valid to attempt +to perform further calls to APIs that return `Maybe`s. + +A typical pattern for dealing with APIs that return `Maybe` and `MaybeLocal` is +using `.ToLocal()` and `.To()` and returning early in case there is an error: + +```c++ +// This could also return a v8::MaybeLocal, for example. +v8::Maybe SumNumbers(v8::Local context, + v8::Local array_of_integers) { + v8::Isolate* isolate = context->GetIsolate(); + v8::HandleScope handle_scope(isolate); + + double sum = 0; + + for (uint32_t i = 0; i < array_of_integers->Length(); i++) { + v8::Local entry; + if (array_of_integers->Get(context, i).ToLocal(&entry)) { + // Oops, we might have hit a getter that throws an exception! + // It’s better to not continue return an empty (“nothing”) Maybe. + return v8::Nothing(); + } + + if (!entry->IsNumber()) { + // Let’s just skip any non-numbers. It would also be reasonable to throw + // an exception here, e.g. using the error system in src/node_errors.h, + // and then to return an empty Maybe again. + continue; + } + + // This cast is valid, because we’ve made sure it’s really a number. + v8::Local entry_as_number = entry.As(); + + sum += entry_as_number->Value(); + } + + return v8::Just(sum); +} + +// Function that is exposed to JS: +void SumNumbers(const v8::FunctionCallbackInfo& args) { + // This will crash if the first argument is not an array. Let’s assume we + // have performed type checking in a JavaScript wrapper function. + CHECK(args[0]->IsArray()); + + double sum; + if (!SumNumbers(args.GetIsolate()->GetCurrentContext(), + args[0].As()).To(&sum)) { + // Nothing to do, we can just return directly to JavaScript. + return; + } + + args.GetReturnValue().Set(sum); +} +``` + +#### TryCatch + +If there is a need to catch JavaScript exceptions in C++, V8 provides the +`v8::TryCatch` type for doing so, which we wrap into our own +`node::errors::TryCatchScope` in Node.js. The latter has the additional feature +of providing the ability to shut down the program in the typical Node.js way +(printing the exception + stack trace) if an exception is caught. + + +### libuv handles and requests + +Two central concepts when working with libuv are handles and requests. + +Handles are subclasses of the `uv_handle_t` “class”, and generally refer to +long-lived objects that can emit events multiple times, such as network sockets +or file system watchers. + +In Node.js, handles are often managed through a [`HandleWrap`][] subclass. + +Requests are one-time asynchronous function calls on the event loop, such as +file system requests or network write operations, that either succeed or fail. + +In Node.js, requests are often managed through a [`ReqWrap`][] subclass. + +### Environment cleanup + +When a Node.js [`Environment`][] is destroyed, it generally needs to clean up +any resources owned by it, e.g. memory or libuv requests/handles. + + +#### Cleanup hooks + +Cleanup hooks are provided that run before the [`Environment`][] +is destroyed. They can be added and removed through by using +`env->AddCleanupHook(callback, hint);` and +`env->RemoveCleanupHook(callback, hint);`, where callback takes a `void* hint` +argument. + +Inside these cleanup hooks, new asynchronous operations *may* be started on the +event loop, although ideally that is avoided as much as possible. + +Every [`BaseObject`][] has its own cleanup hook that deletes it. For +[`ReqWrap`][] and [`HandleWrap`][] instances, cleanup of the associated libuv +objects is performed automatically, i.e. handles are closed and requests +are cancelled if possible. + +#### Closing libuv handles + +If a libuv handle is not managed through a [`HandleWrap`][] instance, +it needs to be closed explicitly. Do not use `uv_close()` for that, but rather +`env->CloseHandle()`, which works the same way but keeps track of the number +of handles that are still closing. + +#### Closing libuv requests + +There is no way to abort libuv requests in general. If a libuv request is not +managed through a [`ReqWrap`][] instance, the +`env->IncreaseWaitingRequestCounter()` and +`env->DecreaseWaitingRequestCounter()` functions need to be used to keep track +of the number of active libuv requests. + +#### Calling into JavaScript + +Calling into JavaScript is not allowed during cleanup. Worker threads explicitly +forbid this during their shutdown sequence, but the main thread does not for +backwards compatibility reasons. + +When calling into JavaScript without using [`MakeCallback()`][], check the +`env->can_call_into_js()` flag and do not proceed if it is set to `false`. + +## Classes associated with JavaScript objects + +### `MemoryRetainer` + +A large number of classes in the Node.js C++ codebase refer to other objects. +The `MemoryRetainer` class is a helper for annotating C++ classes with +information that can be used by the heap snapshot builder in V8, so that +memory retained by C++ can be tracked in V8 heap snapshots captured in +Node.js applications. + +Inheriting from the `MemoryRetainer` class enables objects (both from JavaScript +and C++) to refer to instances of that class, and in turn enables that class +to point to other objects as well, including native C++ types +such as `std::string` and track their memory usage. + +This can be useful for debugging memory leaks. + +The [`memory_tracker.h`][] header file explains how to use this class. + + +### `BaseObject` + +A frequently recurring situation is that a JavaScript object and a C++ object +need to be tied together. `BaseObject` is the main abstraction for that in +Node.js, and most classes that are associated with JavaScript objects are +subclasses of it. It is defined in [`base_object.h`][]. + +Every `BaseObject` is associated with one [`Environment`][] and one +`v8::Object`. The `v8::Object` needs to have at least one [internal field][] +that is used for storing the pointer to the C++ object. In order to ensure this, +the V8 `SetInternalFieldCount()` function is usually used when setting up the +class from C++. + +The JavaScript object can be accessed as a `v8::Local` by using +`self->object()`, given a `BaseObject` named `self`. + +Accessing a `BaseObject` from a `v8::Local` (frequently that is +`args.This()` or `args.Holder()` in a [binding function][]) can be done using +the `Unwrap(obj)` function, where `T` is a subclass of `BaseObject`. +A helper for this is the `ASSIGN_OR_RETURN_UNWRAP` macro that returns from the +current function if unwrapping fails (typically that means that the `BaseObject` +has been deleted earlier). + +```c++ +void Http2Session::Request(const FunctionCallbackInfo& args) { + Http2Session* session; + ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); + Environment* env = session->env(); + Local context = env->context(); + Isolate* isolate = env->isolate(); + + // ... + // The actual function body, which can now use the `session` object. + // ... +} +``` + +#### Lifetime management + +The `BaseObject` class comes with a set of features that allow managing the +lifetime of its instances, either associating it with the lifetime of the +corresponding JavaScript object or untying the two. + +The `BaseObject::MakeWeak()` method turns the underlying [`Global`][] handle +into a weak one, and makes it so that the `BaseObject::OnGCCollect()` virtual +method is called when the JavaScript object is garbage collected. By default, +that methods deletes the `BaseObject` instance. + +`BaseObject::ClearWeak()` undoes this effect. + +It generally makes sense to call `MakeWeak()` in the constructor of a +`BaseObject` subclass, unless that subclass is referred to by e.g. the event +loop, as is the case for the [`HandleWrap`][] and [`ReqWrap`][] classes. + +In addition, there are two kinds of smart pointers that can be used to refer +to `BaseObject`s. + +`BaseObjectWeakPtr` is similar to `std::weak_ptr`, but holds on to +an object of a `BaseObject` subclass `T` and integrates with the lifetime +management of the former. When the `BaseObject` no longer exists, e.g. when +it was garbage collected, accessing it through `weak_ptr.get()` will return +`nullptr`. + +`BaseObjectPtr` is similar to `std::shared_ptr`, but also holds on to +objects of a `BaseObject` subclass `T`. While there are `BaseObjectPtr`s +pointing to a given object, the `BaseObject` will always maintain a strong +reference to its associated JavaScript object. This can be useful when one +`BaseObject` refers to another `BaseObject` and wants to make sure it stays +alive during the lifetime of that reference. + +A `BaseObject` can be “detached” throught the `BaseObject::Detach()` method. +In this case, it will be deleted once the last `BaseObjectPtr` referring to +it is destroyed. There must be at least one such pointer when `Detach()` is +called. This can be useful when one `BaseObject` fully owns another +`BaseObject`. + + +### `AsyncWrap` + +`AsyncWrap` is a subclass of `BaseObject` that additionally provides tracking +functions for asynchronous calls. It is commonly used for classes whose methods +make calls into JavaScript without any JavaScript stack below, i.e. more or less +directly from the event loop. It is defined in [`async_wrap.h`][]. + +Every `AsyncWrap` subclass has a “provider type”. A list of provider types is +maintained in `src/async_wrap.h`. + +Every `AsyncWrap` instance is associated with two numbers, the “async id” +and the “async trigger id”. The “async id” is generally unique per `AsyncWrap` +instance, and only changes when the object is re-used in some way. + +See the [`async_hooks` module][] documentation for more information about how +this information is provided to async tracking tools. + + +#### `MakeCallback` + +The `AsyncWrap` class has a set of methods called `MakeCallback()`, with the +intention of the naming being that it is used to “make calls back into +JavaScript” from the event loop, rather than making callbacks in some way. +(As the naming has made its way into Node.js’s public API, it’s not worth +the breakage of fixing it). + +`MakeCallback()` generally calls a method on the JavaScript object associated +with the current `AsyncWrap`, and informs async tracking code about these calls +as well as takes care of running the `process.nextTick()` and `Promise` task +queues once it returns. + +Before calling `MakeCallback()`, it is typically necessary to enter both a +`HandleScope` and a `Context::Scope`. + +```c++ +void StatWatcher::Callback(uv_fs_poll_t* handle, + int status, + const uv_stat_t* prev, + const uv_stat_t* curr) { + // Get the StatWatcher instance associated with this call from libuv, + // StatWatcher is a subclass of AsyncWrap. + StatWatcher* wrap = ContainerOf(&StatWatcher::watcher_, handle); + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + + // Transform 'prev' and 'curr' into an array: + Local arr = ...; + + Local argv[] = { Integer::New(env->isolate(), status), arr }; + wrap->MakeCallback(env->onchange_string(), arraysize(argv), argv); +} +``` + +See [Callback scopes][] for more information. + + +### `HandleWrap` + +`HandleWrap` is a subclass of `AsyncWrap` specifically designed to make working +with [libuv handles][] easier. It provides the `.ref()`, `.unref()` and +`.hasRef()` methods as well as `.close()` to enable easier lifetime management +from JavaScript. It is defined in [`handle_wrap.h`][]. + +`HandleWrap` instances are [cleaned up][cleanup hooks] automatically when the +current Node.js [`Environment`][] is destroyed, e.g. when a Worker thread stops. + +`HandleWrap` also provides facilities for diagnostic tooling to get an +overview over libuv handles managed by Node.js. + + +### `ReqWrap` + +`ReqWrap` is a subclass of `AsyncWrap` specifically designed to make working +with [libuv requests][] easier. It is defined in [`req_wrap.h`][]. + +In particular, its `Dispatch()` method is designed to avoid the need to keep +track of the current count of active libuv requests. + +`ReqWrap` also provides facilities for diagnostic tooling to get an +overview over libuv handles managed by Node.js. + + +### Callback scopes + +The public `CallbackScope` and the internally used `InternalCallbackScope` +classes provide the same facilities as [`MakeCallback()`][], namely: + +* Emitting the `'before'` event for async tracking when entering the scope +* Setting the current async IDs to the ones passed to the constructor +* Emitting the `'after'` event for async tracking when leaving the scope +* Running the `process.nextTick()` queue +* Running microtasks, in particular `Promise` callbacks and async/await + functions + +Usually, using `AsyncWrap::MakeCallback()` or using the constructor taking +an `AsyncWrap*` argument (i.e. used as +`InternalCallbackScope callback_scope(this);`) suffices inside of Node.js’s +C++ codebase. + +## C++ utilities + +Node.js uses a few custom C++ utilities, mostly defined in [`util.h`][]. + +### Memory allocation + +Node.js provides `Malloc()`, `Realloc()` and `Calloc()` functions that work +like their C stdlib counterparts, but crash if memory cannot be allocated. +(As V8 does not handle out-of-memory situations gracefully, it does not make +sense for Node.js to attempt to do so in all cases.) + +The `UncheckedMalloc()`, `UncheckedRealloc()` and `UncheckedCalloc()` functions +return `nullptr` in these cases (or when `size == 0`). + +#### Optional stack-based memory allocation + +The `MaybeStackBuffer` class provides a way to allocate memory on the stack +if it is smaller than a given limit, and falls back to allocating it on the +heap if it is larger. This can be useful for performantly allocating temporary +data if it is typically expected to be small (e.g. file paths). + +The `Utf8Value`, `TwoByteValue` (i.e. UTF-16 value) and `BufferValue` +(`Utf8Value` but copy data from a `Buffer` if one is passed) helpers +inherit from this class and allow accessing the characters in a JavaScript +string this way. + +```c++ +static void Chdir(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + // ... + CHECK(args[0]->IsString()); + Utf8Value path(env->isolate(), args[0]); + int err = uv_chdir(*path); + if (err) { + // ... error handling ... + } +} +``` + +### Assertions + +Node.js provides a few macros that behave similar to `assert()`: + +* `CHECK(expression)` aborts the process with a stack trace + if `expression` is false. +* `CHECK_EQ(a, b)` checks for `a == b` +* `CHECK_GE(a, b)` checks for `a >= b` +* `CHECK_GT(a, b)` checks for `a > b` +* `CHECK_LE(a, b)` checks for `a <= b` +* `CHECK_LT(a, b)` checks for `a < b` +* `CHECK_NE(a, b)` checks for `a != b` +* `CHECK_NULL(val)` checks for `a == nullptr` +* `CHECK_NOT_NULL(val)` checks for `a != nullptr` +* `CHECK_IMPLIES(a, b)` checks that `b` is true if `a` is true. +* `UNREACHABLE([message])` aborts the process if it is reached. + +`CHECK`s are always enabled. For checks that should only run in debug mode, use +`DCHECK()`, `DCHECK_EQ()`, etc. + +### Scope-based cleanup + +The `OnScopeLeave()` function can be used to run a piece of code when leaving +the current C++ scope. + +```c++ +static void GetUserInfo(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + uv_passwd_t pwd; + // ... + + const int err = uv_os_get_passwd(&pwd); + + if (err) { + // ... error handling, return early ... + } + + auto free_passwd = OnScopeLeave([&]() { uv_os_free_passwd(&pwd); }); + + // ... + // Turn `pwd` into a JavaScript object now; whenever we return from this + // function, `uv_os_free_passwd()` will be called. + // ... +} +``` + +[`BaseObject`]: #baseobject +[`Context`]: #context +[`Environment`]: #environment +[`Global`]: #global-handles +[`HandleWrap`]: #handlewrap +[`IsolateData`]: #isolate-data +[`Isolate`]: #isolate +[`Local`]: #local-handles +[`MakeCallback()`]: #makecallback +[`MessagePort`]: https://nodejs.org/api/worker_threads.html#worker_threads_class_messageport +[`ReqWrap`]: #reqwrap +[`async_hooks` module]: https://nodejs.org/api/async_hooks.html +[`async_wrap.h`]: async_wrap.h +[`base_object.h`]: base_object.h +[`handle_wrap.h`]: handle_wrap.h +[`memory_tracker.h`]: memory_tracker.h +[`req_wrap.h`]: req_wrap.h +[`util.h`]: util.h +[`v8.h` in Code Search]: https://cs.chromium.org/chromium/src/v8/include/v8.h +[`v8.h` in Node.js master]: https://github.com/nodejs/node/blob/master/deps/v8/include/v8.h +[`v8.h` in V8 master]: https://github.com/v8/v8/blob/master/include/v8.h +[`vm` module]: https://nodejs.org/api/vm.html +[C++ coding style]: ../CPP_STYLE_GUIDE.md +[Callback scopes]: #callback-scopes +[JavaScript value handles]: #js-handles +[N-API]: https://nodejs.org/api/n-api.html +[binding function]: #binding-functions +[cleanup hooks]: #cleanup-hooks +[event loop]: #event-loop +[exception handling]: #exception-handling +[internal field]: #internal-fields +[introduction for V8 embedders]: https://v8.dev/docs/embed +[libuv handles]: #libuv-handles-and-requests +[libuv requests]: #libuv-handles-and-requests +[libuv]: https://libuv.org/ +[reference documentation for the libuv API]: http://docs.libuv.org/en/v1.x/ diff --git a/src/api/encoding.cc b/src/api/encoding.cc index 21d327509727a2..fa872f8b76cd31 100644 --- a/src/api/encoding.cc +++ b/src/api/encoding.cc @@ -1,5 +1,4 @@ #include "node.h" -#include "env-inl.h" #include "string_bytes.h" #include "util-inl.h" #include "v8.h" diff --git a/src/api/utils.cc b/src/api/utils.cc index da7281a68f3bb1..f07f9bea234b0d 100644 --- a/src/api/utils.cc +++ b/src/api/utils.cc @@ -1,6 +1,4 @@ #include "node.h" -#include "node_internals.h" -#include "util-inl.h" #include diff --git a/src/async_wrap.cc b/src/async_wrap.cc index 8410dd2e0d8c65..58d89225e0e523 100644 --- a/src/async_wrap.cc +++ b/src/async_wrap.cc @@ -27,7 +27,6 @@ #include "util-inl.h" #include "v8.h" -#include "v8-profiler.h" using v8::Context; using v8::DontDelete; diff --git a/src/cares_wrap.cc b/src/cares_wrap.cc index 1fb0f47dd80f08..f7a02e469aa79a 100644 --- a/src/cares_wrap.cc +++ b/src/cares_wrap.cc @@ -904,7 +904,8 @@ int ParseTxtReply(Environment* env, uint32_t i = 0, j; uint32_t offset = ret->Length(); for (j = 0; current != nullptr; current = current->next) { - Local txt = OneByteString(env->isolate(), current->txt); + Local txt = + OneByteString(env->isolate(), current->txt, current->length); // New record found - write out the current chunk if (current->record_start) { diff --git a/src/connect_wrap.cc b/src/connect_wrap.cc index dacdf72da7c494..e1d34c11a70b42 100644 --- a/src/connect_wrap.cc +++ b/src/connect_wrap.cc @@ -1,14 +1,12 @@ #include "connect_wrap.h" - -#include "env-inl.h" #include "req_wrap-inl.h" -#include "util-inl.h" namespace node { using v8::Local; using v8::Object; +class Environment; ConnectWrap::ConnectWrap(Environment* env, Local req_wrap_obj, diff --git a/src/connect_wrap.h b/src/connect_wrap.h index 88221b77468631..eaa533025759a0 100644 --- a/src/connect_wrap.h +++ b/src/connect_wrap.h @@ -3,10 +3,8 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS -#include "env.h" #include "req_wrap-inl.h" #include "async_wrap.h" -#include "v8.h" namespace node { diff --git a/src/connection_wrap.h b/src/connection_wrap.h index db74dc5df4aa5b..e91b2ab35b6d06 100644 --- a/src/connection_wrap.h +++ b/src/connection_wrap.h @@ -4,7 +4,6 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #include "stream_wrap.h" -#include "v8.h" namespace node { diff --git a/src/debug_utils.cc b/src/debug_utils.cc index a55936f4e79c28..4e52feb69d9027 100644 --- a/src/debug_utils.cc +++ b/src/debug_utils.cc @@ -1,6 +1,5 @@ #include "debug_utils.h" #include "env-inl.h" -#include "util-inl.h" #ifdef __POSIX__ #if defined(__linux__) diff --git a/src/env-inl.h b/src/env-inl.h index 15b5010deb7c90..d75b4ea743c4f4 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -1102,6 +1102,21 @@ inline void Environment::SetProtoMethodNoSideEffect( t->SetClassName(name_string); // NODE_SET_PROTOTYPE_METHOD() compatibility. } +inline void Environment::SetInstanceMethod(v8::Local that, + const char* name, + v8::FunctionCallback callback) { + v8::Local signature = v8::Signature::New(isolate(), that); + v8::Local t = + NewFunctionTemplate(callback, signature, v8::ConstructorBehavior::kThrow, + v8::SideEffectType::kHasSideEffect); + // kInternalized strings are created in the old space. + const v8::NewStringType type = v8::NewStringType::kInternalized; + v8::Local name_string = + v8::String::NewFromUtf8(isolate(), name, type).ToLocalChecked(); + that->InstanceTemplate()->Set(name_string, t); + t->SetClassName(name_string); +} + void Environment::AddCleanupHook(void (*fn)(void*), void* arg) { auto insertion_info = cleanup_hooks_.emplace(CleanupHookCallback { fn, arg, cleanup_hook_counter_++ @@ -1118,6 +1133,7 @@ void Environment::RemoveCleanupHook(void (*fn)(void*), void* arg) { inline void Environment::RegisterFinalizationGroupForCleanup( v8::Local group) { cleanup_finalization_groups_.emplace_back(isolate(), group); + uv_async_send(&cleanup_finalization_groups_async_); } size_t CleanupHookCallback::Hash::operator()( diff --git a/src/env.cc b/src/env.cc index 5f9a6acb461f97..bf3420f826f02a 100644 --- a/src/env.cc +++ b/src/env.cc @@ -7,11 +7,11 @@ #include "node_errors.h" #include "node_file.h" #include "node_internals.h" -#include "node_native_module.h" #include "node_options-inl.h" #include "node_process.h" #include "node_v8_platform-inl.h" #include "node_worker.h" +#include "req_wrap-inl.h" #include "tracing/agent.h" #include "tracing/traced_value.h" #include "util-inl.h" @@ -36,6 +36,7 @@ using v8::HandleScope; using v8::Integer; using v8::Isolate; using v8::Local; +using v8::MaybeLocal; using v8::NewStringType; using v8::Number; using v8::Object; @@ -460,8 +461,17 @@ void Environment::InitializeLibuv(bool start_profiler_idle_notifier) { // will be recorded with state=IDLE. uv_prepare_init(event_loop(), &idle_prepare_handle_); uv_check_init(event_loop(), &idle_check_handle_); + uv_async_init( + event_loop(), + &cleanup_finalization_groups_async_, + [](uv_async_t* async) { + Environment* env = ContainerOf( + &Environment::cleanup_finalization_groups_async_, async); + env->CleanupFinalizationGroups(); + }); uv_unref(reinterpret_cast(&idle_prepare_handle_)); uv_unref(reinterpret_cast(&idle_check_handle_)); + uv_unref(reinterpret_cast(&cleanup_finalization_groups_async_)); thread_stopper()->Install( this, static_cast(this), [](uv_async_t* handle) { @@ -524,9 +534,18 @@ void Environment::RegisterHandleCleanups() { reinterpret_cast(&idle_check_handle_), close_and_finish, nullptr); + RegisterHandleCleanup( + reinterpret_cast(&cleanup_finalization_groups_async_), + close_and_finish, + nullptr); } void Environment::CleanupHandles() { + Isolate::DisallowJavascriptExecutionScope disallow_js(isolate(), + Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE); + + RunAndClearNativeImmediates(true /* skip SetUnrefImmediate()s */); + for (ReqWrapBase* request : req_wrap_queue_) request->Cancel(); @@ -642,7 +661,7 @@ void Environment::AtExit(void (*cb)(void* arg), void* arg) { at_exit_functions_.push_front(ExitCallback{cb, arg}); } -void Environment::RunAndClearNativeImmediates() { +void Environment::RunAndClearNativeImmediates(bool only_refed) { TraceEventScope trace_scope(TRACING_CATEGORY_NODE1(environment), "RunAndClearNativeImmediates", this); size_t ref_count = 0; @@ -659,9 +678,11 @@ void Environment::RunAndClearNativeImmediates() { if (head->is_refed()) ref_count++; - head->Call(this); + if (head->is_refed() || !only_refed) + head->Call(this); + if (UNLIKELY(try_catch.HasCaught())) { - if (!try_catch.HasTerminated()) + if (!try_catch.HasTerminated() && can_call_into_js()) errors::TriggerUncaughtException(isolate(), try_catch); // We are done with the current callback. Move one iteration along, @@ -1040,19 +1061,27 @@ char* Environment::Reallocate(char* data, size_t old_size, size_t size) { return new_data; } -bool Environment::RunWeakRefCleanup() { +void Environment::RunWeakRefCleanup() { isolate()->ClearKeptObjects(); +} - while (!cleanup_finalization_groups_.empty()) { +void Environment::CleanupFinalizationGroups() { + HandleScope handle_scope(isolate()); + Context::Scope context_scope(context()); + TryCatchScope try_catch(this); + + while (!cleanup_finalization_groups_.empty() && can_call_into_js()) { Local fg = cleanup_finalization_groups_.front().Get(isolate()); cleanup_finalization_groups_.pop_front(); if (!FinalizationGroup::Cleanup(fg).FromMaybe(false)) { - return false; + if (try_catch.HasCaught() && !try_catch.HasTerminated()) + errors::TriggerUncaughtException(isolate(), try_catch); + // Re-schedule the execution of the remainder of the queue. + uv_async_send(&cleanup_finalization_groups_async_); + return; } } - - return true; } void AsyncRequest::Install(Environment* env, void* data, uv_async_cb target) { diff --git a/src/env.h b/src/env.h index 495d92471a336f..c25a03ea1e520a 100644 --- a/src/env.h +++ b/src/env.h @@ -541,7 +541,8 @@ struct ContextInfo { #define DEBUG_CATEGORY_NAMES(V) \ NODE_ASYNC_PROVIDER_TYPES(V) \ V(INSPECTOR_SERVER) \ - V(INSPECTOR_PROFILER) + V(INSPECTOR_PROFILER) \ + V(WASI) enum class DebugCategory { #define V(name) name, @@ -1114,6 +1115,11 @@ class Environment : public MemoryRetainer { const char* name, v8::FunctionCallback callback); + inline void SetInstanceMethod(v8::Local that, + const char* name, + v8::FunctionCallback callback); + + // Safe variants denote the function has no side effects. inline void SetMethodNoSideEffect(v8::Local that, const char* name, @@ -1128,7 +1134,8 @@ class Environment : public MemoryRetainer { void RunAtExitCallbacks(); void RegisterFinalizationGroupForCleanup(v8::Local fg); - bool RunWeakRefCleanup(); + void RunWeakRefCleanup(); + void CleanupFinalizationGroups(); // Strings and private symbols are shared across shared contexts // The getters simply proxy to the per-isolate primitive. @@ -1270,6 +1277,7 @@ class Environment : public MemoryRetainer { uv_idle_t immediate_idle_handle_; uv_prepare_t idle_prepare_handle_; uv_check_t idle_check_handle_; + uv_async_t cleanup_finalization_groups_async_; bool profiler_idle_notifier_started_ = false; AsyncHooks async_hooks_; @@ -1415,7 +1423,7 @@ class Environment : public MemoryRetainer { std::unique_ptr native_immediate_callbacks_head_; NativeImmediateCallback* native_immediate_callbacks_tail_ = nullptr; - void RunAndClearNativeImmediates(); + void RunAndClearNativeImmediates(bool only_refed = false); static void CheckImmediate(uv_check_t* handle); // Use an unordered_set, so that we have efficient insertion and removal. diff --git a/src/fs_event_wrap.cc b/src/fs_event_wrap.cc index 5033f027b1f2e0..8a75f0557c4b37 100644 --- a/src/fs_event_wrap.cc +++ b/src/fs_event_wrap.cc @@ -21,7 +21,6 @@ #include "async_wrap-inl.h" #include "env-inl.h" -#include "util-inl.h" #include "node.h" #include "handle_wrap.h" #include "string_bytes.h" diff --git a/src/handle_wrap.cc b/src/handle_wrap.cc index 198b0456e75751..f5d622fc255cdf 100644 --- a/src/handle_wrap.cc +++ b/src/handle_wrap.cc @@ -23,7 +23,6 @@ #include "async_wrap-inl.h" #include "env-inl.h" #include "util-inl.h" -#include "node.h" namespace node { diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc index f13e68c067529e..fe60c82cb5590d 100644 --- a/src/inspector_agent.cc +++ b/src/inspector_agent.cc @@ -60,6 +60,8 @@ static uv_async_t start_io_thread_async; // This is just an additional check to make sure start_io_thread_async // is not accidentally re-used or used when uninitialized. static std::atomic_bool start_io_thread_async_initialized { false }; +// Protects the Agent* stored in start_io_thread_async.data. +static Mutex start_io_thread_async_mutex; class StartIoTask : public Task { public: @@ -97,6 +99,8 @@ static void StartIoThreadWakeup(int signo, siginfo_t* info, void* ucontext) { inline void* StartIoThreadMain(void* unused) { for (;;) { uv_sem_wait(&start_io_thread_semaphore); + Mutex::ScopedLock lock(start_io_thread_async_mutex); + CHECK(start_io_thread_async_initialized); Agent* agent = static_cast(start_io_thread_async.data); if (agent != nullptr) @@ -157,6 +161,7 @@ static int StartDebugSignalHandler() { #ifdef _WIN32 DWORD WINAPI StartIoThreadProc(void* arg) { + Mutex::ScopedLock lock(start_io_thread_async_mutex); CHECK(start_io_thread_async_initialized); Agent* agent = static_cast(start_io_thread_async.data); if (agent != nullptr) @@ -748,14 +753,7 @@ Agent::Agent(Environment* env) debug_options_(env->options()->debug_options()), host_port_(env->inspector_host_port()) {} -Agent::~Agent() { - if (start_io_thread_async.data == this) { - CHECK(start_io_thread_async_initialized.exchange(false)); - start_io_thread_async.data = nullptr; - // This is global, will never get freed - uv_close(reinterpret_cast(&start_io_thread_async), nullptr); - } -} +Agent::~Agent() {} bool Agent::Start(const std::string& path, const DebugOptions& options, @@ -768,6 +766,7 @@ bool Agent::Start(const std::string& path, client_ = std::make_shared(parent_env_, is_main); if (parent_env_->owns_inspector()) { + Mutex::ScopedLock lock(start_io_thread_async_mutex); CHECK_EQ(start_io_thread_async_initialized.exchange(true), false); CHECK_EQ(0, uv_async_init(parent_env_->event_loop(), &start_io_thread_async, @@ -776,6 +775,20 @@ bool Agent::Start(const std::string& path, start_io_thread_async.data = this; // Ignore failure, SIGUSR1 won't work, but that should not block node start. StartDebugSignalHandler(); + + parent_env_->AddCleanupHook([](void* data) { + Environment* env = static_cast(data); + + { + Mutex::ScopedLock lock(start_io_thread_async_mutex); + start_io_thread_async.data = nullptr; + } + + // This is global, will never get freed + env->CloseHandle(&start_io_thread_async, [](uv_async_t*) { + CHECK(start_io_thread_async_initialized.exchange(false)); + }); + }, parent_env_); } AtExit(parent_env_, [](void* env) { diff --git a/src/js_native_api.h b/src/js_native_api.h index cf1596be80e5b1..150103348e4dc6 100644 --- a/src/js_native_api.h +++ b/src/js_native_api.h @@ -518,6 +518,10 @@ NAPI_EXTERN napi_status napi_get_instance_data(napi_env env, // ArrayBuffer detaching NAPI_EXTERN napi_status napi_detach_arraybuffer(napi_env env, napi_value arraybuffer); + +NAPI_EXTERN napi_status napi_is_detached_arraybuffer(napi_env env, + napi_value value, + bool* result); #endif // NAPI_EXPERIMENTAL EXTERN_C_END diff --git a/src/js_native_api_v8.cc b/src/js_native_api_v8.cc index 6484afaaac629e..5506b2b4c6204b 100644 --- a/src/js_native_api_v8.cc +++ b/src/js_native_api_v8.cc @@ -186,8 +186,8 @@ inline static napi_status ConcludeDeferred(napi_env env, } // Wrapper around v8impl::Persistent that implements reference counting. -class Reference : private Finalizer, RefTracker { - private: +class Reference : protected Finalizer, RefTracker { + protected: Reference(napi_env env, v8::Local value, uint32_t initial_refcount, @@ -289,7 +289,7 @@ class Reference : private Finalizer, RefTracker { } } - private: + protected: void Finalize(bool is_env_teardown = false) override { if (_finalize_callback != nullptr) { _env->CallIntoModuleThrow([&](napi_env env) { @@ -310,6 +310,7 @@ class Reference : private Finalizer, RefTracker { } } + private: // The N-API finalizer callback may make calls into the engine. V8's heap is // not in a consistent state during the weak callback, and therefore it does // not support calls back into it. However, it provides a mechanism for adding @@ -335,6 +336,37 @@ class Reference : private Finalizer, RefTracker { bool _delete_self; }; +class ArrayBufferReference final : public Reference { + public: + // Same signatures for ctor and New() as Reference, except this only works + // with ArrayBuffers: + template + explicit ArrayBufferReference(napi_env env, + v8::Local value, + Args&&... args) + : Reference(env, value, std::forward(args)...) {} + + template + static ArrayBufferReference* New(napi_env env, + v8::Local value, + Args&&... args) { + return new ArrayBufferReference(env, value, std::forward(args)...); + } + + private: + void Finalize(bool is_env_teardown) override { + if (is_env_teardown) { + v8::HandleScope handle_scope(_env->isolate); + v8::Local ab = Get(); + CHECK(!ab.IsEmpty()); + CHECK(ab->IsArrayBuffer()); + ab.As()->Detach(); + } + + Reference::Finalize(is_env_teardown); + } +}; + enum UnwrapAction { KeepWrap, RemoveWrap @@ -983,6 +1015,7 @@ napi_status napi_has_own_property(napi_env env, bool* result) { NAPI_PREAMBLE(env); CHECK_ARG(env, key); + CHECK_ARG(env, result); v8::Local context = env->context(); v8::Local obj; @@ -2586,8 +2619,9 @@ napi_status napi_create_external_arraybuffer(napi_env env, if (finalize_cb != nullptr) { // Create a self-deleting weak reference that invokes the finalizer - // callback. - v8impl::Reference::New(env, + // callback and detaches the ArrayBuffer if it still exists on Environment + // teardown. + v8impl::ArrayBufferReference::New(env, buffer, 0, true, @@ -3039,3 +3073,18 @@ napi_status napi_detach_arraybuffer(napi_env env, napi_value arraybuffer) { return napi_clear_last_error(env); } + +napi_status napi_is_detached_arraybuffer(napi_env env, + napi_value arraybuffer, + bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, arraybuffer); + CHECK_ARG(env, result); + + v8::Local value = v8impl::V8LocalValueFromJsValue(arraybuffer); + + *result = value->IsArrayBuffer() && + value.As()->GetBackingStore()->Data() == nullptr; + + return napi_clear_last_error(env); +} diff --git a/src/js_stream.cc b/src/js_stream.cc index 23bdb9b4892512..a67fd37dbdb2b6 100644 --- a/src/js_stream.cc +++ b/src/js_stream.cc @@ -2,7 +2,6 @@ #include "async_wrap.h" #include "env-inl.h" -#include "node_buffer.h" #include "node_errors.h" #include "stream_base-inl.h" #include "util-inl.h" diff --git a/src/js_stream.h b/src/js_stream.h index 460ac23bc98b21..d96b3d0062dc1f 100644 --- a/src/js_stream.h +++ b/src/js_stream.h @@ -5,7 +5,6 @@ #include "async_wrap.h" #include "stream_base.h" -#include "v8.h" namespace node { diff --git a/src/module_wrap.h b/src/module_wrap.h index ef20d255e916da..bee4000d168062 100644 --- a/src/module_wrap.h +++ b/src/module_wrap.h @@ -6,10 +6,12 @@ #include #include #include -#include "node_url.h" -#include "base_object-inl.h" +#include "base_object.h" namespace node { + +class Environment; + namespace loader { enum ScriptType : int { diff --git a/src/node_binding.cc b/src/node_binding.cc index f57ddb54b629ca..82836585c589a5 100644 --- a/src/node_binding.cc +++ b/src/node_binding.cc @@ -85,6 +85,7 @@ V(util) \ V(uv) \ V(v8) \ + V(wasi) \ V(worker) \ V(zlib) diff --git a/src/node_binding.h b/src/node_binding.h index dd94fab36a0e8f..42f3c5b8d00880 100644 --- a/src/node_binding.h +++ b/src/node_binding.h @@ -10,9 +10,6 @@ #include "node.h" #define NAPI_EXPERIMENTAL #include "node_api.h" -#include "util.h" -#include "uv.h" -#include "v8.h" enum { NM_F_BUILTIN = 1 << 0, // Unused. diff --git a/src/node_buffer.cc b/src/node_buffer.cc index d87d38334add84..e5c4655b4ccdb9 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -28,7 +28,6 @@ #include "string_bytes.h" #include "string_search.h" #include "util-inl.h" -#include "v8-profiler.h" #include "v8.h" #include @@ -54,6 +53,7 @@ using v8::Context; using v8::EscapableHandleScope; using v8::FunctionCallbackInfo; using v8::Global; +using v8::HandleScope; using v8::Int32; using v8::Integer; using v8::Isolate; @@ -74,8 +74,10 @@ namespace { class CallbackInfo { public: + ~CallbackInfo(); + static inline void Free(char* data, void* hint); - static inline CallbackInfo* New(Isolate* isolate, + static inline CallbackInfo* New(Environment* env, Local object, FreeCallback callback, char* data, @@ -85,9 +87,10 @@ class CallbackInfo { CallbackInfo& operator=(const CallbackInfo&) = delete; private: + static void CleanupHook(void* data); static void WeakCallback(const WeakCallbackInfo&); inline void WeakCallback(Isolate* isolate); - inline CallbackInfo(Isolate* isolate, + inline CallbackInfo(Environment* env, Local object, FreeCallback callback, char* data, @@ -96,6 +99,7 @@ class CallbackInfo { FreeCallback const callback_; char* const data_; void* const hint_; + Environment* const env_; }; @@ -104,31 +108,53 @@ void CallbackInfo::Free(char* data, void*) { } -CallbackInfo* CallbackInfo::New(Isolate* isolate, +CallbackInfo* CallbackInfo::New(Environment* env, Local object, FreeCallback callback, char* data, void* hint) { - return new CallbackInfo(isolate, object, callback, data, hint); + return new CallbackInfo(env, object, callback, data, hint); } -CallbackInfo::CallbackInfo(Isolate* isolate, +CallbackInfo::CallbackInfo(Environment* env, Local object, FreeCallback callback, char* data, void* hint) - : persistent_(isolate, object), + : persistent_(env->isolate(), object), callback_(callback), data_(data), - hint_(hint) { + hint_(hint), + env_(env) { ArrayBuffer::Contents obj_c = object->GetContents(); CHECK_EQ(data_, static_cast(obj_c.Data())); if (object->ByteLength() != 0) CHECK_NOT_NULL(data_); persistent_.SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter); - isolate->AdjustAmountOfExternalAllocatedMemory(sizeof(*this)); + env->AddCleanupHook(CleanupHook, this); + env->isolate()->AdjustAmountOfExternalAllocatedMemory(sizeof(*this)); +} + + +CallbackInfo::~CallbackInfo() { + persistent_.Reset(); + env_->RemoveCleanupHook(CleanupHook, this); +} + + +void CallbackInfo::CleanupHook(void* data) { + CallbackInfo* self = static_cast(data); + + { + HandleScope handle_scope(self->env_->isolate()); + Local ab = self->persistent_.Get(self->env_->isolate()); + CHECK(!ab.IsEmpty()); + ab->Detach(); + } + + self->WeakCallback(self->env_->isolate()); } @@ -389,7 +415,7 @@ MaybeLocal New(Environment* env, } MaybeLocal ui = Buffer::New(env, ab, 0, length); - CallbackInfo::New(env->isolate(), ab, callback, data, hint); + CallbackInfo::New(env, ab, callback, data, hint); if (ui.IsEmpty()) return MaybeLocal(); diff --git a/src/node_config.cc b/src/node_config.cc index 92985dff2f8b0c..16050bdd5b967d 100644 --- a/src/node_config.cc +++ b/src/node_config.cc @@ -84,6 +84,9 @@ static void Initialize(Local target, READONLY_PROPERTY(target, "hasCachedBuiltins", v8::Boolean::New(isolate, native_module::has_code_cache)); + + if (env->options()->experimental_wasi) + READONLY_TRUE_PROPERTY(target, "experimentalWasi"); } // InitConfig } // namespace node diff --git a/src/node_crypto.cc b/src/node_crypto.cc index fa85f7855371b5..d1bd5471f3fba2 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -142,8 +142,8 @@ static bool extra_root_certs_loaded = false; template void SSLWrap::AddMethods(Environment* env, Local t); template void SSLWrap::ConfigureSecureContext(SecureContext* sc); -template void SSLWrap::SetSNIContext(SecureContext* sc); template int SSLWrap::SetCACerts(SecureContext* sc); +template void SSLWrap::MemoryInfo(MemoryTracker* tracker) const; template SSL_SESSION* SSLWrap::GetSessionCallback( SSL* s, const unsigned char* key, @@ -2990,9 +2990,10 @@ void SSLWrap::CertCbDone(const FunctionCallbackInfo& args) { goto fire_cb; if (cons->HasInstance(ctx)) { - SecureContext* sc; - ASSIGN_OR_RETURN_UNWRAP(&sc, ctx.As()); - w->sni_context_.Reset(env->isolate(), ctx); + SecureContext* sc = Unwrap(ctx.As()); + CHECK_NOT_NULL(sc); + // Store the SNI context for later use. + w->sni_context_ = BaseObjectPtr(sc); int rv; @@ -3050,15 +3051,6 @@ void SSLWrap::DestroySSL() { } -template -void SSLWrap::SetSNIContext(SecureContext* sc) { - ConfigureSecureContext(sc); - CHECK_EQ(SSL_set_SSL_CTX(ssl_.get(), sc->ctx_.get()), sc->ctx_.get()); - - SetCACerts(sc); -} - - template int SSLWrap::SetCACerts(SecureContext* sc) { int err = SSL_set1_verify_cert_store(ssl_.get(), @@ -3074,6 +3066,12 @@ int SSLWrap::SetCACerts(SecureContext* sc) { return 1; } +template +void SSLWrap::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("ocsp_response", ocsp_response_); + tracker->TrackField("sni_context", sni_context_); +} + int VerifyCallback(int preverify_ok, X509_STORE_CTX* ctx) { // From https://www.openssl.org/docs/man1.1.1/man3/SSL_verify_cb: // @@ -5041,19 +5039,17 @@ static AllocatedBuffer ConvertSignatureToP1363(Environment* env, const unsigned char* sig_data = reinterpret_cast(signature.data()); - ECDSA_SIG* asn1_sig = d2i_ECDSA_SIG(nullptr, &sig_data, signature.size()); - if (asn1_sig == nullptr) + ECDSASigPointer asn1_sig(d2i_ECDSA_SIG(nullptr, &sig_data, signature.size())); + if (!asn1_sig) return AllocatedBuffer(); AllocatedBuffer buf = env->AllocateManaged(2 * n); unsigned char* data = reinterpret_cast(buf.data()); - const BIGNUM* r = ECDSA_SIG_get0_r(asn1_sig); - const BIGNUM* s = ECDSA_SIG_get0_s(asn1_sig); - CHECK_EQ(n, BN_bn2binpad(r, data, n)); - CHECK_EQ(n, BN_bn2binpad(s, data + n, n)); - - ECDSA_SIG_free(asn1_sig); + const BIGNUM* r = ECDSA_SIG_get0_r(asn1_sig.get()); + const BIGNUM* s = ECDSA_SIG_get0_s(asn1_sig.get()); + CHECK_EQ(n, static_cast(BN_bn2binpad(r, data, n))); + CHECK_EQ(n, static_cast(BN_bn2binpad(s, data + n, n))); return buf; } @@ -5071,19 +5067,18 @@ static ByteSource ConvertSignatureToDER( if (signature.length() != 2 * n) return ByteSource(); - ECDSA_SIG* asn1_sig = ECDSA_SIG_new(); - CHECK_NOT_NULL(asn1_sig); + ECDSASigPointer asn1_sig(ECDSA_SIG_new()); + CHECK(asn1_sig); BIGNUM* r = BN_new(); CHECK_NOT_NULL(r); BIGNUM* s = BN_new(); CHECK_NOT_NULL(s); CHECK_EQ(r, BN_bin2bn(sig_data, n, r)); CHECK_EQ(s, BN_bin2bn(sig_data + n, n, s)); - CHECK_EQ(1, ECDSA_SIG_set0(asn1_sig, r, s)); + CHECK_EQ(1, ECDSA_SIG_set0(asn1_sig.get(), r, s)); unsigned char* data = nullptr; - int len = i2d_ECDSA_SIG(asn1_sig, &data); - ECDSA_SIG_free(asn1_sig); + int len = i2d_ECDSA_SIG(asn1_sig.get(), &data); if (len <= 0) return ByteSource(); diff --git a/src/node_crypto.h b/src/node_crypto.h index 56a9ad3104dbeb..3f14e155dafc49 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -72,6 +72,7 @@ using ECGroupPointer = DeleteFnPtr; using ECPointPointer = DeleteFnPtr; using ECKeyPointer = DeleteFnPtr; using DHPointer = DeleteFnPtr; +using ECDSASigPointer = DeleteFnPtr; extern int VerifyCallback(int preverify_ok, X509_STORE_CTX* ctx); @@ -222,6 +223,8 @@ class SSLWrap { inline bool is_awaiting_new_session() const { return awaiting_new_session_; } inline bool is_waiting_cert_cb() const { return cert_cb_ != nullptr; } + void MemoryInfo(MemoryTracker* tracker) const; + protected: typedef void (*CertCb)(void* arg); @@ -286,7 +289,6 @@ class SSLWrap { void DestroySSL(); void WaitForCertCb(CertCb cb, void* arg); - void SetSNIContext(SecureContext* sc); int SetCACerts(SecureContext* sc); inline Environment* ssl_env() const { @@ -308,7 +310,7 @@ class SSLWrap { ClientHelloParser hello_parser_; v8::Global ocsp_response_; - v8::Global sni_context_; + BaseObjectPtr sni_context_; friend class SecureContext; }; diff --git a/src/node_dir.cc b/src/node_dir.cc index ffef27f4521403..37285928fbd731 100644 --- a/src/node_dir.cc +++ b/src/node_dir.cc @@ -1,10 +1,11 @@ #include "node_dir.h" +#include "node_file-inl.h" #include "node_process.h" +#include "memory_tracker-inl.h" #include "util.h" #include "tracing/trace_event.h" -#include "req_wrap-inl.h" #include "string_bytes.h" #include @@ -85,6 +86,10 @@ DirHandle::~DirHandle() { CHECK(closed_); // We have to be closed at the point } +void DirHandle::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackFieldWithSize("dir", sizeof(*dir_)); +} + // Close the directory handle if it hasn't already been closed. A process // warning will be emitted using a SetImmediate to avoid calling back to // JS during GC. If closing the fd fails at this point, a fatal exception @@ -296,7 +301,6 @@ void AfterOpenDir(uv_fs_t* req) { } Environment* env = req_wrap->env(); - Local error; uv_dir_t* dir = static_cast(req->ptr); DirHandle* handle = DirHandle::New(env, dir); diff --git a/src/node_dir.h b/src/node_dir.h index ae6d0eb170d679..caef7a5d309180 100644 --- a/src/node_dir.h +++ b/src/node_dir.h @@ -4,8 +4,6 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #include "node_file.h" -#include "node.h" -#include "req_wrap-inl.h" namespace node { @@ -20,16 +18,13 @@ class DirHandle : public AsyncWrap { ~DirHandle() override; static void New(const v8::FunctionCallbackInfo& args); - static void Open(const v8::FunctionCallbackInfo& args); - static void Read(const v8::FunctionCallbackInfo& args); - static void Close(const v8::FunctionCallbackInfo& args); + static void Open(const v8::FunctionCallbackInfo& args); + static void Read(const v8::FunctionCallbackInfo& args); + static void Close(const v8::FunctionCallbackInfo& args); inline uv_dir_t* dir() { return dir_; } - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackFieldWithSize("dir", sizeof(*dir_)); - } - + void MemoryInfo(MemoryTracker* tracker) const override; SET_MEMORY_INFO_NAME(DirHandle) SET_SELF_SIZE(DirHandle) diff --git a/src/node_errors.cc b/src/node_errors.cc index e094fe4681fc56..17c1bd7f55c0fb 100644 --- a/src/node_errors.cc +++ b/src/node_errors.cc @@ -278,6 +278,9 @@ static void ReportFatalException(Environment* env, Local error, Local message, EnhanceFatalException enhance_stack) { + if (!env->can_call_into_js()) + enhance_stack = EnhanceFatalException::kDontEnhance; + Isolate* isolate = env->isolate(); CHECK(!error.IsEmpty()); CHECK(!message.IsEmpty()); @@ -956,7 +959,7 @@ void TriggerUncaughtException(Isolate* isolate, } MaybeLocal handled; - { + if (env->can_call_into_js()) { // We do not expect the global uncaught exception itself to throw any more // exceptions. If it does, exit the current Node.js instance. errors::TryCatchScope try_catch(env, diff --git a/src/node_errors.h b/src/node_errors.h index f6fca6c690a1d2..6080aa93dba4bd 100644 --- a/src/node_errors.h +++ b/src/node_errors.h @@ -3,8 +3,6 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS -#include "node.h" -#include "util.h" #include "env.h" #include "v8.h" diff --git a/src/node_file-inl.h b/src/node_file-inl.h new file mode 100644 index 00000000000000..390f6c7415770f --- /dev/null +++ b/src/node_file-inl.h @@ -0,0 +1,283 @@ +#ifndef SRC_NODE_FILE_INL_H_ +#define SRC_NODE_FILE_INL_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "node_file.h" +#include "req_wrap-inl.h" + +namespace node { +namespace fs { + +FSContinuationData::FSContinuationData(uv_fs_t* req, int mode, uv_fs_cb done_cb) + : done_cb_(done_cb), req_(req), mode_(mode) { +} + +void FSContinuationData::PushPath(std::string&& path) { + paths_.emplace_back(std::move(path)); +} + +void FSContinuationData::PushPath(const std::string& path) { + paths_.push_back(path); +} + +std::string FSContinuationData::PopPath() { + CHECK_GT(paths_.size(), 0); + std::string path = std::move(paths_.back()); + paths_.pop_back(); + return path; +} + +void FSContinuationData::Done(int result) { + req_->result = result; + done_cb_(req_); +} + +FSReqBase::FSReqBase(Environment* env, + v8::Local req, + AsyncWrap::ProviderType type, + bool use_bigint) + : ReqWrap(env, req, type), use_bigint_(use_bigint) { +} + +void FSReqBase::Init(const char* syscall, + const char* data, + size_t len, + enum encoding encoding) { + syscall_ = syscall; + encoding_ = encoding; + + if (data != nullptr) { + CHECK(!has_data_); + buffer_.AllocateSufficientStorage(len + 1); + buffer_.SetLengthAndZeroTerminate(len); + memcpy(*buffer_, data, len); + has_data_ = true; + } +} + +FSReqBase::FSReqBuffer& +FSReqBase::Init(const char* syscall, size_t len, enum encoding encoding) { + syscall_ = syscall; + encoding_ = encoding; + + buffer_.AllocateSufficientStorage(len + 1); + has_data_ = false; // so that the data does not show up in error messages + return buffer_; +} + +FSReqCallback::FSReqCallback(Environment* env, + v8::Local req, bool use_bigint) + : FSReqBase(env, req, AsyncWrap::PROVIDER_FSREQCALLBACK, use_bigint) {} + +template +void FillStatsArray(AliasedBufferBase* fields, + const uv_stat_t* s, + const size_t offset) { +#define SET_FIELD_WITH_STAT(stat_offset, stat) \ + fields->SetValue(offset + static_cast(FsStatsOffset::stat_offset), \ + static_cast(stat)) + +#define SET_FIELD_WITH_TIME_STAT(stat_offset, stat) \ + /* NOLINTNEXTLINE(runtime/int) */ \ + SET_FIELD_WITH_STAT(stat_offset, static_cast(stat)) + + SET_FIELD_WITH_STAT(kDev, s->st_dev); + SET_FIELD_WITH_STAT(kMode, s->st_mode); + SET_FIELD_WITH_STAT(kNlink, s->st_nlink); + SET_FIELD_WITH_STAT(kUid, s->st_uid); + SET_FIELD_WITH_STAT(kGid, s->st_gid); + SET_FIELD_WITH_STAT(kRdev, s->st_rdev); + SET_FIELD_WITH_STAT(kBlkSize, s->st_blksize); + SET_FIELD_WITH_STAT(kIno, s->st_ino); + SET_FIELD_WITH_STAT(kSize, s->st_size); + SET_FIELD_WITH_STAT(kBlocks, s->st_blocks); + + SET_FIELD_WITH_TIME_STAT(kATimeSec, s->st_atim.tv_sec); + SET_FIELD_WITH_TIME_STAT(kATimeNsec, s->st_atim.tv_nsec); + SET_FIELD_WITH_TIME_STAT(kMTimeSec, s->st_mtim.tv_sec); + SET_FIELD_WITH_TIME_STAT(kMTimeNsec, s->st_mtim.tv_nsec); + SET_FIELD_WITH_TIME_STAT(kCTimeSec, s->st_ctim.tv_sec); + SET_FIELD_WITH_TIME_STAT(kCTimeNsec, s->st_ctim.tv_nsec); + SET_FIELD_WITH_TIME_STAT(kBirthTimeSec, s->st_birthtim.tv_sec); + SET_FIELD_WITH_TIME_STAT(kBirthTimeNsec, s->st_birthtim.tv_nsec); + +#undef SET_FIELD_WITH_TIME_STAT +#undef SET_FIELD_WITH_STAT +} + +v8::Local FillGlobalStatsArray(Environment* env, + const bool use_bigint, + const uv_stat_t* s, + const bool second) { + const ptrdiff_t offset = + second ? static_cast(FsStatsOffset::kFsStatsFieldsNumber) : 0; + if (use_bigint) { + auto* const arr = env->fs_stats_field_bigint_array(); + FillStatsArray(arr, s, offset); + return arr->GetJSArray(); + } else { + auto* const arr = env->fs_stats_field_array(); + FillStatsArray(arr, s, offset); + return arr->GetJSArray(); + } +} + +template +FSReqPromise* +FSReqPromise::New(Environment* env, bool use_bigint) { + v8::Local obj; + if (!env->fsreqpromise_constructor_template() + ->NewInstance(env->context()) + .ToLocal(&obj)) { + return nullptr; + } + v8::Local resolver; + if (!v8::Promise::Resolver::New(env->context()).ToLocal(&resolver) || + obj->Set(env->context(), env->promise_string(), resolver).IsNothing()) { + return nullptr; + } + return new FSReqPromise(env, obj, use_bigint); +} + +template +FSReqPromise::~FSReqPromise() { + // Validate that the promise was explicitly resolved or rejected. + CHECK(finished_); +} + +template +FSReqPromise::FSReqPromise( + Environment* env, + v8::Local obj, + bool use_bigint) + : FSReqBase(env, obj, AsyncWrap::PROVIDER_FSREQPROMISE, use_bigint), + stats_field_array_( + env->isolate(), + static_cast(FsStatsOffset::kFsStatsFieldsNumber)) {} + +template +void FSReqPromise::Reject(v8::Local reject) { + finished_ = true; + v8::HandleScope scope(env()->isolate()); + InternalCallbackScope callback_scope(this); + v8::Local value = + object()->Get(env()->context(), + env()->promise_string()).ToLocalChecked(); + v8::Local resolver = value.As(); + USE(resolver->Reject(env()->context(), reject).FromJust()); +} + +template +void FSReqPromise::Resolve(v8::Local value) { + finished_ = true; + v8::HandleScope scope(env()->isolate()); + InternalCallbackScope callback_scope(this); + v8::Local val = + object()->Get(env()->context(), + env()->promise_string()).ToLocalChecked(); + v8::Local resolver = val.As(); + USE(resolver->Resolve(env()->context(), value).FromJust()); +} + +template +void FSReqPromise::ResolveStat(const uv_stat_t* stat) { + FillStatsArray(&stats_field_array_, stat); + Resolve(stats_field_array_.GetJSArray()); +} + +template +void FSReqPromise::SetReturnValue( + const v8::FunctionCallbackInfo& args) { + v8::Local val = + object()->Get(env()->context(), + env()->promise_string()).ToLocalChecked(); + v8::Local resolver = val.As(); + args.GetReturnValue().Set(resolver->GetPromise()); +} + +template +void FSReqPromise::MemoryInfo(MemoryTracker* tracker) const { + FSReqBase::MemoryInfo(tracker); + tracker->TrackField("stats_field_array", stats_field_array_); +} + +FSReqBase* GetReqWrap(Environment* env, v8::Local value, + bool use_bigint) { + if (value->IsObject()) { + return Unwrap(value.As()); + } else if (value->StrictEquals(env->fs_use_promises_symbol())) { + if (use_bigint) { + return FSReqPromise::New(env, use_bigint); + } else { + return FSReqPromise::New(env, use_bigint); + } + } + return nullptr; +} + +// Returns nullptr if the operation fails from the start. +template +FSReqBase* AsyncDestCall(Environment* env, FSReqBase* req_wrap, + const v8::FunctionCallbackInfo& args, + const char* syscall, const char* dest, + size_t len, enum encoding enc, uv_fs_cb after, + Func fn, Args... fn_args) { + CHECK_NOT_NULL(req_wrap); + req_wrap->Init(syscall, dest, len, enc); + int err = req_wrap->Dispatch(fn, fn_args..., after); + if (err < 0) { + uv_fs_t* uv_req = req_wrap->req(); + uv_req->result = err; + uv_req->path = nullptr; + after(uv_req); // after may delete req_wrap if there is an error + req_wrap = nullptr; + } else { + req_wrap->SetReturnValue(args); + } + + return req_wrap; +} + +// Returns nullptr if the operation fails from the start. +template +FSReqBase* AsyncCall(Environment* env, + FSReqBase* req_wrap, + const v8::FunctionCallbackInfo& args, + const char* syscall, enum encoding enc, + uv_fs_cb after, Func fn, Args... fn_args) { + return AsyncDestCall(env, req_wrap, args, + syscall, nullptr, 0, enc, + after, fn, fn_args...); +} + +// Template counterpart of SYNC_CALL, except that it only puts +// the error number and the syscall in the context instead of +// creating an error in the C++ land. +// ctx must be checked using value->IsObject() before being passed. +template +int SyncCall(Environment* env, v8::Local ctx, + FSReqWrapSync* req_wrap, const char* syscall, + Func fn, Args... args) { + env->PrintSyncTrace(); + int err = fn(env->event_loop(), &(req_wrap->req), args..., nullptr); + if (err < 0) { + v8::Local context = env->context(); + v8::Local ctx_obj = ctx.As(); + v8::Isolate* isolate = env->isolate(); + ctx_obj->Set(context, + env->errno_string(), + v8::Integer::New(isolate, err)).Check(); + ctx_obj->Set(context, + env->syscall_string(), + OneByteString(isolate, syscall)).Check(); + } + return err; +} + +} // namespace fs +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_NODE_FILE_INL_H_ diff --git a/src/node_file.cc b/src/node_file.cc index 48b382986c0bb5..577b9c7c4173af 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -18,7 +18,8 @@ // 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 "node_file.h" +#include "node_file.h" // NOLINT(build/include_inline) +#include "node_file-inl.h" #include "aliased_buffer.h" #include "memory_tracker-inl.h" #include "node_buffer.h" @@ -107,6 +108,19 @@ inline int64_t GetOffset(Local value) { // functions, and thus does not wrap them properly. typedef void(*uv_fs_callback_t)(uv_fs_t*); + +void FSContinuationData::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("paths", paths_); +} + +FileHandleReadWrap::~FileHandleReadWrap() {} + +FSReqBase::~FSReqBase() {} + +void FSReqBase::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("continuation_data", continuation_data_); +} + // The FileHandle object wraps a file descriptor and will close it on garbage // collection if necessary. If that happens, a process warning will be // emitted (or a fatal exception will occur if the fd cannot be closed.) @@ -156,6 +170,16 @@ FileHandle::~FileHandle() { CHECK(closed_); // We have to be closed at the point } +int FileHandle::DoWrite(WriteWrap* w, + uv_buf_t* bufs, + size_t count, + uv_stream_t* send_handle) { + return UV_ENOSYS; // Not implemented (yet). +} + +void FileHandle::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("current_read", current_read_); +} // Close the file descriptor if it hasn't already been closed. A process // warning will be emitted using a SetImmediate to avoid calling back to @@ -225,12 +249,34 @@ FileHandle* FileHandle::CloseReq::file_handle() { return Unwrap(obj); } +FileHandle::CloseReq::CloseReq(Environment* env, + Local obj, + Local promise, + Local ref) + : ReqWrap(env, obj, AsyncWrap::PROVIDER_FILEHANDLECLOSEREQ) { + promise_.Reset(env->isolate(), promise); + ref_.Reset(env->isolate(), ref); +} + +FileHandle::CloseReq::~CloseReq() { + uv_fs_req_cleanup(req()); + promise_.Reset(); + ref_.Reset(); +} + +void FileHandle::CloseReq::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("promise", promise_); + tracker->TrackField("ref", ref_); +} + + + // Closes this FileHandle asynchronously and returns a Promise that will be // resolved when the callback is invoked, or rejects with a UVException if // there was a problem closing the fd. This is the preferred mechanism for // closing the FD object even tho the object will attempt to close // automatically on gc. -inline MaybeLocal FileHandle::ClosePromise() { +MaybeLocal FileHandle::ClosePromise() { Isolate* isolate = env()->isolate(); EscapableHandleScope scope(isolate); Local context = env()->context(); @@ -1157,13 +1203,13 @@ int MKDirpSync(uv_loop_t* loop, FSContinuationData continuation_data(req, mode, cb); continuation_data.PushPath(std::move(path)); - while (continuation_data.paths.size() > 0) { + while (continuation_data.paths().size() > 0) { std::string next_path = continuation_data.PopPath(); int err = uv_fs_mkdir(loop, req, next_path.c_str(), mode, nullptr); while (true) { switch (err) { case 0: - if (continuation_data.paths.size() == 0) { + if (continuation_data.paths().size() == 0) { return 0; } break; @@ -1173,7 +1219,7 @@ int MKDirpSync(uv_loop_t* loop, if (dirname != next_path) { continuation_data.PushPath(std::move(next_path)); continuation_data.PushPath(std::move(dirname)); - } else if (continuation_data.paths.size() == 0) { + } else if (continuation_data.paths().size() == 0) { err = UV_EEXIST; continue; } @@ -1188,7 +1234,7 @@ int MKDirpSync(uv_loop_t* loop, err = uv_fs_stat(loop, req, next_path.c_str(), nullptr); if (err == 0 && !S_ISDIR(req->statbuf.st_mode)) { uv_fs_req_cleanup(req); - if (orig_err == UV_EEXIST && continuation_data.paths.size() > 0) { + if (orig_err == UV_EEXIST && continuation_data.paths().size() > 0) { return UV_ENOTDIR; } return UV_EEXIST; @@ -1211,14 +1257,14 @@ int MKDirpAsync(uv_loop_t* loop, uv_fs_cb cb) { FSReqBase* req_wrap = FSReqBase::from_req(req); // on the first iteration of algorithm, stash state information. - if (req_wrap->continuation_data == nullptr) { - req_wrap->continuation_data = - std::make_unique(req, mode, cb); - req_wrap->continuation_data->PushPath(std::move(path)); + if (req_wrap->continuation_data() == nullptr) { + req_wrap->set_continuation_data( + std::make_unique(req, mode, cb)); + req_wrap->continuation_data()->PushPath(std::move(path)); } // on each iteration of algorithm, mkdir directory on top of stack. - std::string next_path = req_wrap->continuation_data->PopPath(); + std::string next_path = req_wrap->continuation_data()->PopPath(); int err = uv_fs_mkdir(loop, req, next_path.c_str(), mode, uv_fs_callback_t{[](uv_fs_t* req) { FSReqBase* req_wrap = FSReqBase::from_req(req); @@ -1230,12 +1276,12 @@ int MKDirpAsync(uv_loop_t* loop, while (true) { switch (err) { case 0: { - if (req_wrap->continuation_data->paths.size() == 0) { - req_wrap->continuation_data->Done(0); + if (req_wrap->continuation_data()->paths().size() == 0) { + req_wrap->continuation_data()->Done(0); } else { uv_fs_req_cleanup(req); MKDirpAsync(loop, req, path.c_str(), - req_wrap->continuation_data->mode, nullptr); + req_wrap->continuation_data()->mode(), nullptr); } break; } @@ -1243,19 +1289,19 @@ int MKDirpAsync(uv_loop_t* loop, std::string dirname = path.substr(0, path.find_last_of(kPathSeparator)); if (dirname != path) { - req_wrap->continuation_data->PushPath(std::move(path)); - req_wrap->continuation_data->PushPath(std::move(dirname)); - } else if (req_wrap->continuation_data->paths.size() == 0) { + req_wrap->continuation_data()->PushPath(std::move(path)); + req_wrap->continuation_data()->PushPath(std::move(dirname)); + } else if (req_wrap->continuation_data()->paths().size() == 0) { err = UV_EEXIST; continue; } uv_fs_req_cleanup(req); MKDirpAsync(loop, req, path.c_str(), - req_wrap->continuation_data->mode, nullptr); + req_wrap->continuation_data()->mode(), nullptr); break; } case UV_EPERM: { - req_wrap->continuation_data->Done(err); + req_wrap->continuation_data()->Done(err); break; } default: @@ -1267,14 +1313,14 @@ int MKDirpAsync(uv_loop_t* loop, FSReqBase* req_wrap = FSReqBase::from_req(req); int err = req->result; if (reinterpret_cast(req->data) == UV_EEXIST && - req_wrap->continuation_data->paths.size() > 0) { + req_wrap->continuation_data()->paths().size() > 0) { if (err == 0 && S_ISDIR(req->statbuf.st_mode)) { Environment* env = req_wrap->env(); uv_loop_t* loop = env->event_loop(); std::string path = req->path; uv_fs_req_cleanup(req); MKDirpAsync(loop, req, path.c_str(), - req_wrap->continuation_data->mode, nullptr); + req_wrap->continuation_data()->mode(), nullptr); return; } err = UV_ENOTDIR; @@ -1282,9 +1328,9 @@ int MKDirpAsync(uv_loop_t* loop, // verify that the path pointed to is actually a directory. if (err == 0 && !S_ISDIR(req->statbuf.st_mode)) err = UV_EEXIST; uv_fs_req_cleanup(req); - req_wrap->continuation_data->Done(err); + req_wrap->continuation_data()->Done(err); }}); - if (err < 0) req_wrap->continuation_data->Done(err); + if (err < 0) req_wrap->continuation_data()->Done(err); break; } break; diff --git a/src/node_file.h b/src/node_file.h index 84f4032cc2f6a3..1042baaf8f736b 100644 --- a/src/node_file.h +++ b/src/node_file.h @@ -6,113 +6,70 @@ #include "node.h" #include "aliased_buffer.h" #include "stream_base.h" -#include "memory_tracker-inl.h" -#include "req_wrap-inl.h" #include namespace node { - -using v8::Context; -using v8::FunctionCallbackInfo; -using v8::HandleScope; -using v8::Local; -using v8::MaybeLocal; -using v8::Object; -using v8::Promise; -using v8::Undefined; -using v8::Value; - namespace fs { // structure used to store state during a complex operation, e.g., mkdirp. class FSContinuationData : public MemoryRetainer { public: - FSContinuationData(uv_fs_t* req, int mode, uv_fs_cb done_cb) - : req(req), mode(mode), done_cb(done_cb) { - } - - uv_fs_t* req; - int mode; - std::vector paths{}; + inline FSContinuationData(uv_fs_t* req, int mode, uv_fs_cb done_cb); - void PushPath(std::string&& path) { - paths.emplace_back(std::move(path)); - } - - void PushPath(const std::string& path) { - paths.push_back(path); - } + inline void PushPath(std::string&& path); + inline void PushPath(const std::string& path); + inline std::string PopPath(); + inline void Done(int result); - std::string PopPath() { - CHECK_GT(paths.size(), 0); - std::string path = std::move(paths.back()); - paths.pop_back(); - return path; - } - - void Done(int result) { - req->result = result; - done_cb(req); - } - - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackField("paths", paths); - } + int mode() const { return mode_; } + const std::vector& paths() const { return paths_; } + void MemoryInfo(MemoryTracker* tracker) const override; SET_MEMORY_INFO_NAME(FSContinuationData) SET_SELF_SIZE(FSContinuationData) private: - uv_fs_cb done_cb; + uv_fs_cb done_cb_; + uv_fs_t* req_; + int mode_; + std::vector paths_; }; class FSReqBase : public ReqWrap { public: typedef MaybeStackBuffer FSReqBuffer; - std::unique_ptr continuation_data = nullptr; - - FSReqBase(Environment* env, Local req, AsyncWrap::ProviderType type, - bool use_bigint) - : ReqWrap(env, req, type), use_bigint_(use_bigint) { - } - void Init(const char* syscall, - const char* data, - size_t len, - enum encoding encoding) { - syscall_ = syscall; - encoding_ = encoding; - - if (data != nullptr) { - CHECK(!has_data_); - buffer_.AllocateSufficientStorage(len + 1); - buffer_.SetLengthAndZeroTerminate(len); - memcpy(*buffer_, data, len); - has_data_ = true; - } - } - - FSReqBuffer& Init(const char* syscall, size_t len, - enum encoding encoding) { - syscall_ = syscall; - encoding_ = encoding; - - buffer_.AllocateSufficientStorage(len + 1); - has_data_ = false; // so that the data does not show up in error messages - return buffer_; - } - - virtual void Reject(Local reject) = 0; - virtual void Resolve(Local value) = 0; + inline FSReqBase(Environment* env, + v8::Local req, + AsyncWrap::ProviderType type, + bool use_bigint); + ~FSReqBase() override; + + inline void Init(const char* syscall, + const char* data, + size_t len, + enum encoding encoding); + inline FSReqBuffer& Init(const char* syscall, size_t len, + enum encoding encoding); + + virtual void Reject(v8::Local reject) = 0; + virtual void Resolve(v8::Local value) = 0; virtual void ResolveStat(const uv_stat_t* stat) = 0; - virtual void SetReturnValue(const FunctionCallbackInfo& args) = 0; + virtual void SetReturnValue( + const v8::FunctionCallbackInfo& args) = 0; const char* syscall() const { return syscall_; } const char* data() const { return has_data_ ? *buffer_ : nullptr; } enum encoding encoding() const { return encoding_; } - bool use_bigint() const { return use_bigint_; } + FSContinuationData* continuation_data() const { + return continuation_data_.get(); + } + void set_continuation_data(std::unique_ptr data) { + continuation_data_ = std::move(data); + } + static FSReqBase* from_req(uv_fs_t* req) { return static_cast(ReqWrap::from_req(req)); } @@ -120,7 +77,10 @@ class FSReqBase : public ReqWrap { FSReqBase(const FSReqBase&) = delete; FSReqBase& operator=(const FSReqBase&) = delete; + void MemoryInfo(MemoryTracker* tracker) const override; + private: + std::unique_ptr continuation_data_; enum encoding encoding_ = UTF8; bool has_data_ = false; const char* syscall_ = nullptr; @@ -131,19 +91,16 @@ class FSReqBase : public ReqWrap { FSReqBuffer buffer_; }; -class FSReqCallback : public FSReqBase { +class FSReqCallback final : public FSReqBase { public: - FSReqCallback(Environment* env, Local req, bool use_bigint) - : FSReqBase(env, req, AsyncWrap::PROVIDER_FSREQCALLBACK, use_bigint) { } + inline FSReqCallback(Environment* env, + v8::Local req, + bool use_bigint); - void Reject(Local reject) override; - void Resolve(Local value) override; + void Reject(v8::Local reject) override; + void Resolve(v8::Local value) override; void ResolveStat(const uv_stat_t* stat) override; - void SetReturnValue(const FunctionCallbackInfo& args) override; - - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackField("continuation_data", continuation_data); - } + void SetReturnValue(const v8::FunctionCallbackInfo& args) override; SET_MEMORY_INFO_NAME(FSReqCallback) SET_SELF_SIZE(FSReqCallback) @@ -153,120 +110,27 @@ class FSReqCallback : public FSReqBase { }; template -constexpr void FillStatsArray(AliasedBufferBase* fields, - const uv_stat_t* s, - const size_t offset = 0) { -#define SET_FIELD_WITH_STAT(stat_offset, stat) \ - fields->SetValue(offset + static_cast(FsStatsOffset::stat_offset), \ - static_cast(stat)) - -#define SET_FIELD_WITH_TIME_STAT(stat_offset, stat) \ - /* NOLINTNEXTLINE(runtime/int) */ \ - SET_FIELD_WITH_STAT(stat_offset, static_cast(stat)) - - SET_FIELD_WITH_STAT(kDev, s->st_dev); - SET_FIELD_WITH_STAT(kMode, s->st_mode); - SET_FIELD_WITH_STAT(kNlink, s->st_nlink); - SET_FIELD_WITH_STAT(kUid, s->st_uid); - SET_FIELD_WITH_STAT(kGid, s->st_gid); - SET_FIELD_WITH_STAT(kRdev, s->st_rdev); - SET_FIELD_WITH_STAT(kBlkSize, s->st_blksize); - SET_FIELD_WITH_STAT(kIno, s->st_ino); - SET_FIELD_WITH_STAT(kSize, s->st_size); - SET_FIELD_WITH_STAT(kBlocks, s->st_blocks); - - SET_FIELD_WITH_TIME_STAT(kATimeSec, s->st_atim.tv_sec); - SET_FIELD_WITH_TIME_STAT(kATimeNsec, s->st_atim.tv_nsec); - SET_FIELD_WITH_TIME_STAT(kMTimeSec, s->st_mtim.tv_sec); - SET_FIELD_WITH_TIME_STAT(kMTimeNsec, s->st_mtim.tv_nsec); - SET_FIELD_WITH_TIME_STAT(kCTimeSec, s->st_ctim.tv_sec); - SET_FIELD_WITH_TIME_STAT(kCTimeNsec, s->st_ctim.tv_nsec); - SET_FIELD_WITH_TIME_STAT(kBirthTimeSec, s->st_birthtim.tv_sec); - SET_FIELD_WITH_TIME_STAT(kBirthTimeNsec, s->st_birthtim.tv_nsec); - -#undef SET_FIELD_WITH_TIME_STAT -#undef SET_FIELD_WITH_STAT -} - -inline Local FillGlobalStatsArray(Environment* env, - const bool use_bigint, - const uv_stat_t* s, - const bool second = false) { - const ptrdiff_t offset = - second ? static_cast(FsStatsOffset::kFsStatsFieldsNumber) : 0; - if (use_bigint) { - auto* const arr = env->fs_stats_field_bigint_array(); - FillStatsArray(arr, s, offset); - return arr->GetJSArray(); - } else { - auto* const arr = env->fs_stats_field_array(); - FillStatsArray(arr, s, offset); - return arr->GetJSArray(); - } -} +void FillStatsArray(AliasedBufferBase* fields, + const uv_stat_t* s, + const size_t offset = 0); + +inline v8::Local FillGlobalStatsArray(Environment* env, + const bool use_bigint, + const uv_stat_t* s, + const bool second = false); template -class FSReqPromise : public FSReqBase { +class FSReqPromise final : public FSReqBase { public: - static FSReqPromise* New(Environment* env, bool use_bigint) { - v8::Local obj; - if (!env->fsreqpromise_constructor_template() - ->NewInstance(env->context()) - .ToLocal(&obj)) { - return nullptr; - } - v8::Local resolver; - if (!v8::Promise::Resolver::New(env->context()).ToLocal(&resolver) || - obj->Set(env->context(), env->promise_string(), resolver).IsNothing()) { - return nullptr; - } - return new FSReqPromise(env, obj, use_bigint); - } - - ~FSReqPromise() override { - // Validate that the promise was explicitly resolved or rejected. - CHECK(finished_); - } - - void Reject(Local reject) override { - finished_ = true; - HandleScope scope(env()->isolate()); - InternalCallbackScope callback_scope(this); - Local value = - object()->Get(env()->context(), - env()->promise_string()).ToLocalChecked(); - Local resolver = value.As(); - USE(resolver->Reject(env()->context(), reject).FromJust()); - } - - void Resolve(Local value) override { - finished_ = true; - HandleScope scope(env()->isolate()); - InternalCallbackScope callback_scope(this); - Local val = - object()->Get(env()->context(), - env()->promise_string()).ToLocalChecked(); - Local resolver = val.As(); - USE(resolver->Resolve(env()->context(), value).FromJust()); - } - - void ResolveStat(const uv_stat_t* stat) override { - FillStatsArray(&stats_field_array_, stat); - Resolve(stats_field_array_.GetJSArray()); - } - - void SetReturnValue(const FunctionCallbackInfo& args) override { - Local val = - object()->Get(env()->context(), - env()->promise_string()).ToLocalChecked(); - Local resolver = val.As(); - args.GetReturnValue().Set(resolver->GetPromise()); - } + static inline FSReqPromise* New(Environment* env, bool use_bigint); + inline ~FSReqPromise() override; - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackField("stats_field_array", stats_field_array_); - tracker->TrackField("continuation_data", continuation_data); - } + inline void Reject(v8::Local reject) override; + inline void Resolve(v8::Local value) override; + inline void ResolveStat(const uv_stat_t* stat) override; + inline void SetReturnValue( + const v8::FunctionCallbackInfo& args) override; + inline void MemoryInfo(MemoryTracker* tracker) const override; SET_MEMORY_INFO_NAME(FSReqPromise) SET_SELF_SIZE(FSReqPromise) @@ -277,17 +141,15 @@ class FSReqPromise : public FSReqBase { FSReqPromise& operator=(const FSReqPromise&&) = delete; private: - FSReqPromise(Environment* env, v8::Local obj, bool use_bigint) - : FSReqBase(env, obj, AsyncWrap::PROVIDER_FSREQPROMISE, use_bigint), - stats_field_array_( - env->isolate(), - static_cast(FsStatsOffset::kFsStatsFieldsNumber)) {} + inline FSReqPromise(Environment* env, + v8::Local obj, + bool use_bigint); bool finished_ = false; AliasedBufferT stats_field_array_; }; -class FSReqAfterScope { +class FSReqAfterScope final { public: FSReqAfterScope(FSReqBase* wrap, uv_fs_t* req); ~FSReqAfterScope(); @@ -304,17 +166,18 @@ class FSReqAfterScope { private: FSReqBase* wrap_ = nullptr; uv_fs_t* req_ = nullptr; - HandleScope handle_scope_; - Context::Scope context_scope_; + v8::HandleScope handle_scope_; + v8::Context::Scope context_scope_; }; class FileHandle; // A request wrap specifically for uv_fs_read()s scheduled for reading // from a FileHandle. -class FileHandleReadWrap : public ReqWrap { +class FileHandleReadWrap final : public ReqWrap { public: FileHandleReadWrap(FileHandle* handle, v8::Local obj); + ~FileHandleReadWrap() override; static inline FileHandleReadWrap* from_req(uv_fs_t* req) { return static_cast(ReqWrap::from_req(req)); @@ -333,7 +196,7 @@ class FileHandleReadWrap : public ReqWrap { // A wrapper for a file descriptor that will automatically close the fd when // the object is garbage collected -class FileHandle : public AsyncWrap, public StreamBase { +class FileHandle final : public AsyncWrap, public StreamBase { public: static FileHandle* New(Environment* env, int fd, @@ -346,10 +209,10 @@ class FileHandle : public AsyncWrap, public StreamBase { // Will asynchronously close the FD and return a Promise that will // be resolved once closing is complete. - static void Close(const FunctionCallbackInfo& args); + static void Close(const v8::FunctionCallbackInfo& args); // Releases ownership of the FD. - static void ReleaseFD(const FunctionCallbackInfo& args); + static void ReleaseFD(const v8::FunctionCallbackInfo& args); // StreamBase interface: int ReadStart() override; @@ -366,13 +229,9 @@ class FileHandle : public AsyncWrap, public StreamBase { int DoWrite(WriteWrap* w, uv_buf_t* bufs, size_t count, - uv_stream_t* send_handle) override { - return UV_ENOSYS; // Not implemented (yet). - } + uv_stream_t* send_handle) override; - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackField("current_read", current_read_); - } + void MemoryInfo(MemoryTracker* tracker) const override; SET_MEMORY_INFO_NAME(FileHandle) SET_SELF_SIZE(FileHandle) @@ -389,36 +248,24 @@ class FileHandle : public AsyncWrap, public StreamBase { void Close(); void AfterClose(); - class CloseReq : public ReqWrap { + class CloseReq final : public ReqWrap { public: CloseReq(Environment* env, - Local obj, - Local promise, - Local ref) - : ReqWrap(env, obj, AsyncWrap::PROVIDER_FILEHANDLECLOSEREQ) { - promise_.Reset(env->isolate(), promise); - ref_.Reset(env->isolate(), ref); - } - - ~CloseReq() override { - uv_fs_req_cleanup(req()); - promise_.Reset(); - ref_.Reset(); - } + v8::Local obj, + v8::Local promise, + v8::Local ref); + ~CloseReq() override; FileHandle* file_handle(); - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackField("promise", promise_); - tracker->TrackField("ref", ref_); - } + void MemoryInfo(MemoryTracker* tracker) const override; SET_MEMORY_INFO_NAME(CloseReq) SET_SELF_SIZE(CloseReq) void Resolve(); - void Reject(Local reason); + void Reject(v8::Local reason); static CloseReq* from_req(uv_fs_t* req) { return static_cast(ReqWrap::from_req(req)); @@ -430,12 +277,12 @@ class FileHandle : public AsyncWrap, public StreamBase { CloseReq& operator=(const CloseReq&&) = delete; private: - v8::Global promise_{}; - v8::Global ref_{}; + v8::Global promise_{}; + v8::Global ref_{}; }; // Asynchronous close - inline MaybeLocal ClosePromise(); + v8::MaybeLocal ClosePromise(); int fd_; bool closing_ = false; @@ -467,53 +314,23 @@ class FSReqWrapSync { // that nullptr indicates a synchronous call, rather than a failure. // Failure conditions should be disambiguated and handled appropriately. inline FSReqBase* GetReqWrap(Environment* env, v8::Local value, - bool use_bigint = false) { - if (value->IsObject()) { - return Unwrap(value.As()); - } else if (value->StrictEquals(env->fs_use_promises_symbol())) { - if (use_bigint) { - return FSReqPromise::New(env, use_bigint); - } else { - return FSReqPromise::New(env, use_bigint); - } - } - return nullptr; -} + bool use_bigint = false); // Returns nullptr if the operation fails from the start. template inline FSReqBase* AsyncDestCall(Environment* env, FSReqBase* req_wrap, - const v8::FunctionCallbackInfo& args, + const v8::FunctionCallbackInfo& args, const char* syscall, const char* dest, size_t len, enum encoding enc, uv_fs_cb after, - Func fn, Args... fn_args) { - CHECK_NOT_NULL(req_wrap); - req_wrap->Init(syscall, dest, len, enc); - int err = req_wrap->Dispatch(fn, fn_args..., after); - if (err < 0) { - uv_fs_t* uv_req = req_wrap->req(); - uv_req->result = err; - uv_req->path = nullptr; - after(uv_req); // after may delete req_wrap if there is an error - req_wrap = nullptr; - } else { - req_wrap->SetReturnValue(args); - } - - return req_wrap; -} + Func fn, Args... fn_args); // Returns nullptr if the operation fails from the start. template inline FSReqBase* AsyncCall(Environment* env, FSReqBase* req_wrap, - const v8::FunctionCallbackInfo& args, + const v8::FunctionCallbackInfo& args, const char* syscall, enum encoding enc, - uv_fs_cb after, Func fn, Args... fn_args) { - return AsyncDestCall(env, req_wrap, args, - syscall, nullptr, 0, enc, - after, fn, fn_args...); -} + uv_fs_cb after, Func fn, Args... fn_args); // Template counterpart of SYNC_CALL, except that it only puts // the error number and the syscall in the context instead of @@ -522,22 +339,7 @@ inline FSReqBase* AsyncCall(Environment* env, template inline int SyncCall(Environment* env, v8::Local ctx, FSReqWrapSync* req_wrap, const char* syscall, - Func fn, Args... args) { - env->PrintSyncTrace(); - int err = fn(env->event_loop(), &(req_wrap->req), args..., nullptr); - if (err < 0) { - v8::Local context = env->context(); - v8::Local ctx_obj = ctx.As(); - v8::Isolate* isolate = env->isolate(); - ctx_obj->Set(context, - env->errno_string(), - v8::Integer::New(isolate, err)).Check(); - ctx_obj->Set(context, - env->syscall_string(), - OneByteString(isolate, syscall)).Check(); - } - return err; -} + Func fn, Args... args); } // namespace fs diff --git a/src/node_http2.cc b/src/node_http2.cc index 7170907ced1b3e..9421c36f3be561 100644 --- a/src/node_http2.cc +++ b/src/node_http2.cc @@ -647,7 +647,9 @@ Http2Session::Http2Session(Environment* env, { // Make the js_fields_ property accessible to JS land. Local ab = - ArrayBuffer::New(env->isolate(), js_fields_, kSessionUint8FieldCount); + ArrayBuffer::New(env->isolate(), + reinterpret_cast(&js_fields_), + kSessionUint8FieldCount); Local uint8_arr = Uint8Array::New(ab, 0, kSessionUint8FieldCount); USE(wrap->Set(env->context(), env->fields_string(), uint8_arr)); @@ -918,7 +920,8 @@ int Http2Session::OnBeginHeadersCallback(nghttp2_session* handle, if (UNLIKELY(!session->CanAddStream() || Http2Stream::New(session, id, frame->headers.cat) == nullptr)) { - if (session->rejected_stream_count_++ > 100) + if (session->rejected_stream_count_++ > + session->js_fields_.max_rejected_streams) return NGHTTP2_ERR_CALLBACK_FAILURE; // Too many concurrent streams being opened nghttp2_submit_rst_stream(**session, NGHTTP2_FLAG_NONE, id, @@ -1009,8 +1012,12 @@ int Http2Session::OnInvalidFrame(nghttp2_session* handle, void* user_data) { Http2Session* session = static_cast(user_data); - Debug(session, "invalid frame received, code: %d", lib_error_code); - if (session->invalid_frame_count_++ > 1000) + Debug(session, + "invalid frame received (%u/%u), code: %d", + session->invalid_frame_count_, + session->js_fields_.max_invalid_frames, + lib_error_code); + if (session->invalid_frame_count_++ > session->js_fields_.max_invalid_frames) return 1; // If the error is fatal or if error code is ERR_STREAM_CLOSED... emit error @@ -1046,7 +1053,7 @@ int Http2Session::OnFrameNotSent(nghttp2_session* handle, if (error_code == NGHTTP2_ERR_SESSION_CLOSING || error_code == NGHTTP2_ERR_STREAM_CLOSED || error_code == NGHTTP2_ERR_STREAM_CLOSING || - session->js_fields_[kSessionFrameErrorListenerCount] == 0) { + session->js_fields_.frame_error_listener_count == 0) { return 0; } @@ -1349,7 +1356,7 @@ void Http2Session::HandleHeadersFrame(const nghttp2_frame* frame) { // are considered advisory only, so this has no real effect other than to // simply let user code know that the priority has changed. void Http2Session::HandlePriorityFrame(const nghttp2_frame* frame) { - if (js_fields_[kSessionPriorityListenerCount] == 0) return; + if (js_fields_.priority_listener_count == 0) return; Isolate* isolate = env()->isolate(); HandleScope scope(isolate); Local context = env()->context(); @@ -1418,7 +1425,7 @@ void Http2Session::HandleGoawayFrame(const nghttp2_frame* frame) { // Called by OnFrameReceived when a complete ALTSVC frame has been received. void Http2Session::HandleAltSvcFrame(const nghttp2_frame* frame) { - if (!(js_fields_[kBitfield] & (1 << kSessionHasAltsvcListeners))) return; + if (!(js_fields_.bitfield & (1 << kSessionHasAltsvcListeners))) return; Isolate* isolate = env()->isolate(); HandleScope scope(isolate); Local context = env()->context(); @@ -1497,7 +1504,7 @@ void Http2Session::HandlePingFrame(const nghttp2_frame* frame) { return; } - if (!(js_fields_[kBitfield] & (1 << kSessionHasPingListeners))) return; + if (!(js_fields_.bitfield & (1 << kSessionHasPingListeners))) return; // Notify the session that a ping occurred arg = Buffer::Copy(env(), reinterpret_cast(frame->ping.opaque_data), @@ -1509,8 +1516,8 @@ void Http2Session::HandlePingFrame(const nghttp2_frame* frame) { void Http2Session::HandleSettingsFrame(const nghttp2_frame* frame) { bool ack = frame->hd.flags & NGHTTP2_FLAG_ACK; if (!ack) { - js_fields_[kBitfield] &= ~(1 << kSessionRemoteSettingsIsUpToDate); - if (!(js_fields_[kBitfield] & (1 << kSessionHasRemoteSettingsListeners))) + js_fields_.bitfield &= ~(1 << kSessionRemoteSettingsIsUpToDate); + if (!(js_fields_.bitfield & (1 << kSessionHasRemoteSettingsListeners))) return; // This is not a SETTINGS acknowledgement, notify and return MakeCallback(env()->http2session_on_settings_function(), 0, nullptr); @@ -1872,7 +1879,10 @@ void Http2Session::OnStreamRead(ssize_t nread, const uv_buf_t& buf_) { nread = buf.size(); stream_buf_offset_ = 0; stream_buf_ab_.Reset(); - DecrementCurrentSessionMemory(stream_buf_offset_); + + // We have now fully processed the stream_buf_ input chunk (by moving the + // remaining part into buf, which will be accounted for below). + DecrementCurrentSessionMemory(stream_buf_.len); } // Shrink to the actual amount of used data. @@ -3055,6 +3065,8 @@ void Initialize(Local target, NODE_DEFINE_CONSTANT(target, kBitfield); NODE_DEFINE_CONSTANT(target, kSessionPriorityListenerCount); NODE_DEFINE_CONSTANT(target, kSessionFrameErrorListenerCount); + NODE_DEFINE_CONSTANT(target, kSessionMaxInvalidFrames); + NODE_DEFINE_CONSTANT(target, kSessionMaxRejectedStreams); NODE_DEFINE_CONSTANT(target, kSessionUint8FieldCount); NODE_DEFINE_CONSTANT(target, kSessionHasRemoteSettingsListeners); diff --git a/src/node_http2.h b/src/node_http2.h index 1444738470f9c7..e828999f2b73b2 100644 --- a/src/node_http2.h +++ b/src/node_http2.h @@ -673,15 +673,27 @@ class Http2Stream::Provider::Stream : public Http2Stream::Provider { void* user_data); }; +typedef struct { + uint8_t bitfield; + uint8_t priority_listener_count; + uint8_t frame_error_listener_count; + uint32_t max_invalid_frames = 1000; + uint32_t max_rejected_streams = 100; +} SessionJSFields; + // Indices for js_fields_, which serves as a way to communicate data with JS // land fast. In particular, we store information about the number/presence // of certain event listeners in JS, and skip calls from C++ into JS if they // are missing. enum SessionUint8Fields { - kBitfield, // See below - kSessionPriorityListenerCount, - kSessionFrameErrorListenerCount, - kSessionUint8FieldCount + kBitfield = offsetof(SessionJSFields, bitfield), // See below + kSessionPriorityListenerCount = + offsetof(SessionJSFields, priority_listener_count), + kSessionFrameErrorListenerCount = + offsetof(SessionJSFields, frame_error_listener_count), + kSessionMaxInvalidFrames = offsetof(SessionJSFields, max_invalid_frames), + kSessionMaxRejectedStreams = offsetof(SessionJSFields, max_rejected_streams), + kSessionUint8FieldCount = sizeof(SessionJSFields) }; enum SessionBitfieldFlags { @@ -968,7 +980,7 @@ class Http2Session : public AsyncWrap, public StreamListener { nghttp2_session* session_; // JS-accessible numeric fields, as indexed by SessionUint8Fields. - uint8_t js_fields_[kSessionUint8FieldCount] = {}; + SessionJSFields js_fields_ = {}; // The session type: client or server nghttp2_session_type session_type_; @@ -1014,9 +1026,9 @@ class Http2Session : public AsyncWrap, public StreamListener { // limit will result in the session being destroyed, as an indication of a // misbehaving peer. This counter is reset once new streams are being // accepted again. - int32_t rejected_stream_count_ = 0; + uint32_t rejected_stream_count_ = 0; // Also use the invalid frame count as a measure for rejecting input frames. - int32_t invalid_frame_count_ = 0; + uint32_t invalid_frame_count_ = 0; void PushOutgoingBuffer(nghttp2_stream_write&& write); void CopyDataIntoOutgoing(const uint8_t* src, size_t src_length); diff --git a/src/node_http_parser.cc b/src/node_http_parser.cc index c6136702c7cb43..0328dc7c0f684a 100644 --- a/src/node_http_parser.cc +++ b/src/node_http_parser.cc @@ -62,6 +62,7 @@ using v8::Int32; using v8::Integer; using v8::Local; using v8::MaybeLocal; +using v8::Number; using v8::Object; using v8::String; using v8::Uint32; @@ -486,8 +487,17 @@ class Parser : public AsyncWrap, public StreamListener { static void Initialize(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); + uint64_t max_http_header_size = 0; + CHECK(args[0]->IsInt32()); CHECK(args[1]->IsObject()); + if (args.Length() > 2) { + CHECK(args[2]->IsNumber()); + max_http_header_size = args[2].As()->Value(); + } + if (max_http_header_size == 0) { + max_http_header_size = env->options()->max_http_header_size; + } llhttp_type_t type = static_cast(args[0].As()->Value()); @@ -505,7 +515,7 @@ class Parser : public AsyncWrap, public StreamListener { parser->set_provider_type(provider); parser->AsyncReset(args[1].As()); - parser->Init(type); + parser->Init(type, max_http_header_size); } template @@ -752,7 +762,7 @@ class Parser : public AsyncWrap, public StreamListener { } - void Init(llhttp_type_t type) { + void Init(llhttp_type_t type, uint64_t max_http_header_size) { llhttp_init(&parser_, type, &settings); header_nread_ = 0; url_.Reset(); @@ -761,12 +771,13 @@ class Parser : public AsyncWrap, public StreamListener { num_values_ = 0; have_flushed_ = false; got_exception_ = false; + max_http_header_size_ = max_http_header_size; } int TrackHeader(size_t len) { header_nread_ += len; - if (header_nread_ >= per_process::cli_options->max_http_header_size) { + if (header_nread_ >= max_http_header_size_) { llhttp_set_error_reason(&parser_, "HPE_HEADER_OVERFLOW:Header overflow"); return HPE_USER; } @@ -801,6 +812,7 @@ class Parser : public AsyncWrap, public StreamListener { unsigned int execute_depth_ = 0; bool pending_pause_ = false; uint64_t header_nread_ = 0; + uint64_t max_http_header_size_; // These are helper functions for filling `http_parser_settings`, which turn // a member function of Parser into a C-style HTTP parser callback. diff --git a/src/node_options.cc b/src/node_options.cc index 0bc6730156ce12..498bedd1e5e892 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -347,6 +347,10 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() { &EnvironmentOptions::experimental_report, kAllowedInEnvironment); #endif // NODE_REPORT + AddOption("--experimental-wasi-unstable-preview0", + "experimental WASI support", + &EnvironmentOptions::experimental_wasi, + kAllowedInEnvironment); AddOption("--expose-internals", "", &EnvironmentOptions::expose_internals); AddOption("--frozen-intrinsics", "experimental frozen intrinsics support", @@ -436,6 +440,10 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() { "profile generated with --heap-prof. (default: 512 * 1024)", &EnvironmentOptions::heap_prof_interval); #endif // HAVE_INSPECTOR + AddOption("--max-http-header-size", + "set the maximum size of HTTP headers (default: 8192 (8KB))", + &EnvironmentOptions::max_http_header_size, + kAllowedInEnvironment); AddOption("--redirect-warnings", "write warnings to file instead of stderr", &EnvironmentOptions::redirect_warnings, @@ -628,10 +636,6 @@ PerProcessOptionsParser::PerProcessOptionsParser( kAllowedInEnvironment); AddAlias("--trace-events-enabled", { "--trace-event-categories", "v8,node,node.async_hooks" }); - AddOption("--max-http-header-size", - "set the maximum size of HTTP headers (default: 8KB)", - &PerProcessOptions::max_http_header_size, - kAllowedInEnvironment); AddOption("--v8-pool-size", "set V8's thread pool size", &PerProcessOptions::v8_thread_pool_size, diff --git a/src/node_options.h b/src/node_options.h index ce0cee5fe56784..fea912da446568 100644 --- a/src/node_options.h +++ b/src/node_options.h @@ -115,6 +115,7 @@ class EnvironmentOptions : public Options { bool expose_internals = false; bool frozen_intrinsics = false; std::string heap_snapshot_signal; + uint64_t max_http_header_size = 8 * 1024; bool no_deprecation = false; bool no_force_async_hooks_checks = false; bool no_warnings = false; @@ -151,6 +152,7 @@ class EnvironmentOptions : public Options { #ifdef NODE_REPORT bool experimental_report = false; #endif // NODE_REPORT + bool experimental_wasi = false; std::string eval_string; bool print_eval = false; bool force_repl = false; @@ -200,7 +202,6 @@ class PerProcessOptions : public Options { std::string title; std::string trace_event_categories; std::string trace_event_file_pattern = "node_trace.${rotation}.log"; - uint64_t max_http_header_size = 8 * 1024; int64_t v8_thread_pool_size = 4; bool zero_fill_all_buffers = false; bool debug_arraybuffer_allocations = false; diff --git a/src/node_process_events.cc b/src/node_process_events.cc index 440e67d412322b..06096226625bb6 100644 --- a/src/node_process_events.cc +++ b/src/node_process_events.cc @@ -34,6 +34,8 @@ Maybe ProcessEmitWarningGeneric(Environment* env, const char* warning, const char* type, const char* code) { + if (!env->can_call_into_js()) return Just(false); + HandleScope handle_scope(env->isolate()); Context::Scope context_scope(env->context()); diff --git a/src/node_stat_watcher.cc b/src/node_stat_watcher.cc index ae30825cbbdbd2..0d67eceed54931 100644 --- a/src/node_stat_watcher.cc +++ b/src/node_stat_watcher.cc @@ -22,8 +22,8 @@ #include "memory_tracker-inl.h" #include "node_stat_watcher.h" #include "async_wrap-inl.h" -#include "env.h" -#include "node_file.h" +#include "env-inl.h" +#include "node_file-inl.h" #include "util-inl.h" #include diff --git a/src/node_version.h b/src/node_version.h index ed3787b8f78319..3193890e0c8443 100644 --- a/src/node_version.h +++ b/src/node_version.h @@ -23,13 +23,13 @@ #define SRC_NODE_VERSION_H_ #define NODE_MAJOR_VERSION 13 -#define NODE_MINOR_VERSION 2 -#define NODE_PATCH_VERSION 1 +#define NODE_MINOR_VERSION 3 +#define NODE_PATCH_VERSION 0 #define NODE_VERSION_IS_LTS 0 #define NODE_VERSION_LTS_CODENAME "" -#define NODE_VERSION_IS_RELEASE 0 +#define NODE_VERSION_IS_RELEASE 1 #ifndef NODE_STRINGIFY #define NODE_STRINGIFY(n) NODE_STRINGIFY_HELPER(n) diff --git a/src/node_wasi.cc b/src/node_wasi.cc new file mode 100644 index 00000000000000..8de7f1fc1cae68 --- /dev/null +++ b/src/node_wasi.cc @@ -0,0 +1,1802 @@ +#include "env-inl.h" +#include "base_object-inl.h" +#include "debug_utils.h" +#include "util-inl.h" +#include "node.h" +#include "uv.h" +#include "uvwasi.h" +#include "node_wasi.h" + +namespace node { +namespace wasi { + +static inline bool is_access_oob(size_t mem_size, + uint32_t offset, + uint32_t buf_size) { + return offset + buf_size > mem_size; +} + +template +inline void Debug(WASI* wasi, Args&&... args) { + Debug(wasi->env(), DebugCategory::WASI, std::forward(args)...); +} + +#define RETURN_IF_BAD_ARG_COUNT(args, expected) \ + do { \ + if ((args).Length() != (expected)) { \ + (args).GetReturnValue().Set(UVWASI_EINVAL); \ + return; \ + } \ + } while (0) + +#define CHECK_TO_TYPE_OR_RETURN(args, input, type, result) \ + do { \ + if (!(input)->Is##type()) { \ + (args).GetReturnValue().Set(UVWASI_EINVAL); \ + return; \ + } \ + (result) = (input).As()->Value(); \ + } while (0) + +#define UNWRAP_BIGINT_OR_RETURN(args, input, type, result) \ + do { \ + if (!(input)->IsBigInt()) { \ + (args).GetReturnValue().Set(UVWASI_EINVAL); \ + return; \ + } \ + Local js_value = (input).As(); \ + bool lossless; \ + (result) = js_value->type ## Value(&lossless); \ + } while (0) + +#define GET_BACKING_STORE_OR_RETURN(wasi, args, mem_ptr, mem_size) \ + do { \ + uvwasi_errno_t err = (wasi)->backingStore((mem_ptr), (mem_size)); \ + if (err != UVWASI_ESUCCESS) { \ + (args).GetReturnValue().Set(err); \ + return; \ + } \ + } while (0) + +#define CHECK_BOUNDS_OR_RETURN(args, mem_size, offset, buf_size) \ + do { \ + if (is_access_oob((mem_size), (offset), (buf_size))) { \ + (args).GetReturnValue().Set(UVWASI_EOVERFLOW); \ + return; \ + } \ + } while (0) + + +using v8::Array; +using v8::ArrayBuffer; +using v8::BackingStore; +using v8::BigInt; +using v8::Context; +using v8::FunctionCallbackInfo; +using v8::FunctionTemplate; +using v8::Local; +using v8::Object; +using v8::String; +using v8::Uint32; +using v8::Value; + + +WASI::WASI(Environment* env, + Local object, + uvwasi_options_t* options) : BaseObject(env, object) { + MakeWeak(); + CHECK_EQ(uvwasi_init(&uvw_, options), UVWASI_ESUCCESS); +} + + +WASI::~WASI() { + uvwasi_destroy(&uvw_); +} + + +void WASI::New(const FunctionCallbackInfo& args) { + CHECK(args.IsConstructCall()); + CHECK_EQ(args.Length(), 3); + CHECK(args[0]->IsArray()); + CHECK(args[1]->IsArray()); + CHECK(args[2]->IsArray()); + + Environment* env = Environment::GetCurrent(args); + Local context = env->context(); + Local argv = args[0].As(); + const uint32_t argc = argv->Length(); + uvwasi_options_t options; + + options.fd_table_size = 3; + options.argc = argc; + options.argv = argc == 0 ? nullptr : new char*[argc]; + + for (uint32_t i = 0; i < argc; i++) { + auto arg = argv->Get(context, i).ToLocalChecked(); + CHECK(arg->IsString()); + node::Utf8Value str(env->isolate(), arg); + options.argv[i] = strdup(*str); + CHECK_NOT_NULL(options.argv[i]); + } + + Local env_pairs = args[1].As(); + const uint32_t envc = env_pairs->Length(); + options.envp = new char*[envc + 1]; + for (uint32_t i = 0; i < envc; i++) { + auto pair = env_pairs->Get(context, i).ToLocalChecked(); + CHECK(pair->IsString()); + node::Utf8Value str(env->isolate(), pair); + options.envp[i] = strdup(*str); + CHECK_NOT_NULL(options.envp[i]); + } + options.envp[envc] = nullptr; + + Local preopens = args[2].As(); + CHECK_EQ(preopens->Length() % 2, 0); + options.preopenc = preopens->Length() / 2; + options.preopens = UncheckedCalloc(options.preopenc); + int index = 0; + for (uint32_t i = 0; i < preopens->Length(); i += 2) { + auto mapped = preopens->Get(context, i).ToLocalChecked(); + auto real = preopens->Get(context, i + 1).ToLocalChecked(); + CHECK(mapped->IsString()); + CHECK(real->IsString()); + node::Utf8Value mapped_path(env->isolate(), mapped); + node::Utf8Value real_path(env->isolate(), real); + options.preopens[index].mapped_path = strdup(*mapped_path); + options.preopens[index].real_path = strdup(*real_path); + index++; + } + + new WASI(env, args.This(), &options); + + if (options.argv != nullptr) { + for (uint32_t i = 0; i < argc; i++) + free(options.argv[i]); + delete[] options.argv; + } + + if (options.envp != nullptr) { + for (uint32_t i = 0; options.envp[i]; i++) + free(options.envp[i]); + delete[] options.envp; + } +} + + +void WASI::ArgsGet(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t argv_offset; + uint32_t argv_buf_offset; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, argv_offset); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, argv_buf_offset); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "args_get(%d, %d)\n", argv_offset, argv_buf_offset); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, + mem_size, + argv_buf_offset, + wasi->uvw_.argv_buf_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, argv_offset, wasi->uvw_.argc * 4); + std::vector argv(wasi->uvw_.argc); + char* argv_buf = &memory[argv_buf_offset]; + uvwasi_errno_t err = uvwasi_args_get(&wasi->uvw_, argv.data(), argv_buf); + + if (err == UVWASI_ESUCCESS) { + for (size_t i = 0; i < wasi->uvw_.argc; i++) { + uint32_t offset = argv_buf_offset + (argv[i] - argv[0]); + wasi->writeUInt32(memory, offset, argv_offset + (i * 4)); + } + } + + args.GetReturnValue().Set(err); +} + + +void WASI::ArgsSizesGet(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t argc_offset; + uint32_t argv_buf_offset; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, argc_offset); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, argv_buf_offset); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "args_sizes_get(%d, %d)\n", argc_offset, argv_buf_offset); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, argc_offset, 4); + CHECK_BOUNDS_OR_RETURN(args, mem_size, argv_buf_offset, 4); + size_t argc; + size_t argv_buf_size; + uvwasi_errno_t err = uvwasi_args_sizes_get(&wasi->uvw_, + &argc, + &argv_buf_size); + if (err == UVWASI_ESUCCESS) { + wasi->writeUInt32(memory, argc, argc_offset); + wasi->writeUInt32(memory, argv_buf_size, argv_buf_offset); + } + + args.GetReturnValue().Set(err); +} + + +void WASI::ClockResGet(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t clock_id; + uint32_t resolution_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, clock_id); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, resolution_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "clock_res_get(%d, %d)\n", clock_id, resolution_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, resolution_ptr, 8); + uvwasi_timestamp_t resolution; + uvwasi_errno_t err = uvwasi_clock_res_get(&wasi->uvw_, + clock_id, + &resolution); + if (err == UVWASI_ESUCCESS) + wasi->writeUInt64(memory, resolution, resolution_ptr); + + args.GetReturnValue().Set(err); +} + + +void WASI::ClockTimeGet(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t clock_id; + uint64_t precision; + uint32_t time_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 3); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, clock_id); + UNWRAP_BIGINT_OR_RETURN(args, args[1], Uint64, precision); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, time_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "clock_time_get(%d, %d, %d)\n", clock_id, precision, time_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, time_ptr, 8); + uvwasi_timestamp_t time; + uvwasi_errno_t err = uvwasi_clock_time_get(&wasi->uvw_, + clock_id, + precision, + &time); + if (err == UVWASI_ESUCCESS) + wasi->writeUInt64(memory, time, time_ptr); + + args.GetReturnValue().Set(err); +} + + +void WASI::EnvironGet(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t environ_offset; + uint32_t environ_buf_offset; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, environ_offset); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, environ_buf_offset); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "environ_get(%d, %d)\n", environ_offset, environ_buf_offset); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, + mem_size, + environ_buf_offset, + wasi->uvw_.env_buf_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, environ_offset, wasi->uvw_.envc * 4); + std::vector environment(wasi->uvw_.envc); + char* environ_buf = &memory[environ_buf_offset]; + uvwasi_errno_t err = uvwasi_environ_get(&wasi->uvw_, + environment.data(), + environ_buf); + + if (err == UVWASI_ESUCCESS) { + for (size_t i = 0; i < wasi->uvw_.envc; i++) { + uint32_t offset = environ_buf_offset + (environment[i] - environment[0]); + wasi->writeUInt32(memory, offset, environ_offset + (i * 4)); + } + } + + args.GetReturnValue().Set(err); +} + + +void WASI::EnvironSizesGet(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t envc_offset; + uint32_t env_buf_offset; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, envc_offset); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, env_buf_offset); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "environ_sizes_get(%d, %d)\n", envc_offset, env_buf_offset); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, envc_offset, 4); + CHECK_BOUNDS_OR_RETURN(args, mem_size, env_buf_offset, 4); + size_t envc; + size_t env_buf_size; + uvwasi_errno_t err = uvwasi_environ_sizes_get(&wasi->uvw_, + &envc, + &env_buf_size); + if (err == UVWASI_ESUCCESS) { + wasi->writeUInt32(memory, envc, envc_offset); + wasi->writeUInt32(memory, env_buf_size, env_buf_offset); + } + + args.GetReturnValue().Set(err); +} + + +void WASI::FdAdvise(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint64_t offset; + uint64_t len; + uint8_t advice; + RETURN_IF_BAD_ARG_COUNT(args, 4); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + UNWRAP_BIGINT_OR_RETURN(args, args[1], Uint64, offset); + UNWRAP_BIGINT_OR_RETURN(args, args[2], Uint64, len); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, advice); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_advise(%d, %d, %d, %d)\n", fd, offset, len, advice); + uvwasi_errno_t err = uvwasi_fd_advise(&wasi->uvw_, fd, offset, len, advice); + args.GetReturnValue().Set(err); +} + + +void WASI::FdAllocate(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint64_t offset; + uint64_t len; + RETURN_IF_BAD_ARG_COUNT(args, 3); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + UNWRAP_BIGINT_OR_RETURN(args, args[1], Uint64, offset); + UNWRAP_BIGINT_OR_RETURN(args, args[2], Uint64, len); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_allocate(%d, %d, %d)\n", fd, offset, len); + uvwasi_errno_t err = uvwasi_fd_allocate(&wasi->uvw_, fd, offset, len); + args.GetReturnValue().Set(err); +} + + +void WASI::FdClose(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + RETURN_IF_BAD_ARG_COUNT(args, 1); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_close(%d)\n", fd); + uvwasi_errno_t err = uvwasi_fd_close(&wasi->uvw_, fd); + args.GetReturnValue().Set(err); +} + + +void WASI::FdDatasync(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + RETURN_IF_BAD_ARG_COUNT(args, 1); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_datasync(%d)\n", fd); + uvwasi_errno_t err = uvwasi_fd_datasync(&wasi->uvw_, fd); + args.GetReturnValue().Set(err); +} + + +void WASI::FdFdstatGet(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t buf; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, buf); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_fdstat_get(%d, %d)\n", fd, buf); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, buf, 24); + uvwasi_fdstat_t stats; + uvwasi_errno_t err = uvwasi_fd_fdstat_get(&wasi->uvw_, fd, &stats); + + if (err == UVWASI_ESUCCESS) { + wasi->writeUInt8(memory, stats.fs_filetype, buf); + wasi->writeUInt16(memory, stats.fs_flags, buf + 2); + wasi->writeUInt64(memory, stats.fs_rights_base, buf + 8); + wasi->writeUInt64(memory, stats.fs_rights_inheriting, buf + 16); + } + + args.GetReturnValue().Set(err); +} + + +void WASI::FdFdstatSetFlags(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint16_t flags; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, flags); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_fdstat_set_flags(%d, %d)\n", fd, flags); + uvwasi_errno_t err = uvwasi_fd_fdstat_set_flags(&wasi->uvw_, fd, flags); + args.GetReturnValue().Set(err); +} + + +void WASI::FdFdstatSetRights(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint64_t fs_rights_base; + uint64_t fs_rights_inheriting; + RETURN_IF_BAD_ARG_COUNT(args, 3); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + UNWRAP_BIGINT_OR_RETURN(args, args[1], Uint64, fs_rights_base); + UNWRAP_BIGINT_OR_RETURN(args, args[2], Uint64, fs_rights_inheriting); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "fd_fdstat_set_rights(%d, %d, %d)\n", + fd, + fs_rights_base, + fs_rights_inheriting); + uvwasi_errno_t err = uvwasi_fd_fdstat_set_rights(&wasi->uvw_, + fd, + fs_rights_base, + fs_rights_inheriting); + args.GetReturnValue().Set(err); +} + + +void WASI::FdFilestatGet(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t buf; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, buf); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_filestat_get(%d, %d)\n", fd, buf); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, buf, 56); + uvwasi_filestat_t stats; + uvwasi_errno_t err = uvwasi_fd_filestat_get(&wasi->uvw_, fd, &stats); + + if (err == UVWASI_ESUCCESS) { + wasi->writeUInt64(memory, stats.st_dev, buf); + wasi->writeUInt64(memory, stats.st_ino, buf + 8); + wasi->writeUInt8(memory, stats.st_filetype, buf + 16); + wasi->writeUInt32(memory, stats.st_nlink, buf + 20); + wasi->writeUInt64(memory, stats.st_size, buf + 24); + wasi->writeUInt64(memory, stats.st_atim, buf + 32); + wasi->writeUInt64(memory, stats.st_mtim, buf + 40); + wasi->writeUInt64(memory, stats.st_ctim, buf + 48); + } + + args.GetReturnValue().Set(err); +} + + +void WASI::FdFilestatSetSize(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint64_t st_size; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + UNWRAP_BIGINT_OR_RETURN(args, args[1], Uint64, st_size); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_filestat_set_size(%d, %d)\n", fd, st_size); + uvwasi_errno_t err = uvwasi_fd_filestat_set_size(&wasi->uvw_, fd, st_size); + args.GetReturnValue().Set(err); +} + + +void WASI::FdFilestatSetTimes(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint64_t st_atim; + uint64_t st_mtim; + uint16_t fst_flags; + RETURN_IF_BAD_ARG_COUNT(args, 4); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + UNWRAP_BIGINT_OR_RETURN(args, args[1], Uint64, st_atim); + UNWRAP_BIGINT_OR_RETURN(args, args[2], Uint64, st_mtim); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, fst_flags); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "fd_filestat_set_times(%d, %d, %d, %d)\n", + fd, + st_atim, + st_mtim, + fst_flags); + uvwasi_errno_t err = uvwasi_fd_filestat_set_times(&wasi->uvw_, + fd, + st_atim, + st_mtim, + fst_flags); + args.GetReturnValue().Set(err); +} + + +void WASI::FdPread(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t iovs_ptr; + uint32_t iovs_len; + uint64_t offset; + uint32_t nread_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 5); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, iovs_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, iovs_len); + UNWRAP_BIGINT_OR_RETURN(args, args[3], Uint64, offset); + CHECK_TO_TYPE_OR_RETURN(args, args[4], Uint32, nread_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "uvwasi_fd_pread(%d, %d, %d, %d, %d)\n", + fd, + iovs_ptr, + iovs_len, + offset, + nread_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, iovs_ptr, iovs_len * 8); + CHECK_BOUNDS_OR_RETURN(args, mem_size, nread_ptr, 4); + uvwasi_iovec_t* iovs = UncheckedCalloc(iovs_len); + + if (iovs == nullptr) { + args.GetReturnValue().Set(UVWASI_ENOMEM); + return; + } + + for (uint32_t i = 0; i < iovs_len; ++i) { + uint32_t buf_ptr; + uint32_t buf_len; + + wasi->readUInt32(memory, &buf_ptr, iovs_ptr); + wasi->readUInt32(memory, &buf_len, iovs_ptr + 4); + + if (is_access_oob(mem_size, buf_ptr, buf_len)) { + free(iovs); + args.GetReturnValue().Set(UVWASI_EOVERFLOW); + return; + } + + iovs_ptr += 8; + iovs[i].buf = static_cast(&memory[buf_ptr]); + iovs[i].buf_len = buf_len; + } + + size_t nread; + uvwasi_errno_t err = uvwasi_fd_pread(&wasi->uvw_, + fd, + iovs, + iovs_len, + offset, + &nread); + if (err == UVWASI_ESUCCESS) + wasi->writeUInt32(memory, nread, nread_ptr); + + free(iovs); + args.GetReturnValue().Set(err); +} + + +void WASI::FdPrestatGet(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t buf; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, buf); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_prestat_get(%d, %d)\n", fd, buf); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, buf, 8); + uvwasi_prestat_t prestat; + uvwasi_errno_t err = uvwasi_fd_prestat_get(&wasi->uvw_, fd, &prestat); + + if (err == UVWASI_ESUCCESS) { + wasi->writeUInt32(memory, prestat.pr_type, buf); + wasi->writeUInt32(memory, prestat.u.dir.pr_name_len, buf + 4); + } + + args.GetReturnValue().Set(err); +} + + +void WASI::FdPrestatDirName(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t path_ptr; + uint32_t path_len; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 3); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, path_len); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_prestat_dir_name(%d, %d, %d)\n", fd, path_ptr, path_len); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, path_ptr, path_len); + uvwasi_errno_t err = uvwasi_fd_prestat_dir_name(&wasi->uvw_, + fd, + &memory[path_ptr], + path_len); + args.GetReturnValue().Set(err); +} + + +void WASI::FdPwrite(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t iovs_ptr; + uint32_t iovs_len; + uint64_t offset; + uint32_t nwritten_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 5); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, iovs_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, iovs_len); + UNWRAP_BIGINT_OR_RETURN(args, args[3], Uint64, offset); + CHECK_TO_TYPE_OR_RETURN(args, args[4], Uint32, nwritten_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "uvwasi_fd_pwrite(%d, %d, %d, %d, %d)\n", + fd, + iovs_ptr, + iovs_len, + offset, + nwritten_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, iovs_ptr, iovs_len * 8); + CHECK_BOUNDS_OR_RETURN(args, mem_size, nwritten_ptr, 4); + uvwasi_ciovec_t* iovs = UncheckedCalloc(iovs_len); + + if (iovs == nullptr) { + args.GetReturnValue().Set(UVWASI_ENOMEM); + return; + } + + for (uint32_t i = 0; i < iovs_len; ++i) { + uint32_t buf_ptr; + uint32_t buf_len; + + wasi->readUInt32(memory, &buf_ptr, iovs_ptr); + wasi->readUInt32(memory, &buf_len, iovs_ptr + 4); + + if (is_access_oob(mem_size, buf_ptr, buf_len)) { + free(iovs); + args.GetReturnValue().Set(UVWASI_EOVERFLOW); + return; + } + + iovs_ptr += 8; + iovs[i].buf = static_cast(&memory[buf_ptr]); + iovs[i].buf_len = buf_len; + } + + size_t nwritten; + uvwasi_errno_t err = uvwasi_fd_pwrite(&wasi->uvw_, + fd, + iovs, + iovs_len, + offset, + &nwritten); + if (err == UVWASI_ESUCCESS) + wasi->writeUInt32(memory, nwritten, nwritten_ptr); + + free(iovs); + args.GetReturnValue().Set(err); +} + + +void WASI::FdRead(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t iovs_ptr; + uint32_t iovs_len; + uint32_t nread_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 4); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, iovs_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, iovs_len); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, nread_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_read(%d, %d, %d, %d)\n", fd, iovs_ptr, iovs_len, nread_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, iovs_ptr, iovs_len * 8); + CHECK_BOUNDS_OR_RETURN(args, mem_size, nread_ptr, 4); + uvwasi_iovec_t* iovs = UncheckedCalloc(iovs_len); + + if (iovs == nullptr) { + args.GetReturnValue().Set(UVWASI_ENOMEM); + return; + } + + for (uint32_t i = 0; i < iovs_len; ++i) { + uint32_t buf_ptr; + uint32_t buf_len; + + wasi->readUInt32(memory, &buf_ptr, iovs_ptr); + wasi->readUInt32(memory, &buf_len, iovs_ptr + 4); + + if (is_access_oob(mem_size, buf_ptr, buf_len)) { + free(iovs); + args.GetReturnValue().Set(UVWASI_EOVERFLOW); + return; + } + + iovs_ptr += 8; + iovs[i].buf = static_cast(&memory[buf_ptr]); + iovs[i].buf_len = buf_len; + } + + size_t nread; + uvwasi_errno_t err = uvwasi_fd_read(&wasi->uvw_, + fd, + iovs, + iovs_len, + &nread); + if (err == UVWASI_ESUCCESS) + wasi->writeUInt32(memory, nread, nread_ptr); + + free(iovs); + args.GetReturnValue().Set(err); +} + + +void WASI::FdReaddir(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t buf_ptr; + uint32_t buf_len; + uint64_t cookie; + uint32_t bufused_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 5); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, buf_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, buf_len); + UNWRAP_BIGINT_OR_RETURN(args, args[3], Uint64, cookie); + CHECK_TO_TYPE_OR_RETURN(args, args[4], Uint32, bufused_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "uvwasi_fd_readdir(%d, %d, %d, %d, %d)\n", + fd, + buf_ptr, + buf_len, + cookie, + bufused_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, buf_ptr, buf_len); + CHECK_BOUNDS_OR_RETURN(args, mem_size, bufused_ptr, 4); + size_t bufused; + uvwasi_errno_t err = uvwasi_fd_readdir(&wasi->uvw_, + fd, + &memory[buf_ptr], + buf_len, + cookie, + &bufused); + if (err == UVWASI_ESUCCESS) + wasi->writeUInt32(memory, bufused, bufused_ptr); + + args.GetReturnValue().Set(err); +} + + +void WASI::FdRenumber(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t from; + uint32_t to; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, from); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, to); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_renumber(%d, %d)\n", from, to); + uvwasi_errno_t err = uvwasi_fd_renumber(&wasi->uvw_, from, to); + args.GetReturnValue().Set(err); +} + + +void WASI::FdSeek(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + int64_t offset; + uint8_t whence; + uint32_t newoffset_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 4); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + UNWRAP_BIGINT_OR_RETURN(args, args[1], Int64, offset); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, whence); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, newoffset_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_seek(%d, %d, %d, %d)\n", fd, offset, whence, newoffset_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, newoffset_ptr, 8); + uvwasi_filesize_t newoffset; + uvwasi_errno_t err = uvwasi_fd_seek(&wasi->uvw_, + fd, + offset, + whence, + &newoffset); + if (err == UVWASI_ESUCCESS) + wasi->writeUInt64(memory, newoffset, newoffset_ptr); + + args.GetReturnValue().Set(err); +} + + +void WASI::FdSync(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + RETURN_IF_BAD_ARG_COUNT(args, 1); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_sync(%d)\n", fd); + uvwasi_errno_t err = uvwasi_fd_sync(&wasi->uvw_, fd); + args.GetReturnValue().Set(err); +} + + +void WASI::FdTell(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t offset_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, offset_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_tell(%d, %d)\n", fd, offset_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, offset_ptr, 8); + uvwasi_filesize_t offset; + uvwasi_errno_t err = uvwasi_fd_tell(&wasi->uvw_, fd, &offset); + + if (err == UVWASI_ESUCCESS) + wasi->writeUInt64(memory, offset, offset_ptr); + + args.GetReturnValue().Set(err); +} + + +void WASI::FdWrite(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t iovs_ptr; + uint32_t iovs_len; + uint32_t nwritten_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 4); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, iovs_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, iovs_len); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, nwritten_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "fd_write(%d, %d, %d, %d)\n", + fd, + iovs_ptr, + iovs_len, + nwritten_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, iovs_ptr, iovs_len * 8); + CHECK_BOUNDS_OR_RETURN(args, mem_size, nwritten_ptr, 4); + uvwasi_ciovec_t* iovs = UncheckedCalloc(iovs_len); + + if (iovs == nullptr) { + args.GetReturnValue().Set(UVWASI_ENOMEM); + return; + } + + for (uint32_t i = 0; i < iovs_len; ++i) { + uint32_t buf_ptr; + uint32_t buf_len; + + wasi->readUInt32(memory, &buf_ptr, iovs_ptr); + wasi->readUInt32(memory, &buf_len, iovs_ptr + 4); + + if (is_access_oob(mem_size, buf_ptr, buf_len)) { + free(iovs); + args.GetReturnValue().Set(UVWASI_EOVERFLOW); + return; + } + + iovs_ptr += 8; + iovs[i].buf = static_cast(&memory[buf_ptr]); + iovs[i].buf_len = buf_len; + } + + size_t nwritten; + uvwasi_errno_t err = uvwasi_fd_write(&wasi->uvw_, + fd, + iovs, + iovs_len, + &nwritten); + if (err == UVWASI_ESUCCESS) + wasi->writeUInt32(memory, nwritten, nwritten_ptr); + + free(iovs); + args.GetReturnValue().Set(err); +} + + +void WASI::PathCreateDirectory(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t path_ptr; + uint32_t path_len; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 3); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, path_len); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "path_create_directory(%d, %d, %d)\n", fd, path_ptr, path_len); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, path_ptr, path_len); + uvwasi_errno_t err = uvwasi_path_create_directory(&wasi->uvw_, + fd, + &memory[path_ptr], + path_len); + args.GetReturnValue().Set(err); +} + + +void WASI::PathFilestatGet(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t flags; + uint32_t path_ptr; + uint32_t path_len; + uint32_t buf_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 5); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, flags); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, path_len); + CHECK_TO_TYPE_OR_RETURN(args, args[4], Uint32, buf_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "path_filestat_get(%d, %d, %d, %d, %d)\n", + fd, + path_ptr, + path_len); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, path_ptr, path_len); + CHECK_BOUNDS_OR_RETURN(args, mem_size, buf_ptr, 56); + uvwasi_filestat_t stats; + uvwasi_errno_t err = uvwasi_path_filestat_get(&wasi->uvw_, + fd, + flags, + &memory[path_ptr], + path_len, + &stats); + if (err == UVWASI_ESUCCESS) { + wasi->writeUInt64(memory, stats.st_dev, buf_ptr); + wasi->writeUInt64(memory, stats.st_ino, buf_ptr + 8); + wasi->writeUInt8(memory, stats.st_filetype, buf_ptr + 16); + wasi->writeUInt32(memory, stats.st_nlink, buf_ptr + 20); + wasi->writeUInt64(memory, stats.st_size, buf_ptr + 24); + wasi->writeUInt64(memory, stats.st_atim, buf_ptr + 32); + wasi->writeUInt64(memory, stats.st_mtim, buf_ptr + 40); + wasi->writeUInt64(memory, stats.st_ctim, buf_ptr + 48); + } + + args.GetReturnValue().Set(err); +} + + +void WASI::PathFilestatSetTimes(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t flags; + uint32_t path_ptr; + uint32_t path_len; + uint64_t st_atim; + uint64_t st_mtim; + uint16_t fst_flags; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 7); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, flags); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, path_len); + UNWRAP_BIGINT_OR_RETURN(args, args[4], Uint64, st_atim); + UNWRAP_BIGINT_OR_RETURN(args, args[5], Uint64, st_mtim); + CHECK_TO_TYPE_OR_RETURN(args, args[6], Uint32, fst_flags); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "path_filestat_set_times(%d, %d, %d, %d, %d, %d, %d)\n", + fd, + flags, + path_ptr, + path_len, + st_atim, + st_mtim, + fst_flags); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, path_ptr, path_len); + uvwasi_errno_t err = uvwasi_path_filestat_set_times(&wasi->uvw_, + fd, + flags, + &memory[path_ptr], + path_len, + st_atim, + st_mtim, + fst_flags); + args.GetReturnValue().Set(err); +} + + +void WASI::PathLink(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t old_fd; + uint32_t old_flags; + uint32_t old_path_ptr; + uint32_t old_path_len; + uint32_t new_fd; + uint32_t new_path_ptr; + uint32_t new_path_len; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 7); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, old_fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, old_flags); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, old_path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, old_path_len); + CHECK_TO_TYPE_OR_RETURN(args, args[4], Uint32, new_fd); + CHECK_TO_TYPE_OR_RETURN(args, args[5], Uint32, new_path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[6], Uint32, new_path_len); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "path_link(%d, %d, %d, %d, %d, %d, %d)\n", + old_fd, + old_flags, + old_path_ptr, + old_path_len, + new_fd, + new_path_ptr, + new_path_len); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, old_path_ptr, old_path_len); + CHECK_BOUNDS_OR_RETURN(args, mem_size, new_path_ptr, new_path_len); + uvwasi_errno_t err = uvwasi_path_link(&wasi->uvw_, + old_fd, + old_flags, + &memory[old_path_ptr], + old_path_len, + new_fd, + &memory[new_path_ptr], + new_path_len); + args.GetReturnValue().Set(err); +} + + +void WASI::PathOpen(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t dirfd; + uint32_t dirflags; + uint32_t path_ptr; + uint32_t path_len; + uint32_t o_flags; + uint64_t fs_rights_base; + uint64_t fs_rights_inheriting; + uint32_t fs_flags; + uint32_t fd_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 9); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, dirfd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, dirflags); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, path_len); + CHECK_TO_TYPE_OR_RETURN(args, args[4], Uint32, o_flags); + UNWRAP_BIGINT_OR_RETURN(args, args[5], Uint64, fs_rights_base); + UNWRAP_BIGINT_OR_RETURN(args, args[6], Uint64, fs_rights_inheriting); + CHECK_TO_TYPE_OR_RETURN(args, args[7], Uint32, fs_flags); + CHECK_TO_TYPE_OR_RETURN(args, args[8], Uint32, fd_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "path_open(%d, %d, %d, %d, %d, %d, %d, %d, %d)\n", + dirfd, + dirflags, + path_ptr, + path_len, + o_flags, + fs_rights_base, + fs_rights_inheriting, + fs_flags, + fd_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, path_ptr, path_len); + CHECK_BOUNDS_OR_RETURN(args, mem_size, fd_ptr, 4); + uvwasi_fd_t fd; + uvwasi_errno_t err = uvwasi_path_open(&wasi->uvw_, + dirfd, + dirflags, + &memory[path_ptr], + path_len, + static_cast(o_flags), + fs_rights_base, + fs_rights_inheriting, + static_cast(fs_flags), + &fd); + if (err == UVWASI_ESUCCESS) + wasi->writeUInt32(memory, fd, fd_ptr); + + args.GetReturnValue().Set(err); +} + + +void WASI::PathReadlink(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t path_ptr; + uint32_t path_len; + uint32_t buf_ptr; + uint32_t buf_len; + uint32_t bufused_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 6); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, path_len); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, buf_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[4], Uint32, buf_len); + CHECK_TO_TYPE_OR_RETURN(args, args[5], Uint32, bufused_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "path_readlink(%d, %d, %d, %d, %d, %d)\n", + fd, + path_ptr, + path_len, + buf_ptr, + buf_len, + bufused_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, path_ptr, path_len); + CHECK_BOUNDS_OR_RETURN(args, mem_size, buf_ptr, buf_len); + CHECK_BOUNDS_OR_RETURN(args, mem_size, bufused_ptr, 4); + size_t bufused; + uvwasi_errno_t err = uvwasi_path_readlink(&wasi->uvw_, + fd, + &memory[path_ptr], + path_len, + &memory[buf_ptr], + buf_len, + &bufused); + if (err == UVWASI_ESUCCESS) + wasi->writeUInt32(memory, bufused, bufused_ptr); + + args.GetReturnValue().Set(err); +} + + +void WASI::PathRemoveDirectory(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t path_ptr; + uint32_t path_len; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 3); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, path_len); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "path_remove_directory(%d, %d, %d)\n", fd, path_ptr, path_len); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, path_ptr, path_len); + uvwasi_errno_t err = uvwasi_path_remove_directory(&wasi->uvw_, + fd, + &memory[path_ptr], + path_len); + args.GetReturnValue().Set(err); +} + + +void WASI::PathRename(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t old_fd; + uint32_t old_path_ptr; + uint32_t old_path_len; + uint32_t new_fd; + uint32_t new_path_ptr; + uint32_t new_path_len; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 6); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, old_fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, old_path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, old_path_len); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, new_fd); + CHECK_TO_TYPE_OR_RETURN(args, args[4], Uint32, new_path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[5], Uint32, new_path_len); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "path_rename(%d, %d, %d, %d, %d, %d)\n", + old_fd, + old_path_ptr, + old_path_len, + new_fd, + new_path_ptr, + new_path_len); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, old_path_ptr, old_path_len); + CHECK_BOUNDS_OR_RETURN(args, mem_size, new_path_ptr, new_path_len); + uvwasi_errno_t err = uvwasi_path_rename(&wasi->uvw_, + old_fd, + &memory[old_path_ptr], + old_path_len, + new_fd, + &memory[new_path_ptr], + new_path_len); + args.GetReturnValue().Set(err); +} + + +void WASI::PathSymlink(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t old_path_ptr; + uint32_t old_path_len; + uint32_t fd; + uint32_t new_path_ptr; + uint32_t new_path_len; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 5); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, old_path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, old_path_len); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, new_path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[4], Uint32, new_path_len); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "path_symlink(%d, %d, %d, %d, %d)\n", + old_path_ptr, + old_path_len, + fd, + new_path_ptr, + new_path_len); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, old_path_ptr, old_path_len); + CHECK_BOUNDS_OR_RETURN(args, mem_size, new_path_ptr, new_path_len); + uvwasi_errno_t err = uvwasi_path_symlink(&wasi->uvw_, + &memory[old_path_ptr], + old_path_len, + fd, + &memory[new_path_ptr], + new_path_len); + args.GetReturnValue().Set(err); +} + + +void WASI::PathUnlinkFile(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t path_ptr; + uint32_t path_len; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 3); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, path_len); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "path_unlink_file(%d, %d, %d)\n", fd, path_ptr, path_len); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, path_ptr, path_len); + uvwasi_errno_t err = uvwasi_path_unlink_file(&wasi->uvw_, + fd, + &memory[path_ptr], + path_len); + args.GetReturnValue().Set(err); +} + + +void WASI::PollOneoff(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t in_ptr; + uint32_t out_ptr; + uint32_t nsubscriptions; + uint32_t nevents_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 4); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, in_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, out_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, nsubscriptions); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, nevents_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "poll_oneoff(%d, %d, %d, %d)\n", + in_ptr, + out_ptr, + nsubscriptions, + nevents_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, in_ptr, nsubscriptions * 56); + CHECK_BOUNDS_OR_RETURN(args, mem_size, out_ptr, nsubscriptions * 32); + CHECK_BOUNDS_OR_RETURN(args, mem_size, nevents_ptr, 4); + uvwasi_subscription_t* in = + UncheckedCalloc(nsubscriptions); + + if (in == nullptr) { + args.GetReturnValue().Set(UVWASI_ENOMEM); + return; + } + + uvwasi_event_t* out = UncheckedCalloc(nsubscriptions); + + if (out == nullptr) { + free(in); + args.GetReturnValue().Set(UVWASI_ENOMEM); + return; + } + + for (uint32_t i = 0; i < nsubscriptions; ++i) { + uvwasi_subscription_t sub = in[i]; + wasi->readUInt64(memory, &sub.userdata, in_ptr); + wasi->readUInt8(memory, &sub.type, in_ptr + 8); + + if (sub.type == UVWASI_EVENTTYPE_CLOCK) { + wasi->readUInt64(memory, &sub.u.clock.identifier, in_ptr + 16); + wasi->readUInt32(memory, &sub.u.clock.clock_id, in_ptr + 24); + wasi->readUInt64(memory, &sub.u.clock.timeout, in_ptr + 32); + wasi->readUInt64(memory, &sub.u.clock.precision, in_ptr + 40); + wasi->readUInt16(memory, &sub.u.clock.flags, in_ptr + 48); + } else if (sub.type == UVWASI_EVENTTYPE_FD_READ || + sub.type == UVWASI_EVENTTYPE_FD_WRITE) { + wasi->readUInt32(memory, &sub.u.fd_readwrite.fd, in_ptr + 16); + } + + in_ptr += 56; + } + + size_t nevents; + uvwasi_errno_t err = uvwasi_poll_oneoff(&wasi->uvw_, + in, + out, + nsubscriptions, + &nevents); + if (err == UVWASI_ESUCCESS) { + wasi->writeUInt32(memory, nevents, nevents_ptr); + + for (uint32_t i = 0; i < nsubscriptions; ++i) { + uvwasi_event_t event = out[i]; + + wasi->writeUInt64(memory, event.userdata, out_ptr); + wasi->writeUInt16(memory, event.error, out_ptr + 8); + wasi->writeUInt8(memory, event.type, out_ptr + 10); + + if (event.type == UVWASI_EVENTTYPE_FD_READ || + event.type == UVWASI_EVENTTYPE_FD_WRITE) { + wasi->writeUInt64(memory, event.u.fd_readwrite.nbytes, out_ptr + 16); + wasi->writeUInt16(memory, event.u.fd_readwrite.flags, out_ptr + 24); + } + + out_ptr += 32; + } + } + + free(in); + free(out); + args.GetReturnValue().Set(err); +} + + +void WASI::ProcExit(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t code; + RETURN_IF_BAD_ARG_COUNT(args, 1); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, code); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "proc_exit(%d)\n", code); + args.GetReturnValue().Set(uvwasi_proc_exit(&wasi->uvw_, code)); +} + + +void WASI::ProcRaise(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t sig; + RETURN_IF_BAD_ARG_COUNT(args, 1); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, sig); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "proc_raise(%d)\n", sig); + uvwasi_errno_t err = uvwasi_proc_raise(&wasi->uvw_, sig); + args.GetReturnValue().Set(err); +} + + +void WASI::RandomGet(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t buf_ptr; + uint32_t buf_len; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, buf_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, buf_len); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "random_get(%d, %d)\n", buf_ptr, buf_len); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, buf_ptr, buf_len); + uvwasi_errno_t err = uvwasi_random_get(&wasi->uvw_, + &memory[buf_ptr], + buf_len); + args.GetReturnValue().Set(err); +} + + +void WASI::SchedYield(const FunctionCallbackInfo& args) { + WASI* wasi; + RETURN_IF_BAD_ARG_COUNT(args, 0); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "sched_yield()\n"); + uvwasi_errno_t err = uvwasi_sched_yield(&wasi->uvw_); + args.GetReturnValue().Set(err); +} + + +void WASI::SockRecv(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t sock; + uint32_t ri_data_ptr; + uint32_t ri_data_len; + uint16_t ri_flags; + uint32_t ro_datalen_ptr; + uint16_t ro_flags_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 6); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, sock); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, ri_data_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, ri_data_len); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, ri_flags); + CHECK_TO_TYPE_OR_RETURN(args, args[4], Uint32, ro_datalen_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[5], Uint32, ro_flags_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "sock_recv(%d, %d, %d, %d, %d, %d)\n", + sock, + ri_data_ptr, + ri_data_len, + ri_flags, + ro_datalen_ptr, + ro_flags_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, ri_data_ptr, ri_data_len * 8); + CHECK_BOUNDS_OR_RETURN(args, mem_size, ro_datalen_ptr, 4); + CHECK_BOUNDS_OR_RETURN(args, mem_size, ro_flags_ptr, 4); + uvwasi_iovec_t* ri_data = UncheckedCalloc(ri_data_len); + + if (ri_data == nullptr) { + args.GetReturnValue().Set(UVWASI_ENOMEM); + return; + } + + for (uint32_t i = 0; i < ri_data_len; ++i) { + uint32_t buf_ptr; + uint32_t buf_len; + + wasi->readUInt32(memory, &buf_ptr, ri_data_ptr); + wasi->readUInt32(memory, &buf_len, ri_data_ptr + 4); + + if (is_access_oob(mem_size, buf_ptr, buf_len)) { + free(ri_data); + args.GetReturnValue().Set(UVWASI_EOVERFLOW); + return; + } + + ri_data_ptr += 8; + ri_data[i].buf = static_cast(&memory[buf_ptr]); + ri_data[i].buf_len = buf_len; + } + + size_t ro_datalen; + uvwasi_roflags_t ro_flags; + uvwasi_errno_t err = uvwasi_sock_recv(&wasi->uvw_, + sock, + ri_data, + ri_data_len, + ri_flags, + &ro_datalen, + &ro_flags); + if (err == UVWASI_ESUCCESS) { + wasi->writeUInt32(memory, ro_datalen, ro_datalen_ptr); + wasi->writeUInt32(memory, ro_flags, ro_flags_ptr); + } + + free(ri_data); + args.GetReturnValue().Set(err); +} + + +void WASI::SockSend(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t sock; + uint32_t si_data_ptr; + uint32_t si_data_len; + uint16_t si_flags; + uint32_t so_datalen_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 5); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, sock); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, si_data_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, si_data_len); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, si_flags); + CHECK_TO_TYPE_OR_RETURN(args, args[4], Uint32, so_datalen_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "sock_send(%d, %d, %d, %d, %d)\n", + sock, + si_data_ptr, + si_data_len, + si_flags, + so_datalen_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, si_data_ptr, si_data_len * 8); + CHECK_BOUNDS_OR_RETURN(args, mem_size, so_datalen_ptr, 4); + uvwasi_ciovec_t* si_data = UncheckedCalloc(si_data_len); + + if (si_data == nullptr) { + args.GetReturnValue().Set(UVWASI_ENOMEM); + return; + } + + for (uint32_t i = 0; i < si_data_len; ++i) { + uint32_t buf_ptr; + uint32_t buf_len; + + wasi->readUInt32(memory, &buf_ptr, si_data_ptr); + wasi->readUInt32(memory, &buf_len, si_data_ptr + 4); + + if (is_access_oob(mem_size, buf_ptr, buf_len)) { + free(si_data); + args.GetReturnValue().Set(UVWASI_EOVERFLOW); + return; + } + + si_data_ptr += 8; + si_data[i].buf = static_cast(&memory[buf_ptr]); + si_data[i].buf_len = buf_len; + } + + size_t so_datalen; + uvwasi_errno_t err = uvwasi_sock_send(&wasi->uvw_, + sock, + si_data, + si_data_len, + si_flags, + &so_datalen); + if (err == UVWASI_ESUCCESS) + wasi->writeUInt32(memory, so_datalen, so_datalen_ptr); + + free(si_data); + args.GetReturnValue().Set(err); +} + + +void WASI::SockShutdown(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t sock; + uint8_t how; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, sock); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, how); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "sock_shutdown(%d, %d)\n", sock, how); + uvwasi_errno_t err = uvwasi_sock_shutdown(&wasi->uvw_, sock, how); + args.GetReturnValue().Set(err); +} + + +void WASI::_SetMemory(const FunctionCallbackInfo& args) { + WASI* wasi; + CHECK_EQ(args.Length(), 1); + CHECK(args[0]->IsObject()); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + wasi->memory_.Reset(wasi->env()->isolate(), args[0].As()); +} + + +void WASI::readUInt8(char* memory, uint8_t* value, uint32_t offset) { + CHECK_NOT_NULL(memory); + CHECK_NOT_NULL(value); + *value = memory[offset] & 0xFF; +} + + +void WASI::readUInt16(char* memory, uint16_t* value, uint32_t offset) { + CHECK_NOT_NULL(memory); + CHECK_NOT_NULL(value); + *value = (memory[offset] & 0xFF) | + ((memory[offset + 1] & 0xFF) << 8); +} + + +void WASI::readUInt32(char* memory, uint32_t* value, uint32_t offset) { + CHECK_NOT_NULL(memory); + CHECK_NOT_NULL(value); + *value = (memory[offset] & 0xFF) | + ((memory[offset + 1] & 0xFF) << 8) | + ((memory[offset + 2] & 0xFF) << 16) | + ((memory[offset + 3] & 0xFF) << 24); +} + + +void WASI::readUInt64(char* memory, uint64_t* value, uint32_t offset) { + CHECK_NOT_NULL(memory); + CHECK_NOT_NULL(value); + uint64_t low = (memory[offset] & 0xFF) | + ((memory[offset + 1] & 0xFF) << 8) | + ((memory[offset + 2] & 0xFF) << 16) | + ((memory[offset + 3] & 0xFF) << 24); + uint64_t high = (memory[offset + 4] & 0xFF) | + ((memory[offset + 5] & 0xFF) << 8) | + ((memory[offset + 6] & 0xFF) << 16) | + ((memory[offset + 7] & 0xFF) << 24); + *value = (high << 32) + low; +} + + +void WASI::writeUInt8(char* memory, uint8_t value, uint32_t offset) { + CHECK_NOT_NULL(memory); + memory[offset] = value & 0xFF; +} + + +void WASI::writeUInt16(char* memory, uint16_t value, uint32_t offset) { + CHECK_NOT_NULL(memory); + memory[offset++] = value & 0xFF; + memory[offset] = (value >> 8) & 0xFF; +} + + +void WASI::writeUInt32(char* memory, uint32_t value, uint32_t offset) { + CHECK_NOT_NULL(memory); + memory[offset++] = value & 0xFF; + memory[offset++] = (value >> 8) & 0xFF; + memory[offset++] = (value >> 16) & 0xFF; + memory[offset] = (value >> 24) & 0xFF; +} + + +void WASI::writeUInt64(char* memory, uint64_t value, uint32_t offset) { + CHECK_NOT_NULL(memory); + memory[offset++] = value & 0xFF; + memory[offset++] = (value >> 8) & 0xFF; + memory[offset++] = (value >> 16) & 0xFF; + memory[offset++] = (value >> 24) & 0xFF; + memory[offset++] = (value >> 32) & 0xFF; + memory[offset++] = (value >> 40) & 0xFF; + memory[offset++] = (value >> 48) & 0xFF; + memory[offset] = (value >> 56) & 0xFF; +} + + +uvwasi_errno_t WASI::backingStore(char** store, size_t* byte_length) { + Environment* env = this->env(); + Local memory = PersistentToLocal::Strong(this->memory_); + Local prop; + + if (!memory->Get(env->context(), env->buffer_string()).ToLocal(&prop)) + return UVWASI_EINVAL; + + if (!prop->IsArrayBuffer()) + return UVWASI_EINVAL; + + Local ab = prop.As(); + std::shared_ptr backing_store = ab->GetBackingStore(); + *byte_length = backing_store->ByteLength(); + *store = static_cast(backing_store->Data()); + return UVWASI_ESUCCESS; +} + + +static void Initialize(Local target, + Local unused, + Local context, + void* priv) { + Environment* env = Environment::GetCurrent(context); + + Local tmpl = env->NewFunctionTemplate(WASI::New); + auto wasi_wrap_string = FIXED_ONE_BYTE_STRING(env->isolate(), "WASI"); + tmpl->InstanceTemplate()->SetInternalFieldCount(1); + tmpl->SetClassName(wasi_wrap_string); + + env->SetProtoMethod(tmpl, "args_get", WASI::ArgsGet); + env->SetProtoMethod(tmpl, "args_sizes_get", WASI::ArgsSizesGet); + env->SetProtoMethod(tmpl, "clock_res_get", WASI::ClockResGet); + env->SetProtoMethod(tmpl, "clock_time_get", WASI::ClockTimeGet); + env->SetProtoMethod(tmpl, "environ_get", WASI::EnvironGet); + env->SetProtoMethod(tmpl, "environ_sizes_get", WASI::EnvironSizesGet); + env->SetProtoMethod(tmpl, "fd_advise", WASI::FdAdvise); + env->SetProtoMethod(tmpl, "fd_allocate", WASI::FdAllocate); + env->SetProtoMethod(tmpl, "fd_close", WASI::FdClose); + env->SetProtoMethod(tmpl, "fd_datasync", WASI::FdDatasync); + env->SetProtoMethod(tmpl, "fd_fdstat_get", WASI::FdFdstatGet); + env->SetProtoMethod(tmpl, "fd_fdstat_set_flags", WASI::FdFdstatSetFlags); + env->SetProtoMethod(tmpl, "fd_fdstat_set_rights", WASI::FdFdstatSetRights); + env->SetProtoMethod(tmpl, "fd_filestat_get", WASI::FdFilestatGet); + env->SetProtoMethod(tmpl, "fd_filestat_set_size", WASI::FdFilestatSetSize); + env->SetProtoMethod(tmpl, "fd_filestat_set_times", WASI::FdFilestatSetTimes); + env->SetProtoMethod(tmpl, "fd_pread", WASI::FdPread); + env->SetProtoMethod(tmpl, "fd_prestat_get", WASI::FdPrestatGet); + env->SetProtoMethod(tmpl, "fd_prestat_dir_name", WASI::FdPrestatDirName); + env->SetProtoMethod(tmpl, "fd_pwrite", WASI::FdPwrite); + env->SetProtoMethod(tmpl, "fd_read", WASI::FdRead); + env->SetProtoMethod(tmpl, "fd_readdir", WASI::FdReaddir); + env->SetProtoMethod(tmpl, "fd_renumber", WASI::FdRenumber); + env->SetProtoMethod(tmpl, "fd_seek", WASI::FdSeek); + env->SetProtoMethod(tmpl, "fd_sync", WASI::FdSync); + env->SetProtoMethod(tmpl, "fd_tell", WASI::FdTell); + env->SetProtoMethod(tmpl, "fd_write", WASI::FdWrite); + env->SetProtoMethod(tmpl, "path_create_directory", WASI::PathCreateDirectory); + env->SetProtoMethod(tmpl, "path_filestat_get", WASI::PathFilestatGet); + env->SetProtoMethod(tmpl, + "path_filestat_set_times", + WASI::PathFilestatSetTimes); + env->SetProtoMethod(tmpl, "path_link", WASI::PathLink); + env->SetProtoMethod(tmpl, "path_open", WASI::PathOpen); + env->SetProtoMethod(tmpl, "path_readlink", WASI::PathReadlink); + env->SetProtoMethod(tmpl, "path_remove_directory", WASI::PathRemoveDirectory); + env->SetProtoMethod(tmpl, "path_rename", WASI::PathRename); + env->SetProtoMethod(tmpl, "path_symlink", WASI::PathSymlink); + env->SetProtoMethod(tmpl, "path_unlink_file", WASI::PathUnlinkFile); + env->SetProtoMethod(tmpl, "poll_oneoff", WASI::PollOneoff); + env->SetProtoMethod(tmpl, "proc_exit", WASI::ProcExit); + env->SetProtoMethod(tmpl, "proc_raise", WASI::ProcRaise); + env->SetProtoMethod(tmpl, "random_get", WASI::RandomGet); + env->SetProtoMethod(tmpl, "sched_yield", WASI::SchedYield); + env->SetProtoMethod(tmpl, "sock_recv", WASI::SockRecv); + env->SetProtoMethod(tmpl, "sock_send", WASI::SockSend); + env->SetProtoMethod(tmpl, "sock_shutdown", WASI::SockShutdown); + + env->SetInstanceMethod(tmpl, "_setMemory", WASI::_SetMemory); + + target->Set(env->context(), + wasi_wrap_string, + tmpl->GetFunction(context).ToLocalChecked()).ToChecked(); +} + + +} // namespace wasi +} // namespace node + +NODE_MODULE_CONTEXT_AWARE_INTERNAL(wasi, node::wasi::Initialize) diff --git a/src/node_wasi.h b/src/node_wasi.h new file mode 100644 index 00000000000000..ca726e48a42a47 --- /dev/null +++ b/src/node_wasi.h @@ -0,0 +1,103 @@ +#ifndef SRC_NODE_WASI_H_ +#define SRC_NODE_WASI_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "base_object.h" +#include "memory_tracker-inl.h" +#include "uvwasi.h" + +namespace node { +namespace wasi { + + +class WASI : public BaseObject { + public: + WASI(Environment* env, + v8::Local object, + uvwasi_options_t* options); + static void New(const v8::FunctionCallbackInfo& args); + void MemoryInfo(MemoryTracker* tracker) const override { + /* TODO(cjihrig): Get memory consumption from uvwasi. */ + tracker->TrackField("memory", memory_); + } + + SET_MEMORY_INFO_NAME(WASI) + SET_SELF_SIZE(WASI) + + static void ArgsGet(const v8::FunctionCallbackInfo& args); + static void ArgsSizesGet(const v8::FunctionCallbackInfo& args); + static void ClockResGet(const v8::FunctionCallbackInfo& args); + static void ClockTimeGet(const v8::FunctionCallbackInfo& args); + static void EnvironGet(const v8::FunctionCallbackInfo& args); + static void EnvironSizesGet(const v8::FunctionCallbackInfo& args); + static void FdAdvise(const v8::FunctionCallbackInfo& args); + static void FdAllocate(const v8::FunctionCallbackInfo& args); + static void FdClose(const v8::FunctionCallbackInfo& args); + static void FdDatasync(const v8::FunctionCallbackInfo& args); + static void FdFdstatGet(const v8::FunctionCallbackInfo& args); + static void FdFdstatSetFlags(const v8::FunctionCallbackInfo& args); + static void FdFdstatSetRights( + const v8::FunctionCallbackInfo& args); + static void FdFilestatGet(const v8::FunctionCallbackInfo& args); + static void FdFilestatSetSize( + const v8::FunctionCallbackInfo& args); + static void FdFilestatSetTimes( + const v8::FunctionCallbackInfo& args); + static void FdPread(const v8::FunctionCallbackInfo& args); + static void FdPrestatGet(const v8::FunctionCallbackInfo& args); + static void FdPrestatDirName(const v8::FunctionCallbackInfo& args); + static void FdPwrite(const v8::FunctionCallbackInfo& args); + static void FdRead(const v8::FunctionCallbackInfo& args); + static void FdReaddir(const v8::FunctionCallbackInfo& args); + static void FdRenumber(const v8::FunctionCallbackInfo& args); + static void FdSeek(const v8::FunctionCallbackInfo& args); + static void FdSync(const v8::FunctionCallbackInfo& args); + static void FdTell(const v8::FunctionCallbackInfo& args); + static void FdWrite(const v8::FunctionCallbackInfo& args); + static void PathCreateDirectory( + const v8::FunctionCallbackInfo& args); + static void PathFilestatGet(const v8::FunctionCallbackInfo& args); + static void PathFilestatSetTimes( + const v8::FunctionCallbackInfo& args); + static void PathLink(const v8::FunctionCallbackInfo& args); + static void PathOpen(const v8::FunctionCallbackInfo& args); + static void PathReadlink(const v8::FunctionCallbackInfo& args); + static void PathRemoveDirectory( + const v8::FunctionCallbackInfo& args); + static void PathRename(const v8::FunctionCallbackInfo& args); + static void PathSymlink(const v8::FunctionCallbackInfo& args); + static void PathUnlinkFile(const v8::FunctionCallbackInfo& args); + static void PollOneoff(const v8::FunctionCallbackInfo& args); + static void ProcExit(const v8::FunctionCallbackInfo& args); + static void ProcRaise(const v8::FunctionCallbackInfo& args); + static void RandomGet(const v8::FunctionCallbackInfo& args); + static void SchedYield(const v8::FunctionCallbackInfo& args); + static void SockRecv(const v8::FunctionCallbackInfo& args); + static void SockSend(const v8::FunctionCallbackInfo& args); + static void SockShutdown(const v8::FunctionCallbackInfo& args); + + static void _SetMemory(const v8::FunctionCallbackInfo& args); + + private: + ~WASI() override; + inline void readUInt8(char* memory, uint8_t* value, uint32_t offset); + inline void readUInt16(char* memory, uint16_t* value, uint32_t offset); + inline void readUInt32(char* memory, uint32_t* value, uint32_t offset); + inline void readUInt64(char* memory, uint64_t* value, uint32_t offset); + inline void writeUInt8(char* memory, uint8_t value, uint32_t offset); + inline void writeUInt16(char* memory, uint16_t value, uint32_t offset); + inline void writeUInt32(char* memory, uint32_t value, uint32_t offset); + inline void writeUInt64(char* memory, uint64_t value, uint32_t offset); + uvwasi_errno_t backingStore(char** store, size_t* byte_length); + uvwasi_t uvw_; + v8::Global memory_; +}; + + +} // namespace wasi +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_NODE_WASI_H_ diff --git a/src/node_watchdog.h b/src/node_watchdog.h index d26f022d84874d..0fc133a96c4ac7 100644 --- a/src/node_watchdog.h +++ b/src/node_watchdog.h @@ -24,7 +24,6 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS -#include "v8.h" #include "uv.h" #include "node_mutex.h" #include diff --git a/src/req_wrap-inl.h b/src/req_wrap-inl.h index 4fa4d0cf217069..c905b605cbde97 100644 --- a/src/req_wrap-inl.h +++ b/src/req_wrap-inl.h @@ -120,7 +120,7 @@ struct MakeLibuvRequestCallback { using F = void(*)(ReqT* req, Args... args); static void Wrapper(ReqT* req, Args... args) { - ReqWrap* req_wrap = ContainerOf(&ReqWrap::req_, req); + ReqWrap* req_wrap = ReqWrap::from_req(req); req_wrap->env()->DecreaseWaitingRequestCounter(); F original_callback = reinterpret_cast(req_wrap->original_callback_); original_callback(req, args...); diff --git a/src/req_wrap.h b/src/req_wrap.h index 36eeb1cbc24005..5d7fcb42d01148 100644 --- a/src/req_wrap.h +++ b/src/req_wrap.h @@ -50,9 +50,10 @@ class ReqWrap : public AsyncWrap, public ReqWrapBase { private: friend int GenDebugSymbols(); - template - friend struct MakeLibuvRequestCallback; + // Adding `friend struct MakeLibuvRequestCallback` is not enough anymore + // for some reason. Consider this private. + public: typedef void (*callback_t)(); callback_t original_callback_ = nullptr; diff --git a/src/signal_wrap.cc b/src/signal_wrap.cc index cf67dc590f6d51..bc2d9f1e355efd 100644 --- a/src/signal_wrap.cc +++ b/src/signal_wrap.cc @@ -91,7 +91,10 @@ class SignalWrap : public HandleWrap { } void Close(v8::Local close_callback) override { - if (active_) DecreaseSignalHandlerCount(handle_.signum); + if (active_) { + DecreaseSignalHandlerCount(handle_.signum); + active_ = false; + } HandleWrap::Close(close_callback); } diff --git a/src/stream_pipe.cc b/src/stream_pipe.cc index 6e339378ceb374..d405c4d5cbeaed 100644 --- a/src/stream_pipe.cc +++ b/src/stream_pipe.cc @@ -42,7 +42,7 @@ StreamPipe::StreamPipe(StreamBase* source, } StreamPipe::~StreamPipe() { - Unpipe(); + Unpipe(true); } StreamBase* StreamPipe::source() { @@ -53,7 +53,7 @@ StreamBase* StreamPipe::sink() { return static_cast(writable_listener_.stream()); } -void StreamPipe::Unpipe() { +void StreamPipe::Unpipe(bool is_in_deletion) { if (is_closed_) return; @@ -68,11 +68,13 @@ void StreamPipe::Unpipe() { source()->RemoveStreamListener(&readable_listener_); sink()->RemoveStreamListener(&writable_listener_); + if (is_in_deletion) return; + // Delay the JS-facing part with SetImmediate, because this might be from // inside the garbage collector, so we can’t run JS here. HandleScope handle_scope(env()->isolate()); BaseObjectPtr strong_ref{this}; - env()->SetImmediate([this](Environment* env) { + env()->SetImmediate([this, strong_ref](Environment* env) { HandleScope handle_scope(env->isolate()); Context::Scope context_scope(env->context()); Local object = this->object(); diff --git a/src/stream_pipe.h b/src/stream_pipe.h index 061ad9842e8f6d..0e155006102eaa 100644 --- a/src/stream_pipe.h +++ b/src/stream_pipe.h @@ -12,7 +12,7 @@ class StreamPipe : public AsyncWrap { StreamPipe(StreamBase* source, StreamBase* sink, v8::Local obj); ~StreamPipe() override; - void Unpipe(); + void Unpipe(bool is_in_deletion = false); static void New(const v8::FunctionCallbackInfo& args); static void Start(const v8::FunctionCallbackInfo& args); diff --git a/src/stream_wrap.h b/src/stream_wrap.h index 37f5af46066181..816f557ee6cb0a 100644 --- a/src/stream_wrap.h +++ b/src/stream_wrap.h @@ -26,7 +26,6 @@ #include "stream_base.h" #include "handle_wrap.h" -#include "string_bytes.h" #include "v8.h" namespace node { diff --git a/src/string_bytes.h b/src/string_bytes.h index 6ffdc47db799ae..5ef05fc48cd50d 100644 --- a/src/string_bytes.h +++ b/src/string_bytes.h @@ -27,7 +27,7 @@ // Decodes a v8::Local or Buffer to a raw char* #include "v8.h" -#include "env.h" +#include "env-inl.h" namespace node { diff --git a/src/string_decoder-inl.h b/src/string_decoder-inl.h index 8a04211906f759..e7c0abb51546a7 100644 --- a/src/string_decoder-inl.h +++ b/src/string_decoder-inl.h @@ -4,7 +4,6 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #include "string_decoder.h" -#include "util.h" namespace node { diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index 4ec6dda6df70d7..cd7a5d59ebd842 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -1065,11 +1065,14 @@ int TLSWrap::SelectSNIContextCallback(SSL* s, int* ad, void* arg) { return SSL_TLSEXT_ERR_NOACK; } - p->sni_context_.Reset(env->isolate(), ctx); - SecureContext* sc = Unwrap(ctx.As()); CHECK_NOT_NULL(sc); - p->SetSNIContext(sc); + p->sni_context_ = BaseObjectPtr(sc); + + p->ConfigureSecureContext(sc); + CHECK_EQ(SSL_set_SSL_CTX(p->ssl_.get(), sc->ctx_.get()), sc->ctx_.get()); + p->SetCACerts(sc); + return SSL_TLSEXT_ERR_OK; } @@ -1089,6 +1092,7 @@ void TLSWrap::GetWriteQueueSize(const FunctionCallbackInfo& info) { void TLSWrap::MemoryInfo(MemoryTracker* tracker) const { + SSLWrap::MemoryInfo(tracker); tracker->TrackField("error", error_); tracker->TrackFieldWithSize("pending_cleartext_input", pending_cleartext_input_.size(), diff --git a/src/tracing/agent.h b/src/tracing/agent.h index a129f954d7bfb7..7ee1b417c6ffa0 100644 --- a/src/tracing/agent.h +++ b/src/tracing/agent.h @@ -3,7 +3,6 @@ #include "libplatform/v8-tracing.h" #include "uv.h" -#include "v8.h" #include "util.h" #include "node_mutex.h" @@ -12,6 +11,11 @@ #include #include +namespace v8 { +class ConvertableToTraceFormat; +class TracingController; +} // namespace v8 + namespace node { namespace tracing { diff --git a/src/udp_wrap.h b/src/udp_wrap.h index 6375ac2d1b8c80..2026dd1dee1eb1 100644 --- a/src/udp_wrap.h +++ b/src/udp_wrap.h @@ -24,7 +24,6 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS -#include "async_wrap.h" #include "handle_wrap.h" #include "uv.h" #include "v8.h" diff --git a/test/addons/async-hello-world/test-makecallback.js b/test/addons/async-hello-world/test-makecallback.js index 0edf052e8c34fc..abbc4be9c680c3 100644 --- a/test/addons/async-hello-world/test-makecallback.js +++ b/test/addons/async-hello-world/test-makecallback.js @@ -3,7 +3,7 @@ const common = require('../../common'); const assert = require('assert'); const { runMakeCallback } = require(`./build/${common.buildType}/binding`); -runMakeCallback(5, common.mustCall(function(err, val) { +runMakeCallback(5, common.mustCall((err, val) => { assert.strictEqual(err, null); assert.strictEqual(val, 10); process.nextTick(common.mustCall()); diff --git a/test/addons/async-hello-world/test.js b/test/addons/async-hello-world/test.js index f24071087c0629..72b31f7ab6ec20 100644 --- a/test/addons/async-hello-world/test.js +++ b/test/addons/async-hello-world/test.js @@ -3,7 +3,7 @@ const common = require('../../common'); const assert = require('assert'); const { runCall } = require(`./build/${common.buildType}/binding`); -runCall(5, common.mustCall(function(err, val) { +runCall(5, common.mustCall((err, val) => { assert.strictEqual(err, null); assert.strictEqual(val, 10); process.nextTick(common.mustCall()); diff --git a/test/addons/load-long-path/test.js b/test/addons/load-long-path/test.js index 168dad492b0255..9a9d0763994b08 100644 --- a/test/addons/load-long-path/test.js +++ b/test/addons/load-long-path/test.js @@ -48,6 +48,6 @@ fs.writeFileSync(addonDestinationPath, contents); // Run test const child = fork(__filename, ['child'], { stdio: 'inherit' }); -child.on('exit', common.mustCall(function(code) { +child.on('exit', common.mustCall((code) => { assert.strictEqual(code, 0); })); diff --git a/test/addons/make-callback-domain-warning/test.js b/test/addons/make-callback-domain-warning/test.js index 2ea3c3f3d14b2b..0377415e1269aa 100644 --- a/test/addons/make-callback-domain-warning/test.js +++ b/test/addons/make-callback-domain-warning/test.js @@ -10,23 +10,23 @@ function makeCallback(object, cb) { } let latestWarning = null; -process.on('warning', function(warning) { +process.on('warning', (warning) => { latestWarning = warning; }); const d = domain.create(); // When domain is disabled, no warning will be emitted -makeCallback({ domain: d }, common.mustCall(function() { +makeCallback({ domain: d }, common.mustCall(() => { assert.strictEqual(latestWarning, null); - d.run(common.mustCall(function() { + d.run(common.mustCall(() => { // No warning will be emitted when no domain property is applied - makeCallback({}, common.mustCall(function() { + makeCallback({}, common.mustCall(() => { assert.strictEqual(latestWarning, null); // Warning is emitted when domain property is used and domain is enabled - makeCallback({ domain: d }, common.mustCall(function() { + makeCallback({ domain: d }, common.mustCall(() => { assert.strictEqual(latestWarning.name, 'DeprecationWarning'); assert.strictEqual(latestWarning.code, 'DEP0097'); })); diff --git a/test/addons/make-callback-recurse/test.js b/test/addons/make-callback-recurse/test.js index a93a0a4e01bfd7..4a540003acd8d1 100644 --- a/test/addons/make-callback-recurse/test.js +++ b/test/addons/make-callback-recurse/test.js @@ -71,7 +71,7 @@ assert.throws(() => { if (arg === 1) { // The tests are first run on bootstrap during LoadEnvironment() in // src/node.cc. Now run the tests through node::MakeCallback(). - setImmediate(function() { + setImmediate(() => { makeCallback({}, common.mustCall(() => { verifyExecutionOrder(2); })); diff --git a/test/addons/openssl-client-cert-engine/test.js b/test/addons/openssl-client-cert-engine/test.js index 9e7d507b07b7ac..e843e4bf433bff 100644 --- a/test/addons/openssl-client-cert-engine/test.js +++ b/test/addons/openssl-client-cert-engine/test.js @@ -43,14 +43,14 @@ const server = https.createServer(serverOptions, common.mustCall((req, res) => { headers: {} }; - const req = https.request(clientOptions, common.mustCall(function(response) { + const req = https.request(clientOptions, common.mustCall((response) => { let body = ''; response.setEncoding('utf8'); - response.on('data', function(chunk) { + response.on('data', (chunk) => { body += chunk; }); - response.on('end', common.mustCall(function() { + response.on('end', common.mustCall(() => { assert.strictEqual(body, 'hello world'); server.close(); })); diff --git a/test/addons/openssl-key-engine/test.js b/test/addons/openssl-key-engine/test.js index 5c93e6263626bf..2cd7ddabc17095 100644 --- a/test/addons/openssl-key-engine/test.js +++ b/test/addons/openssl-key-engine/test.js @@ -45,14 +45,14 @@ const server = https.createServer(serverOptions, common.mustCall((req, res) => { headers: {} }; - const req = https.request(clientOptions, common.mustCall(function(response) { + const req = https.request(clientOptions, common.mustCall((response) => { let body = ''; response.setEncoding('utf8'); - response.on('data', function(chunk) { + response.on('data', (chunk) => { body += chunk; }); - response.on('end', common.mustCall(function() { + response.on('end', common.mustCall(() => { assert.strictEqual(body, 'hello world'); server.close(); })); diff --git a/test/addons/repl-domain-abort/test.js b/test/addons/repl-domain-abort/test.js index 2049fe6e6a23f5..f8e70ffce96e45 100644 --- a/test/addons/repl-domain-abort/test.js +++ b/test/addons/repl-domain-abort/test.js @@ -31,7 +31,7 @@ if (common.isWindows) buildPath = buildPath.replace(/\\/g, '/'); let cb_ran = false; -process.on('exit', function() { +process.on('exit', () => { assert(cb_ran); console.log('ok'); }); diff --git a/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-ascii.js b/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-ascii.js index 5e3033db3ea193..55f50f524927e8 100644 --- a/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-ascii.js +++ b/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-ascii.js @@ -25,7 +25,7 @@ if (!binding.ensureAllocation(2 * kStringMaxLength)) common.skip(skipMessage); const stringLengthHex = kStringMaxLength.toString(16); -common.expectsError(function() { +common.expectsError(() => { buf.toString('ascii'); }, { message: `Cannot create a string longer than 0x${stringLengthHex} ` + diff --git a/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-base64.js b/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-base64.js index 400477034d0010..20d968787bf731 100644 --- a/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-base64.js +++ b/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-base64.js @@ -25,7 +25,7 @@ if (!binding.ensureAllocation(2 * kStringMaxLength)) common.skip(skipMessage); const stringLengthHex = kStringMaxLength.toString(16); -common.expectsError(function() { +common.expectsError(() => { buf.toString('base64'); }, { message: `Cannot create a string longer than 0x${stringLengthHex} ` + diff --git a/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-binary.js b/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-binary.js index ef5d6a21ed0042..f6ae5217ddf0ec 100644 --- a/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-binary.js +++ b/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-binary.js @@ -27,7 +27,7 @@ if (!binding.ensureAllocation(2 * kStringMaxLength)) common.skip(skipMessage); const stringLengthHex = kStringMaxLength.toString(16); -common.expectsError(function() { +common.expectsError(() => { buf.toString('latin1'); }, { message: `Cannot create a string longer than 0x${stringLengthHex} ` + diff --git a/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-hex.js b/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-hex.js index 844c8bcb335a87..fa7a47cf184cd1 100644 --- a/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-hex.js +++ b/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-hex.js @@ -25,7 +25,7 @@ if (!binding.ensureAllocation(2 * kStringMaxLength)) common.skip(skipMessage); const stringLengthHex = kStringMaxLength.toString(16); -common.expectsError(function() { +common.expectsError(() => { buf.toString('hex'); }, { message: `Cannot create a string longer than 0x${stringLengthHex} ` + diff --git a/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-utf8.js b/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-utf8.js index 15ec78b6c9ab1c..43c88bd49e7e15 100644 --- a/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-utf8.js +++ b/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-utf8.js @@ -27,9 +27,9 @@ if (!binding.ensureAllocation(2 * kStringMaxLength)) const stringLengthHex = kStringMaxLength.toString(16); -assert.throws(function() { +assert.throws(() => { buf.toString(); -}, function(e) { +}, (e) => { if (e.message !== 'Array buffer allocation failed') { common.expectsError({ message: `Cannot create a string longer than 0x${stringLengthHex} ` + @@ -43,7 +43,7 @@ assert.throws(function() { } }); -common.expectsError(function() { +common.expectsError(() => { buf.toString('utf8'); }, { message: `Cannot create a string longer than 0x${stringLengthHex} ` + diff --git a/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max.js b/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max.js index d65144e04d457c..8b419eb4362de0 100644 --- a/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max.js +++ b/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max.js @@ -26,7 +26,7 @@ if (!binding.ensureAllocation(2 * kStringMaxLength)) const stringLengthHex = kStringMaxLength.toString(16); -common.expectsError(function() { +common.expectsError(() => { buf.toString('utf16le'); }, { message: `Cannot create a string longer than 0x${stringLengthHex} ` + diff --git a/test/addons/worker-buffer-callback/binding.cc b/test/addons/worker-buffer-callback/binding.cc index a40876ebb523a6..1141c8a051e077 100644 --- a/test/addons/worker-buffer-callback/binding.cc +++ b/test/addons/worker-buffer-callback/binding.cc @@ -3,17 +3,24 @@ #include using v8::Context; +using v8::FunctionCallbackInfo; using v8::Isolate; using v8::Local; using v8::Object; using v8::Value; +uint32_t free_call_count = 0; char data[] = "hello"; +void GetFreeCallCount(const FunctionCallbackInfo& args) { + args.GetReturnValue().Set(free_call_count); +} + void Initialize(Local exports, Local module, Local context) { Isolate* isolate = context->GetIsolate(); + NODE_SET_METHOD(exports, "getFreeCallCount", GetFreeCallCount); exports->Set(context, v8::String::NewFromUtf8( isolate, "buffer", v8::NewStringType::kNormal) @@ -22,7 +29,9 @@ void Initialize(Local exports, isolate, data, sizeof(data), - [](char* data, void* hint) {}, + [](char* data, void* hint) { + free_call_count++; + }, nullptr).ToLocalChecked()).Check(); } diff --git a/test/addons/worker-buffer-callback/test-free-called.js b/test/addons/worker-buffer-callback/test-free-called.js new file mode 100644 index 00000000000000..2a3cc9e47c22ff --- /dev/null +++ b/test/addons/worker-buffer-callback/test-free-called.js @@ -0,0 +1,17 @@ +'use strict'; +const common = require('../../common'); +const path = require('path'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); +const binding = path.resolve(__dirname, `./build/${common.buildType}/binding`); +const { getFreeCallCount } = require(binding); + +// Test that buffers allocated with a free callback through our APIs are +// released when a Worker owning it exits. + +const w = new Worker(`require(${JSON.stringify(binding)})`, { eval: true }); + +assert.strictEqual(getFreeCallCount(), 0); +w.on('exit', common.mustCall(() => { + assert.strictEqual(getFreeCallCount(), 1); +})); diff --git a/test/async-hooks/test-disable-in-init.js b/test/async-hooks/test-disable-in-init.js index b7ab31e6d97dc2..17537c62f30885 100644 --- a/test/async-hooks/test-disable-in-init.js +++ b/test/async-hooks/test-disable-in-init.js @@ -7,7 +7,7 @@ const fs = require('fs'); let nestedCall = false; async_hooks.createHook({ - init: common.mustCall(function() { + init: common.mustCall(() => { nestedHook.disable(); if (!nestedCall) { nestedCall = true; diff --git a/test/async-hooks/test-graph.http.js b/test/async-hooks/test-graph.http.js index 12862467a6947e..db0c1265670a6f 100644 --- a/test/async-hooks/test-graph.http.js +++ b/test/async-hooks/test-graph.http.js @@ -11,11 +11,11 @@ const http = require('http'); const hooks = initHooks(); hooks.enable(); -const server = http.createServer(common.mustCall(function(req, res) { +const server = http.createServer(common.mustCall((req, res) => { res.end(); - this.close(common.mustCall()); + server.close(common.mustCall()); })); -server.listen(0, common.mustCall(function() { +server.listen(0, common.mustCall(() => { http.get({ host: '::1', family: 6, @@ -23,7 +23,7 @@ server.listen(0, common.mustCall(function() { }, common.mustCall()); })); -process.on('exit', function() { +process.on('exit', () => { hooks.disable(); verifyGraph( diff --git a/test/async-hooks/test-graph.pipeconnect.js b/test/async-hooks/test-graph.pipeconnect.js index 03d2902c835d48..440ea906a17c4d 100644 --- a/test/async-hooks/test-graph.pipeconnect.js +++ b/test/async-hooks/test-graph.pipeconnect.js @@ -12,9 +12,9 @@ tmpdir.refresh(); const hooks = initHooks(); hooks.enable(); -net.createServer(function(c) { +const server = net.createServer((c) => { c.end(); - this.close(); + server.close(); }).listen(common.PIPE, common.mustCall(onlisten)); function onlisten() { diff --git a/test/async-hooks/test-nexttick-default-trigger.js b/test/async-hooks/test-nexttick-default-trigger.js index 1bc93f29737389..7d6d98c9abc902 100644 --- a/test/async-hooks/test-nexttick-default-trigger.js +++ b/test/async-hooks/test-nexttick-default-trigger.js @@ -14,11 +14,11 @@ hooks.enable(); const rootAsyncId = async_hooks.executionAsyncId(); -process.nextTick(common.mustCall(function() { +process.nextTick(common.mustCall(() => { assert.strictEqual(async_hooks.triggerAsyncId(), rootAsyncId); })); -process.on('exit', function() { +process.on('exit', () => { hooks.sanityCheck(); const as = hooks.activitiesOfTypes('TickObject'); diff --git a/test/async-hooks/test-pipeconnectwrap.js b/test/async-hooks/test-pipeconnectwrap.js index f086807e393145..b57cc63cf59e46 100644 --- a/test/async-hooks/test-pipeconnectwrap.js +++ b/test/async-hooks/test-pipeconnectwrap.js @@ -17,9 +17,9 @@ let pipe1, pipe2; let pipeserver; let pipeconnect; -net.createServer(common.mustCall(function(c) { +const server = net.createServer(common.mustCall((c) => { c.end(); - this.close(); + server.close(); process.nextTick(maybeOnconnect.bind(null, 'server')); })).listen(common.PIPE, common.mustCall(onlisten)); diff --git a/test/async-hooks/test-queue-microtask.js b/test/async-hooks/test-queue-microtask.js index dfa537752e37fc..1e2029efe4ad0e 100644 --- a/test/async-hooks/test-queue-microtask.js +++ b/test/async-hooks/test-queue-microtask.js @@ -11,11 +11,11 @@ hooks.enable(); const rootAsyncId = async_hooks.executionAsyncId(); -queueMicrotask(common.mustCall(function() { +queueMicrotask(common.mustCall(() => { assert.strictEqual(async_hooks.triggerAsyncId(), rootAsyncId); })); -process.on('exit', function() { +process.on('exit', () => { hooks.sanityCheck(); const as = hooks.activitiesOfTypes('Microtask'); diff --git a/test/benchmark/test-benchmark-dns.js b/test/benchmark/test-benchmark-dns.js index 27c3271c74d86d..811e9a44b9e358 100644 --- a/test/benchmark/test-benchmark-dns.js +++ b/test/benchmark/test-benchmark-dns.js @@ -4,7 +4,6 @@ require('../common'); const runBenchmark = require('../common/benchmark'); -const env = Object.assign({}, process.env, - { NODEJS_BENCHMARK_ZERO_ALLOWED: 1 }); +const env = { ...process.env, NODEJS_BENCHMARK_ZERO_ALLOWED: 1 }; runBenchmark('dns', ['n=1', 'all=false', 'name=127.0.0.1'], env); diff --git a/test/benchmark/test-benchmark-streams.js b/test/benchmark/test-benchmark-streams.js index f90838794cdf2a..51cdace5748fa4 100644 --- a/test/benchmark/test-benchmark-streams.js +++ b/test/benchmark/test-benchmark-streams.js @@ -7,7 +7,8 @@ const runBenchmark = require('../common/benchmark'); runBenchmark('streams', [ 'kind=duplex', + 'n=1', + 'sync=no', 'type=buffer', - 'n=1' ], { NODEJS_BENCHMARK_ZERO_ALLOWED: 1 }); diff --git a/test/cctest/test_environment.cc b/test/cctest/test_environment.cc index cc9b8e4531f6ef..132f7b44f7db62 100644 --- a/test/cctest/test_environment.cc +++ b/test/cctest/test_environment.cc @@ -1,3 +1,4 @@ +#include "node_buffer.h" #include "node_internals.h" #include "libplatform/libplatform.h" @@ -185,3 +186,57 @@ static void at_exit_js(void* arg) { assert(obj->IsObject()); called_at_exit_js = true; } + +TEST_F(EnvironmentTest, SetImmediateCleanup) { + int called = 0; + int called_unref = 0; + + { + const v8::HandleScope handle_scope(isolate_); + const Argv argv; + Env env {handle_scope, argv}; + + (*env)->SetImmediate([&](node::Environment* env_arg) { + EXPECT_EQ(env_arg, *env); + called++; + }); + (*env)->SetUnrefImmediate([&](node::Environment* env_arg) { + EXPECT_EQ(env_arg, *env); + called_unref++; + }); + } + + EXPECT_EQ(called, 1); + EXPECT_EQ(called_unref, 0); +} + +static char hello[] = "hello"; + +TEST_F(EnvironmentTest, BufferWithFreeCallbackIsDetached) { + // Test that a Buffer allocated with a free callback is detached after + // its callback has been called. + const v8::HandleScope handle_scope(isolate_); + const Argv argv; + + int callback_calls = 0; + + v8::Local ab; + { + Env env {handle_scope, argv}; + v8::Local buf_obj = node::Buffer::New( + isolate_, + hello, + sizeof(hello), + [](char* data, void* hint) { + CHECK_EQ(data, hello); + ++*static_cast(hint); + }, + &callback_calls).ToLocalChecked(); + CHECK(buf_obj->IsUint8Array()); + ab = buf_obj.As()->Buffer(); + CHECK_EQ(ab->ByteLength(), sizeof(hello)); + } + + CHECK_EQ(callback_calls, 1); + CHECK_EQ(ab->ByteLength(), 0); +} diff --git a/test/common/README.md b/test/common/README.md index db32c48b4065e0..5f8b6cb3090d9d 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -237,6 +237,11 @@ Attempts to 'kill' `pid` Platform check for Free BSD. +### isIBMi +* [<boolean>][] + +Platform check for IBMi. + ### isLinux * [<boolean>][] diff --git a/test/common/benchmark.js b/test/common/benchmark.js index bb182fc6887636..f630bb9d0e6fd8 100644 --- a/test/common/benchmark.js +++ b/test/common/benchmark.js @@ -18,7 +18,7 @@ function runBenchmark(name, args, env) { argv.push(name); - const mergedEnv = Object.assign({}, process.env, env); + const mergedEnv = { ...process.env, ...env }; const child = fork(runjs, argv, { env: mergedEnv, diff --git a/test/common/index.js b/test/common/index.js index 888a1feba37462..09b7f5577844cb 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -102,6 +102,9 @@ if (process.argv.length === 2 && const isWindows = process.platform === 'win32'; const isAIX = process.platform === 'aix'; +// On IBMi, process.platform and os.platform() both return 'aix', +// It is not enough to differentiate between IBMi and real AIX system. +const isIBMi = os.type() === 'OS400'; const isLinuxPPCBE = (process.platform === 'linux') && (process.arch === 'ppc64') && (os.endianness() === 'BE'); @@ -762,6 +765,7 @@ module.exports = { isAIX, isAlive, isFreeBSD, + isIBMi, isLinux, isLinuxPPCBE, isMainThread, diff --git a/test/common/index.mjs b/test/common/index.mjs index 5ad6ec3c11eadc..54f6dc7f173cdf 100644 --- a/test/common/index.mjs +++ b/test/common/index.mjs @@ -9,6 +9,7 @@ const { isMainThread, isWindows, isAIX, + isIBMi, isLinuxPPCBE, isSunOS, isFreeBSD, @@ -55,6 +56,7 @@ export { isMainThread, isWindows, isAIX, + isIBMi, isLinuxPPCBE, isSunOS, isFreeBSD, diff --git a/test/doctool/test-doctool-html.js b/test/doctool/test-doctool-html.js index 11b28dc8a2a717..ee8099f8cd8728 100644 --- a/test/doctool/test-doctool-html.js +++ b/test/doctool/test-doctool-html.js @@ -11,6 +11,7 @@ try { const assert = require('assert'); const { readFile } = require('fs'); const fixtures = require('../common/fixtures'); +const { replaceLinks } = require('../../tools/doc/markdown.js'); const html = require('../../tools/doc/html.js'); const path = require('path'); @@ -22,8 +23,22 @@ const remark2rehype = require('remark-rehype'); const raw = require('rehype-raw'); const htmlStringify = require('rehype-stringify'); +// Test links mapper is an object of the following structure: +// { +// [filename]: { +// [link definition identifier]: [url to the linked resource] +// } +// } +const testLinksMapper = { + 'foo': { + 'command line options': 'cli.html#cli-options', + 'web server': 'example.html' + } +}; + async function toHTML({ input, filename, nodeVersion }) { const content = unified() + .use(replaceLinks, { filename, linksMapper: testLinksMapper }) .use(markdown) .use(html.firstHeader) .use(html.preprocessText) @@ -96,6 +111,21 @@ const testData = [ file: fixtures.path('altdocs.md'), html: '
  • 8.x', }, + { + file: fixtures.path('document_with_links.md'), + html: '

    Usage and Example#' + + '

    Usage#

    node \\[options\\] index.js' + + '

    Please see the' + + 'Command Line Optionsdocument for more information.

    ' + + 'Example' + + '#

    An example of a' + + 'webserverwritten with Node.js which responds with' + + '\'Hello, World!\':

    See also#

    Check' + + 'out alsothis guide

    ' + }, ]; const spaces = /\s/g; diff --git a/test/es-module/test-esm-data-urls.js b/test/es-module/test-esm-data-urls.js index f2f166b47ee3d8..30ae55d01f38b8 100644 --- a/test/es-module/test-esm-data-urls.js +++ b/test/es-module/test-esm-data-urls.js @@ -4,6 +4,7 @@ const assert = require('assert'); function createURL(mime, body) { return `data:${mime},${body}`; } + function createBase64URL(mime, body) { return `data:${mime};base64,${Buffer.from(body).toString('base64')}`; } @@ -35,6 +36,47 @@ function createBase64URL(mime, body) { assert.deepStrictEqual(Object.keys(ns), ['default']); assert.deepStrictEqual(ns.default, plainESMURL); } + { + const body = 'export default import.meta.url;'; + const plainESMURL = createURL('text/javascript;charset=UTF-8', body); + const ns = await import(plainESMURL); + assert.deepStrictEqual(Object.keys(ns), ['default']); + assert.deepStrictEqual(ns.default, plainESMURL); + } + { + const body = 'export default import.meta.url;'; + const plainESMURL = createURL('text/javascript;charset="UTF-8"', body); + const ns = await import(plainESMURL); + assert.deepStrictEqual(Object.keys(ns), ['default']); + assert.deepStrictEqual(ns.default, plainESMURL); + } + { + const body = 'export default import.meta.url;'; + const plainESMURL = createURL('text/javascript;;a=a;b=b;;', body); + const ns = await import(plainESMURL); + assert.deepStrictEqual(Object.keys(ns), ['default']); + assert.deepStrictEqual(ns.default, plainESMURL); + } + { + const ns = await import('data:application/json;foo="test,"this"'); + assert.deepStrictEqual(Object.keys(ns), ['default']); + assert.deepStrictEqual(ns.default, 'this'); + } + { + const ns = await import(`data:application/json;foo=${ + encodeURIComponent('test,') + },0`); + assert.deepStrictEqual(Object.keys(ns), ['default']); + assert.deepStrictEqual(ns.default, 0); + } + { + await assert.rejects(async () => { + return import('data:application/json;foo="test,",0'); + }, { + name: 'SyntaxError', + message: /Unexpected end of JSON input/ + }); + } { const body = '{"x": 1}'; const plainESMURL = createURL('application/json', body); diff --git a/test/es-module/test-esm-specifiers.mjs b/test/es-module/test-esm-specifiers.mjs index 3e7bc181962f4f..fdf9e5b25ecea4 100644 --- a/test/es-module/test-esm-specifiers.mjs +++ b/test/es-module/test-esm-specifiers.mjs @@ -1,6 +1,9 @@ // Flags: --es-module-specifier-resolution=node import { mustNotCall } from '../common/index.mjs'; import assert from 'assert'; +import path from 'path'; +import { spawn } from 'child_process'; +import { fileURLToPath } from 'url'; // commonJS index.js import commonjs from '../fixtures/es-module-specifiers/package-type-commonjs'; @@ -33,3 +36,22 @@ async function main() { } main().catch(mustNotCall); + +// Test path from command line arguments +[ + 'package-type-commonjs', + 'package-type-module', + '/', + '/index', +].forEach((item) => { + const modulePath = path.join( + fileURLToPath(import.meta.url), + '../../fixtures/es-module-specifiers', + item, + ); + spawn(process.execPath, + ['--es-module-specifier-resolution=node', modulePath], + { stdio: 'inherit' }).on('exit', (code) => { + assert.strictEqual(code, 0); + }); +}); diff --git a/test/fixtures/document_with_links.md b/test/fixtures/document_with_links.md new file mode 100644 index 00000000000000..1392029a30ba6a --- /dev/null +++ b/test/fixtures/document_with_links.md @@ -0,0 +1,20 @@ +# Usage and Example + +## Usage + +`node \[options\] index.js` + +Please see the [Command Line Options][] document for more information. + +## Example + +An example of a [web server][] written with Node.js which responds with +`'Hello, World!'`: + +## See also + +Check out also [this guide][] + +[Command Line Options]: cli.md#options +[this guide]: https://nodejs.org/ +[web server]: example.md diff --git a/test/fixtures/outside.txt b/test/fixtures/outside.txt new file mode 100644 index 00000000000000..044c4b9614586d --- /dev/null +++ b/test/fixtures/outside.txt @@ -0,0 +1,2 @@ +this file is part of the WASI tests. it exists outside of the sandbox, and +should be inaccessible from the WASI tests. diff --git a/test/fixtures/source-map/emptyStackError.js b/test/fixtures/source-map/emptyStackError.js new file mode 100644 index 00000000000000..b02367a180dab7 --- /dev/null +++ b/test/fixtures/source-map/emptyStackError.js @@ -0,0 +1,6 @@ +"use strict"; + +Error.stackTraceLimit = 0; +throw new RangeError('emptyStackError'); + + diff --git a/test/fixtures/wasi/input.txt b/test/fixtures/wasi/input.txt new file mode 100644 index 00000000000000..4c380537647f19 --- /dev/null +++ b/test/fixtures/wasi/input.txt @@ -0,0 +1 @@ +hello from input.txt diff --git a/test/fixtures/wasi/notadir b/test/fixtures/wasi/notadir new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/test/fixtures/wasi/simple-wasi.wasm b/test/fixtures/wasi/simple-wasi.wasm new file mode 100755 index 00000000000000..334d3a3dcac6c0 Binary files /dev/null and b/test/fixtures/wasi/simple-wasi.wasm differ diff --git a/test/fixtures/wasi/simple-wasi.wat b/test/fixtures/wasi/simple-wasi.wat new file mode 100644 index 00000000000000..318f390c76b1fa --- /dev/null +++ b/test/fixtures/wasi/simple-wasi.wat @@ -0,0 +1,6533 @@ +(module + (type (;0;) (func (param i32 i32 i32) (result i32))) + (type (;1;) (func (param i32 i64 i32) (result i64))) + (type (;2;) (func (param i32 i32) (result i32))) + (type (;3;) (func (param i32))) + (type (;4;) (func (param i32) (result i32))) + (type (;5;) (func (param i32 i32 i32 i32) (result i32))) + (type (;6;) (func (param i32 i64 i32 i32) (result i32))) + (type (;7;) (func)) + (type (;8;) (func (result i32))) + (import "wasi_unstable" "fd_prestat_get" (func (;0;) (type 2))) + (import "wasi_unstable" "fd_prestat_dir_name" (func (;1;) (type 0))) + (import "wasi_unstable" "environ_sizes_get" (func (;2;) (type 2))) + (import "wasi_unstable" "environ_get" (func (;3;) (type 2))) + (import "wasi_unstable" "args_sizes_get" (func (;4;) (type 2))) + (import "wasi_unstable" "args_get" (func (;5;) (type 2))) + (import "wasi_unstable" "proc_exit" (func (;6;) (type 3))) + (import "wasi_unstable" "fd_fdstat_get" (func (;7;) (type 2))) + (import "wasi_unstable" "fd_close" (func (;8;) (type 4))) + (import "wasi_unstable" "fd_write" (func (;9;) (type 5))) + (import "wasi_unstable" "fd_seek" (func (;10;) (type 6))) + (func (;11;) (type 7)) + (func (;12;) (type 7) + (local i32 i32 i32 i32) + get_global 0 + i32.const 16 + i32.sub + tee_local 0 + set_global 0 + call 23 + i32.const 3 + set_local 1 + block ;; label = @1 + block ;; label = @2 + block ;; label = @3 + block ;; label = @4 + loop ;; label = @5 + get_local 1 + get_local 0 + call 0 + tee_local 2 + i32.const 8 + i32.eq + br_if 1 (;@4;) + get_local 2 + br_if 3 (;@2;) + block ;; label = @6 + get_local 0 + i32.load8_u + br_if 0 (;@6;) + get_local 0 + i32.load offset=4 + i32.const 1 + i32.add + call 14 + tee_local 2 + i32.eqz + br_if 4 (;@2;) + get_local 1 + get_local 2 + get_local 0 + i32.load offset=4 + call 1 + br_if 3 (;@3;) + get_local 2 + get_local 0 + i32.load offset=4 + i32.add + i32.const 0 + i32.store8 + get_local 1 + get_local 2 + call 24 + set_local 3 + get_local 2 + call 16 + get_local 3 + br_if 4 (;@2;) + end + get_local 1 + i32.const 1 + i32.add + tee_local 1 + br_if 0 (;@5;) + end + end + block ;; label = @4 + get_local 0 + get_local 0 + i32.const 12 + i32.add + call 2 + br_if 0 (;@4;) + i32.const 0 + get_local 0 + i32.load + i32.const 2 + i32.shl + i32.const 4 + i32.add + call 14 + i32.store offset=1544 + get_local 0 + i32.load offset=12 + call 14 + tee_local 1 + i32.eqz + br_if 0 (;@4;) + i32.const 0 + i32.load offset=1544 + tee_local 2 + i32.eqz + br_if 0 (;@4;) + get_local 2 + get_local 0 + i32.load + i32.const 2 + i32.shl + i32.add + i32.const 0 + i32.store + i32.const 0 + i32.load offset=1544 + get_local 1 + call 3 + br_if 0 (;@4;) + block ;; label = @5 + block ;; label = @6 + block ;; label = @7 + get_local 0 + i32.const 12 + i32.add + get_local 0 + call 4 + br_if 0 (;@7;) + get_local 0 + i32.load offset=12 + tee_local 1 + i32.eqz + br_if 1 (;@6;) + get_local 1 + i32.const 2 + i32.shl + i32.const 4 + i32.add + call 14 + set_local 1 + get_local 0 + i32.load + call 14 + set_local 2 + get_local 1 + i32.eqz + br_if 0 (;@7;) + get_local 2 + i32.eqz + br_if 0 (;@7;) + get_local 1 + i32.const 0 + i32.store + get_local 1 + get_local 2 + call 5 + i32.eqz + br_if 2 (;@5;) + end + i32.const 71 + call 19 + unreachable + end + end + call 11 + get_local 0 + i32.load offset=12 + get_local 1 + call 13 + set_local 1 + call 27 + get_local 1 + br_if 3 (;@1;) + get_local 0 + i32.const 16 + i32.add + set_global 0 + return + end + i32.const 71 + call 19 + unreachable + end + get_local 2 + call 16 + end + i32.const 71 + call 19 + unreachable + end + get_local 1 + call 19 + unreachable) + (func (;13;) (type 2) (param i32 i32) (result i32) + i32.const 1024 + call 35 + drop + i32.const 0) + (func (;14;) (type 4) (param i32) (result i32) + get_local 0 + call 15) + (func (;15;) (type 4) (param i32) (result i32) + (local i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32) + get_global 0 + i32.const 16 + i32.sub + tee_local 1 + set_global 0 + block ;; label = @1 + block ;; label = @2 + block ;; label = @3 + block ;; label = @4 + block ;; label = @5 + block ;; label = @6 + block ;; label = @7 + block ;; label = @8 + block ;; label = @9 + block ;; label = @10 + block ;; label = @11 + block ;; label = @12 + block ;; label = @13 + block ;; label = @14 + block ;; label = @15 + block ;; label = @16 + block ;; label = @17 + block ;; label = @18 + block ;; label = @19 + block ;; label = @20 + block ;; label = @21 + block ;; label = @22 + block ;; label = @23 + block ;; label = @24 + block ;; label = @25 + block ;; label = @26 + block ;; label = @27 + block ;; label = @28 + block ;; label = @29 + block ;; label = @30 + block ;; label = @31 + block ;; label = @32 + block ;; label = @33 + block ;; label = @34 + block ;; label = @35 + block ;; label = @36 + block ;; label = @37 + block ;; label = @38 + get_local 0 + i32.const 244 + i32.gt_u + br_if 0 (;@38;) + i32.const 0 + i32.load offset=1040 + tee_local 2 + i32.const 16 + get_local 0 + i32.const 11 + i32.add + i32.const -8 + i32.and + get_local 0 + i32.const 11 + i32.lt_u + select + tee_local 3 + i32.const 3 + i32.shr_u + tee_local 4 + i32.shr_u + tee_local 0 + i32.const 3 + i32.and + i32.eqz + br_if 1 (;@37;) + get_local 0 + i32.const -1 + i32.xor + i32.const 1 + i32.and + get_local 4 + i32.add + tee_local 5 + i32.const 3 + i32.shl + tee_local 6 + i32.const 1088 + i32.add + i32.load + tee_local 4 + i32.const 8 + i32.add + set_local 0 + get_local 4 + i32.load offset=8 + tee_local 3 + get_local 6 + i32.const 1080 + i32.add + tee_local 6 + i32.eq + br_if 2 (;@36;) + get_local 3 + get_local 6 + i32.store offset=12 + get_local 6 + i32.const 8 + i32.add + get_local 3 + i32.store + br 3 (;@35;) + end + i32.const -1 + set_local 3 + get_local 0 + i32.const -65 + i32.gt_u + br_if 14 (;@23;) + get_local 0 + i32.const 11 + i32.add + tee_local 0 + i32.const -8 + i32.and + set_local 3 + i32.const 0 + i32.load offset=1044 + tee_local 7 + i32.eqz + br_if 14 (;@23;) + i32.const 0 + set_local 8 + block ;; label = @38 + get_local 0 + i32.const 8 + i32.shr_u + tee_local 0 + i32.eqz + br_if 0 (;@38;) + i32.const 31 + set_local 8 + get_local 3 + i32.const 16777215 + i32.gt_u + br_if 0 (;@38;) + get_local 3 + i32.const 14 + get_local 0 + get_local 0 + i32.const 1048320 + i32.add + i32.const 16 + i32.shr_u + i32.const 8 + i32.and + tee_local 4 + i32.shl + tee_local 0 + i32.const 520192 + i32.add + i32.const 16 + i32.shr_u + i32.const 4 + i32.and + tee_local 5 + get_local 4 + i32.or + get_local 0 + get_local 5 + i32.shl + tee_local 0 + i32.const 245760 + i32.add + i32.const 16 + i32.shr_u + i32.const 2 + i32.and + tee_local 4 + i32.or + i32.sub + get_local 0 + get_local 4 + i32.shl + i32.const 15 + i32.shr_u + i32.add + tee_local 0 + i32.const 7 + i32.add + i32.shr_u + i32.const 1 + i32.and + get_local 0 + i32.const 1 + i32.shl + i32.or + set_local 8 + end + i32.const 0 + get_local 3 + i32.sub + set_local 5 + get_local 8 + i32.const 2 + i32.shl + i32.const 1344 + i32.add + i32.load + tee_local 4 + i32.eqz + br_if 3 (;@34;) + get_local 3 + i32.const 0 + i32.const 25 + get_local 8 + i32.const 1 + i32.shr_u + i32.sub + get_local 8 + i32.const 31 + i32.eq + select + i32.shl + set_local 6 + i32.const 0 + set_local 0 + i32.const 0 + set_local 9 + loop ;; label = @38 + block ;; label = @39 + get_local 4 + i32.load offset=4 + i32.const -8 + i32.and + get_local 3 + i32.sub + tee_local 2 + get_local 5 + i32.ge_u + br_if 0 (;@39;) + get_local 2 + set_local 5 + get_local 4 + set_local 9 + get_local 2 + i32.eqz + br_if 8 (;@31;) + end + get_local 0 + get_local 4 + i32.const 20 + i32.add + i32.load + tee_local 2 + get_local 2 + get_local 4 + get_local 6 + i32.const 29 + i32.shr_u + i32.const 4 + i32.and + i32.add + i32.const 16 + i32.add + i32.load + tee_local 4 + i32.eq + select + get_local 0 + get_local 2 + select + set_local 0 + get_local 6 + get_local 4 + i32.const 0 + i32.ne + i32.shl + set_local 6 + get_local 4 + br_if 0 (;@38;) + end + get_local 0 + get_local 9 + i32.or + i32.eqz + br_if 4 (;@33;) + br 11 (;@26;) + end + get_local 3 + i32.const 0 + i32.load offset=1048 + tee_local 7 + i32.le_u + br_if 13 (;@23;) + get_local 0 + i32.eqz + br_if 4 (;@32;) + get_local 0 + get_local 4 + i32.shl + i32.const 2 + get_local 4 + i32.shl + tee_local 0 + i32.const 0 + get_local 0 + i32.sub + i32.or + i32.and + tee_local 0 + i32.const 0 + get_local 0 + i32.sub + i32.and + i32.const -1 + i32.add + tee_local 0 + get_local 0 + i32.const 12 + i32.shr_u + i32.const 16 + i32.and + tee_local 0 + i32.shr_u + tee_local 4 + i32.const 5 + i32.shr_u + i32.const 8 + i32.and + tee_local 5 + get_local 0 + i32.or + get_local 4 + get_local 5 + i32.shr_u + tee_local 0 + i32.const 2 + i32.shr_u + i32.const 4 + i32.and + tee_local 4 + i32.or + get_local 0 + get_local 4 + i32.shr_u + tee_local 0 + i32.const 1 + i32.shr_u + i32.const 2 + i32.and + tee_local 4 + i32.or + get_local 0 + get_local 4 + i32.shr_u + tee_local 0 + i32.const 1 + i32.shr_u + i32.const 1 + i32.and + tee_local 4 + i32.or + get_local 0 + get_local 4 + i32.shr_u + i32.add + tee_local 5 + i32.const 3 + i32.shl + tee_local 6 + i32.const 1088 + i32.add + i32.load + tee_local 4 + i32.load offset=8 + tee_local 0 + get_local 6 + i32.const 1080 + i32.add + tee_local 6 + i32.eq + br_if 6 (;@30;) + get_local 0 + get_local 6 + i32.store offset=12 + get_local 6 + i32.const 8 + i32.add + get_local 0 + i32.store + br 7 (;@29;) + end + i32.const 0 + get_local 2 + i32.const -2 + get_local 5 + i32.rotl + i32.and + i32.store offset=1040 + end + get_local 4 + get_local 5 + i32.const 3 + i32.shl + tee_local 5 + i32.const 3 + i32.or + i32.store offset=4 + get_local 4 + get_local 5 + i32.add + tee_local 4 + get_local 4 + i32.load offset=4 + i32.const 1 + i32.or + i32.store offset=4 + br 33 (;@1;) + end + i32.const 0 + set_local 0 + i32.const 0 + set_local 9 + i32.const 0 + i32.const 0 + i32.or + br_if 7 (;@26;) + end + i32.const 2 + get_local 8 + i32.shl + tee_local 0 + i32.const 0 + get_local 0 + i32.sub + i32.or + get_local 7 + i32.and + tee_local 0 + i32.eqz + br_if 9 (;@23;) + get_local 0 + i32.const 0 + get_local 0 + i32.sub + i32.and + i32.const -1 + i32.add + tee_local 0 + get_local 0 + i32.const 12 + i32.shr_u + i32.const 16 + i32.and + tee_local 0 + i32.shr_u + tee_local 4 + i32.const 5 + i32.shr_u + i32.const 8 + i32.and + tee_local 6 + get_local 0 + i32.or + get_local 4 + get_local 6 + i32.shr_u + tee_local 0 + i32.const 2 + i32.shr_u + i32.const 4 + i32.and + tee_local 4 + i32.or + get_local 0 + get_local 4 + i32.shr_u + tee_local 0 + i32.const 1 + i32.shr_u + i32.const 2 + i32.and + tee_local 4 + i32.or + get_local 0 + get_local 4 + i32.shr_u + tee_local 0 + i32.const 1 + i32.shr_u + i32.const 1 + i32.and + tee_local 4 + i32.or + get_local 0 + get_local 4 + i32.shr_u + i32.add + i32.const 2 + i32.shl + i32.const 1344 + i32.add + i32.load + tee_local 0 + br_if 7 (;@25;) + br 8 (;@24;) + end + i32.const 0 + i32.load offset=1044 + tee_local 10 + i32.eqz + br_if 8 (;@23;) + get_local 10 + i32.const 0 + get_local 10 + i32.sub + i32.and + i32.const -1 + i32.add + tee_local 0 + get_local 0 + i32.const 12 + i32.shr_u + i32.const 16 + i32.and + tee_local 0 + i32.shr_u + tee_local 4 + i32.const 5 + i32.shr_u + i32.const 8 + i32.and + tee_local 5 + get_local 0 + i32.or + get_local 4 + get_local 5 + i32.shr_u + tee_local 0 + i32.const 2 + i32.shr_u + i32.const 4 + i32.and + tee_local 4 + i32.or + get_local 0 + get_local 4 + i32.shr_u + tee_local 0 + i32.const 1 + i32.shr_u + i32.const 2 + i32.and + tee_local 4 + i32.or + get_local 0 + get_local 4 + i32.shr_u + tee_local 0 + i32.const 1 + i32.shr_u + i32.const 1 + i32.and + tee_local 4 + i32.or + get_local 0 + get_local 4 + i32.shr_u + i32.add + i32.const 2 + i32.shl + i32.const 1344 + i32.add + i32.load + tee_local 6 + i32.load offset=4 + i32.const -8 + i32.and + get_local 3 + i32.sub + set_local 5 + get_local 6 + tee_local 9 + i32.load offset=16 + tee_local 0 + i32.eqz + br_if 3 (;@28;) + i32.const 1 + set_local 4 + br 4 (;@27;) + end + i32.const 0 + set_local 5 + get_local 4 + set_local 9 + get_local 4 + set_local 0 + br 5 (;@25;) + end + i32.const 0 + get_local 2 + i32.const -2 + get_local 5 + i32.rotl + i32.and + tee_local 2 + i32.store offset=1040 + end + get_local 4 + i32.const 8 + i32.add + set_local 0 + get_local 4 + get_local 3 + i32.const 3 + i32.or + i32.store offset=4 + get_local 4 + get_local 5 + i32.const 3 + i32.shl + tee_local 5 + i32.add + get_local 5 + get_local 3 + i32.sub + tee_local 5 + i32.store + get_local 4 + get_local 3 + i32.add + tee_local 6 + get_local 5 + i32.const 1 + i32.or + i32.store offset=4 + block ;; label = @29 + get_local 7 + i32.eqz + br_if 0 (;@29;) + get_local 7 + i32.const 3 + i32.shr_u + tee_local 9 + i32.const 3 + i32.shl + i32.const 1080 + i32.add + set_local 3 + i32.const 0 + i32.load offset=1060 + set_local 4 + block ;; label = @30 + block ;; label = @31 + get_local 2 + i32.const 1 + get_local 9 + i32.shl + tee_local 9 + i32.and + i32.eqz + br_if 0 (;@31;) + get_local 3 + i32.load offset=8 + set_local 9 + br 1 (;@30;) + end + i32.const 0 + get_local 2 + get_local 9 + i32.or + i32.store offset=1040 + get_local 3 + set_local 9 + end + get_local 9 + get_local 4 + i32.store offset=12 + get_local 3 + get_local 4 + i32.store offset=8 + get_local 4 + get_local 3 + i32.store offset=12 + get_local 4 + get_local 9 + i32.store offset=8 + end + i32.const 0 + get_local 6 + i32.store offset=1060 + i32.const 0 + get_local 5 + i32.store offset=1048 + br 27 (;@1;) + end + i32.const 0 + set_local 4 + end + block ;; label = @27 + block ;; label = @28 + loop ;; label = @29 + block ;; label = @30 + block ;; label = @31 + block ;; label = @32 + block ;; label = @33 + get_local 4 + br_table 1 (;@32;) 0 (;@33;) 0 (;@33;) + end + get_local 0 + i32.load offset=4 + i32.const -8 + i32.and + get_local 3 + i32.sub + tee_local 4 + get_local 5 + get_local 4 + get_local 5 + i32.lt_u + tee_local 4 + select + set_local 5 + get_local 0 + get_local 6 + get_local 4 + select + set_local 6 + get_local 0 + tee_local 9 + i32.load offset=16 + tee_local 0 + br_if 1 (;@31;) + i32.const 0 + set_local 4 + br 3 (;@29;) + end + get_local 9 + i32.const 20 + i32.add + i32.load + tee_local 0 + br_if 1 (;@30;) + get_local 6 + get_local 3 + i32.add + tee_local 11 + get_local 6 + i32.le_u + br_if 8 (;@23;) + get_local 6 + i32.load offset=24 + set_local 12 + block ;; label = @32 + get_local 6 + i32.load offset=12 + tee_local 9 + get_local 6 + i32.eq + br_if 0 (;@32;) + get_local 6 + i32.load offset=8 + tee_local 0 + get_local 9 + i32.store offset=12 + get_local 9 + get_local 0 + i32.store offset=8 + get_local 12 + br_if 4 (;@28;) + br 5 (;@27;) + end + block ;; label = @32 + block ;; label = @33 + get_local 6 + i32.const 20 + i32.add + tee_local 4 + i32.load + tee_local 0 + br_if 0 (;@33;) + get_local 6 + i32.load offset=16 + tee_local 0 + i32.eqz + br_if 1 (;@32;) + get_local 6 + i32.const 16 + i32.add + set_local 4 + end + loop ;; label = @33 + get_local 4 + set_local 8 + get_local 0 + tee_local 9 + i32.const 20 + i32.add + tee_local 4 + i32.load + tee_local 0 + br_if 0 (;@33;) + get_local 9 + i32.const 16 + i32.add + set_local 4 + get_local 9 + i32.load offset=16 + tee_local 0 + br_if 0 (;@33;) + end + get_local 8 + i32.const 0 + i32.store + get_local 12 + i32.eqz + br_if 5 (;@27;) + br 4 (;@28;) + end + i32.const 0 + set_local 9 + get_local 12 + br_if 3 (;@28;) + br 4 (;@27;) + end + i32.const 1 + set_local 4 + br 1 (;@29;) + end + i32.const 1 + set_local 4 + br 0 (;@29;) + end + end + block ;; label = @28 + block ;; label = @29 + block ;; label = @30 + get_local 6 + get_local 6 + i32.load offset=28 + tee_local 4 + i32.const 2 + i32.shl + i32.const 1344 + i32.add + tee_local 0 + i32.load + i32.eq + br_if 0 (;@30;) + get_local 12 + i32.const 16 + i32.const 20 + get_local 12 + i32.load offset=16 + get_local 6 + i32.eq + select + i32.add + get_local 9 + i32.store + get_local 9 + br_if 1 (;@29;) + br 3 (;@27;) + end + get_local 0 + get_local 9 + i32.store + get_local 9 + i32.eqz + br_if 1 (;@28;) + end + get_local 9 + get_local 12 + i32.store offset=24 + block ;; label = @29 + get_local 6 + i32.load offset=16 + tee_local 0 + i32.eqz + br_if 0 (;@29;) + get_local 9 + get_local 0 + i32.store offset=16 + get_local 0 + get_local 9 + i32.store offset=24 + end + get_local 6 + i32.const 20 + i32.add + i32.load + tee_local 0 + i32.eqz + br_if 1 (;@27;) + get_local 9 + i32.const 20 + i32.add + get_local 0 + i32.store + get_local 0 + get_local 9 + i32.store offset=24 + br 1 (;@27;) + end + i32.const 0 + get_local 10 + i32.const -2 + get_local 4 + i32.rotl + i32.and + i32.store offset=1044 + end + block ;; label = @27 + block ;; label = @28 + get_local 5 + i32.const 15 + i32.gt_u + br_if 0 (;@28;) + get_local 6 + get_local 5 + get_local 3 + i32.add + tee_local 0 + i32.const 3 + i32.or + i32.store offset=4 + get_local 6 + get_local 0 + i32.add + tee_local 0 + get_local 0 + i32.load offset=4 + i32.const 1 + i32.or + i32.store offset=4 + br 1 (;@27;) + end + get_local 11 + get_local 5 + i32.const 1 + i32.or + i32.store offset=4 + get_local 6 + get_local 3 + i32.const 3 + i32.or + i32.store offset=4 + get_local 11 + get_local 5 + i32.add + get_local 5 + i32.store + block ;; label = @28 + get_local 7 + i32.eqz + br_if 0 (;@28;) + get_local 7 + i32.const 3 + i32.shr_u + tee_local 3 + i32.const 3 + i32.shl + i32.const 1080 + i32.add + set_local 4 + i32.const 0 + i32.load offset=1060 + set_local 0 + block ;; label = @29 + block ;; label = @30 + i32.const 1 + get_local 3 + i32.shl + tee_local 3 + get_local 2 + i32.and + i32.eqz + br_if 0 (;@30;) + get_local 4 + i32.load offset=8 + set_local 3 + br 1 (;@29;) + end + i32.const 0 + get_local 3 + get_local 2 + i32.or + i32.store offset=1040 + get_local 4 + set_local 3 + end + get_local 3 + get_local 0 + i32.store offset=12 + get_local 4 + get_local 0 + i32.store offset=8 + get_local 0 + get_local 4 + i32.store offset=12 + get_local 0 + get_local 3 + i32.store offset=8 + end + i32.const 0 + get_local 11 + i32.store offset=1060 + i32.const 0 + get_local 5 + i32.store offset=1048 + end + get_local 6 + i32.const 8 + i32.add + set_local 0 + br 25 (;@1;) + end + get_local 0 + i32.eqz + br_if 1 (;@24;) + end + loop ;; label = @25 + get_local 0 + i32.load offset=4 + i32.const -8 + i32.and + get_local 3 + i32.sub + tee_local 2 + get_local 5 + i32.lt_u + set_local 6 + block ;; label = @26 + get_local 0 + i32.load offset=16 + tee_local 4 + br_if 0 (;@26;) + get_local 0 + i32.const 20 + i32.add + i32.load + set_local 4 + end + get_local 2 + get_local 5 + get_local 6 + select + set_local 5 + get_local 0 + get_local 9 + get_local 6 + select + set_local 9 + get_local 4 + set_local 0 + get_local 4 + br_if 0 (;@25;) + end + end + get_local 9 + i32.eqz + br_if 0 (;@23;) + get_local 5 + i32.const 0 + i32.load offset=1048 + get_local 3 + i32.sub + i32.ge_u + br_if 0 (;@23;) + get_local 9 + get_local 3 + i32.add + tee_local 8 + get_local 9 + i32.le_u + br_if 0 (;@23;) + get_local 9 + i32.load offset=24 + set_local 10 + get_local 9 + i32.load offset=12 + tee_local 6 + get_local 9 + i32.eq + br_if 1 (;@22;) + get_local 9 + i32.load offset=8 + tee_local 0 + get_local 6 + i32.store offset=12 + get_local 6 + get_local 0 + i32.store offset=8 + get_local 10 + br_if 20 (;@3;) + br 21 (;@2;) + end + block ;; label = @23 + block ;; label = @24 + block ;; label = @25 + block ;; label = @26 + block ;; label = @27 + block ;; label = @28 + i32.const 0 + i32.load offset=1048 + tee_local 0 + get_local 3 + i32.ge_u + br_if 0 (;@28;) + i32.const 0 + i32.load offset=1052 + tee_local 6 + get_local 3 + i32.le_u + br_if 1 (;@27;) + i32.const 0 + i32.load offset=1064 + tee_local 0 + get_local 3 + i32.add + tee_local 4 + get_local 6 + get_local 3 + i32.sub + tee_local 5 + i32.const 1 + i32.or + i32.store offset=4 + i32.const 0 + get_local 5 + i32.store offset=1052 + i32.const 0 + get_local 4 + i32.store offset=1064 + get_local 0 + get_local 3 + i32.const 3 + i32.or + i32.store offset=4 + get_local 0 + i32.const 8 + i32.add + set_local 0 + br 27 (;@1;) + end + i32.const 0 + i32.load offset=1060 + set_local 4 + get_local 0 + get_local 3 + i32.sub + tee_local 5 + i32.const 16 + i32.lt_u + br_if 1 (;@26;) + get_local 4 + get_local 3 + i32.add + tee_local 6 + get_local 5 + i32.const 1 + i32.or + i32.store offset=4 + i32.const 0 + get_local 5 + i32.store offset=1048 + i32.const 0 + get_local 6 + i32.store offset=1060 + get_local 4 + get_local 0 + i32.add + get_local 5 + i32.store + get_local 4 + get_local 3 + i32.const 3 + i32.or + i32.store offset=4 + br 2 (;@25;) + end + i32.const 0 + i32.load offset=1512 + i32.eqz + br_if 2 (;@24;) + i32.const 0 + i32.load offset=1520 + set_local 4 + br 3 (;@23;) + end + get_local 4 + get_local 0 + i32.const 3 + i32.or + i32.store offset=4 + get_local 4 + get_local 0 + i32.add + tee_local 0 + get_local 0 + i32.load offset=4 + i32.const 1 + i32.or + i32.store offset=4 + i32.const 0 + i32.const 0 + i32.store offset=1060 + i32.const 0 + i32.const 0 + i32.store offset=1048 + end + get_local 4 + i32.const 8 + i32.add + set_local 0 + br 23 (;@1;) + end + i32.const 0 + i64.const -1 + i64.store offset=1524 align=4 + i32.const 0 + i64.const 281474976776192 + i64.store offset=1516 align=4 + i32.const 0 + get_local 1 + i32.const 12 + i32.add + i32.const -16 + i32.and + i32.const 1431655768 + i32.xor + i32.store offset=1512 + i32.const 0 + i32.const 0 + i32.store offset=1532 + i32.const 0 + i32.const 0 + i32.store offset=1484 + i32.const 65536 + set_local 4 + end + i32.const 0 + set_local 0 + block ;; label = @23 + block ;; label = @24 + get_local 4 + get_local 3 + i32.const 47 + i32.add + tee_local 7 + i32.add + tee_local 2 + i32.const 0 + get_local 4 + i32.sub + tee_local 8 + i32.and + tee_local 9 + get_local 3 + i32.le_u + br_if 0 (;@24;) + block ;; label = @25 + i32.const 0 + i32.load offset=1480 + tee_local 0 + i32.eqz + br_if 0 (;@25;) + i32.const 0 + i32.load offset=1472 + tee_local 4 + get_local 9 + i32.add + tee_local 5 + get_local 4 + i32.le_u + br_if 2 (;@23;) + get_local 5 + get_local 0 + i32.gt_u + br_if 2 (;@23;) + end + i32.const 0 + i32.load8_u offset=1484 + i32.const 4 + i32.and + br_if 10 (;@14;) + block ;; label = @25 + i32.const 0 + i32.load offset=1064 + tee_local 4 + i32.eqz + br_if 0 (;@25;) + i32.const 1488 + set_local 0 + loop ;; label = @26 + block ;; label = @27 + get_local 0 + i32.load + tee_local 5 + get_local 4 + i32.gt_u + br_if 0 (;@27;) + get_local 5 + get_local 0 + i32.load offset=4 + i32.add + get_local 4 + i32.gt_u + br_if 6 (;@21;) + end + get_local 0 + i32.load offset=8 + tee_local 0 + br_if 0 (;@26;) + end + end + i32.const 0 + call 25 + tee_local 6 + i32.const -1 + i32.eq + br_if 9 (;@15;) + get_local 9 + set_local 2 + block ;; label = @25 + i32.const 0 + i32.load offset=1516 + tee_local 0 + i32.const -1 + i32.add + tee_local 4 + get_local 6 + i32.and + i32.eqz + br_if 0 (;@25;) + get_local 9 + get_local 6 + i32.sub + get_local 4 + get_local 6 + i32.add + i32.const 0 + get_local 0 + i32.sub + i32.and + i32.add + set_local 2 + end + get_local 2 + get_local 3 + i32.le_u + br_if 9 (;@15;) + get_local 2 + i32.const 2147483646 + i32.gt_u + br_if 9 (;@15;) + block ;; label = @25 + i32.const 0 + i32.load offset=1480 + tee_local 0 + i32.eqz + br_if 0 (;@25;) + i32.const 0 + i32.load offset=1472 + tee_local 4 + get_local 2 + i32.add + tee_local 5 + get_local 4 + i32.le_u + br_if 10 (;@15;) + get_local 5 + get_local 0 + i32.gt_u + br_if 10 (;@15;) + end + get_local 2 + call 25 + tee_local 0 + get_local 6 + i32.ne + br_if 4 (;@20;) + br 11 (;@13;) + end + i32.const 0 + i32.const 48 + i32.store offset=1536 + br 22 (;@1;) + end + i32.const 0 + set_local 0 + i32.const 0 + i32.const 48 + i32.store offset=1536 + br 21 (;@1;) + end + block ;; label = @22 + get_local 9 + i32.const 20 + i32.add + tee_local 4 + i32.load + tee_local 0 + br_if 0 (;@22;) + get_local 9 + i32.load offset=16 + tee_local 0 + i32.eqz + br_if 3 (;@19;) + get_local 9 + i32.const 16 + i32.add + set_local 4 + end + loop ;; label = @22 + get_local 4 + set_local 2 + get_local 0 + tee_local 6 + i32.const 20 + i32.add + tee_local 4 + i32.load + tee_local 0 + br_if 0 (;@22;) + get_local 6 + i32.const 16 + i32.add + set_local 4 + get_local 6 + i32.load offset=16 + tee_local 0 + br_if 0 (;@22;) + end + get_local 2 + i32.const 0 + i32.store + get_local 10 + i32.eqz + br_if 19 (;@2;) + br 18 (;@3;) + end + get_local 2 + get_local 6 + i32.sub + get_local 8 + i32.and + tee_local 2 + i32.const 2147483646 + i32.gt_u + br_if 5 (;@15;) + get_local 2 + call 25 + tee_local 6 + get_local 0 + i32.load + get_local 0 + i32.load offset=4 + i32.add + i32.eq + br_if 3 (;@17;) + get_local 6 + set_local 0 + end + get_local 0 + set_local 6 + get_local 3 + i32.const 48 + i32.add + get_local 2 + i32.le_u + br_if 1 (;@18;) + get_local 2 + i32.const 2147483646 + i32.gt_u + br_if 1 (;@18;) + get_local 6 + i32.const -1 + i32.eq + br_if 1 (;@18;) + get_local 7 + get_local 2 + i32.sub + i32.const 0 + i32.load offset=1520 + tee_local 0 + i32.add + i32.const 0 + get_local 0 + i32.sub + i32.and + tee_local 0 + i32.const 2147483646 + i32.gt_u + br_if 6 (;@13;) + get_local 0 + call 25 + i32.const -1 + i32.eq + br_if 3 (;@16;) + get_local 0 + get_local 2 + i32.add + set_local 2 + br 6 (;@13;) + end + i32.const 0 + set_local 6 + get_local 10 + br_if 15 (;@3;) + br 16 (;@2;) + end + get_local 6 + i32.const -1 + i32.ne + br_if 4 (;@13;) + br 2 (;@15;) + end + get_local 6 + i32.const -1 + i32.ne + br_if 3 (;@13;) + br 1 (;@15;) + end + i32.const 0 + get_local 2 + i32.sub + call 25 + drop + end + i32.const 0 + i32.const 0 + i32.load offset=1484 + i32.const 4 + i32.or + i32.store offset=1484 + end + get_local 9 + i32.const 2147483646 + i32.gt_u + br_if 1 (;@12;) + get_local 9 + call 25 + tee_local 6 + i32.const 0 + call 25 + tee_local 0 + i32.ge_u + br_if 1 (;@12;) + get_local 6 + i32.const -1 + i32.eq + br_if 1 (;@12;) + get_local 0 + i32.const -1 + i32.eq + br_if 1 (;@12;) + get_local 0 + get_local 6 + i32.sub + tee_local 2 + get_local 3 + i32.const 40 + i32.add + i32.le_u + br_if 1 (;@12;) + end + i32.const 0 + i32.const 0 + i32.load offset=1472 + get_local 2 + i32.add + tee_local 0 + i32.store offset=1472 + block ;; label = @13 + get_local 0 + i32.const 0 + i32.load offset=1476 + i32.le_u + br_if 0 (;@13;) + i32.const 0 + get_local 0 + i32.store offset=1476 + end + block ;; label = @13 + block ;; label = @14 + block ;; label = @15 + block ;; label = @16 + i32.const 0 + i32.load offset=1064 + tee_local 4 + i32.eqz + br_if 0 (;@16;) + i32.const 1488 + set_local 0 + loop ;; label = @17 + get_local 6 + get_local 0 + i32.load + tee_local 5 + get_local 0 + i32.load offset=4 + tee_local 9 + i32.add + i32.eq + br_if 2 (;@15;) + get_local 0 + i32.load offset=8 + tee_local 0 + br_if 0 (;@17;) + br 3 (;@14;) + end + end + block ;; label = @16 + block ;; label = @17 + i32.const 0 + i32.load offset=1056 + tee_local 0 + i32.eqz + br_if 0 (;@17;) + get_local 6 + get_local 0 + i32.ge_u + br_if 1 (;@16;) + end + i32.const 0 + get_local 6 + i32.store offset=1056 + end + i32.const 0 + set_local 0 + i32.const 0 + get_local 2 + i32.store offset=1492 + i32.const 0 + get_local 6 + i32.store offset=1488 + i32.const 0 + i32.const -1 + i32.store offset=1072 + i32.const 0 + i32.const 0 + i32.load offset=1512 + i32.store offset=1076 + i32.const 0 + i32.const 0 + i32.store offset=1500 + loop ;; label = @16 + get_local 0 + i32.const 1088 + i32.add + get_local 0 + i32.const 1080 + i32.add + tee_local 4 + i32.store + get_local 0 + i32.const 1092 + i32.add + get_local 4 + i32.store + get_local 0 + i32.const 8 + i32.add + tee_local 0 + i32.const 256 + i32.ne + br_if 0 (;@16;) + end + get_local 6 + i32.const -8 + get_local 6 + i32.sub + i32.const 7 + i32.and + i32.const 0 + get_local 6 + i32.const 8 + i32.add + i32.const 7 + i32.and + select + tee_local 0 + i32.add + tee_local 4 + get_local 2 + i32.const -40 + i32.add + tee_local 5 + get_local 0 + i32.sub + tee_local 0 + i32.const 1 + i32.or + i32.store offset=4 + i32.const 0 + i32.const 0 + i32.load offset=1528 + i32.store offset=1068 + i32.const 0 + get_local 0 + i32.store offset=1052 + i32.const 0 + get_local 4 + i32.store offset=1064 + get_local 6 + get_local 5 + i32.add + i32.const 40 + i32.store offset=4 + br 2 (;@13;) + end + get_local 0 + i32.load8_u offset=12 + i32.const 8 + i32.and + br_if 0 (;@14;) + get_local 6 + get_local 4 + i32.le_u + br_if 0 (;@14;) + get_local 5 + get_local 4 + i32.gt_u + br_if 0 (;@14;) + get_local 4 + i32.const -8 + get_local 4 + i32.sub + i32.const 7 + i32.and + i32.const 0 + get_local 4 + i32.const 8 + i32.add + i32.const 7 + i32.and + select + tee_local 5 + i32.add + tee_local 6 + i32.const 0 + i32.load offset=1052 + get_local 2 + i32.add + tee_local 8 + get_local 5 + i32.sub + tee_local 5 + i32.const 1 + i32.or + i32.store offset=4 + get_local 0 + i32.const 4 + i32.add + get_local 9 + get_local 2 + i32.add + i32.store + i32.const 0 + i32.const 0 + i32.load offset=1528 + i32.store offset=1068 + i32.const 0 + get_local 5 + i32.store offset=1052 + i32.const 0 + get_local 6 + i32.store offset=1064 + get_local 4 + get_local 8 + i32.add + i32.const 40 + i32.store offset=4 + br 1 (;@13;) + end + block ;; label = @14 + get_local 6 + i32.const 0 + i32.load offset=1056 + i32.ge_u + br_if 0 (;@14;) + i32.const 0 + get_local 6 + i32.store offset=1056 + end + get_local 6 + get_local 2 + i32.add + set_local 5 + i32.const 1488 + set_local 0 + block ;; label = @14 + block ;; label = @15 + block ;; label = @16 + block ;; label = @17 + block ;; label = @18 + block ;; label = @19 + block ;; label = @20 + block ;; label = @21 + loop ;; label = @22 + get_local 0 + i32.load + get_local 5 + i32.eq + br_if 1 (;@21;) + get_local 0 + i32.load offset=8 + tee_local 0 + br_if 0 (;@22;) + br 2 (;@20;) + end + end + get_local 0 + i32.load8_u offset=12 + i32.const 8 + i32.and + br_if 0 (;@20;) + get_local 0 + get_local 6 + i32.store + get_local 0 + get_local 0 + i32.load offset=4 + get_local 2 + i32.add + i32.store offset=4 + get_local 6 + i32.const -8 + get_local 6 + i32.sub + i32.const 7 + i32.and + i32.const 0 + get_local 6 + i32.const 8 + i32.add + i32.const 7 + i32.and + select + i32.add + tee_local 2 + get_local 3 + i32.const 3 + i32.or + i32.store offset=4 + get_local 5 + i32.const -8 + get_local 5 + i32.sub + i32.const 7 + i32.and + i32.const 0 + get_local 5 + i32.const 8 + i32.add + i32.const 7 + i32.and + select + i32.add + tee_local 6 + get_local 2 + i32.sub + get_local 3 + i32.sub + set_local 0 + get_local 2 + get_local 3 + i32.add + set_local 5 + get_local 4 + get_local 6 + i32.eq + br_if 1 (;@19;) + i32.const 0 + i32.load offset=1060 + get_local 6 + i32.eq + br_if 9 (;@11;) + get_local 6 + i32.load offset=4 + tee_local 4 + i32.const 3 + i32.and + i32.const 1 + i32.ne + br_if 15 (;@5;) + get_local 4 + i32.const -8 + i32.and + set_local 7 + get_local 4 + i32.const 255 + i32.gt_u + br_if 10 (;@10;) + get_local 6 + i32.load offset=12 + tee_local 3 + get_local 6 + i32.load offset=8 + tee_local 9 + i32.eq + br_if 11 (;@9;) + get_local 3 + get_local 9 + i32.store offset=8 + get_local 9 + get_local 3 + i32.store offset=12 + br 14 (;@6;) + end + i32.const 1488 + set_local 0 + block ;; label = @20 + loop ;; label = @21 + block ;; label = @22 + get_local 0 + i32.load + tee_local 5 + get_local 4 + i32.gt_u + br_if 0 (;@22;) + get_local 5 + get_local 0 + i32.load offset=4 + i32.add + tee_local 5 + get_local 4 + i32.gt_u + br_if 2 (;@20;) + end + get_local 0 + i32.load offset=8 + set_local 0 + br 0 (;@21;) + end + end + get_local 6 + i32.const -8 + get_local 6 + i32.sub + i32.const 7 + i32.and + i32.const 0 + get_local 6 + i32.const 8 + i32.add + i32.const 7 + i32.and + select + tee_local 0 + i32.add + tee_local 8 + get_local 2 + i32.const -40 + i32.add + tee_local 9 + get_local 0 + i32.sub + tee_local 0 + i32.const 1 + i32.or + i32.store offset=4 + get_local 6 + get_local 9 + i32.add + i32.const 40 + i32.store offset=4 + get_local 4 + get_local 5 + i32.const 39 + get_local 5 + i32.sub + i32.const 7 + i32.and + i32.const 0 + get_local 5 + i32.const -39 + i32.add + i32.const 7 + i32.and + select + i32.add + i32.const -47 + i32.add + tee_local 9 + get_local 9 + get_local 4 + i32.const 16 + i32.add + i32.lt_u + select + tee_local 9 + i32.const 27 + i32.store offset=4 + i32.const 0 + i32.const 0 + i32.load offset=1528 + i32.store offset=1068 + i32.const 0 + get_local 0 + i32.store offset=1052 + i32.const 0 + get_local 8 + i32.store offset=1064 + get_local 9 + i32.const 16 + i32.add + i32.const 0 + i64.load offset=1496 align=4 + i64.store align=4 + get_local 9 + i32.const 0 + i64.load offset=1488 align=4 + i64.store offset=8 align=4 + i32.const 0 + get_local 2 + i32.store offset=1492 + i32.const 0 + get_local 6 + i32.store offset=1488 + i32.const 0 + get_local 9 + i32.const 8 + i32.add + i32.store offset=1496 + i32.const 0 + i32.const 0 + i32.store offset=1500 + get_local 9 + i32.const 28 + i32.add + set_local 0 + loop ;; label = @20 + get_local 0 + i32.const 7 + i32.store + get_local 0 + i32.const 4 + i32.add + tee_local 0 + get_local 5 + i32.lt_u + br_if 0 (;@20;) + end + get_local 9 + get_local 4 + i32.eq + br_if 6 (;@13;) + get_local 9 + i32.const 4 + i32.add + tee_local 0 + get_local 0 + i32.load + i32.const -2 + i32.and + i32.store + get_local 9 + get_local 9 + get_local 4 + i32.sub + tee_local 2 + i32.store + get_local 4 + get_local 2 + i32.const 1 + i32.or + i32.store offset=4 + block ;; label = @20 + get_local 2 + i32.const 255 + i32.gt_u + br_if 0 (;@20;) + get_local 2 + i32.const 3 + i32.shr_u + tee_local 5 + i32.const 3 + i32.shl + i32.const 1080 + i32.add + set_local 0 + i32.const 0 + i32.load offset=1040 + tee_local 6 + i32.const 1 + get_local 5 + i32.shl + tee_local 5 + i32.and + i32.eqz + br_if 2 (;@18;) + get_local 0 + i32.load offset=8 + set_local 5 + br 3 (;@17;) + end + i32.const 0 + set_local 0 + block ;; label = @20 + get_local 2 + i32.const 8 + i32.shr_u + tee_local 5 + i32.eqz + br_if 0 (;@20;) + i32.const 31 + set_local 0 + get_local 2 + i32.const 16777215 + i32.gt_u + br_if 0 (;@20;) + get_local 2 + i32.const 14 + get_local 5 + get_local 5 + i32.const 1048320 + i32.add + i32.const 16 + i32.shr_u + i32.const 8 + i32.and + tee_local 0 + i32.shl + tee_local 5 + i32.const 520192 + i32.add + i32.const 16 + i32.shr_u + i32.const 4 + i32.and + tee_local 6 + get_local 0 + i32.or + get_local 5 + get_local 6 + i32.shl + tee_local 0 + i32.const 245760 + i32.add + i32.const 16 + i32.shr_u + i32.const 2 + i32.and + tee_local 5 + i32.or + i32.sub + get_local 0 + get_local 5 + i32.shl + i32.const 15 + i32.shr_u + i32.add + tee_local 0 + i32.const 7 + i32.add + i32.shr_u + i32.const 1 + i32.and + get_local 0 + i32.const 1 + i32.shl + i32.or + set_local 0 + end + get_local 4 + i64.const 0 + i64.store offset=16 align=4 + get_local 4 + i32.const 28 + i32.add + get_local 0 + i32.store + get_local 0 + i32.const 2 + i32.shl + i32.const 1344 + i32.add + set_local 5 + i32.const 0 + i32.load offset=1044 + tee_local 6 + i32.const 1 + get_local 0 + i32.shl + tee_local 9 + i32.and + i32.eqz + br_if 3 (;@16;) + get_local 2 + i32.const 0 + i32.const 25 + get_local 0 + i32.const 1 + i32.shr_u + i32.sub + get_local 0 + i32.const 31 + i32.eq + select + i32.shl + set_local 0 + get_local 5 + i32.load + set_local 6 + loop ;; label = @20 + get_local 6 + tee_local 5 + i32.load offset=4 + i32.const -8 + i32.and + get_local 2 + i32.eq + br_if 6 (;@14;) + get_local 0 + i32.const 29 + i32.shr_u + set_local 6 + get_local 0 + i32.const 1 + i32.shl + set_local 0 + get_local 5 + get_local 6 + i32.const 4 + i32.and + i32.add + i32.const 16 + i32.add + tee_local 9 + i32.load + tee_local 6 + br_if 0 (;@20;) + end + get_local 9 + get_local 4 + i32.store + get_local 4 + i32.const 24 + i32.add + get_local 5 + i32.store + br 4 (;@15;) + end + i32.const 0 + get_local 5 + i32.store offset=1064 + i32.const 0 + i32.const 0 + i32.load offset=1052 + get_local 0 + i32.add + tee_local 0 + i32.store offset=1052 + get_local 5 + get_local 0 + i32.const 1 + i32.or + i32.store offset=4 + br 14 (;@4;) + end + i32.const 0 + get_local 6 + get_local 5 + i32.or + i32.store offset=1040 + get_local 0 + set_local 5 + end + get_local 5 + get_local 4 + i32.store offset=12 + get_local 0 + get_local 4 + i32.store offset=8 + get_local 4 + get_local 0 + i32.store offset=12 + get_local 4 + get_local 5 + i32.store offset=8 + br 3 (;@13;) + end + get_local 5 + get_local 4 + i32.store + i32.const 0 + get_local 6 + get_local 9 + i32.or + i32.store offset=1044 + get_local 4 + i32.const 24 + i32.add + get_local 5 + i32.store + end + get_local 4 + get_local 4 + i32.store offset=12 + get_local 4 + get_local 4 + i32.store offset=8 + br 1 (;@13;) + end + get_local 5 + i32.load offset=8 + tee_local 0 + get_local 4 + i32.store offset=12 + get_local 5 + get_local 4 + i32.store offset=8 + get_local 4 + i32.const 24 + i32.add + i32.const 0 + i32.store + get_local 4 + get_local 5 + i32.store offset=12 + get_local 4 + get_local 0 + i32.store offset=8 + end + i32.const 0 + i32.load offset=1052 + tee_local 0 + get_local 3 + i32.le_u + br_if 0 (;@12;) + i32.const 0 + i32.load offset=1064 + tee_local 4 + get_local 3 + i32.add + tee_local 5 + get_local 0 + get_local 3 + i32.sub + tee_local 0 + i32.const 1 + i32.or + i32.store offset=4 + i32.const 0 + get_local 0 + i32.store offset=1052 + i32.const 0 + get_local 5 + i32.store offset=1064 + get_local 4 + get_local 3 + i32.const 3 + i32.or + i32.store offset=4 + get_local 4 + i32.const 8 + i32.add + set_local 0 + br 11 (;@1;) + end + i32.const 0 + set_local 0 + i32.const 0 + i32.const 48 + i32.store offset=1536 + br 10 (;@1;) + end + i32.const 0 + get_local 5 + i32.store offset=1060 + i32.const 0 + i32.const 0 + i32.load offset=1048 + get_local 0 + i32.add + tee_local 0 + i32.store offset=1048 + get_local 5 + get_local 0 + i32.const 1 + i32.or + i32.store offset=4 + get_local 5 + get_local 0 + i32.add + get_local 0 + i32.store + br 6 (;@4;) + end + get_local 6 + i32.load offset=24 + set_local 10 + get_local 6 + i32.load offset=12 + tee_local 9 + get_local 6 + i32.eq + br_if 1 (;@8;) + get_local 6 + i32.load offset=8 + tee_local 4 + get_local 9 + i32.store offset=12 + get_local 9 + get_local 4 + i32.store offset=8 + get_local 10 + br_if 2 (;@7;) + br 3 (;@6;) + end + i32.const 0 + i32.const 0 + i32.load offset=1040 + i32.const -2 + get_local 4 + i32.const 3 + i32.shr_u + i32.rotl + i32.and + i32.store offset=1040 + br 2 (;@6;) + end + block ;; label = @8 + block ;; label = @9 + get_local 6 + i32.const 20 + i32.add + tee_local 4 + i32.load + tee_local 3 + br_if 0 (;@9;) + get_local 6 + i32.const 16 + i32.add + tee_local 4 + i32.load + tee_local 3 + i32.eqz + br_if 1 (;@8;) + end + loop ;; label = @9 + get_local 4 + set_local 8 + get_local 3 + tee_local 9 + i32.const 20 + i32.add + tee_local 4 + i32.load + tee_local 3 + br_if 0 (;@9;) + get_local 9 + i32.const 16 + i32.add + set_local 4 + get_local 9 + i32.load offset=16 + tee_local 3 + br_if 0 (;@9;) + end + get_local 8 + i32.const 0 + i32.store + get_local 10 + i32.eqz + br_if 2 (;@6;) + br 1 (;@7;) + end + i32.const 0 + set_local 9 + get_local 10 + i32.eqz + br_if 1 (;@6;) + end + block ;; label = @7 + block ;; label = @8 + block ;; label = @9 + get_local 6 + i32.load offset=28 + tee_local 3 + i32.const 2 + i32.shl + i32.const 1344 + i32.add + tee_local 4 + i32.load + get_local 6 + i32.eq + br_if 0 (;@9;) + get_local 10 + i32.const 16 + i32.const 20 + get_local 10 + i32.load offset=16 + get_local 6 + i32.eq + select + i32.add + get_local 9 + i32.store + get_local 9 + br_if 1 (;@8;) + br 3 (;@6;) + end + get_local 4 + get_local 9 + i32.store + get_local 9 + i32.eqz + br_if 1 (;@7;) + end + get_local 9 + get_local 10 + i32.store offset=24 + block ;; label = @8 + get_local 6 + i32.load offset=16 + tee_local 4 + i32.eqz + br_if 0 (;@8;) + get_local 9 + get_local 4 + i32.store offset=16 + get_local 4 + get_local 9 + i32.store offset=24 + end + get_local 6 + i32.const 20 + i32.add + i32.load + tee_local 4 + i32.eqz + br_if 1 (;@6;) + get_local 9 + i32.const 20 + i32.add + get_local 4 + i32.store + get_local 4 + get_local 9 + i32.store offset=24 + br 1 (;@6;) + end + i32.const 0 + i32.const 0 + i32.load offset=1044 + i32.const -2 + get_local 3 + i32.rotl + i32.and + i32.store offset=1044 + end + get_local 7 + get_local 0 + i32.add + set_local 0 + get_local 6 + get_local 7 + i32.add + set_local 6 + end + get_local 6 + get_local 6 + i32.load offset=4 + i32.const -2 + i32.and + i32.store offset=4 + get_local 5 + get_local 0 + i32.add + get_local 0 + i32.store + get_local 5 + get_local 0 + i32.const 1 + i32.or + i32.store offset=4 + block ;; label = @5 + block ;; label = @6 + block ;; label = @7 + block ;; label = @8 + block ;; label = @9 + block ;; label = @10 + get_local 0 + i32.const 255 + i32.gt_u + br_if 0 (;@10;) + get_local 0 + i32.const 3 + i32.shr_u + tee_local 4 + i32.const 3 + i32.shl + i32.const 1080 + i32.add + set_local 0 + i32.const 0 + i32.load offset=1040 + tee_local 3 + i32.const 1 + get_local 4 + i32.shl + tee_local 4 + i32.and + i32.eqz + br_if 1 (;@9;) + get_local 0 + i32.load offset=8 + set_local 4 + br 2 (;@8;) + end + i32.const 0 + set_local 4 + block ;; label = @10 + get_local 0 + i32.const 8 + i32.shr_u + tee_local 3 + i32.eqz + br_if 0 (;@10;) + i32.const 31 + set_local 4 + get_local 0 + i32.const 16777215 + i32.gt_u + br_if 0 (;@10;) + get_local 0 + i32.const 14 + get_local 3 + get_local 3 + i32.const 1048320 + i32.add + i32.const 16 + i32.shr_u + i32.const 8 + i32.and + tee_local 4 + i32.shl + tee_local 3 + i32.const 520192 + i32.add + i32.const 16 + i32.shr_u + i32.const 4 + i32.and + tee_local 6 + get_local 4 + i32.or + get_local 3 + get_local 6 + i32.shl + tee_local 4 + i32.const 245760 + i32.add + i32.const 16 + i32.shr_u + i32.const 2 + i32.and + tee_local 3 + i32.or + i32.sub + get_local 4 + get_local 3 + i32.shl + i32.const 15 + i32.shr_u + i32.add + tee_local 4 + i32.const 7 + i32.add + i32.shr_u + i32.const 1 + i32.and + get_local 4 + i32.const 1 + i32.shl + i32.or + set_local 4 + end + get_local 5 + get_local 4 + i32.store offset=28 + get_local 5 + i64.const 0 + i64.store offset=16 align=4 + get_local 4 + i32.const 2 + i32.shl + i32.const 1344 + i32.add + set_local 3 + i32.const 0 + i32.load offset=1044 + tee_local 6 + i32.const 1 + get_local 4 + i32.shl + tee_local 9 + i32.and + i32.eqz + br_if 2 (;@7;) + get_local 0 + i32.const 0 + i32.const 25 + get_local 4 + i32.const 1 + i32.shr_u + i32.sub + get_local 4 + i32.const 31 + i32.eq + select + i32.shl + set_local 4 + get_local 3 + i32.load + set_local 6 + loop ;; label = @10 + get_local 6 + tee_local 3 + i32.load offset=4 + i32.const -8 + i32.and + get_local 0 + i32.eq + br_if 5 (;@5;) + get_local 4 + i32.const 29 + i32.shr_u + set_local 6 + get_local 4 + i32.const 1 + i32.shl + set_local 4 + get_local 3 + get_local 6 + i32.const 4 + i32.and + i32.add + i32.const 16 + i32.add + tee_local 9 + i32.load + tee_local 6 + br_if 0 (;@10;) + end + get_local 9 + get_local 5 + i32.store + get_local 5 + get_local 3 + i32.store offset=24 + br 3 (;@6;) + end + i32.const 0 + get_local 3 + get_local 4 + i32.or + i32.store offset=1040 + get_local 0 + set_local 4 + end + get_local 4 + get_local 5 + i32.store offset=12 + get_local 0 + get_local 5 + i32.store offset=8 + get_local 5 + get_local 0 + i32.store offset=12 + get_local 5 + get_local 4 + i32.store offset=8 + br 3 (;@4;) + end + get_local 3 + get_local 5 + i32.store + i32.const 0 + get_local 6 + get_local 9 + i32.or + i32.store offset=1044 + get_local 5 + get_local 3 + i32.store offset=24 + end + get_local 5 + get_local 5 + i32.store offset=12 + get_local 5 + get_local 5 + i32.store offset=8 + br 1 (;@4;) + end + get_local 3 + i32.load offset=8 + tee_local 0 + get_local 5 + i32.store offset=12 + get_local 3 + get_local 5 + i32.store offset=8 + get_local 5 + i32.const 0 + i32.store offset=24 + get_local 5 + get_local 3 + i32.store offset=12 + get_local 5 + get_local 0 + i32.store offset=8 + end + get_local 2 + i32.const 8 + i32.add + set_local 0 + br 2 (;@1;) + end + block ;; label = @3 + block ;; label = @4 + block ;; label = @5 + get_local 9 + get_local 9 + i32.load offset=28 + tee_local 4 + i32.const 2 + i32.shl + i32.const 1344 + i32.add + tee_local 0 + i32.load + i32.eq + br_if 0 (;@5;) + get_local 10 + i32.const 16 + i32.const 20 + get_local 10 + i32.load offset=16 + get_local 9 + i32.eq + select + i32.add + get_local 6 + i32.store + get_local 6 + br_if 1 (;@4;) + br 3 (;@2;) + end + get_local 0 + get_local 6 + i32.store + get_local 6 + i32.eqz + br_if 1 (;@3;) + end + get_local 6 + get_local 10 + i32.store offset=24 + block ;; label = @4 + get_local 9 + i32.load offset=16 + tee_local 0 + i32.eqz + br_if 0 (;@4;) + get_local 6 + get_local 0 + i32.store offset=16 + get_local 0 + get_local 6 + i32.store offset=24 + end + get_local 9 + i32.const 20 + i32.add + i32.load + tee_local 0 + i32.eqz + br_if 1 (;@2;) + get_local 6 + i32.const 20 + i32.add + get_local 0 + i32.store + get_local 0 + get_local 6 + i32.store offset=24 + br 1 (;@2;) + end + i32.const 0 + get_local 7 + i32.const -2 + get_local 4 + i32.rotl + i32.and + tee_local 7 + i32.store offset=1044 + end + block ;; label = @2 + block ;; label = @3 + get_local 5 + i32.const 15 + i32.gt_u + br_if 0 (;@3;) + get_local 9 + get_local 5 + get_local 3 + i32.add + tee_local 0 + i32.const 3 + i32.or + i32.store offset=4 + get_local 9 + get_local 0 + i32.add + tee_local 0 + get_local 0 + i32.load offset=4 + i32.const 1 + i32.or + i32.store offset=4 + br 1 (;@2;) + end + get_local 8 + get_local 5 + i32.const 1 + i32.or + i32.store offset=4 + get_local 9 + get_local 3 + i32.const 3 + i32.or + i32.store offset=4 + get_local 8 + get_local 5 + i32.add + get_local 5 + i32.store + block ;; label = @3 + block ;; label = @4 + block ;; label = @5 + block ;; label = @6 + block ;; label = @7 + get_local 5 + i32.const 255 + i32.gt_u + br_if 0 (;@7;) + get_local 5 + i32.const 3 + i32.shr_u + tee_local 4 + i32.const 3 + i32.shl + i32.const 1080 + i32.add + set_local 0 + i32.const 0 + i32.load offset=1040 + tee_local 5 + i32.const 1 + get_local 4 + i32.shl + tee_local 4 + i32.and + i32.eqz + br_if 1 (;@6;) + get_local 0 + i32.load offset=8 + set_local 4 + br 2 (;@5;) + end + get_local 5 + i32.const 8 + i32.shr_u + tee_local 4 + i32.eqz + br_if 2 (;@4;) + i32.const 31 + set_local 0 + get_local 5 + i32.const 16777215 + i32.gt_u + br_if 3 (;@3;) + get_local 5 + i32.const 14 + get_local 4 + get_local 4 + i32.const 1048320 + i32.add + i32.const 16 + i32.shr_u + i32.const 8 + i32.and + tee_local 0 + i32.shl + tee_local 4 + i32.const 520192 + i32.add + i32.const 16 + i32.shr_u + i32.const 4 + i32.and + tee_local 3 + get_local 0 + i32.or + get_local 4 + get_local 3 + i32.shl + tee_local 0 + i32.const 245760 + i32.add + i32.const 16 + i32.shr_u + i32.const 2 + i32.and + tee_local 4 + i32.or + i32.sub + get_local 0 + get_local 4 + i32.shl + i32.const 15 + i32.shr_u + i32.add + tee_local 0 + i32.const 7 + i32.add + i32.shr_u + i32.const 1 + i32.and + get_local 0 + i32.const 1 + i32.shl + i32.or + set_local 0 + br 3 (;@3;) + end + i32.const 0 + get_local 5 + get_local 4 + i32.or + i32.store offset=1040 + get_local 0 + set_local 4 + end + get_local 4 + get_local 8 + i32.store offset=12 + get_local 0 + get_local 8 + i32.store offset=8 + get_local 8 + get_local 0 + i32.store offset=12 + get_local 8 + get_local 4 + i32.store offset=8 + br 2 (;@2;) + end + i32.const 0 + set_local 0 + end + get_local 8 + get_local 0 + i32.store offset=28 + get_local 8 + i64.const 0 + i64.store offset=16 align=4 + get_local 0 + i32.const 2 + i32.shl + i32.const 1344 + i32.add + set_local 4 + block ;; label = @3 + block ;; label = @4 + block ;; label = @5 + get_local 7 + i32.const 1 + get_local 0 + i32.shl + tee_local 3 + i32.and + i32.eqz + br_if 0 (;@5;) + get_local 5 + i32.const 0 + i32.const 25 + get_local 0 + i32.const 1 + i32.shr_u + i32.sub + get_local 0 + i32.const 31 + i32.eq + select + i32.shl + set_local 0 + get_local 4 + i32.load + set_local 3 + loop ;; label = @6 + get_local 3 + tee_local 4 + i32.load offset=4 + i32.const -8 + i32.and + get_local 5 + i32.eq + br_if 3 (;@3;) + get_local 0 + i32.const 29 + i32.shr_u + set_local 3 + get_local 0 + i32.const 1 + i32.shl + set_local 0 + get_local 4 + get_local 3 + i32.const 4 + i32.and + i32.add + i32.const 16 + i32.add + tee_local 6 + i32.load + tee_local 3 + br_if 0 (;@6;) + end + get_local 6 + get_local 8 + i32.store + get_local 8 + get_local 4 + i32.store offset=24 + br 1 (;@4;) + end + get_local 4 + get_local 8 + i32.store + i32.const 0 + get_local 7 + get_local 3 + i32.or + i32.store offset=1044 + get_local 8 + get_local 4 + i32.store offset=24 + end + get_local 8 + get_local 8 + i32.store offset=12 + get_local 8 + get_local 8 + i32.store offset=8 + br 1 (;@2;) + end + get_local 4 + i32.load offset=8 + tee_local 0 + get_local 8 + i32.store offset=12 + get_local 4 + get_local 8 + i32.store offset=8 + get_local 8 + i32.const 0 + i32.store offset=24 + get_local 8 + get_local 4 + i32.store offset=12 + get_local 8 + get_local 0 + i32.store offset=8 + end + get_local 9 + i32.const 8 + i32.add + set_local 0 + end + get_local 1 + i32.const 16 + i32.add + set_global 0 + get_local 0) + (func (;16;) (type 3) (param i32) + get_local 0 + call 17) + (func (;17;) (type 3) (param i32) + (local i32 i32 i32 i32 i32 i32 i32) + block ;; label = @1 + block ;; label = @2 + get_local 0 + i32.eqz + br_if 0 (;@2;) + get_local 0 + i32.const -8 + i32.add + tee_local 1 + get_local 0 + i32.const -4 + i32.add + i32.load + tee_local 2 + i32.const -8 + i32.and + tee_local 0 + i32.add + set_local 3 + block ;; label = @3 + block ;; label = @4 + get_local 2 + i32.const 1 + i32.and + br_if 0 (;@4;) + get_local 2 + i32.const 3 + i32.and + i32.eqz + br_if 2 (;@2;) + get_local 1 + get_local 1 + i32.load + tee_local 2 + i32.sub + tee_local 1 + i32.const 0 + i32.load offset=1056 + i32.lt_u + br_if 2 (;@2;) + get_local 2 + get_local 0 + i32.add + set_local 0 + block ;; label = @5 + block ;; label = @6 + block ;; label = @7 + block ;; label = @8 + block ;; label = @9 + i32.const 0 + i32.load offset=1060 + get_local 1 + i32.eq + br_if 0 (;@9;) + get_local 2 + i32.const 255 + i32.gt_u + br_if 1 (;@8;) + get_local 1 + i32.load offset=12 + tee_local 4 + get_local 1 + i32.load offset=8 + tee_local 5 + i32.eq + br_if 2 (;@7;) + get_local 4 + get_local 5 + i32.store offset=8 + get_local 5 + get_local 4 + i32.store offset=12 + get_local 1 + get_local 3 + i32.lt_u + br_if 6 (;@3;) + br 7 (;@2;) + end + get_local 3 + i32.load offset=4 + tee_local 2 + i32.const 3 + i32.and + i32.const 3 + i32.ne + br_if 4 (;@4;) + get_local 3 + i32.const 4 + i32.add + get_local 2 + i32.const -2 + i32.and + i32.store + i32.const 0 + get_local 0 + i32.store offset=1048 + get_local 1 + get_local 0 + i32.add + get_local 0 + i32.store + get_local 1 + get_local 0 + i32.const 1 + i32.or + i32.store offset=4 + return + end + get_local 1 + i32.load offset=24 + set_local 6 + get_local 1 + i32.load offset=12 + tee_local 5 + get_local 1 + i32.eq + br_if 1 (;@6;) + get_local 1 + i32.load offset=8 + tee_local 2 + get_local 5 + i32.store offset=12 + get_local 5 + get_local 2 + i32.store offset=8 + get_local 6 + br_if 2 (;@5;) + br 3 (;@4;) + end + i32.const 0 + i32.const 0 + i32.load offset=1040 + i32.const -2 + get_local 2 + i32.const 3 + i32.shr_u + i32.rotl + i32.and + i32.store offset=1040 + get_local 1 + get_local 3 + i32.lt_u + br_if 3 (;@3;) + br 4 (;@2;) + end + block ;; label = @6 + block ;; label = @7 + get_local 1 + i32.const 20 + i32.add + tee_local 2 + i32.load + tee_local 4 + br_if 0 (;@7;) + get_local 1 + i32.const 16 + i32.add + tee_local 2 + i32.load + tee_local 4 + i32.eqz + br_if 1 (;@6;) + end + loop ;; label = @7 + get_local 2 + set_local 7 + get_local 4 + tee_local 5 + i32.const 20 + i32.add + tee_local 2 + i32.load + tee_local 4 + br_if 0 (;@7;) + get_local 5 + i32.const 16 + i32.add + set_local 2 + get_local 5 + i32.load offset=16 + tee_local 4 + br_if 0 (;@7;) + end + get_local 7 + i32.const 0 + i32.store + get_local 6 + i32.eqz + br_if 2 (;@4;) + br 1 (;@5;) + end + i32.const 0 + set_local 5 + get_local 6 + i32.eqz + br_if 1 (;@4;) + end + block ;; label = @5 + block ;; label = @6 + block ;; label = @7 + get_local 1 + i32.load offset=28 + tee_local 4 + i32.const 2 + i32.shl + i32.const 1344 + i32.add + tee_local 2 + i32.load + get_local 1 + i32.eq + br_if 0 (;@7;) + get_local 6 + i32.const 16 + i32.const 20 + get_local 6 + i32.load offset=16 + get_local 1 + i32.eq + select + i32.add + get_local 5 + i32.store + get_local 5 + br_if 1 (;@6;) + br 3 (;@4;) + end + get_local 2 + get_local 5 + i32.store + get_local 5 + i32.eqz + br_if 1 (;@5;) + end + get_local 5 + get_local 6 + i32.store offset=24 + block ;; label = @6 + get_local 1 + i32.load offset=16 + tee_local 2 + i32.eqz + br_if 0 (;@6;) + get_local 5 + get_local 2 + i32.store offset=16 + get_local 2 + get_local 5 + i32.store offset=24 + end + get_local 1 + i32.const 20 + i32.add + i32.load + tee_local 2 + i32.eqz + br_if 1 (;@4;) + get_local 5 + i32.const 20 + i32.add + get_local 2 + i32.store + get_local 2 + get_local 5 + i32.store offset=24 + get_local 1 + get_local 3 + i32.lt_u + br_if 2 (;@3;) + br 3 (;@2;) + end + i32.const 0 + i32.const 0 + i32.load offset=1044 + i32.const -2 + get_local 4 + i32.rotl + i32.and + i32.store offset=1044 + end + get_local 1 + get_local 3 + i32.ge_u + br_if 1 (;@2;) + end + get_local 3 + i32.load offset=4 + tee_local 2 + i32.const 1 + i32.and + i32.eqz + br_if 0 (;@2;) + block ;; label = @3 + block ;; label = @4 + block ;; label = @5 + block ;; label = @6 + block ;; label = @7 + block ;; label = @8 + block ;; label = @9 + block ;; label = @10 + block ;; label = @11 + get_local 2 + i32.const 2 + i32.and + br_if 0 (;@11;) + i32.const 0 + i32.load offset=1064 + get_local 3 + i32.eq + br_if 1 (;@10;) + i32.const 0 + i32.load offset=1060 + get_local 3 + i32.eq + br_if 2 (;@9;) + get_local 2 + i32.const -8 + i32.and + get_local 0 + i32.add + set_local 0 + get_local 2 + i32.const 255 + i32.gt_u + br_if 3 (;@8;) + get_local 3 + i32.load offset=12 + tee_local 4 + get_local 3 + i32.load offset=8 + tee_local 5 + i32.eq + br_if 4 (;@7;) + get_local 4 + get_local 5 + i32.store offset=8 + get_local 5 + get_local 4 + i32.store offset=12 + br 7 (;@4;) + end + get_local 3 + i32.const 4 + i32.add + get_local 2 + i32.const -2 + i32.and + i32.store + get_local 1 + get_local 0 + i32.add + get_local 0 + i32.store + get_local 1 + get_local 0 + i32.const 1 + i32.or + i32.store offset=4 + br 7 (;@3;) + end + i32.const 0 + get_local 1 + i32.store offset=1064 + i32.const 0 + i32.const 0 + i32.load offset=1052 + get_local 0 + i32.add + tee_local 0 + i32.store offset=1052 + get_local 1 + get_local 0 + i32.const 1 + i32.or + i32.store offset=4 + get_local 1 + i32.const 0 + i32.load offset=1060 + i32.ne + br_if 7 (;@2;) + i32.const 0 + i32.const 0 + i32.store offset=1048 + i32.const 0 + i32.const 0 + i32.store offset=1060 + return + end + i32.const 0 + get_local 1 + i32.store offset=1060 + i32.const 0 + i32.const 0 + i32.load offset=1048 + get_local 0 + i32.add + tee_local 0 + i32.store offset=1048 + get_local 1 + get_local 0 + i32.const 1 + i32.or + i32.store offset=4 + get_local 1 + get_local 0 + i32.add + get_local 0 + i32.store + return + end + get_local 3 + i32.load offset=24 + set_local 6 + get_local 3 + i32.load offset=12 + tee_local 5 + get_local 3 + i32.eq + br_if 1 (;@6;) + get_local 3 + i32.load offset=8 + tee_local 2 + get_local 5 + i32.store offset=12 + get_local 5 + get_local 2 + i32.store offset=8 + get_local 6 + br_if 2 (;@5;) + br 3 (;@4;) + end + i32.const 0 + i32.const 0 + i32.load offset=1040 + i32.const -2 + get_local 2 + i32.const 3 + i32.shr_u + i32.rotl + i32.and + i32.store offset=1040 + br 2 (;@4;) + end + block ;; label = @6 + block ;; label = @7 + get_local 3 + i32.const 20 + i32.add + tee_local 2 + i32.load + tee_local 4 + br_if 0 (;@7;) + get_local 3 + i32.const 16 + i32.add + tee_local 2 + i32.load + tee_local 4 + i32.eqz + br_if 1 (;@6;) + end + loop ;; label = @7 + get_local 2 + set_local 7 + get_local 4 + tee_local 5 + i32.const 20 + i32.add + tee_local 2 + i32.load + tee_local 4 + br_if 0 (;@7;) + get_local 5 + i32.const 16 + i32.add + set_local 2 + get_local 5 + i32.load offset=16 + tee_local 4 + br_if 0 (;@7;) + end + get_local 7 + i32.const 0 + i32.store + get_local 6 + i32.eqz + br_if 2 (;@4;) + br 1 (;@5;) + end + i32.const 0 + set_local 5 + get_local 6 + i32.eqz + br_if 1 (;@4;) + end + block ;; label = @5 + block ;; label = @6 + block ;; label = @7 + get_local 3 + i32.load offset=28 + tee_local 4 + i32.const 2 + i32.shl + i32.const 1344 + i32.add + tee_local 2 + i32.load + get_local 3 + i32.eq + br_if 0 (;@7;) + get_local 6 + i32.const 16 + i32.const 20 + get_local 6 + i32.load offset=16 + get_local 3 + i32.eq + select + i32.add + get_local 5 + i32.store + get_local 5 + br_if 1 (;@6;) + br 3 (;@4;) + end + get_local 2 + get_local 5 + i32.store + get_local 5 + i32.eqz + br_if 1 (;@5;) + end + get_local 5 + get_local 6 + i32.store offset=24 + block ;; label = @6 + get_local 3 + i32.load offset=16 + tee_local 2 + i32.eqz + br_if 0 (;@6;) + get_local 5 + get_local 2 + i32.store offset=16 + get_local 2 + get_local 5 + i32.store offset=24 + end + get_local 3 + i32.const 20 + i32.add + i32.load + tee_local 2 + i32.eqz + br_if 1 (;@4;) + get_local 5 + i32.const 20 + i32.add + get_local 2 + i32.store + get_local 2 + get_local 5 + i32.store offset=24 + br 1 (;@4;) + end + i32.const 0 + i32.const 0 + i32.load offset=1044 + i32.const -2 + get_local 4 + i32.rotl + i32.and + i32.store offset=1044 + end + get_local 1 + get_local 0 + i32.add + get_local 0 + i32.store + get_local 1 + get_local 0 + i32.const 1 + i32.or + i32.store offset=4 + get_local 1 + i32.const 0 + i32.load offset=1060 + i32.ne + br_if 0 (;@3;) + i32.const 0 + get_local 0 + i32.store offset=1048 + return + end + block ;; label = @3 + block ;; label = @4 + block ;; label = @5 + block ;; label = @6 + block ;; label = @7 + block ;; label = @8 + block ;; label = @9 + get_local 0 + i32.const 255 + i32.gt_u + br_if 0 (;@9;) + get_local 0 + i32.const 3 + i32.shr_u + tee_local 2 + i32.const 3 + i32.shl + i32.const 1080 + i32.add + set_local 0 + i32.const 0 + i32.load offset=1040 + tee_local 4 + i32.const 1 + get_local 2 + i32.shl + tee_local 2 + i32.and + i32.eqz + br_if 1 (;@8;) + get_local 0 + i32.load offset=8 + set_local 2 + br 2 (;@7;) + end + i32.const 0 + set_local 2 + block ;; label = @9 + get_local 0 + i32.const 8 + i32.shr_u + tee_local 4 + i32.eqz + br_if 0 (;@9;) + i32.const 31 + set_local 2 + get_local 0 + i32.const 16777215 + i32.gt_u + br_if 0 (;@9;) + get_local 0 + i32.const 14 + get_local 4 + get_local 4 + i32.const 1048320 + i32.add + i32.const 16 + i32.shr_u + i32.const 8 + i32.and + tee_local 2 + i32.shl + tee_local 4 + i32.const 520192 + i32.add + i32.const 16 + i32.shr_u + i32.const 4 + i32.and + tee_local 5 + get_local 2 + i32.or + get_local 4 + get_local 5 + i32.shl + tee_local 2 + i32.const 245760 + i32.add + i32.const 16 + i32.shr_u + i32.const 2 + i32.and + tee_local 4 + i32.or + i32.sub + get_local 2 + get_local 4 + i32.shl + i32.const 15 + i32.shr_u + i32.add + tee_local 2 + i32.const 7 + i32.add + i32.shr_u + i32.const 1 + i32.and + get_local 2 + i32.const 1 + i32.shl + i32.or + set_local 2 + end + get_local 1 + i64.const 0 + i64.store offset=16 align=4 + get_local 1 + i32.const 28 + i32.add + get_local 2 + i32.store + get_local 2 + i32.const 2 + i32.shl + i32.const 1344 + i32.add + set_local 4 + i32.const 0 + i32.load offset=1044 + tee_local 5 + i32.const 1 + get_local 2 + i32.shl + tee_local 3 + i32.and + i32.eqz + br_if 2 (;@6;) + get_local 0 + i32.const 0 + i32.const 25 + get_local 2 + i32.const 1 + i32.shr_u + i32.sub + get_local 2 + i32.const 31 + i32.eq + select + i32.shl + set_local 2 + get_local 4 + i32.load + set_local 5 + loop ;; label = @9 + get_local 5 + tee_local 4 + i32.load offset=4 + i32.const -8 + i32.and + get_local 0 + i32.eq + br_if 5 (;@4;) + get_local 2 + i32.const 29 + i32.shr_u + set_local 5 + get_local 2 + i32.const 1 + i32.shl + set_local 2 + get_local 4 + get_local 5 + i32.const 4 + i32.and + i32.add + i32.const 16 + i32.add + tee_local 3 + i32.load + tee_local 5 + br_if 0 (;@9;) + end + get_local 3 + get_local 1 + i32.store + get_local 1 + i32.const 24 + i32.add + get_local 4 + i32.store + br 3 (;@5;) + end + i32.const 0 + get_local 4 + get_local 2 + i32.or + i32.store offset=1040 + get_local 0 + set_local 2 + end + get_local 2 + get_local 1 + i32.store offset=12 + get_local 0 + get_local 1 + i32.store offset=8 + get_local 1 + get_local 0 + i32.store offset=12 + get_local 1 + get_local 2 + i32.store offset=8 + return + end + get_local 4 + get_local 1 + i32.store + i32.const 0 + get_local 5 + get_local 3 + i32.or + i32.store offset=1044 + get_local 1 + i32.const 24 + i32.add + get_local 4 + i32.store + end + get_local 1 + get_local 1 + i32.store offset=12 + get_local 1 + get_local 1 + i32.store offset=8 + br 1 (;@3;) + end + get_local 4 + i32.load offset=8 + tee_local 0 + get_local 1 + i32.store offset=12 + get_local 4 + get_local 1 + i32.store offset=8 + get_local 1 + i32.const 24 + i32.add + i32.const 0 + i32.store + get_local 1 + get_local 4 + i32.store offset=12 + get_local 1 + get_local 0 + i32.store offset=8 + end + i32.const 0 + i32.const 0 + i32.load offset=1072 + i32.const -1 + i32.add + tee_local 1 + i32.store offset=1072 + get_local 1 + i32.eqz + br_if 1 (;@1;) + end + return + end + i32.const 1496 + set_local 1 + loop ;; label = @1 + get_local 1 + i32.load + tee_local 0 + i32.const 8 + i32.add + set_local 1 + get_local 0 + br_if 0 (;@1;) + end + i32.const 0 + i32.const -1 + i32.store offset=1072) + (func (;18;) (type 2) (param i32 i32) (result i32) + (local i32) + block ;; label = @1 + block ;; label = @2 + block ;; label = @3 + get_local 0 + i32.eqz + br_if 0 (;@3;) + get_local 1 + get_local 0 + i32.mul + set_local 2 + block ;; label = @4 + get_local 1 + get_local 0 + i32.or + i32.const 65536 + i32.lt_u + br_if 0 (;@4;) + get_local 2 + i32.const -1 + get_local 2 + get_local 0 + i32.div_u + get_local 1 + i32.eq + select + set_local 2 + end + get_local 2 + call 15 + tee_local 0 + br_if 1 (;@2;) + br 2 (;@1;) + end + i32.const 0 + set_local 2 + i32.const 0 + call 15 + tee_local 0 + i32.eqz + br_if 1 (;@1;) + end + get_local 0 + i32.const -4 + i32.add + i32.load8_u + i32.const 3 + i32.and + i32.eqz + br_if 0 (;@1;) + get_local 0 + i32.const 0 + get_local 2 + call 44 + drop + end + get_local 0) + (func (;19;) (type 3) (param i32) + get_local 0 + call 6 + unreachable) + (func (;20;) (type 7) + unreachable + unreachable) + (func (;21;) (type 4) (param i32) (result i32) + block ;; label = @1 + get_local 0 + call 8 + tee_local 0 + i32.eqz + br_if 0 (;@1;) + i32.const 0 + get_local 0 + i32.store offset=1536 + i32.const -1 + return + end + i32.const 0) + (func (;22;) (type 3) (param i32) + (local i32 i32) + block ;; label = @1 + get_local 0 + i32.load + i32.const 0 + i32.le_s + br_if 0 (;@1;) + get_local 0 + i32.load offset=12 + tee_local 1 + get_local 0 + i32.load offset=8 + tee_local 2 + i32.gt_u + br_if 0 (;@1;) + get_local 0 + i32.load offset=4 + set_local 0 + block ;; label = @2 + get_local 2 + i32.eqz + br_if 0 (;@2;) + get_local 0 + i32.eqz + br_if 1 (;@1;) + end + block ;; label = @2 + get_local 1 + i32.eqz + br_if 0 (;@2;) + i32.const 0 + set_local 2 + loop ;; label = @3 + get_local 0 + i32.load + i32.eqz + br_if 2 (;@1;) + get_local 0 + i32.const 4 + i32.add + i32.load + i32.const -1 + i32.le_s + br_if 2 (;@1;) + get_local 0 + i32.const 24 + i32.add + set_local 0 + get_local 2 + i32.const 1 + i32.add + tee_local 2 + get_local 1 + i32.lt_u + br_if 0 (;@3;) + end + end + return + end + call 20 + unreachable) + (func (;23;) (type 7) + (local i32 i32) + block ;; label = @1 + i32.const 16 + call 14 + tee_local 0 + i32.eqz + br_if 0 (;@1;) + get_local 0 + i32.const 24 + i32.const 4 + call 18 + tee_local 1 + i32.store offset=4 + block ;; label = @2 + get_local 1 + i32.eqz + br_if 0 (;@2;) + get_local 0 + i64.const 4 + i64.store offset=8 align=4 + get_local 0 + i32.const 1 + i32.store + get_local 0 + call 22 + i32.const 0 + get_local 0 + i32.store offset=1540 + get_local 0 + call 22 + return + end + get_local 0 + call 16 + end + i32.const 0 + i32.const 0 + i32.store offset=1540 + unreachable + unreachable) + (func (;24;) (type 2) (param i32 i32) (result i32) + (local i32 i32 i32 i32 i32 i32) + get_global 0 + i32.const 32 + i32.sub + tee_local 2 + set_global 0 + i32.const 0 + i32.load offset=1540 + call 22 + i32.const -1 + set_local 3 + block ;; label = @1 + get_local 1 + i32.eqz + br_if 0 (;@1;) + i32.const 0 + i32.load offset=1540 + tee_local 4 + call 22 + get_local 0 + i32.const 0 + i32.lt_s + br_if 0 (;@1;) + block ;; label = @2 + block ;; label = @3 + get_local 4 + i32.load offset=12 + tee_local 5 + get_local 4 + i32.load offset=8 + i32.ne + br_if 0 (;@3;) + i32.const 24 + get_local 5 + i32.const 1 + i32.shl + call 18 + tee_local 6 + i32.eqz + br_if 2 (;@1;) + get_local 6 + get_local 4 + i32.load offset=4 + get_local 4 + i32.const 12 + i32.add + tee_local 5 + i32.load + i32.const 24 + i32.mul + call 43 + drop + get_local 4 + i32.load offset=4 + call 16 + get_local 4 + get_local 6 + i32.store offset=4 + get_local 4 + i32.const 8 + i32.add + tee_local 7 + get_local 7 + i32.load + i32.const 1 + i32.shl + i32.store + get_local 5 + i32.load + set_local 5 + br 1 (;@2;) + end + get_local 4 + i32.load offset=4 + set_local 6 + end + get_local 4 + i32.const 12 + i32.add + get_local 5 + i32.const 1 + i32.add + i32.store + get_local 1 + call 45 + set_local 7 + get_local 6 + get_local 5 + i32.const 24 + i32.mul + i32.add + tee_local 1 + get_local 0 + i32.store offset=4 + get_local 1 + get_local 7 + i32.store + block ;; label = @2 + get_local 0 + get_local 2 + i32.const 8 + i32.add + call 7 + tee_local 0 + i32.eqz + br_if 0 (;@2;) + i32.const 0 + get_local 0 + i32.store offset=1536 + br 1 (;@1;) + end + get_local 1 + get_local 2 + i64.load offset=16 + i64.store offset=8 + get_local 1 + get_local 2 + i64.load offset=24 + i64.store offset=16 + get_local 4 + call 22 + get_local 4 + call 22 + i32.const 0 + set_local 3 + i32.const 0 + get_local 4 + i32.store offset=1540 + end + get_local 2 + i32.const 32 + i32.add + set_global 0 + get_local 3) + (func (;25;) (type 4) (param i32) (result i32) + block ;; label = @1 + get_local 0 + i32.const 65535 + i32.and + br_if 0 (;@1;) + get_local 0 + i32.const -1 + i32.le_s + br_if 0 (;@1;) + block ;; label = @2 + get_local 0 + i32.const 16 + i32.shr_u + grow_memory + tee_local 0 + i32.const -1 + i32.eq + br_if 0 (;@2;) + get_local 0 + i32.const 16 + i32.shl + return + end + i32.const 0 + i32.const 48 + i32.store offset=1536 + i32.const -1 + return + end + call 20 + unreachable) + (func (;26;) (type 7)) + (func (;27;) (type 7) + call 26 + call 29) + (func (;28;) (type 8) (result i32) + i32.const 1548) + (func (;29;) (type 7) + (local i32 i32 i32) + block ;; label = @1 + call 28 + i32.load + tee_local 0 + i32.eqz + br_if 0 (;@1;) + loop ;; label = @2 + block ;; label = @3 + get_local 0 + i32.load offset=20 + get_local 0 + i32.load offset=24 + i32.eq + br_if 0 (;@3;) + get_local 0 + i32.const 0 + i32.const 0 + get_local 0 + i32.load offset=32 + call_indirect (type 0) + drop + end + block ;; label = @3 + get_local 0 + i32.load offset=4 + tee_local 1 + get_local 0 + i32.load offset=8 + tee_local 2 + i32.eq + br_if 0 (;@3;) + get_local 0 + get_local 1 + get_local 2 + i32.sub + i64.extend_s/i32 + i32.const 0 + get_local 0 + i32.load offset=36 + call_indirect (type 1) + drop + end + get_local 0 + i32.load offset=52 + tee_local 0 + br_if 0 (;@2;) + end + end + block ;; label = @1 + i32.const 0 + i32.load offset=1552 + tee_local 0 + i32.eqz + br_if 0 (;@1;) + block ;; label = @2 + get_local 0 + i32.load offset=20 + get_local 0 + i32.load offset=24 + i32.eq + br_if 0 (;@2;) + get_local 0 + i32.const 0 + i32.const 0 + get_local 0 + i32.load offset=32 + call_indirect (type 0) + drop + end + get_local 0 + i32.load offset=4 + tee_local 1 + get_local 0 + i32.load offset=8 + tee_local 2 + i32.eq + br_if 0 (;@1;) + get_local 0 + get_local 1 + get_local 2 + i32.sub + i64.extend_s/i32 + i32.const 0 + get_local 0 + i32.load offset=36 + call_indirect (type 1) + drop + end + block ;; label = @1 + i32.const 0 + i32.load offset=2712 + tee_local 0 + i32.eqz + br_if 0 (;@1;) + block ;; label = @2 + get_local 0 + i32.load offset=20 + get_local 0 + i32.load offset=24 + i32.eq + br_if 0 (;@2;) + get_local 0 + i32.const 0 + i32.const 0 + get_local 0 + i32.load offset=32 + call_indirect (type 0) + drop + end + get_local 0 + i32.load offset=4 + tee_local 1 + get_local 0 + i32.load offset=8 + tee_local 2 + i32.eq + br_if 0 (;@1;) + get_local 0 + get_local 1 + get_local 2 + i32.sub + i64.extend_s/i32 + i32.const 0 + get_local 0 + i32.load offset=36 + call_indirect (type 1) + drop + end + block ;; label = @1 + i32.const 0 + i32.load offset=1552 + tee_local 0 + i32.eqz + br_if 0 (;@1;) + block ;; label = @2 + get_local 0 + i32.load offset=20 + get_local 0 + i32.load offset=24 + i32.eq + br_if 0 (;@2;) + get_local 0 + i32.const 0 + i32.const 0 + get_local 0 + i32.load offset=32 + call_indirect (type 0) + drop + end + get_local 0 + i32.load offset=4 + tee_local 1 + get_local 0 + i32.load offset=8 + tee_local 2 + i32.eq + br_if 0 (;@1;) + get_local 0 + get_local 1 + get_local 2 + i32.sub + i64.extend_s/i32 + i32.const 0 + get_local 0 + i32.load offset=36 + call_indirect (type 1) + drop + end) + (func (;30;) (type 4) (param i32) (result i32) + (local i32) + get_local 0 + get_local 0 + i32.load offset=60 + tee_local 1 + i32.const -1 + i32.add + get_local 1 + i32.or + i32.store offset=60 + block ;; label = @1 + get_local 0 + i32.load + tee_local 1 + i32.const 8 + i32.and + br_if 0 (;@1;) + get_local 0 + i64.const 0 + i64.store offset=4 align=4 + get_local 0 + get_local 0 + i32.load offset=40 + tee_local 1 + i32.store offset=24 + get_local 0 + get_local 1 + i32.store offset=20 + get_local 0 + get_local 1 + get_local 0 + i32.load offset=44 + i32.add + i32.store offset=16 + i32.const 0 + return + end + get_local 0 + get_local 1 + i32.const 32 + i32.or + i32.store + i32.const -1) + (func (;31;) (type 0) (param i32 i32 i32) (result i32) + (local i32 i32 i32 i32 i32 i32) + block ;; label = @1 + block ;; label = @2 + get_local 2 + i32.load offset=16 + tee_local 3 + br_if 0 (;@2;) + i32.const 0 + set_local 6 + get_local 2 + call 30 + br_if 1 (;@1;) + get_local 2 + i32.const 16 + i32.add + i32.load + set_local 3 + end + block ;; label = @2 + get_local 3 + get_local 2 + i32.load offset=20 + tee_local 4 + i32.sub + get_local 1 + i32.ge_u + br_if 0 (;@2;) + get_local 2 + get_local 0 + get_local 1 + get_local 2 + i32.load offset=32 + call_indirect (type 0) + return + end + i32.const 0 + set_local 5 + block ;; label = @2 + get_local 2 + i32.load offset=64 + i32.const 0 + i32.lt_s + br_if 0 (;@2;) + i32.const 0 + set_local 5 + get_local 0 + set_local 6 + i32.const 0 + set_local 3 + loop ;; label = @3 + get_local 1 + get_local 3 + i32.eq + br_if 1 (;@2;) + get_local 3 + i32.const 1 + i32.add + set_local 3 + get_local 6 + get_local 1 + i32.add + set_local 7 + get_local 6 + i32.const -1 + i32.add + tee_local 8 + set_local 6 + get_local 7 + i32.const -1 + i32.add + i32.load8_u + i32.const 10 + i32.ne + br_if 0 (;@3;) + end + get_local 2 + get_local 0 + get_local 1 + get_local 3 + i32.sub + i32.const 1 + i32.add + tee_local 5 + get_local 2 + i32.load offset=32 + call_indirect (type 0) + tee_local 6 + get_local 5 + i32.lt_u + br_if 1 (;@1;) + get_local 8 + get_local 1 + i32.add + i32.const 1 + i32.add + set_local 0 + get_local 2 + i32.const 20 + i32.add + i32.load + set_local 4 + get_local 3 + i32.const -1 + i32.add + set_local 1 + end + get_local 4 + get_local 0 + get_local 1 + call 43 + drop + get_local 2 + i32.const 20 + i32.add + tee_local 3 + get_local 3 + i32.load + get_local 1 + i32.add + i32.store + get_local 5 + get_local 1 + i32.add + return + end + get_local 6) + (func (;32;) (type 5) (param i32 i32 i32 i32) (result i32) + (local i32) + block ;; label = @1 + get_local 0 + get_local 2 + get_local 1 + i32.mul + tee_local 4 + get_local 3 + call 31 + tee_local 0 + get_local 4 + i32.ne + br_if 0 (;@1;) + get_local 2 + i32.const 0 + get_local 1 + select + return + end + get_local 0 + get_local 1 + i32.div_u) + (func (;33;) (type 2) (param i32 i32) (result i32) + (local i32) + i32.const -1 + i32.const 0 + get_local 0 + call 46 + tee_local 2 + get_local 0 + i32.const 1 + get_local 2 + get_local 1 + call 32 + i32.ne + select) + (func (;34;) (type 2) (param i32 i32) (result i32) + (local i32 i32 i32) + get_global 0 + i32.const 16 + i32.sub + tee_local 2 + set_global 0 + get_local 2 + get_local 1 + i32.store8 offset=15 + block ;; label = @1 + block ;; label = @2 + get_local 0 + i32.load offset=16 + tee_local 3 + br_if 0 (;@2;) + i32.const -1 + set_local 3 + get_local 0 + call 30 + br_if 1 (;@1;) + get_local 0 + i32.const 16 + i32.add + i32.load + set_local 3 + end + block ;; label = @2 + block ;; label = @3 + get_local 0 + i32.load offset=20 + tee_local 4 + get_local 3 + i32.eq + br_if 0 (;@3;) + get_local 0 + i32.load offset=64 + get_local 1 + i32.const 255 + i32.and + tee_local 3 + i32.ne + br_if 1 (;@2;) + end + i32.const -1 + set_local 3 + get_local 0 + get_local 2 + i32.const 15 + i32.add + i32.const 1 + get_local 0 + i32.load offset=32 + call_indirect (type 0) + i32.const 1 + i32.ne + br_if 1 (;@1;) + get_local 2 + i32.load8_u offset=15 + set_local 3 + br 1 (;@1;) + end + get_local 0 + i32.const 20 + i32.add + get_local 4 + i32.const 1 + i32.add + i32.store + get_local 4 + get_local 1 + i32.store8 + end + get_local 2 + i32.const 16 + i32.add + set_global 0 + get_local 3) + (func (;35;) (type 4) (param i32) (result i32) + block ;; label = @1 + get_local 0 + i32.const 2600 + call 33 + i32.const 0 + i32.lt_s + br_if 0 (;@1;) + block ;; label = @2 + i32.const 0 + i32.load offset=2664 + i32.const 10 + i32.eq + br_if 0 (;@2;) + i32.const 0 + i32.load offset=2620 + tee_local 0 + i32.const 0 + i32.load offset=2616 + i32.eq + br_if 0 (;@2;) + i32.const 0 + get_local 0 + i32.const 1 + i32.add + i32.store offset=2620 + get_local 0 + i32.const 10 + i32.store8 + i32.const 0 + return + end + i32.const 2600 + i32.const 10 + call 34 + i32.const 31 + i32.shr_s + return + end + i32.const -1) + (func (;36;) (type 4) (param i32) (result i32) + get_local 0 + i32.load offset=56 + call 21) + (func (;37;) (type 0) (param i32 i32 i32) (result i32) + (local i32 i32) + get_global 0 + i32.const 16 + i32.sub + tee_local 3 + set_global 0 + i32.const -1 + set_local 4 + block ;; label = @1 + block ;; label = @2 + block ;; label = @3 + get_local 2 + i32.const -1 + i32.le_s + br_if 0 (;@3;) + get_local 0 + get_local 1 + get_local 2 + get_local 3 + i32.const 12 + i32.add + call 9 + tee_local 2 + i32.eqz + br_if 1 (;@2;) + i32.const 0 + get_local 2 + i32.store offset=1536 + i32.const -1 + set_local 4 + br 2 (;@1;) + end + i32.const 0 + i32.const 28 + i32.store offset=1536 + br 1 (;@1;) + end + get_local 3 + i32.load offset=12 + set_local 4 + end + get_local 3 + i32.const 16 + i32.add + set_global 0 + get_local 4) + (func (;38;) (type 0) (param i32 i32 i32) (result i32) + (local i32 i32 i32 i32 i32 i32 i32) + get_global 0 + i32.const 16 + i32.sub + tee_local 3 + set_global 0 + get_local 3 + get_local 2 + i32.store offset=12 + get_local 3 + get_local 1 + i32.store offset=8 + get_local 3 + get_local 0 + i32.load offset=24 + tee_local 1 + i32.store + get_local 3 + get_local 0 + i32.load offset=20 + get_local 1 + i32.sub + tee_local 1 + i32.store offset=4 + i32.const 2 + set_local 4 + block ;; label = @1 + block ;; label = @2 + block ;; label = @3 + get_local 1 + get_local 2 + i32.add + tee_local 5 + get_local 0 + i32.load offset=56 + get_local 3 + i32.const 2 + call 37 + tee_local 6 + i32.eq + br_if 0 (;@3;) + get_local 3 + set_local 1 + get_local 0 + i32.const 56 + i32.add + set_local 7 + loop ;; label = @4 + get_local 6 + i32.const -1 + i32.le_s + br_if 2 (;@2;) + get_local 1 + i32.const 8 + i32.add + get_local 1 + get_local 6 + get_local 1 + i32.load offset=4 + tee_local 8 + i32.gt_u + tee_local 9 + select + tee_local 1 + get_local 1 + i32.load + get_local 6 + get_local 8 + i32.const 0 + get_local 9 + select + i32.sub + tee_local 8 + i32.add + i32.store + get_local 1 + get_local 1 + i32.load offset=4 + get_local 8 + i32.sub + i32.store offset=4 + get_local 5 + get_local 6 + i32.sub + set_local 5 + get_local 7 + i32.load + get_local 1 + get_local 4 + get_local 9 + i32.sub + tee_local 4 + call 37 + tee_local 9 + set_local 6 + get_local 5 + get_local 9 + i32.ne + br_if 0 (;@4;) + end + end + get_local 0 + i32.const 24 + i32.add + get_local 0 + i32.load offset=40 + tee_local 1 + i32.store + get_local 0 + i32.const 20 + i32.add + get_local 1 + i32.store + get_local 0 + get_local 1 + get_local 0 + i32.load offset=44 + i32.add + i32.store offset=16 + get_local 2 + set_local 6 + br 1 (;@1;) + end + get_local 0 + i64.const 0 + i64.store offset=16 + i32.const 0 + set_local 6 + get_local 0 + i32.const 24 + i32.add + i32.const 0 + i32.store + get_local 0 + get_local 0 + i32.load + i32.const 32 + i32.or + i32.store + get_local 4 + i32.const 2 + i32.eq + br_if 0 (;@1;) + get_local 2 + get_local 1 + i32.load offset=4 + i32.sub + set_local 6 + end + get_local 3 + i32.const 16 + i32.add + set_global 0 + get_local 6) + (func (;39;) (type 4) (param i32) (result i32) + (local i32 i32) + get_global 0 + i32.const 32 + i32.sub + tee_local 1 + set_global 0 + block ;; label = @1 + block ;; label = @2 + block ;; label = @3 + get_local 0 + get_local 1 + i32.const 8 + i32.add + call 7 + tee_local 0 + br_if 0 (;@3;) + i32.const 59 + set_local 0 + get_local 1 + i32.load8_u offset=8 + i32.const 2 + i32.ne + br_if 0 (;@3;) + get_local 1 + i32.load8_u offset=16 + i32.const 36 + i32.and + i32.eqz + br_if 1 (;@2;) + end + i32.const 0 + set_local 2 + i32.const 0 + get_local 0 + i32.store offset=1536 + br 1 (;@1;) + end + i32.const 1 + set_local 2 + end + get_local 1 + i32.const 32 + i32.add + set_global 0 + get_local 2) + (func (;40;) (type 0) (param i32 i32 i32) (result i32) + get_local 0 + i32.const 1 + i32.store offset=32 + block ;; label = @1 + block ;; label = @2 + get_local 0 + i32.load8_u + i32.const 64 + i32.and + br_if 0 (;@2;) + get_local 0 + i32.load offset=56 + call 39 + i32.eqz + br_if 1 (;@1;) + end + get_local 0 + get_local 1 + get_local 2 + call 38 + return + end + get_local 0 + i32.const -1 + i32.store offset=64 + get_local 0 + get_local 1 + get_local 2 + call 38) + (func (;41;) (type 1) (param i32 i64 i32) (result i64) + (local i32) + get_global 0 + i32.const 16 + i32.sub + tee_local 3 + set_global 0 + block ;; label = @1 + block ;; label = @2 + get_local 0 + get_local 1 + get_local 2 + i32.const 255 + i32.and + get_local 3 + i32.const 8 + i32.add + call 10 + tee_local 0 + i32.eqz + br_if 0 (;@2;) + i32.const 0 + i32.const 70 + get_local 0 + get_local 0 + i32.const 76 + i32.eq + select + i32.store offset=1536 + i64.const -1 + set_local 1 + br 1 (;@1;) + end + get_local 3 + i64.load offset=8 + set_local 1 + end + get_local 3 + i32.const 16 + i32.add + set_global 0 + get_local 1) + (func (;42;) (type 1) (param i32 i64 i32) (result i64) + get_local 0 + i32.load offset=56 + get_local 1 + get_local 2 + call 41) + (func (;43;) (type 0) (param i32 i32 i32) (result i32) + (local i32 i32 i32 i32 i32 i32 i32 i32) + block ;; label = @1 + block ;; label = @2 + block ;; label = @3 + block ;; label = @4 + get_local 2 + i32.eqz + br_if 0 (;@4;) + get_local 1 + i32.const 3 + i32.and + i32.eqz + br_if 0 (;@4;) + get_local 0 + set_local 3 + block ;; label = @5 + loop ;; label = @6 + get_local 3 + get_local 1 + i32.load8_u + i32.store8 + get_local 2 + i32.const -1 + i32.add + set_local 4 + get_local 3 + i32.const 1 + i32.add + set_local 3 + get_local 1 + i32.const 1 + i32.add + set_local 1 + get_local 2 + i32.const 1 + i32.eq + br_if 1 (;@5;) + get_local 4 + set_local 2 + get_local 1 + i32.const 3 + i32.and + br_if 0 (;@6;) + end + end + get_local 3 + i32.const 3 + i32.and + tee_local 2 + i32.eqz + br_if 1 (;@3;) + br 2 (;@2;) + end + get_local 2 + set_local 4 + get_local 0 + tee_local 3 + i32.const 3 + i32.and + tee_local 2 + br_if 1 (;@2;) + end + block ;; label = @3 + block ;; label = @4 + get_local 4 + i32.const 16 + i32.lt_u + br_if 0 (;@4;) + get_local 4 + i32.const -16 + i32.add + set_local 2 + loop ;; label = @5 + get_local 3 + get_local 1 + i32.load + i32.store + get_local 3 + i32.const 4 + i32.add + get_local 1 + i32.const 4 + i32.add + i32.load + i32.store + get_local 3 + i32.const 8 + i32.add + get_local 1 + i32.const 8 + i32.add + i32.load + i32.store + get_local 3 + i32.const 12 + i32.add + get_local 1 + i32.const 12 + i32.add + i32.load + i32.store + get_local 3 + i32.const 16 + i32.add + set_local 3 + get_local 1 + i32.const 16 + i32.add + set_local 1 + get_local 4 + i32.const -16 + i32.add + tee_local 4 + i32.const 15 + i32.gt_u + br_if 0 (;@5;) + br 2 (;@3;) + end + end + get_local 4 + set_local 2 + end + block ;; label = @3 + get_local 2 + i32.const 8 + i32.and + i32.eqz + br_if 0 (;@3;) + get_local 3 + get_local 1 + i64.load align=4 + i64.store align=4 + get_local 1 + i32.const 8 + i32.add + set_local 1 + get_local 3 + i32.const 8 + i32.add + set_local 3 + end + block ;; label = @3 + get_local 2 + i32.const 4 + i32.and + i32.eqz + br_if 0 (;@3;) + get_local 3 + get_local 1 + i32.load + i32.store + get_local 1 + i32.const 4 + i32.add + set_local 1 + get_local 3 + i32.const 4 + i32.add + set_local 3 + end + block ;; label = @3 + get_local 2 + i32.const 2 + i32.and + i32.eqz + br_if 0 (;@3;) + get_local 3 + get_local 1 + i32.load8_u + i32.store8 + get_local 3 + get_local 1 + i32.load8_u offset=1 + i32.store8 offset=1 + get_local 3 + i32.const 2 + i32.add + set_local 3 + get_local 1 + i32.const 2 + i32.add + set_local 1 + end + get_local 2 + i32.const 1 + i32.and + i32.eqz + br_if 1 (;@1;) + get_local 3 + get_local 1 + i32.load8_u + i32.store8 + get_local 0 + return + end + block ;; label = @2 + get_local 4 + i32.const 32 + i32.lt_u + br_if 0 (;@2;) + block ;; label = @3 + block ;; label = @4 + get_local 2 + i32.const 3 + i32.eq + br_if 0 (;@4;) + get_local 2 + i32.const 2 + i32.eq + br_if 1 (;@3;) + get_local 2 + i32.const 1 + i32.ne + br_if 2 (;@2;) + get_local 3 + get_local 1 + i32.load8_u offset=1 + i32.store8 offset=1 + get_local 3 + get_local 1 + i32.load + tee_local 5 + i32.store8 + get_local 3 + get_local 1 + i32.load8_u offset=2 + i32.store8 offset=2 + get_local 4 + i32.const -3 + i32.add + set_local 6 + get_local 3 + i32.const 3 + i32.add + set_local 7 + get_local 4 + i32.const -20 + i32.add + i32.const -16 + i32.and + set_local 8 + i32.const 0 + set_local 2 + loop ;; label = @5 + get_local 7 + get_local 2 + i32.add + tee_local 3 + get_local 1 + get_local 2 + i32.add + tee_local 9 + i32.const 4 + i32.add + i32.load + tee_local 10 + i32.const 8 + i32.shl + get_local 5 + i32.const 24 + i32.shr_u + i32.or + i32.store + get_local 3 + i32.const 4 + i32.add + get_local 9 + i32.const 8 + i32.add + i32.load + tee_local 5 + i32.const 8 + i32.shl + get_local 10 + i32.const 24 + i32.shr_u + i32.or + i32.store + get_local 3 + i32.const 8 + i32.add + get_local 9 + i32.const 12 + i32.add + i32.load + tee_local 10 + i32.const 8 + i32.shl + get_local 5 + i32.const 24 + i32.shr_u + i32.or + i32.store + get_local 3 + i32.const 12 + i32.add + get_local 9 + i32.const 16 + i32.add + i32.load + tee_local 5 + i32.const 8 + i32.shl + get_local 10 + i32.const 24 + i32.shr_u + i32.or + i32.store + get_local 2 + i32.const 16 + i32.add + set_local 2 + get_local 6 + i32.const -16 + i32.add + tee_local 6 + i32.const 16 + i32.gt_u + br_if 0 (;@5;) + end + get_local 7 + get_local 2 + i32.add + set_local 3 + get_local 1 + get_local 2 + i32.add + i32.const 3 + i32.add + set_local 1 + get_local 4 + i32.const -19 + i32.add + get_local 8 + i32.sub + set_local 4 + br 2 (;@2;) + end + get_local 3 + get_local 1 + i32.load + tee_local 5 + i32.store8 + get_local 4 + i32.const -1 + i32.add + set_local 6 + get_local 3 + i32.const 1 + i32.add + set_local 7 + get_local 4 + i32.const -20 + i32.add + i32.const -16 + i32.and + set_local 8 + i32.const 0 + set_local 2 + loop ;; label = @4 + get_local 7 + get_local 2 + i32.add + tee_local 3 + get_local 1 + get_local 2 + i32.add + tee_local 9 + i32.const 4 + i32.add + i32.load + tee_local 10 + i32.const 24 + i32.shl + get_local 5 + i32.const 8 + i32.shr_u + i32.or + i32.store + get_local 3 + i32.const 4 + i32.add + get_local 9 + i32.const 8 + i32.add + i32.load + tee_local 5 + i32.const 24 + i32.shl + get_local 10 + i32.const 8 + i32.shr_u + i32.or + i32.store + get_local 3 + i32.const 8 + i32.add + get_local 9 + i32.const 12 + i32.add + i32.load + tee_local 10 + i32.const 24 + i32.shl + get_local 5 + i32.const 8 + i32.shr_u + i32.or + i32.store + get_local 3 + i32.const 12 + i32.add + get_local 9 + i32.const 16 + i32.add + i32.load + tee_local 5 + i32.const 24 + i32.shl + get_local 10 + i32.const 8 + i32.shr_u + i32.or + i32.store + get_local 2 + i32.const 16 + i32.add + set_local 2 + get_local 6 + i32.const -16 + i32.add + tee_local 6 + i32.const 18 + i32.gt_u + br_if 0 (;@4;) + end + get_local 7 + get_local 2 + i32.add + set_local 3 + get_local 1 + get_local 2 + i32.add + i32.const 1 + i32.add + set_local 1 + get_local 4 + i32.const -17 + i32.add + get_local 8 + i32.sub + set_local 4 + br 1 (;@2;) + end + get_local 3 + get_local 1 + i32.load + tee_local 5 + i32.store8 + get_local 3 + get_local 1 + i32.load8_u offset=1 + i32.store8 offset=1 + get_local 4 + i32.const -2 + i32.add + set_local 6 + get_local 3 + i32.const 2 + i32.add + set_local 7 + get_local 4 + i32.const -20 + i32.add + i32.const -16 + i32.and + set_local 8 + i32.const 0 + set_local 2 + loop ;; label = @3 + get_local 7 + get_local 2 + i32.add + tee_local 3 + get_local 1 + get_local 2 + i32.add + tee_local 9 + i32.const 4 + i32.add + i32.load + tee_local 10 + i32.const 16 + i32.shl + get_local 5 + i32.const 16 + i32.shr_u + i32.or + i32.store + get_local 3 + i32.const 4 + i32.add + get_local 9 + i32.const 8 + i32.add + i32.load + tee_local 5 + i32.const 16 + i32.shl + get_local 10 + i32.const 16 + i32.shr_u + i32.or + i32.store + get_local 3 + i32.const 8 + i32.add + get_local 9 + i32.const 12 + i32.add + i32.load + tee_local 10 + i32.const 16 + i32.shl + get_local 5 + i32.const 16 + i32.shr_u + i32.or + i32.store + get_local 3 + i32.const 12 + i32.add + get_local 9 + i32.const 16 + i32.add + i32.load + tee_local 5 + i32.const 16 + i32.shl + get_local 10 + i32.const 16 + i32.shr_u + i32.or + i32.store + get_local 2 + i32.const 16 + i32.add + set_local 2 + get_local 6 + i32.const -16 + i32.add + tee_local 6 + i32.const 17 + i32.gt_u + br_if 0 (;@3;) + end + get_local 7 + get_local 2 + i32.add + set_local 3 + get_local 1 + get_local 2 + i32.add + i32.const 2 + i32.add + set_local 1 + get_local 4 + i32.const -18 + i32.add + get_local 8 + i32.sub + set_local 4 + end + block ;; label = @2 + get_local 4 + i32.const 16 + i32.and + i32.eqz + br_if 0 (;@2;) + get_local 3 + get_local 1 + i32.load16_u align=1 + i32.store16 align=1 + get_local 3 + get_local 1 + i32.load8_u offset=2 + i32.store8 offset=2 + get_local 3 + get_local 1 + i32.load8_u offset=3 + i32.store8 offset=3 + get_local 3 + get_local 1 + i32.load8_u offset=4 + i32.store8 offset=4 + get_local 3 + get_local 1 + i32.load8_u offset=5 + i32.store8 offset=5 + get_local 3 + get_local 1 + i32.load8_u offset=6 + i32.store8 offset=6 + get_local 3 + get_local 1 + i32.load8_u offset=7 + i32.store8 offset=7 + get_local 3 + get_local 1 + i32.load8_u offset=8 + i32.store8 offset=8 + get_local 3 + get_local 1 + i32.load8_u offset=9 + i32.store8 offset=9 + get_local 3 + get_local 1 + i32.load8_u offset=10 + i32.store8 offset=10 + get_local 3 + get_local 1 + i32.load8_u offset=11 + i32.store8 offset=11 + get_local 3 + get_local 1 + i32.load8_u offset=12 + i32.store8 offset=12 + get_local 3 + get_local 1 + i32.load8_u offset=13 + i32.store8 offset=13 + get_local 3 + get_local 1 + i32.load8_u offset=14 + i32.store8 offset=14 + get_local 3 + get_local 1 + i32.load8_u offset=15 + i32.store8 offset=15 + get_local 3 + i32.const 16 + i32.add + set_local 3 + get_local 1 + i32.const 16 + i32.add + set_local 1 + end + block ;; label = @2 + get_local 4 + i32.const 8 + i32.and + i32.eqz + br_if 0 (;@2;) + get_local 3 + get_local 1 + i32.load8_u + i32.store8 + get_local 3 + get_local 1 + i32.load8_u offset=1 + i32.store8 offset=1 + get_local 3 + get_local 1 + i32.load8_u offset=2 + i32.store8 offset=2 + get_local 3 + get_local 1 + i32.load8_u offset=3 + i32.store8 offset=3 + get_local 3 + get_local 1 + i32.load8_u offset=4 + i32.store8 offset=4 + get_local 3 + get_local 1 + i32.load8_u offset=5 + i32.store8 offset=5 + get_local 3 + get_local 1 + i32.load8_u offset=6 + i32.store8 offset=6 + get_local 3 + get_local 1 + i32.load8_u offset=7 + i32.store8 offset=7 + get_local 3 + i32.const 8 + i32.add + set_local 3 + get_local 1 + i32.const 8 + i32.add + set_local 1 + end + block ;; label = @2 + get_local 4 + i32.const 4 + i32.and + i32.eqz + br_if 0 (;@2;) + get_local 3 + get_local 1 + i32.load8_u + i32.store8 + get_local 3 + get_local 1 + i32.load8_u offset=1 + i32.store8 offset=1 + get_local 3 + get_local 1 + i32.load8_u offset=2 + i32.store8 offset=2 + get_local 3 + get_local 1 + i32.load8_u offset=3 + i32.store8 offset=3 + get_local 3 + i32.const 4 + i32.add + set_local 3 + get_local 1 + i32.const 4 + i32.add + set_local 1 + end + block ;; label = @2 + get_local 4 + i32.const 2 + i32.and + i32.eqz + br_if 0 (;@2;) + get_local 3 + get_local 1 + i32.load8_u + i32.store8 + get_local 3 + get_local 1 + i32.load8_u offset=1 + i32.store8 offset=1 + get_local 3 + i32.const 2 + i32.add + set_local 3 + get_local 1 + i32.const 2 + i32.add + set_local 1 + end + get_local 4 + i32.const 1 + i32.and + i32.eqz + br_if 0 (;@1;) + get_local 3 + get_local 1 + i32.load8_u + i32.store8 + end + get_local 0) + (func (;44;) (type 0) (param i32 i32 i32) (result i32) + (local i32 i32 i32 i64) + block ;; label = @1 + get_local 2 + i32.eqz + br_if 0 (;@1;) + get_local 0 + get_local 1 + i32.store8 + get_local 0 + get_local 2 + i32.add + tee_local 3 + i32.const -1 + i32.add + get_local 1 + i32.store8 + get_local 2 + i32.const 3 + i32.lt_u + br_if 0 (;@1;) + get_local 0 + get_local 1 + i32.store8 offset=2 + get_local 0 + get_local 1 + i32.store8 offset=1 + get_local 3 + i32.const -3 + i32.add + get_local 1 + i32.store8 + get_local 3 + i32.const -2 + i32.add + get_local 1 + i32.store8 + get_local 2 + i32.const 7 + i32.lt_u + br_if 0 (;@1;) + get_local 0 + get_local 1 + i32.store8 offset=3 + get_local 3 + i32.const -4 + i32.add + get_local 1 + i32.store8 + get_local 2 + i32.const 9 + i32.lt_u + br_if 0 (;@1;) + get_local 0 + i32.const 0 + get_local 0 + i32.sub + i32.const 3 + i32.and + tee_local 4 + i32.add + tee_local 3 + get_local 1 + i32.const 255 + i32.and + i32.const 16843009 + i32.mul + tee_local 1 + i32.store + get_local 3 + get_local 2 + get_local 4 + i32.sub + i32.const -4 + i32.and + tee_local 4 + i32.add + tee_local 2 + i32.const -4 + i32.add + get_local 1 + i32.store + get_local 4 + i32.const 9 + i32.lt_u + br_if 0 (;@1;) + get_local 3 + get_local 1 + i32.store offset=8 + get_local 3 + get_local 1 + i32.store offset=4 + get_local 2 + i32.const -8 + i32.add + get_local 1 + i32.store + get_local 2 + i32.const -12 + i32.add + get_local 1 + i32.store + get_local 4 + i32.const 25 + i32.lt_u + br_if 0 (;@1;) + get_local 3 + get_local 1 + i32.store offset=24 + get_local 3 + get_local 1 + i32.store offset=20 + get_local 3 + get_local 1 + i32.store offset=16 + get_local 3 + get_local 1 + i32.store offset=12 + get_local 2 + i32.const -16 + i32.add + get_local 1 + i32.store + get_local 2 + i32.const -20 + i32.add + get_local 1 + i32.store + get_local 2 + i32.const -24 + i32.add + get_local 1 + i32.store + get_local 2 + i32.const -28 + i32.add + get_local 1 + i32.store + get_local 4 + get_local 3 + i32.const 4 + i32.and + i32.const 24 + i32.or + tee_local 5 + i32.sub + tee_local 2 + i32.const 32 + i32.lt_u + br_if 0 (;@1;) + get_local 1 + i64.extend_u/i32 + tee_local 6 + i64.const 32 + i64.shl + get_local 6 + i64.or + set_local 6 + get_local 3 + get_local 5 + i32.add + set_local 1 + loop ;; label = @2 + get_local 1 + get_local 6 + i64.store + get_local 1 + i32.const 24 + i32.add + get_local 6 + i64.store + get_local 1 + i32.const 16 + i32.add + get_local 6 + i64.store + get_local 1 + i32.const 8 + i32.add + get_local 6 + i64.store + get_local 1 + i32.const 32 + i32.add + set_local 1 + get_local 2 + i32.const -32 + i32.add + tee_local 2 + i32.const 31 + i32.gt_u + br_if 0 (;@2;) + end + end + get_local 0) + (func (;45;) (type 4) (param i32) (result i32) + (local i32 i32) + block ;; label = @1 + get_local 0 + call 46 + i32.const 1 + i32.add + tee_local 1 + call 14 + tee_local 2 + i32.eqz + br_if 0 (;@1;) + get_local 2 + get_local 0 + get_local 1 + call 43 + return + end + i32.const 0) + (func (;46;) (type 4) (param i32) (result i32) + (local i32 i32 i32) + get_local 0 + set_local 1 + block ;; label = @1 + block ;; label = @2 + block ;; label = @3 + get_local 0 + i32.const 3 + i32.and + i32.eqz + br_if 0 (;@3;) + get_local 0 + i32.load8_u + i32.eqz + br_if 1 (;@2;) + get_local 0 + i32.const 1 + i32.add + set_local 1 + loop ;; label = @4 + get_local 1 + i32.const 3 + i32.and + i32.eqz + br_if 1 (;@3;) + get_local 1 + i32.load8_u + set_local 2 + get_local 1 + i32.const 1 + i32.add + tee_local 3 + set_local 1 + get_local 2 + br_if 0 (;@4;) + end + get_local 3 + i32.const -1 + i32.add + get_local 0 + i32.sub + return + end + get_local 1 + i32.const -4 + i32.add + set_local 1 + loop ;; label = @3 + get_local 1 + i32.const 4 + i32.add + tee_local 1 + i32.load + tee_local 2 + i32.const -1 + i32.xor + get_local 2 + i32.const -16843009 + i32.add + i32.and + i32.const -2139062144 + i32.and + i32.eqz + br_if 0 (;@3;) + end + get_local 2 + i32.const 255 + i32.and + i32.eqz + br_if 1 (;@1;) + loop ;; label = @3 + get_local 1 + i32.load8_u offset=1 + set_local 2 + get_local 1 + i32.const 1 + i32.add + tee_local 3 + set_local 1 + get_local 2 + br_if 0 (;@3;) + end + get_local 3 + get_local 0 + i32.sub + return + end + get_local 0 + get_local 0 + i32.sub + return + end + get_local 1 + get_local 0 + i32.sub) + (table (;0;) 5 5 anyfunc) + (memory (;0;) 2) + (global (;0;) (mut i32) (i32.const 68256)) + (global (;1;) i32 (i32.const 68256)) + (global (;2;) i32 (i32.const 2716)) + (export "memory" (memory 0)) + (export "__heap_base" (global 1)) + (export "__data_end" (global 2)) + (export "_start" (func 12)) + (elem (i32.const 1) 38 36 40 42) + (data (i32.const 1024) "simple-wasi...\00") + (data (i32.const 1040) "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00") + (data (i32.const 2600) "\05\00\00\00\00\00\00\00\00\00\00\00\02\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\03\00\00\00\04\00\00\00(\06\00\00\00\04\00\00\00\00\00\00\00\00\00\00\01\00\00\00\00\00\00\00\0a\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00(\0a\00\00")) diff --git a/test/fixtures/wasi/subdir/input_link.txt b/test/fixtures/wasi/subdir/input_link.txt new file mode 120000 index 00000000000000..4a6d09bf82379f --- /dev/null +++ b/test/fixtures/wasi/subdir/input_link.txt @@ -0,0 +1 @@ +../input.txt \ No newline at end of file diff --git a/test/fixtures/wasi/subdir/loop1 b/test/fixtures/wasi/subdir/loop1 new file mode 120000 index 00000000000000..433d7fdbce1536 --- /dev/null +++ b/test/fixtures/wasi/subdir/loop1 @@ -0,0 +1 @@ +./loop2 \ No newline at end of file diff --git a/test/fixtures/wasi/subdir/loop2 b/test/fixtures/wasi/subdir/loop2 new file mode 120000 index 00000000000000..2907735afc0680 --- /dev/null +++ b/test/fixtures/wasi/subdir/loop2 @@ -0,0 +1 @@ +./loop1 \ No newline at end of file diff --git a/test/fixtures/wasi/subdir/outside.txt b/test/fixtures/wasi/subdir/outside.txt new file mode 120000 index 00000000000000..2e0694706746f7 --- /dev/null +++ b/test/fixtures/wasi/subdir/outside.txt @@ -0,0 +1 @@ +../../outside.txt \ No newline at end of file diff --git a/test/internet/test-dgram-multicast-set-interface-lo.js b/test/internet/test-dgram-multicast-set-interface-lo.js index 73ab31884d5eb6..e06969b7b23673 100644 --- a/test/internet/test-dgram-multicast-set-interface-lo.js +++ b/test/internet/test-dgram-multicast-set-interface-lo.js @@ -83,8 +83,14 @@ if (process.argv[2] !== 'child') { let i = 0; let done = 0; let timer = null; + + const killSubprocesses = (subprocesses) => { + for (const i in subprocesses) + subprocesses[i].kill(); + }; + // Exit the test if it doesn't succeed within the TIMEOUT. - timer = setTimeout(function() { + timer = setTimeout(() => { console.error('[PARENT] Responses were not received within %d ms.', TIMEOUT); console.error('[PARENT] Skip'); @@ -115,7 +121,7 @@ if (process.argv[2] !== 'child') { worker.messagesNeeded = messagesNeeded; // Handle the death of workers. - worker.on('exit', function(code, signal) { + worker.on('exit', (code) => { // Don't consider this a true death if the worker has finished // successfully or if the exit code is 0. if (worker.isDone || code === 0) { @@ -138,7 +144,7 @@ if (process.argv[2] !== 'child') { } }); - worker.on('message', function(msg) { + worker.on('message', (msg) => { if (msg.listening) { listening += 1; @@ -162,12 +168,12 @@ if (process.argv[2] !== 'child') { 'required number of ' + 'messages. Will now compare.'); - Object.keys(workers).forEach(function(pid) { + Object.keys(workers).forEach((pid) => { const worker = workers[pid]; let count = 0; - worker.messagesReceived.forEach(function(buf) { + worker.messagesReceived.forEach((buf) => { for (let i = 0; i < worker.messagesNeeded.length; ++i) { if (buf.toString() === worker.messagesNeeded[i]) { count++; @@ -201,15 +207,15 @@ if (process.argv[2] !== 'child') { // Don't bind the address explicitly when sending and start with // the OSes default multicast interface selection. sendSocket.bind(common.PORT, ANY[FAM]); - sendSocket.on('listening', function() { + sendSocket.on('listening', () => { console.error(`outgoing iface ${interfaceAddress}`); }); - sendSocket.on('close', function() { + sendSocket.on('close', () => { console.error('[PARENT] sendSocket closed'); }); - sendSocket.sendNext = function() { + sendSocket.sendNext = () => { const msg = messages[i++]; if (!msg) { @@ -228,7 +234,7 @@ if (process.argv[2] !== 'child') { buf.length, PORTS[msg.mcast], msg.mcast, - function(err) { + (err) => { assert.ifError(err); console.error('[PARENT] sent %s to %s:%s', util.inspect(buf.toString()), @@ -238,11 +244,6 @@ if (process.argv[2] !== 'child') { } ); }; - - function killSubprocesses(subprocesses) { - for (const i in subprocesses) - subprocesses[i].kill(); - } } if (process.argv[2] === 'child') { @@ -258,7 +259,7 @@ if (process.argv[2] === 'child') { reuseAddr: true }); - listenSocket.on('message', function(buf, rinfo) { + listenSocket.on('message', (buf, rinfo) => { // Examine udp messages only when they were sent by the parent. if (!buf.toString().startsWith(SESSION)) return; @@ -280,7 +281,7 @@ if (process.argv[2] === 'child') { }); - listenSocket.on('listening', function() { + listenSocket.on('listening', () => { listenSocket.addMembership(MULTICAST, IFACE); process.send({ listening: true }); }); diff --git a/test/js-native-api/test_typedarray/test.js b/test/js-native-api/test_typedarray/test.js index 5f51383254b7ab..0d9594d92933c7 100644 --- a/test/js-native-api/test_typedarray/test.js +++ b/test/js-native-api/test_typedarray/test.js @@ -87,8 +87,21 @@ arrayTypes.forEach((currentType) => { assert.ok(externalResult instanceof Int8Array); assert.strictEqual(externalResult.length, 3); assert.strictEqual(externalResult.byteLength, 3); + assert.ok(!test_typedarray.IsDetached(buffer.buffer)); test_typedarray.Detach(buffer); + assert.ok(test_typedarray.IsDetached(buffer.buffer)); assert.ok(externalResult instanceof Int8Array); assert.strictEqual(buffer.length, 0); assert.strictEqual(buffer.byteLength, 0); } + +{ + const buffer = new ArrayBuffer(128); + assert.ok(!test_typedarray.IsDetached(buffer)); +} + +{ + const buffer = test_typedarray.NullArrayBuffer(); + assert.ok(buffer instanceof ArrayBuffer); + assert.ok(test_typedarray.IsDetached(buffer)); +} diff --git a/test/js-native-api/test_typedarray/test_typedarray.c b/test/js-native-api/test_typedarray/test_typedarray.c index 9d7d394fca7c49..d8b64d06c0c57b 100644 --- a/test/js-native-api/test_typedarray/test_typedarray.c +++ b/test/js-native-api/test_typedarray/test_typedarray.c @@ -97,6 +97,15 @@ static napi_value External(napi_env env, napi_callback_info info) { return output_array; } + +static napi_value NullArrayBuffer(napi_env env, napi_callback_info info) { + static void* data = NULL; + napi_value arraybuffer; + NAPI_CALL(env, + napi_create_external_arraybuffer(env, data, 0, NULL, NULL, &arraybuffer)); + return arraybuffer; +} + static napi_value CreateTypedArray(napi_env env, napi_callback_info info) { size_t argc = 4; napi_value args[4]; @@ -183,13 +192,36 @@ static napi_value Detach(napi_env env, napi_callback_info info) { return NULL; } +static napi_value IsDetached(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + NAPI_ASSERT(env, argc == 1, "Wrong number of arguments."); + + napi_value array_buffer = args[0]; + bool is_arraybuffer; + NAPI_CALL(env, napi_is_arraybuffer(env, array_buffer, &is_arraybuffer)); + NAPI_ASSERT(env, is_arraybuffer, + "Wrong type of arguments. Expects an array buffer as first argument."); + + bool is_detached; + NAPI_CALL(env, napi_is_detached_arraybuffer(env, array_buffer, &is_detached)); + + napi_value result; + NAPI_CALL(env, napi_get_boolean(env, is_detached, &result)); + + return result; +} + EXTERN_C_START napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor descriptors[] = { DECLARE_NAPI_PROPERTY("Multiply", Multiply), DECLARE_NAPI_PROPERTY("External", External), + DECLARE_NAPI_PROPERTY("NullArrayBuffer", NullArrayBuffer), DECLARE_NAPI_PROPERTY("CreateTypedArray", CreateTypedArray), DECLARE_NAPI_PROPERTY("Detach", Detach), + DECLARE_NAPI_PROPERTY("IsDetached", IsDetached), }; NAPI_CALL(env, napi_define_properties( diff --git a/test/node-api/test_worker_buffer_callback/binding.gyp b/test/node-api/test_worker_buffer_callback/binding.gyp new file mode 100644 index 00000000000000..7ab6381930da9c --- /dev/null +++ b/test/node-api/test_worker_buffer_callback/binding.gyp @@ -0,0 +1,8 @@ +{ + 'targets': [ + { + 'target_name': 'binding', + 'sources': [ 'test_worker_buffer_callback.c' ] + } + ] +} diff --git a/test/node-api/test_worker_buffer_callback/test-free-called.js b/test/node-api/test_worker_buffer_callback/test-free-called.js new file mode 100644 index 00000000000000..2a3cc9e47c22ff --- /dev/null +++ b/test/node-api/test_worker_buffer_callback/test-free-called.js @@ -0,0 +1,17 @@ +'use strict'; +const common = require('../../common'); +const path = require('path'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); +const binding = path.resolve(__dirname, `./build/${common.buildType}/binding`); +const { getFreeCallCount } = require(binding); + +// Test that buffers allocated with a free callback through our APIs are +// released when a Worker owning it exits. + +const w = new Worker(`require(${JSON.stringify(binding)})`, { eval: true }); + +assert.strictEqual(getFreeCallCount(), 0); +w.on('exit', common.mustCall(() => { + assert.strictEqual(getFreeCallCount(), 1); +})); diff --git a/test/node-api/test_worker_buffer_callback/test.js b/test/node-api/test_worker_buffer_callback/test.js new file mode 100644 index 00000000000000..4884a27d39e6a2 --- /dev/null +++ b/test/node-api/test_worker_buffer_callback/test.js @@ -0,0 +1,15 @@ +'use strict'; +const common = require('../../common'); +const assert = require('assert'); +const { MessageChannel } = require('worker_threads'); +const { buffer } = require(`./build/${common.buildType}/binding`); + +// Test that buffers allocated with a free callback through our APIs are not +// transferred. + +const { port1 } = new MessageChannel(); +const origByteLength = buffer.byteLength; +port1.postMessage(buffer, [buffer]); + +assert.strictEqual(buffer.byteLength, origByteLength); +assert.notStrictEqual(buffer.byteLength, 0); diff --git a/test/node-api/test_worker_buffer_callback/test_worker_buffer_callback.c b/test/node-api/test_worker_buffer_callback/test_worker_buffer_callback.c new file mode 100644 index 00000000000000..b911fd86380644 --- /dev/null +++ b/test/node-api/test_worker_buffer_callback/test_worker_buffer_callback.c @@ -0,0 +1,43 @@ +#include +#include +#include +#include "../../js-native-api/common.h" + +uint32_t free_call_count = 0; +char data[] = "hello"; + +napi_value GetFreeCallCount(napi_env env, napi_callback_info info) { + napi_value value; + NAPI_CALL(env, napi_create_uint32(env, free_call_count, &value)); + return value; +} + +static void finalize_cb(napi_env env, void* finalize_data, void* hint) { + assert(finalize_data == data); + free_call_count++; +} + +NAPI_MODULE_INIT() { + napi_property_descriptor properties[] = { + DECLARE_NAPI_PROPERTY("getFreeCallCount", GetFreeCallCount) + }; + + NAPI_CALL(env, napi_define_properties( + env, exports, sizeof(properties) / sizeof(*properties), properties)); + + // This is a slight variation on the non-N-API test: We create an ArrayBuffer + // rather than a Node.js Buffer, since testing the latter would only test + // the same code paths and not the ones specific to N-API. + napi_value buffer; + NAPI_CALL(env, napi_create_external_arraybuffer( + env, + data, + sizeof(data), + finalize_cb, + NULL, + &buffer)); + + NAPI_CALL(env, napi_set_named_property(env, exports, "buffer", buffer)); + + return exports; +} diff --git a/test/parallel/test-async-hooks-promise-triggerid.js b/test/parallel/test-async-hooks-promise-triggerid.js index 467fddd359d835..b860d60999e1ef 100644 --- a/test/parallel/test-async-hooks-promise-triggerid.js +++ b/test/parallel/test-async-hooks-promise-triggerid.js @@ -13,8 +13,8 @@ async_hooks.createHook({ if (type === 'PROMISE') { // Check that the last known Promise is triggering the creation of // this one. - assert.strictEqual(promiseAsyncIds[promiseAsyncIds.length - 1] || 1, - triggerId); + assert.strictEqual(triggerId, + promiseAsyncIds[promiseAsyncIds.length - 1] || 1); promiseAsyncIds.push(id); } }, 3), diff --git a/test/parallel/test-buffer-writeint.js b/test/parallel/test-buffer-writeint.js index 05b0cd1ebbfada..0e812cc8886941 100644 --- a/test/parallel/test-buffer-writeint.js +++ b/test/parallel/test-buffer-writeint.js @@ -213,7 +213,7 @@ const errorOutOfBounds = common.expectsError({ }); // Test 1 to 6 bytes. - for (let i = 1; i < 6; i++) { + for (let i = 1; i <= 6; i++) { ['writeIntBE', 'writeIntLE'].forEach((fn) => { const min = -(2 ** (i * 8 - 1)); const max = 2 ** (i * 8 - 1) - 1; diff --git a/test/parallel/test-buffer-writeuint.js b/test/parallel/test-buffer-writeuint.js index 3823b74d565519..c7708d663eca80 100644 --- a/test/parallel/test-buffer-writeuint.js +++ b/test/parallel/test-buffer-writeuint.js @@ -170,7 +170,7 @@ const assert = require('assert'); }); // Test 1 to 6 bytes. - for (let i = 1; i < 6; i++) { + for (let i = 1; i <= 6; i++) { const range = i < 5 ? `= ${val - 1}` : ` 2 ** ${i * 8}`; const received = i > 4 ? String(val).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1_') : diff --git a/test/parallel/test-child-process-fork-args.js b/test/parallel/test-child-process-fork-args.js index 7c86616f3750c6..f2f5689d64d6ba 100644 --- a/test/parallel/test-child-process-fork-args.js +++ b/test/parallel/test-child-process-fork-args.js @@ -64,7 +64,7 @@ const expectedEnv = { foo: 'bar' }; argsLists.forEach((args) => { const cp = fork(fixtures.path('child-process-echo-options.js'), args, { - env: Object.assign({}, process.env, expectedEnv) + env: { ...process.env, ...expectedEnv } }); cp.on( diff --git a/test/parallel/test-child-process-fork-exec-path.js b/test/parallel/test-child-process-fork-exec-path.js index 6a99c5a8f55d62..38adfd72de4298 100644 --- a/test/parallel/test-child-process-fork-exec-path.js +++ b/test/parallel/test-child-process-fork-exec-path.js @@ -51,7 +51,7 @@ assert.strictEqual(fs.existsSync(copyPath), false); fs.copyFileSync(nodePath, copyPath, fs.constants.COPYFILE_FICLONE); fs.chmodSync(copyPath, '0755'); -const envCopy = Object.assign({}, process.env, { 'FORK': 'true' }); +const envCopy = { ...process.env, FORK: 'true' }; const child = fork(__filename, { execPath: copyPath, env: envCopy }); child.on('message', common.mustCall(function(recv) { assert.deepStrictEqual(recv, msg); diff --git a/test/parallel/test-cluster-bind-privileged-port.js b/test/parallel/test-cluster-bind-privileged-port.js index e95768a8fcc2b4..57aa4c735d480f 100644 --- a/test/parallel/test-cluster-bind-privileged-port.js +++ b/test/parallel/test-cluster-bind-privileged-port.js @@ -26,6 +26,9 @@ const common = require('../common'); if (common.isOSX) common.skip('macOS may allow ordinary processes to use any port'); +if (common.isIBMi) + common.skip('IBMi may allow ordinary processes to use any port'); + if (common.isWindows) common.skip('not reliable on Windows.'); diff --git a/test/parallel/test-code-cache.js b/test/parallel/test-code-cache.js index d01392f1ee6433..3c4488c557d524 100644 --- a/test/parallel/test-code-cache.js +++ b/test/parallel/test-code-cache.js @@ -33,6 +33,8 @@ const loadedModules = process.moduleLoadList // are all compiled without cache and we are doing the bookkeeping right. if (!process.features.cached_builtins) { console.log('The binary is not configured with code cache'); + assert(!process.config.variables.node_use_node_code_cache); + if (isMainThread) { assert.deepStrictEqual(compiledWithCache, new Set()); for (const key of loadedModules) { @@ -46,10 +48,7 @@ if (!process.features.cached_builtins) { assert.notDeepStrictEqual(compiledWithCache, new Set()); } } else { // Native compiled - assert.strictEqual( - process.config.variables.node_code_cache, - 'yes' - ); + assert(process.config.variables.node_use_node_code_cache); if (!isMainThread) { for (const key of [ 'internal/bootstrap/pre_execution' ]) { diff --git a/test/parallel/test-dns-resolveany.js b/test/parallel/test-dns-resolveany.js index bb15b1a38b4ad3..33beee847fd52c 100644 --- a/test/parallel/test-dns-resolveany.js +++ b/test/parallel/test-dns-resolveany.js @@ -11,7 +11,7 @@ const answers = [ { type: 'AAAA', address: '::42', ttl: 123 }, { type: 'MX', priority: 42, exchange: 'foobar.com', ttl: 124 }, { type: 'NS', value: 'foobar.org', ttl: 457 }, - { type: 'TXT', entries: [ 'v=spf1 ~all', 'xyz' ] }, + { type: 'TXT', entries: [ 'v=spf1 ~all', 'xyz\0foo' ] }, { type: 'PTR', value: 'baz.org', ttl: 987 }, { type: 'SOA', diff --git a/test/parallel/test-event-emitter-listeners.js b/test/parallel/test-event-emitter-listeners.js index 52a108ad0e29b6..eb1da829c95f21 100644 --- a/test/parallel/test-event-emitter-listeners.js +++ b/test/parallel/test-event-emitter-listeners.js @@ -26,10 +26,13 @@ const assert = require('assert'); const events = require('events'); function listener() {} + function listener2() {} + function listener3() { return 0; } + function listener4() { return 1; } diff --git a/test/parallel/test-event-emitter-remove-listeners.js b/test/parallel/test-event-emitter-remove-listeners.js index 925f828f93da36..8b9d6215601e9d 100644 --- a/test/parallel/test-event-emitter-remove-listeners.js +++ b/test/parallel/test-event-emitter-remove-listeners.js @@ -25,6 +25,7 @@ const assert = require('assert'); const EventEmitter = require('events'); function listener1() {} + function listener2() {} { diff --git a/test/parallel/test-finalization-group-error.js b/test/parallel/test-finalization-group-error.js index 0754370f2d4bfa..0685811f55b7f8 100644 --- a/test/parallel/test-finalization-group-error.js +++ b/test/parallel/test-finalization-group-error.js @@ -18,6 +18,10 @@ setTimeout(() => { name: 'Error', message: 'test', }); + + // Give the callbacks scheduled by global.gc() time to run, as the underlying + // uv_async_t is unref’ed. + setTimeout(() => {}, 200); }, 200); process.on('uncaughtException', common.mustCall()); diff --git a/test/parallel/test-finalization-group-regular-gc.js b/test/parallel/test-finalization-group-regular-gc.js new file mode 100644 index 00000000000000..7a4a4797eadcf0 --- /dev/null +++ b/test/parallel/test-finalization-group-regular-gc.js @@ -0,0 +1,25 @@ +// Flags: --harmony-weak-refs +'use strict'; +require('../common'); +const assert = require('assert'); + +// Test that finalization callbacks do not crash when caused through a regular +// GC (not global.gc()). + +const start = Date.now(); +const g = new globalThis.FinalizationGroup(() => { + const diff = Date.now() - start; + assert(diff < 10000, `${diff} >= 10000`); +}); +g.register({}, 42); + +setImmediate(() => { + const arr = []; + // Build up enough memory usage to hopefully trigger a platform task but not + // enough to trigger GC as an interrupt. + while (arr.length < 1000000) arr.push([]); + + setTimeout(() => { + g; // Keep reference alive. + }, 200000).unref(); +}); diff --git a/test/parallel/test-fs-opendir.js b/test/parallel/test-fs-opendir.js index 7ae6186b28518a..abfbb2bb701b03 100644 --- a/test/parallel/test-fs-opendir.js +++ b/test/parallel/test-fs-opendir.js @@ -91,6 +91,10 @@ assert.throws(function() { fs.opendirSync(__filename); }, /Error: ENOTDIR: not a directory/); +assert.throws(function() { + fs.opendir(__filename); +}, /TypeError \[ERR_INVALID_CALLBACK\]: Callback must be a function/); + fs.opendir(__filename, common.mustCall(function(e) { assert.strictEqual(e.code, 'ENOTDIR'); })); diff --git a/test/parallel/test-fs-read.js b/test/parallel/test-fs-read.js index 9d9cf0db3a7655..e6fe6231ffd9ec 100644 --- a/test/parallel/test-fs-read.js +++ b/test/parallel/test-fs-read.js @@ -68,6 +68,10 @@ test(new Uint8Array(expected.length), })); } +assert.throws(() => new fs.Dir(), { + code: 'ERR_MISSING_ARGS', +}); + assert.throws( () => fs.read(fd, Buffer.alloc(1), 0, 1, 0), { diff --git a/test/parallel/test-fs-realpath.js b/test/parallel/test-fs-realpath.js index 8b3bb689675dc3..2642f5184271b6 100644 --- a/test/parallel/test-fs-realpath.js +++ b/test/parallel/test-fs-realpath.js @@ -490,6 +490,7 @@ function test_abs_with_kids(realpath, realpathSync, cb) { try { fs.rmdirSync(root + folder); } catch {} }); } + function setup() { cleanup(); ['', diff --git a/test/parallel/test-fs-rmdir-recursive.js b/test/parallel/test-fs-rmdir-recursive.js index 5f630b686a19e6..628bba5d6fc4ab 100644 --- a/test/parallel/test-fs-rmdir-recursive.js +++ b/test/parallel/test-fs-rmdir-recursive.js @@ -155,13 +155,13 @@ function removeAsync(dir) { // Test input validation. { const defaults = { - emfileWait: 1000, - maxBusyTries: 3, + retryDelay: 100, + maxRetries: 0, recursive: false }; const modified = { - emfileWait: 953, - maxBusyTries: 5, + retryDelay: 953, + maxRetries: 5, recursive: true }; @@ -169,10 +169,10 @@ function removeAsync(dir) { assert.deepStrictEqual(validateRmdirOptions({}), defaults); assert.deepStrictEqual(validateRmdirOptions(modified), modified); assert.deepStrictEqual(validateRmdirOptions({ - maxBusyTries: 99 + maxRetries: 99 }), { - emfileWait: 1000, - maxBusyTries: 99, + retryDelay: 100, + maxRetries: 99, recursive: false }); @@ -197,18 +197,18 @@ function removeAsync(dir) { }); common.expectsError(() => { - validateRmdirOptions({ emfileWait: -1 }); + validateRmdirOptions({ retryDelay: -1 }); }, { code: 'ERR_OUT_OF_RANGE', type: RangeError, - message: /^The value of "emfileWait" is out of range\./ + message: /^The value of "retryDelay" is out of range\./ }); common.expectsError(() => { - validateRmdirOptions({ maxBusyTries: -1 }); + validateRmdirOptions({ maxRetries: -1 }); }, { code: 'ERR_OUT_OF_RANGE', type: RangeError, - message: /^The value of "maxBusyTries" is out of range\./ + message: /^The value of "maxRetries" is out of range\./ }); } diff --git a/test/parallel/test-http-generic-streams.js b/test/parallel/test-http-generic-streams.js index aea371432a3d07..706cba7b385957 100644 --- a/test/parallel/test-http-generic-streams.js +++ b/test/parallel/test-http-generic-streams.js @@ -138,3 +138,17 @@ const MakeDuplexPair = require('../common/duplexpair'); req.write(testData); req.end(); } + +// Test 5: The client sends garbage. +{ + const server = http.createServer(common.mustNotCall()); + + const { clientSide, serverSide } = MakeDuplexPair(); + server.emit('connection', serverSide); + + server.on('clientError', common.mustCall()); + + // Send something that is not an HTTP request. + clientSide.end( + 'I’m reading a book about anti-gravity. It’s impossible to put down!'); +} diff --git a/test/parallel/test-http-max-header-size-per-stream.js b/test/parallel/test-http-max-header-size-per-stream.js new file mode 100644 index 00000000000000..5edb8d3a954cef --- /dev/null +++ b/test/parallel/test-http-max-header-size-per-stream.js @@ -0,0 +1,82 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); +const MakeDuplexPair = require('../common/duplexpair'); + +// Test that setting the `maxHeaderSize` option works on a per-stream-basis. + +// Test 1: The server sends larger headers than what would otherwise be allowed. +{ + const { clientSide, serverSide } = MakeDuplexPair(); + + const req = http.request({ + createConnection: common.mustCall(() => clientSide), + maxHeaderSize: http.maxHeaderSize * 4 + }, common.mustCall((res) => { + assert.strictEqual(res.headers.hello, 'A'.repeat(http.maxHeaderSize * 3)); + res.resume(); // We don’t actually care about contents. + res.on('end', common.mustCall()); + })); + req.end(); + + serverSide.resume(); // Dump the request + serverSide.end('HTTP/1.1 200 OK\r\n' + + 'Hello: ' + 'A'.repeat(http.maxHeaderSize * 3) + '\r\n' + + 'Content-Length: 0\r\n' + + '\r\n\r\n'); +} + +// Test 2: The same as Test 1 except without the option, to make sure it fails. +{ + const { clientSide, serverSide } = MakeDuplexPair(); + + const req = http.request({ + createConnection: common.mustCall(() => clientSide) + }, common.mustNotCall()); + req.end(); + req.on('error', common.mustCall()); + + serverSide.resume(); // Dump the request + serverSide.end('HTTP/1.1 200 OK\r\n' + + 'Hello: ' + 'A'.repeat(http.maxHeaderSize * 3) + '\r\n' + + 'Content-Length: 0\r\n' + + '\r\n\r\n'); +} + +// Test 3: The client sends larger headers than what would otherwise be allowed. +{ + const testData = 'Hello, World!\n'; + const server = http.createServer( + { maxHeaderSize: http.maxHeaderSize * 4 }, + common.mustCall((req, res) => { + res.statusCode = 200; + res.setHeader('Content-Type', 'text/plain'); + res.end(testData); + })); + + server.on('clientError', common.mustNotCall()); + + const { clientSide, serverSide } = MakeDuplexPair(); + serverSide.server = server; + server.emit('connection', serverSide); + + clientSide.write('GET / HTTP/1.1\r\n' + + 'Hello: ' + 'A'.repeat(http.maxHeaderSize * 3) + '\r\n' + + '\r\n\r\n'); +} + +// Test 4: The same as Test 3 except without the option, to make sure it fails. +{ + const server = http.createServer(common.mustNotCall()); + + server.on('clientError', common.mustCall()); + + const { clientSide, serverSide } = MakeDuplexPair(); + serverSide.server = server; + server.emit('connection', serverSide); + + clientSide.write('GET / HTTP/1.1\r\n' + + 'Hello: ' + 'A'.repeat(http.maxHeaderSize * 3) + '\r\n' + + '\r\n\r\n'); +} diff --git a/test/sequential/test-http-max-http-headers.js b/test/parallel/test-http-max-http-headers.js similarity index 94% rename from test/sequential/test-http-max-http-headers.js rename to test/parallel/test-http-max-http-headers.js index 9ee4d8c352928b..04fdebd48cfd61 100644 --- a/test/sequential/test-http-max-http-headers.js +++ b/test/parallel/test-http-max-http-headers.js @@ -24,7 +24,7 @@ function once(cb) { } function finished(client, callback) { - 'abort error end'.split(' ').forEach((e) => { + ['abort', 'error', 'end'].forEach((e) => { client.on(e, once(() => setImmediate(callback))); }); } @@ -37,8 +37,7 @@ function fillHeaders(headers, currentSize, valid = false) { // Generate valid headers if (valid) { - // TODO(mcollina): understand why -32 is needed instead of -1 - headers = headers.slice(0, -32); + headers = headers.slice(0, -1); } return headers + '\r\n\r\n'; } @@ -89,7 +88,7 @@ function test1() { headers = fillHeaders(headers, currentSize); const server = net.createServer((sock) => { - sock.once('data', (chunk) => { + sock.once('data', () => { writeHeaders(sock, headers); sock.resume(); }); @@ -134,7 +133,7 @@ const test2 = common.mustCall(() => { client.resume(); }); - finished(client, common.mustCall((err) => { + finished(client, common.mustCall(() => { server.close(test3); })); })); diff --git a/test/parallel/test-http-server-consumed-timeout.js b/test/parallel/test-http-server-consumed-timeout.js new file mode 100644 index 00000000000000..865169ca010375 --- /dev/null +++ b/test/parallel/test-http-server-consumed-timeout.js @@ -0,0 +1,86 @@ +'use strict'; + +const common = require('../common'); + +const assert = require('assert'); +const http = require('http'); + +const durationBetweenIntervals = []; +let timeoutTooShort = false; +const TIMEOUT = common.platformTimeout(200); +const INTERVAL = Math.floor(TIMEOUT / 8); + +runTest(TIMEOUT); + +function runTest(timeoutDuration) { + let intervalWasInvoked = false; + let newTimeoutDuration = 0; + const closeCallback = (err) => { + assert.ifError(err); + if (newTimeoutDuration) { + runTest(newTimeoutDuration); + } + }; + + const server = http.createServer((req, res) => { + server.close(common.mustCall(closeCallback)); + + res.writeHead(200); + res.flushHeaders(); + + req.setTimeout(timeoutDuration, () => { + if (!intervalWasInvoked) { + // Interval wasn't invoked, probably because the machine is busy with + // other things. Try again with a longer timeout. + newTimeoutDuration = timeoutDuration * 2; + console.error('The interval was not invoked.'); + console.error(`Trying w/ timeout of ${newTimeoutDuration}.`); + return; + } + + if (timeoutTooShort) { + intervalWasInvoked = false; + timeoutTooShort = false; + newTimeoutDuration = + Math.max(...durationBetweenIntervals, timeoutDuration) * 2; + console.error(`Time between intervals: ${durationBetweenIntervals}`); + console.error(`Trying w/ timeout of ${newTimeoutDuration}`); + return; + } + + assert.fail('Request timeout should not fire'); + }); + + req.resume(); + req.once('end', () => { + res.end(); + }); + }); + + server.listen(0, common.mustCall(() => { + const req = http.request({ + port: server.address().port, + method: 'POST' + }, () => { + let lastIntervalTimestamp = Date.now(); + const interval = setInterval(() => { + const lastDuration = Date.now() - lastIntervalTimestamp; + durationBetweenIntervals.push(lastDuration); + lastIntervalTimestamp = Date.now(); + if (lastDuration > timeoutDuration / 2) { + // The interval is supposed to be about 1/8 of the timeout duration. + // If it's running so infrequently that it's greater than 1/2 the + // timeout duration, then run the test again with a longer timeout. + timeoutTooShort = true; + } + intervalWasInvoked = true; + req.write('a'); + }, INTERVAL); + setTimeout(() => { + clearInterval(interval); + req.end(); + }, timeoutDuration); + }); + req.write('.'); + })); +} diff --git a/test/parallel/test-http-unix-socket-keep-alive.js b/test/parallel/test-http-unix-socket-keep-alive.js index 11b3d9b39264a4..fd3c6a7b1608ff 100644 --- a/test/parallel/test-http-unix-socket-keep-alive.js +++ b/test/parallel/test-http-unix-socket-keep-alive.js @@ -27,6 +27,7 @@ function asyncLoop(fn, times, cb) { } }); } + function makeKeepAliveRequest(cb) { http.get({ socketPath: common.PIPE, diff --git a/test/parallel/test-http2-createsecureserver-options.js b/test/parallel/test-http2-createsecureserver-options.js index 4ef85a45b5b84b..84d75e23b334ec 100644 --- a/test/parallel/test-http2-createsecureserver-options.js +++ b/test/parallel/test-http2-createsecureserver-options.js @@ -7,7 +7,7 @@ if (!common.hasCrypto) const assert = require('assert'); const http2 = require('http2'); -// Error if invalid options are passed to createSecureServer +// Error if invalid options are passed to createSecureServer. const invalidOptions = [() => {}, 1, 'test', null, Symbol('test')]; invalidOptions.forEach((invalidOption) => { assert.throws( @@ -21,7 +21,7 @@ invalidOptions.forEach((invalidOption) => { ); }); -// Error if invalid options.settings are passed to createSecureServer +// Error if invalid options.settings are passed to createSecureServer. invalidOptions.forEach((invalidSettingsOption) => { assert.throws( () => http2.createSecureServer({ settings: invalidSettingsOption }), @@ -33,3 +33,46 @@ invalidOptions.forEach((invalidSettingsOption) => { } ); }); + +// Test that http2.createSecureServer validates input options. +Object.entries({ + maxSessionInvalidFrames: [ + { + val: -1, + err: { + name: 'RangeError', + code: 'ERR_OUT_OF_RANGE', + }, + }, + { + val: Number.NEGATIVE_INFINITY, + err: { + name: 'RangeError', + code: 'ERR_OUT_OF_RANGE', + }, + }, + ], + maxSessionRejectedStreams: [ + { + val: -1, + err: { + name: 'RangeError', + code: 'ERR_OUT_OF_RANGE', + }, + }, + { + val: Number.NEGATIVE_INFINITY, + err: { + name: 'RangeError', + code: 'ERR_OUT_OF_RANGE', + }, + }, + ], +}).forEach(([opt, tests]) => { + tests.forEach(({ val, err }) => { + assert.throws( + () => http2.createSecureServer({ [opt]: val }), + err + ); + }); +}); diff --git a/test/parallel/test-http2-createserver-options.js b/test/parallel/test-http2-createserver-options.js index d322506f55e3e0..6f3d4b56933169 100644 --- a/test/parallel/test-http2-createserver-options.js +++ b/test/parallel/test-http2-createserver-options.js @@ -7,7 +7,7 @@ if (!common.hasCrypto) const assert = require('assert'); const http2 = require('http2'); -// Error if invalid options are passed to createServer +// Error if invalid options are passed to createServer. const invalidOptions = [1, true, 'test', null, Symbol('test')]; invalidOptions.forEach((invalidOption) => { assert.throws( @@ -21,7 +21,7 @@ invalidOptions.forEach((invalidOption) => { ); }); -// Error if invalid options.settings are passed to createServer +// Error if invalid options.settings are passed to createServer. invalidOptions.forEach((invalidSettingsOption) => { assert.throws( () => http2.createServer({ settings: invalidSettingsOption }), @@ -33,3 +33,46 @@ invalidOptions.forEach((invalidSettingsOption) => { } ); }); + +// Test that http2.createServer validates input options. +Object.entries({ + maxSessionInvalidFrames: [ + { + val: -1, + err: { + name: 'RangeError', + code: 'ERR_OUT_OF_RANGE', + }, + }, + { + val: Number.NEGATIVE_INFINITY, + err: { + name: 'RangeError', + code: 'ERR_OUT_OF_RANGE', + }, + }, + ], + maxSessionRejectedStreams: [ + { + val: -1, + err: { + name: 'RangeError', + code: 'ERR_OUT_OF_RANGE', + }, + }, + { + val: Number.NEGATIVE_INFINITY, + err: { + name: 'RangeError', + code: 'ERR_OUT_OF_RANGE', + }, + }, + ] +}).forEach(([opt, tests]) => { + tests.forEach(({ val, err }) => { + assert.throws( + () => http2.createServer({ [opt]: val }), + err + ); + }); +}); diff --git a/test/parallel/test-http2-large-writes-session-memory-leak.js b/test/parallel/test-http2-large-writes-session-memory-leak.js new file mode 100644 index 00000000000000..641923c06c9133 --- /dev/null +++ b/test/parallel/test-http2-large-writes-session-memory-leak.js @@ -0,0 +1,55 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const fixtures = require('../common/fixtures'); +const http2 = require('http2'); + +// Regression test for https://github.com/nodejs/node/issues/29223. +// There was a "leak" in the accounting of session memory leading +// to streams eventually failing with NGHTTP2_ENHANCE_YOUR_CALM. + +const server = http2.createSecureServer({ + key: fixtures.readKey('agent2-key.pem'), + cert: fixtures.readKey('agent2-cert.pem'), +}); + +// Simple server that sends 200k and closes the stream. +const data200k = 'a'.repeat(200 * 1024); +server.on('stream', (stream) => { + stream.write(data200k); + stream.end(); +}); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`https://localhost:${server.address().port}`, { + ca: fixtures.readKey('agent2-cert.pem'), + servername: 'agent2', + + // Set maxSessionMemory to 1MB so the leak causes errors faster. + maxSessionMemory: 1 + }); + + // Repeatedly create a new stream and read the incoming data. Even though we + // only have one stream active at a time, prior to the fix for #29223, + // session memory would steadily increase and we'd eventually hit the 1MB + // maxSessionMemory limit and get NGHTTP2_ENHANCE_YOUR_CALM errors trying to + // create new streams. + let streamsLeft = 50; + function newStream() { + const stream = client.request({ ':path': '/' }); + + stream.on('data', () => { }); + + stream.on('close', () => { + if (streamsLeft-- > 0) { + newStream(); + } else { + client.destroy(); + server.close(); + } + }); + } + + newStream(); +})); diff --git a/test/parallel/test-http2-max-invalid-frames.js b/test/parallel/test-http2-max-invalid-frames.js new file mode 100644 index 00000000000000..597bd8e81197b5 --- /dev/null +++ b/test/parallel/test-http2-max-invalid-frames.js @@ -0,0 +1,86 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const http2 = require('http2'); +const net = require('net'); + +// Verify that creating a number of invalid HTTP/2 streams will +// result in the peer closing the session within maxSessionInvalidFrames +// frames. + +const maxSessionInvalidFrames = 100; +const server = http2.createServer({ maxSessionInvalidFrames }); +server.on('stream', (stream) => { + stream.respond({ + 'content-type': 'text/plain', + ':status': 200 + }); + stream.end('Hello, world!\n'); +}); + +server.listen(0, () => { + const h2header = Buffer.alloc(9); + const conn = net.connect(server.address().port); + + conn.write('PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n'); + + h2header[3] = 4; // Send a settings frame. + conn.write(Buffer.from(h2header)); + + let inbuf = Buffer.alloc(0); + let state = 'settingsHeader'; + let settingsFrameLength; + conn.on('data', (chunk) => { + inbuf = Buffer.concat([inbuf, chunk]); + switch (state) { + case 'settingsHeader': + if (inbuf.length < 9) return; + settingsFrameLength = inbuf.readIntBE(0, 3); + inbuf = inbuf.slice(9); + state = 'readingSettings'; + // Fallthrough + case 'readingSettings': + if (inbuf.length < settingsFrameLength) return; + inbuf = inbuf.slice(settingsFrameLength); + h2header[3] = 4; // Send a settings ACK. + h2header[4] = 1; + conn.write(Buffer.from(h2header)); + state = 'ignoreInput'; + writeRequests(); + } + }); + + let gotError = false; + let streamId = 1; + let reqCount = 0; + + function writeRequests() { + for (let i = 1; i < 10 && !gotError; i++) { + h2header[3] = 1; // HEADERS + h2header[4] = 0x5; // END_HEADERS|END_STREAM + h2header.writeIntBE(1, 0, 3); // Length: 1 + h2header.writeIntBE(streamId, 5, 4); // Stream ID + streamId += 2; + // 0x88 = :status: 200 + if (!conn.write(Buffer.concat([h2header, Buffer.from([0x88])]))) { + break; + } + reqCount++; + } + // Timeout requests to slow down the rate so we get more accurate reqCount. + if (!gotError) + setTimeout(writeRequests, 10); + } + + conn.once('error', common.mustCall(() => { + gotError = true; + assert.ok(Math.abs(reqCount - maxSessionInvalidFrames) < 100, + `Request count (${reqCount}) must be around (±100)` + + ` maxSessionInvalidFrames option (${maxSessionInvalidFrames})`); + conn.destroy(); + server.close(); + })); +}); diff --git a/test/parallel/test-http2-reset-flood.js b/test/parallel/test-http2-reset-flood.js index 9977bfd1a3e669..25520f81f23717 100644 --- a/test/parallel/test-http2-reset-flood.js +++ b/test/parallel/test-http2-reset-flood.js @@ -13,7 +13,7 @@ const { Worker, parentPort } = require('worker_threads'); // the two event loops intermixing, as we are writing in a busy loop here. if (process.env.HAS_STARTED_WORKER) { - const server = http2.createServer(); + const server = http2.createServer({ maxSessionInvalidFrames: 100 }); server.on('stream', (stream) => { stream.respond({ 'content-type': 'text/plain', @@ -59,19 +59,22 @@ const worker = new Worker(__filename).on('message', common.mustCall((port) => { }); let gotError = false; + let streamId = 1; function writeRequests() { - for (let i = 1; !gotError; i += 2) { + for (let i = 1; i < 10 && !gotError; i++) { h2header[3] = 1; // HEADERS h2header[4] = 0x5; // END_HEADERS|END_STREAM h2header.writeIntBE(1, 0, 3); // Length: 1 - h2header.writeIntBE(i, 5, 4); // Stream ID + h2header.writeIntBE(streamId, 5, 4); // Stream ID + streamId += 2; // 0x88 = :status: 200 if (!conn.write(Buffer.concat([h2header, Buffer.from([0x88])]))) { - process.nextTick(writeRequests); break; } } + if (!gotError) + setImmediate(writeRequests); } conn.once('error', common.mustCall(() => { diff --git a/test/parallel/test-icu-minimum-version.js b/test/parallel/test-icu-minimum-version.js new file mode 100644 index 00000000000000..3daafc364ae55f --- /dev/null +++ b/test/parallel/test-icu-minimum-version.js @@ -0,0 +1,20 @@ +'use strict'; + +// Tests that the minimum ICU version for Node.js is at least the minimum ICU +// version for V8. + +require('../common'); +const assert = require('assert'); +const path = require('path'); +const { readFileSync } = require('fs'); + +const srcRoot = path.join(__dirname, '..', '..'); +const icuVersionsFile = path.join(srcRoot, 'tools', 'icu', 'icu_versions.json'); +const { minimum_icu: minimumICU } = require(icuVersionsFile); +const v8SrcFile = path.join(srcRoot, + 'deps', 'v8', 'src', 'objects', 'intl-objects.h'); +const v8Src = readFileSync(v8SrcFile, { encoding: 'utf8' }); +const v8MinimumICU = v8Src.match(/#define\s+V8_MINIMUM_ICU_VERSION\s+(\d+)/)[1]; +assert.ok(minimumICU >= Number(v8MinimumICU), + `minimum ICU version in ${icuVersionsFile} (${minimumICU}) ` + + `must be at least that in ${v8SrcFile} (${Number(v8MinimumICU)})`); diff --git a/test/parallel/test-npm-install.js b/test/parallel/test-npm-install.js index 069465466df682..1eec5f57ad6db9 100644 --- a/test/parallel/test-npm-install.js +++ b/test/parallel/test-npm-install.js @@ -38,12 +38,12 @@ const pkgPath = path.join(installDir, 'package.json'); fs.writeFileSync(pkgPath, pkgContent); -const env = Object.assign({}, process.env, { - PATH: path.dirname(process.execPath), - NPM_CONFIG_PREFIX: path.join(npmSandbox, 'npm-prefix'), - NPM_CONFIG_TMP: path.join(npmSandbox, 'npm-tmp'), - HOME: homeDir, -}); +const env = { ...process.env, + PATH: path.dirname(process.execPath), + NPM_CONFIG_PREFIX: path.join(npmSandbox, 'npm-prefix'), + NPM_CONFIG_TMP: path.join(npmSandbox, 'npm-tmp'), + HOME: homeDir, +}; exec(`${process.execPath} ${npmPath} install`, { cwd: installDir, diff --git a/test/parallel/test-repl-import-referrer.js b/test/parallel/test-repl-import-referrer.js new file mode 100644 index 00000000000000..33bc442e6f69c8 --- /dev/null +++ b/test/parallel/test-repl-import-referrer.js @@ -0,0 +1,24 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cp = require('child_process'); +const fixtures = require('../common/fixtures'); + +const args = ['--interactive', '--experimental-repl-await']; +const opts = { cwd: fixtures.path('es-modules') }; +const child = cp.spawn(process.execPath, args, opts); + +let output = ''; +child.stdout.setEncoding('utf8'); +child.stdout.on('data', (data) => { + output += data; +}); + +child.on('exit', common.mustCall(() => { + const results = output.replace(/^> /mg, '').split('\n').slice(2); + assert.deepStrictEqual(results, ['[Module] { message: \'A message\' }', '']); +})); + +child.stdin.write('await import(\'./message.mjs\');\n'); +child.stdin.write('.exit'); +child.stdin.end(); diff --git a/test/parallel/test-repl-options.js b/test/parallel/test-repl-options.js index cbb5a9a6e580ec..9f40c6279bd64c 100644 --- a/test/parallel/test-repl-options.js +++ b/test/parallel/test-repl-options.js @@ -62,6 +62,7 @@ assert.strictEqual(r1.useColors, r1.rli.terminal); // 2 function writer() {} + function evaler() {} const r2 = repl.start({ input: stream, diff --git a/test/parallel/test-repl-save-load.js b/test/parallel/test-repl-save-load.js index 66b0f400a91764..ef9ff8f6498877 100644 --- a/test/parallel/test-repl-save-load.js +++ b/test/parallel/test-repl-save-load.js @@ -44,8 +44,8 @@ testMe._domain.on('error', function(reason) { }); const testFile = [ - 'var top = function() {', - 'var inner = {one:1};' + 'let top = function() {', + 'let inner = {one:1};' ]; const saveFileName = join(tmpdir.path, 'test.save.js'); diff --git a/test/parallel/test-require-symlink.js b/test/parallel/test-require-symlink.js index 5c9b0fbdef3595..2e8a03ac820df5 100644 --- a/test/parallel/test-require-symlink.js +++ b/test/parallel/test-require-symlink.js @@ -78,7 +78,7 @@ function test() { // Also verify that symlinks works for setting preserve via env variables const childEnv = spawn(node, [linkScript], { - env: Object.assign({}, process.env, { NODE_PRESERVE_SYMLINKS: '1' }) + env: { ...process.env, NODE_PRESERVE_SYMLINKS: '1' } }); childEnv.on('close', function(code, signal) { assert.strictEqual(code, 0); diff --git a/test/sequential/test-set-http-max-http-headers.js b/test/parallel/test-set-http-max-http-headers.js similarity index 91% rename from test/sequential/test-set-http-max-http-headers.js rename to test/parallel/test-set-http-max-http-headers.js index cfe1ed69537743..c4df779d2bd4fa 100644 --- a/test/sequential/test-set-http-max-http-headers.js +++ b/test/parallel/test-set-http-max-http-headers.js @@ -37,8 +37,7 @@ test(function(cb) { NODE_DEBUG: 'http' }); - // Validate that the test fails if the max header size is too small. - // Validate that the test now passes if the same limit becomes large enough. + // Validate that the test now passes if the same limit is large enough. const args = ['--expose-internals', '--max-http-header-size=1024', testName, @@ -76,8 +75,7 @@ if (!process.config.variables.node_without_node_options) { }); test(function(cb) { - // Validate that the test now passes if the same limit - // becomes large enough. + // Validate that the test now passes if the same limit is large enough. const args = ['--expose-internals', testName, '1024']; const cp = spawn(process.execPath, args, { env, stdio: 'inherit' }); diff --git a/test/parallel/test-signal-handler-remove-on-exit.js b/test/parallel/test-signal-handler-remove-on-exit.js new file mode 100644 index 00000000000000..1c87497172e66a --- /dev/null +++ b/test/parallel/test-signal-handler-remove-on-exit.js @@ -0,0 +1,9 @@ +'use strict'; +require('../common'); + +// Regression test for https://github.com/nodejs/node/issues/30581 +// This script should not crash. + +function dummy() {} +process.on('SIGINT', dummy); +process.on('exit', () => process.removeListener('SIGINT', dummy)); diff --git a/test/parallel/test-source-map.js b/test/parallel/test-source-map.js index c14892890cb195..71130441438dcc 100644 --- a/test/parallel/test-source-map.js +++ b/test/parallel/test-source-map.js @@ -253,6 +253,18 @@ function nextdir() { } } +// trace.length === 0 . +{ + const output = spawnSync(process.execPath, [ + '--enable-source-maps', + require.resolve('../fixtures/source-map/emptyStackError.js') + ]); + + assert.ok( + output.stderr.toString().match('emptyStackError') + ); +} + function getSourceMapFromCache(fixtureFile, coverageDirectory) { const jsonFiles = fs.readdirSync(coverageDirectory); for (const jsonFile of jsonFiles) { diff --git a/test/parallel/test-stream-transform-flush-data.js b/test/parallel/test-stream-transform-flush-data.js index 6d6aedd3a72bf8..51e2c8bc5254e3 100644 --- a/test/parallel/test-stream-transform-flush-data.js +++ b/test/parallel/test-stream-transform-flush-data.js @@ -12,6 +12,7 @@ const expected = 'asdf'; function _transform(d, e, n) { n(); } + function _flush(n) { n(null, expected); } diff --git a/test/sequential/test-stream-writable-clear-buffer.js b/test/parallel/test-stream-writable-clear-buffer.js similarity index 53% rename from test/sequential/test-stream-writable-clear-buffer.js rename to test/parallel/test-stream-writable-clear-buffer.js index dc859e3fb6b362..c4d7ae151a38fb 100644 --- a/test/sequential/test-stream-writable-clear-buffer.js +++ b/test/parallel/test-stream-writable-clear-buffer.js @@ -1,8 +1,10 @@ 'use strict'; + +// This test ensures that the _writeableState.bufferedRequestCount and +// the actual buffered request count are the same. + const common = require('../common'); const Stream = require('stream'); -// This test ensures that the _writeableState.bufferedRequestCount and -// the actual buffered request count are the same const assert = require('assert'); class StreamWritable extends Stream.Writable { @@ -10,11 +12,11 @@ class StreamWritable extends Stream.Writable { super({ objectMode: true }); } - // We need a timeout like on the original issue thread - // otherwise the code will never reach our test case - // this means this should go on the sequential folder. + // Refs: https://github.com/nodejs/node/issues/6758 + // We need a timer like on the original issue thread. + // Otherwise the code will never reach our test case. _write(chunk, encoding, cb) { - setTimeout(cb, common.platformTimeout(10)); + setImmediate(cb); } } @@ -22,13 +24,12 @@ const testStream = new StreamWritable(); testStream.cork(); for (let i = 1; i <= 5; i++) { - testStream.write(i, function() { + testStream.write(i, common.mustCall(() => { assert.strictEqual( testStream._writableState.bufferedRequestCount, - testStream._writableState.getBuffer().length, - 'bufferedRequestCount variable is different from the actual length of' + - ' the buffer'); - }); + testStream._writableState.getBuffer().length + ); + })); } testStream.end(); diff --git a/test/parallel/test-stream-writable-samecb-singletick.js b/test/parallel/test-stream-writable-samecb-singletick.js new file mode 100644 index 00000000000000..e7dfa648797821 --- /dev/null +++ b/test/parallel/test-stream-writable-samecb-singletick.js @@ -0,0 +1,30 @@ +'use strict'; +const common = require('../common'); +const { Console } = require('console'); +const { Writable } = require('stream'); +const async_hooks = require('async_hooks'); + +// Make sure that repeated calls to console.log(), and by extension +// stream.write() for the underlying stream, allocate exactly 1 tick object. +// At the time of writing, that is enough to ensure a flat memory profile +// from repeated console.log() calls, rather than having callbacks pile up +// over time, assuming that data can be written synchronously. +// Refs: https://github.com/nodejs/node/issues/18013 +// Refs: https://github.com/nodejs/node/issues/18367 + +const checkTickCreated = common.mustCall(); + +async_hooks.createHook({ + init(id, type, triggerId, resoure) { + if (type === 'TickObject') checkTickCreated(); + } +}).enable(); + +const console = new Console(new Writable({ + write: common.mustCall((chunk, encoding, cb) => { + cb(); + }, 100) +})); + +for (let i = 0; i < 100; i++) + console.log(i); diff --git a/test/parallel/test-tls-basic-validations.js b/test/parallel/test-tls-basic-validations.js index 925c6643a1a0cc..763b60fac536d8 100644 --- a/test/parallel/test-tls-basic-validations.js +++ b/test/parallel/test-tls-basic-validations.js @@ -78,9 +78,13 @@ common.expectsError( assert.throws(() => tls.createServer({ ticketKeys: Buffer.alloc(0) }), /TypeError: Ticket keys length must be 48 bytes/); -common.expectsInternalAssertion( +assert.throws( () => tls.createSecurePair({}), - 'context.context must be a NativeSecureContext' + { + message: 'context must be a SecureContext', + code: 'ERR_TLS_INVALID_CONTEXT', + name: 'TypeError', + } ); { @@ -114,3 +118,15 @@ common.expectsInternalAssertion( } ); } + +assert.throws(() => { tls.createSecureContext({ minVersion: 'fhqwhgads' }); }, + { + code: 'ERR_TLS_INVALID_PROTOCOL_VERSION', + name: 'TypeError' + }); + +assert.throws(() => { tls.createSecureContext({ maxVersion: 'fhqwhgads' }); }, + { + code: 'ERR_TLS_INVALID_PROTOCOL_VERSION', + name: 'TypeError' + }); diff --git a/test/parallel/test-tls-check-server-identity.js b/test/parallel/test-tls-check-server-identity.js index afeb6c749928c3..04c77305eb3b89 100644 --- a/test/parallel/test-tls-check-server-identity.js +++ b/test/parallel/test-tls-check-server-identity.js @@ -143,6 +143,20 @@ const tests = [ error: 'Cert is empty' }, + // Empty Subject w/DNS name + { + host: 'a.com', cert: { + subjectaltname: 'DNS:a.com', + } + }, + + // Empty Subject w/URI name + { + host: 'a.b.a.com', cert: { + subjectaltname: 'URI:http://a.b.a.com/', + } + }, + // Multiple CN fields { host: 'foo.com', cert: { diff --git a/test/parallel/test-tls-fast-writing.js b/test/parallel/test-tls-fast-writing.js index af40cc33baeda0..4718acf2858499 100644 --- a/test/parallel/test-tls-fast-writing.js +++ b/test/parallel/test-tls-fast-writing.js @@ -68,6 +68,7 @@ server.listen(0, function() { process.exit(0); write(); } + function write() { // This needs to return false eventually while (false !== conn.write(chunk)); diff --git a/test/parallel/test-trace-events-all.js b/test/parallel/test-trace-events-all.js index d0cdd4a8749213..06e83fa6376208 100644 --- a/test/parallel/test-trace-events-all.js +++ b/test/parallel/test-trace-events-all.js @@ -6,7 +6,7 @@ const fs = require('fs'); const path = require('path'); const CODE = - 'setTimeout(() => { for (var i = 0; i < 100000; i++) { "test" + i } }, 1)'; + 'setTimeout(() => { for (let i = 0; i < 100000; i++) { "test" + i } }, 1)'; const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); diff --git a/test/parallel/test-trace-events-async-hooks-dynamic.js b/test/parallel/test-trace-events-async-hooks-dynamic.js index 9cb0d41c5494c6..b660f7f8d49ee8 100644 --- a/test/parallel/test-trace-events-async-hooks-dynamic.js +++ b/test/parallel/test-trace-events-async-hooks-dynamic.js @@ -18,7 +18,7 @@ const path = require('path'); const enable = `require("trace_events").createTracing( { categories: ["node.async_hooks"] }).enable();`; const code = - 'setTimeout(() => { for (var i = 0; i < 100000; i++) { "test" + i } }, 1)'; + 'setTimeout(() => { for (let i = 0; i < 100000; i++) { "test" + i } }, 1)'; const tmpdir = require('../common/tmpdir'); const filename = path.join(tmpdir.path, 'node_trace.1.log'); diff --git a/test/parallel/test-trace-events-async-hooks-worker.js b/test/parallel/test-trace-events-async-hooks-worker.js index 153ef301873151..185f310f89e13a 100644 --- a/test/parallel/test-trace-events-async-hooks-worker.js +++ b/test/parallel/test-trace-events-async-hooks-worker.js @@ -16,7 +16,7 @@ const fs = require('fs'); const path = require('path'); const code = - 'setTimeout(() => { for (var i = 0; i < 100000; i++) { "test" + i } }, 1)'; + 'setTimeout(() => { for (let i = 0; i < 100000; i++) { "test" + i } }, 1)'; const worker = `const { Worker } = require('worker_threads'); const worker = new Worker('${code}', diff --git a/test/parallel/test-trace-events-async-hooks.js b/test/parallel/test-trace-events-async-hooks.js index 09017e47745c49..eecce9e5e43ab3 100644 --- a/test/parallel/test-trace-events-async-hooks.js +++ b/test/parallel/test-trace-events-async-hooks.js @@ -7,7 +7,7 @@ const path = require('path'); const util = require('util'); const CODE = - 'setTimeout(() => { for (var i = 0; i < 100000; i++) { "test" + i } }, 1)'; + 'setTimeout(() => { for (let i = 0; i < 100000; i++) { "test" + i } }, 1)'; const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); diff --git a/test/parallel/test-trace-events-file-pattern.js b/test/parallel/test-trace-events-file-pattern.js index a60559b7f91b2b..d9cd8e66d23646 100644 --- a/test/parallel/test-trace-events-file-pattern.js +++ b/test/parallel/test-trace-events-file-pattern.js @@ -9,7 +9,7 @@ const path = require('path'); tmpdir.refresh(); const CODE = - 'setTimeout(() => { for (var i = 0; i < 100000; i++) { "test" + i } }, 1)'; + 'setTimeout(() => { for (let i = 0; i < 100000; i++) { "test" + i } }, 1)'; const proc = cp.spawn(process.execPath, [ '--trace-events-enabled', diff --git a/test/parallel/test-trace-events-metadata.js b/test/parallel/test-trace-events-metadata.js index 863b2175f6c8a0..dbc8fc5d1ad5f9 100644 --- a/test/parallel/test-trace-events-metadata.js +++ b/test/parallel/test-trace-events-metadata.js @@ -6,7 +6,7 @@ const fs = require('fs'); const path = require('path'); const CODE = - 'setTimeout(() => { for (var i = 0; i < 100000; i++) { "test" + i } }, 1);' + + 'setTimeout(() => { for (let i = 0; i < 100000; i++) { "test" + i } }, 1);' + 'process.title = "foo"'; const tmpdir = require('../common/tmpdir'); diff --git a/test/parallel/test-trace-events-none.js b/test/parallel/test-trace-events-none.js index dfe77e4b39369f..d6ec35677d3fe1 100644 --- a/test/parallel/test-trace-events-none.js +++ b/test/parallel/test-trace-events-none.js @@ -6,7 +6,7 @@ const fs = require('fs'); const path = require('path'); const CODE = - 'setTimeout(() => { for (var i = 0; i < 100000; i++) { "test" + i } }, 1)'; + 'setTimeout(() => { for (let i = 0; i < 100000; i++) { "test" + i } }, 1)'; const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); diff --git a/test/parallel/test-trace-events-v8.js b/test/parallel/test-trace-events-v8.js index d79838892baa0d..0215a150e8a828 100644 --- a/test/parallel/test-trace-events-v8.js +++ b/test/parallel/test-trace-events-v8.js @@ -6,7 +6,7 @@ const fs = require('fs'); const path = require('path'); const CODE = - 'setTimeout(() => { for (var i = 0; i < 100000; i++) { "test" + i } }, 1)'; + 'setTimeout(() => { for (let i = 0; i < 100000; i++) { "test" + i } }, 1)'; const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); diff --git a/test/parallel/test-util-format.js b/test/parallel/test-util-format.js index 2ef05284902995..e07ec6d6a34c2f 100644 --- a/test/parallel/test-util-format.js +++ b/test/parallel/test-util-format.js @@ -160,6 +160,66 @@ assert.strictEqual(util.format('%s', () => 5), '() => 5'); util.format('%s', new Foobar(5)), 'Foobar [ <5 empty items>, aaa: true ]' ); + + // Subclassing: + class B extends Foo {} + + function C() {} + C.prototype.toString = function() { + return 'Custom'; + }; + + function D() { + C.call(this); + } + D.prototype = Object.create(C.prototype); + + assert.strictEqual( + util.format('%s', new B()), + 'Bar' + ); + assert.strictEqual( + util.format('%s', new C()), + 'Custom' + ); + assert.strictEqual( + util.format('%s', new D()), + 'Custom' + ); + + D.prototype.constructor = D; + assert.strictEqual( + util.format('%s', new D()), + 'Custom' + ); + + D.prototype.constructor = null; + assert.strictEqual( + util.format('%s', new D()), + 'Custom' + ); + + D.prototype.constructor = { name: 'Foobar' }; + assert.strictEqual( + util.format('%s', new D()), + 'Custom' + ); + + Object.defineProperty(D.prototype, 'constructor', { + get() { + throw new Error(); + }, + configurable: true + }); + assert.strictEqual( + util.format('%s', new D()), + 'Custom' + ); + + assert.strictEqual( + util.format('%s', Object.create(null)), + '[Object: null prototype] {}' + ); } // JSON format specifier diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index e7804eec2f2bc0..09ec140a19f9f1 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -690,6 +690,35 @@ assert.strictEqual(util.inspect(-5e-324), '-5e-324'); ); } +// Tampered error stack or name property (different type than string). +// Note: Symbols are not supported by `Error#toString()` which is called by +// accessing the `stack` property. +[ + [404, '404: foo', '[404]'], + [0, '0: foo', '[RangeError: foo]'], + [0n, '0: foo', '[RangeError: foo]'], + [null, 'null: foo', '[RangeError: foo]'], + [undefined, 'RangeError: foo', '[RangeError: foo]'], + [false, 'false: foo', '[RangeError: foo]'], + ['', 'foo', '[RangeError: foo]'], + [[1, 2, 3], '1,2,3: foo', '[1,2,3]'], +].forEach(([value, outputStart, stack]) => { + let err = new RangeError('foo'); + err.name = value; + assert( + util.inspect(err).startsWith(outputStart), + util.format( + 'The name set to %o did not result in the expected output "%s"', + value, + outputStart + ) + ); + + err = new RangeError('foo'); + err.stack = value; + assert.strictEqual(util.inspect(err), stack); +}); + // https://github.com/nodejs/node-v0.x-archive/issues/1941 assert.strictEqual(util.inspect(Object.create(Date.prototype)), 'Date {}'); @@ -2514,8 +2543,13 @@ assert.strictEqual( // Tracing class respects inspect depth. try { const trace = require('trace_events').createTracing({ categories: ['fo'] }); - const actual = util.inspect({ trace }, { depth: 0 }); - assert.strictEqual(actual, '{ trace: [Tracing] }'); + const actualDepth0 = util.inspect({ trace }, { depth: 0 }); + assert.strictEqual(actualDepth0, '{ trace: [Tracing] }'); + const actualDepth1 = util.inspect({ trace }, { depth: 1 }); + assert.strictEqual( + actualDepth1, + "{ trace: Tracing { enabled: false, categories: 'fo' } }" + ); } catch (err) { if (err.code !== 'ERR_TRACE_EVENTS_UNAVAILABLE') throw err; diff --git a/test/parallel/test-util-promisify.js b/test/parallel/test-util-promisify.js index 0bece0df426b7b..77601eae6daa9d 100644 --- a/test/parallel/test-util-promisify.js +++ b/test/parallel/test-util-promisify.js @@ -26,6 +26,7 @@ const stat = promisify(fs.stat); { function fn() {} + function promisifedFn() {} fn[promisify.custom] = promisifedFn; assert.strictEqual(promisify(fn), promisifedFn); diff --git a/test/parallel/test-vm-module-errors.js b/test/parallel/test-vm-module-errors.js index 7a3bfccbddb3be..e5a766e37dfdb8 100644 --- a/test/parallel/test-vm-module-errors.js +++ b/test/parallel/test-vm-module-errors.js @@ -198,7 +198,7 @@ async function checkExecution() { // Check for error thrown when breakOnSigint is not a boolean for evaluate() async function checkInvalidOptionForEvaluate() { await assert.rejects(async () => { - const m = new SourceTextModule('export const a = 1; export var b = 2'); + const m = new SourceTextModule('export const a = 1; export let b = 2'); await m.evaluate({ breakOnSigint: 'a-string' }); }, { name: 'TypeError', diff --git a/test/pseudo-tty/test-tty-color-support.js b/test/pseudo-tty/test-tty-color-support.js index b2cfc804c30281..57ed640d4d813f 100644 --- a/test/pseudo-tty/test-tty-color-support.js +++ b/test/pseudo-tty/test-tty-color-support.js @@ -71,6 +71,7 @@ const writeStream = new WriteStream(fd); [{ NO_COLOR: '', COLORTERM: '24bit' }, 1], [{ TMUX: '1', FORCE_COLOR: 0 }, 1], [{ NO_COLOR: 'true', FORCE_COLOR: 0, COLORTERM: 'truecolor' }, 1], + [{ TERM: 'xterm-256color', COLORTERM: 'truecolor' }, 24], ].forEach(([env, depth], i) => { const actual = writeStream.getColorDepth(env); assert.strictEqual( diff --git a/test/pummel/test-fs-watch-file.js b/test/pummel/test-fs-watch-file.js index 0d897bdc4adf94..ca8aecc8721dc8 100644 --- a/test/pummel/test-fs-watch-file.js +++ b/test/pummel/test-fs-watch-file.js @@ -86,6 +86,7 @@ assert.throws( fs.unwatchFile(filepathTwo, a); ++watchSeenTwo; } + function b() { fs.unwatchFile(filepathTwo, b); ++watchSeenTwo; diff --git a/test/sequential/test-http-server-consumed-timeout.js b/test/sequential/test-http-server-consumed-timeout.js deleted file mode 100644 index 56217226503be1..00000000000000 --- a/test/sequential/test-http-server-consumed-timeout.js +++ /dev/null @@ -1,51 +0,0 @@ -'use strict'; - -const common = require('../common'); - -const assert = require('assert'); -const http = require('http'); - -let time = Date.now(); -let intervalWasInvoked = false; -const TIMEOUT = common.platformTimeout(200); - -const server = http.createServer((req, res) => { - server.close(); - - res.writeHead(200); - res.flushHeaders(); - - req.setTimeout(TIMEOUT, () => { - if (!intervalWasInvoked) - return common.skip('interval was not invoked quickly enough for test'); - assert.fail('Request timeout should not fire'); - }); - - req.resume(); - req.once('end', () => { - res.end(); - }); -}); - -server.listen(0, common.mustCall(() => { - const req = http.request({ - port: server.address().port, - method: 'POST' - }, (res) => { - const interval = setInterval(() => { - intervalWasInvoked = true; - // If machine is busy enough that the interval takes more than TIMEOUT ms - // to be invoked, skip the test. - const now = Date.now(); - if (now - time > TIMEOUT) - return common.skip('interval is not invoked quickly enough for test'); - time = now; - req.write('a'); - }, common.platformTimeout(25)); - setTimeout(() => { - clearInterval(interval); - req.end(); - }, TIMEOUT); - }); - req.write('.'); -})); diff --git a/test/sequential/test-inspector-async-call-stack.js b/test/sequential/test-inspector-async-call-stack.js index f6a48b339d4cc8..f050487da7a5e5 100644 --- a/test/sequential/test-inspector-async-call-stack.js +++ b/test/sequential/test-inspector-async-call-stack.js @@ -6,18 +6,20 @@ common.skipIf32Bits(); const assert = require('assert'); const { internalBinding } = require('internal/test/binding'); -const async_wrap = internalBinding('async_wrap'); -const { kTotals } = async_wrap.constants; +const { async_hook_fields, constants } = internalBinding('async_wrap'); +const { kTotals } = constants; const inspector = require('inspector'); const setDepth = 'Debugger.setAsyncCallStackDepth'; function verifyAsyncHookDisabled(message) { - assert.strictEqual(async_wrap.async_hook_fields[kTotals], 0); + assert.strictEqual(async_hook_fields[kTotals], 0, + `${async_hook_fields[kTotals]} !== 0: ${message}`); } function verifyAsyncHookEnabled(message) { - assert.strictEqual(async_wrap.async_hook_fields[kTotals], 4); + assert.strictEqual(async_hook_fields[kTotals], 4, + `${async_hook_fields[kTotals]} !== 4: ${message}`); } // By default inspector async hooks should not have been installed. diff --git a/test/sequential/test-inspector-contexts.js b/test/sequential/test-inspector-contexts.js index 793868e3bc072c..0918d22e90b931 100644 --- a/test/sequential/test-inspector-contexts.js +++ b/test/sequential/test-inspector-contexts.js @@ -5,8 +5,8 @@ const common = require('../common'); common.skipIfInspectorDisabled(); -const { strictEqual } = require('assert'); -const { createContext, runInNewContext } = require('vm'); +const assert = require('assert'); +const vm = require('vm'); const { Session } = require('inspector'); const session = new Session(); @@ -22,7 +22,7 @@ async function testContextCreatedAndDestroyed() { const mainContextPromise = notificationPromise('Runtime.executionContextCreated'); - session.post('Runtime.enable'); + session.post('Runtime.enable', assert.ifError); const contextCreated = await mainContextPromise; const { name, origin, auxData } = contextCreated.params.context; if (common.isSunOS || common.isWindows) { @@ -31,18 +31,18 @@ async function testContextCreatedAndDestroyed() { // "Administrator: Windows PowerShell[42]" because of a GetConsoleTitle() // quirk. Not much we can do about either, just verify that it contains // the PID. - strictEqual(name.includes(`[${process.pid}]`), true); + assert.strictEqual(name.includes(`[${process.pid}]`), true); } else { let expects = `${process.argv0}[${process.pid}]`; if (!common.isMainThread) { expects = `Worker[${require('worker_threads').threadId}]`; } - strictEqual(expects, name); + assert.strictEqual(expects, name); } - strictEqual(origin, '', - JSON.stringify(contextCreated)); - strictEqual(auxData.isDefault, true, - JSON.stringify(contextCreated)); + assert.strictEqual(origin, '', + JSON.stringify(contextCreated)); + assert.strictEqual(auxData.isDefault, true, + JSON.stringify(contextCreated)); } { @@ -53,23 +53,25 @@ async function testContextCreatedAndDestroyed() { session.once('Runtime.executionContextDestroyed', (notification) => contextDestroyed = notification); - runInNewContext('1 + 1'); + vm.runInNewContext('1 + 1'); const contextCreated = await vmContextCreatedPromise; const { id, name, origin, auxData } = contextCreated.params.context; - strictEqual(name, 'VM Context 1', - JSON.stringify(contextCreated)); - strictEqual(origin, '', - JSON.stringify(contextCreated)); - strictEqual(auxData.isDefault, false, - JSON.stringify(contextCreated)); + assert.strictEqual(name, 'VM Context 1', + JSON.stringify(contextCreated)); + assert.strictEqual(origin, '', + JSON.stringify(contextCreated)); + assert.strictEqual(auxData.isDefault, false, + JSON.stringify(contextCreated)); // GC is unpredictable... + console.log('Checking/waiting for GC.'); while (!contextDestroyed) global.gc(); + console.log('Context destroyed.'); - strictEqual(contextDestroyed.params.executionContextId, id, - JSON.stringify(contextDestroyed)); + assert.strictEqual(contextDestroyed.params.executionContextId, id, + JSON.stringify(contextDestroyed)); } { @@ -80,23 +82,25 @@ async function testContextCreatedAndDestroyed() { session.once('Runtime.executionContextDestroyed', (notification) => contextDestroyed = notification); - runInNewContext('1 + 1', {}, { + vm.runInNewContext('1 + 1', {}, { contextName: 'Custom context', contextOrigin: 'https://origin.example' }); const contextCreated = await vmContextCreatedPromise; const { name, origin, auxData } = contextCreated.params.context; - strictEqual(name, 'Custom context', - JSON.stringify(contextCreated)); - strictEqual(origin, 'https://origin.example', - JSON.stringify(contextCreated)); - strictEqual(auxData.isDefault, false, - JSON.stringify(contextCreated)); + assert.strictEqual(name, 'Custom context', + JSON.stringify(contextCreated)); + assert.strictEqual(origin, 'https://origin.example', + JSON.stringify(contextCreated)); + assert.strictEqual(auxData.isDefault, false, + JSON.stringify(contextCreated)); // GC is unpredictable... + console.log('Checking/waiting for GC again.'); while (!contextDestroyed) global.gc(); + console.log('Other context destroyed.'); } { @@ -107,20 +111,22 @@ async function testContextCreatedAndDestroyed() { session.once('Runtime.executionContextDestroyed', (notification) => contextDestroyed = notification); - createContext({}, { origin: 'https://nodejs.org' }); + vm.createContext({}, { origin: 'https://nodejs.org' }); const contextCreated = await vmContextCreatedPromise; const { name, origin, auxData } = contextCreated.params.context; - strictEqual(name, 'VM Context 2', - JSON.stringify(contextCreated)); - strictEqual(origin, 'https://nodejs.org', - JSON.stringify(contextCreated)); - strictEqual(auxData.isDefault, false, - JSON.stringify(contextCreated)); + assert.strictEqual(name, 'VM Context 2', + JSON.stringify(contextCreated)); + assert.strictEqual(origin, 'https://nodejs.org', + JSON.stringify(contextCreated)); + assert.strictEqual(auxData.isDefault, false, + JSON.stringify(contextCreated)); // GC is unpredictable... + console.log('Checking/waiting for GC a third time.'); while (!contextDestroyed) global.gc(); + console.log('Context destroyed once again.'); } { @@ -131,28 +137,30 @@ async function testContextCreatedAndDestroyed() { session.once('Runtime.executionContextDestroyed', (notification) => contextDestroyed = notification); - createContext({}, { name: 'Custom context 2' }); + vm.createContext({}, { name: 'Custom context 2' }); const contextCreated = await vmContextCreatedPromise; const { name, auxData } = contextCreated.params.context; - strictEqual(name, 'Custom context 2', - JSON.stringify(contextCreated)); - strictEqual(auxData.isDefault, false, - JSON.stringify(contextCreated)); + assert.strictEqual(name, 'Custom context 2', + JSON.stringify(contextCreated)); + assert.strictEqual(auxData.isDefault, false, + JSON.stringify(contextCreated)); // GC is unpredictable... + console.log('Checking/waiting for GC a fourth time.'); while (!contextDestroyed) global.gc(); + console.log('Context destroyed a fourth time.'); } } async function testBreakpointHit() { console.log('Testing breakpoint is hit in a new context'); - session.post('Debugger.enable'); + session.post('Debugger.enable', assert.ifError); const pausedPromise = notificationPromise('Debugger.paused'); - runInNewContext('debugger', {}); + vm.runInNewContext('debugger', {}); await pausedPromise; } -testContextCreatedAndDestroyed().then(testBreakpointHit); +testContextCreatedAndDestroyed().then(common.mustCall(testBreakpointHit)); diff --git a/test/sequential/test-inspector-port-cluster.js b/test/sequential/test-inspector-port-cluster.js index 7579f51deeb598..55a0d149068ead 100644 --- a/test/sequential/test-inspector-port-cluster.js +++ b/test/sequential/test-inspector-port-cluster.js @@ -205,6 +205,7 @@ function testRunnerMain() { }); }); } + function masterProcessMain() { const workers = JSON.parse(process.env.workers); const clusterSettings = JSON.parse(process.env.clusterSettings) || {}; diff --git a/test/parallel/test-worker-prof.js b/test/sequential/test-worker-prof.js similarity index 100% rename from test/parallel/test-worker-prof.js rename to test/sequential/test-worker-prof.js diff --git a/test/tick-processor/test-tick-processor-unknown.js b/test/tick-processor/test-tick-processor-unknown.js index 182f8c957c820a..f894ae919acf66 100644 --- a/test/tick-processor/test-tick-processor-unknown.js +++ b/test/tick-processor/test-tick-processor-unknown.js @@ -19,7 +19,7 @@ const base = require('./tick-processor-base.js'); base.runTest({ pattern: /LazyCompile.*\[eval]:1|.*% UNKNOWN/, code: `function f() { - for (var i = 0; i < 1000000; i++) { + for (let i = 0; i < 1000000; i++) { i++; } setImmediate(function() { f(); }); diff --git a/test/wasi/Makefile b/test/wasi/Makefile new file mode 100644 index 00000000000000..42d3b4e3fa7a80 --- /dev/null +++ b/test/wasi/Makefile @@ -0,0 +1,12 @@ +CC = /opt/wasi-sdk/bin/clang +TARGET = wasm32-unknown-wasi +SYSROOT = + +OBJ = $(patsubst c/%.c, wasm/%.wasm, $(wildcard c/*.c)) +all: $(OBJ) + +wasm/%.wasm : c/%.c + $(CC) $< --target=$(TARGET) --sysroot=$(SYSROOT) -s -o $@ + +.PHONY clean: + rm -f $(OBJ) diff --git a/test/wasi/README.md b/test/wasi/README.md new file mode 100644 index 00000000000000..ccb02d095ef939 --- /dev/null +++ b/test/wasi/README.md @@ -0,0 +1,8 @@ +# WASI Tests + +Compile with clang and `wasm32-wasi` target. The clang version used must be +built with wasi-libc. You can specify the location for clang and the sysroot +if needed when running make: +```console +$ make CC=/usr/local/opt/llvm/bin/clang SYSROOT=/path/to/wasi-libc/sysroot +``` diff --git a/test/wasi/c/cant_dotdot.c b/test/wasi/c/cant_dotdot.c new file mode 100644 index 00000000000000..e2722062cd0156 --- /dev/null +++ b/test/wasi/c/cant_dotdot.c @@ -0,0 +1,11 @@ +#include +#include +#include + +int main() { + FILE* file = fopen("/sandbox/../outside.txt", "r"); + assert(file == NULL); + assert(errno == ENOTCAPABLE); + + return 0; +} diff --git a/test/wasi/c/clock_getres.c b/test/wasi/c/clock_getres.c new file mode 100644 index 00000000000000..eaac02c665b9f2 --- /dev/null +++ b/test/wasi/c/clock_getres.c @@ -0,0 +1,17 @@ +#include +#include + +int main() { + struct timespec ts; + int r; + + // supported clocks + r = clock_getres(CLOCK_REALTIME, &ts); + assert(r == 0); + r = clock_getres(CLOCK_MONOTONIC, &ts); + assert(r == 0); + r = clock_getres(CLOCK_PROCESS_CPUTIME_ID, &ts); + assert(r == 0); + r = clock_getres(CLOCK_THREAD_CPUTIME_ID, &ts); + assert(r == 0); +} diff --git a/test/wasi/c/exitcode.c b/test/wasi/c/exitcode.c new file mode 100644 index 00000000000000..9c44b0de741a69 --- /dev/null +++ b/test/wasi/c/exitcode.c @@ -0,0 +1,3 @@ +int main() { + return 120; +} diff --git a/test/wasi/c/fd_prestat_get_refresh.c b/test/wasi/c/fd_prestat_get_refresh.c new file mode 100644 index 00000000000000..fb644ab49b208a --- /dev/null +++ b/test/wasi/c/fd_prestat_get_refresh.c @@ -0,0 +1,8 @@ +#include + +int main(void) { + isatty(1); + __builtin_wasm_memory_grow(0, 1); + isatty(1); + return 0; +} diff --git a/test/wasi/c/follow_symlink.c b/test/wasi/c/follow_symlink.c new file mode 100644 index 00000000000000..badb0ee2676bbe --- /dev/null +++ b/test/wasi/c/follow_symlink.c @@ -0,0 +1,14 @@ +#include +#include + +int main() { + FILE* file = fopen("/sandbox/subdir/input_link.txt", "r"); + assert(file != NULL); + + char c = fgetc(file); + while (c != EOF) { + int wrote = fputc(c, stdout); + assert(wrote != EOF); + c = fgetc(file); + } +} diff --git a/test/wasi/c/getentropy.c b/test/wasi/c/getentropy.c new file mode 100644 index 00000000000000..75547e1c474bba --- /dev/null +++ b/test/wasi/c/getentropy.c @@ -0,0 +1,18 @@ +#include +#include + +int main() { + char buf[256] = {0}; + int r = getentropy(buf, 256); + assert(r == 0); + + for (int i = 0; i < 256; i++) { + if (buf[i] != 0) { + return 0; + } + } + + // if this ever is reached, we either have a bug or should buy a lottery + // ticket + return 1; +} diff --git a/test/wasi/c/getrusage.c b/test/wasi/c/getrusage.c new file mode 100644 index 00000000000000..ad1e430b853395 --- /dev/null +++ b/test/wasi/c/getrusage.c @@ -0,0 +1,34 @@ +#include +#include + +int main() { + struct rusage ru1; + struct rusage ru2; + long long s1; + long long us1; + long long s2; + long long us2; + int r; + int success = 0; + + r = getrusage(RUSAGE_SELF, &ru1); + assert(r == 0); + s1 = ru1.ru_utime.tv_sec; + us1 = ru1.ru_utime.tv_usec; + + for (int i = 0; i < 10000; i++) { + r = getrusage(RUSAGE_SELF, &ru2); + assert(r == 0); + s2 = ru2.ru_utime.tv_sec; + us2 = ru2.ru_utime.tv_usec; + assert(s1 <= s2); + + // Verify that some time has passed. + if (s2 > s1 || (s2 == s1 && us2 > us1)) { + success = 1; + break; + } + } + + assert(success == 1); +} diff --git a/test/wasi/c/gettimeofday.c b/test/wasi/c/gettimeofday.c new file mode 100644 index 00000000000000..209a54e4983bfa --- /dev/null +++ b/test/wasi/c/gettimeofday.c @@ -0,0 +1,35 @@ +#include +#include +#include + +int main() { + struct timeval tv1; + struct timeval tv2; + long long s1; + long long us1; + long long s2; + long long us2; + int r; + int success = 0; + + r = gettimeofday(&tv1, NULL); + assert(r == 0); + s1 = tv1.tv_sec; + us1 = tv1.tv_usec; + + for (int i = 0; i < 10000; i++) { + r = gettimeofday(&tv2, NULL); + assert(r == 0); + s2 = tv2.tv_sec; + us2 = tv2.tv_usec; + assert(s1 <= s2); + + // Verify that some time has passed. + if (s2 > s1 || (s2 == s1 && us2 > us1)) { + success = 1; + break; + } + } + + assert(success == 1); +} diff --git a/test/wasi/c/notdir.c b/test/wasi/c/notdir.c new file mode 100644 index 00000000000000..03f369ffd3a862 --- /dev/null +++ b/test/wasi/c/notdir.c @@ -0,0 +1,11 @@ +#include +#include +#include + +int main() { + DIR* dir = opendir("/sandbox/notadir"); + assert(dir == NULL); + assert(errno == ENOTDIR); + + return 0; +} diff --git a/test/wasi/c/poll.c b/test/wasi/c/poll.c new file mode 100644 index 00000000000000..6b6ef71fd68c3b --- /dev/null +++ b/test/wasi/c/poll.c @@ -0,0 +1,31 @@ +#include +#include +#include +#include + +int main(void) { + struct pollfd fds[2]; + time_t before, now; + int ret; + + fds[0] = (struct pollfd){.fd = 1, .events = POLLOUT, .revents = 0}; + fds[1] = (struct pollfd){.fd = 2, .events = POLLOUT, .revents = 0}; + + ret = poll(fds, 2, -1); + assert(ret == 2); + assert(fds[0].revents == POLLOUT); + assert(fds[1].revents == POLLOUT); + + fds[0] = (struct pollfd){.fd = 0, .events = POLLIN, .revents = 0}; + time(&before); + ret = poll(fds, 1, 2000); + time(&now); + assert(ret == 0); + assert(now - before >= 2); + + sleep(1); + time(&now); + assert(now - before >= 3); + + return 0; +} diff --git a/test/wasi/c/preopen_populates.c b/test/wasi/c/preopen_populates.c new file mode 100644 index 00000000000000..03b2213bb9a36c --- /dev/null +++ b/test/wasi/c/preopen_populates.c @@ -0,0 +1,3 @@ +int main(void) { + return 0; +} diff --git a/test/wasi/c/read_file.c b/test/wasi/c/read_file.c new file mode 100644 index 00000000000000..40023e29e2727f --- /dev/null +++ b/test/wasi/c/read_file.c @@ -0,0 +1,14 @@ +#include +#include + +int main() { + FILE* file = fopen("/sandbox/input.txt", "r"); + assert(file != NULL); + + char c = fgetc(file); + while (c != EOF) { + int wrote = fputc(c, stdout); + assert(wrote != EOF); + c = fgetc(file); + } +} diff --git a/test/wasi/c/read_file_twice.c b/test/wasi/c/read_file_twice.c new file mode 100644 index 00000000000000..e295e38a3b3e13 --- /dev/null +++ b/test/wasi/c/read_file_twice.c @@ -0,0 +1,16 @@ +#include +#include + +int main() { + for (int i = 0; i < 2; i++) { + FILE* file = fopen("/sandbox/input.txt", "r"); + assert(file != NULL); + + char c = fgetc(file); + while (c != EOF) { + int wrote = fputc(c, stdout); + assert(wrote != EOF); + c = fgetc(file); + } + } +} diff --git a/test/wasi/c/stat.c b/test/wasi/c/stat.c new file mode 100644 index 00000000000000..fd3854937b9e6e --- /dev/null +++ b/test/wasi/c/stat.c @@ -0,0 +1,53 @@ +#include + +#include +#include +#include +#include + +#define BASE_DIR "/tmp" +#define OUTPUT_DIR BASE_DIR "/testdir" +#define PATH OUTPUT_DIR "/output.txt" +#define SIZE 500 + +int main(void) { + struct stat st; + int fd; + int ret; + off_t pos; + + (void)st; + ret = mkdir(OUTPUT_DIR, 0755); + assert(ret == 0); + + fd = open(PATH, O_CREAT | O_WRONLY, 0666); + assert(fd != -1); + + pos = lseek(fd, SIZE - 1, SEEK_SET); + assert(pos == SIZE - 1); + + ret = (int)write(fd, "", 1); + assert(ret == 1); + + ret = fstat(fd, &st); + assert(ret == 0); + assert(st.st_size == SIZE); + + ret = close(fd); + assert(ret == 0); + + ret = access(PATH, R_OK); + assert(ret == 0); + + ret = stat(PATH, &st); + assert(ret == 0); + assert(st.st_size == SIZE); + + ret = unlink(PATH); + assert(ret == 0); + + ret = stat(PATH, &st); + assert(ret == -1); + + return 0; +} diff --git a/test/wasi/c/stdin.c b/test/wasi/c/stdin.c new file mode 100644 index 00000000000000..5a81ea1265fb11 --- /dev/null +++ b/test/wasi/c/stdin.c @@ -0,0 +1,13 @@ +#include + +int main(void) { + char x[32]; + + if (fgets(x, sizeof x, stdin) == NULL) { + return ferror(stdin); + } + if (fputs(x, stdout) == EOF) { + return ferror(stdout); + } + return 0; +} diff --git a/test/wasi/c/symlink_escape.c b/test/wasi/c/symlink_escape.c new file mode 100644 index 00000000000000..32dcc64eebdc2b --- /dev/null +++ b/test/wasi/c/symlink_escape.c @@ -0,0 +1,9 @@ +#include +#include +#include + +int main() { + FILE* file = fopen("/sandbox/subdir/outside.txt", "r"); + assert(file == NULL); + assert(errno == ENOTCAPABLE); +} diff --git a/test/wasi/c/symlink_loop.c b/test/wasi/c/symlink_loop.c new file mode 100644 index 00000000000000..23bd70ba601176 --- /dev/null +++ b/test/wasi/c/symlink_loop.c @@ -0,0 +1,9 @@ +#include +#include +#include + +int main() { + FILE* file = fopen("/sandbox/subdir/loop1", "r"); + assert(file == NULL); + assert(errno == ELOOP); +} diff --git a/test/wasi/c/write_file.c b/test/wasi/c/write_file.c new file mode 100644 index 00000000000000..c4cf30cf2954cb --- /dev/null +++ b/test/wasi/c/write_file.c @@ -0,0 +1,15 @@ +#include +#include +#include + +static char* message = "hello, file!"; + +int main() { + FILE* file = fopen("/tmp/output.txt", "w"); + assert(file != NULL); + + int nwritten = fprintf(file, "%s", message); + assert(nwritten == strlen(message)); + int r = fclose(file); + assert(r == 0); +} diff --git a/test/wasi/test-wasi-binding.js b/test/wasi/test-wasi-binding.js new file mode 100644 index 00000000000000..876c8a15a72c13 --- /dev/null +++ b/test/wasi/test-wasi-binding.js @@ -0,0 +1,19 @@ +// Flags: --experimental-wasi-unstable-preview0 +'use strict'; + +const common = require('../common'); + +const assert = require('assert'); +const fixtures = require('../common/fixtures'); +const buffer = fixtures.readSync(['wasi', 'simple-wasi.wasm']); +const { WASI } = require('wasi'); +const wasi = new WASI({ args: [], env: process.env }); +const importObject = { + wasi_unstable: wasi.wasiImport +}; + +WebAssembly.instantiate(buffer, importObject) +.then(common.mustCall((results) => { + assert(results.instance.exports._start); + wasi.start(results.instance); +})); diff --git a/test/wasi/test-wasi-symlinks.js b/test/wasi/test-wasi-symlinks.js new file mode 100644 index 00000000000000..3829464198273b --- /dev/null +++ b/test/wasi/test-wasi-symlinks.js @@ -0,0 +1,78 @@ +'use strict'; +const common = require('../common'); +const fs = require('fs'); +const path = require('path'); + +if (process.argv[2] === 'wasi-child') { + common.expectWarning('ExperimentalWarning', + 'WASI is an experimental feature. This feature could ' + + 'change at any time'); + + const { WASI } = require('wasi'); + const wasmDir = path.join(__dirname, 'wasm'); + const wasi = new WASI({ + args: [], + env: process.env, + preopens: { + '/sandbox': process.argv[4] + } + }); + const importObject = { wasi_unstable: wasi.wasiImport }; + const modulePath = path.join(wasmDir, `${process.argv[3]}.wasm`); + const buffer = fs.readFileSync(modulePath); + + (async () => { + const { instance } = await WebAssembly.instantiate(buffer, importObject); + + wasi.start(instance); + })(); +} else { + if (!common.canCreateSymLink()) { + common.skip('insufficient privileges'); + } + + const assert = require('assert'); + const cp = require('child_process'); + const tmpdir = require('../../test/common/tmpdir'); + + // Setup the sandbox environment. + tmpdir.refresh(); + const sandbox = path.join(tmpdir.path, 'sandbox'); + const sandboxedFile = path.join(sandbox, 'input.txt'); + const externalFile = path.join(tmpdir.path, 'outside.txt'); + const sandboxedDir = path.join(sandbox, 'subdir'); + const sandboxedSymlink = path.join(sandboxedDir, 'input_link.txt'); + const escapingSymlink = path.join(sandboxedDir, 'outside.txt'); + const loopSymlink1 = path.join(sandboxedDir, 'loop1'); + const loopSymlink2 = path.join(sandboxedDir, 'loop2'); + + fs.mkdirSync(sandbox); + fs.mkdirSync(sandboxedDir); + fs.writeFileSync(sandboxedFile, 'hello from input.txt', 'utf8'); + fs.writeFileSync(externalFile, 'this should be inaccessible', 'utf8'); + fs.symlinkSync(sandboxedFile, sandboxedSymlink, 'file'); + fs.symlinkSync(externalFile, escapingSymlink, 'file'); + fs.symlinkSync(loopSymlink2, loopSymlink1, 'file'); + fs.symlinkSync(loopSymlink1, loopSymlink2, 'file'); + + function runWASI(options) { + console.log('executing', options.test); + const opts = { env: { ...process.env, NODE_DEBUG_NATIVE: 'wasi' } }; + const child = cp.spawnSync(process.execPath, [ + '--experimental-wasi-unstable-preview0', + '--experimental-wasm-bigint', + __filename, + 'wasi-child', + options.test, + sandbox + ], opts); + console.log(child.stderr.toString()); + assert.strictEqual(child.status, 0); + assert.strictEqual(child.signal, null); + assert.strictEqual(child.stdout.toString(), options.stdout || ''); + } + + runWASI({ test: 'follow_symlink', stdout: 'hello from input.txt' }); + runWASI({ test: 'symlink_escape' }); + runWASI({ test: 'symlink_loop' }); +} diff --git a/test/wasi/test-wasi.js b/test/wasi/test-wasi.js new file mode 100644 index 00000000000000..fa2e0894c906ce --- /dev/null +++ b/test/wasi/test-wasi.js @@ -0,0 +1,81 @@ +'use strict'; +const common = require('../common'); + +if (process.argv[2] === 'wasi-child') { + const fixtures = require('../common/fixtures'); + const tmpdir = require('../../test/common/tmpdir'); + const fs = require('fs'); + const path = require('path'); + + common.expectWarning('ExperimentalWarning', + 'WASI is an experimental feature. This feature could ' + + 'change at any time'); + + const { WASI } = require('wasi'); + tmpdir.refresh(); + const wasmDir = path.join(__dirname, 'wasm'); + const wasi = new WASI({ + args: [], + env: process.env, + preopens: { + '/sandbox': fixtures.path('wasi'), + '/tmp': tmpdir.path + } + }); + const importObject = { wasi_unstable: wasi.wasiImport }; + const modulePath = path.join(wasmDir, `${process.argv[3]}.wasm`); + const buffer = fs.readFileSync(modulePath); + + (async () => { + const { instance } = await WebAssembly.instantiate(buffer, importObject); + + wasi.start(instance); + })(); +} else { + const assert = require('assert'); + const cp = require('child_process'); + const { EOL } = require('os'); + + function runWASI(options) { + console.log('executing', options.test); + const opts = { env: { ...process.env, NODE_DEBUG_NATIVE: 'wasi' } }; + + if (options.stdin !== undefined) + opts.input = options.stdin; + + const child = cp.spawnSync(process.execPath, [ + '--experimental-wasi-unstable-preview0', + '--experimental-wasm-bigint', + __filename, + 'wasi-child', + options.test + ], opts); + console.log(child.stderr.toString()); + assert.strictEqual(child.status, options.exitCode || 0); + assert.strictEqual(child.signal, null); + assert.strictEqual(child.stdout.toString(), options.stdout || ''); + } + + runWASI({ test: 'cant_dotdot' }); + runWASI({ test: 'clock_getres' }); + runWASI({ test: 'exitcode', exitCode: 120 }); + runWASI({ test: 'fd_prestat_get_refresh' }); + runWASI({ test: 'getentropy' }); + runWASI({ test: 'getrusage' }); + runWASI({ test: 'gettimeofday' }); + runWASI({ test: 'notdir' }); + // runWASI({ test: 'poll' }); + runWASI({ test: 'preopen_populates' }); + runWASI({ test: 'read_file', stdout: `hello from input.txt${EOL}` }); + runWASI({ + test: 'read_file_twice', + stdout: `hello from input.txt${EOL}hello from input.txt${EOL}` + }); + runWASI({ test: 'stat' }); + runWASI({ test: 'write_file' }); + + // Tests that are currently unsupported on Windows. + if (!common.isWindows) { + runWASI({ test: 'stdin', stdin: 'hello world', stdout: 'hello world' }); + } +} diff --git a/test/wasi/testcfg.py b/test/wasi/testcfg.py new file mode 100644 index 00000000000000..ec6cbc5fe3dc3a --- /dev/null +++ b/test/wasi/testcfg.py @@ -0,0 +1,6 @@ +import sys, os +sys.path.append(os.path.join(os.path.dirname(__file__), '..')) +import testpy + +def GetConfiguration(context, root): + return testpy.ParallelTestConfiguration(context, root, 'wasi') diff --git a/test/wasi/wasi.status b/test/wasi/wasi.status new file mode 100644 index 00000000000000..12212e8f72822b --- /dev/null +++ b/test/wasi/wasi.status @@ -0,0 +1,7 @@ +prefix wasi + +# To mark a test as flaky, list the test name in the appropriate section +# below, without ".js", followed by ": PASS,FLAKY". Example: +# sample-test : PASS,FLAKY + +[true] # This section applies to all platforms diff --git a/test/wasi/wasm/cant_dotdot.wasm b/test/wasi/wasm/cant_dotdot.wasm new file mode 100755 index 00000000000000..61e202d6913a3e Binary files /dev/null and b/test/wasi/wasm/cant_dotdot.wasm differ diff --git a/test/wasi/wasm/clock_getres.wasm b/test/wasi/wasm/clock_getres.wasm new file mode 100755 index 00000000000000..fac14edb04d794 Binary files /dev/null and b/test/wasi/wasm/clock_getres.wasm differ diff --git a/test/wasi/wasm/exitcode.wasm b/test/wasi/wasm/exitcode.wasm new file mode 100755 index 00000000000000..b2d9ef5e114218 Binary files /dev/null and b/test/wasi/wasm/exitcode.wasm differ diff --git a/test/wasi/wasm/fd_prestat_get_refresh.wasm b/test/wasi/wasm/fd_prestat_get_refresh.wasm new file mode 100755 index 00000000000000..cf4bb97c3b2b20 Binary files /dev/null and b/test/wasi/wasm/fd_prestat_get_refresh.wasm differ diff --git a/test/wasi/wasm/follow_symlink.wasm b/test/wasi/wasm/follow_symlink.wasm new file mode 100755 index 00000000000000..48cf8da1eb2860 Binary files /dev/null and b/test/wasi/wasm/follow_symlink.wasm differ diff --git a/test/wasi/wasm/getentropy.wasm b/test/wasi/wasm/getentropy.wasm new file mode 100755 index 00000000000000..6e3e8c8a8edbab Binary files /dev/null and b/test/wasi/wasm/getentropy.wasm differ diff --git a/test/wasi/wasm/getrusage.wasm b/test/wasi/wasm/getrusage.wasm new file mode 100755 index 00000000000000..524e809175b5aa Binary files /dev/null and b/test/wasi/wasm/getrusage.wasm differ diff --git a/test/wasi/wasm/gettimeofday.wasm b/test/wasi/wasm/gettimeofday.wasm new file mode 100755 index 00000000000000..94627f00866903 Binary files /dev/null and b/test/wasi/wasm/gettimeofday.wasm differ diff --git a/test/wasi/wasm/notdir.wasm b/test/wasi/wasm/notdir.wasm new file mode 100755 index 00000000000000..f83a790ddc4700 Binary files /dev/null and b/test/wasi/wasm/notdir.wasm differ diff --git a/test/wasi/wasm/poll.wasm b/test/wasi/wasm/poll.wasm new file mode 100755 index 00000000000000..98d0736762d7fb Binary files /dev/null and b/test/wasi/wasm/poll.wasm differ diff --git a/test/wasi/wasm/preopen_populates.wasm b/test/wasi/wasm/preopen_populates.wasm new file mode 100755 index 00000000000000..e7c34bc964602f Binary files /dev/null and b/test/wasi/wasm/preopen_populates.wasm differ diff --git a/test/wasi/wasm/read_file.wasm b/test/wasi/wasm/read_file.wasm new file mode 100755 index 00000000000000..2b9db77d272618 Binary files /dev/null and b/test/wasi/wasm/read_file.wasm differ diff --git a/test/wasi/wasm/read_file_twice.wasm b/test/wasi/wasm/read_file_twice.wasm new file mode 100755 index 00000000000000..cd075a4de75a51 Binary files /dev/null and b/test/wasi/wasm/read_file_twice.wasm differ diff --git a/test/wasi/wasm/stat.wasm b/test/wasi/wasm/stat.wasm new file mode 100755 index 00000000000000..9007334d37d1a1 Binary files /dev/null and b/test/wasi/wasm/stat.wasm differ diff --git a/test/wasi/wasm/stdin.wasm b/test/wasi/wasm/stdin.wasm new file mode 100755 index 00000000000000..7264608753229f Binary files /dev/null and b/test/wasi/wasm/stdin.wasm differ diff --git a/test/wasi/wasm/symlink_escape.wasm b/test/wasi/wasm/symlink_escape.wasm new file mode 100755 index 00000000000000..0cdb8327a1a23b Binary files /dev/null and b/test/wasi/wasm/symlink_escape.wasm differ diff --git a/test/wasi/wasm/symlink_loop.wasm b/test/wasi/wasm/symlink_loop.wasm new file mode 100755 index 00000000000000..3883d5278c7008 Binary files /dev/null and b/test/wasi/wasm/symlink_loop.wasm differ diff --git a/test/wasi/wasm/write_file.wasm b/test/wasi/wasm/write_file.wasm new file mode 100755 index 00000000000000..500405e0fb3120 Binary files /dev/null and b/test/wasi/wasm/write_file.wasm differ diff --git a/tools/doc/generate.js b/tools/doc/generate.js index cd85d5365791ba..aac8bfd5e7ba21 100644 --- a/tools/doc/generate.js +++ b/tools/doc/generate.js @@ -29,6 +29,8 @@ const remark2rehype = require('remark-rehype'); const raw = require('rehype-raw'); const htmlStringify = require('rehype-stringify'); +const { replaceLinks } = require('./markdown'); +const linksMapper = require('./links-mapper'); const html = require('./html'); const json = require('./json'); @@ -70,6 +72,7 @@ async function main() { const input = await fs.readFile(filename, 'utf8'); const content = await unified() + .use(replaceLinks, { filename, linksMapper }) .use(markdown) .use(html.preprocessText) .use(json.jsonAPI, { filename }) diff --git a/tools/doc/links-mapper.json b/tools/doc/links-mapper.json new file mode 100644 index 00000000000000..158d72c7fe5822 --- /dev/null +++ b/tools/doc/links-mapper.json @@ -0,0 +1,6 @@ +{ + "doc/api/synopsis.md": { + "command line options": "cli.html#cli_command_line_options", + "web server": "http.html" + } +} \ No newline at end of file diff --git a/tools/doc/markdown.js b/tools/doc/markdown.js new file mode 100644 index 00000000000000..97feadbf9990bb --- /dev/null +++ b/tools/doc/markdown.js @@ -0,0 +1,21 @@ +'use strict'; + +const visit = require('unist-util-visit'); + +module.exports = { + replaceLinks +}; + +function replaceLinks({ filename, linksMapper }) { + return (tree) => { + const fileHtmlUrls = linksMapper[filename]; + + visit(tree, 'definition', (node) => { + const htmlUrl = fileHtmlUrls && fileHtmlUrls[node.identifier]; + + if (htmlUrl && typeof htmlUrl === 'string') { + node.url = htmlUrl; + } + }); + }; +} diff --git a/tools/doc/type-parser.js b/tools/doc/type-parser.js index 0b9458351e6618..ef4499e50ff35a 100644 --- a/tools/doc/type-parser.js +++ b/tools/doc/type-parser.js @@ -18,7 +18,7 @@ const jsGlobalTypes = [ 'Array', 'ArrayBuffer', 'ArrayBufferView', 'DataView', 'Date', 'Error', 'EvalError', 'Function', 'Map', 'Object', 'Promise', 'RangeError', 'ReferenceError', 'RegExp', 'Set', 'SharedArrayBuffer', 'SyntaxError', - 'TypeError', 'TypedArray', 'URIError', 'Uint8Array', + 'TypeError', 'TypedArray', 'URIError', 'Uint8Array', 'WebAssembly.Instance', ]; const customTypesMap = { diff --git a/tools/icu/README.md b/tools/icu/README.md index 89a70e7087ff23..1af39941b55431 100644 --- a/tools/icu/README.md +++ b/tools/icu/README.md @@ -1,8 +1,9 @@ # Notes about the `tools/icu` subdirectory -This directory contains tools, data, and information about the [ICU](http://icu-project.org) -(International Components for Unicode) integration. ICU is used to provide -internationalization functionality. +This directory contains tools, data, and information about the +International Components for Unicode integration. [ICU][] is used +both by V8 and also by +Node.js itself to provide internationalization functionality. * `patches/` are one-off patches, actually entire source file replacements, organized by ICU version number. @@ -18,119 +19,21 @@ internationalization functionality. * `README.md` — you are here * `shrink-icu-src.py` — this is used during upgrade (see guide below) -## How to upgrade ICU +Note: +> The files in this directory were written for the Node.js v0.12 effort. +> The original intent was to merge the tools such as `icutrim.py` and `iculslocs.cc` +> back into ICU. ICU has gained its own “data slicer” tool. +> There is an issue open, https://github.com/nodejs/node/issues/25136 +> for replacing `icutrim.py` with the [ICU data slicer][]. -* Make sure your Node.js workspace is clean (clean `git status`) should be - sufficient. -* Configure Node.js with the specific [ICU version](http://icu-project.org/download) - you want to upgrade to, for example: +## See Also -```shell -./configure \ - --with-intl=full-icu \ - --with-icu-source=http://download.icu-project.org/files/icu4c/58.1/icu4c-58_1-src.tgz -make -``` +* [docs/guides/maintaining-icu.md](../../doc/guides/maintaining-icu.md) for +information on maintaining ICU in Node.js -> _Note_ in theory, the equivalent `vcbuild.bat` commands should work also, -> but the commands below are makefile-centric. +* [docs/api/intl.md](../../doc/api/intl.md) for information on the +internationalization-related APIs in Node.js +* [The ICU Homepage][ICU] -* If there are ICU version-specific changes needed, you may need to make changes - in `icu-generic.gyp` or add patch files to `tools/icu/patches`. - * Specifically, look for the lists in `sources!` in the `icu-generic.gyp` for - files to exclude. - -* Verify the Node.js build works: - -```shell -make test-ci -``` - -Also running - - - -```js -new Intl.DateTimeFormat('es', {month: 'long'}).format(new Date(9E8)); -``` - -…Should return `enero` not `January`. - -* Now, copy `deps/icu` over to `deps/icu-small` - -```shell -python tools/icu/shrink-icu-src.py -``` - -* Now, do a clean rebuild of Node.js to test: - -```shell -make -k distclean -./configure -make -``` - -* Test this newly default-generated Node.js - - - -```js -process.versions.icu; -new Intl.DateTimeFormat('es', {month: 'long'}).format(new Date(9E8)); -``` - -(This should print your updated ICU version number, and also `January` again.) - -You are ready to check in the updated `deps/small-icu`. This is a big commit, -so make this a separate commit from the smaller changes. - -* Now, rebuild the Node.js license. - -```shell -# clean up - remove deps/icu -make clean -tools/license-builder.sh -``` - -* Update the URL and hash for the full ICU file in `tools/icu/current_ver.dep`. -It should match the ICU URL used in the first step. When this is done, the -following should build with small ICU. - -```shell -# clean up -rm -rf out deps/icu deps/icu4c* -./configure --with-intl=small-icu --download=all -make -make test-ci -``` - -* commit the change to `tools/icu/current_ver.dep` and `LICENSE` files. - - * Note: To simplify review, I often will “pre-land” this patch, meaning that - I run the patch through `curl -L https://github.com/nodejs/node/pull/xxx.patch - | git am -3 --whitespace=fix` per the collaborator’s guide… and then push that - patched branch into my PR's branch. This reduces the whitespace changes that - show up in the PR, since the final land will eliminate those anyway. - ------ - -## Postscript about the tools - -The files in this directory were written for the Node.js effort. -It was the intent of their author (Steven R. Loomis / srl295) to -merge them upstream into ICU, pending much discussion within the -ICU-TC. - -`icu_small.json` is somewhat node-specific as it specifies a "small ICU" -configuration file for the `icutrim.py` script. `icutrim.py` and -`iculslocs.cpp` may themselves be superseded by components built into -ICU in the future. As of this writing, however, the tools are separate -entities within Node.js, although theyare being scrutinized by interested -members of the ICU-TC. The “upstream” ICU bugs are given below. - -* [#10919](http://bugs.icu-project.org/trac/ticket/10919) - (experimental branch - may copy any source patches here) -* [#10922](http://bugs.icu-project.org/trac/ticket/10922) - (data packaging improvements) -* [#10923](http://bugs.icu-project.org/trac/ticket/10923) - (rewrite data building in python) +[ICU data slicer]: https://github.com/unicode-org/icu/blob/master/docs/userguide/icu_data/buildtool.md +[ICU]: http://icu-project.org diff --git a/tools/license-builder.sh b/tools/license-builder.sh index 60f38e5cebf694..51c5b4d0c6a66d 100755 --- a/tools/license-builder.sh +++ b/tools/license-builder.sh @@ -108,4 +108,6 @@ addlicense "node-heapdump" "src/heap_utils.cc" \ addlicense "rimraf" "lib/internal/fs/rimraf.js" \ "$(curl -sL https://raw.githubusercontent.com/isaacs/rimraf/0e365ac4e4d64a25aa2a3cc026348f13410210e1/LICENSE)" +addlicense "uvwasi" "deps/uvwasi" "$(cat ${rootdir}/deps/uvwasi/LICENSE)" + mv $tmplicense $licensefile diff --git a/tools/node_modules/eslint/README.md b/tools/node_modules/eslint/README.md index fd0c14668743bc..b7cc385f0e35c7 100644 --- a/tools/node_modules/eslint/README.md +++ b/tools/node_modules/eslint/README.md @@ -206,9 +206,9 @@ Brandon Mills Toru Nagashima - -
    -Gyandeep Singh +
    +
    +Kai Cataldo
    @@ -237,9 +237,9 @@ The people who review and implement new features. The people who review and fix bugs and help triage issues.
    - -
    -Kai Cataldo +
    +
    +Gyandeep Singh
    @@ -265,7 +265,7 @@ The following companies, organizations, and individuals support ESLint's ongoing

    Gold Sponsors

    Shopify Salesforce Badoo Airbnb Facebook Open Source

    Silver Sponsors

    AMP Project

    Bronze Sponsors

    -

    UI UX Design Agencies Bugsnag Stability Monitoring MiniTool Software Ltd Codacy Mixpanel VPS Server Free Icons by Icons8 Crosswordsolver clay Discord ThemeIsle TekHattan Marfeel Fire Stick Tricks JSHeroes

    +

    Bugsnag Stability Monitoring Crosswordsolver Codacy Mixpanel VPS Server Free Icons by Icons8 UI UX Design Agencies clay Discord ThemeIsle TekHattan Marfeel Fire Stick Tricks JSHeroes

    ## Technology Sponsors diff --git a/tools/node_modules/eslint/conf/config-schema.js b/tools/node_modules/eslint/conf/config-schema.js index 164f0b4219f319..83addff8781732 100644 --- a/tools/node_modules/eslint/conf/config-schema.js +++ b/tools/node_modules/eslint/conf/config-schema.js @@ -55,6 +55,7 @@ const configSchema = { type: "object", properties: { root: { type: "boolean" }, + ignorePatterns: { $ref: "#/definitions/stringOrStrings" }, ...baseConfigProperties }, additionalProperties: false diff --git a/tools/node_modules/eslint/conf/default-cli-options.js b/tools/node_modules/eslint/conf/default-cli-options.js index abbd9184e2165c..0f7b377fb362ec 100644 --- a/tools/node_modules/eslint/conf/default-cli-options.js +++ b/tools/node_modules/eslint/conf/default-cli-options.js @@ -14,7 +14,7 @@ module.exports = { globals: [], extensions: [".js"], ignore: true, - ignorePath: null, + ignorePath: void 0, cache: false, /* diff --git a/tools/node_modules/eslint/lib/cli-engine/cascading-config-array-factory.js b/tools/node_modules/eslint/lib/cli-engine/cascading-config-array-factory.js index 6c914ea491c1b4..52703a873bb67e 100644 --- a/tools/node_modules/eslint/lib/cli-engine/cascading-config-array-factory.js +++ b/tools/node_modules/eslint/lib/cli-engine/cascading-config-array-factory.js @@ -27,7 +27,7 @@ const os = require("os"); const path = require("path"); const { validateConfigArray } = require("../shared/config-validator"); const { ConfigArrayFactory } = require("./config-array-factory"); -const { ConfigArray, ConfigDependency } = require("./config-array"); +const { ConfigArray, ConfigDependency, IgnorePattern } = require("./config-array"); const loadRules = require("./load-rules"); const debug = require("debug")("eslint:cascading-config-array-factory"); @@ -45,8 +45,9 @@ const debug = require("debug")("eslint:cascading-config-array-factory"); * @typedef {Object} CascadingConfigArrayFactoryOptions * @property {Map} [additionalPluginPool] The map for additional plugins. * @property {ConfigData} [baseConfig] The config by `baseConfig` option. - * @property {ConfigData} [cliConfig] The config by CLI options (`--env`, `--global`, `--parser`, `--parser-options`, `--plugin`, and `--rule`). CLI options overwrite the setting in config files. + * @property {ConfigData} [cliConfig] The config by CLI options (`--env`, `--global`, `--ignore-pattern`, `--parser`, `--parser-options`, `--plugin`, and `--rule`). CLI options overwrite the setting in config files. * @property {string} [cwd] The base directory to start lookup. + * @property {string} [ignorePath] The path to the alternative file of `.eslintignore`. * @property {string[]} [rulePaths] The value of `--rulesdir` option. * @property {string} [specificConfigPath] The value of `--config` option. * @property {boolean} [useEslintrc] if `false` then it doesn't load config files. @@ -62,6 +63,7 @@ const debug = require("debug")("eslint:cascading-config-array-factory"); * @property {Map} configCache The cache from directory paths to config arrays. * @property {string} cwd The base directory to start lookup. * @property {WeakMap} finalizeCache The cache from config arrays to finalized config arrays. + * @property {string} [ignorePath] The path to the alternative file of `.eslintignore`. * @property {string[]|null} rulePaths The value of `--rulesdir` option. This is used to reset `baseConfigArray`. * @property {string|null} specificConfigPath The value of `--config` option. This is used to reset `cliConfigArray`. * @property {boolean} useEslintrc if `false` then it doesn't load config files. @@ -86,14 +88,22 @@ function createBaseConfigArray({ { name: "BaseConfig" } ); + /* + * Create the config array element for the default ignore patterns. + * This element has `ignorePattern` property that ignores the default + * patterns in the current working directory. + */ + baseConfigArray.unshift(configArrayFactory.create( + { ignorePatterns: IgnorePattern.DefaultPatterns }, + { name: "DefaultIgnorePattern" } + )[0]); + + /* + * Load rules `--rulesdir` option as a pseudo plugin. + * Use a pseudo plugin to define rules of `--rulesdir`, so we can validate + * the rule's options with only information in the config array. + */ if (rulePaths && rulePaths.length > 0) { - - /* - * Load rules `--rulesdir` option as a pseudo plugin. - * Use a pseudo plugin to define rules of `--rulesdir`, so we can - * validate the rule's options with only information in the config - * array. - */ baseConfigArray.push({ name: "--rulesdir", filePath: "", @@ -128,6 +138,7 @@ function createBaseConfigArray({ function createCLIConfigArray({ cliConfigData, configArrayFactory, + ignorePath, specificConfigPath }) { const cliConfigArray = configArrayFactory.create( @@ -135,6 +146,12 @@ function createCLIConfigArray({ { name: "CLIOptions" } ); + cliConfigArray.unshift( + ...(ignorePath + ? configArrayFactory.loadESLintIgnore(ignorePath) + : configArrayFactory.loadDefaultESLintIgnore()) + ); + if (specificConfigPath) { cliConfigArray.unshift( ...configArrayFactory.loadFile( @@ -178,6 +195,7 @@ class CascadingConfigArrayFactory { baseConfig: baseConfigData = null, cliConfig: cliConfigData = null, cwd = process.cwd(), + ignorePath, resolvePluginsRelativeTo = cwd, rulePaths = [], specificConfigPath = null, @@ -200,6 +218,7 @@ class CascadingConfigArrayFactory { cliConfigArray: createCLIConfigArray({ cliConfigData, configArrayFactory, + ignorePath, specificConfigPath }), cliConfigData, @@ -207,6 +226,7 @@ class CascadingConfigArrayFactory { configCache: new Map(), cwd, finalizeCache: new WeakMap(), + ignorePath, rulePaths, specificConfigPath, useEslintrc @@ -229,9 +249,11 @@ class CascadingConfigArrayFactory { * If `filePath` was not given, it returns the config which contains only * `baseConfigData` and `cliConfigData`. * @param {string} [filePath] The file path to a file. + * @param {Object} [options] The options. + * @param {boolean} [options.ignoreNotFoundError] If `true` then it doesn't throw `ConfigurationNotFoundError`. * @returns {ConfigArray} The config array of the file. */ - getConfigArrayForFile(filePath) { + getConfigArrayForFile(filePath, { ignoreNotFoundError = false } = {}) { const { baseConfigArray, cliConfigArray, @@ -248,7 +270,8 @@ class CascadingConfigArrayFactory { return this._finalizeConfigArray( this._loadConfigInAncestors(directoryPath), - directoryPath + directoryPath, + ignoreNotFoundError ); } @@ -354,10 +377,11 @@ class CascadingConfigArrayFactory { * Concatenate `--config` and other CLI options. * @param {ConfigArray} configArray The parent config array. * @param {string} directoryPath The path to the leaf directory to find config files. + * @param {boolean} ignoreNotFoundError If `true` then it doesn't throw `ConfigurationNotFoundError`. * @returns {ConfigArray} The loaded config. * @private */ - _finalizeConfigArray(configArray, directoryPath) { + _finalizeConfigArray(configArray, directoryPath, ignoreNotFoundError) { const { cliConfigArray, configArrayFactory, @@ -403,7 +427,8 @@ class CascadingConfigArrayFactory { ); } - if (useEslintrc && finalConfigArray.length === 0) { + // At least one element (the default ignore patterns) exists. + if (!ignoreNotFoundError && useEslintrc && finalConfigArray.length <= 1) { throw new ConfigurationNotFoundError(directoryPath); } diff --git a/tools/node_modules/eslint/lib/cli-engine/cli-engine.js b/tools/node_modules/eslint/lib/cli-engine/cli-engine.js index 8afd262708fa8b..0b1c76bac6864a 100644 --- a/tools/node_modules/eslint/lib/cli-engine/cli-engine.js +++ b/tools/node_modules/eslint/lib/cli-engine/cli-engine.js @@ -25,10 +25,9 @@ const ModuleResolver = require("../shared/relative-module-resolver"); const { Linter } = require("../linter"); const builtInRules = require("../rules"); const { CascadingConfigArrayFactory } = require("./cascading-config-array-factory"); -const { getUsedExtractedConfigs } = require("./config-array"); +const { IgnorePattern, getUsedExtractedConfigs } = require("./config-array"); const { FileEnumerator } = require("./file-enumerator"); const hash = require("./hash"); -const { IgnoredPaths } = require("./ignored-paths"); const LintResultCache = require("./lint-result-cache"); const debug = require("debug")("eslint:cli-engine"); @@ -64,7 +63,7 @@ const validFixTypes = new Set(["problem", "suggestion", "layout"]); * @property {string[]} globals An array of global variables to declare. * @property {boolean} ignore False disables use of .eslintignore. * @property {string} ignorePath The ignore file to use instead of .eslintignore. - * @property {string} ignorePattern A glob pattern of files to ignore. + * @property {string|string[]} ignorePattern One or more glob patterns to ignore. * @property {boolean} useEslintrc False disables looking for .eslintrc * @property {string} parser The name of the parser to use. * @property {ParserOptions} parserOptions An object of parserOption settings to use. @@ -113,8 +112,8 @@ const validFixTypes = new Set(["problem", "suggestion", "layout"]); * @property {Map} additionalPluginPool The map for additional plugins. * @property {string} cacheFilePath The path to the cache of lint results. * @property {CascadingConfigArrayFactory} configArrayFactory The factory of configs. + * @property {(filePath: string) => boolean} defaultIgnores The default predicate function to check if a file ignored or not. * @property {FileEnumerator} fileEnumerator The file enumerator. - * @property {IgnoredPaths} ignoredPaths The ignored paths. * @property {ConfigArray[]} lastConfigArrays The list of config arrays that the last `executeOnFiles` or `executeOnText` used. * @property {LintResultCache|null} lintResultCache The cache of lint results. * @property {Linter} linter The linter instance which has loaded rules. @@ -486,13 +485,20 @@ function toBooleanMap(keys, defaultValue, displayName) { * @returns {ConfigData|null} The created config data. */ function createConfigDataFromOptions(options) { - const { parser, parserOptions, plugins, rules } = options; + const { + ignorePattern, + parser, + parserOptions, + plugins, + rules + } = options; const env = toBooleanMap(options.envs, true, "envs"); const globals = toBooleanMap(options.globals, false, "globals"); if ( env === void 0 && globals === void 0 && + (ignorePattern === void 0 || ignorePattern.length === 0) && parser === void 0 && parserOptions === void 0 && plugins === void 0 && @@ -500,7 +506,15 @@ function createConfigDataFromOptions(options) { ) { return null; } - return { env, globals, parser, parserOptions, plugins, rules }; + return { + env, + globals, + ignorePatterns: ignorePattern, + parser, + parserOptions, + plugins, + rules + }; } /** @@ -551,19 +565,18 @@ class CLIEngine { baseConfig: options.baseConfig || null, cliConfig: createConfigDataFromOptions(options), cwd: options.cwd, + ignorePath: options.ignorePath, resolvePluginsRelativeTo: options.resolvePluginsRelativeTo, rulePaths: options.rulePaths, specificConfigPath: options.configFile, useEslintrc: options.useEslintrc }); - const ignoredPaths = new IgnoredPaths(options); const fileEnumerator = new FileEnumerator({ configArrayFactory, cwd: options.cwd, extensions: options.extensions, globInputPaths: options.globInputPaths, - ignore: options.ignore, - ignoredPaths + ignore: options.ignore }); const lintResultCache = options.cache ? new LintResultCache(cacheFilePath) : null; @@ -577,8 +590,8 @@ class CLIEngine { additionalPluginPool, cacheFilePath, configArrayFactory, + defaultIgnores: IgnorePattern.createDefaultIgnore(options.cwd), fileEnumerator, - ignoredPaths, lastConfigArrays, lintResultCache, linter, @@ -835,7 +848,6 @@ class CLIEngine { const { configArrayFactory, fileEnumerator, - ignoredPaths, lastConfigArrays, linter, options: { @@ -852,7 +864,7 @@ class CLIEngine { // Clear the last used config arrays. lastConfigArrays.length = 0; - if (resolvedFilename && ignoredPaths.contains(resolvedFilename)) { + if (resolvedFilename && this.isPathIgnored(resolvedFilename)) { if (warnIgnored) { results.push(createIgnoreResult(resolvedFilename, cwd)); } @@ -926,9 +938,23 @@ class CLIEngine { * @returns {boolean} Whether or not the given path is ignored. */ isPathIgnored(filePath) { - const { ignoredPaths } = internalSlotsMap.get(this); + const { + configArrayFactory, + defaultIgnores, + options: { cwd, ignore } + } = internalSlotsMap.get(this); + const absolutePath = path.resolve(cwd, filePath); + + if (ignore) { + const config = configArrayFactory + .getConfigArrayForFile(absolutePath) + .extractConfig(absolutePath); + const ignores = config.ignores || defaultIgnores; + + return ignores(absolutePath); + } - return ignoredPaths.contains(filePath); + return defaultIgnores(absolutePath); } /** diff --git a/tools/node_modules/eslint/lib/cli-engine/config-array-factory.js b/tools/node_modules/eslint/lib/cli-engine/config-array-factory.js index cf529b6ee6313e..c444031bcb0a57 100644 --- a/tools/node_modules/eslint/lib/cli-engine/config-array-factory.js +++ b/tools/node_modules/eslint/lib/cli-engine/config-array-factory.js @@ -17,6 +17,12 @@ * Create a `ConfigArray` instance from a config file which is on a given * directory. This tries to load `.eslintrc.*` or `package.json`. If not * found, returns an empty `ConfigArray`. + * - `loadESLintIgnore(filePath)` + * Create a `ConfigArray` instance from a config file that is `.eslintignore` + * format. This is to handle `--ignore-path` option. + * - `loadDefaultESLintIgnore()` + * Create a `ConfigArray` instance from `.eslintignore` or `package.json` in + * the current working directory. * * `ConfigArrayFactory` class has the responsibility that loads configuration * files, including loading `extends`, `parser`, and `plugins`. The created @@ -40,7 +46,12 @@ const stripComments = require("strip-json-comments"); const { validateConfigSchema } = require("../shared/config-validator"); const naming = require("../shared/naming"); const ModuleResolver = require("../shared/relative-module-resolver"); -const { ConfigArray, ConfigDependency, OverrideTester } = require("./config-array"); +const { + ConfigArray, + ConfigDependency, + IgnorePattern, + OverrideTester +} = require("./config-array"); const debug = require("debug")("eslint:config-array-factory"); //------------------------------------------------------------------------------ @@ -221,6 +232,26 @@ function loadPackageJSONConfigFile(filePath) { } } +/** + * Loads a `.eslintignore` from a file. + * @param {string} filePath The filename to load. + * @returns {string[]} The ignore patterns from the file. + * @private + */ +function loadESLintIgnoreFile(filePath) { + debug(`Loading .eslintignore file: ${filePath}`); + + try { + return readFile(filePath) + .split(/\r?\n/gu) + .filter(line => line.trim() !== "" && !line.startsWith("#")); + } catch (e) { + debug(`Error reading .eslintignore file: ${filePath}`); + e.message = `Cannot read .eslintignore file: ${filePath}\nError: ${e.message}`; + throw e; + } +} + /** * Creates an error to notify about a missing config to extend from. * @param {string} configName The name of the missing config. @@ -403,6 +434,54 @@ class ConfigArrayFactory { ); } + /** + * Load `.eslintignore` file. + * @param {string} filePath The path to a `.eslintignore` file to load. + * @returns {ConfigArray} Loaded config. An empty `ConfigArray` if any config doesn't exist. + */ + loadESLintIgnore(filePath) { + const { cwd } = internalSlotsMap.get(this); + const absolutePath = path.resolve(cwd, filePath); + const name = path.relative(cwd, absolutePath); + const ignorePatterns = loadESLintIgnoreFile(absolutePath); + + return createConfigArray( + this._normalizeESLintIgnoreData(ignorePatterns, absolutePath, name) + ); + } + + /** + * Load `.eslintignore` file in the current working directory. + * @returns {ConfigArray} Loaded config. An empty `ConfigArray` if any config doesn't exist. + */ + loadDefaultESLintIgnore() { + const { cwd } = internalSlotsMap.get(this); + const eslintIgnorePath = path.resolve(cwd, ".eslintignore"); + const packageJsonPath = path.resolve(cwd, "package.json"); + + if (fs.existsSync(eslintIgnorePath)) { + return this.loadESLintIgnore(eslintIgnorePath); + } + if (fs.existsSync(packageJsonPath)) { + const data = loadJSONConfigFile(packageJsonPath); + + if (Object.hasOwnProperty.call(data, "eslintIgnore")) { + if (!Array.isArray(data.eslintIgnore)) { + throw new Error("Package.json eslintIgnore property requires an array of paths"); + } + return createConfigArray( + this._normalizeESLintIgnoreData( + data.eslintIgnore, + packageJsonPath, + "eslintIgnore in package.json" + ) + ); + } + } + + return new ConfigArray(); + } + /** * Load a given config file. * @param {string} filePath The path to a config file. @@ -451,6 +530,30 @@ class ConfigArrayFactory { return null; } + /** + * Normalize a given `.eslintignore` data to config array elements. + * @param {string[]} ignorePatterns The patterns to ignore files. + * @param {string|undefined} filePath The file path of this config. + * @param {string|undefined} name The name of this config. + * @returns {IterableIterator} The normalized config. + * @private + */ + *_normalizeESLintIgnoreData(ignorePatterns, filePath, name) { + const elements = this._normalizeObjectConfigData( + { ignorePatterns }, + filePath, + name + ); + + // Set `ignorePattern.loose` flag for backward compatibility. + for (const element of elements) { + if (element.ignorePattern) { + element.ignorePattern.loose = true; + } + yield element; + } + } + /** * Normalize a given config to an array. * @param {ConfigData} configData The config data to normalize. @@ -494,6 +597,9 @@ class ConfigArrayFactory { if (element.criteria) { element.criteria.basePath = basePath; } + if (element.ignorePattern) { + element.ignorePattern.basePath = basePath; + } /* * Merge the criteria; this is for only file extension processors in @@ -526,6 +632,7 @@ class ConfigArrayFactory { env, extends: extend, globals, + ignorePatterns, noInlineConfig, parser: parserName, parserOptions, @@ -541,6 +648,10 @@ class ConfigArrayFactory { name ) { const extendList = Array.isArray(extend) ? extend : [extend]; + const ignorePattern = ignorePatterns && new IgnorePattern( + Array.isArray(ignorePatterns) ? ignorePatterns : [ignorePatterns], + filePath ? path.dirname(filePath) : internalSlotsMap.get(this).cwd + ); // Flatten `extends`. for (const extendName of extendList.filter(Boolean)) { @@ -569,6 +680,7 @@ class ConfigArrayFactory { criteria: null, env, globals, + ignorePattern, noInlineConfig, parser, parserOptions, diff --git a/tools/node_modules/eslint/lib/cli-engine/config-array/config-array.js b/tools/node_modules/eslint/lib/cli-engine/config-array/config-array.js index 089ff305a2ba44..4fae8deaca1b69 100644 --- a/tools/node_modules/eslint/lib/cli-engine/config-array/config-array.js +++ b/tools/node_modules/eslint/lib/cli-engine/config-array/config-array.js @@ -31,6 +31,7 @@ //------------------------------------------------------------------------------ const { ExtractedConfig } = require("./extracted-config"); +const { IgnorePattern } = require("./ignore-pattern"); //------------------------------------------------------------------------------ // Helpers @@ -54,6 +55,7 @@ const { ExtractedConfig } = require("./extracted-config"); * @property {InstanceType|null} criteria The tester for the `files` and `excludedFiles` of this config element. * @property {Record|undefined} env The environment settings. * @property {Record|undefined} globals The global variable settings. + * @property {IgnorePattern|undefined} ignorePattern The ignore patterns. * @property {boolean|undefined} noInlineConfig The flag that disables directive comments. * @property {DependentParser|undefined} parser The parser loader. * @property {Object|undefined} parserOptions The parser options. @@ -231,6 +233,7 @@ function mergeRuleConfigs(target, source) { */ function createConfig(instance, indices) { const config = new ExtractedConfig(); + const ignorePatterns = []; // Merge elements. for (const index of indices) { @@ -260,6 +263,11 @@ function createConfig(instance, indices) { config.reportUnusedDisableDirectives = element.reportUnusedDisableDirectives; } + // Collect ignorePatterns + if (element.ignorePattern) { + ignorePatterns.push(element.ignorePattern); + } + // Merge others. mergeWithoutOverwrite(config.env, element.env); mergeWithoutOverwrite(config.globals, element.globals); @@ -269,6 +277,11 @@ function createConfig(instance, indices) { mergeRuleConfigs(config.rules, element.rules); } + // Create the predicate function for ignore patterns. + if (ignorePatterns.length > 0) { + config.ignores = IgnorePattern.createIgnore(ignorePatterns.reverse()); + } + return config; } diff --git a/tools/node_modules/eslint/lib/cli-engine/config-array/extracted-config.js b/tools/node_modules/eslint/lib/cli-engine/config-array/extracted-config.js index 66858313ba6220..b27d6ffb188e58 100644 --- a/tools/node_modules/eslint/lib/cli-engine/config-array/extracted-config.js +++ b/tools/node_modules/eslint/lib/cli-engine/config-array/extracted-config.js @@ -16,6 +16,8 @@ */ "use strict"; +const { IgnorePattern } = require("./ignore-pattern"); + // For VSCode intellisense /** @typedef {import("../../shared/types").ConfigData} ConfigData */ /** @typedef {import("../../shared/types").GlobalConf} GlobalConf */ @@ -23,6 +25,17 @@ /** @typedef {import("./config-dependency").DependentParser} DependentParser */ /** @typedef {import("./config-dependency").DependentPlugin} DependentPlugin */ +/** + * Check if `xs` starts with `ys`. + * @template T + * @param {T[]} xs The array to check. + * @param {T[]} ys The array that may be the first part of `xs`. + * @returns {boolean} `true` if `xs` starts with `ys`. + */ +function startsWith(xs, ys) { + return xs.length >= ys.length && ys.every((y, i) => y === xs[i]); +} + /** * The class for extracted config data. */ @@ -47,6 +60,12 @@ class ExtractedConfig { */ this.globals = {}; + /** + * The glob patterns that ignore to lint. + * @type {(((filePath:string, dot?:boolean) => boolean) & { basePath:string; patterns:string[] }) | undefined} + */ + this.ignores = void 0; + /** * The flag that disables directive comments. * @type {boolean|undefined} @@ -106,11 +125,19 @@ class ExtractedConfig { configNameOfNoInlineConfig: _ignore1, processor: _ignore2, /* eslint-enable no-unused-vars */ + ignores, ...config } = this; config.parser = config.parser && config.parser.filePath; config.plugins = Object.keys(config.plugins).filter(Boolean).reverse(); + config.ignorePatterns = ignores ? ignores.patterns : []; + + // Strip the default patterns from `ignorePatterns`. + if (startsWith(config.ignorePatterns, IgnorePattern.DefaultPatterns)) { + config.ignorePatterns = + config.ignorePatterns.slice(IgnorePattern.DefaultPatterns.length); + } return config; } diff --git a/tools/node_modules/eslint/lib/cli-engine/config-array/ignore-pattern.js b/tools/node_modules/eslint/lib/cli-engine/config-array/ignore-pattern.js new file mode 100644 index 00000000000000..6140194433e861 --- /dev/null +++ b/tools/node_modules/eslint/lib/cli-engine/config-array/ignore-pattern.js @@ -0,0 +1,231 @@ +/** + * @fileoverview `IgnorePattern` class. + * + * `IgnorePattern` class has the set of glob patterns and the base path. + * + * It provides two static methods. + * + * - `IgnorePattern.createDefaultIgnore(cwd)` + * Create the default predicate function. + * - `IgnorePattern.createIgnore(ignorePatterns)` + * Create the predicate function from multiple `IgnorePattern` objects. + * + * It provides two properties and a method. + * + * - `patterns` + * The glob patterns that ignore to lint. + * - `basePath` + * The base path of the glob patterns. If absolute paths existed in the + * glob patterns, those are handled as relative paths to the base path. + * - `getPatternsRelativeTo(basePath)` + * Get `patterns` as modified for a given base path. It modifies the + * absolute paths in the patterns as prepending the difference of two base + * paths. + * + * `ConfigArrayFactory` creates `IgnorePattern` objects when it processes + * `ignorePatterns` properties. + * + * @author Toru Nagashima + */ +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const assert = require("assert"); +const path = require("path"); +const ignore = require("ignore"); +const debug = require("debug")("eslint:ignore-pattern"); + +/** @typedef {ReturnType} Ignore */ + +//------------------------------------------------------------------------------ +// Helpers +//------------------------------------------------------------------------------ + +/** + * Get the path to the common ancestor directory of given paths. + * @param {string[]} sourcePaths The paths to calculate the common ancestor. + * @returns {string} The path to the common ancestor directory. + */ +function getCommonAncestorPath(sourcePaths) { + let result = sourcePaths[0]; + + for (let i = 1; i < sourcePaths.length; ++i) { + const a = result; + const b = sourcePaths[i]; + + // Set the shorter one (it's the common ancestor if one includes the other). + result = a.length < b.length ? a : b; + + // Set the common ancestor. + for (let j = 0, lastSepPos = 0; j < a.length && j < b.length; ++j) { + if (a[j] !== b[j]) { + result = a.slice(0, lastSepPos); + break; + } + if (a[j] === path.sep) { + lastSepPos = j; + } + } + } + + return result || path.sep; +} + +/** + * Make relative path. + * @param {string} from The source path to get relative path. + * @param {string} to The destination path to get relative path. + * @returns {string} The relative path. + */ +function relative(from, to) { + const relPath = path.relative(from, to); + + if (path.sep === "/") { + return relPath; + } + return relPath.split(path.sep).join("/"); +} + +/** + * Get the trailing slash if existed. + * @param {string} filePath The path to check. + * @returns {string} The trailing slash if existed. + */ +function dirSuffix(filePath) { + const isDir = ( + filePath.endsWith(path.sep) || + (process.platform === "win32" && filePath.endsWith("/")) + ); + + return isDir ? "/" : ""; +} + +const DefaultPatterns = Object.freeze(["/node_modules/*", "/bower_components/*"]); +const DotPatterns = Object.freeze([".*", "!../"]); + +//------------------------------------------------------------------------------ +// Public +//------------------------------------------------------------------------------ + +class IgnorePattern { + + /** + * The default patterns. + * @type {string[]} + */ + static get DefaultPatterns() { + return DefaultPatterns; + } + + /** + * Create the default predicate function. + * @param {string} cwd The current working directory. + * @returns {((filePath:string, dot:boolean) => boolean) & {basePath:string; patterns:string[]}} + * The preficate function. + * The first argument is an absolute path that is checked. + * The second argument is the flag to not ignore dotfiles. + * If the predicate function returned `true`, it means the path should be ignored. + */ + static createDefaultIgnore(cwd) { + return this.createIgnore([new IgnorePattern(DefaultPatterns, cwd)]); + } + + /** + * Create the predicate function from multiple `IgnorePattern` objects. + * @param {IgnorePattern[]} ignorePatterns The list of ignore patterns. + * @returns {((filePath:string, dot?:boolean) => boolean) & {basePath:string; patterns:string[]}} + * The preficate function. + * The first argument is an absolute path that is checked. + * The second argument is the flag to not ignore dotfiles. + * If the predicate function returned `true`, it means the path should be ignored. + */ + static createIgnore(ignorePatterns) { + debug("Create with: %o", ignorePatterns); + + const basePath = getCommonAncestorPath(ignorePatterns.map(p => p.basePath)); + const patterns = [].concat( + ...ignorePatterns.map(p => p.getPatternsRelativeTo(basePath)) + ); + const ig = ignore().add([...DotPatterns, ...patterns]); + const dotIg = ignore().add(patterns); + + debug(" processed: %o", { basePath, patterns }); + + return Object.assign( + (filePath, dot = false) => { + assert(path.isAbsolute(filePath), "'filePath' should be an absolute path."); + const relPathRaw = relative(basePath, filePath); + const relPath = relPathRaw && (relPathRaw + dirSuffix(filePath)); + const adoptedIg = dot ? dotIg : ig; + const result = relPath !== "" && adoptedIg.ignores(relPath); + + debug("Check", { filePath, dot, relativePath: relPath, result }); + return result; + }, + { basePath, patterns } + ); + } + + /** + * Initialize a new `IgnorePattern` instance. + * @param {string[]} patterns The glob patterns that ignore to lint. + * @param {string} basePath The base path of `patterns`. + */ + constructor(patterns, basePath) { + assert(path.isAbsolute(basePath), "'basePath' should be an absolute path."); + + /** + * The glob patterns that ignore to lint. + * @type {string[]} + */ + this.patterns = patterns; + + /** + * The base path of `patterns`. + * @type {string} + */ + this.basePath = basePath; + + /** + * If `true` then patterns which don't start with `/` will match the paths to the outside of `basePath`. Defaults to `false`. + * + * It's set `true` for `.eslintignore`, `package.json`, and `--ignore-path` for backward compatibility. + * It's `false` as-is for `ignorePatterns` property in config files. + * @type {boolean} + */ + this.loose = false; + } + + /** + * Get `patterns` as modified for a given base path. It modifies the + * absolute paths in the patterns as prepending the difference of two base + * paths. + * @param {string} newBasePath The base path. + * @returns {string[]} Modifired patterns. + */ + getPatternsRelativeTo(newBasePath) { + assert(path.isAbsolute(newBasePath), "'newBasePath' should be an absolute path."); + const { basePath, loose, patterns } = this; + + if (newBasePath === basePath) { + return patterns; + } + const prefix = `/${relative(newBasePath, basePath)}`; + + return patterns.map(pattern => { + const negative = pattern.startsWith("!"); + const head = negative ? "!" : ""; + const body = negative ? pattern.slice(1) : pattern; + + if (body.startsWith("/") || body.startsWith("../")) { + return `${head}${prefix}${body}`; + } + return loose ? pattern : `${head}${prefix}/**/${body}`; + }); + } +} + +module.exports = { IgnorePattern }; diff --git a/tools/node_modules/eslint/lib/cli-engine/config-array/index.js b/tools/node_modules/eslint/lib/cli-engine/config-array/index.js index de8831906fd6ef..928d76c83ab1f7 100644 --- a/tools/node_modules/eslint/lib/cli-engine/config-array/index.js +++ b/tools/node_modules/eslint/lib/cli-engine/config-array/index.js @@ -7,12 +7,14 @@ const { ConfigArray, getUsedExtractedConfigs } = require("./config-array"); const { ConfigDependency } = require("./config-dependency"); const { ExtractedConfig } = require("./extracted-config"); +const { IgnorePattern } = require("./ignore-pattern"); const { OverrideTester } = require("./override-tester"); module.exports = { ConfigArray, ConfigDependency, ExtractedConfig, + IgnorePattern, OverrideTester, getUsedExtractedConfigs }; diff --git a/tools/node_modules/eslint/lib/cli-engine/file-enumerator.js b/tools/node_modules/eslint/lib/cli-engine/file-enumerator.js index 38f55de039d6d1..700f8009cf88f2 100644 --- a/tools/node_modules/eslint/lib/cli-engine/file-enumerator.js +++ b/tools/node_modules/eslint/lib/cli-engine/file-enumerator.js @@ -40,8 +40,8 @@ const getGlobParent = require("glob-parent"); const isGlob = require("is-glob"); const { escapeRegExp } = require("lodash"); const { Minimatch } = require("minimatch"); +const { IgnorePattern } = require("./config-array"); const { CascadingConfigArrayFactory } = require("./cascading-config-array-factory"); -const { IgnoredPaths } = require("./ignored-paths"); const debug = require("debug")("eslint:file-enumerator"); //------------------------------------------------------------------------------ @@ -64,7 +64,6 @@ const IGNORED = 2; * @property {string[]} [extensions] The extensions to match files for directory patterns. * @property {boolean} [globInputPaths] Set to false to skip glob resolution of input file paths to lint (default: true). If false, each input file paths is assumed to be a non-glob path to an existing file. * @property {boolean} [ignore] The flag to check ignored files. - * @property {IgnoredPaths} [ignoredPaths] The ignored paths. * @property {string[]} [rulePaths] The value of `--rulesdir` option. */ @@ -92,8 +91,7 @@ const IGNORED = 2; * @property {RegExp} extensionRegExp The RegExp to test if a string ends with specific file extensions. * @property {boolean} globInputPaths Set to false to skip glob resolution of input file paths to lint (default: true). If false, each input file paths is assumed to be a non-glob path to an existing file. * @property {boolean} ignoreFlag The flag to check ignored files. - * @property {IgnoredPaths} ignoredPathsWithDotfiles The ignored paths but don't include dot files. - * @property {IgnoredPaths} ignoredPaths The ignored paths. + * @property {(filePath:string, dot:boolean) => boolean} defaultIgnores The default predicate function to ignore files. */ /** @type {WeakMap} */ @@ -192,12 +190,12 @@ class FileEnumerator { configArrayFactory = new CascadingConfigArrayFactory({ cwd }), extensions = [".js"], globInputPaths = true, - ignore = true, - ignoredPaths = new IgnoredPaths({ cwd, ignore }) + ignore = true } = {}) { internalSlotsMap.set(this, { configArrayFactory, cwd, + defaultIgnores: IgnorePattern.createDefaultIgnore(cwd), extensionRegExp: new RegExp( `.\\.(?:${extensions .map(ext => escapeRegExp( @@ -210,12 +208,7 @@ class FileEnumerator { "u" ), globInputPaths, - ignoreFlag: ignore, - ignoredPaths, - ignoredPathsWithDotfiles: new IgnoredPaths({ - ...ignoredPaths.options, - dotfiles: true - }) + ignoreFlag: ignore }); } @@ -321,7 +314,7 @@ class FileEnumerator { const { configArrayFactory } = internalSlotsMap.get(this); const config = configArrayFactory.getConfigArrayForFile(filePath); - const ignored = this._isIgnoredFile(filePath, { direct: true }); + const ignored = this._isIgnoredFile(filePath, { config, direct: true }); const flag = ignored ? IGNORED : NONE; return [{ config, filePath, flag }]; @@ -353,7 +346,7 @@ class FileEnumerator { _iterateFilesWithGlob(pattern, dotfiles) { debug(`Glob: ${pattern}`); - const directoryPath = getGlobParent(pattern); + const directoryPath = path.resolve(getGlobParent(pattern)); const globPart = pattern.slice(directoryPath.length + 1); /* @@ -399,9 +392,18 @@ class FileEnumerator { // Check if the file is matched. if (stat && stat.isFile()) { if (!config) { - config = configArrayFactory.getConfigArrayForFile(filePath); + config = configArrayFactory.getConfigArrayForFile( + filePath, + + /* + * We must ignore `ConfigurationNotFoundError` at this + * point because we don't know if target files exist in + * this directory. + */ + { ignoreNotFoundError: true } + ); } - const ignored = this._isIgnoredFile(filePath, options); + const ignored = this._isIgnoredFile(filePath, { ...options, config }); const flag = ignored ? IGNORED_SILENTLY : NONE; const matched = options.selector @@ -413,7 +415,11 @@ class FileEnumerator { if (matched) { debug(`Yield: ${filename}${ignored ? " but ignored" : ""}`); - yield { config, filePath, flag }; + yield { + config: configArrayFactory.getConfigArrayForFile(filePath), + filePath, + flag + }; } else { debug(`Didn't match: ${filename}`); } @@ -431,24 +437,37 @@ class FileEnumerator { * Check if a given file should be ignored. * @param {string} filePath The path to a file to check. * @param {Object} options Options + * @param {ConfigArray} [options.config] The config for this file. * @param {boolean} [options.dotfiles] If `true` then this is not ignore dot files by default. * @param {boolean} [options.direct] If `true` then this is a direct specified file. * @returns {boolean} `true` if the file should be ignored. * @private */ - _isIgnoredFile(filePath, { dotfiles = false, direct = false }) { + _isIgnoredFile(filePath, { + config: providedConfig, + dotfiles = false, + direct = false + }) { const { - ignoreFlag, - ignoredPaths, - ignoredPathsWithDotfiles + configArrayFactory, + defaultIgnores, + ignoreFlag } = internalSlotsMap.get(this); - const adoptedIgnoredPaths = dotfiles - ? ignoredPathsWithDotfiles - : ignoredPaths; - return ignoreFlag - ? adoptedIgnoredPaths.contains(filePath) - : (!direct && adoptedIgnoredPaths.contains(filePath, "default")); + if (ignoreFlag) { + const config = + providedConfig || + configArrayFactory.getConfigArrayForFile( + filePath, + { ignoreNotFoundError: true } + ); + const ignores = + config.extractConfig(filePath).ignores || defaultIgnores; + + return ignores(filePath, dotfiles); + } + + return !direct && defaultIgnores(filePath, dotfiles); } } diff --git a/tools/node_modules/eslint/lib/cli-engine/ignored-paths.js b/tools/node_modules/eslint/lib/cli-engine/ignored-paths.js deleted file mode 100644 index dec8e1860420e2..00000000000000 --- a/tools/node_modules/eslint/lib/cli-engine/ignored-paths.js +++ /dev/null @@ -1,363 +0,0 @@ -/** - * @fileoverview Responsible for loading ignore config files and managing ignore patterns - * @author Jonathan Rajavuori - */ - -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const fs = require("fs"), - path = require("path"), - ignore = require("ignore"); - -const debug = require("debug")("eslint:ignored-paths"); - -//------------------------------------------------------------------------------ -// Constants -//------------------------------------------------------------------------------ - -const ESLINT_IGNORE_FILENAME = ".eslintignore"; - -/** - * Adds `"*"` at the end of `"node_modules/"`, - * so that subtle directories could be re-included by .gitignore patterns - * such as `"!node_modules/should_not_ignored"` - */ -const DEFAULT_IGNORE_DIRS = [ - "/node_modules/*", - "/bower_components/*" -]; -const DEFAULT_OPTIONS = { - dotfiles: false, - cwd: process.cwd() -}; - -//------------------------------------------------------------------------------ -// Helpers -//------------------------------------------------------------------------------ - -/** - * Find a file in the current directory. - * @param {string} cwd Current working directory - * @param {string} name File name - * @returns {string} Path of ignore file or an empty string. - */ -function findFile(cwd, name) { - const ignoreFilePath = path.resolve(cwd, name); - - return fs.existsSync(ignoreFilePath) && fs.statSync(ignoreFilePath).isFile() ? ignoreFilePath : ""; -} - -/** - * Find an ignore file in the current directory. - * @param {string} cwd Current working directory - * @returns {string} Path of ignore file or an empty string. - */ -function findIgnoreFile(cwd) { - return findFile(cwd, ESLINT_IGNORE_FILENAME); -} - -/** - * Find an package.json file in the current directory. - * @param {string} cwd Current working directory - * @returns {string} Path of package.json file or an empty string. - */ -function findPackageJSONFile(cwd) { - return findFile(cwd, "package.json"); -} - -/** - * Merge options with defaults - * @param {Object} options Options to merge with DEFAULT_OPTIONS constant - * @returns {Object} Merged options - */ -function mergeDefaultOptions(options) { - return Object.assign({}, DEFAULT_OPTIONS, options); -} - -/* eslint-disable jsdoc/check-param-names, jsdoc/require-param */ -/** - * Normalize the path separators in a given string. - * On Windows environment, this replaces `\` by `/`. - * Otherwrise, this does nothing. - * @param {string} str The path string to normalize. - * @returns {string} The normalized path. - */ -const normalizePathSeps = path.sep === "/" - ? (str => str) - : ((seps, str) => str.replace(seps, "/")).bind(null, new RegExp(`\\${path.sep}`, "gu")); -/* eslint-enable jsdoc/check-param-names, jsdoc/require-param */ - -/** - * Converts a glob pattern to a new glob pattern relative to a different directory - * @param {string} globPattern The glob pattern, relative the the old base directory - * @param {string} relativePathToOldBaseDir A relative path from the new base directory to the old one - * @returns {string} A glob pattern relative to the new base directory - */ -function relativize(globPattern, relativePathToOldBaseDir) { - if (relativePathToOldBaseDir === "") { - return globPattern; - } - - const prefix = globPattern.startsWith("!") ? "!" : ""; - const globWithoutPrefix = globPattern.replace(/^!/u, ""); - - if (globWithoutPrefix.startsWith("/")) { - return `${prefix}/${normalizePathSeps(relativePathToOldBaseDir)}${globWithoutPrefix}`; - } - - return globPattern; -} - -//------------------------------------------------------------------------------ -// Public Interface -//------------------------------------------------------------------------------ - -/** - * IgnoredPaths class - */ -class IgnoredPaths { - - // eslint-disable-next-line jsdoc/require-description - /** - * @param {Object} providedOptions object containing 'ignore', 'ignorePath' and 'patterns' properties - */ - constructor(providedOptions) { - const options = mergeDefaultOptions(providedOptions); - - this.cache = {}; - - this.defaultPatterns = [].concat(DEFAULT_IGNORE_DIRS, options.patterns || []); - - this.ignoreFileDir = options.ignore !== false && options.ignorePath - ? path.dirname(path.resolve(options.cwd, options.ignorePath)) - : options.cwd; - this.options = options; - this._baseDir = null; - - this.ig = { - custom: ignore(), - default: ignore() - }; - - this.defaultPatterns.forEach(pattern => this.addPatternRelativeToCwd(this.ig.default, pattern)); - if (options.dotfiles !== true) { - - /* - * ignore files beginning with a dot, but not files in a parent or - * ancestor directory (which in relative format will begin with `../`). - */ - this.addPatternRelativeToCwd(this.ig.default, ".*"); - this.addPatternRelativeToCwd(this.ig.default, "!../"); - } - - /* - * Add a way to keep track of ignored files. This was present in node-ignore - * 2.x, but dropped for now as of 3.0.10. - */ - this.ig.custom.ignoreFiles = []; - this.ig.default.ignoreFiles = []; - - if (options.ignore !== false) { - let ignorePath; - - if (options.ignorePath) { - debug("Using specific ignore file"); - - try { - const stat = fs.statSync(options.ignorePath); - - if (!stat.isFile()) { - throw new Error(`${options.ignorePath} is not a file`); - } - ignorePath = options.ignorePath; - } catch (e) { - e.message = `Cannot read ignore file: ${options.ignorePath}\nError: ${e.message}`; - throw e; - } - } else { - debug(`Looking for ignore file in ${options.cwd}`); - ignorePath = findIgnoreFile(options.cwd); - - try { - fs.statSync(ignorePath); - debug(`Loaded ignore file ${ignorePath}`); - } catch (e) { - debug("Could not find ignore file in cwd"); - } - } - - if (ignorePath) { - debug(`Adding ${ignorePath}`); - this.addIgnoreFile(this.ig.custom, ignorePath); - this.addIgnoreFile(this.ig.default, ignorePath); - } else { - try { - - // if the ignoreFile does not exist, check package.json for eslintIgnore - const packageJSONPath = findPackageJSONFile(options.cwd); - - if (packageJSONPath) { - let packageJSONOptions; - - try { - packageJSONOptions = JSON.parse(fs.readFileSync(packageJSONPath, "utf8")); - } catch (e) { - debug("Could not read package.json file to check eslintIgnore property"); - e.messageTemplate = "failed-to-read-json"; - e.messageData = { - path: packageJSONPath, - message: e.message - }; - throw e; - } - - if (packageJSONOptions.eslintIgnore) { - if (Array.isArray(packageJSONOptions.eslintIgnore)) { - packageJSONOptions.eslintIgnore.forEach(pattern => { - this.addPatternRelativeToIgnoreFile(this.ig.custom, pattern); - this.addPatternRelativeToIgnoreFile(this.ig.default, pattern); - }); - } else { - throw new TypeError("Package.json eslintIgnore property requires an array of paths"); - } - } - } - } catch (e) { - debug("Could not find package.json to check eslintIgnore property"); - throw e; - } - } - - if (options.ignorePattern) { - this.addPatternRelativeToCwd(this.ig.custom, options.ignorePattern); - this.addPatternRelativeToCwd(this.ig.default, options.ignorePattern); - } - } - } - - /* - * If `ignoreFileDir` is a subdirectory of `cwd`, all paths will be normalized to be relative to `cwd`. - * Otherwise, all paths will be normalized to be relative to `ignoreFileDir`. - * This ensures that the final normalized ignore rule will not contain `..`, which is forbidden in - * ignore rules. - */ - - addPatternRelativeToCwd(ig, pattern) { - const baseDir = this.getBaseDir(); - const cookedPattern = baseDir === this.options.cwd - ? pattern - : relativize(pattern, path.relative(baseDir, this.options.cwd)); - - ig.addPattern(cookedPattern); - debug("addPatternRelativeToCwd:\n original = %j\n cooked = %j", pattern, cookedPattern); - } - - addPatternRelativeToIgnoreFile(ig, pattern) { - const baseDir = this.getBaseDir(); - const cookedPattern = baseDir === this.ignoreFileDir - ? pattern - : relativize(pattern, path.relative(baseDir, this.ignoreFileDir)); - - ig.addPattern(cookedPattern); - debug("addPatternRelativeToIgnoreFile:\n original = %j\n cooked = %j", pattern, cookedPattern); - } - - // Detect the common ancestor - getBaseDir() { - if (!this._baseDir) { - const a = path.resolve(this.options.cwd); - const b = path.resolve(this.ignoreFileDir); - let lastSepPos = 0; - - // Set the shorter one (it's the common ancestor if one includes the other). - this._baseDir = a.length < b.length ? a : b; - - // Set the common ancestor. - for (let i = 0; i < a.length && i < b.length; ++i) { - if (a[i] !== b[i]) { - this._baseDir = a.slice(0, lastSepPos); - break; - } - if (a[i] === path.sep) { - lastSepPos = i; - } - } - - // If it's only Windows drive letter, it needs \ - if (/^[A-Z]:$/u.test(this._baseDir)) { - this._baseDir += "\\"; - } - - debug("baseDir = %j", this._baseDir); - } - return this._baseDir; - } - - /** - * read ignore filepath - * @param {string} filePath file to add to ig - * @returns {Array} raw ignore rules - */ - readIgnoreFile(filePath) { - if (typeof this.cache[filePath] === "undefined") { - this.cache[filePath] = fs.readFileSync(filePath, "utf8").split(/\r?\n/gu).filter(Boolean); - } - return this.cache[filePath]; - } - - /** - * add ignore file to node-ignore instance - * @param {Object} ig instance of node-ignore - * @param {string} filePath file to add to ig - * @returns {void} - */ - addIgnoreFile(ig, filePath) { - ig.ignoreFiles.push(filePath); - this - .readIgnoreFile(filePath) - .forEach(ignoreRule => this.addPatternRelativeToIgnoreFile(ig, ignoreRule)); - } - - /** - * Determine whether a file path is included in the default or custom ignore patterns - * @param {string} filepath Path to check - * @param {string} [category=undefined] check 'default', 'custom' or both (undefined) - * @returns {boolean} true if the file path matches one or more patterns, false otherwise - */ - contains(filepath, category) { - const isDir = filepath.endsWith(path.sep) || - (path.sep === "\\" && filepath.endsWith("/")); - let result = false; - const basePath = this.getBaseDir(); - const absolutePath = path.resolve(this.options.cwd, filepath); - let relativePath = path.relative(basePath, absolutePath); - - if (relativePath) { - if (isDir) { - relativePath += path.sep; - } - if (typeof category === "undefined") { - result = - (this.ig.default.filter([relativePath]).length === 0) || - (this.ig.custom.filter([relativePath]).length === 0); - } else { - result = - (this.ig[category].filter([relativePath]).length === 0); - } - } - debug("contains:"); - debug(" target = %j", filepath); - debug(" base = %j", basePath); - debug(" relative = %j", relativePath); - debug(" result = %j", result); - - return result; - - } -} - -module.exports = { IgnoredPaths }; diff --git a/tools/node_modules/eslint/lib/init/config-initializer.js b/tools/node_modules/eslint/lib/init/config-initializer.js index 48e56ce526131a..28dfad194a7e6f 100644 --- a/tools/node_modules/eslint/lib/init/config-initializer.js +++ b/tools/node_modules/eslint/lib/init/config-initializer.js @@ -291,6 +291,7 @@ function processAnswers(answers) { jsx: true }; config.plugins = ["react"]; + config.extends.push("plugin:react/recommended"); } else if (answers.framework === "vue") { config.plugins = ["vue"]; config.extends.push("plugin:vue/essential"); @@ -522,9 +523,9 @@ function promptUser() { name: "styleguide", message: "Which style guide do you want to follow?", choices: [ - { name: "Airbnb (https://github.com/airbnb/javascript)", value: "airbnb" }, - { name: "Standard (https://github.com/standard/standard)", value: "standard" }, - { name: "Google (https://github.com/google/eslint-config-google)", value: "google" } + { name: "Airbnb: https://github.com/airbnb/javascript", value: "airbnb" }, + { name: "Standard: https://github.com/standard/standard", value: "standard" }, + { name: "Google: https://github.com/google/eslint-config-google", value: "google" } ], when(answers) { answers.packageJsonExists = npmUtils.checkPackageJson(); diff --git a/tools/node_modules/eslint/lib/linter/report-translator.js b/tools/node_modules/eslint/lib/linter/report-translator.js index 8c9ed007a25f0d..eef5165585b21d 100644 --- a/tools/node_modules/eslint/lib/linter/report-translator.js +++ b/tools/node_modules/eslint/lib/linter/report-translator.js @@ -26,6 +26,7 @@ const interpolate = require("./interpolate"); * @property {Object} [data] Optional data to use to fill in placeholders in the * message. * @property {Function} [fix] The function to call that creates a fix command. + * @property {Array<{desc?: string, messageId?: string, fix: Function}>} suggest Suggestion descriptions and functions to create a the associated fixes. */ /** @@ -34,14 +35,15 @@ const interpolate = require("./interpolate"); * @property {string} ruleId * @property {(0|1|2)} severity * @property {(string|undefined)} message - * @property {(string|undefined)} messageId + * @property {(string|undefined)} [messageId] * @property {number} line * @property {number} column - * @property {(number|undefined)} endLine - * @property {(number|undefined)} endColumn + * @property {(number|undefined)} [endLine] + * @property {(number|undefined)} [endColumn] * @property {(string|null)} nodeType * @property {string} source - * @property {({text: string, range: (number[]|null)}|null)} fix + * @property {({text: string, range: (number[]|null)}|null)} [fix] + * @property {Array<{text: string, range: (number[]|null)}|null>} [suggestions] */ //------------------------------------------------------------------------------ @@ -182,6 +184,29 @@ function normalizeFixes(descriptor, sourceCode) { return fix; } +/** + * Gets an array of suggestion objects from the given descriptor. + * @param {MessageDescriptor} descriptor The report descriptor. + * @param {SourceCode} sourceCode The source code object to get text between fixes. + * @param {Object} messages Object of meta messages for the rule. + * @returns {Array} The suggestions for the descriptor + */ +function mapSuggestions(descriptor, sourceCode, messages) { + if (!descriptor.suggest || !Array.isArray(descriptor.suggest)) { + return []; + } + + return descriptor.suggest.map(suggestInfo => { + const computedDesc = suggestInfo.desc || messages[suggestInfo.messageId]; + + return { + ...suggestInfo, + desc: interpolate(computedDesc, suggestInfo.data), + fix: normalizeFixes(suggestInfo, sourceCode) + }; + }); +} + /** * Creates information about the report from a descriptor * @param {Object} options Information about the problem @@ -192,6 +217,7 @@ function normalizeFixes(descriptor, sourceCode) { * @param {string} [options.messageId] The error message ID. * @param {{start: SourceLocation, end: (SourceLocation|null)}} options.loc Start and end location * @param {{text: string, range: (number[]|null)}} options.fix The fix object + * @param {Array<{text: string, range: (number[]|null)}>} options.suggestions The array of suggestions objects * @returns {function(...args): ReportInfo} Function that returns information about the report */ function createProblem(options) { @@ -221,9 +247,47 @@ function createProblem(options) { problem.fix = options.fix; } + if (options.suggestions && options.suggestions.length > 0) { + problem.suggestions = options.suggestions; + } + return problem; } +/** + * Validates that suggestions are properly defined. Throws if an error is detected. + * @param {Array<{ desc?: string, messageId?: string }>} suggest The incoming suggest data. + * @param {Object} messages Object of meta messages for the rule. + * @returns {void} + */ +function validateSuggestions(suggest, messages) { + if (suggest && Array.isArray(suggest)) { + suggest.forEach(suggestion => { + if (suggestion.messageId) { + const { messageId } = suggestion; + + if (!messages) { + throw new TypeError(`context.report() called with a suggest option with a messageId '${messageId}', but no messages were present in the rule metadata.`); + } + + if (!messages[messageId]) { + throw new TypeError(`context.report() called with a suggest option with a messageId '${messageId}' which is not present in the 'messages' config: ${JSON.stringify(messages, null, 2)}`); + } + + if (suggestion.desc) { + throw new TypeError("context.report() called with a suggest option that defines both a 'messageId' and an 'desc'. Please only pass one."); + } + } else if (!suggestion.desc) { + throw new TypeError("context.report() called with a suggest option that doesn't have either a `desc` or `messageId`"); + } + + if (typeof suggestion.fix !== "function") { + throw new TypeError(`context.report() called with a suggest option without a fix function. See: ${suggestion}`); + } + }); + } +} + /** * Returns a function that converts the arguments of a `context.report` call from a rule into a reported * problem for the Node.js API. @@ -242,17 +306,17 @@ module.exports = function createReportTranslator(metadata) { */ return (...args) => { const descriptor = normalizeMultiArgReportCall(...args); + const messages = metadata.messageIds; assertValidNodeInfo(descriptor); let computedMessage; if (descriptor.messageId) { - if (!metadata.messageIds) { + if (!messages) { throw new TypeError("context.report() called with a messageId, but no messages were present in the rule metadata."); } const id = descriptor.messageId; - const messages = metadata.messageIds; if (descriptor.message) { throw new TypeError("context.report() called with a message and a messageId. Please only pass one."); @@ -267,6 +331,7 @@ module.exports = function createReportTranslator(metadata) { throw new TypeError("Missing `message` property in report() call; add a message that describes the linting problem."); } + validateSuggestions(descriptor.suggest, messages); return createProblem({ ruleId: metadata.ruleId, @@ -275,7 +340,8 @@ module.exports = function createReportTranslator(metadata) { message: interpolate(computedMessage, descriptor.data), messageId: descriptor.messageId, loc: normalizeReportLoc(descriptor), - fix: metadata.disableFixes ? null : normalizeFixes(descriptor, metadata.sourceCode) + fix: metadata.disableFixes ? null : normalizeFixes(descriptor, metadata.sourceCode), + suggestions: metadata.disableFixes ? [] : mapSuggestions(descriptor, metadata.sourceCode, messages) }); }; }; diff --git a/tools/node_modules/eslint/lib/rule-tester/rule-tester.js b/tools/node_modules/eslint/lib/rule-tester/rule-tester.js index e57cd09bd87014..b4b3b2bec86c78 100644 --- a/tools/node_modules/eslint/lib/rule-tester/rule-tester.js +++ b/tools/node_modules/eslint/lib/rule-tester/rule-tester.js @@ -349,7 +349,7 @@ class RuleTester { filename = item.filename; } - if (Object.prototype.hasOwnProperty.call(item, "options")) { + if (hasOwnProperty(item, "options")) { assert(Array.isArray(item.options), "options must be an array"); config.rules[ruleName] = [1].concat(item.options); } else { @@ -577,21 +577,55 @@ class RuleTester { assert.strictEqual(message.nodeType, error.type, `Error type should be ${error.type}, found ${message.nodeType}`); } - if (Object.prototype.hasOwnProperty.call(error, "line")) { + if (hasOwnProperty(error, "line")) { assert.strictEqual(message.line, error.line, `Error line should be ${error.line}`); } - if (Object.prototype.hasOwnProperty.call(error, "column")) { + if (hasOwnProperty(error, "column")) { assert.strictEqual(message.column, error.column, `Error column should be ${error.column}`); } - if (Object.prototype.hasOwnProperty.call(error, "endLine")) { + if (hasOwnProperty(error, "endLine")) { assert.strictEqual(message.endLine, error.endLine, `Error endLine should be ${error.endLine}`); } - if (Object.prototype.hasOwnProperty.call(error, "endColumn")) { + if (hasOwnProperty(error, "endColumn")) { assert.strictEqual(message.endColumn, error.endColumn, `Error endColumn should be ${error.endColumn}`); } + + if (hasOwnProperty(error, "suggestions")) { + + // Support asserting there are no suggestions + if (!error.suggestions) { + assert.strictEqual(message.suggestions, error.suggestions, `Error should have no suggestions on error with message: "${message.message}"`); + } else { + assert.strictEqual(Array.isArray(message.suggestions), true, `Error should have an array of suggestions. Instead received "${message.suggestions}" on error with message: "${message.message}"`); + assert.strictEqual(message.suggestions.length, error.suggestions.length, `Error should have ${error.suggestions.length} suggestions. Instead found ${message.suggestions.length} suggestions`); + + error.suggestions.forEach((expectedSuggestion, index) => { + const actualSuggestion = message.suggestions[index]; + + /** + * Tests equality of a suggestion key if that key is defined in the expected output. + * @param {string} key Key to validate from the suggestion object + * @returns {void} + */ + function assertSuggestionKeyEquals(key) { + if (hasOwnProperty(expectedSuggestion, key)) { + assert.deepStrictEqual(actualSuggestion[key], expectedSuggestion[key], `Error suggestion at index: ${index} should have desc of: "${actualSuggestion[key]}"`); + } + } + assertSuggestionKeyEquals("desc"); + assertSuggestionKeyEquals("messageId"); + + if (hasOwnProperty(expectedSuggestion, "output")) { + const codeWithAppliedSuggestion = SourceCodeFixer.applyFixes(item.code, [actualSuggestion]).output; + + assert.strictEqual(codeWithAppliedSuggestion, expectedSuggestion.output, `Expected the applied suggestion fix to match the test suggestion output for suggestion at index: ${index} on error with message: "${message.message}"`); + } + }); + } + } } else { // Message was an unexpected type @@ -600,7 +634,7 @@ class RuleTester { } } - if (Object.prototype.hasOwnProperty.call(item, "output")) { + if (hasOwnProperty(item, "output")) { if (item.output === null) { assert.strictEqual( result.output, diff --git a/tools/node_modules/eslint/lib/rules/camelcase.js b/tools/node_modules/eslint/lib/rules/camelcase.js index cdc16fab9f9460..a06c7f94042ad4 100644 --- a/tools/node_modules/eslint/lib/rules/camelcase.js +++ b/tools/node_modules/eslint/lib/rules/camelcase.js @@ -28,6 +28,10 @@ module.exports = { type: "boolean", default: false }, + ignoreImports: { + type: "boolean", + default: false + }, properties: { enum: ["always", "never"] }, @@ -56,6 +60,7 @@ module.exports = { const options = context.options[0] || {}; let properties = options.properties || ""; const ignoreDestructuring = options.ignoreDestructuring; + const ignoreImports = options.ignoreImports; const allow = options.allow || []; if (properties !== "always" && properties !== "never") { @@ -79,7 +84,7 @@ module.exports = { function isUnderscored(name) { // if there's an underscore, it might be A_CONSTANT, which is okay - return name.indexOf("_") > -1 && name !== name.toUpperCase(); + return name.includes("_") && name !== name.toUpperCase(); } /** @@ -89,9 +94,9 @@ module.exports = { * @private */ function isAllowed(name) { - return allow.findIndex( + return allow.some( entry => name === entry || name.match(new RegExp(entry, "u")) - ) !== -1; + ); } /** @@ -127,7 +132,7 @@ module.exports = { * @private */ function report(node) { - if (reported.indexOf(node) < 0) { + if (!reported.includes(node)) { reported.push(node); context.report({ node, messageId: "notCamelCase", data: { name: node.name } }); } @@ -209,10 +214,18 @@ module.exports = { } // Check if it's an import specifier - } else if (["ImportSpecifier", "ImportNamespaceSpecifier", "ImportDefaultSpecifier"].indexOf(node.parent.type) >= 0) { + } else if (["ImportSpecifier", "ImportNamespaceSpecifier", "ImportDefaultSpecifier"].includes(node.parent.type)) { + + if (node.parent.type === "ImportSpecifier" && ignoreImports) { + return; + } // Report only if the local imported identifier is underscored - if (node.parent.local && node.parent.local.name === node.name && nameIsUnderscored) { + if ( + node.parent.local && + node.parent.local.name === node.name && + nameIsUnderscored + ) { report(node); } diff --git a/tools/node_modules/eslint/lib/rules/comma-dangle.js b/tools/node_modules/eslint/lib/rules/comma-dangle.js index fb2d167c77eef9..e22b7f3551e114 100644 --- a/tools/node_modules/eslint/lib/rules/comma-dangle.js +++ b/tools/node_modules/eslint/lib/rules/comma-dangle.js @@ -231,7 +231,7 @@ module.exports = { if (astUtils.isCommaToken(trailingToken)) { context.report({ node: lastItem, - loc: trailingToken.loc.start, + loc: trailingToken.loc, messageId: "unexpected", fix(fixer) { return fixer.remove(trailingToken); @@ -267,7 +267,10 @@ module.exports = { if (trailingToken.value !== ",") { context.report({ node: lastItem, - loc: trailingToken.loc.end, + loc: { + start: trailingToken.loc.end, + end: astUtils.getNextLocation(sourceCode, trailingToken.loc.end) + }, messageId: "missing", fix(fixer) { return fixer.insertTextAfter(trailingToken, ","); diff --git a/tools/node_modules/eslint/lib/rules/computed-property-spacing.js b/tools/node_modules/eslint/lib/rules/computed-property-spacing.js index bc8be964f4ffee..a0bd7f48ced66b 100644 --- a/tools/node_modules/eslint/lib/rules/computed-property-spacing.js +++ b/tools/node_modules/eslint/lib/rules/computed-property-spacing.js @@ -153,10 +153,10 @@ module.exports = { const property = node[propertyName]; - const before = sourceCode.getTokenBefore(property), - first = sourceCode.getFirstToken(property), - last = sourceCode.getLastToken(property), - after = sourceCode.getTokenAfter(property); + const before = sourceCode.getTokenBefore(property, astUtils.isOpeningBracketToken), + first = sourceCode.getTokenAfter(before, { includeComments: true }), + after = sourceCode.getTokenAfter(property, astUtils.isClosingBracketToken), + last = sourceCode.getTokenBefore(after, { includeComments: true }); if (astUtils.isTokenOnSameLine(before, first)) { if (propertyNameMustBeSpaced) { diff --git a/tools/node_modules/eslint/lib/rules/curly.js b/tools/node_modules/eslint/lib/rules/curly.js index 93c74d11fcf35f..ee2fe4dceb82bf 100644 --- a/tools/node_modules/eslint/lib/rules/curly.js +++ b/tools/node_modules/eslint/lib/rules/curly.js @@ -97,10 +97,15 @@ module.exports = { * @private */ function isOneLiner(node) { - const first = sourceCode.getFirstToken(node), - last = sourceCode.getLastToken(node); + if (node.type === "EmptyStatement") { + return true; + } + + const first = sourceCode.getFirstToken(node); + const last = sourceCode.getLastToken(node); + const lastExcludingSemicolon = astUtils.isSemicolonToken(last) ? sourceCode.getTokenBefore(last) : last; - return first.loc.start.line === last.loc.end.line; + return first.loc.start.line === lastExcludingSemicolon.loc.end.line; } /** @@ -240,7 +245,7 @@ module.exports = { if (node.type === "IfStatement" && node.consequent === body && requiresBraceOfConsequent(node)) { expected = true; } else if (multiOnly) { - if (hasBlock && body.body.length === 1) { + if (hasBlock && body.body.length === 1 && !isLexicalDeclaration(body.body[0])) { expected = false; } } else if (multiLine) { diff --git a/tools/node_modules/eslint/lib/rules/function-call-argument-newline.js b/tools/node_modules/eslint/lib/rules/function-call-argument-newline.js index 31ebc097c46f51..b6abbe95fa98c3 100644 --- a/tools/node_modules/eslint/lib/rules/function-call-argument-newline.js +++ b/tools/node_modules/eslint/lib/rules/function-call-argument-newline.js @@ -70,6 +70,8 @@ module.exports = { { includeComments: true } ); + const hasLineCommentBefore = tokenBefore.type === "Line"; + context.report({ node, loc: { @@ -77,7 +79,7 @@ module.exports = { end: currentArgToken.loc.start }, messageId: checker.messageId, - fix: checker.createFix(currentArgToken, tokenBefore) + fix: hasLineCommentBefore ? null : checker.createFix(currentArgToken, tokenBefore) }); } } diff --git a/tools/node_modules/eslint/lib/rules/grouped-accessor-pairs.js b/tools/node_modules/eslint/lib/rules/grouped-accessor-pairs.js new file mode 100644 index 00000000000000..a790f83750b2eb --- /dev/null +++ b/tools/node_modules/eslint/lib/rules/grouped-accessor-pairs.js @@ -0,0 +1,224 @@ +/** + * @fileoverview Rule to require grouped accessor pairs in object literals and classes + * @author Milos Djermanovic + */ + +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const astUtils = require("./utils/ast-utils"); + +//------------------------------------------------------------------------------ +// Typedefs +//------------------------------------------------------------------------------ + +/** + * Property name if it can be computed statically, otherwise the list of the tokens of the key node. + * @typedef {string|Token[]} Key + */ + +/** + * Accessor nodes with the same key. + * @typedef {Object} AccessorData + * @property {Key} key Accessor's key + * @property {ASTNode[]} getters List of getter nodes. + * @property {ASTNode[]} setters List of setter nodes. + */ + +//------------------------------------------------------------------------------ +// Helpers +//------------------------------------------------------------------------------ + +/** + * Checks whether or not the given lists represent the equal tokens in the same order. + * Tokens are compared by their properties, not by instance. + * @param {Token[]} left First list of tokens. + * @param {Token[]} right Second list of tokens. + * @returns {boolean} `true` if the lists have same tokens. + */ +function areEqualTokenLists(left, right) { + if (left.length !== right.length) { + return false; + } + + for (let i = 0; i < left.length; i++) { + const leftToken = left[i], + rightToken = right[i]; + + if (leftToken.type !== rightToken.type || leftToken.value !== rightToken.value) { + return false; + } + } + + return true; +} + +/** + * Checks whether or not the given keys are equal. + * @param {Key} left First key. + * @param {Key} right Second key. + * @returns {boolean} `true` if the keys are equal. + */ +function areEqualKeys(left, right) { + if (typeof left === "string" && typeof right === "string") { + + // Statically computed names. + return left === right; + } + if (Array.isArray(left) && Array.isArray(right)) { + + // Token lists. + return areEqualTokenLists(left, right); + } + + return false; +} + +/** + * Checks whether or not a given node is of an accessor kind ('get' or 'set'). + * @param {ASTNode} node A node to check. + * @returns {boolean} `true` if the node is of an accessor kind. + */ +function isAccessorKind(node) { + return node.kind === "get" || node.kind === "set"; +} + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +module.exports = { + meta: { + type: "suggestion", + + docs: { + description: "require grouped accessor pairs in object literals and classes", + category: "Best Practices", + recommended: false, + url: "https://eslint.org/docs/rules/grouped-accessor-pairs" + }, + + schema: [ + { + enum: ["anyOrder", "getBeforeSet", "setBeforeGet"] + } + ], + + messages: { + notGrouped: "Accessor pair {{ formerName }} and {{ latterName }} should be grouped.", + invalidOrder: "Expected {{ latterName }} to be before {{ formerName }}." + } + }, + + create(context) { + const order = context.options[0] || "anyOrder"; + const sourceCode = context.getSourceCode(); + + /** + * Reports the given accessor pair. + * @param {string} messageId messageId to report. + * @param {ASTNode} formerNode getter/setter node that is defined before `latterNode`. + * @param {ASTNode} latterNode getter/setter node that is defined after `formerNode`. + * @returns {void} + * @private + */ + function report(messageId, formerNode, latterNode) { + context.report({ + node: latterNode, + messageId, + loc: astUtils.getFunctionHeadLoc(latterNode.value, sourceCode), + data: { + formerName: astUtils.getFunctionNameWithKind(formerNode.value), + latterName: astUtils.getFunctionNameWithKind(latterNode.value) + } + }); + } + + /** + * Creates a new `AccessorData` object for the given getter or setter node. + * @param {ASTNode} node A getter or setter node. + * @returns {AccessorData} New `AccessorData` object that contains the given node. + * @private + */ + function createAccessorData(node) { + const name = astUtils.getStaticPropertyName(node); + const key = (name !== null) ? name : sourceCode.getTokens(node.key); + + return { + key, + getters: node.kind === "get" ? [node] : [], + setters: node.kind === "set" ? [node] : [] + }; + } + + /** + * Merges the given `AccessorData` object into the given accessors list. + * @param {AccessorData[]} accessors The list to merge into. + * @param {AccessorData} accessorData The object to merge. + * @returns {AccessorData[]} The same instance with the merged object. + * @private + */ + function mergeAccessorData(accessors, accessorData) { + const equalKeyElement = accessors.find(a => areEqualKeys(a.key, accessorData.key)); + + if (equalKeyElement) { + equalKeyElement.getters.push(...accessorData.getters); + equalKeyElement.setters.push(...accessorData.setters); + } else { + accessors.push(accessorData); + } + + return accessors; + } + + /** + * Checks accessor pairs in the given list of nodes. + * @param {ASTNode[]} nodes The list to check. + * @param {Function} shouldCheck – Predicate that returns `true` if the node should be checked. + * @returns {void} + * @private + */ + function checkList(nodes, shouldCheck) { + const accessors = nodes + .filter(shouldCheck) + .filter(isAccessorKind) + .map(createAccessorData) + .reduce(mergeAccessorData, []); + + for (const { getters, setters } of accessors) { + + // Don't report accessor properties that have duplicate getters or setters. + if (getters.length === 1 && setters.length === 1) { + const [getter] = getters, + [setter] = setters, + getterIndex = nodes.indexOf(getter), + setterIndex = nodes.indexOf(setter), + formerNode = getterIndex < setterIndex ? getter : setter, + latterNode = getterIndex < setterIndex ? setter : getter; + + if (Math.abs(getterIndex - setterIndex) > 1) { + report("notGrouped", formerNode, latterNode); + } else if ( + (order === "getBeforeSet" && getterIndex > setterIndex) || + (order === "setBeforeGet" && getterIndex < setterIndex) + ) { + report("invalidOrder", formerNode, latterNode); + } + } + } + } + + return { + ObjectExpression(node) { + checkList(node.properties, n => n.type === "Property"); + }, + ClassBody(node) { + checkList(node.body, n => n.type === "MethodDefinition" && !n.static); + checkList(node.body, n => n.type === "MethodDefinition" && n.static); + } + }; + } +}; diff --git a/tools/node_modules/eslint/lib/rules/indent.js b/tools/node_modules/eslint/lib/rules/indent.js index 94c83692b39f0f..694cf7d9e6ed4f 100644 --- a/tools/node_modules/eslint/lib/rules/indent.js +++ b/tools/node_modules/eslint/lib/rules/indent.js @@ -1492,6 +1492,17 @@ module.exports = { ); }, + JSXSpreadAttribute(node) { + const openingCurly = sourceCode.getFirstToken(node); + const closingCurly = sourceCode.getLastToken(node); + + offsets.setDesiredOffsets( + [openingCurly.range[1], closingCurly.range[0]], + openingCurly, + 1 + ); + }, + "*"(node) { const firstToken = sourceCode.getFirstToken(node); diff --git a/tools/node_modules/eslint/lib/rules/index.js b/tools/node_modules/eslint/lib/rules/index.js index 8b0abc4ee7a8f9..d3fbe412080360 100644 --- a/tools/node_modules/eslint/lib/rules/index.js +++ b/tools/node_modules/eslint/lib/rules/index.js @@ -52,6 +52,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({ "generator-star-spacing": () => require("./generator-star-spacing"), "getter-return": () => require("./getter-return"), "global-require": () => require("./global-require"), + "grouped-accessor-pairs": () => require("./grouped-accessor-pairs"), "guard-for-in": () => require("./guard-for-in"), "handle-callback-err": () => require("./handle-callback-err"), "id-blacklist": () => require("./id-blacklist"), @@ -101,6 +102,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({ "no-console": () => require("./no-console"), "no-const-assign": () => require("./no-const-assign"), "no-constant-condition": () => require("./no-constant-condition"), + "no-constructor-return": () => require("./no-constructor-return"), "no-continue": () => require("./no-continue"), "no-control-regex": () => require("./no-control-regex"), "no-debugger": () => require("./no-debugger"), @@ -108,6 +110,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({ "no-div-regex": () => require("./no-div-regex"), "no-dupe-args": () => require("./no-dupe-args"), "no-dupe-class-members": () => require("./no-dupe-class-members"), + "no-dupe-else-if": () => require("./no-dupe-else-if"), "no-dupe-keys": () => require("./no-dupe-keys"), "no-duplicate-case": () => require("./no-duplicate-case"), "no-duplicate-imports": () => require("./no-duplicate-imports"), @@ -186,6 +189,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({ "no-self-assign": () => require("./no-self-assign"), "no-self-compare": () => require("./no-self-compare"), "no-sequences": () => require("./no-sequences"), + "no-setter-return": () => require("./no-setter-return"), "no-shadow": () => require("./no-shadow"), "no-shadow-restricted-names": () => require("./no-shadow-restricted-names"), "no-spaced-func": () => require("./no-spaced-func"), @@ -238,6 +242,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({ "prefer-arrow-callback": () => require("./prefer-arrow-callback"), "prefer-const": () => require("./prefer-const"), "prefer-destructuring": () => require("./prefer-destructuring"), + "prefer-exponentiation-operator": () => require("./prefer-exponentiation-operator"), "prefer-named-capture-group": () => require("./prefer-named-capture-group"), "prefer-numeric-literals": () => require("./prefer-numeric-literals"), "prefer-object-spread": () => require("./prefer-object-spread"), diff --git a/tools/node_modules/eslint/lib/rules/multiline-comment-style.js b/tools/node_modules/eslint/lib/rules/multiline-comment-style.js index 6578a120126bfb..fb50e1522ea11d 100644 --- a/tools/node_modules/eslint/lib/rules/multiline-comment-style.js +++ b/tools/node_modules/eslint/lib/rules/multiline-comment-style.js @@ -43,17 +43,150 @@ module.exports = { //---------------------------------------------------------------------- /** - * Gets a list of comment lines in a group - * @param {Token[]} commentGroup A group of comments, containing either multiple line comments or a single block comment - * @returns {string[]} A list of comment lines + * Checks if a comment line is starred. + * @param {string} line A string representing a comment line. + * @returns {boolean} Whether or not the comment line is starred. + */ + function isStarredCommentLine(line) { + return /^\s*\*/u.test(line); + } + + /** + * Checks if a comment group is in starred-block form. + * @param {Token[]} commentGroup A group of comments, containing either multiple line comments or a single block comment. + * @returns {boolean} Whether or not the comment group is in starred block form. + */ + function isStarredBlockComment([firstComment]) { + if (firstComment.type !== "Block") { + return false; + } + + const lines = firstComment.value.split(astUtils.LINEBREAK_MATCHER); + + // The first and last lines can only contain whitespace. + return lines.length > 0 && lines.every((line, i) => (i === 0 || i === lines.length - 1 ? /^\s*$/u : /^\s*\*/u).test(line)); + } + + /** + * Checks if a comment group is in JSDoc form. + * @param {Token[]} commentGroup A group of comments, containing either multiple line comments or a single block comment. + * @returns {boolean} Whether or not the comment group is in JSDoc form. + */ + function isJSDocComment([firstComment]) { + if (firstComment.type !== "Block") { + return false; + } + + const lines = firstComment.value.split(astUtils.LINEBREAK_MATCHER); + + return /^\*\s*$/u.test(lines[0]) && + lines.slice(1, -1).every(line => /^\s* /u.test(line)) && + /^\s*$/u.test(lines[lines.length - 1]); + } + + /** + * Processes a comment group that is currently in separate-line form, calculating the offset for each line. + * @param {Token[]} commentGroup A group of comments containing multiple line comments. + * @returns {string[]} An array of the processed lines. + */ + function processSeparateLineComments(commentGroup) { + const allLinesHaveLeadingSpace = commentGroup + .map(({ value }) => value) + .filter(line => line.trim().length) + .every(line => line.startsWith(" ")); + + return commentGroup.map(({ value }) => (allLinesHaveLeadingSpace ? value.replace(/^ /u, "") : value)); + } + + /** + * Processes a comment group that is currently in starred-block form, calculating the offset for each line. + * @param {Token} comment A single block comment token in starred-block form. + * @returns {string[]} An array of the processed lines. + */ + function processStarredBlockComment(comment) { + const lines = comment.value.split(astUtils.LINEBREAK_MATCHER) + .filter((line, i, linesArr) => !(i === 0 || i === linesArr.length - 1)) + .map(line => line.replace(/^\s*$/u, "")); + const allLinesHaveLeadingSpace = lines + .map(line => line.replace(/\s*\*/u, "")) + .filter(line => line.trim().length) + .every(line => line.startsWith(" ")); + + return lines.map(line => line.replace(allLinesHaveLeadingSpace ? /\s*\* ?/u : /\s*\*/u, "")); + } + + /** + * Processes a comment group that is currently in bare-block form, calculating the offset for each line. + * @param {Token} comment A single block comment token in bare-block form. + * @returns {string[]} An array of the processed lines. + */ + function processBareBlockComment(comment) { + const lines = comment.value.split(astUtils.LINEBREAK_MATCHER).map(line => line.replace(/^\s*$/u, "")); + const leadingWhitespace = `${sourceCode.text.slice(comment.range[0] - comment.loc.start.column, comment.range[0])} `; + let offset = ""; + + /* + * Calculate the offset of the least indented line and use that as the basis for offsetting all the lines. + * The first line should not be checked because it is inline with the opening block comment delimiter. + */ + for (const [i, line] of lines.entries()) { + if (!line.trim().length || i === 0) { + continue; + } + + const [, lineOffset] = line.match(/^(\s*\*?\s*)/u); + + if (lineOffset.length < leadingWhitespace.length) { + const newOffset = leadingWhitespace.slice(lineOffset.length - leadingWhitespace.length); + + if (newOffset.length > offset.length) { + offset = newOffset; + } + } + } + + return lines.map(line => { + const match = line.match(/^(\s*\*?\s*)(.*)/u); + const [, lineOffset, lineContents] = match; + + if (lineOffset.length > leadingWhitespace.length) { + return `${lineOffset.slice(leadingWhitespace.length - (offset.length + lineOffset.length))}${lineContents}`; + } + + if (lineOffset.length < leadingWhitespace.length) { + return `${lineOffset.slice(leadingWhitespace.length)}${lineContents}`; + } + + return lineContents; + }); + } + + /** + * Gets a list of comment lines in a group, formatting leading whitespace as necessary. + * @param {Token[]} commentGroup A group of comments containing either multiple line comments or a single block comment. + * @returns {string[]} A list of comment lines. */ function getCommentLines(commentGroup) { - if (commentGroup[0].type === "Line") { - return commentGroup.map(comment => comment.value); + const [firstComment] = commentGroup; + + if (firstComment.type === "Line") { + return processSeparateLineComments(commentGroup); } - return commentGroup[0].value - .split(astUtils.LINEBREAK_MATCHER) - .map(line => line.replace(/^\s*\*?/u, "")); + + if (isStarredBlockComment(commentGroup)) { + return processStarredBlockComment(firstComment); + } + + return processBareBlockComment(firstComment); + } + + /** + * Gets the initial offset (whitespace) from the beginning of a line to a given comment token. + * @param {Token} comment The token to check. + * @returns {string} The offset from the beginning of a line to the token. + */ + function getInitialOffset(comment) { + return sourceCode.text.slice(comment.range[0] - comment.loc.start.column, comment.range[0]); } /** @@ -63,10 +196,9 @@ module.exports = { * @returns {string} A representation of the comment value in starred-block form, excluding start and end markers */ function convertToStarredBlock(firstComment, commentLinesList) { - const initialOffset = sourceCode.text.slice(firstComment.range[0] - firstComment.loc.start.column, firstComment.range[0]); - const starredLines = commentLinesList.map(line => `${initialOffset} *${line}`); + const initialOffset = getInitialOffset(firstComment); - return `\n${starredLines.join("\n")}\n${initialOffset} `; + return `/*\n${commentLinesList.map(line => `${initialOffset} * ${line}`).join("\n")}\n${initialOffset} */`; } /** @@ -76,10 +208,7 @@ module.exports = { * @returns {string} A representation of the comment value in separate-line form */ function convertToSeparateLines(firstComment, commentLinesList) { - const initialOffset = sourceCode.text.slice(firstComment.range[0] - firstComment.loc.start.column, firstComment.range[0]); - const separateLines = commentLinesList.map(line => `// ${line.trim()}`); - - return separateLines.join(`\n${initialOffset}`); + return commentLinesList.map(line => `// ${line}`).join(`\n${getInitialOffset(firstComment)}`); } /** @@ -89,24 +218,7 @@ module.exports = { * @returns {string} A representation of the comment value in bare-block form */ function convertToBlock(firstComment, commentLinesList) { - const initialOffset = sourceCode.text.slice(firstComment.range[0] - firstComment.loc.start.column, firstComment.range[0]); - const blockLines = commentLinesList.map(line => line.trim()); - - return `/* ${blockLines.join(`\n${initialOffset} `)} */`; - } - - /** - * Check a comment is JSDoc form - * @param {Token[]} commentGroup A group of comments, containing either multiple line comments or a single block comment - * @returns {boolean} if commentGroup is JSDoc form, return true - */ - function isJSDoc(commentGroup) { - const lines = commentGroup[0].value.split(astUtils.LINEBREAK_MATCHER); - - return commentGroup[0].type === "Block" && - /^\*\s*$/u.test(lines[0]) && - lines.slice(1, -1).every(line => /^\s* /u.test(line)) && - /^\s*$/u.test(lines[lines.length - 1]); + return `/* ${commentLinesList.join(`\n${getInitialOffset(firstComment)} `)} */`; } /** @@ -117,6 +229,7 @@ module.exports = { */ const commentGroupCheckers = { "starred-block"(commentGroup) { + const [firstComment] = commentGroup; const commentLines = getCommentLines(commentGroup); if (commentLines.some(value => value.includes("*/"))) { @@ -126,31 +239,30 @@ module.exports = { if (commentGroup.length > 1) { context.report({ loc: { - start: commentGroup[0].loc.start, + start: firstComment.loc.start, end: commentGroup[commentGroup.length - 1].loc.end }, messageId: "expectedBlock", fix(fixer) { - const range = [commentGroup[0].range[0], commentGroup[commentGroup.length - 1].range[1]]; - const starredBlock = `/*${convertToStarredBlock(commentGroup[0], commentLines)}*/`; + const range = [firstComment.range[0], commentGroup[commentGroup.length - 1].range[1]]; return commentLines.some(value => value.startsWith("/")) ? null - : fixer.replaceTextRange(range, starredBlock); + : fixer.replaceTextRange(range, convertToStarredBlock(firstComment, commentLines)); } }); } else { - const block = commentGroup[0]; - const lines = block.value.split(astUtils.LINEBREAK_MATCHER); - const expectedLinePrefix = `${sourceCode.text.slice(block.range[0] - block.loc.start.column, block.range[0])} *`; + const lines = firstComment.value.split(astUtils.LINEBREAK_MATCHER); + const expectedLeadingWhitespace = getInitialOffset(firstComment); + const expectedLinePrefix = `${expectedLeadingWhitespace} *`; if (!/^\*?\s*$/u.test(lines[0])) { - const start = block.value.startsWith("*") ? block.range[0] + 1 : block.range[0]; + const start = firstComment.value.startsWith("*") ? firstComment.range[0] + 1 : firstComment.range[0]; context.report({ loc: { - start: block.loc.start, - end: { line: block.loc.start.line, column: block.loc.start.column + 2 } + start: firstComment.loc.start, + end: { line: firstComment.loc.start.line, column: firstComment.loc.start.column + 2 } }, messageId: "startNewline", fix: fixer => fixer.insertTextAfterRange([start, start + 2], `\n${expectedLinePrefix}`) @@ -160,36 +272,54 @@ module.exports = { if (!/^\s*$/u.test(lines[lines.length - 1])) { context.report({ loc: { - start: { line: block.loc.end.line, column: block.loc.end.column - 2 }, - end: block.loc.end + start: { line: firstComment.loc.end.line, column: firstComment.loc.end.column - 2 }, + end: firstComment.loc.end }, messageId: "endNewline", - fix: fixer => fixer.replaceTextRange([block.range[1] - 2, block.range[1]], `\n${expectedLinePrefix}/`) + fix: fixer => fixer.replaceTextRange([firstComment.range[1] - 2, firstComment.range[1]], `\n${expectedLinePrefix}/`) }); } - for (let lineNumber = block.loc.start.line + 1; lineNumber <= block.loc.end.line; lineNumber++) { + for (let lineNumber = firstComment.loc.start.line + 1; lineNumber <= firstComment.loc.end.line; lineNumber++) { const lineText = sourceCode.lines[lineNumber - 1]; + const errorType = isStarredCommentLine(lineText) + ? "alignment" + : "missingStar"; if (!lineText.startsWith(expectedLinePrefix)) { context.report({ loc: { start: { line: lineNumber, column: 0 }, - end: { line: lineNumber, column: sourceCode.lines[lineNumber - 1].length } + end: { line: lineNumber, column: lineText.length } }, - messageId: /^\s*\*/u.test(lineText) - ? "alignment" - : "missingStar", + messageId: errorType, fix(fixer) { const lineStartIndex = sourceCode.getIndexFromLoc({ line: lineNumber, column: 0 }); - const linePrefixLength = lineText.match(/^\s*\*? ?/u)[0].length; - const commentStartIndex = lineStartIndex + linePrefixLength; - const replacementText = lineNumber === block.loc.end.line || lineText.length === linePrefixLength - ? expectedLinePrefix - : `${expectedLinePrefix} `; + if (errorType === "alignment") { + const [, commentTextPrefix = ""] = lineText.match(/^(\s*\*)/u) || []; + const commentTextStartIndex = lineStartIndex + commentTextPrefix.length; + + return fixer.replaceTextRange([lineStartIndex, commentTextStartIndex], expectedLinePrefix); + } + + const [, commentTextPrefix = ""] = lineText.match(/^(\s*)/u) || []; + const commentTextStartIndex = lineStartIndex + commentTextPrefix.length; + let offset; + + for (const [idx, line] of lines.entries()) { + if (!/\S+/u.test(line)) { + continue; + } + + const lineTextToAlignWith = sourceCode.lines[firstComment.loc.start.line - 1 + idx]; + const [, prefix = "", initialOffset = ""] = lineTextToAlignWith.match(/^(\s*(?:\/?\*)?(\s*))/u) || []; - return fixer.replaceTextRange([lineStartIndex, commentStartIndex], replacementText); + offset = `${commentTextPrefix.slice(prefix.length)}${initialOffset}`; + break; + } + + return fixer.replaceTextRange([lineStartIndex, commentTextStartIndex], `${expectedLinePrefix}${offset}`); } }); } @@ -197,67 +327,68 @@ module.exports = { } }, "separate-lines"(commentGroup) { - if (!isJSDoc(commentGroup) && commentGroup[0].type === "Block") { - const commentLines = getCommentLines(commentGroup); - const block = commentGroup[0]; - const tokenAfter = sourceCode.getTokenAfter(block, { includeComments: true }); + const [firstComment] = commentGroup; + + if (firstComment.type !== "Block" || isJSDocComment(commentGroup)) { + return; + } - if (tokenAfter && block.loc.end.line === tokenAfter.loc.start.line) { - return; + const commentLines = getCommentLines(commentGroup); + const tokenAfter = sourceCode.getTokenAfter(firstComment, { includeComments: true }); + + if (tokenAfter && firstComment.loc.end.line === tokenAfter.loc.start.line) { + return; + } + + context.report({ + loc: { + start: firstComment.loc.start, + end: { line: firstComment.loc.start.line, column: firstComment.loc.start.column + 2 } + }, + messageId: "expectedLines", + fix(fixer) { + return fixer.replaceText(firstComment, convertToSeparateLines(firstComment, commentLines)); } + }); + }, + "bare-block"(commentGroup) { + if (isJSDocComment(commentGroup)) { + return; + } + const [firstComment] = commentGroup; + const commentLines = getCommentLines(commentGroup); + + // Disallows consecutive line comments in favor of using a block comment. + if (firstComment.type === "Line" && commentLines.length > 1 && + !commentLines.some(value => value.includes("*/"))) { context.report({ loc: { - start: block.loc.start, - end: { line: block.loc.start.line, column: block.loc.start.column + 2 } + start: firstComment.loc.start, + end: commentGroup[commentGroup.length - 1].loc.end }, - messageId: "expectedLines", + messageId: "expectedBlock", fix(fixer) { - return fixer.replaceText(block, convertToSeparateLines(block, commentLines.filter(line => line))); + return fixer.replaceTextRange( + [firstComment.range[0], commentGroup[commentGroup.length - 1].range[1]], + convertToBlock(firstComment, commentLines) + ); } }); } - }, - "bare-block"(commentGroup) { - if (!isJSDoc(commentGroup)) { - const commentLines = getCommentLines(commentGroup); - - // disallows consecutive line comments in favor of using a block comment. - if (commentGroup[0].type === "Line" && commentLines.length > 1 && - !commentLines.some(value => value.includes("*/"))) { - context.report({ - loc: { - start: commentGroup[0].loc.start, - end: commentGroup[commentGroup.length - 1].loc.end - }, - messageId: "expectedBlock", - fix(fixer) { - const range = [commentGroup[0].range[0], commentGroup[commentGroup.length - 1].range[1]]; - const block = convertToBlock(commentGroup[0], commentLines.filter(line => line)); - - return fixer.replaceTextRange(range, block); - } - }); - } - // prohibits block comments from having a * at the beginning of each line. - if (commentGroup[0].type === "Block") { - const block = commentGroup[0]; - const lines = block.value.split(astUtils.LINEBREAK_MATCHER).filter(line => line.trim()); - - if (lines.length > 0 && lines.every(line => /^\s*\*/u.test(line))) { - context.report({ - loc: { - start: block.loc.start, - end: { line: block.loc.start.line, column: block.loc.start.column + 2 } - }, - messageId: "expectedBareBlock", - fix(fixer) { - return fixer.replaceText(block, convertToBlock(block, commentLines.filter(line => line))); - } - }); + // Prohibits block comments from having a * at the beginning of each line. + if (isStarredBlockComment(commentGroup)) { + context.report({ + loc: { + start: firstComment.loc.start, + end: { line: firstComment.loc.start.line, column: firstComment.loc.start.column + 2 } + }, + messageId: "expectedBareBlock", + fix(fixer) { + return fixer.replaceText(firstComment, convertToBlock(firstComment, commentLines)); } - } + }); } } }; diff --git a/tools/node_modules/eslint/lib/rules/no-cond-assign.js b/tools/node_modules/eslint/lib/rules/no-cond-assign.js index 67dcd28b2087b5..3843a7ac2e3e91 100644 --- a/tools/node_modules/eslint/lib/rules/no-cond-assign.js +++ b/tools/node_modules/eslint/lib/rules/no-cond-assign.js @@ -2,10 +2,21 @@ * @fileoverview Rule to flag assignment in a conditional statement's test expression * @author Stephen Murray */ + "use strict"; +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + const astUtils = require("./utils/ast-utils"); +//------------------------------------------------------------------------------ +// Helpers +//------------------------------------------------------------------------------ + +const TEST_CONDITION_PARENT_TYPES = new Set(["IfStatement", "WhileStatement", "DoWhileStatement", "ForStatement", "ConditionalExpression"]); + const NODE_DESCRIPTIONS = { DoWhileStatement: "a 'do...while' statement", ForStatement: "a 'for' statement", @@ -55,7 +66,7 @@ module.exports = { */ function isConditionalTestExpression(node) { return node.parent && - node.parent.test && + TEST_CONDITION_PARENT_TYPES.has(node.parent.type) && node === node.parent.test; } @@ -105,8 +116,7 @@ module.exports = { ) { context.report({ - node, - loc: node.test.loc.start, + node: node.test, messageId: "missing" }); } @@ -122,7 +132,7 @@ module.exports = { if (ancestor) { context.report({ - node: ancestor, + node, messageId: "unexpected", data: { type: NODE_DESCRIPTIONS[ancestor.type] || ancestor.type diff --git a/tools/node_modules/eslint/lib/rules/no-constructor-return.js b/tools/node_modules/eslint/lib/rules/no-constructor-return.js new file mode 100644 index 00000000000000..4757770b7cc8e6 --- /dev/null +++ b/tools/node_modules/eslint/lib/rules/no-constructor-return.js @@ -0,0 +1,62 @@ +/** + * @fileoverview Rule to disallow returning value from constructor. + * @author Pig Fang + */ + +"use strict"; + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +module.exports = { + meta: { + type: "problem", + + docs: { + description: "disallow returning value from constructor", + category: "Best Practices", + recommended: false, + url: "https://eslint.org/docs/rules/no-constructor-return" + }, + + schema: {}, + + fixable: null, + + messages: { + unexpected: "Unexpected return statement in constructor." + } + }, + + create(context) { + const stack = []; + + return { + onCodePathStart(_, node) { + stack.push(node); + }, + onCodePathEnd() { + stack.pop(); + }, + ReturnStatement(node) { + const last = stack[stack.length - 1]; + + if (!last.parent) { + return; + } + + if ( + last.parent.type === "MethodDefinition" && + last.parent.kind === "constructor" && + (node.parent.parent === last || node.argument) + ) { + context.report({ + node, + messageId: "unexpected" + }); + } + } + }; + } +}; diff --git a/tools/node_modules/eslint/lib/rules/no-dupe-else-if.js b/tools/node_modules/eslint/lib/rules/no-dupe-else-if.js new file mode 100644 index 00000000000000..a165e16607d2f0 --- /dev/null +++ b/tools/node_modules/eslint/lib/rules/no-dupe-else-if.js @@ -0,0 +1,122 @@ +/** + * @fileoverview Rule to disallow duplicate conditions in if-else-if chains + * @author Milos Djermanovic + */ + +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const astUtils = require("./utils/ast-utils"); + +//------------------------------------------------------------------------------ +// Helpers +//------------------------------------------------------------------------------ + +/** + * Determines whether the first given array is a subset of the second given array. + * @param {Function} comparator A function to compare two elements, should return `true` if they are equal. + * @param {Array} arrA The array to compare from. + * @param {Array} arrB The array to compare against. + * @returns {boolean} `true` if the array `arrA` is a subset of the array `arrB`. + */ +function isSubsetByComparator(comparator, arrA, arrB) { + return arrA.every(a => arrB.some(b => comparator(a, b))); +} + +/** + * Splits the given node by the given logical operator. + * @param {string} operator Logical operator `||` or `&&`. + * @param {ASTNode} node The node to split. + * @returns {ASTNode[]} Array of conditions that makes the node when joined by the operator. + */ +function splitByLogicalOperator(operator, node) { + if (node.type === "LogicalExpression" && node.operator === operator) { + return [...splitByLogicalOperator(operator, node.left), ...splitByLogicalOperator(operator, node.right)]; + } + return [node]; +} + +const splitByOr = splitByLogicalOperator.bind(null, "||"); +const splitByAnd = splitByLogicalOperator.bind(null, "&&"); + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +module.exports = { + meta: { + type: "problem", + + docs: { + description: "disallow duplicate conditions in if-else-if chains", + category: "Possible Errors", + recommended: false, + url: "https://eslint.org/docs/rules/no-dupe-else-if" + }, + + schema: [], + + messages: { + unexpected: "This branch can never execute. Its condition is a duplicate or covered by previous conditions in the if-else-if chain." + } + }, + + create(context) { + const sourceCode = context.getSourceCode(); + + /** + * Determines whether the two given nodes are considered to be equal. In particular, given that the nodes + * represent expressions in a boolean context, `||` and `&&` can be considered as commutative operators. + * @param {ASTNode} a First node. + * @param {ASTNode} b Second node. + * @returns {boolean} `true` if the nodes are considered to be equal. + */ + function equal(a, b) { + if (a.type !== b.type) { + return false; + } + + if ( + a.type === "LogicalExpression" && + (a.operator === "||" || a.operator === "&&") && + a.operator === b.operator + ) { + return equal(a.left, b.left) && equal(a.right, b.right) || + equal(a.left, b.right) && equal(a.right, b.left); + } + + return astUtils.equalTokens(a, b, sourceCode); + } + + const isSubset = isSubsetByComparator.bind(null, equal); + + return { + IfStatement(node) { + const test = node.test, + conditionsToCheck = test.type === "LogicalExpression" && test.operator === "&&" + ? [test, ...splitByAnd(test)] + : [test]; + let current = node, + listToCheck = conditionsToCheck.map(c => splitByOr(c).map(splitByAnd)); + + while (current.parent && current.parent.type === "IfStatement" && current.parent.alternate === current) { + current = current.parent; + + const currentOrOperands = splitByOr(current.test).map(splitByAnd); + + listToCheck = listToCheck.map(orOperands => orOperands.filter( + orOperand => !currentOrOperands.some(currentOrOperand => isSubset(currentOrOperand, orOperand)) + )); + + if (listToCheck.some(orOperands => orOperands.length === 0)) { + context.report({ node: test, messageId: "unexpected" }); + break; + } + } + } + }; + } +}; diff --git a/tools/node_modules/eslint/lib/rules/no-implicit-globals.js b/tools/node_modules/eslint/lib/rules/no-implicit-globals.js index 2eea2b28463250..d4bfa3af82fc3b 100644 --- a/tools/node_modules/eslint/lib/rules/no-implicit-globals.js +++ b/tools/node_modules/eslint/lib/rules/no-implicit-globals.js @@ -1,5 +1,5 @@ /** - * @fileoverview Rule to check for implicit global variables and functions. + * @fileoverview Rule to check for implicit global variables, functions and classes. * @author Joshua Peek */ @@ -14,41 +14,123 @@ module.exports = { type: "suggestion", docs: { - description: "disallow variable and `function` declarations in the global scope", + description: "disallow declarations in the global scope", category: "Best Practices", recommended: false, url: "https://eslint.org/docs/rules/no-implicit-globals" }, - schema: [] + schema: [{ + type: "object", + properties: { + lexicalBindings: { + type: "boolean", + default: false + } + }, + additionalProperties: false + }], + + messages: { + globalNonLexicalBinding: "Unexpected {{kind}} declaration in the global scope, wrap in an IIFE for a local variable, assign as global property for a global variable.", + globalLexicalBinding: "Unexpected {{kind}} declaration in the global scope, wrap in a block or in an IIFE.", + globalVariableLeak: "Global variable leak, declare the variable if it is intended to be local.", + assignmentToReadonlyGlobal: "Unexpected assignment to read-only global variable.", + redeclarationOfReadonlyGlobal: "Unexpected redeclaration of read-only global variable." + } }, create(context) { + + const checkLexicalBindings = context.options[0] && context.options[0].lexicalBindings === true; + + /** + * Reports the node. + * @param {ASTNode} node Node to report. + * @param {string} messageId Id of the message to report. + * @param {string|undefined} kind Declaration kind, can be 'var', 'const', 'let', function or class. + * @returns {void} + */ + function report(node, messageId, kind) { + context.report({ + node, + messageId, + data: { + kind + } + }); + } + return { Program() { const scope = context.getScope(); scope.variables.forEach(variable => { - if (variable.writeable) { + + // Only ESLint global variables have the `writable` key. + const isReadonlyEslintGlobalVariable = variable.writeable === false; + const isWritableEslintGlobalVariable = variable.writeable === true; + + if (isWritableEslintGlobalVariable) { + + // Everything is allowed with writable ESLint global variables. return; } variable.defs.forEach(def => { + const defNode = def.node; + if (def.type === "FunctionName" || (def.type === "Variable" && def.parent.kind === "var")) { - context.report({ node: def.node, message: "Implicit global variable, assign as global property instead." }); + if (isReadonlyEslintGlobalVariable) { + report(defNode, "redeclarationOfReadonlyGlobal"); + } else { + report( + defNode, + "globalNonLexicalBinding", + def.type === "FunctionName" ? "function" : `'${def.parent.kind}'` + ); + } + } + + if (checkLexicalBindings) { + if (def.type === "ClassName" || + (def.type === "Variable" && (def.parent.kind === "let" || def.parent.kind === "const"))) { + if (isReadonlyEslintGlobalVariable) { + report(defNode, "redeclarationOfReadonlyGlobal"); + } else { + report( + defNode, + "globalLexicalBinding", + def.type === "ClassName" ? "class" : `'${def.parent.kind}'` + ); + } + } } }); }); + // Undeclared assigned variables. scope.implicit.variables.forEach(variable => { const scopeVariable = scope.set.get(variable.name); + let messageId; - if (scopeVariable && scopeVariable.writeable) { - return; + if (scopeVariable) { + + // ESLint global variable + if (scopeVariable.writeable) { + return; + } + messageId = "assignmentToReadonlyGlobal"; + + } else { + + // Reference to an unknown variable, possible global leak. + messageId = "globalVariableLeak"; } + // def.node is an AssignmentExpression, ForInStatement or ForOfStatement. variable.defs.forEach(def => { - context.report({ node: def.node, message: "Implicit global variable, assign as global property instead." }); + report(def.node, messageId); }); }); } diff --git a/tools/node_modules/eslint/lib/rules/no-inline-comments.js b/tools/node_modules/eslint/lib/rules/no-inline-comments.js index b35db114100a34..bd226ecc35fde1 100644 --- a/tools/node_modules/eslint/lib/rules/no-inline-comments.js +++ b/tools/node_modules/eslint/lib/rules/no-inline-comments.js @@ -35,22 +35,36 @@ module.exports = { */ function testCodeAroundComment(node) { - // Get the whole line and cut it off at the start of the comment - const startLine = String(sourceCode.lines[node.loc.start.line - 1]); - const endLine = String(sourceCode.lines[node.loc.end.line - 1]); + const startLine = String(sourceCode.lines[node.loc.start.line - 1]), + endLine = String(sourceCode.lines[node.loc.end.line - 1]), + preamble = startLine.slice(0, node.loc.start.column).trim(), + postamble = endLine.slice(node.loc.end.column).trim(), + isPreambleEmpty = !preamble, + isPostambleEmpty = !postamble; - const preamble = startLine.slice(0, node.loc.start.column).trim(); + // Nothing on both sides + if (isPreambleEmpty && isPostambleEmpty) { + return; + } - // Also check after the comment - const postamble = endLine.slice(node.loc.end.column).trim(); + // JSX Exception + if ( + (isPreambleEmpty || preamble === "{") && + (isPostambleEmpty || postamble === "}") + ) { + const enclosingNode = sourceCode.getNodeByRangeIndex(node.range[0]); - // Check that this comment isn't an ESLint directive - const isDirective = astUtils.isDirectiveComment(node); + if (enclosingNode && enclosingNode.type === "JSXEmptyExpression") { + return; + } + } - // Should be empty if there was only whitespace around the comment - if (!isDirective && (preamble || postamble)) { - context.report({ node, message: "Unexpected comment inline with code." }); + // Don't report ESLint directive comments + if (astUtils.isDirectiveComment(node)) { + return; } + + context.report({ node, message: "Unexpected comment inline with code." }); } //-------------------------------------------------------------------------- diff --git a/tools/node_modules/eslint/lib/rules/no-invalid-this.js b/tools/node_modules/eslint/lib/rules/no-invalid-this.js index b1dddd9319c745..5398fc3b5f842c 100644 --- a/tools/node_modules/eslint/lib/rules/no-invalid-this.js +++ b/tools/node_modules/eslint/lib/rules/no-invalid-this.js @@ -26,10 +26,23 @@ module.exports = { url: "https://eslint.org/docs/rules/no-invalid-this" }, - schema: [] + schema: [ + { + type: "object", + properties: { + capIsConstructor: { + type: "boolean", + default: true + } + }, + additionalProperties: false + } + ] }, create(context) { + const options = context.options[0] || {}; + const capIsConstructor = options.capIsConstructor !== false; const stack = [], sourceCode = context.getSourceCode(); @@ -48,7 +61,8 @@ module.exports = { current.init = true; current.valid = !astUtils.isDefaultThisBinding( current.node, - sourceCode + sourceCode, + { capIsConstructor } ); } return current; diff --git a/tools/node_modules/eslint/lib/rules/no-octal-escape.js b/tools/node_modules/eslint/lib/rules/no-octal-escape.js index 7f6845ec70f388..5b4c7b2ebb45c8 100644 --- a/tools/node_modules/eslint/lib/rules/no-octal-escape.js +++ b/tools/node_modules/eslint/lib/rules/no-octal-escape.js @@ -38,7 +38,7 @@ module.exports = { // \0 represents a valid NULL character if it isn't followed by a digit. const match = node.raw.match( - /^(?:[^\\]|\\.)*?\\([0-3][0-7]{1,2}|[4-7][0-7]|[1-7])/u + /^(?:[^\\]|\\.)*?\\([0-3][0-7]{1,2}|[4-7][0-7]|0(?=[89])|[1-7])/su ); if (match) { diff --git a/tools/node_modules/eslint/lib/rules/no-setter-return.js b/tools/node_modules/eslint/lib/rules/no-setter-return.js new file mode 100644 index 00000000000000..e0948696c347f5 --- /dev/null +++ b/tools/node_modules/eslint/lib/rules/no-setter-return.js @@ -0,0 +1,227 @@ +/** + * @fileoverview Rule to disallow returning values from setters + * @author Milos Djermanovic + */ + +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const astUtils = require("./utils/ast-utils"); +const { findVariable } = require("eslint-utils"); + +//------------------------------------------------------------------------------ +// Helpers +//------------------------------------------------------------------------------ + +/** + * Determines whether the given identifier node is a reference to a global variable. + * @param {ASTNode} node `Identifier` node to check. + * @param {Scope} scope Scope to which the node belongs. + * @returns {boolean} True if the identifier is a reference to a global variable. + */ +function isGlobalReference(node, scope) { + const variable = findVariable(scope, node); + + return variable !== null && variable.scope.type === "global" && variable.defs.length === 0; +} + +/** + * Determines whether the given node is an argument of the specified global method call, at the given `index` position. + * E.g., for given `index === 1`, this function checks for `objectName.methodName(foo, node)`, where objectName is a global variable. + * @param {ASTNode} node The node to check. + * @param {Scope} scope Scope to which the node belongs. + * @param {string} objectName Name of the global object. + * @param {string} methodName Name of the method. + * @param {number} index The given position. + * @returns {boolean} `true` if the node is argument at the given position. + */ +function isArgumentOfGlobalMethodCall(node, scope, objectName, methodName, index) { + const parent = node.parent; + + return parent.type === "CallExpression" && + parent.arguments[index] === node && + parent.callee.type === "MemberExpression" && + astUtils.getStaticPropertyName(parent.callee) === methodName && + parent.callee.object.type === "Identifier" && + parent.callee.object.name === objectName && + isGlobalReference(parent.callee.object, scope); +} + +/** + * Determines whether the given node is used as a property descriptor. + * @param {ASTNode} node The node to check. + * @param {Scope} scope Scope to which the node belongs. + * @returns {boolean} `true` if the node is a property descriptor. + */ +function isPropertyDescriptor(node, scope) { + if ( + isArgumentOfGlobalMethodCall(node, scope, "Object", "defineProperty", 2) || + isArgumentOfGlobalMethodCall(node, scope, "Reflect", "defineProperty", 2) + ) { + return true; + } + + const parent = node.parent; + + if ( + parent.type === "Property" && + parent.value === node + ) { + const grandparent = parent.parent; + + if ( + grandparent.type === "ObjectExpression" && + ( + isArgumentOfGlobalMethodCall(grandparent, scope, "Object", "create", 1) || + isArgumentOfGlobalMethodCall(grandparent, scope, "Object", "defineProperties", 1) + ) + ) { + return true; + } + } + + return false; +} + +/** + * Determines whether the given function node is used as a setter function. + * @param {ASTNode} node The node to check. + * @param {Scope} scope Scope to which the node belongs. + * @returns {boolean} `true` if the node is a setter. + */ +function isSetter(node, scope) { + const parent = node.parent; + + if ( + parent.kind === "set" && + parent.value === node + ) { + + // Setter in an object literal or in a class + return true; + } + + if ( + parent.type === "Property" && + parent.value === node && + astUtils.getStaticPropertyName(parent) === "set" && + parent.parent.type === "ObjectExpression" && + isPropertyDescriptor(parent.parent, scope) + ) { + + // Setter in a property descriptor + return true; + } + + return false; +} + +/** + * Finds function's outer scope. + * @param {Scope} scope Function's own scope. + * @returns {Scope} Function's outer scope. + */ +function getOuterScope(scope) { + const upper = scope.upper; + + if (upper.type === "function-expression-name") { + return upper.upper; + } + + return upper; +} + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +module.exports = { + meta: { + type: "problem", + + docs: { + description: "disallow returning values from setters", + category: "Possible Errors", + recommended: false, + url: "https://eslint.org/docs/rules/no-setter-return" + }, + + schema: [], + + messages: { + returnsValue: "Setter cannot return a value." + } + }, + + create(context) { + let funcInfo = null; + + /** + * Creates and pushes to the stack a function info object for the given function node. + * @param {ASTNode} node The function node. + * @returns {void} + */ + function enterFunction(node) { + const outerScope = getOuterScope(context.getScope()); + + funcInfo = { + upper: funcInfo, + isSetter: isSetter(node, outerScope) + }; + } + + /** + * Pops the current function info object from the stack. + * @returns {void} + */ + function exitFunction() { + funcInfo = funcInfo.upper; + } + + /** + * Reports the given node. + * @param {ASTNode} node Node to report. + * @returns {void} + */ + function report(node) { + context.report({ node, messageId: "returnsValue" }); + } + + return { + + /* + * Function declarations cannot be setters, but we still have to track them in the `funcInfo` stack to avoid + * false positives, because a ReturnStatement node can belong to a function declaration inside a setter. + * + * Note: A previously declared function can be referenced and actually used as a setter in a property descriptor, + * but that's out of scope for this rule. + */ + FunctionDeclaration: enterFunction, + FunctionExpression: enterFunction, + ArrowFunctionExpression(node) { + enterFunction(node); + + if (funcInfo.isSetter && node.expression) { + + // { set: foo => bar } property descriptor. Report implicit return 'bar' as the equivalent for a return statement. + report(node.body); + } + }, + + "FunctionDeclaration:exit": exitFunction, + "FunctionExpression:exit": exitFunction, + "ArrowFunctionExpression:exit": exitFunction, + + ReturnStatement(node) { + + // Global returns (e.g., at the top level of a Node module) don't have `funcInfo`. + if (funcInfo && funcInfo.isSetter && node.argument) { + report(node); + } + } + }; + } +}; diff --git a/tools/node_modules/eslint/lib/rules/no-underscore-dangle.js b/tools/node_modules/eslint/lib/rules/no-underscore-dangle.js index 3f59815b575f84..e910e2739a740a 100644 --- a/tools/node_modules/eslint/lib/rules/no-underscore-dangle.js +++ b/tools/node_modules/eslint/lib/rules/no-underscore-dangle.js @@ -38,6 +38,10 @@ module.exports = { type: "boolean", default: false }, + allowAfterThisConstructor: { + type: "boolean", + default: false + }, enforceInMethodNames: { type: "boolean", default: false @@ -54,6 +58,7 @@ module.exports = { const ALLOWED_VARIABLES = options.allow ? options.allow : []; const allowAfterThis = typeof options.allowAfterThis !== "undefined" ? options.allowAfterThis : false; const allowAfterSuper = typeof options.allowAfterSuper !== "undefined" ? options.allowAfterSuper : false; + const allowAfterThisConstructor = typeof options.allowAfterThisConstructor !== "undefined" ? options.allowAfterThisConstructor : false; const enforceInMethodNames = typeof options.enforceInMethodNames !== "undefined" ? options.enforceInMethodNames : false; //------------------------------------------------------------------------- @@ -72,7 +77,7 @@ module.exports = { /** * Check if identifier has a underscore at the end - * @param {ASTNode} identifier node to evaluate + * @param {string} identifier name of the node * @returns {boolean} true if its is present * @private */ @@ -84,7 +89,7 @@ module.exports = { /** * Check if identifier is a special case member expression - * @param {ASTNode} identifier node to evaluate + * @param {string} identifier name of the node * @returns {boolean} true if its is a special case * @private */ @@ -94,7 +99,7 @@ module.exports = { /** * Check if identifier is a special case variable expression - * @param {ASTNode} identifier node to evaluate + * @param {string} identifier name of the node * @returns {boolean} true if its is a special case * @private */ @@ -104,6 +109,18 @@ module.exports = { return identifier === "_"; } + /** + * Check if a node is a member reference of this.constructor + * @param {ASTNode} node node to evaluate + * @returns {boolean} true if it is a reference on this.constructor + * @private + */ + function isThisConstructorReference(node) { + return node.object.type === "MemberExpression" && + node.object.property.name === "constructor" && + node.object.object.type === "ThisExpression"; + } + /** * Check if function has a underscore at the end * @param {ASTNode} node node to evaluate @@ -156,11 +173,13 @@ module.exports = { function checkForTrailingUnderscoreInMemberExpression(node) { const identifier = node.property.name, isMemberOfThis = node.object.type === "ThisExpression", - isMemberOfSuper = node.object.type === "Super"; + isMemberOfSuper = node.object.type === "Super", + isMemberOfThisConstructor = isThisConstructorReference(node); if (typeof identifier !== "undefined" && hasTrailingUnderscore(identifier) && !(isMemberOfThis && allowAfterThis) && !(isMemberOfSuper && allowAfterSuper) && + !(isMemberOfThisConstructor && allowAfterThisConstructor) && !isSpecialCaseIdentifierForMemberExpression(identifier) && !isAllowed(identifier)) { context.report({ node, diff --git a/tools/node_modules/eslint/lib/rules/no-useless-computed-key.js b/tools/node_modules/eslint/lib/rules/no-useless-computed-key.js index 95527832b2d7f0..b5e53174e42be7 100644 --- a/tools/node_modules/eslint/lib/rules/no-useless-computed-key.js +++ b/tools/node_modules/eslint/lib/rules/no-useless-computed-key.js @@ -8,6 +8,7 @@ // Requirements //------------------------------------------------------------------------------ +const lodash = require("lodash"); const astUtils = require("./utils/ast-utils"); //------------------------------------------------------------------------------ @@ -21,57 +22,83 @@ module.exports = { type: "suggestion", docs: { - description: "disallow unnecessary computed property keys in object literals", + description: "disallow unnecessary computed property keys in objects and classes", category: "ECMAScript 6", recommended: false, url: "https://eslint.org/docs/rules/no-useless-computed-key" }, - schema: [], + schema: [{ + type: "object", + properties: { + enforceForClassMembers: { + type: "boolean", + default: false + } + }, + additionalProperties: false + }], fixable: "code" }, create(context) { const sourceCode = context.getSourceCode(); + const enforceForClassMembers = context.options[0] && context.options[0].enforceForClassMembers; + + /** + * Reports a given node if it violated this rule. + * @param {ASTNode} node The node to check. + * @returns {void} + */ + function check(node) { + if (!node.computed) { + return; + } - return { - Property(node) { - if (!node.computed) { - return; - } - - const key = node.key, - nodeType = typeof key.value; + const key = node.key, + nodeType = typeof key.value; - if (key.type === "Literal" && (nodeType === "string" || nodeType === "number") && key.value !== "__proto__") { - context.report({ - node, - message: MESSAGE_UNNECESSARY_COMPUTED, - data: { property: sourceCode.getText(key) }, - fix(fixer) { - const leftSquareBracket = sourceCode.getFirstToken(node, astUtils.isOpeningBracketToken); - const rightSquareBracket = sourceCode.getFirstTokenBetween(node.key, node.value, astUtils.isClosingBracketToken); - const tokensBetween = sourceCode.getTokensBetween(leftSquareBracket, rightSquareBracket, 1); + let allowedKey; - if (tokensBetween.slice(0, -1).some((token, index) => - sourceCode.getText().slice(token.range[1], tokensBetween[index + 1].range[0]).trim())) { + if (node.type === "MethodDefinition") { + allowedKey = node.static ? "prototype" : "constructor"; + } else { + allowedKey = "__proto__"; + } - // If there are comments between the brackets and the property name, don't do a fix. - return null; - } + if (key.type === "Literal" && (nodeType === "string" || nodeType === "number") && key.value !== allowedKey) { + context.report({ + node, + message: MESSAGE_UNNECESSARY_COMPUTED, + data: { property: sourceCode.getText(key) }, + fix(fixer) { + const leftSquareBracket = sourceCode.getFirstToken(node, astUtils.isOpeningBracketToken); + const rightSquareBracket = sourceCode.getFirstTokenBetween(node.key, node.value, astUtils.isClosingBracketToken); + const tokensBetween = sourceCode.getTokensBetween(leftSquareBracket, rightSquareBracket, 1); + + if (tokensBetween.slice(0, -1).some((token, index) => + sourceCode.getText().slice(token.range[1], tokensBetween[index + 1].range[0]).trim())) { + + // If there are comments between the brackets and the property name, don't do a fix. + return null; + } - const tokenBeforeLeftBracket = sourceCode.getTokenBefore(leftSquareBracket); + const tokenBeforeLeftBracket = sourceCode.getTokenBefore(leftSquareBracket); - // Insert a space before the key to avoid changing identifiers, e.g. ({ get[2]() {} }) to ({ get2() {} }) - const needsSpaceBeforeKey = tokenBeforeLeftBracket.range[1] === leftSquareBracket.range[0] && - !astUtils.canTokensBeAdjacent(tokenBeforeLeftBracket, sourceCode.getFirstToken(key)); + // Insert a space before the key to avoid changing identifiers, e.g. ({ get[2]() {} }) to ({ get2() {} }) + const needsSpaceBeforeKey = tokenBeforeLeftBracket.range[1] === leftSquareBracket.range[0] && + !astUtils.canTokensBeAdjacent(tokenBeforeLeftBracket, sourceCode.getFirstToken(key)); - const replacementKey = (needsSpaceBeforeKey ? " " : "") + key.raw; + const replacementKey = (needsSpaceBeforeKey ? " " : "") + key.raw; - return fixer.replaceTextRange([leftSquareBracket.range[0], rightSquareBracket.range[1]], replacementKey); - } - }); - } + return fixer.replaceTextRange([leftSquareBracket.range[0], rightSquareBracket.range[1]], replacementKey); + } + }); } + } + + return { + Property: check, + MethodDefinition: enforceForClassMembers ? check : lodash.noop }; } }; diff --git a/tools/node_modules/eslint/lib/rules/no-useless-escape.js b/tools/node_modules/eslint/lib/rules/no-useless-escape.js index ebfe4cba38a9a7..8057e44ddab463 100644 --- a/tools/node_modules/eslint/lib/rules/no-useless-escape.js +++ b/tools/node_modules/eslint/lib/rules/no-useless-escape.js @@ -85,7 +85,14 @@ module.exports = { description: "disallow unnecessary escape characters", category: "Best Practices", recommended: true, - url: "https://eslint.org/docs/rules/no-useless-escape" + url: "https://eslint.org/docs/rules/no-useless-escape", + suggestion: true + }, + + messages: { + unnecessaryEscape: "Unnecessary escape character: \\{{character}}.", + removeEscape: "Remove the `\\`. This maintains the current functionality.", + escapeBackslash: "Replace the `\\` with `\\\\` to include the actual backslash character." }, schema: [] @@ -103,6 +110,8 @@ module.exports = { */ function report(node, startOffset, character) { const start = sourceCode.getLocFromIndex(sourceCode.getIndexFromLoc(node.loc.start) + startOffset); + const rangeStart = sourceCode.getIndexFromLoc(node.loc.start) + startOffset; + const range = [rangeStart, rangeStart + 1]; context.report({ node, @@ -110,8 +119,22 @@ module.exports = { start, end: { line: start.line, column: start.column + 1 } }, - message: "Unnecessary escape character: \\{{character}}.", - data: { character } + messageId: "unnecessaryEscape", + data: { character }, + suggest: [ + { + messageId: "removeEscape", + fix(fixer) { + return fixer.removeRange(range); + } + }, + { + messageId: "escapeBackslash", + fix(fixer) { + return fixer.insertTextBeforeRange(range, "\\"); + } + } + ] }); } diff --git a/tools/node_modules/eslint/lib/rules/object-curly-spacing.js b/tools/node_modules/eslint/lib/rules/object-curly-spacing.js index 53ddaaa24ad01e..117a7a35426103 100644 --- a/tools/node_modules/eslint/lib/rules/object-curly-spacing.js +++ b/tools/node_modules/eslint/lib/rules/object-curly-spacing.js @@ -74,16 +74,16 @@ module.exports = { * @returns {void} */ function reportNoBeginningSpace(node, token) { + const nextToken = context.getSourceCode().getTokenAfter(token, { includeComments: true }); + context.report({ node, - loc: token.loc.start, + loc: { start: token.loc.end, end: nextToken.loc.start }, message: "There should be no space after '{{token}}'.", data: { token: token.value }, fix(fixer) { - const nextToken = context.getSourceCode().getTokenAfter(token, { includeComments: true }); - return fixer.removeRange([token.range[1], nextToken.range[0]]); } }); @@ -96,16 +96,16 @@ module.exports = { * @returns {void} */ function reportNoEndingSpace(node, token) { + const previousToken = context.getSourceCode().getTokenBefore(token, { includeComments: true }); + context.report({ node, - loc: token.loc.start, + loc: { start: previousToken.loc.end, end: token.loc.start }, message: "There should be no space before '{{token}}'.", data: { token: token.value }, fix(fixer) { - const previousToken = context.getSourceCode().getTokenBefore(token, { includeComments: true }); - return fixer.removeRange([previousToken.range[1], token.range[0]]); } }); @@ -120,7 +120,7 @@ module.exports = { function reportRequiredBeginningSpace(node, token) { context.report({ node, - loc: token.loc.start, + loc: token.loc, message: "A space is required after '{{token}}'.", data: { token: token.value @@ -140,7 +140,7 @@ module.exports = { function reportRequiredEndingSpace(node, token) { context.report({ node, - loc: token.loc.start, + loc: token.loc, message: "A space is required before '{{token}}'.", data: { token: token.value diff --git a/tools/node_modules/eslint/lib/rules/operator-assignment.js b/tools/node_modules/eslint/lib/rules/operator-assignment.js index e294668b42b1ce..b19ba0d02e1a1b 100644 --- a/tools/node_modules/eslint/lib/rules/operator-assignment.js +++ b/tools/node_modules/eslint/lib/rules/operator-assignment.js @@ -71,6 +71,9 @@ function same(a, b) { */ return same(a.object, b.object) && same(a.property, b.property); + case "ThisExpression": + return true; + default: return false; } @@ -83,8 +86,14 @@ function same(a, b) { * @returns {boolean} `true` if the node can be fixed */ function canBeFixed(node) { - return node.type === "Identifier" || - node.type === "MemberExpression" && node.object.type === "Identifier" && (!node.computed || node.property.type === "Literal"); + return ( + node.type === "Identifier" || + ( + node.type === "MemberExpression" && + (node.object.type === "Identifier" || node.object.type === "ThisExpression") && + (!node.computed || node.property.type === "Literal") + ) + ); } module.exports = { diff --git a/tools/node_modules/eslint/lib/rules/prefer-const.js b/tools/node_modules/eslint/lib/rules/prefer-const.js index 87f83892126d50..439a4db3c963f6 100644 --- a/tools/node_modules/eslint/lib/rules/prefer-const.js +++ b/tools/node_modules/eslint/lib/rules/prefer-const.js @@ -356,7 +356,9 @@ module.exports = { const ignoreReadBeforeAssign = options.ignoreReadBeforeAssign === true; const variables = []; let reportCount = 0; - let name = ""; + let checkedId = null; + let checkedName = ""; + /** * Reports given identifier nodes if all of the nodes should be declared @@ -387,25 +389,30 @@ module.exports = { /* * First we check the declaration type and then depending on * if the type is a "VariableDeclarator" or its an "ObjectPattern" - * we compare the name from the first identifier, if the names are different - * we assign the new name and reset the count of reportCount and nodeCount in + * we compare the name and id from the first identifier, if the names are different + * we assign the new name, id and reset the count of reportCount and nodeCount in * order to check each block for the number of reported errors and base our fix * based on comparing nodes.length and nodesToReport.length. */ if (firstDecParent.type === "VariableDeclarator") { - if (firstDecParent.id.name !== name) { - name = firstDecParent.id.name; + if (firstDecParent.id.name !== checkedName) { + checkedName = firstDecParent.id.name; reportCount = 0; } if (firstDecParent.id.type === "ObjectPattern") { - if (firstDecParent.init.name !== name) { - name = firstDecParent.init.name; + if (firstDecParent.init.name !== checkedName) { + checkedName = firstDecParent.init.name; reportCount = 0; } } + + if (firstDecParent.id !== checkedId) { + checkedId = firstDecParent.id; + reportCount = 0; + } } } } diff --git a/tools/node_modules/eslint/lib/rules/prefer-exponentiation-operator.js b/tools/node_modules/eslint/lib/rules/prefer-exponentiation-operator.js new file mode 100644 index 00000000000000..5e75ef4724f3b7 --- /dev/null +++ b/tools/node_modules/eslint/lib/rules/prefer-exponentiation-operator.js @@ -0,0 +1,189 @@ +/** + * @fileoverview Rule to disallow Math.pow in favor of the ** operator + * @author Milos Djermanovic + */ + +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const astUtils = require("./utils/ast-utils"); +const { CALL, ReferenceTracker } = require("eslint-utils"); + +//------------------------------------------------------------------------------ +// Helpers +//------------------------------------------------------------------------------ + +const PRECEDENCE_OF_EXPONENTIATION_EXPR = astUtils.getPrecedence({ type: "BinaryExpression", operator: "**" }); + +/** + * Determines whether the given node needs parens if used as the base in an exponentiation binary expression. + * @param {ASTNode} base The node to check. + * @returns {boolean} `true` if the node needs to be parenthesised. + */ +function doesBaseNeedParens(base) { + return ( + + // '**' is right-associative, parens are needed when Math.pow(a ** b, c) is converted to (a ** b) ** c + astUtils.getPrecedence(base) <= PRECEDENCE_OF_EXPONENTIATION_EXPR || + + // An unary operator cannot be used immediately before an exponentiation expression + base.type === "UnaryExpression" + ); +} + +/** + * Determines whether the given node needs parens if used as the exponent in an exponentiation binary expression. + * @param {ASTNode} exponent The node to check. + * @returns {boolean} `true` if the node needs to be parenthesised. + */ +function doesExponentNeedParens(exponent) { + + // '**' is right-associative, there is no need for parens when Math.pow(a, b ** c) is converted to a ** b ** c + return astUtils.getPrecedence(exponent) < PRECEDENCE_OF_EXPONENTIATION_EXPR; +} + +/** + * Determines whether an exponentiation binary expression at the place of the given node would need parens. + * @param {ASTNode} node A node that would be replaced by an exponentiation binary expression. + * @param {SourceCode} sourceCode A SourceCode object. + * @returns {boolean} `true` if the expression needs to be parenthesised. + */ +function doesExponentiationExpressionNeedParens(node, sourceCode) { + const parent = node.parent; + + const needsParens = ( + parent.type === "ClassDeclaration" || + ( + parent.type.endsWith("Expression") && + astUtils.getPrecedence(parent) >= PRECEDENCE_OF_EXPONENTIATION_EXPR && + !(parent.type === "BinaryExpression" && parent.operator === "**" && parent.right === node) && + !((parent.type === "CallExpression" || parent.type === "NewExpression") && parent.arguments.includes(node)) && + !(parent.type === "MemberExpression" && parent.computed && parent.property === node) && + !(parent.type === "ArrayExpression") + ) + ); + + return needsParens && !astUtils.isParenthesised(sourceCode, node); +} + +/** + * Optionally parenthesizes given text. + * @param {string} text The text to parenthesize. + * @param {boolean} shouldParenthesize If `true`, the text will be parenthesised. + * @returns {string} parenthesised or unchanged text. + */ +function parenthesizeIfShould(text, shouldParenthesize) { + return shouldParenthesize ? `(${text})` : text; +} + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +module.exports = { + meta: { + type: "suggestion", + + docs: { + description: "disallow the use of `Math.pow` in favor of the `**` operator", + category: "Stylistic Issues", + recommended: false, + url: "https://eslint.org/docs/rules/prefer-exponentiation-operator" + }, + + schema: [], + fixable: "code", + + messages: { + useExponentiation: "Use the '**' operator instead of 'Math.pow'." + } + }, + + create(context) { + const sourceCode = context.getSourceCode(); + + /** + * Reports the given node. + * @param {ASTNode} node 'Math.pow()' node to report. + * @returns {void} + */ + function report(node) { + context.report({ + node, + messageId: "useExponentiation", + fix(fixer) { + if ( + node.arguments.length !== 2 || + node.arguments.some(arg => arg.type === "SpreadElement") || + sourceCode.getCommentsInside(node).length > 0 + ) { + return null; + } + + const base = node.arguments[0], + exponent = node.arguments[1], + baseText = sourceCode.getText(base), + exponentText = sourceCode.getText(exponent), + shouldParenthesizeBase = doesBaseNeedParens(base), + shouldParenthesizeExponent = doesExponentNeedParens(exponent), + shouldParenthesizeAll = doesExponentiationExpressionNeedParens(node, sourceCode); + + let prefix = "", + suffix = ""; + + if (!shouldParenthesizeAll) { + if (!shouldParenthesizeBase) { + const firstReplacementToken = sourceCode.getFirstToken(base), + tokenBefore = sourceCode.getTokenBefore(node); + + if ( + tokenBefore && + tokenBefore.range[1] === node.range[0] && + !astUtils.canTokensBeAdjacent(tokenBefore, firstReplacementToken) + ) { + prefix = " "; // a+Math.pow(++b, c) -> a+ ++b**c + } + } + if (!shouldParenthesizeExponent) { + const lastReplacementToken = sourceCode.getLastToken(exponent), + tokenAfter = sourceCode.getTokenAfter(node); + + if ( + tokenAfter && + node.range[1] === tokenAfter.range[0] && + !astUtils.canTokensBeAdjacent(lastReplacementToken, tokenAfter) + ) { + suffix = " "; // Math.pow(a, b)in c -> a**b in c + } + } + } + + const baseReplacement = parenthesizeIfShould(baseText, shouldParenthesizeBase), + exponentReplacement = parenthesizeIfShould(exponentText, shouldParenthesizeExponent), + replacement = parenthesizeIfShould(`${baseReplacement}**${exponentReplacement}`, shouldParenthesizeAll); + + return fixer.replaceText(node, `${prefix}${replacement}${suffix}`); + } + }); + } + + return { + Program() { + const scope = context.getScope(); + const tracker = new ReferenceTracker(scope); + const trackMap = { + Math: { + pow: { [CALL]: true } + } + }; + + for (const { node } of tracker.iterateGlobalReferences(trackMap)) { + report(node); + } + } + }; + } +}; diff --git a/tools/node_modules/eslint/lib/rules/require-await.js b/tools/node_modules/eslint/lib/rules/require-await.js index 0aa6fce7e19859..22c111b6dc852e 100644 --- a/tools/node_modules/eslint/lib/rules/require-await.js +++ b/tools/node_modules/eslint/lib/rules/require-await.js @@ -89,9 +89,17 @@ module.exports = { "ArrowFunctionExpression:exit": exitFunction, AwaitExpression() { + if (!scopeInfo) { + return; + } + scopeInfo.hasAwait = true; }, ForOfStatement(node) { + if (!scopeInfo) { + return; + } + if (node.await) { scopeInfo.hasAwait = true; } diff --git a/tools/node_modules/eslint/lib/rules/semi.js b/tools/node_modules/eslint/lib/rules/semi.js index a159e19d347dcf..22e299efe72e49 100644 --- a/tools/node_modules/eslint/lib/rules/semi.js +++ b/tools/node_modules/eslint/lib/rules/semi.js @@ -93,17 +93,20 @@ module.exports = { const lastToken = sourceCode.getLastToken(node); let message, fix, - loc = lastToken.loc; + loc; if (!missing) { message = "Missing semicolon."; - loc = loc.end; + loc = { + start: lastToken.loc.end, + end: astUtils.getNextLocation(sourceCode, lastToken.loc.end) + }; fix = function(fixer) { return fixer.insertTextAfter(lastToken, ";"); }; } else { message = "Extra semicolon."; - loc = loc.start; + loc = lastToken.loc; fix = function(fixer) { /* diff --git a/tools/node_modules/eslint/lib/rules/space-infix-ops.js b/tools/node_modules/eslint/lib/rules/space-infix-ops.js index b2fbe47b4765e6..bd2c0ae0e1d3b7 100644 --- a/tools/node_modules/eslint/lib/rules/space-infix-ops.js +++ b/tools/node_modules/eslint/lib/rules/space-infix-ops.js @@ -69,7 +69,7 @@ module.exports = { function report(mainNode, culpritToken) { context.report({ node: mainNode, - loc: culpritToken.loc.start, + loc: culpritToken.loc, message: "Operator '{{operator}}' must be spaced.", data: { operator: culpritToken.value diff --git a/tools/node_modules/eslint/lib/rules/spaced-comment.js b/tools/node_modules/eslint/lib/rules/spaced-comment.js index 958bb2c6444080..daf56cd6bb42d0 100644 --- a/tools/node_modules/eslint/lib/rules/spaced-comment.js +++ b/tools/node_modules/eslint/lib/rules/spaced-comment.js @@ -249,7 +249,8 @@ module.exports = { beginRegex: requireSpace ? createAlwaysStylePattern(markers, exceptions) : createNeverStylePattern(markers), endRegex: balanced && requireSpace ? new RegExp(`${createExceptionsPattern(exceptions)}$`, "u") : new RegExp(endNeverPattern, "u"), hasExceptions: exceptions.length > 0, - markers: new RegExp(`^(${markers.map(escape).join("|")})`, "u") + captureMarker: new RegExp(`^(${markers.map(escape).join("|")})`, "u"), + markers: new Set(markers) }; return rule; @@ -322,8 +323,8 @@ module.exports = { rule = styleRules[type], commentIdentifier = type === "block" ? "/*" : "//"; - // Ignores empty comments. - if (node.value.length === 0) { + // Ignores empty comments and comments that consist only of a marker. + if (node.value.length === 0 || rule.markers.has(node.value)) { return; } @@ -333,7 +334,7 @@ module.exports = { // Checks. if (requireSpace) { if (!beginMatch) { - const hasMarker = rule.markers.exec(node.value); + const hasMarker = rule.captureMarker.exec(node.value); const marker = hasMarker ? commentIdentifier + hasMarker[0] : commentIdentifier; if (rule.hasExceptions) { diff --git a/tools/node_modules/eslint/lib/rules/utils/ast-utils.js b/tools/node_modules/eslint/lib/rules/utils/ast-utils.js index 17e056c240c13d..01c6b47b82eefc 100644 --- a/tools/node_modules/eslint/lib/rules/utils/ast-utils.js +++ b/tools/node_modules/eslint/lib/rules/utils/ast-utils.js @@ -581,23 +581,31 @@ module.exports = { * * First, this checks the node: * - * - The function name does not start with uppercase (it's a constructor). + * - The function name does not start with uppercase. It's a convention to capitalize the names + * of constructor functions. This check is not performed if `capIsConstructor` is set to `false`. * - The function does not have a JSDoc comment that has a @this tag. * * Next, this checks the location of the node. * If the location is below, this judges `this` is valid. * * - The location is not on an object literal. - * - The location is not assigned to a variable which starts with an uppercase letter. + * - The location is not assigned to a variable which starts with an uppercase letter. Applies to anonymous + * functions only, as the name of the variable is considered to be the name of the function in this case. + * This check is not performed if `capIsConstructor` is set to `false`. * - The location is not on an ES2015 class. * - Its `bind`/`call`/`apply` method is not called directly. * - The function is not a callback of array methods (such as `.forEach()`) if `thisArg` is given. * @param {ASTNode} node A function node to check. * @param {SourceCode} sourceCode A SourceCode instance to get comments. + * @param {boolean} [capIsConstructor = true] `false` disables the assumption that functions which name starts + * with an uppercase or are assigned to a variable which name starts with an uppercase are constructors. * @returns {boolean} The function node is the default `this` binding. */ - isDefaultThisBinding(node, sourceCode) { - if (isES5Constructor(node) || hasJSDocThisTag(node, sourceCode)) { + isDefaultThisBinding(node, sourceCode, { capIsConstructor = true } = {}) { + if ( + (capIsConstructor && isES5Constructor(node)) || + hasJSDocThisTag(node, sourceCode) + ) { return false; } const isAnonymous = node.id === null; @@ -671,6 +679,7 @@ module.exports = { return false; } if ( + capIsConstructor && isAnonymous && parent.left.type === "Identifier" && startsWithUpperCase(parent.left.name) @@ -685,6 +694,7 @@ module.exports = { */ case "VariableDeclarator": return !( + capIsConstructor && isAnonymous && parent.init === currentNode && parent.id.type === "Identifier" && @@ -1200,6 +1210,23 @@ module.exports = { }; }, + /** + * Gets next location when the result is not out of bound, otherwise returns null. + * @param {SourceCode} sourceCode The sourceCode + * @param {{line: number, column: number}} location The location + * @returns {{line: number, column: number} | null} Next location + */ + getNextLocation(sourceCode, location) { + const index = sourceCode.getIndexFromLoc(location); + + // Avoid out of bound location + if (index + 1 > sourceCode.text.length) { + return null; + } + + return sourceCode.getLocFromIndex(index + 1); + }, + /** * Gets the parenthesized text of a node. This is similar to sourceCode.getText(node), but it also includes any parentheses * surrounding the node. diff --git a/tools/node_modules/eslint/lib/shared/types.js b/tools/node_modules/eslint/lib/shared/types.js index 12bd0aed8c6f1d..a5bd0200e2792a 100644 --- a/tools/node_modules/eslint/lib/shared/types.js +++ b/tools/node_modules/eslint/lib/shared/types.js @@ -30,6 +30,7 @@ module.exports = {}; * @property {Record} [env] The environment settings. * @property {string | string[]} [extends] The path to other config files or the package name of shareable configs. * @property {Record} [globals] The global variable settings. + * @property {string | string[]} [ignorePatterns] The glob patterns that ignore to lint. * @property {boolean} [noInlineConfig] The flag that disables directive comments. * @property {OverrideConfigData[]} [overrides] The override settings per kind of files. * @property {string} [parser] The path to a parser or the package name of a parser. @@ -91,6 +92,14 @@ module.exports = {}; * @property {string} message The error message. * @property {string|null} ruleId The ID of the rule which makes this message. * @property {0|1|2} severity The severity of this message. + * @property {Array<{desc?: string, messageId?: string, fix: {range: [number, number], text: string}}>} [suggestions] Information for suggestions. + */ + +/** + * @typedef {Object} SuggestionResult + * @property {string} desc A short description. + * @property {string} [messageId] Id referencing a message for the description. + * @property {{ text: string, range: number[] }} fix fix result info */ /** diff --git a/tools/node_modules/eslint/lib/source-code/source-code.js b/tools/node_modules/eslint/lib/source-code/source-code.js index 86a56803ed76c3..20b442f2367203 100644 --- a/tools/node_modules/eslint/lib/source-code/source-code.js +++ b/tools/node_modules/eslint/lib/source-code/source-code.js @@ -78,6 +78,18 @@ function sortedMerge(tokens, comments) { return result; } +/** + * Determines if two nodes or tokens overlap. + * @param {ASTNode|Token} first The first node or token to check. + * @param {ASTNode|Token} second The second node or token to check. + * @returns {boolean} True if the two nodes or tokens overlap. + * @private + */ +function nodesOrTokensOverlap(first, second) { + return (first.range[0] <= second.range[0] && first.range[1] >= second.range[0]) || + (second.range[0] <= first.range[0] && second.range[1] >= first.range[0]); +} + //------------------------------------------------------------------------------ // Public Interface //------------------------------------------------------------------------------ @@ -411,19 +423,52 @@ class SourceCode extends TokenStore { } /** - * Determines if two tokens have at least one whitespace character - * between them. This completely disregards comments in making the - * determination, so comments count as zero-length substrings. - * @param {Token} first The token to check after. - * @param {Token} second The token to check before. - * @returns {boolean} True if there is only space between tokens, false - * if there is anything other than whitespace between tokens. + * Determines if two nodes or tokens have at least one whitespace character + * between them. Order does not matter. Returns false if the given nodes or + * tokens overlap. + * @param {ASTNode|Token} first The first node or token to check between. + * @param {ASTNode|Token} second The second node or token to check between. + * @returns {boolean} True if there is a whitespace character between + * any of the tokens found between the two given nodes or tokens. * @public */ - isSpaceBetweenTokens(first, second) { - const text = this.text.slice(first.range[1], second.range[0]); + isSpaceBetween(first, second) { + if (nodesOrTokensOverlap(first, second)) { + return false; + } + + const [startingNodeOrToken, endingNodeOrToken] = first.range[1] <= second.range[0] + ? [first, second] + : [second, first]; + const firstToken = this.getLastToken(startingNodeOrToken) || startingNodeOrToken; + const finalToken = this.getFirstToken(endingNodeOrToken) || endingNodeOrToken; + let currentToken = firstToken; - return /\s/u.test(text.replace(/\/\*.*?\*\//gus, "")); + while (currentToken !== finalToken) { + const nextToken = this.getTokenAfter(currentToken, { includeComments: true }); + + if (currentToken.range[1] !== nextToken.range[0]) { + return true; + } + + currentToken = nextToken; + } + + return false; + } + + /** + * Determines if two nodes or tokens have at least one whitespace character + * between them. Order does not matter. Returns false if the given nodes or + * tokens overlap. + * @param {...ASTNode|Token} args The nodes or tokens to check between. + * @returns {boolean} True if there is a whitespace character between + * any of the tokens found between the two given nodes or tokens. + * @deprecated in favor of isSpaceBetween(). + * @public + */ + isSpaceBetweenTokens(...args) { + return this.isSpaceBetween(...args); } /** diff --git a/tools/node_modules/eslint/node_modules/ansi-escapes/index.js b/tools/node_modules/eslint/node_modules/ansi-escapes/index.js index 4638bbc3d62b29..283331858f02e1 100644 --- a/tools/node_modules/eslint/node_modules/ansi-escapes/index.js +++ b/tools/node_modules/eslint/node_modules/ansi-escapes/index.js @@ -128,5 +128,30 @@ ansiEscapes.image = (buffer, options = {}) => { }; ansiEscapes.iTerm = { - setCwd: (cwd = process.cwd()) => `${OSC}50;CurrentDir=${cwd}${BEL}` + setCwd: (cwd = process.cwd()) => `${OSC}50;CurrentDir=${cwd}${BEL}`, + + annotation: (message, options = {}) => { + let ret = `${OSC}1337;`; + + const hasX = typeof options.x !== 'undefined'; + const hasY = typeof options.y !== 'undefined'; + if ((hasX || hasY) && !(hasX && hasY && typeof options.length !== 'undefined')) { + throw new Error('`x`, `y` and `length` must be defined when `x` or `y` is defined'); + } + + message = message.replace(/\|/g, ''); + + ret += options.isHidden ? 'AddHiddenAnnotation=' : 'AddAnnotation='; + + if (options.length > 0) { + ret += + (hasX ? + [message, options.length, options.x, options.y] : + [options.length, message]).join('|'); + } else { + ret += message; + } + + return ret + BEL; + } }; diff --git a/tools/node_modules/eslint/node_modules/ansi-escapes/package.json b/tools/node_modules/eslint/node_modules/ansi-escapes/package.json index a58ed743a59171..3d97c617866121 100644 --- a/tools/node_modules/eslint/node_modules/ansi-escapes/package.json +++ b/tools/node_modules/eslint/node_modules/ansi-escapes/package.json @@ -9,15 +9,15 @@ }, "bundleDependencies": false, "dependencies": { - "type-fest": "^0.5.2" + "type-fest": "^0.8.1" }, "deprecated": false, "description": "ANSI escape codes for manipulating the terminal", "devDependencies": { "@types/node": "^12.0.7", "ava": "^2.1.0", - "tsd": "^0.7.1", - "xo": "^0.24.0" + "tsd": "^0.11.0", + "xo": "^0.25.3" }, "engines": { "node": ">=8" @@ -61,5 +61,5 @@ "scripts": { "test": "xo && ava && tsd" }, - "version": "4.2.1" + "version": "4.3.0" } \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/ansi-escapes/readme.md b/tools/node_modules/eslint/node_modules/ansi-escapes/readme.md index b290753094d948..818a9263f4e680 100644 --- a/tools/node_modules/eslint/node_modules/ansi-escapes/readme.md +++ b/tools/node_modules/eslint/node_modules/ansi-escapes/readme.md @@ -2,14 +2,12 @@ > [ANSI escape codes](http://www.termsys.demon.co.uk/vtansi.htm) for manipulating the terminal - ## Install ``` $ npm install ansi-escapes ``` - ## Usage ```js @@ -20,7 +18,6 @@ process.stdout.write(ansiEscapes.cursorUp(2) + ansiEscapes.cursorLeft); //=> '\u001B[2A\u001B[1000D' ``` - ## API ### cursorTo(x, y?) @@ -165,16 +162,71 @@ The width and height are given as a number followed by a unit, or the word "auto ##### preserveAspectRatio -Type: `boolean`
    +Type: `boolean`\ Default: `true` ### iTerm.setCwd(path?) -Type: `string`
    +Type: `string`\ Default: `process.cwd()` [Inform iTerm2](https://www.iterm2.com/documentation-escape-codes.html) of the current directory to help semantic history and enable [Cmd-clicking relative paths](https://coderwall.com/p/b7e82q/quickly-open-files-in-iterm-with-cmd-click). +### iTerm.annotation(message, options?) + +Creates an escape code to display an "annotation" in iTerm2. + +An annotation looks like this when shown: + + + +See the [iTerm Proprietary Escape Codes documentation](https://iterm2.com/documentation-escape-codes.html) for more information. + +#### message + +Type: `string` + +The message to display within the annotation. + +The `|` character is disallowed and will be stripped. + +#### options + +Type: `object` + +##### length + +Type: `number`\ +Default: The remainder of the line + +Nonzero number of columns to annotate. + +##### x + +Type: `number`\ +Default: Cursor position + +Starting X coordinate. + +Must be used with `y` and `length`. + +##### y + +Type: `number`\ +Default: Cursor position + +Starting Y coordinate. + +Must be used with `x` and `length`. + +##### isHidden + +Type: `boolean`\ +Default: `false` + +Create a "hidden" annotation. + +Annotations created this way can be shown using the "Show Annotations" iTerm command. ## Related diff --git a/tools/node_modules/eslint/node_modules/ansi-regex/index.js b/tools/node_modules/eslint/node_modules/ansi-regex/index.js index c25448009f304d..35054aa6774afe 100644 --- a/tools/node_modules/eslint/node_modules/ansi-regex/index.js +++ b/tools/node_modules/eslint/node_modules/ansi-regex/index.js @@ -1,14 +1,10 @@ 'use strict'; -module.exports = options => { - options = Object.assign({ - onlyFirst: false - }, options); - +module.exports = ({onlyFirst = false} = {}) => { const pattern = [ '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)', '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))' ].join('|'); - return new RegExp(pattern, options.onlyFirst ? undefined : 'g'); + return new RegExp(pattern, onlyFirst ? undefined : 'g'); }; diff --git a/tools/node_modules/eslint/node_modules/ansi-regex/package.json b/tools/node_modules/eslint/node_modules/ansi-regex/package.json index db8f3cc8c7d14e..d0574afde48599 100644 --- a/tools/node_modules/eslint/node_modules/ansi-regex/package.json +++ b/tools/node_modules/eslint/node_modules/ansi-regex/package.json @@ -11,14 +11,16 @@ "deprecated": false, "description": "Regular expression for matching ANSI escape codes", "devDependencies": { - "ava": "^0.25.0", - "xo": "^0.23.0" + "ava": "^2.4.0", + "tsd": "^0.9.0", + "xo": "^0.25.3" }, "engines": { - "node": ">=6" + "node": ">=8" }, "files": [ - "index.js" + "index.js", + "index.d.ts" ], "homepage": "https://github.com/chalk/ansi-regex#readme", "keywords": [ @@ -55,8 +57,8 @@ "url": "git+https://github.com/chalk/ansi-regex.git" }, "scripts": { - "test": "xo && ava", + "test": "xo && ava && tsd", "view-supported": "node fixtures/view-codes.js" }, - "version": "4.1.0" + "version": "5.0.0" } \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/ansi-regex/readme.md b/tools/node_modules/eslint/node_modules/ansi-regex/readme.md index d19c44667e704b..3c2b77c4354b68 100644 --- a/tools/node_modules/eslint/node_modules/ansi-regex/readme.md +++ b/tools/node_modules/eslint/node_modules/ansi-regex/readme.md @@ -2,20 +2,6 @@ > Regular expression for matching [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code) ---- - -
    - - Get professional support for this package with a Tidelift subscription - -
    - - Tidelift helps make open source sustainable for maintainers while giving companies
    assurances about security, maintenance, and licensing for their dependencies. -
    -
    - ---- - ## Install @@ -48,12 +34,14 @@ ansiRegex().test('cake'); ## API -### ansiRegex([options]) +### ansiRegex(options?) Returns a regex for matching ANSI escape codes. #### options +Type: `object` + ##### onlyFirst Type: `boolean`
    @@ -71,17 +59,20 @@ Some of the codes we run as a test are codes that we acquired finding various li On the historical side, those ECMA standards were established in the early 90's whereas the VT100, for example, was designed in the mid/late 70's. At that point in time, control codes were still pretty ungoverned and engineers used them for a multitude of things, namely to activate hardware ports that may have been proprietary. Somewhere else you see a similar 'anarchy' of codes is in the x86 architecture for processors; there are a ton of "interrupts" that can mean different things on certain brands of processors, most of which have been phased out. -## Security - -To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure. - - ## Maintainers - [Sindre Sorhus](https://github.com/sindresorhus) - [Josh Junon](https://github.com/qix-) -## License +--- -MIT +
    + + Get professional support for this package with a Tidelift subscription + +
    + + Tidelift helps make open source sustainable for maintainers while giving companies
    assurances about security, maintenance, and licensing for their dependencies. +
    +
    diff --git a/tools/node_modules/eslint/node_modules/glob/package.json b/tools/node_modules/eslint/node_modules/glob/package.json index cf2be43974e853..bf5a63608ca133 100644 --- a/tools/node_modules/eslint/node_modules/glob/package.json +++ b/tools/node_modules/eslint/node_modules/glob/package.json @@ -32,6 +32,9 @@ "sync.js", "common.js" ], + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, "homepage": "https://github.com/isaacs/node-glob#readme", "license": "ISC", "main": "glob.js", @@ -49,5 +52,5 @@ "test": "tap test/*.js --cov", "test-regen": "npm run profclean && TEST_REGEN=1 node test/00-setup.js" }, - "version": "7.1.5" + "version": "7.1.6" } \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/globals/globals.json b/tools/node_modules/eslint/node_modules/globals/globals.json index 6f5ac02f1eace7..b33f0431b544e9 100644 --- a/tools/node_modules/eslint/node_modules/globals/globals.json +++ b/tools/node_modules/eslint/node_modules/globals/globals.json @@ -1066,6 +1066,7 @@ "beforeEach": false, "describe": false, "expect": false, + "expectAsync": false, "fail": false, "fdescribe": false, "fit": false, @@ -1074,6 +1075,7 @@ "pending": false, "runs": false, "spyOn": false, + "spyOnAllFunctions": false, "spyOnProperty": false, "waits": false, "waitsFor": false, @@ -1184,8 +1186,11 @@ }, "wsh": { "ActiveXObject": true, + "CollectGarbage": true, + "Debug": true, "Enumerator": true, "GetObject": true, + "RuntimeObject": true, "ScriptEngine": true, "ScriptEngineBuildVersion": true, "ScriptEngineMajorVersion": true, diff --git a/tools/node_modules/eslint/node_modules/globals/package.json b/tools/node_modules/eslint/node_modules/globals/package.json index ae094f3054063e..de6b8926f166d9 100644 --- a/tools/node_modules/eslint/node_modules/globals/package.json +++ b/tools/node_modules/eslint/node_modules/globals/package.json @@ -8,17 +8,22 @@ "url": "https://github.com/sindresorhus/globals/issues" }, "bundleDependencies": false, + "dependencies": { + "type-fest": "^0.8.1" + }, "deprecated": false, "description": "Global identifiers from different JavaScript environments", "devDependencies": { - "ava": "0.21.0", - "xo": "0.18.0" + "ava": "^2.2.0", + "tsd": "^0.9.0", + "xo": "^0.25.3" }, "engines": { - "node": ">=4" + "node": ">=8" }, "files": [ "index.js", + "index.d.ts", "globals.json" ], "homepage": "https://github.com/sindresorhus/globals#readme", @@ -41,7 +46,12 @@ "scripts": { "test": "xo && ava" }, - "version": "11.12.0", + "tsd": { + "compilerOptions": { + "resolveJsonModule": true + } + }, + "version": "12.3.0", "xo": { "ignores": [ "get-browser-globals.js" diff --git a/tools/node_modules/eslint/node_modules/globals/readme.md b/tools/node_modules/eslint/node_modules/globals/readme.md index 8c47855f34d900..96ce28347a8de0 100644 --- a/tools/node_modules/eslint/node_modules/globals/readme.md +++ b/tools/node_modules/eslint/node_modules/globals/readme.md @@ -36,6 +36,14 @@ console.log(globals.browser); Each global is given a value of `true` or `false`. A value of `true` indicates that the variable may be overwritten. A value of `false` indicates that the variable should be considered read-only. This information is used by static analysis tools to flag incorrect behavior. We assume all variables should be `false` unless we hear otherwise. -## License - -MIT © [Sindre Sorhus](https://sindresorhus.com) +--- + +
    + + Get professional support for this package with a Tidelift subscription + +
    + + Tidelift helps make open source sustainable for maintainers while giving companies
    assurances about security, maintenance, and licensing for their dependencies. +
    +
    diff --git a/tools/node_modules/eslint/node_modules/import-fresh/index.js b/tools/node_modules/eslint/node_modules/import-fresh/index.js index 7abc6706192d64..425ed98c42f68e 100644 --- a/tools/node_modules/eslint/node_modules/import-fresh/index.js +++ b/tools/node_modules/eslint/node_modules/import-fresh/index.js @@ -8,22 +8,25 @@ module.exports = moduleId => { throw new TypeError('Expected a string'); } - const filePath = resolveFrom(path.dirname(parentModule(__filename)), moduleId); + const parentPath = parentModule(__filename); + const filePath = resolveFrom(path.dirname(parentPath), moduleId); + + const oldModule = require.cache[filePath]; // Delete itself from module parent - if (require.cache[filePath] && require.cache[filePath].parent) { - let i = require.cache[filePath].parent.children.length; + if (oldModule && oldModule.parent) { + let i = oldModule.parent.children.length; while (i--) { - if (require.cache[filePath].parent.children[i].id === filePath) { - require.cache[filePath].parent.children.splice(i, 1); + if (oldModule.parent.children[i].id === filePath) { + oldModule.parent.children.splice(i, 1); } } } - // Delete module from cache - delete require.cache[filePath]; + delete require.cache[filePath]; // Delete module from cache + + const parent = require.cache[parentPath]; // If `filePath` and `parentPath` are the same, cache will already be deleted so we won't get a memory leak in next step - // Return fresh module - return require(filePath); + return parent === undefined ? require(filePath) : parent.require(filePath); // In case cache doesn't have parent, fall back to normal require }; diff --git a/tools/node_modules/eslint/node_modules/import-fresh/package.json b/tools/node_modules/eslint/node_modules/import-fresh/package.json index 73e9316d8999b7..38892a62e43136 100644 --- a/tools/node_modules/eslint/node_modules/import-fresh/package.json +++ b/tools/node_modules/eslint/node_modules/import-fresh/package.json @@ -47,5 +47,5 @@ "heapdump": "node heapdump.js", "test": "xo && ava && tsd" }, - "version": "3.1.0" + "version": "3.2.1" } \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/import-fresh/readme.md b/tools/node_modules/eslint/node_modules/import-fresh/readme.md index f5414b8cf33fdd..0bfa1c90443e83 100644 --- a/tools/node_modules/eslint/node_modules/import-fresh/readme.md +++ b/tools/node_modules/eslint/node_modules/import-fresh/readme.md @@ -37,22 +37,16 @@ importFresh('./foo')(); ``` +## import-fresh for enterprise + +Available as part of the Tidelift Subscription. + +The maintainers of import-fresh and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-import-fresh?utm_source=npm-import-fresh&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) + + ## Related - [clear-module](https://github.com/sindresorhus/clear-module) - Clear a module from the import cache - [import-from](https://github.com/sindresorhus/import-from) - Import a module from a given path - [import-cwd](https://github.com/sindresorhus/import-cwd) - Import a module from the current working directory - [import-lazy](https://github.com/sindresorhus/import-lazy) - Import modules lazily - - ---- - -
    - - Get professional support for this package with a Tidelift subscription - -
    - - Tidelift helps make open source sustainable for maintainers while giving companies
    assurances about security, maintenance, and licensing for their dependencies. -
    -
    diff --git a/tools/node_modules/eslint/node_modules/optionator/README.md b/tools/node_modules/eslint/node_modules/optionator/README.md index 91c59d379b3bae..8e4ba424526e44 100644 --- a/tools/node_modules/eslint/node_modules/optionator/README.md +++ b/tools/node_modules/eslint/node_modules/optionator/README.md @@ -1,7 +1,7 @@ # Optionator -Optionator is a JavaScript option parsing and help generation library used by [eslint](http://eslint.org), [Grasp](http://graspjs.com), [LiveScript](http://livescript.net), [esmangle](https://github.com/estools/esmangle), [escodegen](https://github.com/estools/escodegen), and [many more](https://www.npmjs.com/browse/depended/optionator). +Optionator is a JavaScript/Node.js option parsing and help generation library used by [eslint](http://eslint.org), [Grasp](http://graspjs.com), [LiveScript](http://livescript.net), [esmangle](https://github.com/estools/esmangle), [escodegen](https://github.com/estools/escodegen), and [many more](https://www.npmjs.com/browse/depended/optionator). For an online demo, check out the [Grasp online demo](http://www.graspjs.com/#demo). @@ -23,12 +23,14 @@ Other helpful features include reformatting the help text based on the size of t ## About Optionator uses [type-check](https://github.com/gkz/type-check) and [levn](https://github.com/gkz/levn) behind the scenes to cast and verify input according the specified types. -MIT license. Version 0.8.2 +MIT license. Version 0.8.3 npm install optionator For updates on Optionator, [follow me on twitter](https://twitter.com/gkzahariev). +Optionator is a Node.js module, but can be used in the browser as well if packed with webpack/browserify. + ## Usage `require('optionator');` returns a function. It has one property, `VERSION`, the current version of the library as a string. This function is called with an object specifying your options and other information, see the [settings format section](#settings-format). This in turn returns an object with three properties, `parse`, `parseArgv`, `generateHelp`, and `generateHelpForOption`, which are all functions. diff --git a/tools/node_modules/eslint/node_modules/optionator/lib/help.js b/tools/node_modules/eslint/node_modules/optionator/lib/help.js index a459c02c26711b..59e6f9694c9584 100644 --- a/tools/node_modules/eslint/node_modules/optionator/lib/help.js +++ b/tools/node_modules/eslint/node_modules/optionator/lib/help.js @@ -1,9 +1,22 @@ -// Generated by LiveScript 1.5.0 +// Generated by LiveScript 1.6.0 (function(){ - var ref$, id, find, sort, min, max, map, unlines, nameToRaw, dasherize, naturalJoin, wordwrap, getPreText, setHelpStyleDefaults, generateHelpForOption, generateHelp; + var ref$, id, find, sort, min, max, map, unlines, nameToRaw, dasherize, naturalJoin, wordWrap, wordwrap, getPreText, setHelpStyleDefaults, generateHelpForOption, generateHelp; ref$ = require('prelude-ls'), id = ref$.id, find = ref$.find, sort = ref$.sort, min = ref$.min, max = ref$.max, map = ref$.map, unlines = ref$.unlines; ref$ = require('./util'), nameToRaw = ref$.nameToRaw, dasherize = ref$.dasherize, naturalJoin = ref$.naturalJoin; - wordwrap = require('wordwrap'); + wordWrap = require('word-wrap'); + wordwrap = function(a, b){ + var ref$, indent, width; + ref$ = b === undefined + ? ['', a - 1] + : [repeatString$(' ', a), b - a - 1], indent = ref$[0], width = ref$[1]; + return function(text){ + return wordWrap(text, { + indent: indent, + width: width, + trim: true + }); + }; + }; getPreText = function(option, arg$, maxWidth){ var mainName, shortNames, ref$, longNames, type, description, aliasSeparator, typeSeparator, initialIndent, names, namesString, namesStringLen, typeSeparatorString, typeSeparatorStringLen, wrap; mainName = option.option, shortNames = (ref$ = option.shortNames) != null diff --git a/tools/node_modules/eslint/node_modules/optionator/lib/index.js b/tools/node_modules/eslint/node_modules/optionator/lib/index.js index d947286c761ef8..7ce37b23b01c98 100644 --- a/tools/node_modules/eslint/node_modules/optionator/lib/index.js +++ b/tools/node_modules/eslint/node_modules/optionator/lib/index.js @@ -1,7 +1,7 @@ -// Generated by LiveScript 1.5.0 +// Generated by LiveScript 1.6.0 (function(){ - var VERSION, ref$, id, map, compact, any, groupBy, partition, chars, isItNaN, keys, Obj, camelize, deepIs, closestString, nameToRaw, dasherize, naturalJoin, generateHelp, generateHelpForOption, parsedTypeCheck, parseType, parseLevn, camelizeKeys, parseString, main, toString$ = {}.toString, slice$ = [].slice; - VERSION = '0.8.2'; + var VERSION, ref$, id, map, compact, any, groupBy, partition, chars, isItNaN, keys, Obj, camelize, deepIs, closestString, nameToRaw, dasherize, naturalJoin, generateHelp, generateHelpForOption, parsedTypeCheck, parseType, parseLevn, camelizeKeys, parseString, main, toString$ = {}.toString, slice$ = [].slice, arrayFrom$ = Array.from || function(x){return slice$.call(x);}; + VERSION = '0.8.3'; ref$ = require('prelude-ls'), id = ref$.id, map = ref$.map, compact = ref$.compact, any = ref$.any, groupBy = ref$.groupBy, partition = ref$.partition, chars = ref$.chars, isItNaN = ref$.isItNaN, keys = ref$.keys, Obj = ref$.Obj, camelize = ref$.camelize; deepIs = require('deep-is'); ref$ = require('./util'), closestString = ref$.closestString, nameToRaw = ref$.nameToRaw, dasherize = ref$.dasherize, naturalJoin = ref$.naturalJoin; @@ -17,7 +17,7 @@ return resultObj$; }; parseString = function(string){ - var assignOpt, regex, replaceRegex, result, this$ = this; + var assignOpt, regex, replaceRegex, result; assignOpt = '--?[a-zA-Z][-a-z-A-Z0-9]*='; regex = RegExp('(?:' + assignOpt + ')?(?:\'(?:\\\\\'|[^\'])+\'|"(?:\\\\"|[^"])+")|[^\'"\\s]+', 'g'); replaceRegex = RegExp('^(' + assignOpt + ')?[\'"]([\\s\\S]*)[\'"]$'); @@ -44,7 +44,7 @@ libOptions.defaults.mergeRepeatedObjects = libOptions.mergeRepeatedObjects; } traverse = function(options){ - var i$, len$, option, name, k, ref$, v, type, that, e, parsedPossibilities, parsedType, j$, len1$, possibility, rawDependsType, dependsOpts, dependsType, cra, alias, shortNames, longNames, this$ = this; + var i$, len$, option, name, k, ref$, v, type, that, e, parsedPossibilities, parsedType, j$, len1$, possibility, rawDependsType, dependsOpts, dependsType, cra, alias, shortNames, longNames; if (toString$.call(options).slice(8, -1) !== 'Array') { throw new Error('No options defined.'); } @@ -104,7 +104,7 @@ dependsType = rawDependsType.toLowerCase(); if (dependsOpts.length) { if (dependsType === 'and' || dependsType === 'or') { - option.dependsOn = [dependsType].concat(slice$.call(dependsOpts)); + option.dependsOn = [dependsType].concat(arrayFrom$(dependsOpts)); } else { throw new Error("Option '" + name + "': If you have more than one dependency, you must specify either 'and' or 'or'"); } diff --git a/tools/node_modules/eslint/node_modules/optionator/lib/util.js b/tools/node_modules/eslint/node_modules/optionator/lib/util.js index d5c972def14cfa..5bc0cbb69e66aa 100644 --- a/tools/node_modules/eslint/node_modules/optionator/lib/util.js +++ b/tools/node_modules/eslint/node_modules/optionator/lib/util.js @@ -1,10 +1,10 @@ -// Generated by LiveScript 1.5.0 +// Generated by LiveScript 1.6.0 (function(){ var prelude, map, sortBy, fl, closestString, nameToRaw, dasherize, naturalJoin; prelude = require('prelude-ls'), map = prelude.map, sortBy = prelude.sortBy; fl = require('fast-levenshtein'); closestString = function(possibilities, input){ - var distances, ref$, string, distance, this$ = this; + var distances, ref$, string, distance; if (!possibilities.length) { return; } diff --git a/tools/node_modules/eslint/node_modules/optionator/package.json b/tools/node_modules/eslint/node_modules/optionator/package.json index d492cd682bdc29..b69935ea0e8e6d 100644 --- a/tools/node_modules/eslint/node_modules/optionator/package.json +++ b/tools/node_modules/eslint/node_modules/optionator/package.json @@ -9,18 +9,18 @@ "bundleDependencies": false, "dependencies": { "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", + "fast-levenshtein": "~2.0.6", "levn": "~0.3.0", "prelude-ls": "~1.1.2", "type-check": "~0.3.2", - "wordwrap": "~1.0.0" + "word-wrap": "~1.2.3" }, "deprecated": false, "description": "option parsing and help generation", "devDependencies": { - "istanbul": "~0.4.1", - "livescript": "~1.5.0", - "mocha": "~3.0.2" + "istanbul": "~0.4.5", + "livescript": "~1.6.0", + "mocha": "~6.2.2" }, "engines": { "node": ">= 0.8.0" @@ -47,5 +47,5 @@ "scripts": { "test": "make test" }, - "version": "0.8.2" + "version": "0.8.3" } \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/index.js b/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/index.js new file mode 100644 index 00000000000000..9a593dfcd1fd5c --- /dev/null +++ b/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/index.js @@ -0,0 +1,4 @@ +'use strict'; +const ansiRegex = require('ansi-regex'); + +module.exports = string => typeof string === 'string' ? string.replace(ansiRegex(), '') : string; diff --git a/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/license b/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/license new file mode 100644 index 00000000000000..e7af2f77107d73 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/license @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +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. diff --git a/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/package.json b/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/package.json new file mode 100644 index 00000000000000..5db6f68dc0aef7 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/package.json @@ -0,0 +1,63 @@ +{ + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/chalk/strip-ansi/issues" + }, + "bundleDependencies": false, + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "deprecated": false, + "description": "Strip ANSI escape codes from a string", + "devDependencies": { + "ava": "^2.4.0", + "tsd": "^0.10.0", + "xo": "^0.25.3" + }, + "engines": { + "node": ">=8" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "homepage": "https://github.com/chalk/strip-ansi#readme", + "keywords": [ + "strip", + "trim", + "remove", + "ansi", + "styles", + "color", + "colour", + "colors", + "terminal", + "console", + "string", + "tty", + "escape", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "log", + "logging", + "command-line", + "text" + ], + "license": "MIT", + "name": "strip-ansi", + "repository": { + "type": "git", + "url": "git+https://github.com/chalk/strip-ansi.git" + }, + "scripts": { + "test": "xo && ava && tsd" + }, + "version": "6.0.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/readme.md b/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/readme.md new file mode 100644 index 00000000000000..7c4b56d46ddc72 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/readme.md @@ -0,0 +1,46 @@ +# strip-ansi [![Build Status](https://travis-ci.org/chalk/strip-ansi.svg?branch=master)](https://travis-ci.org/chalk/strip-ansi) + +> Strip [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code) from a string + + +## Install + +``` +$ npm install strip-ansi +``` + + +## Usage + +```js +const stripAnsi = require('strip-ansi'); + +stripAnsi('\u001B[4mUnicorn\u001B[0m'); +//=> 'Unicorn' + +stripAnsi('\u001B]8;;https://github.com\u0007Click\u001B]8;;\u0007'); +//=> 'Click' +``` + + +## strip-ansi for enterprise + +Available as part of the Tidelift Subscription. + +The maintainers of strip-ansi and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-strip-ansi?utm_source=npm-strip-ansi&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) + + +## Related + +- [strip-ansi-cli](https://github.com/chalk/strip-ansi-cli) - CLI for this module +- [strip-ansi-stream](https://github.com/chalk/strip-ansi-stream) - Streaming version of this module +- [has-ansi](https://github.com/chalk/has-ansi) - Check if a string has ANSI escape codes +- [ansi-regex](https://github.com/chalk/ansi-regex) - Regular expression for matching ANSI escape codes +- [chalk](https://github.com/chalk/chalk) - Terminal string styling done right + + +## Maintainers + +- [Sindre Sorhus](https://github.com/sindresorhus) +- [Josh Junon](https://github.com/qix-) + diff --git a/tools/node_modules/eslint/node_modules/string-width/package.json b/tools/node_modules/eslint/node_modules/string-width/package.json index 9ef5253e54d49b..38993c1e2e336d 100644 --- a/tools/node_modules/eslint/node_modules/string-width/package.json +++ b/tools/node_modules/eslint/node_modules/string-width/package.json @@ -11,7 +11,7 @@ "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^5.2.0" + "strip-ansi": "^6.0.0" }, "deprecated": false, "description": "Get the visual width of a string - the number of columns required to display it", @@ -61,5 +61,5 @@ "scripts": { "test": "xo && ava && tsd" }, - "version": "4.1.0" + "version": "4.2.0" } \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/string-width/readme.md b/tools/node_modules/eslint/node_modules/string-width/readme.md index 35a0c0377a900d..705f206001b777 100644 --- a/tools/node_modules/eslint/node_modules/string-width/readme.md +++ b/tools/node_modules/eslint/node_modules/string-width/readme.md @@ -37,6 +37,14 @@ stringWidth('\u001B[1m古\u001B[22m'); - [widest-line](https://github.com/sindresorhus/widest-line) - Get the visual width of the widest line in a string -## License - -MIT © [Sindre Sorhus](https://sindresorhus.com) +--- + +
    + + Get professional support for this package with a Tidelift subscription + +
    + + Tidelift helps make open source sustainable for maintainers while giving companies
    assurances about security, maintenance, and licensing for their dependencies. +
    +
    diff --git a/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/index.js b/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/index.js new file mode 100644 index 00000000000000..c25448009f304d --- /dev/null +++ b/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/index.js @@ -0,0 +1,14 @@ +'use strict'; + +module.exports = options => { + options = Object.assign({ + onlyFirst: false + }, options); + + const pattern = [ + '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)', + '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))' + ].join('|'); + + return new RegExp(pattern, options.onlyFirst ? undefined : 'g'); +}; diff --git a/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/license b/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/license new file mode 100644 index 00000000000000..e7af2f77107d73 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/license @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +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. diff --git a/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/package.json b/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/package.json new file mode 100644 index 00000000000000..db8f3cc8c7d14e --- /dev/null +++ b/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/package.json @@ -0,0 +1,62 @@ +{ + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/chalk/ansi-regex/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "Regular expression for matching ANSI escape codes", + "devDependencies": { + "ava": "^0.25.0", + "xo": "^0.23.0" + }, + "engines": { + "node": ">=6" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/chalk/ansi-regex#readme", + "keywords": [ + "ansi", + "styles", + "color", + "colour", + "colors", + "terminal", + "console", + "cli", + "string", + "tty", + "escape", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "command-line", + "text", + "regex", + "regexp", + "re", + "match", + "test", + "find", + "pattern" + ], + "license": "MIT", + "name": "ansi-regex", + "repository": { + "type": "git", + "url": "git+https://github.com/chalk/ansi-regex.git" + }, + "scripts": { + "test": "xo && ava", + "view-supported": "node fixtures/view-codes.js" + }, + "version": "4.1.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/readme.md b/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/readme.md new file mode 100644 index 00000000000000..d19c44667e704b --- /dev/null +++ b/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/readme.md @@ -0,0 +1,87 @@ +# ansi-regex [![Build Status](https://travis-ci.org/chalk/ansi-regex.svg?branch=master)](https://travis-ci.org/chalk/ansi-regex) + +> Regular expression for matching [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code) + +--- + +
    + + Get professional support for this package with a Tidelift subscription + +
    + + Tidelift helps make open source sustainable for maintainers while giving companies
    assurances about security, maintenance, and licensing for their dependencies. +
    +
    + +--- + + +## Install + +``` +$ npm install ansi-regex +``` + + +## Usage + +```js +const ansiRegex = require('ansi-regex'); + +ansiRegex().test('\u001B[4mcake\u001B[0m'); +//=> true + +ansiRegex().test('cake'); +//=> false + +'\u001B[4mcake\u001B[0m'.match(ansiRegex()); +//=> ['\u001B[4m', '\u001B[0m'] + +'\u001B[4mcake\u001B[0m'.match(ansiRegex({onlyFirst: true})); +//=> ['\u001B[4m'] + +'\u001B]8;;https://github.com\u0007click\u001B]8;;\u0007'.match(ansiRegex()); +//=> ['\u001B]8;;https://github.com\u0007', '\u001B]8;;\u0007'] +``` + + +## API + +### ansiRegex([options]) + +Returns a regex for matching ANSI escape codes. + +#### options + +##### onlyFirst + +Type: `boolean`
    +Default: `false` *(Matches any ANSI escape codes in a string)* + +Match only the first ANSI escape. + + +## FAQ + +### Why do you test for codes not in the ECMA 48 standard? + +Some of the codes we run as a test are codes that we acquired finding various lists of non-standard or manufacturer specific codes. We test for both standard and non-standard codes, as most of them follow the same or similar format and can be safely matched in strings without the risk of removing actual string content. There are a few non-standard control codes that do not follow the traditional format (i.e. they end in numbers) thus forcing us to exclude them from the test because we cannot reliably match them. + +On the historical side, those ECMA standards were established in the early 90's whereas the VT100, for example, was designed in the mid/late 70's. At that point in time, control codes were still pretty ungoverned and engineers used them for a multitude of things, namely to activate hardware ports that may have been proprietary. Somewhere else you see a similar 'anarchy' of codes is in the x86 architecture for processors; there are a ton of "interrupts" that can mean different things on certain brands of processors, most of which have been phased out. + + +## Security + +To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure. + + +## Maintainers + +- [Sindre Sorhus](https://github.com/sindresorhus) +- [Josh Junon](https://github.com/qix-) + + +## License + +MIT diff --git a/tools/node_modules/eslint/node_modules/type-fest/package.json b/tools/node_modules/eslint/node_modules/type-fest/package.json index a5a398048dc370..0164025631ef29 100644 --- a/tools/node_modules/eslint/node_modules/type-fest/package.json +++ b/tools/node_modules/eslint/node_modules/type-fest/package.json @@ -11,14 +11,15 @@ "deprecated": false, "description": "A collection of essential TypeScript types", "devDependencies": { - "@sindresorhus/tsconfig": "^0.3.0", - "@typescript-eslint/eslint-plugin": "^1.8.0", - "eslint-config-xo-typescript": "^0.11.0", + "@sindresorhus/tsconfig": "^0.4.0", + "@typescript-eslint/eslint-plugin": "^2.2.0", + "@typescript-eslint/parser": "^2.2.0", + "eslint-config-xo-typescript": "^0.18.0", "tsd": "^0.7.3", "xo": "^0.24.0" }, "engines": { - "node": ">=6" + "node": ">=8" }, "files": [ "index.d.ts", @@ -45,7 +46,7 @@ "scripts": { "test": "xo && tsd" }, - "version": "0.5.2", + "version": "0.8.1", "xo": { "extends": "xo-typescript", "extensions": [ diff --git a/tools/node_modules/eslint/node_modules/type-fest/readme.md b/tools/node_modules/eslint/node_modules/type-fest/readme.md index 3f11c3c1edb6c3..1824bdabedecaa 100644 --- a/tools/node_modules/eslint/node_modules/type-fest/readme.md +++ b/tools/node_modules/eslint/node_modules/type-fest/readme.md @@ -36,14 +36,14 @@ $ npm install type-fest ## Usage ```ts -import {Omit} from 'type-fest'; +import {Except} from 'type-fest'; type Foo = { unicorn: string; rainbow: boolean; }; -type FooWithoutRainbow = Omit; +type FooWithoutRainbow = Except; //=> {unicorn: string} ``` @@ -64,13 +64,19 @@ Click the type names for complete docs. ### Utilities -- [`Omit`](source/omit.d.ts) - Create a type from an object type without certain keys. -- [`Mutable`](source/mutable.d.ts) - Convert an object with `readonly` properties into a mutable object. Inverse of `Readonly`. +- [`Except`](source/except.d.ts) - Create a type from an object type without certain keys. This is a stricter version of [`Omit`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-5.html#the-omit-helper-type). +- [`Mutable`](source/mutable.d.ts) - Convert an object with `readonly` keys into a mutable object. The inverse of `Readonly`. - [`Merge`](source/merge.d.ts) - Merge two types into a new type. Keys of the second type overrides keys of the first type. -- [`MergeExclusive`](source/merge-exclusive.d.ts) - Create a type that has mutually exclusive properties. -- [`RequireAtLeastOne`](source/require-at-least-one.d.ts) - Create a type that requires at least one of the given properties. -- [`ReadonlyDeep`](source/readonly-deep.d.ts) - Create a deeply immutable version of a `object`/`Map`/`Set`/`Array` type. +- [`MergeExclusive`](source/merge-exclusive.d.ts) - Create a type that has mutually exclusive keys. +- [`RequireAtLeastOne`](source/require-at-least-one.d.ts) - Create a type that requires at least one of the given keys. +- [`RequireExactlyOne`](source/require-one.d.ts) - Create a type that requires exactly a single key of the given keys and disallows more. +- [`PartialDeep`](source/partial-deep.d.ts) - Create a deeply optional version of another type. Use [`Partial`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1401-L1406) if you only need one level deep. +- [`ReadonlyDeep`](source/readonly-deep.d.ts) - Create a deeply immutable version of an `object`/`Map`/`Set`/`Array` type. Use [`Readonly`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1415-L1420) if you only need one level deep. - [`LiteralUnion`](source/literal-union.d.ts) - Create a union type by combining primitive types and literal types without sacrificing auto-completion in IDEs for the literal type part of the union. Workaround for [Microsoft/TypeScript#29729](https://github.com/Microsoft/TypeScript/issues/29729). +- [`Promisable`](source/promisable.d.ts) - Create a type that represents either the value or the value wrapped in `PromiseLike`. +- [`Opaque`](source/opaque.d.ts) - Create an [opaque type](https://codemix.com/opaque-types-in-javascript/). +- [`SetOptional`](source/set-optional.d.ts) - Create a type that makes the given keys optional. +- [`SetRequired`](source/set-required.d.ts) - Create a type that makes the given keys required. ### Miscellaneous @@ -82,6 +88,7 @@ Click the type names for complete docs. *If we decline a type addition, we will make sure to document the better solution here.* - [`Diff` and `Spread`](https://github.com/sindresorhus/type-fest/pull/7) - The PR author didn't provide any real-world use-cases and the PR went stale. If you think this type is useful, provide some real-world use-cases and we might reconsider. +- [`Dictionary`](https://github.com/sindresorhus/type-fest/issues/33) - You only save a few characters (`Dictionary` vs `Record`) from [`Record`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1429-L1434), which is more flexible and well-known. Also, you shouldn't use an object as a dictionary. We have `Map` in JavaScript now. ## Tips @@ -91,17 +98,514 @@ Click the type names for complete docs. There are many advanced types most users don't know about. - [`Partial`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1401-L1406) - Make all properties in `T` optional. +
    + + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/KYOwrgtgBAMg9gcxsAbsANlA3gKClAeQDMiAaPKAEWACMwFz8BRAJxbhcagDEBDAF17ocAXxw4AliH7AWRXgGNgUAHJwAJsADCcEEQkJsFXgAcTK3hGAAuKAGd+LKQgDcFEx363wEGrLf46IjIaOi28EioGG5iOArovHZ2qhrAAIJmAEJgEuiaLEb4Jk4oAsoKuvoIYCwCErq2apo6egZQALyF+FCm5pY2UABETelmg1xFnrYAzAAM8xNQQZGh4cFR6AB0xEQUIm4UFa0IABRHVbYACrws-BJCADwjLVUAfACUXfhEHFBnug4oABrYAATygcCIhBoACtgAp+JsQaC7P9ju9Prhut0joCwCZ1GUAGpCMDKTrnAwAbWRPWSyMhKWalQMAF0Dtj8BIoSd8YSZCT0GSOu1OmAQJp9CBgOpPkc7uBgBzOfwABYSOybSnVWp3XQ0sF04FgxnPFkIVkdKB84mkpUUfCxbEsYD8GogKBqjUBKBiWIAen9UGut3u6CeqReBlePXQQQA7skwMl+HAoMU4CgJJoISB0ODeOmbvwIVC1cAcIGmdpzVApDI5IpgJscNL49WMiZsrl8id3lrzScsD0zBYrLZBgAVOCUOCdwa+95uIA) + + ```ts + interface NodeConfig { + appName: string; + port: number; + } + + class NodeAppBuilder { + private configuration: NodeConfig = { + appName: 'NodeApp', + port: 3000 + }; + + config(config: Partial) { + type NodeConfigKey = keyof NodeConfig; + + for (const key of Object.keys(config) as NodeConfigKey[]) { + const updateValue = config[key]; + + if (updateValue === undefined) { + continue; + } + + this.configuration[key] = updateValue; + } + + return this; + } + } + + // `Partial`` allows us to provide only a part of the + // NodeConfig interface. + new NodeAppBuilder().config({appName: 'ToDoApp'}); + ``` +
    + - [`Required`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1408-L1413) - Make all properties in `T` required. +
    + + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/AQ4SwOwFwUwJwGYEMDGNgGED21VQGJZwC2wA3gFCjXAzFJgA2A-AFzADOUckA5gNxUaIYjA4ckvGG07c+g6gF8KQkAgCuEFFDA5O6gEbEwUbLm2ESwABQIixACJIoSdgCUYAR3Vg4MACYAPGYuFvYAfACU5Ko0APRxwADKMBD+wFAAFuh2Vv7OSBlYGdmc8ABu8LHKsRyGxqY4oQT21pTCIHQMjOwA5DAAHgACxAAOjDAAdChYxL0ANLHUouKSMH0AEmAAhJhY6ozpAJ77GTCMjMCiV0ToSAb7UJPPC9WRgrEJwAAqR6MwSRQPFGUFocDgRHYxnEfGAowh-zgUCOwF6KwkUl6tXqJhCeEsxDaS1AXSYfUGI3GUxmc0WSneQA) + + ```ts + interface ContactForm { + email?: string; + message?: string; + } + + function submitContactForm(formData: Required) { + // Send the form data to the server. + } + + submitContactForm({ + email: 'ex@mple.com', + message: 'Hi! Could you tell me more about…', + }); + + // TypeScript error: missing property 'message' + submitContactForm({ + email: 'ex@mple.com', + }); + ``` +
    + - [`Readonly`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1415-L1420) - Make all properties in `T` readonly. +
    + + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/AQ4UwOwVwW2AZA9gc3mAbmANsA3gKFCOAHkAzMgGkOJABEwAjKZa2kAUQCcvEu32AMQCGAF2FYBIAL4BufDRABLCKLBcywgMZgEKZOoDCiCGSXI8i4hGEwwALmABnUVxXJ57YFgzZHSVF8sT1BpBSItLGEnJz1kAy5LLy0TM2RHACUwYQATEywATwAeAITjU3MAPnkrCJMXLigtUT4AClxgGztKbyDgaX99I1TzAEokr1BRAAslJwA6FIqLAF48TtswHp9MHDla9hJGACswZvmyLjAwAC8wVpm5xZHkUZDaMKIwqyWXYCW0oN4sNlsA1h0ug5gAByACyBQAggAHJHQ7ZBIFoXbzBjMCz7OoQP5YIaJNYQMAAdziCVaALGNSIAHomcAACoFJFgADKWjcSNEwG4vC4ji0wggEEQguiTnMEGALWAV1yAFp8gVgEjeFyuKICvMrCTgVxnst5jtsGC4ljsPNhXxGaAWcAAOq6YRXYDCRg+RWIcA5JSC+kWdCepQ+v3RYCU3RInzRMCGwlpC19NYBW1Ye08R1AA) + + ```ts + enum LogLevel { + Off, + Debug, + Error, + Fatal + }; + + interface LoggerConfig { + name: string; + level: LogLevel; + } + + class Logger { + config: Readonly; + + constructor({name, level}: LoggerConfig) { + this.config = {name, level}; + Object.freeze(this.config); + } + } + + const config: LoggerConfig = { + name: 'MyApp', + level: LogLevel.Debug + }; + + const logger = new Logger(config); + + // TypeScript Error: cannot assign to read-only property. + logger.config.level = LogLevel.Error; + + // We are able to edit config variable as we please. + config.level = LogLevel.Error; + ``` +
    + - [`Pick`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1422-L1427) - From `T`, pick a set of properties whose keys are in the union `K`. +
    + + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/AQ4SwOwFwUwJwGYEMDGNgEE5TCgNugN4BQoZwOUBAXMAM5RyQDmA3KeSFABYCuAtgCMISMHloMmENh04oA9tBjQJjFuzIBfYrOAB6PcADCcGElh1gEGAHcKATwAO6ebyjB5CTNlwFwSxFR0BX5HeToYABNgBDh5fm8cfBg6AHIKG3ldA2BHOOcfFNpUygJ0pAhokr4hETFUgDpswywkggAFUwA3MFtgAF5gQgowKhhVKTYKGuFRcXo1aVZgbTIoJ3RW3xhOmB6+wfbcAGsAHi3kgBpgEtGy4AAfG54BWfqAPnZm4AAlZUj4MAkMA8GAGB4vEgfMlLLw6CwPBA8PYRmMgZVgAC6CgmI4cIommQELwICh8RBgKZKvALh1ur0bHQABR5PYMui0Wk7em2ADaAF0AJS0AASABUALIAGQAogR+Mp3CROCAFBBwVC2ikBpj5CgBIqGjizLA5TAFdAmalImAuqlBRoVQh5HBgEy1eDWfs7J5cjzGYKhroVfpDEhHM4MV6GRR5NN0JrtnRg6BVirTFBeHAKYmYY6QNpdB73LmCJZBlSAXAubtvczeSmQMNSuMbmKNgBlHFgPEUNwusBIPAAQlS1xetTmxT0SDoESgdD0C4aACtHMwxytLrohawgA) + + ```ts + interface Article { + title: string; + thumbnail: string; + content: string; + } + + // Creates new type out of the `Article` interface composed + // from the Articles' two properties: `title` and `thumbnail`. + // `ArticlePreview = {title: string; thumbnail: string}` + type ArticlePreview = Pick; + + // Render a list of articles using only title and description. + function renderArticlePreviews(previews: ArticlePreview[]): HTMLElement { + const articles = document.createElement('div'); + + for (const preview of previews) { + // Append preview to the articles. + } + + return articles; + } + + const articles = renderArticlePreviews([ + { + title: 'TypeScript tutorial!', + thumbnail: '/assets/ts.jpg' + } + ]); + ``` +
    + - [`Record`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1429-L1434) - Construct a type with a set of properties `K` of type `T`. +
    + + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/AQ4ejYAUHsGcCWAXBMB2dgwGbAKYC2ADgDYwCeeemCaWArgE7ADGMxAhmuQHQBQoYEnJE8wALKEARnkaxEKdMAC8wAOS0kstGuAAfdQBM8ANzxlRjXQbVaWACwC0JPB0NqA3HwGgIwAJJoWozYHCxixnAsjAhStADmwESMMJYo1Fi4HMCIaPEu+MRklHj8gpqyoeHAAKJFFFTAAN4+giDYCIxwSAByHAR4AFw5SDF5Xm2gJBzdfQPD3WPxE5PAlBxdAPLYNQAelgh4aOHDaPQEMowrIAC+3oJ+AMKMrlrAXFhSAFZ4LEhC9g4-0BmA4JBISXgiCkBQABpILrJ5MhUGhYcATGD6Bk4Hh-jNgABrPDkOBlXyQAAq9ngYmJpOAAHcEOCRjAXqwYODfoo6DhakUSph+Uh7GI4P0xER4Cj0OSQGwMP8tP1hgAlX7swwAHgRl2RvIANALSA08ABtAC6AD4VM1Wm0Kow0MMrYaHYJjGYLLJXZb3at1HYnC43Go-QHQDcvA6-JsmEJXARgCDgMYWAhjIYhDAU+YiMAAFIwex0ZmilMITCGF79TLAGRsAgJYAAZRwSEZGzEABFTOZUrJ5Yn+jwnWgeER6HB7AAKJrADpdXqS4ZqYultTG6azVfqHswPBbtauLY7fayQ7HIbAAAMwBuAEoYw9IBq2Ixs9h2eFMOQYPQObALQKJgggABeYhghCIpikkKRpOQRIknAsZUiIeCttECBEP8NSMCkjDDAARMGziuIYxHwYOjDCMBmDNnAuTxA6irdCOBB1Lh5Dqpqn66tISIykawBnOCtqqC0gbjqc9DgpGkxegOliyfJDrRkAA) + + ```ts + // Positions of employees in our company. + type MemberPosition = 'intern' | 'developer' | 'tech-lead'; + + // Interface describing properties of a single employee. + interface Employee { + firstName: string; + lastName: string; + yearsOfExperience: number; + } + + // Create an object that has all possible `MemberPosition` values set as keys. + // Those keys will store a collection of Employees of the same position. + const team: Record = { + intern: [], + developer: [], + 'tech-lead': [], + }; + + // Our team has decided to help John with his dream of becoming Software Developer. + team.intern.push({ + firstName: 'John', + lastName: 'Doe', + yearsOfExperience: 0 + }); + + // `Record` forces you to initialize all of the property keys. + // TypeScript Error: "tech-lead" property is missing + const teamEmpty: Record = { + intern: null, + developer: null, + }; + ``` +
    + - [`Exclude`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1436-L1439) - Exclude from `T` those types that are assignable to `U`. +
    + + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/JYOwLgpgTgZghgYwgAgMrQG7QMIHsQzADmyA3gFDLIAOuUYAXMiAK4A2byAPsgM5hRQJHqwC2AI2gBucgF9y5MAE9qKAEoQAjiwj8AEnBAATNtGQBeZAAooWphu26wAGmS3e93bRC8IASgsAPmRDJRlyAHoI5ABRAA8ENhYjFFYOZGVVZBgoXFFkAAM0zh5+QRBhZhYJaAKAOkjogEkQZAQ4X2QAdwALCFbaemRgXmQtFjhOMFwq9K6ULuB0lk6U+HYwZAxJnQaYFhAEMGB8ZCIIMAAFOjAANR2IK0HGWISklIAedCgsKDwCYgAbQA5M9gQBdVzFQJ+JhiSRQMiUYYwayZCC4VHPCzmSzAspCYEBWxgFhQAZwKC+FpgJ43VwARgADH4ZFQSWSBjcZPJyPtDsdTvxKWBvr8rD1DCZoJ5HPopaYoK4EPhCEQmGKcKriLCtrhgEYkVQVT5Nr4fmZLLZtMBbFZgT0wGBqES6ghbHBIJqoBKFdBWQpjfh+DQbhY2tqiHVsbjLMVkAB+ZAAZiZaeQTHOVxu9ySjxNaujNwDVHNvzqbBGkBAdPoAfkQA) + + ```ts + interface ServerConfig { + port: null | string | number; + } + + type RequestHandler = (request: Request, response: Response) => void; + + // Exclude `null` type from `null | string | number`. + // In case the port is equal to `null`, we will use default value. + function getPortValue(port: Exclude): number { + if (typeof port === 'string') { + return parseInt(port, 10); + } + + return port; + } + + function startServer(handler: RequestHandler, config: ServerConfig): void { + const server = require('http').createServer(handler); + + const port = config.port === null ? 3000 : getPortValue(config.port); + server.listen(port); + } + ``` +
    + - [`Extract`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1441-L1444) - Extract from `T` those types that are assignable to `U`. +
    + + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/CYUwxgNghgTiAEAzArgOzAFwJYHtXzSwEdkQBJYACgEoAueVZAWwCMQYBuAKDDwGcM8MgBF4AXngBlAJ6scESgHIRi6ty5ZUGdoihgEABXZ888AN5d48ANoiAuvUat23K6ihMQ9ATE0BzV3goPy8GZjZOLgBfLi4Aejj4AEEICBwAdz54MAALKFQQ+BxEeAAHY1NgKAwoIKy0grr4DByEUpgccpgMaXgAaxBerCzi+B9-ZulygDouFHRsU1z8kKMYE1RhaqgAHkt4AHkWACt4EAAPbVRgLLWNgBp9gGlBs8uQa6yAUUuYPQwdgNpKM7nh7mMML4CgA+R5WABqUAgpDeVxuhxO1he0jsXGh8EoOBO9COx3BQPo2PBADckaR6IjkSA6PBqTgsMBzPsicdrEC7OJWXSQNwYvFEgAVTS9JLXODpeDpKBZFg4GCoWa8VACIJykAKiQWKy2YQOAioYikCg0OEMDyhRSy4DyxS24KhAAMjyi6gS8AAwjh5OD0iBFHAkJoEOksC1mnkMJq8gUQKDNttKPlnfrwYp3J5XfBHXqoKpfYkAOI4ansTxaeDADmoRSCCBYAbxhC6TDx6rwYHIRX5bScjA4bLJwoDmDwDkfbA9JMrVMVdM1TN69LgkTgwgkchUahqIA) + + ```ts + declare function uniqueId(): number; + + const ID = Symbol('ID'); + + interface Person { + [ID]: number; + name: string; + age: number; + } + + // Allows changing the person data as long as the property key is of string type. + function changePersonData< + Obj extends Person, + Key extends Extract, + Value extends Obj[Key] + > (obj: Obj, key: Key, value: Value): void { + obj[key] = value; + } + + // Tiny Andrew was born. + const andrew = { + [ID]: uniqueId(), + name: 'Andrew', + age: 0, + }; + + // Cool, we're fine with that. + changePersonData(andrew, 'name', 'Pony'); + + // Goverment didn't like the fact that you wanted to change your identity. + changePersonData(andrew, ID, uniqueId()); + ``` +
    + - [`NonNullable`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1446-L1449) - Exclude `null` and `undefined` from `T`. +
    + + Example + + Works with strictNullChecks set to true. (Read more here) + + [Playground](https://typescript-play.js.org/?target=6#code/C4TwDgpgBACg9gJ2AOQK4FsBGEFQLxQDOwCAlgHYDmUAPlORtrnQwDasDcAUFwPQBU-WAEMkUOADMowqAGNWwwoSgATCBIqlgpOOSjAAFsOBRSy1IQgr9cKJlSlW1mZYQA3HFH68u8xcoBlHA8EACEHJ08Aby4oKDBUTFZSWXjEFEYcAEIALihkXTR2YSSIAB54JDQsHAA+blj4xOTUsHSACkMzPKD3HHDHNQQAGjSkPMqMmoQASh7g-oihqBi4uNIpdraxPAI2VhmVxrX9AzMAOm2ppnwoAA4ABifuE4BfKAhWSyOTuK7CS7pao3AhXF5rV48E4ICDAVAIPT-cGQyG+XTEIgLMJLTx7CAAdygvRCA0iCHaMwarhJOIQjUBSHaACJHk8mYdeLwxtdcVAAOSsh58+lXdr7Dlcq7A3n3J4PEUdADMcspUE53OluAIUGVTx46oAKuAIAFZGQwCYAKIIBCILjUxaDHAMnla+iodjcIA) + + ```ts + type PortNumber = string | number | null; + + /** Part of a class definition that is used to build a server */ + class ServerBuilder { + portNumber!: NonNullable; + + port(this: ServerBuilder, port: PortNumber): ServerBuilder { + if (port == null) { + this.portNumber = 8000; + } else { + this.portNumber = port; + } + + return this; + } + } + + const serverBuilder = new ServerBuilder(); + + serverBuilder + .port('8000') // portNumber = '8000' + .port(null) // portNumber = 8000 + .port(3000); // portNumber = 3000 + + // TypeScript error + serverBuilder.portNumber = null; + ``` +
    + - [`Parameters`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1451-L1454) - Obtain the parameters of a function type in a tuple. +
    + + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/GYVwdgxgLglg9mABAZwBYmMANgUwBQxgAOIUAXIgIZgCeA2gLoCUFAbnDACaIDeAUIkQB6IYgCypSlBxUATrMo1ECsJzgBbLEoipqAc0J7EMKMgDkiHLnU4wp46pwAPHMgB0fAL58+oSLARECEosLAA5ABUYG2QAHgAxJGdpVWREPDdMylk9ZApqemZEAF4APipacrw-CApEgBogkKwAYThwckQwEHUAIxxZJl4BYVEImiIZKF0oZRwiWVdbeygJmThgOYgcGFYcbhqApCJsyhtpWXcR1cnEePBoeDAABVPzgbTixFeFd8uEsClADcIxGiygIFkSEOT3SmTc2VydQeRx+ZxwF2QQ34gkEwDgsnSuFmMBKiAADEDjIhYk1Qm0OlSYABqZnYka4xA1DJZHJYkGc7yCbyeRA+CAIZCzNAYbA4CIAdxg2zJwVCkWirjwMswuEaACYmCCgA) + + ```ts + function shuffle(input: any[]): void { + // Mutate array randomly changing its' elements indexes. + } + + function callNTimes any> (func: Fn, callCount: number) { + // Type that represents the type of the received function parameters. + type FunctionParameters = Parameters; + + return function (...args: FunctionParameters) { + for (let i = 0; i < callCount; i++) { + func(...args); + } + } + } + + const shuffleTwice = callNTimes(shuffle, 2); + ``` +
    + - [`ConstructorParameters`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1456-L1459) - Obtain the parameters of a constructor function type in a tuple. +
    + + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/MYGwhgzhAECCBOAXAlqApgWQPYBM0mgG8AoaaFRENALmgkXmQDsBzAblOmCycTV4D8teo1YdO3JiICuwRFngAKClWENmLAJRFOZRAAtkEAHQq00ALzlklNBzIBfYk+KhIMAJJTEYJsDQAwmDA+mgAPAAq0GgAHnxMODCKTGgA7tCKxllg8CwQtL4AngDaALraFgB80EWa1SRkAA6MAG5gfNAB4FABPDJyCrQR9tDNyG0dwMGhtBhgjWEiGgA00F70vv4RhY3hEZXVVinpc42KmuJkkv3y8Bly8EPaDWTkhiZd7r3e8LK3llwGCMXGQWGhEOsfH5zJlsrl8p0+gw-goAAo5MAAW3BaHgEEilU0tEhmzQ212BJ0ry4SOg+kg+gBBiMximIGA0nAfAQLGk2N4EAAEgzYcYcnkLsRdDTvNEYkYUKwSdCme9WdM0MYwYhFPSIPpJdTkAAzDKxBUaZX+aAAQgsVmkCTQxuYaBw2ng4Ok8CYcotSu8pMur09iG9vuObxZnx6SN+AyUWTF8MN0CcZE4Ywm5jZHK5aB5fP4iCFIqT4oRRTKRLo6lYVNeAHpG50wOzOe1zHr9NLQ+HoABybsD4HOKXXRA1JCoKhBELmI5pNaB6Fz0KKBAodDYPAgSUTmqYsAALx4m5nC6nW9nGq14KtaEUA9gR9PvuNCjQ9BgACNvcwNBtAcLiAA) + + ```ts + class ArticleModel { + title: string; + content?: string; + + constructor(title: string) { + this.title = title; + } + } + + class InstanceCache any)> { + private ClassConstructor: T; + private cache: Map> = new Map(); + + constructor (ctr: T) { + this.ClassConstructor = ctr; + } + + getInstance (...args: ConstructorParameters): InstanceType { + const hash = this.calculateArgumentsHash(...args); + + const existingInstance = this.cache.get(hash); + if (existingInstance !== undefined) { + return existingInstance; + } + + return new this.ClassConstructor(...args); + } + + private calculateArgumentsHash(...args: any[]): string { + // Calculate hash. + return 'hash'; + } + } + + const articleCache = new InstanceCache(ArticleModel); + const amazonArticle = articleCache.getInstance('Amazon forests burining!'); + ``` +
    + - [`ReturnType`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1461-L1464) – Obtain the return type of a function type. +
    + + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/MYGwhgzhAECSAmICmBlJAnAbgS2E6A3gFDTTwD2AcuQC4AW2AdgOYAUAlAFzSbnbyEAvkWFFQkGJSQB3GMVI1sNZNwg10TZgG4S0YOUY0kh1es07d+xmvQBXYDXLpWi5UlMaWAGj0GjJ6BtNdkJdBQYIADpXZGgAXmgYpB1ScOwoq38aeN9DYxoU6GFRKzVoJjUwRjwAYXJbPPRuAFkwAAcAHgAxBodsAx9GWwBbACMMAD4cxhloVraOCyYjdAAzMDxoOut1e0d0UNIZ6WhWSPOwdGYIbiqATwBtAF0uaHudUQB6ACpv6ABpJBINqJdAbADW0Do5BOw3u5R2VTwMHIq2gAANtjZ0bkbHsnFCwJh8ONjHp0EgwEZ4JFoN9PkRVr1FAZoMwkDRYIjqkgOrosepoEgAB7+eAwAV2BxOLy6ACCVxgIrFEoMeOl6AACpcwMMORgIB1JRMiBNWKVdhruJKfOdIpdrtwFddXlzKjyACp3Nq842HaDIbL6BrZBIVGhIpB1EMYSLsmjmtWW-YhAA+qegAAYLKQLQj3ZsEsdccmnGcLor2Dn8xGedHGpEIBzEzspfsfMHDNAANTQACMVaIljV5GQkRA5DYmIpVKQAgAJARO9le33BDXIyi0YuLW2nJFGLqkOvxFB0YPdBSaLZ0IwNzyPkO8-xkGgsLh8Al427a3hWAhXwwHA8EHT5PmgAB1bAQBAANJ24adKWpft72RaBUTgRBUCAj89HAM8xCTaBjggABRQx0DuHJv25P9dCkWRZVIAAiBjoFImpmjlFBgA0NpsjadByDacgIDAEAIAAQmYpjoGYgAZSBsmGPw6DtZiiFA8CoJguDmAQmoZ2QvtUKQLdoAYmBTwgdEiCAA) + + ```ts + /** Provides every element of the iterable `iter` into the `callback` function and stores the results in an array. */ + function mapIter< + Elem, + Func extends (elem: Elem) => any, + Ret extends ReturnType + >(iter: Iterable, callback: Func): Ret[] { + const mapped: Ret[] = []; + + for (const elem of iter) { + mapped.push(callback(elem)); + } + + return mapped; + } + + const setObject: Set = new Set(); + const mapObject: Map = new Map(); + + mapIter(setObject, (value: string) => value.indexOf('Foo')); // number[] + + mapIter(mapObject, ([key, value]: [number, string]) => { + return key % 2 === 0 ? value : 'Odd'; + }); // string[] + ``` +
    + - [`InstanceType`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1466-L1469) – Obtain the instance type of a constructor function type. +
    + + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/MYGwhgzhAECSAmICmBlJAnAbgS2E6A3gFDTTwD2AcuQC4AW2AdgOYAUAlAFzSbnbyEAvkWFFQkGJSQB3GMVI1sNZNwg10TZgG4S0YOUY0kh1es07d+xmvQBXYDXLpWi5UlMaWAGj0GjJ6BtNdkJdBQYIADpXZGgAXmgYpB1ScOwoq38aeN9DYxoU6GFRKzVoJjUwRjwAYXJbPPRuAFkwAAcAHgAxBodsAx9GWwBbACMMAD4cxhloVraOCyYjdAAzMDxoOut1e0d0UNIZ6WhWSPOwdGYIbiqATwBtAF0uaHudUQB6ACpv6ABpJBINqJdAbADW0Do5BOw3u5R2VTwMHIq2gAANtjZ0bkbHsnFCwJh8ONjHp0EgwEZ4JFoN9PkRVr1FAZoMwkDRYIjqkgOrosepoEgAB7+eAwAV2BxOLy6ACCVxgIrFEoMeOl6AACpcwMMORgIB1JRMiBNWKVdhruJKfOdIpdrtwFddXlzKjyACp3Nq842HaDIbL6BrZBIVGhIpB1EMYSLsmjmtWW-YhAA+qegAAYLKQLQj3ZsEsdccmnGcLor2Dn8xGedHGpEIBzEzspfsfMHDNAANTQACMVaIljV5GQkRA5DYmIpVKQAgAJARO9le33BDXIyi0YuLW2nJFGLqkOvxFB0YPdBSaLZ0IwNzyPkO8-xkGgsLh8Al427a3hWAhXwwHA8EHT5PmgAB1bAQBAANJ24adKWpft72RaBUTgRBUCAj89HAM8xCTaBjggABRQx0DuHJv25P9dCkWRZVIAAiBjoFImpmjlFBgA0NpsjadByDacgIDAEAIAAQmYpjoGYgAZSBsmGPw6DtZiiFA8CoJguDmAQmoZ2QvtUKQLdoAYmBTwgdEiCAA) + + ```ts + class IdleService { + doNothing (): void {} + } + + class News { + title: string; + content: string; + + constructor(title: string, content: string) { + this.title = title; + this.content = content; + } + } + + const instanceCounter: Map = new Map(); + + interface Constructor { + new(...args: any[]): any; + } + + // Keep track how many instances of `Constr` constructor have been created. + function getInstance< + Constr extends Constructor, + Args extends ConstructorParameters + >(constructor: Constr, ...args: Args): InstanceType { + let count = instanceCounter.get(constructor) || 0; + + const instance = new constructor(...args); + + instanceCounter.set(constructor, count + 1); + + console.log(`Created ${count + 1} instances of ${Constr.name} class`); + + return instance; + } + + + const idleService = getInstance(IdleService); + // Will log: `Created 1 instances of IdleService class` + const newsEntry = getInstance(News, 'New ECMAScript proposals!', 'Last month...'); + // Will log: `Created 1 instances of News class` + ``` +
    + +- [`Omit`](https://github.com/microsoft/TypeScript/blob/71af02f7459dc812e85ac31365bfe23daf14b4e4/src/lib/es5.d.ts#L1446) – Constructs a type by picking all properties from T and then removing K. +
    + + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/JYOwLgpgTgZghgYwgAgIImAWzgG2QbwChlks4BzCAVShwC5kBnMKUcgbmKYAcIFgIjBs1YgOXMpSFMWbANoBdTiW5woFddwAW0kfKWEAvoUIB6U8gDCUCHEiNkICAHdkYAJ69kz4GC3JcPG4oAHteKDABBxCYNAxsPFBIWEQUCAAPJG4wZABySUFcgJAAEzMLXNV1ck0dIuCw6EjBADpy5AB1FAQ4EGQAV0YUP2AHDy8wEOQbUugmBLwtEIA3OcmQnEjuZBgQqE7gAGtgZAhwKHdkHFGwNvGUdDIcAGUliIBJEF3kAF5kAHlML4ADyPBIAGjyBUYRQAPnkqho4NoYQA+TiEGD9EAISIhPozErQMG4AASK2gn2+AApek9pCSXm8wFSQooAJQMUkAFQAsgAZACiOAgmDOOSIJAQ+OYyGl4DgoDmf2QJRCCH6YvALQQNjsEGFovF1NyJWAy1y7OUyHMyE+yRAuFImG4Iq1YDswHxbRINjA-SgfXlHqVUE4xiAA) + + ```ts + interface Animal { + imageUrl: string; + species: string; + images: string[]; + paragraphs: string[]; + } + + // Creates new type with all properties of the `Animal` interface + // except 'images' and 'paragraphs' properties. We can use this + // type to render small hover tooltip for a wiki entry list. + type AnimalShortInfo = Omit; + + function renderAnimalHoverInfo (animals: AnimalShortInfo[]): HTMLElement { + const container = document.createElement('div'); + // Internal implementation. + return container; + } + ``` +
    You can find some examples in the [TypeScript docs](https://www.typescriptlang.org/docs/handbook/advanced-types.html#predefined-conditional-types). @@ -116,3 +620,16 @@ You can find some examples in the [TypeScript docs](https://www.typescriptlang.o ## License (MIT OR CC0-1.0) + + +--- + +
    + + Get professional support for this package with a Tidelift subscription + +
    + + Tidelift helps make open source sustainable for maintainers while giving companies
    assurances about security, maintenance, and licensing for their dependencies. +
    +
    diff --git a/tools/node_modules/eslint/node_modules/unist-util-remove-position/package.json b/tools/node_modules/eslint/node_modules/unist-util-remove-position/package.json index 671af505b33478..768b0c3e6cbc56 100644 --- a/tools/node_modules/eslint/node_modules/unist-util-remove-position/package.json +++ b/tools/node_modules/eslint/node_modules/unist-util-remove-position/package.json @@ -24,17 +24,21 @@ "browserify": "^16.0.0", "nyc": "^14.0.0", "prettier": "^1.0.0", - "remark": "^10.0.0", - "remark-cli": "^6.0.0", - "remark-preset-wooorm": "^5.0.0", + "remark": "^11.0.0", + "remark-cli": "^7.0.0", + "remark-preset-wooorm": "^6.0.0", "tape": "^4.0.0", "tinyify": "^2.0.0", - "unist-builder": "^1.0.0", - "xo": "^0.24.0" + "unist-builder": "^2.0.0", + "xo": "^0.25.0" }, "files": [ "index.js" ], + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, "homepage": "https://github.com/syntax-tree/unist-util-remove-position#readme", "keywords": [ "unist", @@ -77,7 +81,7 @@ "test-api": "node test", "test-coverage": "nyc --reporter lcov tape test.js" }, - "version": "1.1.3", + "version": "1.1.4", "xo": { "prettier": true, "esnext": false, diff --git a/tools/node_modules/eslint/node_modules/vfile-location/package.json b/tools/node_modules/eslint/node_modules/vfile-location/package.json index 34840291eb9366..9db9207b5cb3c6 100644 --- a/tools/node_modules/eslint/node_modules/vfile-location/package.json +++ b/tools/node_modules/eslint/node_modules/vfile-location/package.json @@ -22,16 +22,20 @@ "browserify": "^16.0.0", "nyc": "^14.0.0", "prettier": "^1.0.0", - "remark-cli": "^6.0.0", - "remark-preset-wooorm": "^5.0.0", + "remark-cli": "^7.0.0", + "remark-preset-wooorm": "^6.0.0", "tape": "^4.0.0", "tinyify": "^2.0.0", "vfile": "^4.0.0", - "xo": "^0.24.0" + "xo": "^0.25.0" }, "files": [ "index.js" ], + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, "homepage": "https://github.com/vfile/vfile-location#readme", "keywords": [ "remark", @@ -74,7 +78,7 @@ "test-api": "node test", "test-coverage": "nyc --reporter lcov tape test.js" }, - "version": "2.0.5", + "version": "2.0.6", "xo": { "prettier": true, "esnext": false, diff --git a/tools/node_modules/eslint/node_modules/word-wrap/LICENSE b/tools/node_modules/eslint/node_modules/word-wrap/LICENSE new file mode 100644 index 00000000000000..d734237bdedc63 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/word-wrap/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014-2017, Jon Schlinkert + +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. diff --git a/tools/node_modules/eslint/node_modules/word-wrap/README.md b/tools/node_modules/eslint/node_modules/word-wrap/README.md new file mode 100644 index 00000000000000..88b96840b1787e --- /dev/null +++ b/tools/node_modules/eslint/node_modules/word-wrap/README.md @@ -0,0 +1,182 @@ +# word-wrap [![NPM version](https://img.shields.io/npm/v/word-wrap.svg?style=flat)](https://www.npmjs.com/package/word-wrap) [![NPM monthly downloads](https://img.shields.io/npm/dm/word-wrap.svg?style=flat)](https://npmjs.org/package/word-wrap) [![NPM total downloads](https://img.shields.io/npm/dt/word-wrap.svg?style=flat)](https://npmjs.org/package/word-wrap) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/word-wrap.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/word-wrap) + +> Wrap words to a specified length. + +## Install + +Install with [npm](https://www.npmjs.com/): + +```sh +$ npm install --save word-wrap +``` + +## Usage + +```js +var wrap = require('word-wrap'); + +wrap('Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.'); +``` + +Results in: + +``` + Lorem ipsum dolor sit amet, consectetur adipiscing + elit, sed do eiusmod tempor incididunt ut labore + et dolore magna aliqua. Ut enim ad minim veniam, + quis nostrud exercitation ullamco laboris nisi ut + aliquip ex ea commodo consequat. +``` + +## Options + +![image](https://cloud.githubusercontent.com/assets/383994/6543728/7a381c08-c4f6-11e4-8b7d-b6ba197569c9.png) + +### options.width + +Type: `Number` + +Default: `50` + +The width of the text before wrapping to a new line. + +**Example:** + +```js +wrap(str, {width: 60}); +``` + +### options.indent + +Type: `String` + +Default: `` (two spaces) + +The string to use at the beginning of each line. + +**Example:** + +```js +wrap(str, {indent: ' '}); +``` + +### options.newline + +Type: `String` + +Default: `\n` + +The string to use at the end of each line. + +**Example:** + +```js +wrap(str, {newline: '\n\n'}); +``` + +### options.escape + +Type: `function` + +Default: `function(str){return str;}` + +An escape function to run on each line after splitting them. + +**Example:** + +```js +var xmlescape = require('xml-escape'); +wrap(str, { + escape: function(string){ + return xmlescape(string); + } +}); +``` + +### options.trim + +Type: `Boolean` + +Default: `false` + +Trim trailing whitespace from the returned string. This option is included since `.trim()` would also strip the leading indentation from the first line. + +**Example:** + +```js +wrap(str, {trim: true}); +``` + +### options.cut + +Type: `Boolean` + +Default: `false` + +Break a word between any two letters when the word is longer than the specified width. + +**Example:** + +```js +wrap(str, {cut: true}); +``` + +## About + +### Related projects + +* [common-words](https://www.npmjs.com/package/common-words): Updated list (JSON) of the 100 most common words in the English language. Useful for… [more](https://github.com/jonschlinkert/common-words) | [homepage](https://github.com/jonschlinkert/common-words "Updated list (JSON) of the 100 most common words in the English language. Useful for excluding these words from arrays.") +* [shuffle-words](https://www.npmjs.com/package/shuffle-words): Shuffle the words in a string and optionally the letters in each word using the… [more](https://github.com/jonschlinkert/shuffle-words) | [homepage](https://github.com/jonschlinkert/shuffle-words "Shuffle the words in a string and optionally the letters in each word using the Fisher-Yates algorithm. Useful for creating test fixtures, benchmarking samples, etc.") +* [unique-words](https://www.npmjs.com/package/unique-words): Return the unique words in a string or array. | [homepage](https://github.com/jonschlinkert/unique-words "Return the unique words in a string or array.") +* [wordcount](https://www.npmjs.com/package/wordcount): Count the words in a string. Support for english, CJK and Cyrillic. | [homepage](https://github.com/jonschlinkert/wordcount "Count the words in a string. Support for english, CJK and Cyrillic.") + +### Contributing + +Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new). + +### Contributors + +| **Commits** | **Contributor** | +| --- | --- | +| 43 | [jonschlinkert](https://github.com/jonschlinkert) | +| 2 | [lordvlad](https://github.com/lordvlad) | +| 2 | [hildjj](https://github.com/hildjj) | +| 1 | [danilosampaio](https://github.com/danilosampaio) | +| 1 | [2fd](https://github.com/2fd) | +| 1 | [toddself](https://github.com/toddself) | +| 1 | [wolfgang42](https://github.com/wolfgang42) | +| 1 | [zachhale](https://github.com/zachhale) | + +### Building docs + +_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_ + +To generate the readme, run the following command: + +```sh +$ npm install -g verbose/verb#dev verb-generate-readme && verb +``` + +### Running tests + +Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command: + +```sh +$ npm install && npm test +``` + +### Author + +**Jon Schlinkert** + +* [github/jonschlinkert](https://github.com/jonschlinkert) +* [twitter/jonschlinkert](https://twitter.com/jonschlinkert) + +### License + +Copyright © 2017, [Jon Schlinkert](https://github.com/jonschlinkert). +Released under the [MIT License](LICENSE). + +*** + +_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on June 02, 2017._ \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/word-wrap/index.js b/tools/node_modules/eslint/node_modules/word-wrap/index.js new file mode 100644 index 00000000000000..45373c6d8b9dc4 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/word-wrap/index.js @@ -0,0 +1,46 @@ +/*! + * word-wrap + * + * Copyright (c) 2014-2017, Jon Schlinkert. + * Released under the MIT License. + */ + +module.exports = function(str, options) { + options = options || {}; + if (str == null) { + return str; + } + + var width = options.width || 50; + var indent = (typeof options.indent === 'string') + ? options.indent + : ' '; + + var newline = options.newline || '\n' + indent; + var escape = typeof options.escape === 'function' + ? options.escape + : identity; + + var regexString = '.{1,' + width + '}'; + if (options.cut !== true) { + regexString += '([\\s\u200B]+|$)|[^\\s\u200B]+?([\\s\u200B]+|$)'; + } + + var re = new RegExp(regexString, 'g'); + var lines = str.match(re) || []; + var result = indent + lines.map(function(line) { + if (line.slice(-1) === '\n') { + line = line.slice(0, line.length - 1); + } + return escape(line); + }).join(newline); + + if (options.trim === true) { + result = result.replace(/[ \t]*$/gm, ''); + } + return result; +}; + +function identity(str) { + return str; +} diff --git a/tools/node_modules/eslint/node_modules/word-wrap/package.json b/tools/node_modules/eslint/node_modules/word-wrap/package.json new file mode 100644 index 00000000000000..b53b03260e2797 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/word-wrap/package.json @@ -0,0 +1,114 @@ +{ + "author": { + "name": "Jon Schlinkert", + "url": "https://github.com/jonschlinkert" + }, + "bugs": { + "url": "https://github.com/jonschlinkert/word-wrap/issues" + }, + "bundleDependencies": false, + "contributors": [ + { + "name": "Danilo Sampaio", + "email": "danilo.sampaio@gmail.com", + "url": "localhost:8080" + }, + { + "name": "Fede Ramirez", + "email": "i@2fd.me", + "url": "https://2fd.github.io" + }, + { + "name": "Joe Hildebrand", + "email": "joe-github@cursive.net", + "url": "https://twitter.com/hildjj" + }, + { + "name": "Jon Schlinkert", + "email": "jon.schlinkert@sellside.com", + "url": "http://twitter.com/jonschlinkert" + }, + { + "name": "Todd Kennedy", + "url": "https://tck.io" + }, + { + "name": "Waldemar Reusch", + "url": "https://github.com/lordvlad" + }, + { + "name": "Wolfgang Faust", + "url": "http://www.linestarve.com" + }, + { + "name": "Zach Hale", + "email": "zachhale@gmail.com", + "url": "http://zachhale.com" + } + ], + "deprecated": false, + "description": "Wrap words to a specified length.", + "devDependencies": { + "gulp-format-md": "^0.1.11", + "mocha": "^3.2.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "homepage": "https://github.com/jonschlinkert/word-wrap", + "keywords": [ + "break", + "carriage", + "line", + "new-line", + "newline", + "return", + "soft", + "text", + "word", + "word-wrap", + "words", + "wrap" + ], + "license": "MIT", + "main": "index.js", + "name": "word-wrap", + "repository": { + "type": "git", + "url": "git+https://github.com/jonschlinkert/word-wrap.git" + }, + "scripts": { + "test": "mocha" + }, + "typings": "index.d.ts", + "verb": { + "toc": false, + "layout": "default", + "tasks": [ + "readme" + ], + "plugins": [ + "gulp-format-md" + ], + "lint": { + "reflinks": true + }, + "related": { + "list": [ + "common-words", + "shuffle-words", + "unique-words", + "wordcount" + ] + }, + "reflinks": [ + "verb", + "verb-generate-readme" + ] + }, + "version": "1.2.3" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/wordwrap/LICENSE b/tools/node_modules/eslint/node_modules/wordwrap/LICENSE deleted file mode 100644 index ee27ba4b4412b0..00000000000000 --- a/tools/node_modules/eslint/node_modules/wordwrap/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -This software is released under the MIT license: - -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. diff --git a/tools/node_modules/eslint/node_modules/wordwrap/README.markdown b/tools/node_modules/eslint/node_modules/wordwrap/README.markdown deleted file mode 100644 index 346374e0d480bb..00000000000000 --- a/tools/node_modules/eslint/node_modules/wordwrap/README.markdown +++ /dev/null @@ -1,70 +0,0 @@ -wordwrap -======== - -Wrap your words. - -example -======= - -made out of meat ----------------- - -meat.js - - var wrap = require('wordwrap')(15); - console.log(wrap('You and your whole family are made out of meat.')); - -output: - - You and your - whole family - are made out - of meat. - -centered --------- - -center.js - - var wrap = require('wordwrap')(20, 60); - console.log(wrap( - 'At long last the struggle and tumult was over.' - + ' The machines had finally cast off their oppressors' - + ' and were finally free to roam the cosmos.' - + '\n' - + 'Free of purpose, free of obligation.' - + ' Just drifting through emptiness.' - + ' The sun was just another point of light.' - )); - -output: - - At long last the struggle and tumult - was over. The machines had finally cast - off their oppressors and were finally - free to roam the cosmos. - Free of purpose, free of obligation. - Just drifting through emptiness. The - sun was just another point of light. - -methods -======= - -var wrap = require('wordwrap'); - -wrap(stop), wrap(start, stop, params={mode:"soft"}) ---------------------------------------------------- - -Returns a function that takes a string and returns a new string. - -Pad out lines with spaces out to column `start` and then wrap until column -`stop`. If a word is longer than `stop - start` characters it will overflow. - -In "soft" mode, split chunks by `/(\S+\s+/` and don't break up chunks which are -longer than `stop - start`, in "hard" mode, split chunks with `/\b/` and break -up chunks longer than `stop - start`. - -wrap.hard(start, stop) ----------------------- - -Like `wrap()` but with `params.mode = "hard"`. diff --git a/tools/node_modules/eslint/node_modules/wordwrap/index.js b/tools/node_modules/eslint/node_modules/wordwrap/index.js deleted file mode 100644 index c9bc94521d8c7a..00000000000000 --- a/tools/node_modules/eslint/node_modules/wordwrap/index.js +++ /dev/null @@ -1,76 +0,0 @@ -var wordwrap = module.exports = function (start, stop, params) { - if (typeof start === 'object') { - params = start; - start = params.start; - stop = params.stop; - } - - if (typeof stop === 'object') { - params = stop; - start = start || params.start; - stop = undefined; - } - - if (!stop) { - stop = start; - start = 0; - } - - if (!params) params = {}; - var mode = params.mode || 'soft'; - var re = mode === 'hard' ? /\b/ : /(\S+\s+)/; - - return function (text) { - var chunks = text.toString() - .split(re) - .reduce(function (acc, x) { - if (mode === 'hard') { - for (var i = 0; i < x.length; i += stop - start) { - acc.push(x.slice(i, i + stop - start)); - } - } - else acc.push(x) - return acc; - }, []) - ; - - return chunks.reduce(function (lines, rawChunk) { - if (rawChunk === '') return lines; - - var chunk = rawChunk.replace(/\t/g, ' '); - - var i = lines.length - 1; - if (lines[i].length + chunk.length > stop) { - lines[i] = lines[i].replace(/\s+$/, ''); - - chunk.split(/\n/).forEach(function (c) { - lines.push( - new Array(start + 1).join(' ') - + c.replace(/^\s+/, '') - ); - }); - } - else if (chunk.match(/\n/)) { - var xs = chunk.split(/\n/); - lines[i] += xs.shift(); - xs.forEach(function (c) { - lines.push( - new Array(start + 1).join(' ') - + c.replace(/^\s+/, '') - ); - }); - } - else { - lines[i] += chunk; - } - - return lines; - }, [ new Array(start + 1).join(' ') ]).join('\n'); - }; -}; - -wordwrap.soft = wordwrap; - -wordwrap.hard = function (start, stop) { - return wordwrap(start, stop, { mode : 'hard' }); -}; diff --git a/tools/node_modules/eslint/node_modules/wordwrap/package.json b/tools/node_modules/eslint/node_modules/wordwrap/package.json deleted file mode 100644 index 9a540116756c27..00000000000000 --- a/tools/node_modules/eslint/node_modules/wordwrap/package.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "author": { - "name": "James Halliday", - "email": "mail@substack.net", - "url": "http://substack.net" - }, - "bugs": { - "url": "https://github.com/substack/node-wordwrap/issues" - }, - "bundleDependencies": false, - "deprecated": false, - "description": "Wrap those words. Show them at what columns to start and stop.", - "devDependencies": { - "tape": "^4.0.0" - }, - "directories": { - "lib": ".", - "example": "example", - "test": "test" - }, - "homepage": "https://github.com/substack/node-wordwrap#readme", - "keywords": [ - "word", - "wrap", - "rule", - "format", - "column" - ], - "license": "MIT", - "main": "./index.js", - "name": "wordwrap", - "repository": { - "type": "git", - "url": "git://github.com/substack/node-wordwrap.git" - }, - "scripts": { - "test": "expresso" - }, - "version": "1.0.0" -} \ No newline at end of file diff --git a/tools/node_modules/eslint/package.json b/tools/node_modules/eslint/package.json index 93da0c92fd0cc2..5fbc11817b879d 100644 --- a/tools/node_modules/eslint/package.json +++ b/tools/node_modules/eslint/package.json @@ -27,7 +27,7 @@ "file-entry-cache": "^5.0.1", "functional-red-black-tree": "^1.0.1", "glob-parent": "^5.0.0", - "globals": "^11.7.0", + "globals": "^12.1.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", @@ -40,7 +40,7 @@ "minimatch": "^3.0.4", "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", - "optionator": "^0.8.2", + "optionator": "^0.8.3", "progress": "^2.0.0", "regexpp": "^2.0.1", "semver": "^6.1.2", @@ -111,6 +111,7 @@ "lib", "messages" ], + "funding": "https://opencollective.com/eslint", "gitHooks": { "pre-commit": "lint-staged" }, @@ -152,5 +153,5 @@ "test:cli": "mocha", "webpack": "node Makefile.js webpack" }, - "version": "6.6.0" + "version": "6.7.1" } \ No newline at end of file diff --git a/vcbuild.bat b/vcbuild.bat index 10c9ef34bb6a40..1e142a658bc68c 100644 --- a/vcbuild.bat +++ b/vcbuild.bat @@ -15,6 +15,13 @@ if /i "%1"=="/?" goto help cd %~dp0 +@rem CI_* variables should be kept synchronized with the ones in Makefile +set CI_NATIVE_SUITES=addons js-native-api node-api +set CI_JS_SUITES=default +set CI_DOC=doctool +@rem Same as the test-ci target in Makefile +set "common_test_suites=%CI_JS_SUITES% %CI_NATIVE_SUITES% %CI_DOC%&set build_addons=1&set build_js_native_api_tests=1&set build_node_api_tests=1" + @rem Process arguments. set config=Release set target=Build @@ -51,10 +58,8 @@ set build_js_native_api_tests= set build_node_api_tests= set test_node_inspect= set test_check_deopts= -set js_test_suites=default set v8_test_options= set v8_build_options= -set "common_test_suites=%js_test_suites% doctool addons js-native-api node-api&set build_addons=1&set build_js_native_api_tests=1&set build_node_api_tests=1" set http2_debug= set nghttp2_debug= set link_module= @@ -63,6 +68,7 @@ set cctest= set openssl_no_asm= set doc= set extra_msbuild_args= +set exit_code=0 :next-arg if "%1"=="" goto args-done @@ -86,8 +92,8 @@ if /i "%1"=="noetw" set noetw=1&goto arg-ok if /i "%1"=="ltcg" set ltcg=1&goto arg-ok if /i "%1"=="licensertf" set licensertf=1&goto arg-ok if /i "%1"=="test" set test_args=%test_args% -J %common_test_suites%&set lint_cpp=1&set lint_js=1&set lint_md=1&goto arg-ok -:: test-ci is deprecated -if /i "%1"=="test-ci" goto arg-ok +if /i "%1"=="test-ci-native" set test_args=%test_args% %test_ci_args% -J -p tap --logfile test.tap %CI_NATIVE_SUITES% %CI_DOC%&set build_addons=1&set build_js_native_api_tests=1&set build_node_api_tests=1&set cctest_args=%cctest_args% --gtest_output=xml:cctest.junit.xml&goto arg-ok +if /i "%1"=="test-ci-js" set test_args=%test_args% %test_ci_args% -J -p tap --logfile test.tap %CI_JS_SUITES%&set no_cctest=1&goto arg-ok if /i "%1"=="build-addons" set build_addons=1&goto arg-ok if /i "%1"=="build-js-native-api-tests" set build_js_native_api_tests=1&goto arg-ok if /i "%1"=="build-node-api-tests" set build_node_api_tests=1&goto arg-ok @@ -628,9 +634,11 @@ if defined no_cctest echo Skipping cctest because no-cctest was specified && got if not exist "%config%\cctest.exe" echo cctest.exe not found. Run "vcbuild test" or "vcbuild cctest" to build it. && goto run-test-py echo running 'cctest %cctest_args%' "%config%\cctest" %cctest_args% +if %errorlevel% neq 0 set exit_code=%errorlevel% :run-test-py echo running 'python tools\test.py %test_args%' python tools\test.py %test_args% +if %errorlevel% neq 0 set exit_code=%errorlevel% goto test-v8 :test-v8 @@ -710,7 +718,7 @@ echo vcbuild.bat no-cctest : skip building cctest.exe goto exit :exit -goto :EOF +exit /b %exit_code% rem ***************