diff --git a/.github/workflows/lint-release-proposal.yml b/.github/workflows/lint-release-proposal.yml
index ecda2b616c0d02..9d8ba5998a7a5c 100644
--- a/.github/workflows/lint-release-proposal.yml
+++ b/.github/workflows/lint-release-proposal.yml
@@ -19,6 +19,8 @@ permissions:
jobs:
lint-release-commit:
runs-on: ubuntu-latest
+ permissions:
+ pull-requests: read
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e7dab220d26dde..246f126f74c2d0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -39,7 +39,8 @@ release.
-23.6.0
+23.6.1
+23.6.0
23.5.0
23.4.0
23.3.0
@@ -48,7 +49,8 @@ release.
23.0.0
|
-22.13.0
+22.13.1
+22.13.0
22.12.0
22.11.0
22.10.0
@@ -66,7 +68,8 @@ release.
22.0.0
|
-20.18.1
+20.18.2
+20.18.1
20.18.0
20.17.0
20.16.0
@@ -97,7 +100,8 @@ release.
20.0.0
|
-18.20.5
+18.20.6
+18.20.5
18.20.4
18.20.3
18.20.2
diff --git a/benchmark/es/error-stack.js b/benchmark/es/error-stack.js
index 907f308ea41558..3b373dcdae63c8 100644
--- a/benchmark/es/error-stack.js
+++ b/benchmark/es/error-stack.js
@@ -2,13 +2,19 @@
const common = require('../common.js');
const modPath = require.resolve('../fixtures/simple-error-stack.js');
+const nodeModulePath = require.resolve('../fixtures/node_modules/error-stack/simple-error-stack.js');
+const Module = require('node:module');
const bench = common.createBenchmark(main, {
- method: ['without-sourcemap', 'sourcemap'],
+ method: [
+ 'without-sourcemap',
+ 'sourcemap',
+ 'node-modules-without-sourcemap',
+ 'node-module-sourcemap'],
n: [1e5],
});
-function runN(n) {
+function runN(n, modPath) {
delete require.cache[modPath];
const mod = require(modPath);
bench.start();
@@ -22,11 +28,23 @@ function main({ n, method }) {
switch (method) {
case 'without-sourcemap':
process.setSourceMapsEnabled(false);
- runN(n);
+ runN(n, modPath);
break;
case 'sourcemap':
process.setSourceMapsEnabled(true);
- runN(n);
+ runN(n, modPath);
+ break;
+ case 'node-modules-without-sourcemap':
+ Module.setSourceMapsSupport(true, {
+ nodeModules: false,
+ });
+ runN(n, nodeModulePath);
+ break;
+ case 'node-modules-sourcemap':
+ Module.setSourceMapsSupport(true, {
+ nodeModules: true,
+ });
+ runN(n, nodeModulePath);
break;
default:
throw new Error(`Unexpected method "${method}"`);
diff --git a/benchmark/fixtures/node_modules/error-stack/simple-error-stack.js b/benchmark/fixtures/node_modules/error-stack/simple-error-stack.js
new file mode 100644
index 00000000000000..33c3ad7f324d40
--- /dev/null
+++ b/benchmark/fixtures/node_modules/error-stack/simple-error-stack.js
@@ -0,0 +1,16 @@
+'use strict';
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.simpleErrorStack = simpleErrorStack;
+// Compile with `tsc --inlineSourceMap benchmark/fixtures/node_modules/error-stack/simple-error-stack.ts`.
+var lorem = '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. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.';
+function simpleErrorStack() {
+ [1].map(function () {
+ try {
+ lorem.BANG();
+ }
+ catch (e) {
+ return e.stack;
+ }
+ });
+}
+//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2ltcGxlLWVycm9yLXN0YWNrLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsic2ltcGxlLWVycm9yLXN0YWNrLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLFlBQVksQ0FBQzs7QUFpQlgsNENBQWdCO0FBZmxCLGlGQUFpRjtBQUVqRixJQUFNLEtBQUssR0FBRywrYkFBK2IsQ0FBQztBQUU5YyxTQUFTLGdCQUFnQjtJQUN2QixDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQztRQUNOLElBQUksQ0FBQztZQUNGLEtBQWEsQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUN4QixDQUFDO1FBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztZQUNYLE9BQU8sQ0FBQyxDQUFDLEtBQUssQ0FBQztRQUNqQixDQUFDO0lBQ0gsQ0FBQyxDQUFDLENBQUE7QUFDSixDQUFDIn0=
diff --git a/benchmark/fixtures/node_modules/error-stack/simple-error-stack.ts b/benchmark/fixtures/node_modules/error-stack/simple-error-stack.ts
new file mode 100644
index 00000000000000..58d3d7eedd2f1b
--- /dev/null
+++ b/benchmark/fixtures/node_modules/error-stack/simple-error-stack.ts
@@ -0,0 +1,19 @@
+'use strict';
+
+// Compile with `tsc --inlineSourceMap benchmark/fixtures/node_modules/error-stack/simple-error-stack.ts`.
+
+const lorem = '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. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.';
+
+function simpleErrorStack() {
+ [1].map(() => {
+ try {
+ (lorem as any).BANG();
+ } catch (e) {
+ return e.stack;
+ }
+ })
+}
+
+export {
+ simpleErrorStack,
+};
diff --git a/deps/ncrypto/ncrypto.cc b/deps/ncrypto/ncrypto.cc
index fa0cf58062d897..ce2e7b384eb198 100644
--- a/deps/ncrypto/ncrypto.cc
+++ b/deps/ncrypto/ncrypto.cc
@@ -1346,8 +1346,11 @@ DHPointer DHPointer::New(BignumPointer&& p, BignumPointer&& g) {
if (DH_set0_pqg(dh.get(), p.get(), nullptr, g.get()) != 1) return {};
// If the call above is successful, the DH object takes ownership of the
- // BIGNUMs, so we must release them here.
+ // BIGNUMs, so we must release them here. Unfortunately coverity does not
+ // know that so we need to tell it not to complain.
+ // coverity[resource_leak]
p.release();
+ // coverity[resource_leak]
g.release();
return dh;
@@ -1430,7 +1433,10 @@ DataPointer DHPointer::generateKeys() const {
size_t DHPointer::size() const {
if (!dh_) return 0;
- return DH_size(dh_.get());
+ int ret = DH_size(dh_.get());
+ // DH_size can return a -1 on error but we just want to return a 0
+ // in that case so we don't wrap around when returning the size_t.
+ return ret >= 0 ? static_cast(ret) : 0;
}
DataPointer DHPointer::computeSecret(const BignumPointer& peer) const {
@@ -1459,6 +1465,10 @@ DataPointer DHPointer::computeSecret(const BignumPointer& peer) const {
bool DHPointer::setPublicKey(BignumPointer&& key) {
if (!dh_) return false;
if (DH_set0_key(dh_.get(), key.get(), nullptr) == 1) {
+ // If DH_set0_key returns successfully, then dh_ takes ownership of the
+ // BIGNUM, so we must release it here. Unfortunately coverity does not
+ // know that so we need to tell it not to complain.
+ // coverity[resource_leak]
key.release();
return true;
}
@@ -1468,6 +1478,10 @@ bool DHPointer::setPublicKey(BignumPointer&& key) {
bool DHPointer::setPrivateKey(BignumPointer&& key) {
if (!dh_) return false;
if (DH_set0_key(dh_.get(), nullptr, key.get()) == 1) {
+ // If DH_set0_key returns successfully, then dh_ takes ownership of the
+ // BIGNUM, so we must release it here. Unfortunately coverity does not
+ // know that so we need to tell it not to complain.
+ // coverity[resource_leak]
key.release();
return true;
}
diff --git a/deps/uv/.mailmap b/deps/uv/.mailmap
index 97f5d1f2c004c9..f5d5375e044e18 100644
--- a/deps/uv/.mailmap
+++ b/deps/uv/.mailmap
@@ -52,6 +52,7 @@ San-Tai Hsu
Santiago Gimeno
Saúl Ibarra Corretgé
Saúl Ibarra Corretgé
+Saúl Ibarra Corretgé
Shigeki Ohtsu
Shuowang (Wayne) Zhang
TK-one
diff --git a/deps/uv/AUTHORS b/deps/uv/AUTHORS
index 041b7aff610f57..39550bbc535eb2 100644
--- a/deps/uv/AUTHORS
+++ b/deps/uv/AUTHORS
@@ -588,5 +588,7 @@ Raihaan Shouhell
Rialbat
Adam
Poul T Lomholt
-dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Thad House
+Julian A Avar C <28635807+julian-a-avar-c@users.noreply.github.com>
+amcgoogan <105525867+amcgoogan@users.noreply.github.com>
+Rafael Gonzaga
diff --git a/deps/uv/CMakeLists.txt b/deps/uv/CMakeLists.txt
index 28c6df25666967..af89db2dfc2762 100644
--- a/deps/uv/CMakeLists.txt
+++ b/deps/uv/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.9)
+cmake_minimum_required(VERSION 3.10)
if(POLICY CMP0091)
cmake_policy(SET CMP0091 NEW) # Enable MSVC_RUNTIME_LIBRARY setting
@@ -186,7 +186,7 @@ set(uv_sources
src/version.c)
if(WIN32)
- list(APPEND uv_defines WIN32_LEAN_AND_MEAN _WIN32_WINNT=0x0602 _CRT_DECLARE_NONSTDC_NAMES=0)
+ list(APPEND uv_defines WIN32_LEAN_AND_MEAN _WIN32_WINNT=0x0A00 _CRT_DECLARE_NONSTDC_NAMES=0)
list(APPEND uv_libraries
psapi
user32
@@ -667,6 +667,7 @@ if(LIBUV_BUILD_TESTS)
test/test-thread-affinity.c
test/test-thread-equal.c
test/test-thread.c
+ test/test-thread-name.c
test/test-thread-priority.c
test/test-threadpool-cancel.c
test/test-threadpool.c
diff --git a/deps/uv/ChangeLog b/deps/uv/ChangeLog
index dc2dd2790c57d3..006a9e1b415de9 100644
--- a/deps/uv/ChangeLog
+++ b/deps/uv/ChangeLog
@@ -1,4 +1,85 @@
-2024.10.18, Version 1.49.2 (Stable)
+2025.01.15, Version 1.50.0 (Stable)
+
+Changes since version 1.49.2:
+
+* ci: run macOS and iOS tests also on macOS 14 (Saúl Ibarra Corretgé)
+
+* unix,win: map ENOEXEC errno (Saúl Ibarra Corretgé)
+
+* test: skip multicast join test on ENOEXEC (Saúl Ibarra Corretgé)
+
+* ci: make sure the macOS firewall is disabled (Saúl Ibarra Corretgé)
+
+* darwin,test: squelch EBUSY error on multicast join (Saúl Ibarra Corretgé)
+
+* build: update minimum cmake to 3.10 (Ben Noordhuis)
+
+* kqueue: use EVFILT_USER for async if available (Jameson Nash)
+
+* unix,win: fix off-by-one in uv_wtf8_to_utf16() (Ben Noordhuis)
+
+* doc: add scala-native-loop to LINKS.md (Julian A Avar C)
+
+* unix: fix build breakage on haiku, openbsd, etc (Jeffrey H. Johnson)
+
+* kqueue: lower overhead in uv__io_check_fd (Andy Pan)
+
+* doc: move cjihrig back to active maintainers (cjihrig)
+
+* build(deps): bump actions/checkout from 3 to 4 (dependabot[bot])
+
+* unix,pipe: fix handling null buffer in uv_pipe_get{sock,peer}name (Saúl
+ Ibarra Corretgé)
+
+* unix,win: harmonize buffer checking (Saúl Ibarra Corretgé)
+
+* unix,win: add support for detached threads (Juan José Arboleda)
+
+* src: add uv_thread_set/getname() methods (Santiago Gimeno)
+
+* build: fix qemu builds (Ben Noordhuis)
+
+* win: drop support for windows 8 (Ben Noordhuis)
+
+* linux: fix uv_cpu_info() arm cpu model detection (Ben Noordhuis)
+
+* linux: always use io_uring for epoll batching (Ben Noordhuis)
+
+* doc: clarify repeating timer behavior more (Ben Noordhuis)
+
+* unix,win: handle nbufs=0 in uv_udp_try_send (Ben Noordhuis)
+
+* win: use GetQueuedCompletionStatusEx directly (Saúl Ibarra Corretgé)
+
+* win: enable uv_thread_{get,set}name on MinGW (Saúl Ibarra Corretgé)
+
+* win: drop support for the legacy MinGW (Saúl Ibarra Corretgé)
+
+* win,fs: get (most) fstat when no permission (Jameson Nash)
+
+* win: plug uv_fs_event_start memory leak (amcgoogan)
+
+* test: address FreeBSD kernel bug causing NULL path in fsevents (Juan José
+ Arboleda)
+
+* unix: refactor udp sendmsg code (Ben Noordhuis)
+
+* unix,win: add uv_udp_try_send2 (Ben Noordhuis)
+
+* test: fix flaky flaky udp_mmsg test (Juan José Arboleda)
+
+* build: enable fdsan in Android (Juan José Arboleda)
+
+* test: fix udp-multicast-join for FreeBSD (Juan José Arboleda)
+
+* win: fix leak processing fs event (Saúl Ibarra Corretgé)
+
+* src: set a default thread name for workers (Rafael Gonzaga)
+
+* misc: implement uv_getrusage_thread (Juan José Arboleda)
+
+
+2024.10.18, Version 1.49.2 (Stable), e1095c7a4373ce00cd8874d8e820de5afb25776e
Changes since version 1.49.1:
diff --git a/deps/uv/LINKS.md b/deps/uv/LINKS.md
index 3e5800747bc7dd..743935cebb8532 100644
--- a/deps/uv/LINKS.md
+++ b/deps/uv/LINKS.md
@@ -37,6 +37,7 @@
* [Pixie-io](https://github.com/pixie-io/pixie): Open-source observability tool for Kubernetes applications.
* [potion](https://github.com/perl11/potion)/[p2](https://github.com/perl11/p2): runtime
* [racer](https://libraries.io/rubygems/racer): Ruby web server written as an C extension
+* [scala-native-loop](https://github.com/scala-native/scala-native-loop): Extensible event loop and async-oriented IO for Scala Native; powered by libuv
* [Socket Runtime](https://sockets.sh): A runtime for creating native cross-platform software on mobile and desktop using HTML, CSS, and JavaScript
* [spider-gazelle](https://github.com/cotag/spider-gazelle): Ruby web server using libuv bindings
* [Suave](http://suave.io/): A simple web development F# library providing a lightweight web server and a set of combinators to manipulate route flow and task composition
diff --git a/deps/uv/MAINTAINERS.md b/deps/uv/MAINTAINERS.md
index 41c60cb383cfbe..ff8be88b7b7cd5 100644
--- a/deps/uv/MAINTAINERS.md
+++ b/deps/uv/MAINTAINERS.md
@@ -4,6 +4,9 @@ libuv is currently managed by the following individuals:
* **Ben Noordhuis** ([@bnoordhuis](https://github.com/bnoordhuis))
- GPG key: D77B 1E34 243F BAF0 5F8E 9CC3 4F55 C8C8 46AB 89B9 (pubkey-bnoordhuis)
+* **Colin Ihrig** ([@cjihrig](https://github.com/cjihrig))
+ - GPG key: 94AE 3667 5C46 4D64 BAFA 68DD 7434 390B DBE9 B9C5 (pubkey-cjihrig)
+ - GPG key: 5735 3E0D BDAA A7E8 39B6 6A1A FF47 D5E4 AD8B 4FDC (pubkey-cjihrig-kb)
* **Jameson Nash** ([@vtjnash](https://github.com/vtjnash))
- GPG key: AEAD 0A4B 6867 6775 1A0E 4AEF 34A2 5FB1 2824 6514 (pubkey-vtjnash)
- GPG key: CFBB 9CA9 A5BE AFD7 0E2B 3C5A 79A6 7C55 A367 9C8B (pubkey2022-vtjnash)
@@ -24,9 +27,6 @@ libuv is currently managed by the following individuals:
* **Anna Henningsen** ([@addaleax](https://github.com/addaleax))
* **Bartosz Sosnowski** ([@bzoz](https://github.com/bzoz))
* **Bert Belder** ([@piscisaureus](https://github.com/piscisaureus))
-* **Colin Ihrig** ([@cjihrig](https://github.com/cjihrig))
- - GPG key: 94AE 3667 5C46 4D64 BAFA 68DD 7434 390B DBE9 B9C5 (pubkey-cjihrig)
- - GPG key: 5735 3E0D BDAA A7E8 39B6 6A1A FF47 D5E4 AD8B 4FDC (pubkey-cjihrig-kb)
* **Fedor Indutny** ([@indutny](https://github.com/indutny))
- GPG key: AF2E EA41 EC34 47BF DD86 FED9 D706 3CCE 19B7 E890 (pubkey-indutny)
* **Imran Iqbal** ([@imran-iq](https://github.com/imran-iq))
diff --git a/deps/uv/Makefile.am b/deps/uv/Makefile.am
index f85a41316c8a43..9b9e6be7178b22 100644
--- a/deps/uv/Makefile.am
+++ b/deps/uv/Makefile.am
@@ -59,7 +59,7 @@ if WINNT
uvinclude_HEADERS += include/uv/win.h include/uv/tree.h
AM_CPPFLAGS += -I$(top_srcdir)/src/win \
-DWIN32_LEAN_AND_MEAN \
- -D_WIN32_WINNT=0x0602
+ -D_WIN32_WINNT=0x0A00
libuv_la_SOURCES += src/win/async.c \
src/win/atomicops-inl.h \
src/win/core.c \
@@ -294,6 +294,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \
test/test-thread-equal.c \
test/test-thread.c \
test/test-thread-affinity.c \
+ test/test-thread-name.c \
test/test-thread-priority.c \
test/test-threadpool-cancel.c \
test/test-threadpool.c \
diff --git a/deps/uv/SUPPORTED_PLATFORMS.md b/deps/uv/SUPPORTED_PLATFORMS.md
index 8a435d2592e47f..9597801b919687 100644
--- a/deps/uv/SUPPORTED_PLATFORMS.md
+++ b/deps/uv/SUPPORTED_PLATFORMS.md
@@ -4,14 +4,14 @@
|---|---|---|---|
| GNU/Linux | Tier 1 | Linux >= 3.10 with glibc >= 2.17 | |
| macOS | Tier 1 | macOS >= 11 | Currently supported macOS releases |
-| Windows | Tier 1 | >= Windows 8 | VS 2015 and later are supported |
+| Windows | Tier 1 | >= Windows 10 | VS 2015 and later are supported |
| FreeBSD | Tier 2 | >= 12 | |
| AIX | Tier 2 | >= 6 | Maintainers: @libuv/aix |
| IBM i | Tier 2 | >= IBM i 7.2 | Maintainers: @libuv/ibmi |
| z/OS | Tier 2 | >= V2R2 | Maintainers: @libuv/zos |
| Linux with musl | Tier 2 | musl >= 1.0 | |
| Android | Tier 3 | NDK >= r15b | Android 7.0, `-DANDROID_PLATFORM=android-24` |
-| MinGW | Tier 3 | MinGW32 and MinGW-w64 | |
+| MinGW | Tier 3 | MinGW-w64 | |
| SunOS | Tier 3 | Solaris 121 and later | |
| Other | Tier 3 | N/A | |
diff --git a/deps/uv/configure.ac b/deps/uv/configure.ac
index 98c59363026f86..fc8316b8e8fa75 100644
--- a/deps/uv/configure.ac
+++ b/deps/uv/configure.ac
@@ -13,7 +13,7 @@
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
AC_PREREQ(2.57)
-AC_INIT([libuv], [1.49.2], [https://github.com/libuv/libuv/issues])
+AC_INIT([libuv], [1.50.0], [https://github.com/libuv/libuv/issues])
AC_CONFIG_MACRO_DIR([m4])
m4_include([m4/libuv-extra-automake-flags.m4])
m4_include([m4/as_case.m4])
diff --git a/deps/uv/docs/src/fs_event.rst b/deps/uv/docs/src/fs_event.rst
index 983db1a9d5608a..bfdecdd7329cd2 100644
--- a/deps/uv/docs/src/fs_event.rst
+++ b/deps/uv/docs/src/fs_event.rst
@@ -47,6 +47,11 @@ Data types
The `events` parameter is an ORed mask of :c:enum:`uv_fs_event` elements.
+.. note::
+ For FreeBSD path could sometimes be `NULL` due to a kernel bug.
+
+ .. _Reference: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=197695
+
.. c:enum:: uv_fs_event
Event types that :c:type:`uv_fs_event_t` handles monitor.
diff --git a/deps/uv/docs/src/misc.rst b/deps/uv/docs/src/misc.rst
index 61883b7e21e527..db95e2dde83ea1 100644
--- a/deps/uv/docs/src/misc.rst
+++ b/deps/uv/docs/src/misc.rst
@@ -360,6 +360,17 @@ API
On Windows not all fields are set, the unsupported fields are filled with zeroes.
See :c:type:`uv_rusage_t` for more details.
+.. c:function:: int uv_getrusage_thread(uv_rusage_t* rusage)
+
+ Gets the resource usage measures for the calling thread.
+
+ .. versionadded:: 1.50.0
+
+ .. note::
+ Not supported on all platforms. May return `UV_ENOTSUP`.
+ On macOS and Windows not all fields are set, the unsupported fields are filled with zeroes.
+ See :c:type:`uv_rusage_t` for more details.
+
.. c:function:: uv_pid_t uv_os_getpid(void)
Returns the current process ID.
diff --git a/deps/uv/docs/src/threading.rst b/deps/uv/docs/src/threading.rst
index 883218fa829ccb..f40cf0a33c8121 100644
--- a/deps/uv/docs/src/threading.rst
+++ b/deps/uv/docs/src/threading.rst
@@ -78,6 +78,14 @@ Threads
.. versionchanged:: 1.4.1 returns a UV_E* error code on failure
+.. c:function:: int uv_thread_detach(uv_thread_t* tid)
+
+ Detaches a thread. Detached threads automatically release their
+ resources upon termination, eliminating the need for the application to
+ call `uv_thread_join`.
+
+ .. versionadded:: 1.50.0
+
.. c:function:: int uv_thread_create_ex(uv_thread_t* tid, const uv_thread_options_t* params, uv_thread_cb entry, void* arg)
Like :c:func:`uv_thread_create`, but additionally specifies options for creating a new thread.
@@ -132,6 +140,23 @@ Threads
.. c:function:: int uv_thread_join(uv_thread_t *tid)
.. c:function:: int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2)
+.. c:function:: int uv_thread_setname(const char* name)
+
+ Sets the name of the current thread. Different platforms define different limits on the max number of characters
+ a thread name can be: Linux, IBM i (16), macOS (64), Windows (32767), and NetBSD (32), etc. `uv_thread_setname()`
+ will truncate it in case `name` is larger than the limit of the platform.
+
+ .. versionadded:: 1.50.0
+
+.. c:function:: int uv_thread_getname(uv_thread_t* tid, char* name, size_t* size)
+
+ Gets the name of the thread specified by `tid`. The thread name is copied, with the trailing NUL, into the buffer
+ pointed to by `name`. The `size` parameter specifies the size of the buffer pointed to by `name`.
+ The buffer should be large enough to hold the name of the thread plus the trailing NUL, or it will be truncated to fit
+ with the trailing NUL.
+
+ .. versionadded:: 1.50.0
+
.. c:function:: int uv_thread_setpriority(uv_thread_t tid, int priority)
If the function succeeds, the return value is 0.
If the function fails, the return value is less than zero.
diff --git a/deps/uv/docs/src/threadpool.rst b/deps/uv/docs/src/threadpool.rst
index 7cfa797314ca48..05f31d2ccf30b8 100644
--- a/deps/uv/docs/src/threadpool.rst
+++ b/deps/uv/docs/src/threadpool.rst
@@ -17,6 +17,8 @@ is 1024).
.. versionchanged:: 1.45.0 threads now have an 8 MB stack instead of the
(sometimes too low) platform default.
+.. versionchanged:: 1.50.0 threads now have a default name of libuv-worker.
+
The threadpool is global and shared across all event loops. When a particular
function makes use of the threadpool (i.e. when using :c:func:`uv_queue_work`)
libuv preallocates and initializes the maximum number of threads allowed by
diff --git a/deps/uv/docs/src/timer.rst b/deps/uv/docs/src/timer.rst
index 070fa79da9d6df..474c6b8c4cd4f6 100644
--- a/deps/uv/docs/src/timer.rst
+++ b/deps/uv/docs/src/timer.rst
@@ -6,6 +6,15 @@
Timer handles are used to schedule callbacks to be called in the future.
+Timers are either single-shot or repeating. Repeating timers do not adjust
+for overhead but are rearmed relative to the event loop's idea of "now".
+
+Libuv updates its idea of "now" right before executing timer callbacks, and
+right after waking up from waiting for I/O. See also :c:func:`uv_update_time`.
+
+Example: a repeating timer with a 50 ms interval whose callback takes 17 ms
+to complete, runs again 33 ms later. If other tasks take longer than 33 ms,
+the timer callback runs as soon as possible.
Data types
----------
@@ -64,11 +73,6 @@ API
duration, and will follow normal timer semantics in the case of a
time-slice overrun.
- For example, if a 50ms repeating timer first runs for 17ms, it will be
- scheduled to run again 33ms later. If other tasks consume more than the
- 33ms following the first timer callback, then the callback will run as soon
- as possible.
-
.. note::
If the repeat value is set from a timer callback it does not immediately take effect.
If the timer was non-repeating before, it will have been stopped. If it was repeating,
diff --git a/deps/uv/docs/src/udp.rst b/deps/uv/docs/src/udp.rst
index 31f7f7fd71ff47..5f225e5cda4011 100644
--- a/deps/uv/docs/src/udp.rst
+++ b/deps/uv/docs/src/udp.rst
@@ -426,6 +426,20 @@ API
.. versionchanged:: 1.27.0 added support for connected sockets
+.. c:function:: int uv_udp_try_send2(uv_udp_t* handle, unsigned int count, uv_buf_t* bufs[/*count*/], unsigned int nbufs[/*count*/], struct sockaddr* addrs[/*count*/], unsigned int flags)
+
+ Like :c:func:`uv_udp_try_send`, but can send multiple datagrams.
+ Lightweight abstraction around :man:`sendmmsg(2)`, with a :man:`sendmsg(2)`
+ fallback loop for platforms that do not support the former. The handle must
+ be fully initialized; call c:func:`uv_udp_bind` first.
+
+ :returns: >= 0: number of datagrams sent. Zero only if `count` was zero.
+ < 0: negative error code. Only if sending the first datagram fails,
+ otherwise returns a positive send count. ``UV_EAGAIN`` when datagrams
+ cannot be sent right now; fall back to :c:func:`uv_udp_send`.
+
+ .. versionadded:: 1.50.0
+
.. c:function:: int uv_udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, uv_udp_recv_cb recv_cb)
Prepare for receiving data. If the socket has not previously been bound
diff --git a/deps/uv/include/uv.h b/deps/uv/include/uv.h
index 9e450c5110fe57..f0ec376b607c05 100644
--- a/deps/uv/include/uv.h
+++ b/deps/uv/include/uv.h
@@ -157,6 +157,7 @@ struct uv__queue {
XX(ESOCKTNOSUPPORT, "socket type not supported") \
XX(ENODATA, "no data available") \
XX(EUNATCH, "protocol driver not attached") \
+ XX(ENOEXEC, "exec format error") \
#define UV_HANDLE_TYPE_MAP(XX) \
XX(ASYNC, async) \
@@ -775,6 +776,12 @@ UV_EXTERN int uv_udp_try_send(uv_udp_t* handle,
const uv_buf_t bufs[],
unsigned int nbufs,
const struct sockaddr* addr);
+UV_EXTERN int uv_udp_try_send2(uv_udp_t* handle,
+ unsigned int count,
+ uv_buf_t* bufs[/*count*/],
+ unsigned int nbufs[/*count*/],
+ struct sockaddr* addrs[/*count*/],
+ unsigned int flags);
UV_EXTERN int uv_udp_recv_start(uv_udp_t* handle,
uv_alloc_cb alloc_cb,
uv_udp_recv_cb recv_cb);
@@ -1288,6 +1295,7 @@ typedef struct {
} uv_rusage_t;
UV_EXTERN int uv_getrusage(uv_rusage_t* rusage);
+UV_EXTERN int uv_getrusage_thread(uv_rusage_t* rusage);
UV_EXTERN int uv_os_homedir(char* buffer, size_t* size);
UV_EXTERN int uv_os_tmpdir(char* buffer, size_t* size);
@@ -1869,6 +1877,7 @@ UV_EXTERN int uv_gettimeofday(uv_timeval64_t* tv);
typedef void (*uv_thread_cb)(void* arg);
UV_EXTERN int uv_thread_create(uv_thread_t* tid, uv_thread_cb entry, void* arg);
+UV_EXTERN int uv_thread_detach(uv_thread_t* tid);
typedef enum {
UV_THREAD_NO_FLAGS = 0x00,
@@ -1898,6 +1907,9 @@ UV_EXTERN int uv_thread_getcpu(void);
UV_EXTERN uv_thread_t uv_thread_self(void);
UV_EXTERN int uv_thread_join(uv_thread_t *tid);
UV_EXTERN int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2);
+UV_EXTERN int uv_thread_setname(const char* name);
+UV_EXTERN int uv_thread_getname(uv_thread_t* tid, char* name, size_t size);
+
/* The presence of these unions force similar struct layout. */
#define XX(_, name) uv_ ## name ## _t name;
diff --git a/deps/uv/include/uv/errno.h b/deps/uv/include/uv/errno.h
index 127278ef916161..ac00778cfc59fb 100644
--- a/deps/uv/include/uv/errno.h
+++ b/deps/uv/include/uv/errno.h
@@ -474,4 +474,10 @@
# define UV__EUNATCH (-4023)
#endif
+#if defined(ENOEXEC) && !defined(_WIN32)
+# define UV__ENOEXEC UV__ERR(ENOEXEC)
+#else
+# define UV__ENOEXEC (-4022)
+#endif
+
#endif /* UV_ERRNO_H_ */
diff --git a/deps/uv/include/uv/unix.h b/deps/uv/include/uv/unix.h
index 538f98b6c5d657..7c972026f688e8 100644
--- a/deps/uv/include/uv/unix.h
+++ b/deps/uv/include/uv/unix.h
@@ -271,7 +271,10 @@ typedef struct {
#define UV_UDP_SEND_PRIVATE_FIELDS \
struct uv__queue queue; \
- struct sockaddr_storage addr; \
+ union { \
+ struct sockaddr addr; \
+ struct sockaddr_storage storage; \
+ } u; \
unsigned int nbufs; \
uv_buf_t* bufs; \
ssize_t status; \
diff --git a/deps/uv/include/uv/version.h b/deps/uv/include/uv/version.h
index cfa7871322e690..76eb7d125fe468 100644
--- a/deps/uv/include/uv/version.h
+++ b/deps/uv/include/uv/version.h
@@ -31,8 +31,8 @@
*/
#define UV_VERSION_MAJOR 1
-#define UV_VERSION_MINOR 49
-#define UV_VERSION_PATCH 2
+#define UV_VERSION_MINOR 50
+#define UV_VERSION_PATCH 0
#define UV_VERSION_IS_RELEASE 1
#define UV_VERSION_SUFFIX ""
diff --git a/deps/uv/include/uv/win.h b/deps/uv/include/uv/win.h
index 12ac53b4f217d2..58d10b8d07fa0b 100644
--- a/deps/uv/include/uv/win.h
+++ b/deps/uv/include/uv/win.h
@@ -20,7 +20,7 @@
*/
#ifndef _WIN32_WINNT
-# define _WIN32_WINNT 0x0600
+# define _WIN32_WINNT 0x0A00
#endif
#if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED)
@@ -32,14 +32,6 @@ typedef intptr_t ssize_t;
#include
-#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
-typedef struct pollfd {
- SOCKET fd;
- short events;
- short revents;
-} WSAPOLLFD, *PWSAPOLLFD, *LPWSAPOLLFD;
-#endif
-
#ifndef LOCALE_INVARIANT
# define LOCALE_INVARIANT 0x007f
#endif
diff --git a/deps/uv/src/fs-poll.c b/deps/uv/src/fs-poll.c
index 1bac1c568e36ca..44f6263a5832ec 100644
--- a/deps/uv/src/fs-poll.c
+++ b/deps/uv/src/fs-poll.c
@@ -139,6 +139,9 @@ int uv_fs_poll_getpath(uv_fs_poll_t* handle, char* buffer, size_t* size) {
struct poll_ctx* ctx;
size_t required_len;
+ if (buffer == NULL || size == NULL || *size == 0)
+ return UV_EINVAL;
+
if (!uv_is_active((uv_handle_t*)handle)) {
*size = 0;
return UV_EINVAL;
diff --git a/deps/uv/src/idna.c b/deps/uv/src/idna.c
index efc5f283ce2ef9..5fcaf64c974a8a 100644
--- a/deps/uv/src/idna.c
+++ b/deps/uv/src/idna.c
@@ -393,7 +393,7 @@ void uv_wtf8_to_utf16(const char* source_ptr,
code_point = uv__wtf8_decode1(&source_ptr);
/* uv_wtf8_length_as_utf16 should have been called and checked first. */
assert(code_point >= 0);
- if (code_point > 0x10000) {
+ if (code_point > 0xFFFF) {
assert(code_point < 0x10FFFF);
*w_target++ = (((code_point - 0x10000) >> 10) + 0xD800);
*w_target++ = ((code_point - 0x10000) & 0x3FF) + 0xDC00;
diff --git a/deps/uv/src/threadpool.c b/deps/uv/src/threadpool.c
index 45af50dcd04ea6..98d81cc7b6a4ed 100644
--- a/deps/uv/src/threadpool.c
+++ b/deps/uv/src/threadpool.c
@@ -59,6 +59,7 @@ static void worker(void* arg) {
struct uv__queue* q;
int is_slow_work;
+ uv_thread_setname("libuv-worker");
uv_sem_post((uv_sem_t*) arg);
arg = NULL;
diff --git a/deps/uv/src/unix/async.c b/deps/uv/src/unix/async.c
index 0ff2669e30a628..8265a43ab47046 100644
--- a/deps/uv/src/unix/async.c
+++ b/deps/uv/src/unix/async.c
@@ -38,6 +38,34 @@
#include
#endif
+#if UV__KQUEUE_EVFILT_USER
+static uv_once_t kqueue_runtime_detection_guard = UV_ONCE_INIT;
+static int kqueue_evfilt_user_support = 1;
+
+
+static void uv__kqueue_runtime_detection(void) {
+ int kq;
+ struct kevent ev[2];
+ struct timespec timeout = {0, 0};
+
+ /* Perform the runtime detection to ensure that kqueue with
+ * EVFILT_USER actually works. */
+ kq = kqueue();
+ EV_SET(ev, UV__KQUEUE_EVFILT_USER_IDENT, EVFILT_USER,
+ EV_ADD | EV_CLEAR, 0, 0, 0);
+ EV_SET(ev + 1, UV__KQUEUE_EVFILT_USER_IDENT, EVFILT_USER,
+ 0, NOTE_TRIGGER, 0, 0);
+ if (kevent(kq, ev, 2, ev, 1, &timeout) < 1 ||
+ ev[0].filter != EVFILT_USER ||
+ ev[0].ident != UV__KQUEUE_EVFILT_USER_IDENT ||
+ ev[0].flags & EV_ERROR)
+ /* If we wind up here, we can assume that EVFILT_USER is defined but
+ * broken on the current system. */
+ kqueue_evfilt_user_support = 0;
+ uv__close(kq);
+}
+#endif
+
static void uv__async_send(uv_loop_t* loop);
static int uv__async_start(uv_loop_t* loop);
static void uv__cpu_relax(void);
@@ -139,7 +167,11 @@ static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
assert(w == &loop->async_io_watcher);
+#if UV__KQUEUE_EVFILT_USER
+ for (;!kqueue_evfilt_user_support;) {
+#else
for (;;) {
+#endif
r = read(w->fd, buf, sizeof(buf));
if (r == sizeof(buf))
@@ -195,6 +227,17 @@ static void uv__async_send(uv_loop_t* loop) {
len = sizeof(val);
fd = loop->async_io_watcher.fd; /* eventfd */
}
+#elif UV__KQUEUE_EVFILT_USER
+ struct kevent ev;
+
+ if (kqueue_evfilt_user_support) {
+ fd = loop->async_io_watcher.fd; /* magic number for EVFILT_USER */
+ EV_SET(&ev, fd, EVFILT_USER, 0, NOTE_TRIGGER, 0, 0);
+ r = kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL);
+ if (r == 0)
+ return;
+ abort();
+ }
#endif
do
@@ -215,6 +258,9 @@ static void uv__async_send(uv_loop_t* loop) {
static int uv__async_start(uv_loop_t* loop) {
int pipefd[2];
int err;
+#if UV__KQUEUE_EVFILT_USER
+ struct kevent ev;
+#endif
if (loop->async_io_watcher.fd != -1)
return 0;
@@ -226,6 +272,36 @@ static int uv__async_start(uv_loop_t* loop) {
pipefd[0] = err;
pipefd[1] = -1;
+#elif UV__KQUEUE_EVFILT_USER
+ uv_once(&kqueue_runtime_detection_guard, uv__kqueue_runtime_detection);
+ if (kqueue_evfilt_user_support) {
+ /* In order not to break the generic pattern of I/O polling, a valid
+ * file descriptor is required to take up a room in loop->watchers,
+ * thus we create one for that, but this fd will not be actually used,
+ * it's just a placeholder and magic number which is going to be closed
+ * during the cleanup, as other FDs. */
+ err = uv__open_cloexec("/dev/null", O_RDONLY);
+ if (err < 0)
+ return err;
+
+ pipefd[0] = err;
+ pipefd[1] = -1;
+
+ /* When using EVFILT_USER event to wake up the kqueue, this event must be
+ * registered beforehand. Otherwise, calling kevent() to issue an
+ * unregistered EVFILT_USER event will get an ENOENT.
+ * Since uv__async_send() may happen before uv__io_poll() with multi-threads,
+ * we can't defer this registration of EVFILT_USER event as we did for other
+ * events, but must perform it right away. */
+ EV_SET(&ev, err, EVFILT_USER, EV_ADD | EV_CLEAR, 0, 0, 0);
+ err = kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL);
+ if (err < 0)
+ return UV__ERR(errno);
+ } else {
+ err = uv__make_pipe(pipefd, UV_NONBLOCK_PIPE);
+ if (err < 0)
+ return err;
+ }
#else
err = uv__make_pipe(pipefd, UV_NONBLOCK_PIPE);
if (err < 0)
@@ -236,6 +312,13 @@ static int uv__async_start(uv_loop_t* loop) {
uv__io_start(loop, &loop->async_io_watcher, POLLIN);
loop->async_wfd = pipefd[1];
+#if UV__KQUEUE_EVFILT_USER
+ /* Prevent the EVFILT_USER event from being added to kqueue redundantly
+ * and mistakenly later in uv__io_poll(). */
+ if (kqueue_evfilt_user_support)
+ loop->async_io_watcher.events = loop->async_io_watcher.pevents;
+#endif
+
return 0;
}
diff --git a/deps/uv/src/unix/core.c b/deps/uv/src/unix/core.c
index 0c52ccf2ad7b2d..61cbc0d027f04a 100644
--- a/deps/uv/src/unix/core.c
+++ b/deps/uv/src/unix/core.c
@@ -52,6 +52,8 @@
#endif
#if defined(__APPLE__)
+# include
+# include
# include
# include
#endif /* defined(__APPLE__) */
@@ -751,7 +753,7 @@ ssize_t uv__recvmsg(int fd, struct msghdr* msg, int flags) {
int uv_cwd(char* buffer, size_t* size) {
char scratch[1 + UV__PATH_MAX];
- if (buffer == NULL || size == NULL)
+ if (buffer == NULL || size == NULL || *size == 0)
return UV_EINVAL;
/* Try to read directly into the user's buffer first... */
@@ -999,10 +1001,10 @@ int uv__fd_exists(uv_loop_t* loop, int fd) {
}
-int uv_getrusage(uv_rusage_t* rusage) {
+static int uv__getrusage(int who, uv_rusage_t* rusage) {
struct rusage usage;
- if (getrusage(RUSAGE_SELF, &usage))
+ if (getrusage(who, &usage))
return UV__ERR(errno);
rusage->ru_utime.tv_sec = usage.ru_utime.tv_sec;
@@ -1041,6 +1043,48 @@ int uv_getrusage(uv_rusage_t* rusage) {
}
+int uv_getrusage(uv_rusage_t* rusage) {
+ return uv__getrusage(RUSAGE_SELF, rusage);
+}
+
+
+int uv_getrusage_thread(uv_rusage_t* rusage) {
+#if defined(__APPLE__)
+ mach_msg_type_number_t count;
+ thread_basic_info_data_t info;
+ kern_return_t kr;
+ thread_t thread;
+
+ thread = mach_thread_self();
+ count = THREAD_BASIC_INFO_COUNT;
+ kr = thread_info(thread,
+ THREAD_BASIC_INFO,
+ (thread_info_t)&info,
+ &count);
+
+ if (kr != KERN_SUCCESS) {
+ mach_port_deallocate(mach_task_self(), thread);
+ return UV_EINVAL;
+ }
+
+ memset(rusage, 0, sizeof(*rusage));
+
+ rusage->ru_utime.tv_sec = info.user_time.seconds;
+ rusage->ru_utime.tv_usec = info.user_time.microseconds;
+ rusage->ru_stime.tv_sec = info.system_time.seconds;
+ rusage->ru_stime.tv_usec = info.system_time.microseconds;
+
+ mach_port_deallocate(mach_task_self(), thread);
+
+ return 0;
+
+#elif defined(RUSAGE_THREAD)
+ return uv__getrusage(RUSAGE_THREAD, rusage);
+#endif /* defined(__APPLE__) */
+ return UV_ENOTSUP;
+}
+
+
int uv__open_cloexec(const char* path, int flags) {
#if defined(O_CLOEXEC)
int fd;
diff --git a/deps/uv/src/unix/darwin-proctitle.c b/deps/uv/src/unix/darwin-proctitle.c
index 5288083ef04fd7..5e5642972a4df6 100644
--- a/deps/uv/src/unix/darwin-proctitle.c
+++ b/deps/uv/src/unix/darwin-proctitle.c
@@ -33,25 +33,9 @@
#include "darwin-stub.h"
#endif
-
-static int uv__pthread_setname_np(const char* name) {
- char namebuf[64]; /* MAXTHREADNAMESIZE */
- int err;
-
- strncpy(namebuf, name, sizeof(namebuf) - 1);
- namebuf[sizeof(namebuf) - 1] = '\0';
-
- err = pthread_setname_np(namebuf);
- if (err)
- return UV__ERR(err);
-
- return 0;
-}
-
-
int uv__set_process_title(const char* title) {
#if TARGET_OS_IPHONE
- return uv__pthread_setname_np(title);
+ return uv__thread_setname(title);
#else
CFStringRef (*pCFStringCreateWithCString)(CFAllocatorRef,
const char*,
@@ -177,7 +161,7 @@ int uv__set_process_title(const char* title) {
goto out;
}
- uv__pthread_setname_np(title); /* Don't care if it fails. */
+ uv__thread_setname(title); /* Don't care if it fails. */
err = 0;
out:
diff --git a/deps/uv/src/unix/internal.h b/deps/uv/src/unix/internal.h
index 8d586b0b64a96c..b1d2b21756da36 100644
--- a/deps/uv/src/unix/internal.h
+++ b/deps/uv/src/unix/internal.h
@@ -35,6 +35,10 @@
#include
#include
#include
+#if defined(__APPLE__) || defined(__DragonFly__) || \
+ defined(__FreeBSD__) || defined(__NetBSD__)
+#include
+#endif
#define uv__msan_unpoison(p, n) \
do { \
@@ -323,6 +327,8 @@ void uv__prepare_close(uv_prepare_t* handle);
void uv__process_close(uv_process_t* handle);
void uv__stream_close(uv_stream_t* handle);
void uv__tcp_close(uv_tcp_t* handle);
+int uv__thread_setname(const char* name);
+int uv__thread_getname(uv_thread_t* tid, char* name, size_t size);
size_t uv__thread_stack_size(void);
void uv__udp_close(uv_udp_t* handle);
void uv__udp_finish_close(uv_udp_t* handle);
@@ -504,4 +510,22 @@ int uv__get_constrained_cpu(uv__cpu_constraint* constraint);
#endif
#endif
+#if defined(EVFILT_USER) && defined(NOTE_TRIGGER)
+/* EVFILT_USER is available since OS X 10.6, DragonFlyBSD 4.0,
+ * FreeBSD 8.1, and NetBSD 10.0.
+ *
+ * Note that even though EVFILT_USER is defined on the current system,
+ * it may still fail to work at runtime somehow. In that case, we fall
+ * back to pipe-based signaling.
+ */
+#define UV__KQUEUE_EVFILT_USER 1
+/* Magic number of identifier used for EVFILT_USER during runtime detection.
+ * There are no Google hits for this number when I create it. That way,
+ * people will be directed here if this number gets printed due to some
+ * kqueue error and they google for help. */
+#define UV__KQUEUE_EVFILT_USER_IDENT 0x1e7e7711
+#else
+#define UV__KQUEUE_EVFILT_USER 0
+#endif
+
#endif /* UV_UNIX_INTERNAL_H_ */
diff --git a/deps/uv/src/unix/kqueue.c b/deps/uv/src/unix/kqueue.c
index 66aa166f053f52..e0166c344b05c4 100644
--- a/deps/uv/src/unix/kqueue.c
+++ b/deps/uv/src/unix/kqueue.c
@@ -97,8 +97,7 @@ int uv__io_fork(uv_loop_t* loop) {
int uv__io_check_fd(uv_loop_t* loop, int fd) {
- struct kevent ev;
- int rc;
+ struct kevent ev[2];
struct stat sb;
#ifdef __APPLE__
char path[MAXPATHLEN];
@@ -133,17 +132,12 @@ int uv__io_check_fd(uv_loop_t* loop, int fd) {
}
#endif
- rc = 0;
- EV_SET(&ev, fd, EVFILT_READ, EV_ADD, 0, 0, 0);
- if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL))
- rc = UV__ERR(errno);
-
- EV_SET(&ev, fd, EVFILT_READ, EV_DELETE, 0, 0, 0);
- if (rc == 0)
- if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL))
- abort();
+ EV_SET(ev, fd, EVFILT_READ, EV_ADD, 0, 0, 0);
+ EV_SET(ev + 1, fd, EVFILT_READ, EV_DELETE, 0, 0, 0);
+ if (kevent(loop->backend_fd, ev, 2, NULL, 0, NULL))
+ return UV__ERR(errno);
- return rc;
+ return 0;
}
@@ -367,6 +361,17 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
continue;
}
+#if UV__KQUEUE_EVFILT_USER
+ if (ev->filter == EVFILT_USER) {
+ w = &loop->async_io_watcher;
+ assert(fd == w->fd);
+ uv__metrics_update_idle_time(loop);
+ w->cb(loop, w, w->events);
+ nevents++;
+ continue;
+ }
+#endif
+
if (ev->filter == EVFILT_VNODE) {
assert(w->events == POLLIN);
assert(w->pevents == POLLIN);
diff --git a/deps/uv/src/unix/linux.c b/deps/uv/src/unix/linux.c
index 857a4ef8a6686f..763f5dd5917b44 100644
--- a/deps/uv/src/unix/linux.c
+++ b/deps/uv/src/unix/linux.c
@@ -455,7 +455,7 @@ int uv__io_uring_register(int fd, unsigned opcode, void* arg, unsigned nargs) {
}
-static int uv__use_io_uring(void) {
+static int uv__use_io_uring(uint32_t flags) {
#if defined(__ANDROID_API__)
return 0; /* Possibly available but blocked by seccomp. */
#elif defined(__arm__) && __SIZEOF_POINTER__ == 4
@@ -470,25 +470,27 @@ static int uv__use_io_uring(void) {
char* val;
int use;
- use = atomic_load_explicit(&use_io_uring, memory_order_relaxed);
-
- if (use == 0) {
- use = uv__kernel_version() >=
#if defined(__hppa__)
- /* io_uring first supported on parisc in 6.1, functional in .51 */
- /* https://lore.kernel.org/all/cb912694-b1fe-dbb0-4d8c-d608f3526905@gmx.de/ */
- /* 6.1.51 */ 0x060133
-#else
- /* Older kernels have a bug where the sqpoll thread uses 100% CPU. */
- /* 5.10.186 */ 0x050ABA
+ /* io_uring first supported on parisc in 6.1, functional in .51
+ * https://lore.kernel.org/all/cb912694-b1fe-dbb0-4d8c-d608f3526905@gmx.de/
+ */
+ if (uv__kernel_version() < /*6.1.51*/0x060133)
+ return 0;
#endif
- ? 1 : -1;
- /* But users can still enable it if they so desire. */
- val = getenv("UV_USE_IO_URING");
- if (val != NULL)
- use = atoi(val) ? 1 : -1;
+ /* SQPOLL is all kinds of buggy but epoll batching should work fine. */
+ if (0 == (flags & UV__IORING_SETUP_SQPOLL))
+ return 1;
+
+ /* Older kernels have a bug where the sqpoll thread uses 100% CPU. */
+ if (uv__kernel_version() < /*5.10.186*/0x050ABA)
+ return 0;
+
+ use = atomic_load_explicit(&use_io_uring, memory_order_relaxed);
+ if (use == 0) {
+ val = getenv("UV_USE_IO_URING");
+ use = val != NULL && atoi(val) > 0 ? 1 : -1;
atomic_store_explicit(&use_io_uring, use, memory_order_relaxed);
}
@@ -518,7 +520,7 @@ static void uv__iou_init(int epollfd,
sq = MAP_FAILED;
sqe = MAP_FAILED;
- if (!uv__use_io_uring())
+ if (!uv__use_io_uring(flags))
return;
kernel_version = uv__kernel_version();
@@ -766,14 +768,13 @@ static struct uv__io_uring_sqe* uv__iou_get_sqe(struct uv__iou* iou,
*/
if (iou->ringfd == -2) {
/* By default, the SQPOLL is not created. Enable only if the loop is
- * configured with UV_LOOP_USE_IO_URING_SQPOLL.
+ * configured with UV_LOOP_USE_IO_URING_SQPOLL and the UV_USE_IO_URING
+ * environment variable is unset or a positive number.
*/
- if ((loop->flags & UV_LOOP_ENABLE_IO_URING_SQPOLL) == 0) {
- iou->ringfd = -1;
- return NULL;
- }
+ if (loop->flags & UV_LOOP_ENABLE_IO_URING_SQPOLL)
+ if (uv__use_io_uring(UV__IORING_SETUP_SQPOLL))
+ uv__iou_init(loop->backend_fd, iou, 64, UV__IORING_SETUP_SQPOLL);
- uv__iou_init(loop->backend_fd, iou, 64, UV__IORING_SETUP_SQPOLL);
if (iou->ringfd == -2)
iou->ringfd = -1; /* "failed" */
}
@@ -1713,16 +1714,22 @@ int uv_uptime(double* uptime) {
int uv_cpu_info(uv_cpu_info_t** ci, int* count) {
#if defined(__PPC__)
static const char model_marker[] = "cpu\t\t: ";
+ static const char model_marker2[] = "";
#elif defined(__arm__)
- static const char model_marker[] = "Processor\t: ";
+ static const char model_marker[] = "model name\t: ";
+ static const char model_marker2[] = "Processor\t: ";
#elif defined(__aarch64__)
static const char model_marker[] = "CPU part\t: ";
+ static const char model_marker2[] = "";
#elif defined(__mips__)
static const char model_marker[] = "cpu model\t\t: ";
+ static const char model_marker2[] = "";
#elif defined(__loongarch__)
static const char model_marker[] = "cpu family\t\t: ";
+ static const char model_marker2[] = "";
#else
static const char model_marker[] = "model name\t: ";
+ static const char model_marker2[] = "";
#endif
static const char parts[] =
#ifdef __aarch64__
@@ -1821,14 +1828,22 @@ int uv_cpu_info(uv_cpu_info_t** ci, int* count) {
if (1 != fscanf(fp, "processor\t: %u\n", &cpu))
break; /* Parse error. */
- found = 0;
- while (!found && fgets(buf, sizeof(buf), fp))
- found = !strncmp(buf, model_marker, sizeof(model_marker) - 1);
+ while (fgets(buf, sizeof(buf), fp)) {
+ if (!strncmp(buf, model_marker, sizeof(model_marker) - 1)) {
+ p = buf + sizeof(model_marker) - 1;
+ goto parts;
+ }
+ if (!*model_marker2)
+ continue;
+ if (!strncmp(buf, model_marker2, sizeof(model_marker2) - 1)) {
+ p = buf + sizeof(model_marker2) - 1;
+ goto parts;
+ }
+ }
- if (!found)
- goto next;
+ goto next; /* Not found. */
- p = buf + sizeof(model_marker) - 1;
+parts:
n = (int) strcspn(p, "\n");
/* arm64: translate CPU part code to model name. */
diff --git a/deps/uv/src/unix/pipe.c b/deps/uv/src/unix/pipe.c
index 1f9acfac41e9c5..bd57b17fb0367a 100644
--- a/deps/uv/src/unix/pipe.c
+++ b/deps/uv/src/unix/pipe.c
@@ -360,6 +360,9 @@ static int uv__pipe_getsockpeername(const uv_pipe_t* handle,
char* p;
int err;
+ if (buffer == NULL || size == NULL || *size == 0)
+ return UV_EINVAL;
+
addrlen = sizeof(sa);
memset(&sa, 0, addrlen);
err = uv__getsockpeername((const uv_handle_t*) handle,
@@ -444,7 +447,7 @@ uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle) {
int uv_pipe_chmod(uv_pipe_t* handle, int mode) {
unsigned desired_mode;
struct stat pipe_stat;
- char* name_buffer;
+ char name_buffer[1 + UV__PATH_MAX];
size_t name_len;
int r;
@@ -457,26 +460,14 @@ int uv_pipe_chmod(uv_pipe_t* handle, int mode) {
return UV_EINVAL;
/* Unfortunately fchmod does not work on all platforms, we will use chmod. */
- name_len = 0;
- r = uv_pipe_getsockname(handle, NULL, &name_len);
- if (r != UV_ENOBUFS)
- return r;
-
- name_buffer = uv__malloc(name_len);
- if (name_buffer == NULL)
- return UV_ENOMEM;
-
+ name_len = sizeof(name_buffer);
r = uv_pipe_getsockname(handle, name_buffer, &name_len);
- if (r != 0) {
- uv__free(name_buffer);
+ if (r != 0)
return r;
- }
/* stat must be used as fstat has a bug on Darwin */
- if (uv__stat(name_buffer, &pipe_stat) == -1) {
- uv__free(name_buffer);
- return -errno;
- }
+ if (uv__stat(name_buffer, &pipe_stat) == -1)
+ return UV__ERR(errno);
desired_mode = 0;
if (mode & UV_READABLE)
@@ -485,15 +476,12 @@ int uv_pipe_chmod(uv_pipe_t* handle, int mode) {
desired_mode |= S_IWUSR | S_IWGRP | S_IWOTH;
/* Exit early if pipe already has desired mode. */
- if ((pipe_stat.st_mode & desired_mode) == desired_mode) {
- uv__free(name_buffer);
+ if ((pipe_stat.st_mode & desired_mode) == desired_mode)
return 0;
- }
pipe_stat.st_mode |= desired_mode;
r = chmod(name_buffer, pipe_stat.st_mode);
- uv__free(name_buffer);
return r != -1 ? 0 : UV__ERR(errno);
}
diff --git a/deps/uv/src/unix/thread.c b/deps/uv/src/unix/thread.c
index f05e6fe0f7dd5a..e51c290466d08b 100644
--- a/deps/uv/src/unix/thread.c
+++ b/deps/uv/src/unix/thread.c
@@ -23,6 +23,9 @@
#include "internal.h"
#include
+#ifdef __OpenBSD__
+#include
+#endif
#include
#include
@@ -126,6 +129,12 @@ int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) {
return uv_thread_create_ex(tid, ¶ms, entry, arg);
}
+
+int uv_thread_detach(uv_thread_t *tid) {
+ return UV__ERR(pthread_detach(*tid));
+}
+
+
int uv_thread_create_ex(uv_thread_t* tid,
const uv_thread_options_t* params,
void (*entry)(void *arg),
@@ -291,6 +300,18 @@ int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2) {
return pthread_equal(*t1, *t2);
}
+int uv_thread_setname(const char* name) {
+ if (name == NULL)
+ return UV_EINVAL;
+ return uv__thread_setname(name);
+}
+
+int uv_thread_getname(uv_thread_t* tid, char* name, size_t size) {
+ if (name == NULL || size == 0)
+ return UV_EINVAL;
+
+ return uv__thread_getname(tid, name, size);
+}
int uv_mutex_init(uv_mutex_t* mutex) {
#if defined(NDEBUG) || !defined(PTHREAD_MUTEX_ERRORCHECK)
@@ -875,3 +896,80 @@ void uv_key_set(uv_key_t* key, void* value) {
if (pthread_setspecific(*key, value))
abort();
}
+
+#if defined(_AIX) || defined(__MVS__) || defined(__PASE__)
+int uv__thread_setname(const char* name) {
+ return UV_ENOSYS;
+}
+#elif defined(__APPLE__)
+int uv__thread_setname(const char* name) {
+ char namebuf[UV_PTHREAD_MAX_NAMELEN_NP];
+ strncpy(namebuf, name, sizeof(namebuf) - 1);
+ namebuf[sizeof(namebuf) - 1] = '\0';
+ int err = pthread_setname_np(namebuf);
+ if (err)
+ return UV__ERR(errno);
+ return 0;
+}
+#elif defined(__NetBSD__)
+int uv__thread_setname(const char* name) {
+ char namebuf[UV_PTHREAD_MAX_NAMELEN_NP];
+ strncpy(namebuf, name, sizeof(namebuf) - 1);
+ namebuf[sizeof(namebuf) - 1] = '\0';
+ return UV__ERR(pthread_setname_np(pthread_self(), "%s", namebuf));
+}
+#elif defined(__OpenBSD__)
+int uv__thread_setname(const char* name) {
+ char namebuf[UV_PTHREAD_MAX_NAMELEN_NP];
+ strncpy(namebuf, name, sizeof(namebuf) - 1);
+ namebuf[sizeof(namebuf) - 1] = '\0';
+ pthread_set_name_np(pthread_self(), namebuf);
+ return 0;
+}
+#else
+int uv__thread_setname(const char* name) {
+ char namebuf[UV_PTHREAD_MAX_NAMELEN_NP];
+ strncpy(namebuf, name, sizeof(namebuf) - 1);
+ namebuf[sizeof(namebuf) - 1] = '\0';
+ return UV__ERR(pthread_setname_np(pthread_self(), namebuf));
+}
+#endif
+
+#if (defined(__ANDROID_API__) && __ANDROID_API__ < 26) || \
+ defined(_AIX) || \
+ defined(__MVS__) || \
+ defined(__PASE__)
+int uv__thread_getname(uv_thread_t* tid, char* name, size_t size) {
+ return UV_ENOSYS;
+}
+#elif defined(__OpenBSD__)
+int uv__thread_getname(uv_thread_t* tid, char* name, size_t size) {
+ char thread_name[UV_PTHREAD_MAX_NAMELEN_NP];
+ pthread_get_name_np(*tid, thread_name, sizeof(thread_name));
+ strncpy(name, thread_name, size - 1);
+ name[size - 1] = '\0';
+ return 0;
+}
+#elif defined(__APPLE__)
+int uv__thread_getname(uv_thread_t* tid, char* name, size_t size) {
+ char thread_name[UV_PTHREAD_MAX_NAMELEN_NP];
+ if (pthread_getname_np(*tid, thread_name, sizeof(thread_name)) != 0)
+ return UV__ERR(errno);
+
+ strncpy(name, thread_name, size - 1);
+ name[size - 1] = '\0';
+ return 0;
+}
+#else
+int uv__thread_getname(uv_thread_t* tid, char* name, size_t size) {
+ int r;
+ char thread_name[UV_PTHREAD_MAX_NAMELEN_NP];
+ r = pthread_getname_np(*tid, thread_name, sizeof(thread_name));
+ if (r != 0)
+ return UV__ERR(r);
+
+ strncpy(name, thread_name, size - 1);
+ name[size - 1] = '\0';
+ return 0;
+}
+#endif
diff --git a/deps/uv/src/unix/udp.c b/deps/uv/src/unix/udp.c
index f6640fc7231863..67c01f7dce8e18 100644
--- a/deps/uv/src/unix/udp.c
+++ b/deps/uv/src/unix/udp.c
@@ -47,6 +47,10 @@ static void uv__udp_sendmsg(uv_udp_t* handle);
static int uv__udp_maybe_deferred_bind(uv_udp_t* handle,
int domain,
unsigned int flags);
+static int uv__udp_sendmsg1(int fd,
+ const uv_buf_t* bufs,
+ unsigned int nbufs,
+ const struct sockaddr* addr);
void uv__udp_close(uv_udp_t* handle) {
@@ -282,169 +286,6 @@ static void uv__udp_recvmsg(uv_udp_t* handle) {
&& handle->recv_cb != NULL);
}
-static void uv__udp_sendmsg_one(uv_udp_t* handle, uv_udp_send_t* req) {
- struct uv__queue* q;
- struct msghdr h;
- ssize_t size;
-
- for (;;) {
- memset(&h, 0, sizeof h);
- if (req->addr.ss_family == AF_UNSPEC) {
- h.msg_name = NULL;
- h.msg_namelen = 0;
- } else {
- h.msg_name = &req->addr;
- if (req->addr.ss_family == AF_INET6)
- h.msg_namelen = sizeof(struct sockaddr_in6);
- else if (req->addr.ss_family == AF_INET)
- h.msg_namelen = sizeof(struct sockaddr_in);
- else if (req->addr.ss_family == AF_UNIX)
- h.msg_namelen = sizeof(struct sockaddr_un);
- else {
- assert(0 && "unsupported address family");
- abort();
- }
- }
- h.msg_iov = (struct iovec*) req->bufs;
- h.msg_iovlen = req->nbufs;
-
- do
- size = sendmsg(handle->io_watcher.fd, &h, 0);
- while (size == -1 && errno == EINTR);
-
- if (size == -1)
- if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS)
- return;
-
- req->status = (size == -1 ? UV__ERR(errno) : size);
-
- /* Sending a datagram is an atomic operation: either all data
- * is written or nothing is (and EMSGSIZE is raised). That is
- * why we don't handle partial writes. Just pop the request
- * off the write queue and onto the completed queue, done.
- */
- uv__queue_remove(&req->queue);
- uv__queue_insert_tail(&handle->write_completed_queue, &req->queue);
- uv__io_feed(handle->loop, &handle->io_watcher);
-
- if (uv__queue_empty(&handle->write_queue))
- return;
-
- q = uv__queue_head(&handle->write_queue);
- req = uv__queue_data(q, uv_udp_send_t, queue);
- }
-}
-
-#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
-static void uv__udp_sendmsg_many(uv_udp_t* handle) {
- uv_udp_send_t* req;
- struct mmsghdr h[20];
- struct mmsghdr* p;
- struct uv__queue* q;
- ssize_t npkts;
- size_t pkts;
- size_t i;
-
-write_queue_drain:
- for (pkts = 0, q = uv__queue_head(&handle->write_queue);
- pkts < ARRAY_SIZE(h) && q != &handle->write_queue;
- ++pkts, q = uv__queue_head(q)) {
- req = uv__queue_data(q, uv_udp_send_t, queue);
-
- p = &h[pkts];
- memset(p, 0, sizeof(*p));
- if (req->addr.ss_family == AF_UNSPEC) {
- p->msg_hdr.msg_name = NULL;
- p->msg_hdr.msg_namelen = 0;
- } else {
- p->msg_hdr.msg_name = &req->addr;
- if (req->addr.ss_family == AF_INET6)
- p->msg_hdr.msg_namelen = sizeof(struct sockaddr_in6);
- else if (req->addr.ss_family == AF_INET)
- p->msg_hdr.msg_namelen = sizeof(struct sockaddr_in);
- else if (req->addr.ss_family == AF_UNIX)
- p->msg_hdr.msg_namelen = sizeof(struct sockaddr_un);
- else {
- assert(0 && "unsupported address family");
- abort();
- }
- }
- h[pkts].msg_hdr.msg_iov = (struct iovec*) req->bufs;
- h[pkts].msg_hdr.msg_iovlen = req->nbufs;
- }
-
-#if defined(__APPLE__)
- do
- npkts = sendmsg_x(handle->io_watcher.fd, h, pkts, MSG_DONTWAIT);
- while (npkts == -1 && errno == EINTR);
-#else
- do
- npkts = sendmmsg(handle->io_watcher.fd, h, pkts, 0);
- while (npkts == -1 && errno == EINTR);
-#endif
-
- if (npkts < 1) {
- if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS)
- return;
- for (i = 0, q = uv__queue_head(&handle->write_queue);
- i < pkts && q != &handle->write_queue;
- ++i, q = uv__queue_head(&handle->write_queue)) {
- req = uv__queue_data(q, uv_udp_send_t, queue);
- req->status = UV__ERR(errno);
- uv__queue_remove(&req->queue);
- uv__queue_insert_tail(&handle->write_completed_queue, &req->queue);
- }
- uv__io_feed(handle->loop, &handle->io_watcher);
- return;
- }
-
- /* Safety: npkts known to be >0 below. Hence cast from ssize_t
- * to size_t safe.
- */
- for (i = 0, q = uv__queue_head(&handle->write_queue);
- i < (size_t)npkts && q != &handle->write_queue;
- ++i, q = uv__queue_head(&handle->write_queue)) {
- req = uv__queue_data(q, uv_udp_send_t, queue);
- req->status = req->bufs[0].len;
-
- /* Sending a datagram is an atomic operation: either all data
- * is written or nothing is (and EMSGSIZE is raised). That is
- * why we don't handle partial writes. Just pop the request
- * off the write queue and onto the completed queue, done.
- */
- uv__queue_remove(&req->queue);
- uv__queue_insert_tail(&handle->write_completed_queue, &req->queue);
- }
-
- /* couldn't batch everything, continue sending (jump to avoid stack growth) */
- if (!uv__queue_empty(&handle->write_queue))
- goto write_queue_drain;
-
- uv__io_feed(handle->loop, &handle->io_watcher);
-}
-#endif /* __linux__ || ____FreeBSD__ || __APPLE__ */
-
-static void uv__udp_sendmsg(uv_udp_t* handle) {
- struct uv__queue* q;
- uv_udp_send_t* req;
-
- if (uv__queue_empty(&handle->write_queue))
- return;
-
- q = uv__queue_head(&handle->write_queue);
- req = uv__queue_data(q, uv_udp_send_t, queue);
-
-#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
- /* Use sendmmsg() if this send request contains more than one datagram OR
- * there is more than one send request (because that automatically implies
- * there is more than one datagram.)
- */
- if (req->nbufs != 1 || &handle->write_queue != uv__queue_next(&req->queue))
- return uv__udp_sendmsg_many(handle);
-#endif
-
- return uv__udp_sendmsg_one(handle, req);
-}
/* On the BSDs, SO_REUSEPORT implies SO_REUSEADDR but with some additional
* refinements for programs that use multicast. Therefore we preferentially
@@ -743,11 +584,11 @@ int uv__udp_send(uv_udp_send_t* req,
empty_queue = (handle->send_queue_count == 0);
uv__req_init(handle->loop, req, UV_UDP_SEND);
- assert(addrlen <= sizeof(req->addr));
+ assert(addrlen <= sizeof(req->u.storage));
if (addr == NULL)
- req->addr.ss_family = AF_UNSPEC;
+ req->u.storage.ss_family = AF_UNSPEC;
else
- memcpy(&req->addr, addr, addrlen);
+ memcpy(&req->u.storage, addr, addrlen);
req->send_cb = send_cb;
req->handle = handle;
req->nbufs = nbufs;
@@ -790,10 +631,9 @@ int uv__udp_try_send(uv_udp_t* handle,
const struct sockaddr* addr,
unsigned int addrlen) {
int err;
- struct msghdr h;
- ssize_t size;
- assert(nbufs > 0);
+ if (nbufs < 1)
+ return UV_EINVAL;
/* already sending a message */
if (handle->send_queue_count != 0)
@@ -807,24 +647,11 @@ int uv__udp_try_send(uv_udp_t* handle,
assert(handle->flags & UV_HANDLE_UDP_CONNECTED);
}
- memset(&h, 0, sizeof h);
- h.msg_name = (struct sockaddr*) addr;
- h.msg_namelen = addrlen;
- h.msg_iov = (struct iovec*) bufs;
- h.msg_iovlen = nbufs;
+ err = uv__udp_sendmsg1(handle->io_watcher.fd, bufs, nbufs, addr);
+ if (err > 0)
+ return uv__count_bufs(bufs, nbufs);
- do {
- size = sendmsg(handle->io_watcher.fd, &h, 0);
- } while (size == -1 && errno == EINTR);
-
- if (size == -1) {
- if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS)
- return UV_EAGAIN;
- else
- return UV__ERR(errno);
- }
-
- return size;
+ return err;
}
@@ -1401,3 +1228,191 @@ int uv__udp_recv_stop(uv_udp_t* handle) {
return 0;
}
+
+
+static int uv__udp_prep_pkt(struct msghdr* h,
+ const uv_buf_t* bufs,
+ const unsigned int nbufs,
+ const struct sockaddr* addr) {
+ memset(h, 0, sizeof(*h));
+ h->msg_name = (void*) addr;
+ h->msg_iov = (void*) bufs;
+ h->msg_iovlen = nbufs;
+ if (addr == NULL)
+ return 0;
+ switch (addr->sa_family) {
+ case AF_INET:
+ h->msg_namelen = sizeof(struct sockaddr_in);
+ return 0;
+ case AF_INET6:
+ h->msg_namelen = sizeof(struct sockaddr_in6);
+ return 0;
+ case AF_UNIX:
+ h->msg_namelen = sizeof(struct sockaddr_un);
+ return 0;
+ case AF_UNSPEC:
+ h->msg_name = NULL;
+ return 0;
+ }
+ return UV_EINVAL;
+}
+
+
+static int uv__udp_sendmsg1(int fd,
+ const uv_buf_t* bufs,
+ unsigned int nbufs,
+ const struct sockaddr* addr) {
+ struct msghdr h;
+ int r;
+
+ if ((r = uv__udp_prep_pkt(&h, bufs, nbufs, addr)))
+ return r;
+
+ do
+ r = sendmsg(fd, &h, 0);
+ while (r == -1 && errno == EINTR);
+
+ if (r < 0) {
+ r = UV__ERR(errno);
+ if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS)
+ r = UV_EAGAIN;
+ return r;
+ }
+
+ /* UDP sockets don't EOF so we don't have to handle r=0 specially,
+ * that only happens when the input was a zero-sized buffer.
+ */
+ return 1;
+}
+
+
+static int uv__udp_sendmsgv(int fd,
+ unsigned int count,
+ uv_buf_t* bufs[/*count*/],
+ unsigned int nbufs[/*count*/],
+ struct sockaddr* addrs[/*count*/]) {
+ unsigned int i;
+ int nsent;
+ int r;
+
+ r = 0;
+ nsent = 0;
+
+#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
+ if (count > 1) {
+ for (i = 0; i < count; /*empty*/) {
+ struct mmsghdr m[20];
+ unsigned int n;
+
+ for (n = 0; i < count && n < ARRAY_SIZE(m); i++, n++)
+ if ((r = uv__udp_prep_pkt(&m[n].msg_hdr, bufs[i], nbufs[i], addrs[i])))
+ goto exit;
+
+ do
+#if defined(__APPLE__)
+ r = sendmsg_x(fd, m, n, MSG_DONTWAIT);
+#else
+ r = sendmmsg(fd, m, n, 0);
+#endif
+ while (r == -1 && errno == EINTR);
+
+ if (r < 1)
+ goto exit;
+
+ nsent += r;
+ i += r;
+ }
+
+ goto exit;
+ }
+#endif /* defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) */
+
+ for (i = 0; i < count; i++, nsent++)
+ if ((r = uv__udp_sendmsg1(fd, bufs[i], nbufs[i], addrs[i])))
+ goto exit; /* goto to avoid unused label warning. */
+
+exit:
+
+ if (nsent > 0)
+ return nsent;
+
+ if (r < 0) {
+ r = UV__ERR(errno);
+ if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS)
+ r = UV_EAGAIN;
+ }
+
+ return r;
+}
+
+
+static void uv__udp_sendmsg(uv_udp_t* handle) {
+ static const int N = 20;
+ struct sockaddr* addrs[N];
+ unsigned int nbufs[N];
+ uv_buf_t* bufs[N];
+ struct uv__queue* q;
+ uv_udp_send_t* req;
+ int n;
+
+ if (uv__queue_empty(&handle->write_queue))
+ return;
+
+again:
+ n = 0;
+ q = uv__queue_head(&handle->write_queue);
+ do {
+ req = uv__queue_data(q, uv_udp_send_t, queue);
+ addrs[n] = &req->u.addr;
+ nbufs[n] = req->nbufs;
+ bufs[n] = req->bufs;
+ q = uv__queue_next(q);
+ n++;
+ } while (n < N && q != &handle->write_queue);
+
+ n = uv__udp_sendmsgv(handle->io_watcher.fd, n, bufs, nbufs, addrs);
+ while (n > 0) {
+ q = uv__queue_head(&handle->write_queue);
+ req = uv__queue_data(q, uv_udp_send_t, queue);
+ req->status = uv__count_bufs(req->bufs, req->nbufs);
+ uv__queue_remove(&req->queue);
+ uv__queue_insert_tail(&handle->write_completed_queue, &req->queue);
+ n--;
+ }
+
+ if (n == 0) {
+ if (uv__queue_empty(&handle->write_queue))
+ goto feed;
+ goto again;
+ }
+
+ if (n == UV_EAGAIN)
+ return;
+
+ /* Register the error against first request in queue because that
+ * is the request that uv__udp_sendmsgv tried but failed to send,
+ * because if it did send any requests, it won't return an error.
+ */
+ q = uv__queue_head(&handle->write_queue);
+ req = uv__queue_data(q, uv_udp_send_t, queue);
+ req->status = n;
+ uv__queue_remove(&req->queue);
+ uv__queue_insert_tail(&handle->write_completed_queue, &req->queue);
+feed:
+ uv__io_feed(handle->loop, &handle->io_watcher);
+}
+
+
+int uv__udp_try_send2(uv_udp_t* handle,
+ unsigned int count,
+ uv_buf_t* bufs[/*count*/],
+ unsigned int nbufs[/*count*/],
+ struct sockaddr* addrs[/*count*/]) {
+ int fd;
+
+ fd = handle->io_watcher.fd;
+ if (fd == -1)
+ return UV_EINVAL;
+
+ return uv__udp_sendmsgv(fd, count, bufs, nbufs, addrs);
+}
diff --git a/deps/uv/src/uv-common.c b/deps/uv/src/uv-common.c
index 2200fe3f0a41e2..60ff56b9dd7391 100644
--- a/deps/uv/src/uv-common.c
+++ b/deps/uv/src/uv-common.c
@@ -514,6 +514,25 @@ int uv_udp_try_send(uv_udp_t* handle,
}
+int uv_udp_try_send2(uv_udp_t* handle,
+ unsigned int count,
+ uv_buf_t* bufs[/*count*/],
+ unsigned int nbufs[/*count*/],
+ struct sockaddr* addrs[/*count*/],
+ unsigned int flags) {
+ if (count < 1)
+ return UV_EINVAL;
+
+ if (flags != 0)
+ return UV_EINVAL;
+
+ if (handle->send_queue_count > 0)
+ return UV_EAGAIN;
+
+ return uv__udp_try_send2(handle, count, bufs, nbufs, addrs);
+}
+
+
int uv_udp_recv_start(uv_udp_t* handle,
uv_alloc_cb alloc_cb,
uv_udp_recv_cb recv_cb) {
@@ -644,6 +663,9 @@ int uv_send_buffer_size(uv_handle_t* handle, int *value) {
int uv_fs_event_getpath(uv_fs_event_t* handle, char* buffer, size_t* size) {
size_t required_len;
+ if (buffer == NULL || size == NULL || *size == 0)
+ return UV_EINVAL;
+
if (!uv__is_active(handle)) {
*size = 0;
return UV_EINVAL;
diff --git a/deps/uv/src/uv-common.h b/deps/uv/src/uv-common.h
index 4baede2e506ee1..372f0c4b3ac39e 100644
--- a/deps/uv/src/uv-common.h
+++ b/deps/uv/src/uv-common.h
@@ -191,6 +191,12 @@ int uv__udp_try_send(uv_udp_t* handle,
const struct sockaddr* addr,
unsigned int addrlen);
+int uv__udp_try_send2(uv_udp_t* handle,
+ unsigned int count,
+ uv_buf_t* bufs[/*count*/],
+ unsigned int nbufs[/*count*/],
+ struct sockaddr* addrs[/*count*/]);
+
int uv__udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloccb,
uv_udp_recv_cb recv_cb);
@@ -428,4 +434,18 @@ struct uv__loop_internal_fields_s {
#endif /* __linux__ */
};
+#if defined(_WIN32)
+# define UV_PTHREAD_MAX_NAMELEN_NP 32767
+#elif defined(__APPLE__)
+# define UV_PTHREAD_MAX_NAMELEN_NP 64
+#elif defined(__NetBSD__) || defined(__illumos__)
+# define UV_PTHREAD_MAX_NAMELEN_NP PTHREAD_MAX_NAMELEN_NP
+#elif defined (__linux__)
+# define UV_PTHREAD_MAX_NAMELEN_NP 16
+#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
+# define UV_PTHREAD_MAX_NAMELEN_NP (MAXCOMLEN + 1)
+#else
+# define UV_PTHREAD_MAX_NAMELEN_NP 16
+#endif
+
#endif /* UV_COMMON_H_ */
diff --git a/deps/uv/src/win/core.c b/deps/uv/src/win/core.c
index e9885a0f1ff389..bc63b06673ac1a 100644
--- a/deps/uv/src/win/core.c
+++ b/deps/uv/src/win/core.c
@@ -423,97 +423,6 @@ int uv_backend_timeout(const uv_loop_t* loop) {
}
-static void uv__poll_wine(uv_loop_t* loop, DWORD timeout) {
- uv__loop_internal_fields_t* lfields;
- DWORD bytes;
- ULONG_PTR key;
- OVERLAPPED* overlapped;
- uv_req_t* req;
- int repeat;
- uint64_t timeout_time;
- uint64_t user_timeout;
- int reset_timeout;
-
- lfields = uv__get_internal_fields(loop);
- timeout_time = loop->time + timeout;
-
- if (lfields->flags & UV_METRICS_IDLE_TIME) {
- reset_timeout = 1;
- user_timeout = timeout;
- timeout = 0;
- } else {
- reset_timeout = 0;
- }
-
- for (repeat = 0; ; repeat++) {
- /* Only need to set the provider_entry_time if timeout != 0. The function
- * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME.
- */
- if (timeout != 0)
- uv__metrics_set_provider_entry_time(loop);
-
- /* Store the current timeout in a location that's globally accessible so
- * other locations like uv__work_done() can determine whether the queue
- * of events in the callback were waiting when poll was called.
- */
- lfields->current_timeout = timeout;
-
- GetQueuedCompletionStatus(loop->iocp,
- &bytes,
- &key,
- &overlapped,
- timeout);
-
- if (reset_timeout != 0) {
- if (overlapped && timeout == 0)
- uv__metrics_inc_events_waiting(loop, 1);
- timeout = user_timeout;
- reset_timeout = 0;
- }
-
- /* Placed here because on success the loop will break whether there is an
- * empty package or not, or if GetQueuedCompletionStatus returned early then
- * the timeout will be updated and the loop will run again. In either case
- * the idle time will need to be updated.
- */
- uv__metrics_update_idle_time(loop);
-
- if (overlapped) {
- uv__metrics_inc_events(loop, 1);
-
- /* Package was dequeued */
- req = uv__overlapped_to_req(overlapped);
- uv__insert_pending_req(loop, req);
-
- /* Some time might have passed waiting for I/O,
- * so update the loop time here.
- */
- uv_update_time(loop);
- } else if (GetLastError() != WAIT_TIMEOUT) {
- /* Serious error */
- uv_fatal_error(GetLastError(), "GetQueuedCompletionStatus");
- } else if (timeout > 0) {
- /* GetQueuedCompletionStatus can occasionally return a little early.
- * Make sure that the desired timeout target time is reached.
- */
- uv_update_time(loop);
- if (timeout_time > loop->time) {
- timeout = (DWORD)(timeout_time - loop->time);
- /* The first call to GetQueuedCompletionStatus should return very
- * close to the target time and the second should reach it, but
- * this is not stated in the documentation. To make sure a busy
- * loop cannot happen, the timeout is increased exponentially
- * starting on the third round.
- */
- timeout += repeat ? (1 << (repeat - 1)) : 0;
- continue;
- }
- }
- break;
- }
-}
-
-
static void uv__poll(uv_loop_t* loop, DWORD timeout) {
uv__loop_internal_fields_t* lfields;
BOOL success;
@@ -553,12 +462,12 @@ static void uv__poll(uv_loop_t* loop, DWORD timeout) {
*/
lfields->current_timeout = timeout;
- success = pGetQueuedCompletionStatusEx(loop->iocp,
- overlappeds,
- ARRAY_SIZE(overlappeds),
- &count,
- timeout,
- FALSE);
+ success = GetQueuedCompletionStatusEx(loop->iocp,
+ overlappeds,
+ ARRAY_SIZE(overlappeds),
+ &count,
+ timeout,
+ FALSE);
if (reset_timeout != 0) {
timeout = user_timeout;
@@ -566,7 +475,7 @@ static void uv__poll(uv_loop_t* loop, DWORD timeout) {
}
/* Placed here because on success the loop will break whether there is an
- * empty package or not, or if pGetQueuedCompletionStatusEx returned early
+ * empty package or not, or if GetQueuedCompletionStatusEx returned early
* then the timeout will be updated and the loop will run again. In either
* case the idle time will need to be updated.
*/
@@ -647,10 +556,7 @@ int uv_run(uv_loop_t *loop, uv_run_mode mode) {
uv__metrics_inc_loop_count(loop);
- if (pGetQueuedCompletionStatusEx)
- uv__poll(loop, timeout);
- else
- uv__poll_wine(loop, timeout);
+ uv__poll(loop, timeout);
/* Process immediate callbacks (e.g. write_cb) a small fixed number of
* times to avoid loop starvation.*/
diff --git a/deps/uv/src/win/fs-event.c b/deps/uv/src/win/fs-event.c
index 7ab407e05345f9..1bbb8c52be2d82 100644
--- a/deps/uv/src/win/fs-event.c
+++ b/deps/uv/src/win/fs-event.c
@@ -253,6 +253,8 @@ int uv_fs_event_start(uv_fs_event_t* handle,
}
dir_to_watch = dir;
+ uv__free(short_path);
+ short_path = NULL;
uv__free(pathw);
pathw = NULL;
}
@@ -577,6 +579,8 @@ void uv__process_fs_event_req(uv_loop_t* loop, uv_req_t* req,
info.DeletePending) {
uv__convert_utf16_to_utf8(handle->dirw, -1, &filename);
handle->cb(handle, filename, UV_RENAME, 0);
+ uv__free(filename);
+ filename = NULL;
} else {
handle->cb(handle, NULL, 0, uv_translate_sys_error(err));
}
diff --git a/deps/uv/src/win/fs.c b/deps/uv/src/win/fs.c
index f2215bb3082178..a4742aa2ec13fd 100644
--- a/deps/uv/src/win/fs.c
+++ b/deps/uv/src/win/fs.c
@@ -58,6 +58,19 @@
#define FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE 0x0010
#endif /* FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE */
+NTSTATUS uv__RtlUnicodeStringInit(
+ PUNICODE_STRING DestinationString,
+ PWSTR SourceString,
+ size_t SourceStringLen
+) {
+ if (SourceStringLen > 0x7FFF)
+ return STATUS_INVALID_PARAMETER;
+ DestinationString->MaximumLength = DestinationString->Length =
+ SourceStringLen * sizeof(SourceString[0]);
+ DestinationString->Buffer = SourceString;
+ return STATUS_SUCCESS;
+}
+
#define INIT(subtype) \
do { \
if (req == NULL) \
@@ -1689,12 +1702,12 @@ INLINE static fs__stat_path_return_t fs__stat_path(WCHAR* path,
uv_stat_t* statbuf, int do_lstat) {
FILE_STAT_BASIC_INFORMATION stat_info;
- // Check if the new fast API is available.
+ /* Check if the new fast API is available. */
if (!pGetFileInformationByName) {
return FS__STAT_PATH_TRY_SLOW;
}
- // Check if the API call fails.
+ /* Check if the API call fails. */
if (!pGetFileInformationByName(path, FileStatBasicByNameInfo, &stat_info,
sizeof(stat_info))) {
switch(GetLastError()) {
@@ -1708,7 +1721,7 @@ INLINE static fs__stat_path_return_t fs__stat_path(WCHAR* path,
return FS__STAT_PATH_TRY_SLOW;
}
- // A file handle is needed to get st_size for links.
+ /* A file handle is needed to get st_size for links. */
if ((stat_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
return FS__STAT_PATH_TRY_SLOW;
}
@@ -1802,7 +1815,6 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf,
* detect this failure and retry without do_lstat if appropriate.
*/
if (fs__readlink_handle(handle, NULL, &target_length) != 0) {
- fs__stat_assign_statbuf(statbuf, stat_info, do_lstat);
return -1;
}
stat_info.EndOfFile.QuadPart = target_length;
@@ -1941,6 +1953,179 @@ INLINE static void fs__stat_prepare_path(WCHAR* pathw) {
}
}
+INLINE static DWORD fs__stat_directory(WCHAR* path, uv_stat_t* statbuf,
+ int do_lstat, DWORD ret_error) {
+ HANDLE handle = INVALID_HANDLE_VALUE;
+ FILE_STAT_BASIC_INFORMATION stat_info;
+ FILE_ID_FULL_DIR_INFORMATION dir_info;
+ FILE_FS_VOLUME_INFORMATION volume_info;
+ FILE_FS_DEVICE_INFORMATION device_info;
+ IO_STATUS_BLOCK io_status;
+ NTSTATUS nt_status;
+ WCHAR* path_dirpath = NULL;
+ WCHAR* path_filename = NULL;
+ UNICODE_STRING FileMask;
+ size_t len;
+ size_t split;
+ WCHAR splitchar;
+ int includes_name;
+
+ /* AKA strtok or wcscspn, in reverse. */
+ len = wcslen(path);
+ split = len;
+
+ includes_name = 0;
+ while (split > 0 && path[split - 1] != L'\\' && path[split - 1] != L'/' &&
+ path[split - 1] != L':') {
+ /* check if the path contains a character other than /,\,:,. */
+ if (path[split-1] != '.') {
+ includes_name = 1;
+ }
+ split--;
+ }
+ /* If the path is a relative path with a file name or a folder name */
+ if (split == 0 && includes_name) {
+ path_dirpath = L".";
+ /* If there is a slash or a backslash */
+ } else if (path[split - 1] == L'\\' || path[split - 1] == L'/') {
+ path_dirpath = path;
+ /* If there is no filename, consider it as a relative folder path */
+ if (!includes_name) {
+ split = len;
+ /* Else, split it */
+ } else {
+ splitchar = path[split - 1];
+ path[split - 1] = L'\0';
+ }
+ /* e.g. "..", "c:" */
+ } else {
+ path_dirpath = path;
+ split = len;
+ }
+ path_filename = &path[split];
+
+ len = 0;
+ while (1) {
+ if (path_filename[len] == L'\0')
+ break;
+ if (path_filename[len] == L'*' || path_filename[len] == L'?' ||
+ path_filename[len] == L'>' || path_filename[len] == L'<' ||
+ path_filename[len] == L'"') {
+ ret_error = ERROR_INVALID_NAME;
+ goto cleanup;
+ }
+ len++;
+ }
+
+ /* Get directory handle */
+ handle = CreateFileW(path_dirpath,
+ FILE_LIST_DIRECTORY,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+
+ if (handle == INVALID_HANDLE_VALUE) {
+ ret_error = GetLastError();
+ goto cleanup;
+ }
+
+ /* Get files in the directory */
+ nt_status = uv__RtlUnicodeStringInit(&FileMask, path_filename, len);
+ if (!NT_SUCCESS(nt_status)) {
+ ret_error = pRtlNtStatusToDosError(nt_status);
+ goto cleanup;
+ }
+ nt_status = pNtQueryDirectoryFile(handle,
+ NULL,
+ NULL,
+ NULL,
+ &io_status,
+ &dir_info,
+ sizeof(dir_info),
+ FileIdFullDirectoryInformation,
+ TRUE,
+ &FileMask,
+ TRUE);
+
+ /* Buffer overflow (a warning status code) is expected here since there isn't
+ * enough space to store the FileName, and actually indicates success. */
+ if (!NT_SUCCESS(nt_status) && nt_status != STATUS_BUFFER_OVERFLOW) {
+ if (nt_status == STATUS_NO_MORE_FILES)
+ ret_error = ERROR_PATH_NOT_FOUND;
+ else
+ ret_error = pRtlNtStatusToDosError(nt_status);
+ goto cleanup;
+ }
+
+ /* Assign values to stat_info */
+ memset(&stat_info, 0, sizeof(FILE_STAT_BASIC_INFORMATION));
+ stat_info.FileAttributes = dir_info.FileAttributes;
+ stat_info.CreationTime.QuadPart = dir_info.CreationTime.QuadPart;
+ stat_info.LastAccessTime.QuadPart = dir_info.LastAccessTime.QuadPart;
+ stat_info.LastWriteTime.QuadPart = dir_info.LastWriteTime.QuadPart;
+ if (stat_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
+ /* A file handle is needed to get st_size for the link (from
+ * FSCTL_GET_REPARSE_POINT), which is required by posix, but we are here
+ * because getting the file handle failed. We could get just the
+ * ReparsePointTag by querying FILE_ID_EXTD_DIR_INFORMATION instead to make
+ * sure this really is a link before giving up here on the uv_fs_stat call,
+ * but that doesn't seem essential. */
+ if (!do_lstat)
+ goto cleanup;
+ stat_info.EndOfFile.QuadPart = 0;
+ stat_info.AllocationSize.QuadPart = 0;
+ } else {
+ stat_info.EndOfFile.QuadPart = dir_info.EndOfFile.QuadPart;
+ stat_info.AllocationSize.QuadPart = dir_info.AllocationSize.QuadPart;
+ }
+ stat_info.ChangeTime.QuadPart = dir_info.ChangeTime.QuadPart;
+ stat_info.FileId.QuadPart = dir_info.FileId.QuadPart;
+
+ /* Finish up by getting device info from the directory handle,
+ * since files presumably must live on their device. */
+ nt_status = pNtQueryVolumeInformationFile(handle,
+ &io_status,
+ &volume_info,
+ sizeof volume_info,
+ FileFsVolumeInformation);
+
+ /* Buffer overflow (a warning status code) is expected here. */
+ if (io_status.Status == STATUS_NOT_IMPLEMENTED) {
+ stat_info.VolumeSerialNumber.QuadPart = 0;
+ } else if (NT_ERROR(nt_status)) {
+ ret_error = pRtlNtStatusToDosError(nt_status);
+ goto cleanup;
+ } else {
+ stat_info.VolumeSerialNumber.QuadPart = volume_info.VolumeSerialNumber;
+ }
+
+ nt_status = pNtQueryVolumeInformationFile(handle,
+ &io_status,
+ &device_info,
+ sizeof device_info,
+ FileFsDeviceInformation);
+
+ /* Buffer overflow (a warning status code) is expected here. */
+ if (NT_ERROR(nt_status)) {
+ ret_error = pRtlNtStatusToDosError(nt_status);
+ goto cleanup;
+ }
+
+ stat_info.DeviceType = device_info.DeviceType;
+ stat_info.NumberOfLinks = 1; /* No way to recover this info. */
+
+ fs__stat_assign_statbuf(statbuf, stat_info, do_lstat);
+ ret_error = 0;
+
+cleanup:
+ if (split != 0)
+ path[split - 1] = splitchar;
+ if (handle != INVALID_HANDLE_VALUE)
+ CloseHandle(handle);
+ return ret_error;
+}
INLINE static DWORD fs__stat_impl_from_path(WCHAR* path,
int do_lstat,
@@ -1949,7 +2134,7 @@ INLINE static DWORD fs__stat_impl_from_path(WCHAR* path,
DWORD flags;
DWORD ret;
- // If new API exists, try to use it.
+ /* If new API exists, try to use it. */
switch (fs__stat_path(path, statbuf, do_lstat)) {
case FS__STAT_PATH_SUCCESS:
return 0;
@@ -1959,7 +2144,7 @@ INLINE static DWORD fs__stat_impl_from_path(WCHAR* path,
break;
}
- // If the new API does not exist, use the old API.
+ /* If the new API does not exist, use the old API. */
flags = FILE_FLAG_BACKUP_SEMANTICS;
if (do_lstat)
flags |= FILE_FLAG_OPEN_REPARSE_POINT;
@@ -1972,8 +2157,12 @@ INLINE static DWORD fs__stat_impl_from_path(WCHAR* path,
flags,
NULL);
- if (handle == INVALID_HANDLE_VALUE)
- return GetLastError();
+ if (handle == INVALID_HANDLE_VALUE) {
+ ret = GetLastError();
+ if (ret != ERROR_ACCESS_DENIED && ret != ERROR_SHARING_VIOLATION)
+ return ret;
+ return fs__stat_directory(path, statbuf, do_lstat, ret);
+ }
if (fs__stat_handle(handle, statbuf, do_lstat) != 0)
ret = GetLastError();
diff --git a/deps/uv/src/win/pipe.c b/deps/uv/src/win/pipe.c
index d46ecb9fc702e6..d05bfd28aec8b9 100644
--- a/deps/uv/src/win/pipe.c
+++ b/deps/uv/src/win/pipe.c
@@ -1161,9 +1161,9 @@ int uv__pipe_accept(uv_pipe_t* server, uv_stream_t* client) {
err = uv__tcp_xfer_import(
(uv_tcp_t*) client, item->xfer_type, &item->xfer_info);
-
+
uv__free(item);
-
+
if (err != 0)
return err;
@@ -1738,7 +1738,7 @@ static DWORD uv__pipe_get_ipc_remote_pid(uv_pipe_t* handle) {
GetNamedPipeServerProcessId(handle->handle, pid);
}
}
-
+
return *pid;
}
@@ -2602,6 +2602,9 @@ int uv_pipe_pending_count(uv_pipe_t* handle) {
int uv_pipe_getsockname(const uv_pipe_t* handle, char* buffer, size_t* size) {
+ if (buffer == NULL || size == NULL || *size == 0)
+ return UV_EINVAL;
+
if (handle->flags & UV_HANDLE_BOUND)
return uv__pipe_getname(handle, buffer, size);
@@ -2616,6 +2619,9 @@ int uv_pipe_getsockname(const uv_pipe_t* handle, char* buffer, size_t* size) {
int uv_pipe_getpeername(const uv_pipe_t* handle, char* buffer, size_t* size) {
+ if (buffer == NULL || size == NULL || *size == 0)
+ return UV_EINVAL;
+
/* emulate unix behaviour */
if (handle->flags & UV_HANDLE_BOUND)
return UV_ENOTCONN;
diff --git a/deps/uv/src/win/thread.c b/deps/uv/src/win/thread.c
index bf39b88633b0d8..436846a716807e 100644
--- a/deps/uv/src/win/thread.c
+++ b/deps/uv/src/win/thread.c
@@ -95,6 +95,15 @@ int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) {
return uv_thread_create_ex(tid, ¶ms, entry, arg);
}
+
+int uv_thread_detach(uv_thread_t *tid) {
+ if (CloseHandle(*tid) == 0)
+ return uv_translate_sys_error(GetLastError());
+
+ return 0;
+}
+
+
int uv_thread_create_ex(uv_thread_t* tid,
const uv_thread_options_t* params,
void (*entry)(void *arg),
@@ -269,6 +278,71 @@ int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2) {
}
+int uv_thread_setname(const char* name) {
+ HRESULT hr;
+ WCHAR* namew;
+ int err;
+ char namebuf[UV_PTHREAD_MAX_NAMELEN_NP];
+
+ if (name == NULL)
+ return UV_EINVAL;
+
+ strncpy(namebuf, name, sizeof(namebuf) - 1);
+ namebuf[sizeof(namebuf) - 1] = '\0';
+
+ namew = NULL;
+ err = uv__convert_utf8_to_utf16(namebuf, &namew);
+ if (err)
+ return err;
+
+ hr = SetThreadDescription(GetCurrentThread(), namew);
+ uv__free(namew);
+ if (FAILED(hr))
+ return uv_translate_sys_error(HRESULT_CODE(hr));
+
+ return 0;
+}
+
+
+int uv_thread_getname(uv_thread_t* tid, char* name, size_t size) {
+ HRESULT hr;
+ WCHAR* namew;
+ char* thread_name;
+ size_t buf_size;
+ int r;
+ DWORD exit_code;
+
+ if (name == NULL || size == 0)
+ return UV_EINVAL;
+
+ if (tid == NULL || *tid == NULL)
+ return UV_EINVAL;
+
+ /* Check if the thread handle is valid */
+ if (!GetExitCodeThread(*tid, &exit_code) || exit_code != STILL_ACTIVE)
+ return UV_ENOENT;
+
+ namew = NULL;
+ thread_name = NULL;
+ hr = GetThreadDescription(*tid, &namew);
+ if (FAILED(hr))
+ return uv_translate_sys_error(HRESULT_CODE(hr));
+
+ buf_size = size;
+ r = uv__copy_utf16_to_utf8(namew, -1, name, &buf_size);
+ if (r == UV_ENOBUFS) {
+ r = uv__convert_utf16_to_utf8(namew, wcslen(namew), &thread_name);
+ if (r == 0) {
+ uv__strscpy(name, thread_name, size);
+ uv__free(thread_name);
+ }
+ }
+
+ LocalFree(namew);
+ return r;
+}
+
+
int uv_mutex_init(uv_mutex_t* mutex) {
InitializeCriticalSection(mutex);
return 0;
diff --git a/deps/uv/src/win/udp.c b/deps/uv/src/win/udp.c
index 5c8f6e1dd0b449..e0873c2a899c24 100644
--- a/deps/uv/src/win/udp.c
+++ b/deps/uv/src/win/udp.c
@@ -1101,7 +1101,8 @@ int uv__udp_try_send(uv_udp_t* handle,
struct sockaddr_storage converted;
int err;
- assert(nbufs > 0);
+ if (nbufs < 1)
+ return UV_EINVAL;
if (addr != NULL) {
err = uv__convert_to_localhost_if_unspecified(addr, &converted);
@@ -1141,3 +1142,21 @@ int uv__udp_try_send(uv_udp_t* handle,
return bytes;
}
+
+
+int uv__udp_try_send2(uv_udp_t* handle,
+ unsigned int count,
+ uv_buf_t* bufs[/*count*/],
+ unsigned int nbufs[/*count*/],
+ struct sockaddr* addrs[/*count*/]) {
+ unsigned int i;
+ int r;
+
+ for (i = 0; i < count; i++) {
+ r = uv_udp_try_send(handle, bufs[i], nbufs[i], addrs[i]);
+ if (r < 0)
+ return i > 0 ? i : r; /* Error if first packet, else send count. */
+ }
+
+ return i;
+}
diff --git a/deps/uv/src/win/util.c b/deps/uv/src/win/util.c
index e0dba1aaa94e28..1d1b2837e1a190 100644
--- a/deps/uv/src/win/util.c
+++ b/deps/uv/src/win/util.c
@@ -191,7 +191,7 @@ int uv_cwd(char* buffer, size_t* size) {
WCHAR *utf16_buffer;
int r;
- if (buffer == NULL || size == NULL) {
+ if (buffer == NULL || size == NULL || *size == 0) {
return UV_EINVAL;
}
@@ -874,56 +874,100 @@ void uv_free_interface_addresses(uv_interface_address_t* addresses,
int uv_getrusage(uv_rusage_t *uv_rusage) {
- FILETIME createTime, exitTime, kernelTime, userTime;
- SYSTEMTIME kernelSystemTime, userSystemTime;
- PROCESS_MEMORY_COUNTERS memCounters;
- IO_COUNTERS ioCounters;
+ FILETIME create_time, exit_time, kernel_time, user_time;
+ SYSTEMTIME kernel_system_time, user_system_time;
+ PROCESS_MEMORY_COUNTERS mem_counters;
+ IO_COUNTERS io_counters;
int ret;
- ret = GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &kernelTime, &userTime);
+ ret = GetProcessTimes(GetCurrentProcess(),
+ &create_time,
+ &exit_time,
+ &kernel_time,
+ &user_time);
if (ret == 0) {
return uv_translate_sys_error(GetLastError());
}
- ret = FileTimeToSystemTime(&kernelTime, &kernelSystemTime);
+ ret = FileTimeToSystemTime(&kernel_time, &kernel_system_time);
if (ret == 0) {
return uv_translate_sys_error(GetLastError());
}
- ret = FileTimeToSystemTime(&userTime, &userSystemTime);
+ ret = FileTimeToSystemTime(&user_time, &user_system_time);
if (ret == 0) {
return uv_translate_sys_error(GetLastError());
}
ret = GetProcessMemoryInfo(GetCurrentProcess(),
- &memCounters,
- sizeof(memCounters));
+ &mem_counters,
+ sizeof(mem_counters));
if (ret == 0) {
return uv_translate_sys_error(GetLastError());
}
- ret = GetProcessIoCounters(GetCurrentProcess(), &ioCounters);
+ ret = GetProcessIoCounters(GetCurrentProcess(), &io_counters);
if (ret == 0) {
return uv_translate_sys_error(GetLastError());
}
memset(uv_rusage, 0, sizeof(*uv_rusage));
- uv_rusage->ru_utime.tv_sec = userSystemTime.wHour * 3600 +
- userSystemTime.wMinute * 60 +
- userSystemTime.wSecond;
- uv_rusage->ru_utime.tv_usec = userSystemTime.wMilliseconds * 1000;
+ uv_rusage->ru_utime.tv_sec = user_system_time.wHour * 3600 +
+ user_system_time.wMinute * 60 +
+ user_system_time.wSecond;
+ uv_rusage->ru_utime.tv_usec = user_system_time.wMilliseconds * 1000;
- uv_rusage->ru_stime.tv_sec = kernelSystemTime.wHour * 3600 +
- kernelSystemTime.wMinute * 60 +
- kernelSystemTime.wSecond;
- uv_rusage->ru_stime.tv_usec = kernelSystemTime.wMilliseconds * 1000;
+ uv_rusage->ru_stime.tv_sec = kernel_system_time.wHour * 3600 +
+ kernel_system_time.wMinute * 60 +
+ kernel_system_time.wSecond;
+ uv_rusage->ru_stime.tv_usec = kernel_system_time.wMilliseconds * 1000;
- uv_rusage->ru_majflt = (uint64_t) memCounters.PageFaultCount;
- uv_rusage->ru_maxrss = (uint64_t) memCounters.PeakWorkingSetSize / 1024;
+ uv_rusage->ru_majflt = (uint64_t) mem_counters.PageFaultCount;
+ uv_rusage->ru_maxrss = (uint64_t) mem_counters.PeakWorkingSetSize / 1024;
- uv_rusage->ru_oublock = (uint64_t) ioCounters.WriteOperationCount;
- uv_rusage->ru_inblock = (uint64_t) ioCounters.ReadOperationCount;
+ uv_rusage->ru_oublock = (uint64_t) io_counters.WriteOperationCount;
+ uv_rusage->ru_inblock = (uint64_t) io_counters.ReadOperationCount;
+
+ return 0;
+}
+
+
+int uv_getrusage_thread(uv_rusage_t* uv_rusage) {
+ FILETIME create_time, exit_time, kernel_time, user_time;
+ SYSTEMTIME kernel_system_time, user_system_time;
+ int ret;
+
+ ret = GetThreadTimes(GetCurrentThread(),
+ &create_time,
+ &exit_time,
+ &kernel_time,
+ &user_time);
+ if (ret == 0) {
+ return uv_translate_sys_error(GetLastError());
+ }
+
+ ret = FileTimeToSystemTime(&kernel_time, &kernel_system_time);
+ if (ret == 0) {
+ return uv_translate_sys_error(GetLastError());
+ }
+
+ ret = FileTimeToSystemTime(&user_time, &user_system_time);
+ if (ret == 0) {
+ return uv_translate_sys_error(GetLastError());
+ }
+
+ memset(uv_rusage, 0, sizeof(*uv_rusage));
+
+ uv_rusage->ru_utime.tv_sec = user_system_time.wHour * 3600 +
+ user_system_time.wMinute * 60 +
+ user_system_time.wSecond;
+ uv_rusage->ru_utime.tv_usec = user_system_time.wMilliseconds * 1000;
+
+ uv_rusage->ru_stime.tv_sec = kernel_system_time.wHour * 3600 +
+ kernel_system_time.wMinute * 60 +
+ kernel_system_time.wSecond;
+ uv_rusage->ru_stime.tv_usec = kernel_system_time.wMilliseconds * 1000;
return 0;
}
@@ -1589,7 +1633,7 @@ int uv_os_uname(uv_utsname_t* buffer) {
version_size = sizeof(buffer->version) - version_size;
r = uv__copy_utf16_to_utf8(os_info.szCSDVersion,
-1,
- buffer->version +
+ buffer->version +
sizeof(buffer->version) - version_size,
&version_size);
if (r)
diff --git a/deps/uv/src/win/winapi.c b/deps/uv/src/win/winapi.c
index a74108db03e701..315a0d49aff50b 100644
--- a/deps/uv/src/win/winapi.c
+++ b/deps/uv/src/win/winapi.c
@@ -36,9 +36,6 @@ sNtQueryDirectoryFile pNtQueryDirectoryFile;
sNtQuerySystemInformation pNtQuerySystemInformation;
sNtQueryInformationProcess pNtQueryInformationProcess;
-/* Kernel32 function pointers */
-sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx;
-
/* Powrprof.dll function pointer */
sPowerRegisterSuspendResumeNotification pPowerRegisterSuspendResumeNotification;
@@ -55,7 +52,6 @@ void uv__winapi_init(void) {
HMODULE ntdll_module;
HMODULE powrprof_module;
HMODULE user32_module;
- HMODULE kernel32_module;
HMODULE ws2_32_module;
HMODULE api_win_core_file_module;
@@ -121,15 +117,6 @@ void uv__winapi_init(void) {
uv_fatal_error(GetLastError(), "GetProcAddress");
}
- kernel32_module = GetModuleHandleA("kernel32.dll");
- if (kernel32_module == NULL) {
- uv_fatal_error(GetLastError(), "GetModuleHandleA");
- }
-
- pGetQueuedCompletionStatusEx = (sGetQueuedCompletionStatusEx) GetProcAddress(
- kernel32_module,
- "GetQueuedCompletionStatusEx");
-
powrprof_module = LoadLibraryExA("powrprof.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
if (powrprof_module != NULL) {
pPowerRegisterSuspendResumeNotification = (sPowerRegisterSuspendResumeNotification)
diff --git a/deps/uv/src/win/winapi.h b/deps/uv/src/win/winapi.h
index 5800e70dfd7d11..4e0ccc61baf225 100644
--- a/deps/uv/src/win/winapi.h
+++ b/deps/uv/src/win/winapi.h
@@ -4150,40 +4150,35 @@ typedef struct _FILE_STAT_BASIC_INFORMATION {
} FILE_STAT_BASIC_INFORMATION;
#endif
-/* MinGW already has a definition for REPARSE_DATA_BUFFER, but mingw-w64 does
- * not.
- */
-#if defined(_MSC_VER) || defined(__MINGW64_VERSION_MAJOR)
- typedef struct _REPARSE_DATA_BUFFER {
- ULONG ReparseTag;
- USHORT ReparseDataLength;
- USHORT Reserved;
- union {
- struct {
- USHORT SubstituteNameOffset;
- USHORT SubstituteNameLength;
- USHORT PrintNameOffset;
- USHORT PrintNameLength;
- ULONG Flags;
- WCHAR PathBuffer[1];
- } SymbolicLinkReparseBuffer;
- struct {
- USHORT SubstituteNameOffset;
- USHORT SubstituteNameLength;
- USHORT PrintNameOffset;
- USHORT PrintNameLength;
- WCHAR PathBuffer[1];
- } MountPointReparseBuffer;
- struct {
- UCHAR DataBuffer[1];
- } GenericReparseBuffer;
- struct {
- ULONG StringCount;
- WCHAR StringList[1];
- } AppExecLinkReparseBuffer;
- };
- } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
-#endif
+typedef struct _REPARSE_DATA_BUFFER {
+ ULONG ReparseTag;
+ USHORT ReparseDataLength;
+ USHORT Reserved;
+ union {
+ struct {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ ULONG Flags;
+ WCHAR PathBuffer[1];
+ } SymbolicLinkReparseBuffer;
+ struct {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ WCHAR PathBuffer[1];
+ } MountPointReparseBuffer;
+ struct {
+ UCHAR DataBuffer[1];
+ } GenericReparseBuffer;
+ struct {
+ ULONG StringCount;
+ WCHAR StringList[1];
+ } AppExecLinkReparseBuffer;
+ };
+} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
typedef struct _IO_STATUS_BLOCK {
union {
@@ -4292,6 +4287,22 @@ typedef struct _FILE_BOTH_DIR_INFORMATION {
WCHAR FileName[1];
} FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION;
+typedef struct _FILE_ID_FULL_DIR_INFORMATION {
+ ULONG NextEntryOffset;
+ ULONG FileIndex;
+ LARGE_INTEGER CreationTime;
+ LARGE_INTEGER LastAccessTime;
+ LARGE_INTEGER LastWriteTime;
+ LARGE_INTEGER ChangeTime;
+ LARGE_INTEGER EndOfFile;
+ LARGE_INTEGER AllocationSize;
+ ULONG FileAttributes;
+ ULONG FileNameLength;
+ ULONG EaSize;
+ LARGE_INTEGER FileId;
+ WCHAR FileName[1];
+} FILE_ID_FULL_DIR_INFORMATION, *PFILE_ID_FULL_DIR_INFORMATION;
+
typedef struct _FILE_BASIC_INFORMATION {
LARGE_INTEGER CreationTime;
LARGE_INTEGER LastAccessTime;
@@ -4661,15 +4672,6 @@ typedef NTSTATUS (NTAPI *sNtQueryInformationProcess)
# define SYMBOLIC_LINK_FLAG_DIRECTORY 0x1
#endif
-#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
- typedef struct _OVERLAPPED_ENTRY {
- ULONG_PTR lpCompletionKey;
- LPOVERLAPPED lpOverlapped;
- ULONG_PTR Internal;
- DWORD dwNumberOfBytesTransferred;
- } OVERLAPPED_ENTRY, *LPOVERLAPPED_ENTRY;
-#endif
-
/* from wincon.h */
#ifndef ENABLE_INSERT_MODE
# define ENABLE_INSERT_MODE 0x20
@@ -4716,14 +4718,6 @@ typedef NTSTATUS (NTAPI *sNtQueryInformationProcess)
# define ERROR_MUI_FILE_NOT_LOADED 15105
#endif
-typedef BOOL (WINAPI *sGetQueuedCompletionStatusEx)
- (HANDLE CompletionPort,
- LPOVERLAPPED_ENTRY lpCompletionPortEntries,
- ULONG ulCount,
- PULONG ulNumEntriesRemoved,
- DWORD dwMilliseconds,
- BOOL fAlertable);
-
/* from powerbase.h */
#ifndef DEVICE_NOTIFY_CALLBACK
# define DEVICE_NOTIFY_CALLBACK 2
@@ -4818,9 +4812,6 @@ extern sNtQueryDirectoryFile pNtQueryDirectoryFile;
extern sNtQuerySystemInformation pNtQuerySystemInformation;
extern sNtQueryInformationProcess pNtQueryInformationProcess;
-/* Kernel32 function pointers */
-extern sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx;
-
/* Powrprof.dll function pointer */
extern sPowerRegisterSuspendResumeNotification pPowerRegisterSuspendResumeNotification;
@@ -4837,4 +4828,13 @@ typedef int (WINAPI *uv_sGetHostNameW)
int);
extern uv_sGetHostNameW pGetHostNameW;
+/* processthreadsapi.h */
+#if defined(__MINGW32__)
+WINBASEAPI
+HRESULT WINAPI GetThreadDescription(HANDLE hThread,
+ PWSTR *ppszThreadDescription);
+WINBASEAPI
+HRESULT WINAPI SetThreadDescription(HANDLE hThread, PCWSTR lpThreadDescription);
+#endif
+
#endif /* UV_WIN_WINAPI_H_ */
diff --git a/deps/uv/src/win/winsock.h b/deps/uv/src/win/winsock.h
index 2af958870a7de6..bb3808a35c27e6 100644
--- a/deps/uv/src/win/winsock.h
+++ b/deps/uv/src/win/winsock.h
@@ -154,47 +154,6 @@ typedef struct _AFD_RECV_INFO {
#define IOCTL_AFD_POLL \
_AFD_CONTROL_CODE(AFD_POLL, METHOD_BUFFERED)
-#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
-typedef struct _IP_ADAPTER_UNICAST_ADDRESS_XP {
- /* FIXME: __C89_NAMELESS was removed */
- /* __C89_NAMELESS */ union {
- ULONGLONG Alignment;
- /* __C89_NAMELESS */ struct {
- ULONG Length;
- DWORD Flags;
- };
- };
- struct _IP_ADAPTER_UNICAST_ADDRESS_XP *Next;
- SOCKET_ADDRESS Address;
- IP_PREFIX_ORIGIN PrefixOrigin;
- IP_SUFFIX_ORIGIN SuffixOrigin;
- IP_DAD_STATE DadState;
- ULONG ValidLifetime;
- ULONG PreferredLifetime;
- ULONG LeaseLifetime;
-} IP_ADAPTER_UNICAST_ADDRESS_XP,*PIP_ADAPTER_UNICAST_ADDRESS_XP;
-
-typedef struct _IP_ADAPTER_UNICAST_ADDRESS_LH {
- union {
- ULONGLONG Alignment;
- struct {
- ULONG Length;
- DWORD Flags;
- };
- };
- struct _IP_ADAPTER_UNICAST_ADDRESS_LH *Next;
- SOCKET_ADDRESS Address;
- IP_PREFIX_ORIGIN PrefixOrigin;
- IP_SUFFIX_ORIGIN SuffixOrigin;
- IP_DAD_STATE DadState;
- ULONG ValidLifetime;
- ULONG PreferredLifetime;
- ULONG LeaseLifetime;
- UINT8 OnLinkPrefixLength;
-} IP_ADAPTER_UNICAST_ADDRESS_LH,*PIP_ADAPTER_UNICAST_ADDRESS_LH;
-
-#endif
-
int uv__convert_to_localhost_if_unspecified(const struct sockaddr* addr,
struct sockaddr_storage* storage);
diff --git a/deps/uv/test/runner.c b/deps/uv/test/runner.c
index d1dd02f5ce0806..54abb39dd22886 100644
--- a/deps/uv/test/runner.c
+++ b/deps/uv/test/runner.c
@@ -27,6 +27,11 @@
#include "task.h"
#include "uv.h"
+/* Refs: https://github.com/libuv/libuv/issues/4369 */
+#if defined(__ANDROID__)
+#include
+#endif
+
char executable_path[sizeof(executable_path)];
@@ -142,6 +147,13 @@ void log_tap_result(int test_count,
fflush(stdout);
}
+void enable_fdsan(void) {
+/* Refs: https://github.com/libuv/libuv/issues/4369 */
+#if defined(__ANDROID__)
+ android_fdsan_set_error_level(ANDROID_FDSAN_ERROR_LEVEL_WARN_ALWAYS);
+#endif
+}
+
int run_test(const char* test,
int benchmark_output,
@@ -160,6 +172,8 @@ int run_test(const char* test,
main_proc = NULL;
process_count = 0;
+ enable_fdsan();
+
#ifndef _WIN32
/* Clean up stale socket from previous run. */
remove(TEST_PIPENAME);
diff --git a/deps/uv/test/test-fs-event.c b/deps/uv/test/test-fs-event.c
index bb223a5f654c03..b53057dc25bb22 100644
--- a/deps/uv/test/test-fs-event.c
+++ b/deps/uv/test/test-fs-event.c
@@ -153,7 +153,14 @@ static void fs_event_cb_del_dir(uv_fs_event_t* handle,
ASSERT_PTR_EQ(handle, &fs_event);
ASSERT_OK(status);
ASSERT(events == UV_CHANGE || events == UV_RENAME);
+ /* There is a bug in the FreeBSD kernel where the filename is sometimes NULL.
+ * Refs: https://github.com/libuv/libuv/issues/4606
+ */
+ #if defined(__FreeBSD__)
+ ASSERT(filename == NULL || strcmp(filename, "watch_del_dir") == 0);
+ #else
ASSERT_OK(strcmp(filename, "watch_del_dir"));
+ #endif
ASSERT_OK(uv_fs_event_stop(handle));
uv_close((uv_handle_t*)handle, close_cb);
}
@@ -1121,7 +1128,7 @@ TEST_IMPL(fs_event_getpath) {
ASSERT_EQ(r, UV_EINVAL);
r = uv_fs_event_start(&fs_event, fail_cb, watch_dir[i], 0);
ASSERT_OK(r);
- len = 0;
+ len = 1;
r = uv_fs_event_getpath(&fs_event, buf, &len);
ASSERT_EQ(r, UV_ENOBUFS);
ASSERT_LT(len, sizeof buf); /* sanity check */
diff --git a/deps/uv/test/test-fs.c b/deps/uv/test/test-fs.c
index 33cbd428707c36..423d72dd2f7b84 100644
--- a/deps/uv/test/test-fs.c
+++ b/deps/uv/test/test-fs.c
@@ -4507,6 +4507,60 @@ TEST_IMPL(fs_open_readonly_acl) {
MAKE_VALGRIND_HAPPY(loop);
return 0;
}
+
+TEST_IMPL(fs_stat_no_permission) {
+ uv_passwd_t pwd;
+ uv_fs_t req;
+ int r;
+ char* filename = "test_file_no_permission.txt";
+
+ /* Setup - clear the ACL and remove the file */
+ loop = uv_default_loop();
+ r = uv_os_get_passwd(&pwd);
+ ASSERT_OK(r);
+ call_icacls("icacls %s /remove *S-1-1-0:(F)", filename);
+ unlink(filename);
+
+ /* Create the file */
+ r = uv_fs_open(loop,
+ &open_req1,
+ filename,
+ UV_FS_O_RDONLY | UV_FS_O_CREAT,
+ S_IRUSR,
+ NULL);
+ ASSERT_GE(r, 0);
+ ASSERT_GE(open_req1.result, 0);
+ uv_fs_req_cleanup(&open_req1);
+ r = uv_fs_close(NULL, &close_req, open_req1.result, NULL);
+ ASSERT_OK(r);
+ ASSERT_OK(close_req.result);
+ uv_fs_req_cleanup(&close_req);
+
+ /* Set up ACL */
+ r = call_icacls("icacls %s /deny *S-1-1-0:(F)", filename);
+ if (r != 0) {
+ goto acl_cleanup;
+ }
+
+ /* Read file stats */
+ r = uv_fs_stat(NULL, &req, filename, NULL);
+ if (r != 0) {
+ goto acl_cleanup;
+ }
+
+ uv_fs_req_cleanup(&req);
+
+ acl_cleanup:
+ /* Cleanup */
+ call_icacls("icacls %s /reset", filename);
+ uv_fs_unlink(NULL, &unlink_req, filename, NULL);
+ uv_fs_req_cleanup(&unlink_req);
+ unlink(filename);
+ uv_os_free_passwd(&pwd);
+ ASSERT_OK(r);
+ MAKE_VALGRIND_HAPPY(loop);
+ return 0;
+}
#endif
#ifdef _WIN32
diff --git a/deps/uv/test/test-idna.c b/deps/uv/test/test-idna.c
index 28f9eaaae9e77a..46df9f3c581015 100644
--- a/deps/uv/test/test-idna.c
+++ b/deps/uv/test/test-idna.c
@@ -39,7 +39,7 @@ TEST_IMPL(utf8_decode1) {
/* Two-byte sequences. */
p = b;
- snprintf(b, sizeof(b), "\xC2\x80\xDF\xBF");
+ snprintf(b, sizeof(b), "%s", "\xC2\x80\xDF\xBF");
ASSERT_EQ(128, uv__utf8_decode1(&p, b + sizeof(b)));
ASSERT_PTR_EQ(p, b + 2);
ASSERT_EQ(0x7FF, uv__utf8_decode1(&p, b + sizeof(b)));
@@ -47,7 +47,7 @@ TEST_IMPL(utf8_decode1) {
/* Three-byte sequences. */
p = b;
- snprintf(b, sizeof(b), "\xE0\xA0\x80\xEF\xBF\xBF");
+ snprintf(b, sizeof(b), "%s", "\xE0\xA0\x80\xEF\xBF\xBF");
ASSERT_EQ(0x800, uv__utf8_decode1(&p, b + sizeof(b)));
ASSERT_PTR_EQ(p, b + 3);
ASSERT_EQ(0xFFFF, uv__utf8_decode1(&p, b + sizeof(b)));
@@ -55,7 +55,7 @@ TEST_IMPL(utf8_decode1) {
/* Four-byte sequences. */
p = b;
- snprintf(b, sizeof(b), "\xF0\x90\x80\x80\xF4\x8F\xBF\xBF");
+ snprintf(b, sizeof(b), "%s", "\xF0\x90\x80\x80\xF4\x8F\xBF\xBF");
ASSERT_EQ(0x10000, uv__utf8_decode1(&p, b + sizeof(b)));
ASSERT_PTR_EQ(p, b + 4);
ASSERT_EQ(0x10FFFF, uv__utf8_decode1(&p, b + sizeof(b)));
@@ -63,7 +63,7 @@ TEST_IMPL(utf8_decode1) {
/* Four-byte sequences > U+10FFFF; disallowed. */
p = b;
- snprintf(b, sizeof(b), "\xF4\x90\xC0\xC0\xF7\xBF\xBF\xBF");
+ snprintf(b, sizeof(b), "%s", "\xF4\x90\xC0\xC0\xF7\xBF\xBF\xBF");
ASSERT_EQ((unsigned) -1, uv__utf8_decode1(&p, b + sizeof(b)));
ASSERT_PTR_EQ(p, b + 4);
ASSERT_EQ((unsigned) -1, uv__utf8_decode1(&p, b + sizeof(b)));
@@ -71,7 +71,7 @@ TEST_IMPL(utf8_decode1) {
/* Overlong; disallowed. */
p = b;
- snprintf(b, sizeof(b), "\xC0\x80\xC1\x80");
+ snprintf(b, sizeof(b), "%s", "\xC0\x80\xC1\x80");
ASSERT_EQ((unsigned) -1, uv__utf8_decode1(&p, b + sizeof(b)));
ASSERT_PTR_EQ(p, b + 2);
ASSERT_EQ((unsigned) -1, uv__utf8_decode1(&p, b + sizeof(b)));
@@ -79,7 +79,7 @@ TEST_IMPL(utf8_decode1) {
/* Surrogate pairs; disallowed. */
p = b;
- snprintf(b, sizeof(b), "\xED\xA0\x80\xED\xA3\xBF");
+ snprintf(b, sizeof(b), "%s", "\xED\xA0\x80\xED\xA3\xBF");
ASSERT_EQ((unsigned) -1, uv__utf8_decode1(&p, b + sizeof(b)));
ASSERT_PTR_EQ(p, b + 3);
ASSERT_EQ((unsigned) -1, uv__utf8_decode1(&p, b + sizeof(b)));
@@ -87,7 +87,7 @@ TEST_IMPL(utf8_decode1) {
/* Simply illegal. */
p = b;
- snprintf(b, sizeof(b), "\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF");
+ snprintf(b, sizeof(b), "%s", "\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF");
for (i = 1; i <= 8; i++) {
ASSERT_EQ((unsigned) -1, uv__utf8_decode1(&p, b + sizeof(b)));
@@ -218,3 +218,15 @@ TEST_IMPL(idna_toascii) {
#undef T
#endif /* __MVS__ */
+
+TEST_IMPL(wtf8) {
+ static const char input[] = "ᜄȺy𐞲:𞢢𘴇𐀀'¥3̞[
+
+struct semaphores {
+ uv_sem_t main;
+ uv_sem_t worker;
+};
+
+static void thread_run(void* arg) {
+ int r;
+ char thread_name[16];
+ struct semaphores* sem;
+ uv_thread_t thread;
+
+ sem = arg;
+
+#ifdef _WIN32
+ /* uv_thread_self isn't defined for the main thread on Windows. */
+ thread = GetCurrentThread();
+#else
+ thread = uv_thread_self();
+#endif
+
+ r = uv_thread_setname("worker-thread");
+ ASSERT_OK(r);
+
+ uv_sem_post(&sem->worker);
+
+ r = uv_thread_getname(&thread, thread_name, sizeof(thread_name));
+ ASSERT_OK(r);
+
+ ASSERT_STR_EQ(thread_name, "worker-thread");
+
+ uv_sem_wait(&sem->main);
+}
+
+TEST_IMPL(thread_name) {
+ int r;
+ uv_thread_t threads[2];
+ char tn[UV_PTHREAD_MAX_NAMELEN_NP];
+ char thread_name[UV_PTHREAD_MAX_NAMELEN_NP];
+ char long_thread_name[UV_PTHREAD_MAX_NAMELEN_NP + 1];
+ struct semaphores sem;
+
+#if defined(__ANDROID_API__) && __ANDROID_API__ < 26 || \
+ defined(_AIX) || \
+ defined(__MVS__) || \
+ defined(__PASE__)
+ RETURN_SKIP("API not available on this platform");
+#endif
+
+ ASSERT_OK(uv_sem_init(&sem.main, 0));
+ ASSERT_OK(uv_sem_init(&sem.worker, 0));
+
+ memset(thread_name, 'a', sizeof(thread_name) - 1);
+ thread_name[sizeof(thread_name) - 1] = '\0';
+
+ memset(long_thread_name, 'a', sizeof(long_thread_name) - 1);
+ long_thread_name[sizeof(long_thread_name) - 1] = '\0';
+
+#ifdef _WIN32
+ /* uv_thread_self isn't defined for the main thread on Windows. */
+ threads[0] = GetCurrentThread();
+#else
+ threads[0] = uv_thread_self();
+#endif
+
+ r = uv_thread_getname(&threads[0], tn, sizeof(tn));
+ ASSERT_OK(r);
+
+ r = uv_thread_setname(long_thread_name);
+ ASSERT_OK(r);
+
+ r = uv_thread_getname(&threads[0], tn, sizeof(tn));
+ ASSERT_OK(r);
+ ASSERT_STR_EQ(tn, thread_name);
+
+ r = uv_thread_setname(thread_name);
+ ASSERT_OK(r);
+
+ r = uv_thread_getname(&threads[0], tn, sizeof(tn));
+ ASSERT_OK(r);
+ ASSERT_STR_EQ(tn, thread_name);
+
+ r = uv_thread_getname(&threads[0], tn, 3);
+ ASSERT_OK(r);
+ ASSERT_EQ(strlen(tn), 2);
+ ASSERT_OK(memcmp(thread_name, tn, 2));
+
+ /* Illumos doesn't support non-ASCII thread names. */
+#ifndef __illumos__
+ r = uv_thread_setname("~½¬{½");
+ ASSERT_OK(r);
+
+ r = uv_thread_getname(&threads[0], tn, sizeof(tn));
+ ASSERT_OK(r);
+ ASSERT_STR_EQ(tn, "~½¬{½");
+#endif
+
+ ASSERT_OK(uv_thread_create(threads + 1, thread_run, &sem));
+
+ uv_sem_wait(&sem.worker);
+
+ r = uv_thread_getname(threads + 1, tn, sizeof(tn));
+ ASSERT_OK(r);
+
+ ASSERT_STR_EQ(tn, "worker-thread");
+
+ uv_sem_post(&sem.main);
+
+ ASSERT_OK(uv_thread_join(threads + 1));
+
+ uv_sem_destroy(&sem.main);
+ uv_sem_destroy(&sem.worker);
+
+ return 0;
+}
+
+#define MAX_THREADS 4
+
+static void* executedThreads[MAX_THREADS] = { NULL };
+static int size;
+static uv_loop_t* loop;
+
+static unsigned short int key_exists(void* key) {
+ size_t i;
+ for (i = 0; i < MAX_THREADS; i++) {
+ if (executedThreads[i] == key) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void work_cb(uv_work_t* req) {
+ uv_thread_t thread = uv_thread_self();
+ req->data = &thread;
+ char tn[UV_PTHREAD_MAX_NAMELEN_NP];
+ ASSERT_OK(uv_thread_getname(&thread, tn, sizeof(tn)));
+ ASSERT_STR_EQ(tn, "libuv-worker");
+}
+
+static void after_work_cb(uv_work_t* req, int status) {
+ ASSERT_OK(status);
+ if (!key_exists(req->data)) {
+ executedThreads[size++] = req->data;
+ }
+
+ if (size == MAX_THREADS) {
+ return;
+ }
+
+ uv_queue_work(loop, req, work_cb, after_work_cb);
+}
+
+TEST_IMPL(thread_name_threadpool) {
+ uv_work_t req;
+ loop = uv_default_loop();
+ // Just to make sure all workers will be executed
+ // with the correct thread name
+ ASSERT_OK(uv_queue_work(loop, &req, work_cb, after_work_cb));
+ uv_run(loop, UV_RUN_DEFAULT);
+ MAKE_VALGRIND_HAPPY(uv_default_loop());
+ return 0;
+}
diff --git a/deps/uv/test/test-thread.c b/deps/uv/test/test-thread.c
index d0094e304435bb..819bbd5c92399d 100644
--- a/deps/uv/test/test-thread.c
+++ b/deps/uv/test/test-thread.c
@@ -294,3 +294,13 @@ TEST_IMPL(thread_stack_size_explicit) {
return 0;
}
+
+static void thread_detach_cb(void* arg) {}
+
+TEST_IMPL(thread_detach) {
+ uv_thread_t thread;
+ ASSERT_OK(uv_thread_create(&thread, thread_detach_cb, NULL));
+ ASSERT_OK(uv_thread_detach(&thread));
+
+ return 0;
+}
diff --git a/deps/uv/test/test-udp-mmsg.c b/deps/uv/test/test-udp-mmsg.c
index c0e000b9d92bbf..73213c43d97aa2 100644
--- a/deps/uv/test/test-udp-mmsg.c
+++ b/deps/uv/test/test-udp-mmsg.c
@@ -32,12 +32,12 @@
#define BUFFER_MULTIPLIER 20
#define MAX_DGRAM_SIZE (64 * 1024)
#define NUM_SENDS 40
-#define EXPECTED_MMSG_ALLOCS (NUM_SENDS / BUFFER_MULTIPLIER)
static uv_udp_t recver;
static uv_udp_t sender;
static int recv_cb_called;
static int received_datagrams;
+static int read_bytes;
static int close_cb_called;
static int alloc_cb_called;
@@ -74,6 +74,7 @@ static void recv_cb(uv_udp_t* handle,
const struct sockaddr* addr,
unsigned flags) {
ASSERT_GE(nread, 0);
+ read_bytes += nread;
/* free and return if this is a mmsg free-only callback invocation */
if (flags & UV_UDP_MMSG_FREE) {
@@ -140,7 +141,7 @@ TEST_IMPL(udp_mmsg) {
/* On platforms that don't support mmsg, each recv gets its own alloc */
if (uv_udp_using_recvmmsg(&recver))
- ASSERT_EQ(alloc_cb_called, EXPECTED_MMSG_ALLOCS);
+ ASSERT_EQ(read_bytes, NUM_SENDS * 4); /* we're sending 4 bytes per datagram */
else
ASSERT_EQ(alloc_cb_called, recv_cb_called);
diff --git a/deps/uv/test/test-udp-multicast-join.c b/deps/uv/test/test-udp-multicast-join.c
index 9e322dc579fc33..58b055561c6ded 100644
--- a/deps/uv/test/test-udp-multicast-join.c
+++ b/deps/uv/test/test-udp-multicast-join.c
@@ -36,10 +36,9 @@ static uv_udp_t client;
static uv_udp_send_t req;
static uv_udp_send_t req_ss;
+static int darwin_ebusy_errors;
static int cl_recv_cb_called;
-
static int sv_send_cb_called;
-
static int close_cb_called;
static void alloc_cb(uv_handle_t* handle,
@@ -128,6 +127,13 @@ static void cl_recv_cb(uv_udp_t* handle,
#if !defined(__NetBSD__)
r = uv_udp_set_source_membership(&server, MULTICAST_ADDR, NULL, source_addr, UV_JOIN_GROUP);
+#if defined(__APPLE__)
+ if (r == UV_EBUSY) {
+ uv_close((uv_handle_t*) &server, close_cb);
+ darwin_ebusy_errors++;
+ return;
+ }
+#endif
ASSERT_OK(r);
#endif
@@ -160,7 +166,13 @@ TEST_IMPL(udp_multicast_join) {
r = uv_udp_set_membership(&server, MULTICAST_ADDR, NULL, UV_JOIN_GROUP);
if (r == UV_ENODEV)
RETURN_SKIP("No multicast support.");
+ if (r == UV_ENOEXEC)
+ RETURN_SKIP("No multicast support (likely a firewall issue).");
ASSERT_OK(r);
+#if defined(__ANDROID__)
+ /* It returns an ENOSYS error */
+ RETURN_SKIP("Test does not currently work in ANDROID");
+#endif
r = uv_udp_recv_start(&server, alloc_cb, cl_recv_cb);
ASSERT_OK(r);
@@ -175,6 +187,9 @@ TEST_IMPL(udp_multicast_join) {
/* run the loop till all events are processed */
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+ if (darwin_ebusy_errors > 0)
+ RETURN_SKIP("Unexplained macOS IP_ADD_SOURCE_MEMBERSHIP EBUSY bug");
+
ASSERT_EQ(2, cl_recv_cb_called);
ASSERT_EQ(2, sv_send_cb_called);
ASSERT_EQ(2, close_cb_called);
diff --git a/deps/uv/test/test-udp-multicast-join6.c b/deps/uv/test/test-udp-multicast-join6.c
index c6872e4283247d..430e4e3321e859 100644
--- a/deps/uv/test/test-udp-multicast-join6.c
+++ b/deps/uv/test/test-udp-multicast-join6.c
@@ -33,6 +33,7 @@
#if defined(__APPLE__) || \
defined(_AIX) || \
defined(__MVS__) || \
+ defined(__FreeBSD__) || \
defined(__NetBSD__) || \
defined(__OpenBSD__)
#define MULTICAST_ADDR "ff02::1%lo0"
diff --git a/deps/uv/test/test-udp-try-send.c b/deps/uv/test/test-udp-try-send.c
index 0c76fb1c84df68..6181fbbbffca3b 100644
--- a/deps/uv/test/test-udp-try-send.c
+++ b/deps/uv/test/test-udp-try-send.c
@@ -60,8 +60,6 @@ static void sv_recv_cb(uv_udp_t* handle,
const uv_buf_t* rcvbuf,
const struct sockaddr* addr,
unsigned flags) {
- ASSERT_GT(nread, 0);
-
if (nread == 0) {
ASSERT_NULL(addr);
return;
@@ -70,11 +68,17 @@ static void sv_recv_cb(uv_udp_t* handle,
ASSERT_EQ(4, nread);
ASSERT_NOT_NULL(addr);
- ASSERT_OK(memcmp("EXIT", rcvbuf->base, nread));
- uv_close((uv_handle_t*) handle, close_cb);
- uv_close((uv_handle_t*) &client, close_cb);
+ if (!memcmp("EXIT", rcvbuf->base, nread)) {
+ uv_close((uv_handle_t*) handle, close_cb);
+ uv_close((uv_handle_t*) &client, close_cb);
+ } else {
+ ASSERT_MEM_EQ(rcvbuf->base, "HELO", 4);
+ }
sv_recv_cb_called++;
+
+ if (sv_recv_cb_called == 2)
+ uv_udp_recv_stop(handle);
}
@@ -101,9 +105,33 @@ TEST_IMPL(udp_try_send) {
ASSERT_OK(r);
buf = uv_buf_init(buffer, sizeof(buffer));
+
+ r = uv_udp_try_send(&client, &buf, 0, (const struct sockaddr*) &addr);
+ ASSERT_EQ(r, UV_EINVAL);
+
r = uv_udp_try_send(&client, &buf, 1, (const struct sockaddr*) &addr);
ASSERT_EQ(r, UV_EMSGSIZE);
+ uv_buf_t* bufs[] = {&buf, &buf};
+ unsigned int nbufs[] = {1, 1};
+ struct sockaddr* addrs[] = {
+ (struct sockaddr*) &addr,
+ (struct sockaddr*) &addr,
+ };
+
+ ASSERT_EQ(0, sv_recv_cb_called);
+
+ buf = uv_buf_init("HELO", 4);
+ r = uv_udp_try_send2(&client, 2, bufs, nbufs, addrs, /*flags*/0);
+ ASSERT_EQ(r, 2);
+
+ uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+
+ ASSERT_EQ(2, sv_recv_cb_called);
+
+ r = uv_udp_recv_start(&server, alloc_cb, sv_recv_cb);
+ ASSERT_OK(r);
+
buf = uv_buf_init("EXIT", 4);
r = uv_udp_try_send(&client, &buf, 1, (const struct sockaddr*) &addr);
ASSERT_EQ(4, r);
@@ -111,7 +139,7 @@ TEST_IMPL(udp_try_send) {
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
ASSERT_EQ(2, close_cb_called);
- ASSERT_EQ(1, sv_recv_cb_called);
+ ASSERT_EQ(3, sv_recv_cb_called);
ASSERT_OK(client.send_queue_size);
ASSERT_OK(server.send_queue_size);
diff --git a/doc/api/assert.md b/doc/api/assert.md
index 3f44dffbe7de2d..32740d2f303a8d 100644
--- a/doc/api/assert.md
+++ b/doc/api/assert.md
@@ -804,8 +804,10 @@ are recursively evaluated also by the following rules.
* [`Map`][] keys and [`Set`][] items are compared unordered.
* Recursion stops when both sides differ or both sides encounter a circular
reference.
-* [`WeakMap`][] and [`WeakSet`][] comparison does not rely on their values. See
- below for further details.
+* [`WeakMap`][] and [`WeakSet`][] instances are **not** compared structurally.
+ They are only equal if they reference the same object. Any comparison between
+ different `WeakMap` or `WeakSet` instances will result in inequality,
+ even if they contain the same entries.
* [`RegExp`][] lastIndex, flags, and source are always compared, even if these
are not enumerable properties.
@@ -882,23 +884,40 @@ assert.deepStrictEqual({ [symbol1]: 1 }, { [symbol2]: 1 });
// }
const weakMap1 = new WeakMap();
-const weakMap2 = new WeakMap([[{}, {}]]);
-const weakMap3 = new WeakMap();
-weakMap3.unequal = true;
+const weakMap2 = new WeakMap();
+const obj = {};
+weakMap1.set(obj, 'value');
+weakMap2.set(obj, 'value');
+
+// Comparing different instances fails, even with same contents
assert.deepStrictEqual(weakMap1, weakMap2);
-// OK, because it is impossible to compare the entries
+// AssertionError: Values have same structure but are not reference-equal:
+//
+// WeakMap {
+//
+// }
+
+// Comparing the same instance to itself succeeds
+assert.deepStrictEqual(weakMap1, weakMap1);
+// OK
-// Fails because weakMap3 has a property that weakMap1 does not contain:
-assert.deepStrictEqual(weakMap1, weakMap3);
+const weakSet1 = new WeakSet();
+const weakSet2 = new WeakSet();
+weakSet1.add(obj);
+weakSet2.add(obj);
+
+// Comparing different instances fails, even with same contents
+assert.deepStrictEqual(weakSet1, weakSet2);
// AssertionError: Expected inputs to be strictly deep-equal:
// + actual - expected
//
-// WeakMap {
-// + [items unknown]
-// - [items unknown],
-// - unequal: true
-// }
+// + WeakSet { }
+// - WeakSet { }
+
+// Comparing the same instance to itself succeeds
+assert.deepStrictEqual(weakSet1, weakSet1);
+// OK
```
```cjs
@@ -974,23 +993,40 @@ assert.deepStrictEqual({ [symbol1]: 1 }, { [symbol2]: 1 });
// }
const weakMap1 = new WeakMap();
-const weakMap2 = new WeakMap([[{}, {}]]);
-const weakMap3 = new WeakMap();
-weakMap3.unequal = true;
+const weakMap2 = new WeakMap();
+const obj = {};
+weakMap1.set(obj, 'value');
+weakMap2.set(obj, 'value');
+
+// Comparing different instances fails, even with same contents
assert.deepStrictEqual(weakMap1, weakMap2);
-// OK, because it is impossible to compare the entries
+// AssertionError: Values have same structure but are not reference-equal:
+//
+// WeakMap {
+//
+// }
+
+// Comparing the same instance to itself succeeds
+assert.deepStrictEqual(weakMap1, weakMap1);
+// OK
-// Fails because weakMap3 has a property that weakMap1 does not contain:
-assert.deepStrictEqual(weakMap1, weakMap3);
+const weakSet1 = new WeakSet();
+const weakSet2 = new WeakSet();
+weakSet1.add(obj);
+weakSet2.add(obj);
+
+// Comparing different instances fails, even with same contents
+assert.deepStrictEqual(weakSet1, weakSet2);
// AssertionError: Expected inputs to be strictly deep-equal:
// + actual - expected
//
-// WeakMap {
-// + [items unknown]
-// - [items unknown],
-// - unequal: true
-// }
+// + WeakSet { }
+// - WeakSet { }
+
+// Comparing the same instance to itself succeeds
+assert.deepStrictEqual(weakSet1, weakSet1);
+// OK
```
If the values are not equal, an [`AssertionError`][] is thrown with a `message`
diff --git a/doc/api/cli.md b/doc/api/cli.md
index da2506326fc5bd..e48340024e8ebf 100644
--- a/doc/api/cli.md
+++ b/doc/api/cli.md
@@ -1061,6 +1061,8 @@ added:
Enable module mocking in the test runner.
+This feature requires `--allow-worker` if used with the [Permission Model][].
+
### `--experimental-transform-types`
-The provided TypeScript syntax is not valid or unsupported.
-This could happen when using TypeScript syntax that requires
-transformation with [type-stripping][].
+The provided TypeScript syntax is not valid.
@@ -2829,25 +2831,6 @@ An unspecified or non-specific system error has occurred within the Node.js
process. The error object will have an `err.info` object property with
additional details.
-
-
-### `ERR_TAP_LEXER_ERROR`
-
-An error representing a failing lexer state.
-
-
-
-### `ERR_TAP_PARSER_ERROR`
-
-An error representing a failing parser state. Additional information about
-the token causing the error is available via the `cause` property.
-
-
-
-### `ERR_TAP_VALIDATION_ERROR`
-
-This error represents a failed TAP validation.
-
### `ERR_TEST_FAILURE`
@@ -3135,6 +3118,18 @@ try {
}
```
+
+
+### `ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX`
+
+
+
+The provided TypeScript syntax is unsupported.
+This could happen when using TypeScript syntax that requires
+transformation with [type-stripping][].
+
### `ERR_USE_AFTER_CLOSE`
@@ -3883,6 +3878,25 @@ removed: v10.0.0
Used when an attempt is made to use a readable stream that has not implemented
[`readable._read()`][].
+
+
+### `ERR_TAP_LEXER_ERROR`
+
+An error representing a failing lexer state.
+
+
+
+### `ERR_TAP_PARSER_ERROR`
+
+An error representing a failing parser state. Additional information about
+the token causing the error is available via the `cause` property.
+
+
+
+### `ERR_TAP_VALIDATION_ERROR`
+
+This error represents a failed TAP validation.
+
### `ERR_TLS_RENEGOTIATION_FAILED`
diff --git a/doc/api/module.md b/doc/api/module.md
index 6f399fb07a44ee..2cc6c786ab1c34 100644
--- a/doc/api/module.md
+++ b/doc/api/module.md
@@ -1587,6 +1587,43 @@ import { findSourceMap, SourceMap } from 'node:module';
const { findSourceMap, SourceMap } = require('node:module');
```
+### `module.setSourceMapsSupport(enabled[, options])`
+
+
+
+* `enabled` {boolean} Enable the source map support.
+* `options` {Object} Optional
+ * `nodeModules` {boolean} If enabling the support for files in `node_modules`.
+ * `generatedCode` {boolean} If enabling the support for generated code from `eval` or `new Function`.
+
+This function enables or disables the [Source Map v3][Source Map] support for
+stack traces.
+
+It provides same features as launching Node.js process with commandline options
+`--enable-source-maps`, with additional options to alter the support for files
+in `node_modules` or generated codes.
+
+Only source maps in JavaScript files that are loaded after source maps has been
+enabled will be parsed and loaded. Preferably, use the commandline options
+`--enable-source-maps` to avoid losing track of source maps of modules loaded
+before this API call.
+
+### `module.getSourceMapsSupport()`
+
+
+
+* Returns: {Object}
+ * `enabled` {boolean} If the source maps support is enabled
+ * `nodeModules` {boolean} If the support is enabled for files in `node_modules`.
+ * `generatedCode` {boolean} If the support is enabled for generated code from `eval` or `new Function`.
+
+This method returns whether the [Source Map v3][Source Map] support for stack
+traces is enabled.
+
@@ -1705,6 +1742,7 @@ returned object contains the following keys:
[Conditional exports]: packages.md#conditional-exports
[Customization hooks]: #customization-hooks
[ES Modules]: esm.md
+[Source Map]: https://sourcemaps.info/spec.html
[Source map v3 format]: https://sourcemaps.info/spec.html#h.mofvlxcwqzej
[V8 JavaScript code coverage]: https://v8project.blogspot.com/2017/12/javascript-code-coverage.html
[V8 code cache]: https://v8.dev/blog/code-caching-for-devs
diff --git a/doc/api/process.md b/doc/api/process.md
index 95b35897f9d568..c45e808c553be1 100644
--- a/doc/api/process.md
+++ b/doc/api/process.md
@@ -3020,34 +3020,40 @@ function definitelyAsync(arg, cb) {
### When to use `queueMicrotask()` vs. `process.nextTick()`
-The [`queueMicrotask()`][] API is an alternative to `process.nextTick()` that
-also defers execution of a function using the same microtask queue used to
-execute the then, catch, and finally handlers of resolved promises. Within
-Node.js, every time the "next tick queue" is drained, the microtask queue
+The [`queueMicrotask()`][] API is an alternative to `process.nextTick()` that instead of using the
+"next tick queue" defers execution of a function using the same microtask queue used to execute the
+then, catch, and finally handlers of resolved promises.
+
+Within Node.js, every time the "next tick queue" is drained, the microtask queue
is drained immediately after.
+So in CJS modules `process.nextTick()` callbacks are always run before `queueMicrotask()` ones.
+However since ESM modules are processed already as part of the microtask queue, there
+`queueMicrotask()` callbacks are always exectued before `process.nextTick()` ones since Node.js
+is already in the process of draining the microtask queue.
+
```mjs
import { nextTick } from 'node:process';
-Promise.resolve().then(() => console.log(2));
-queueMicrotask(() => console.log(3));
-nextTick(() => console.log(1));
+Promise.resolve().then(() => console.log('resolve'));
+queueMicrotask(() => console.log('microtask'));
+nextTick(() => console.log('nextTick'));
// Output:
-// 1
-// 2
-// 3
+// resolve
+// microtask
+// nextTick
```
```cjs
const { nextTick } = require('node:process');
-Promise.resolve().then(() => console.log(2));
-queueMicrotask(() => console.log(3));
-nextTick(() => console.log(1));
+Promise.resolve().then(() => console.log('resolve'));
+queueMicrotask(() => console.log('microtask'));
+nextTick(() => console.log('nextTick'));
// Output:
-// 1
-// 2
-// 3
+// nextTick
+// resolve
+// microtask
```
For _most_ userland use cases, the `queueMicrotask()` API provides a portable
@@ -3995,9 +4001,13 @@ This feature is not available in [`Worker`][] threads.
added:
- v16.6.0
- v14.18.0
+changes:
+ - version: REPLACEME
+ pr-url: https://github.com/nodejs/node/pull/56639
+ description: the `process.setSourceMapsEnabled` has been deprecated.
-->
-> Stability: 1 - Experimental
+> Stability: 0 - Deprecated: Use [`module.setSourceMapsSupport()`][] instead.
* `val` {boolean}
@@ -4010,6 +4020,9 @@ It provides same features as launching Node.js process with commandline options
Only source maps in JavaScript files that are loaded after source maps has been
enabled will be parsed and loaded.
+This implies calling `module.setSourceMapsSupport()` with an option
+`{ nodeModule: true, generatedCode: true }`.
+
## `process.setUncaughtExceptionCaptureCallback(fn)`
-> Stability: 1 - Experimental
+> Stability: 0 - Deprecated: Use [`module.getSourceMapsSupport()`][] instead.
* {boolean}
@@ -4511,7 +4528,9 @@ cases:
[`console.error()`]: console.md#consoleerrordata-args
[`console.log()`]: console.md#consolelogdata-args
[`domain`]: domain.md
+[`module.getSourceMapsSupport()`]: module.md#modulegetsourcemapssupport
[`module.isBuiltin(id)`]: module.md#moduleisbuiltinmodulename
+[`module.setSourceMapsSupport()`]: module.md#modulesetsourcemapssupportenabled-options
[`net.Server`]: net.md#class-netserver
[`net.Socket`]: net.md#class-netsocket
[`os.constants.dlopen`]: os.md#dlopen-constants
diff --git a/doc/changelogs/CHANGELOG_V18.md b/doc/changelogs/CHANGELOG_V18.md
index 4f3cedbc171c4f..c73fcf2d698849 100644
--- a/doc/changelogs/CHANGELOG_V18.md
+++ b/doc/changelogs/CHANGELOG_V18.md
@@ -9,6 +9,7 @@
|
+23.6.1
23.6.0
23.5.0
23.4.0
@@ -44,6 +45,29 @@
* [io.js](CHANGELOG_IOJS.md)
* [Archive](CHANGELOG_ARCHIVE.md)
+
+
+## 2025-01-21, Version 23.6.1 (Current), @RafaelGSS
+
+This is a security release.
+
+### Notable Changes
+
+* CVE-2025-23083 - src,loader,permission: throw on InternalWorker use when permission model is enabled (High)
+* CVE-2025-23085 - src: fix HTTP2 mem leak on premature close and ERR\_PROTO (Medium)
+* CVE-2025-23084 - path: fix path traversal in normalize() on Windows (Medium)
+
+Dependency update:
+
+* CVE-2025-22150 - Use of Insufficiently Random Values in undici fetch() (Medium)
+
+### Commits
+
+* \[[`f2ad4d3af8`](https://github.com/nodejs/node/commit/f2ad4d3af8)] - **(CVE-2025-22150)** **deps**: update undici to v6.21.1 (Matteo Collina) [nodejs-private/node-private#654](https://github.com/nodejs-private/node-private/pull/654)
+* \[[`0afc6f9600`](https://github.com/nodejs/node/commit/0afc6f9600)] - **(CVE-2025-23084)** **path**: fix path traversal in normalize() on Windows (RafaelGSS) [nodejs-private/node-private#555](https://github.com/nodejs-private/node-private/pull/555)
+* \[[`3c7686163e`](https://github.com/nodejs/node/commit/3c7686163e)] - **(CVE-2025-23085)** **src**: fix HTTP2 mem leak on premature close and ERR\_PROTO (RafaelGSS) [nodejs-private/node-private#650](https://github.com/nodejs-private/node-private/pull/650)
+* \[[`51938f023a`](https://github.com/nodejs/node/commit/51938f023a)] - **(CVE-2025-23083)** **src,loader,permission**: throw on InternalWorker use (RafaelGSS) [nodejs-private/node-private#629](https://github.com/nodejs-private/node-private/pull/629)
+
## 2025-01-07, Version 23.6.0 (Current), @marco-ippolito
diff --git a/doc/contributing/advocacy-ambassador-program.md b/doc/contributing/advocacy-ambassador-program.md
index 5237dadaf09c7e..76f29b73586691 100644
--- a/doc/contributing/advocacy-ambassador-program.md
+++ b/doc/contributing/advocacy-ambassador-program.md
@@ -94,11 +94,14 @@ process. An ambassador can request promotion of content in the following ways:
* Posting a link to the content in the "what's new" issue in nodejs/ambassadors
so that it goes out on the news feed.
-Foundation staff will repost the social media post
-without any need for validation based on the request coming from
-an ambassador. These requests can be made through the existing social channel
-in the OpenJS Slack. For that reason and for communication purposes and
-collaboration opportunities, ambassadors should be members of the
+For accounts managed by foundation staff, the staff will repost the social
+media post without any need for validation based on the request coming from
+an ambassador. For accounts managed by the project with an approval process,
+(for example bluesky) documentation for the approval process will indicate
+that repost requests from ambassadors should generally be approved. These
+requests can be made through the existing social channel in the OpenJS Slack.
+For that reason and for communication purposes and collaboration opportunities,
+ambassadors should be members of the
[OpenJS Slack](https://slack-invite.openjsf.org/).
## Messages and topics to promote
@@ -218,3 +221,30 @@ Some of the things to highlight include:
* @marco-ippolito
You can find their contact email in the [`README.md`](../../README.md#tsc-technical-steering-committee)
+
+#### Node.js Type Stripping
+
+##### Goal
+
+The goal is to raise awareness of the Node.js TypeScript Type Stripping in the JavaScript ecosystem.
+Some of the things to highlight include:
+
+* The benefits and limitations of the current implementation.
+* The `tsconfig.json` configuration options to use in combination for type checking.
+* Updates on the implementation advancements.
+
+#### Related Links
+
+
+
+*
+*
+*
+*
+*
+
+
+
+#### Project contacts
+
+* @marco-ippolito
diff --git a/doc/contributing/releases.md b/doc/contributing/releases.md
index 40ba96da602033..5b6d2180515565 100644
--- a/doc/contributing/releases.md
+++ b/doc/contributing/releases.md
@@ -308,6 +308,22 @@ branch.
git checkout -b v1.2.3-proposal upstream/v1.x-staging
```
+You can also run:
+
+```bash
+git node release -S --prepare --security --filterLabel vX.x
+```
+
+Example:
+
+```bash
+git checkout v20.x
+git node release -S --prepare --security --filterLabel v20.x
+```
+
+to automate the remaining steps until step 6 or you can perform it manually
+following the below steps.
+
Security release
diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js
index de2f0e00e14092..5b24a44741b0d6 100644
--- a/lib/internal/bootstrap/node.js
+++ b/lib/internal/bootstrap/node.js
@@ -368,8 +368,8 @@ internalBinding('process_methods').setEmitWarningSync(emitWarningSync);
{
const {
- getSourceMapsEnabled,
- setSourceMapsEnabled,
+ getSourceMapsSupport,
+ setSourceMapsSupport,
maybeCacheGeneratedSourceMap,
} = require('internal/source_map/source_map_cache');
const {
@@ -381,10 +381,19 @@ internalBinding('process_methods').setEmitWarningSync(emitWarningSync);
enumerable: true,
configurable: true,
get() {
- return getSourceMapsEnabled();
+ return getSourceMapsSupport().enabled;
},
});
- process.setSourceMapsEnabled = setSourceMapsEnabled;
+ process.setSourceMapsEnabled = function setSourceMapsEnabled(val) {
+ setSourceMapsSupport(val, {
+ __proto__: null,
+ // TODO(legendecas): In order to smoothly improve the source map support,
+ // skip source maps in node_modules and generated code with
+ // `process.setSourceMapsEnabled(true)` in a semver major version.
+ nodeModules: val,
+ generatedCode: val,
+ });
+ };
// The C++ land calls back to maybeCacheGeneratedSourceMap()
// when code is generated by user with eval() or new Function()
// to cache the source maps from the evaluated code, if any.
diff --git a/lib/internal/child_process/serialization.js b/lib/internal/child_process/serialization.js
index 7be39f0d48c3c2..46bb1faaf9fc21 100644
--- a/lib/internal/child_process/serialization.js
+++ b/lib/internal/child_process/serialization.js
@@ -61,7 +61,12 @@ const advanced = {
*parseChannelMessages(channel, readData) {
if (readData.length === 0) return;
- ArrayPrototypePush(channel[kMessageBuffer], readData);
+ if (channel[kMessageBufferSize] && channel[kMessageBuffer][0].length < 4) {
+ // Message length split into two buffers, so let's concatenate it.
+ channel[kMessageBuffer][0] = Buffer.concat([channel[kMessageBuffer][0], readData]);
+ } else {
+ ArrayPrototypePush(channel[kMessageBuffer], readData);
+ }
channel[kMessageBufferSize] += readData.length;
// Index 0 should always be present because we just pushed data into it.
diff --git a/lib/internal/errors.js b/lib/internal/errors.js
index d990f8d5a106aa..d6b2ceb5962351 100644
--- a/lib/internal/errors.js
+++ b/lib/internal/errors.js
@@ -1739,21 +1739,6 @@ E('ERR_STREAM_WRAP', 'Stream has StringDecoder set or is in objectMode', Error);
E('ERR_STREAM_WRITE_AFTER_END', 'write after end', Error);
E('ERR_SYNTHETIC', 'JavaScript Callstack', Error);
E('ERR_SYSTEM_ERROR', 'A system error occurred', SystemError, HideStackFramesError);
-E('ERR_TAP_LEXER_ERROR', function(errorMsg) {
- hideInternalStackFrames(this);
- return errorMsg;
-}, Error);
-E('ERR_TAP_PARSER_ERROR', function(errorMsg, details, tokenCausedError, source) {
- hideInternalStackFrames(this);
- this.cause = tokenCausedError;
- const { column, line, start, end } = tokenCausedError.location;
- const errorDetails = `${details} at line ${line}, column ${column} (start ${start}, end ${end})`;
- return errorMsg + errorDetails;
-}, SyntaxError);
-E('ERR_TAP_VALIDATION_ERROR', function(errorMsg) {
- hideInternalStackFrames(this);
- return errorMsg;
-}, Error);
E('ERR_TEST_FAILURE', function(error, failureType) {
hideInternalStackFrames(this);
assert(typeof failureType === 'string' || typeof failureType === 'symbol',
@@ -1853,6 +1838,7 @@ E('ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING',
E('ERR_UNSUPPORTED_RESOLVE_REQUEST',
'Failed to resolve module specifier "%s" from "%s": Invalid relative URL or base scheme is not hierarchical.',
TypeError);
+E('ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX', '%s', SyntaxError);
E('ERR_USE_AFTER_CLOSE', '%s was closed', Error);
// This should probably be a `TypeError`.
diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js
index b41e1baee24644..554221ac614636 100644
--- a/lib/internal/http2/core.js
+++ b/lib/internal/http2/core.js
@@ -614,11 +614,20 @@ function onFrameError(id, type, code) {
return;
debugSessionObj(session, 'error sending frame type %d on stream %d, code: %d',
type, id, code);
- const emitter = session[kState].streams.get(id) || session;
+
+ const stream = session[kState].streams.get(id);
+ const emitter = stream || session;
emitter[kUpdateTimer]();
emitter.emit('frameError', type, code, id);
- session[kState].streams.get(id).close(code);
- session.close();
+
+ // When a frameError happens is not uncommon that a pending GOAWAY
+ // package from nghttp2 is on flight with a correct error code.
+ // We schedule it using setImmediate to give some time for that
+ // package to arrive.
+ setImmediate(() => {
+ stream?.close(code);
+ session.close();
+ });
}
function onAltSvc(stream, origin, alt) {
@@ -636,15 +645,21 @@ function initOriginSet(session) {
if (originSet === undefined) {
const socket = session[kSocket];
session[kState].originSet = originSet = new SafeSet();
- if (socket.servername != null) {
- let originString = `https://${socket.servername}`;
- if (socket.remotePort != null)
- originString += `:${socket.remotePort}`;
- // We have to ensure that it is a properly serialized
- // ASCII origin string. The socket.servername might not
- // be properly ASCII encoded.
- originSet.add(getURLOrigin(originString));
+ let hostName = socket.servername;
+ if (hostName === null || hostName === false) {
+ if (socket.remoteFamily === 'IPv6') {
+ hostName = `[${socket.remoteAddress}]`;
+ } else {
+ hostName = socket.remoteAddress;
+ }
}
+ let originString = `https://${hostName}`;
+ if (socket.remotePort != null)
+ originString += `:${socket.remotePort}`;
+ // We have to ensure that it is a properly serialized
+ // ASCII origin string. The socket.servername might not
+ // be properly ASCII encoded.
+ originSet.add(getURLOrigin(originString));
}
return originSet;
}
@@ -3333,7 +3348,7 @@ function connect(authority, options, listener) {
socket = net.connect({ port, host, ...options });
break;
case 'https:':
- socket = tls.connect(port, host, initializeTLSOptions(options, host));
+ socket = tls.connect(port, host, initializeTLSOptions(options, net.isIP(host) ? undefined : host));
break;
default:
throw new ERR_HTTP2_UNSUPPORTED_PROTOCOL(protocol);
diff --git a/lib/internal/modules/esm/module_job.js b/lib/internal/modules/esm/module_job.js
index 8039e2f57a500f..846a336d27547e 100644
--- a/lib/internal/modules/esm/module_job.js
+++ b/lib/internal/modules/esm/module_job.js
@@ -30,7 +30,7 @@ const {
} = internalBinding('util');
const { decorateErrorStack, kEmptyObject } = require('internal/util');
const {
- getSourceMapsEnabled,
+ getSourceMapsSupport,
} = require('internal/source_map/source_map_cache');
const assert = require('internal/assert');
const resolvedPromise = PromiseResolve();
@@ -186,7 +186,7 @@ class ModuleJob extends ModuleJobBase {
// of missing named export. This is currently not possible because
// stack trace originates in module_job, not the file itself. A hidden
// symbol with filename could be set in node_errors.cc to facilitate this.
- if (!getSourceMapsEnabled() &&
+ if (!getSourceMapsSupport().enabled &&
StringPrototypeIncludes(e.message,
' does not provide an export named')) {
const splitStack = StringPrototypeSplit(e.stack, '\n');
diff --git a/lib/internal/modules/typescript.js b/lib/internal/modules/typescript.js
index 993fd3ff72d74d..689788b09853c4 100644
--- a/lib/internal/modules/typescript.js
+++ b/lib/internal/modules/typescript.js
@@ -12,8 +12,10 @@ const { assertTypeScript,
isUnderNodeModules,
kEmptyObject } = require('internal/util');
const {
+ ERR_INTERNAL_ASSERTION,
ERR_INVALID_TYPESCRIPT_SYNTAX,
ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING,
+ ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX,
} = require('internal/errors').codes;
const { getOptionValue } = require('internal/options');
const assert = require('internal/assert');
@@ -49,7 +51,20 @@ function parseTypeScript(source, options) {
try {
return parse(source, options);
} catch (error) {
- throw new ERR_INVALID_TYPESCRIPT_SYNTAX(error.message);
+ /**
+ * Amaro v0.3.0 (from SWC v1.10.7) throws an object with `message` and `code` properties.
+ * It allows us to distinguish between invalid syntax and unsupported syntax.
+ */
+ switch (error.code) {
+ case 'UnsupportedSyntax':
+ throw new ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX(error.message);
+ case 'InvalidSyntax':
+ throw new ERR_INVALID_TYPESCRIPT_SYNTAX(error.message);
+ default:
+ // SWC will throw strings when something goes wrong.
+ // Check if has the `message` property or treat it as a string.
+ throw new ERR_INTERNAL_ASSERTION(error.message ?? error);
+ }
}
}
diff --git a/lib/internal/process/execution.js b/lib/internal/process/execution.js
index f5b19d5a7e8c9c..d4d7a604851ef1 100644
--- a/lib/internal/process/execution.js
+++ b/lib/internal/process/execution.js
@@ -35,7 +35,7 @@ const { getOptionValue } = require('internal/options');
const {
makeContextifyScript, runScriptInThisContext,
} = require('internal/vm');
-const { emitExperimentalWarning, isError } = require('internal/util');
+const { emitExperimentalWarning } = require('internal/util');
// shouldAbortOnUncaughtToggle is a typed array for faster
// communication with JS.
const { shouldAbortOnUncaughtToggle } = internalBinding('util');
@@ -254,10 +254,6 @@ function evalTypeScript(name, source, breakFirstLine, print, shouldLoadESM = fal
try {
compiledScript = compileScript(name, source, baseUrl);
} catch (originalError) {
- // If it's not a SyntaxError, rethrow it.
- if (!isError(originalError) || originalError.name !== 'SyntaxError') {
- throw originalError;
- }
try {
sourceToRun = stripTypeScriptModuleTypes(source, name, false);
// Retry the CJS/ESM syntax detection after stripping the types.
@@ -270,15 +266,14 @@ function evalTypeScript(name, source, breakFirstLine, print, shouldLoadESM = fal
// Emit the experimental warning after the code was successfully evaluated.
emitExperimentalWarning('Type Stripping');
} catch (tsError) {
- // If its not an error, or it's not an invalid typescript syntax error, rethrow it.
- if (!isError(tsError) || tsError?.code !== 'ERR_INVALID_TYPESCRIPT_SYNTAX') {
- throw tsError;
+ // If it's invalid or unsupported TypeScript syntax, rethrow the original error
+ // with the TypeScript error message added to the stack.
+ if (tsError.code === 'ERR_INVALID_TYPESCRIPT_SYNTAX' || tsError.code === 'ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX') {
+ originalError.stack = decorateCJSErrorWithTSMessage(originalError.stack, tsError.message);
+ throw originalError;
}
- try {
- originalError.stack = decorateCJSErrorWithTSMessage(originalError.stack, tsError.message);
- } catch { /* Ignore potential errors coming from `stack` getter/setter */ }
- throw originalError;
+ throw tsError;
}
}
@@ -322,28 +317,23 @@ function evalTypeScriptModuleEntryPoint(source, print) {
// Compile the module to check for syntax errors.
moduleWrap = loader.createModuleWrap(source, url);
} catch (originalError) {
- // If it's not a SyntaxError, rethrow it.
- if (!isError(originalError) || originalError.name !== 'SyntaxError') {
- throw originalError;
- }
- let strippedSource;
try {
- strippedSource = stripTypeScriptModuleTypes(source, url, false);
+ const strippedSource = stripTypeScriptModuleTypes(source, url, false);
// If the moduleWrap was successfully created, execute the module job.
// outside the try-catch block to avoid catching runtime errors.
moduleWrap = loader.createModuleWrap(strippedSource, url);
// Emit the experimental warning after the code was successfully compiled.
emitExperimentalWarning('Type Stripping');
} catch (tsError) {
- // If its not an error, or it's not an invalid typescript syntax error, rethrow it.
- if (!isError(tsError) || tsError?.code !== 'ERR_INVALID_TYPESCRIPT_SYNTAX') {
- throw tsError;
- }
- try {
+ // If it's invalid or unsupported TypeScript syntax, rethrow the original error
+ // with the TypeScript error message added to the stack.
+ if (tsError.code === 'ERR_INVALID_TYPESCRIPT_SYNTAX' ||
+ tsError.code === 'ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX') {
originalError.stack = `${tsError.message}\n\n${originalError.stack}`;
- } catch { /* Ignore potential errors coming from `stack` getter/setter */ }
+ throw originalError;
+ }
- throw originalError;
+ throw tsError;
}
}
// If the moduleWrap was successfully created either with by just compiling
diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js
index 3ea9a934726462..109890e5986ee4 100644
--- a/lib/internal/process/pre_execution.js
+++ b/lib/internal/process/pre_execution.js
@@ -618,9 +618,17 @@ function initializeESMLoader(forceDefaultLoader) {
function initializeSourceMapsHandlers() {
const {
- setSourceMapsEnabled,
+ setSourceMapsSupport,
} = require('internal/source_map/source_map_cache');
- setSourceMapsEnabled(getOptionValue('--enable-source-maps'));
+ const enabled = getOptionValue('--enable-source-maps');
+ setSourceMapsSupport(enabled, {
+ __proto__: null,
+ // TODO(legendecas): In order to smoothly improve the source map support,
+ // skip source maps in node_modules and generated code with
+ // `--enable-source-maps` in a semver major version.
+ nodeModules: enabled,
+ generatedCode: enabled,
+ });
}
function initializeFrozenIntrinsics() {
diff --git a/lib/internal/source_map/source_map_cache.js b/lib/internal/source_map/source_map_cache.js
index aaca27136e66a0..bdef338e3dd086 100644
--- a/lib/internal/source_map/source_map_cache.js
+++ b/lib/internal/source_map/source_map_cache.js
@@ -3,6 +3,7 @@
const {
ArrayPrototypePush,
JSONParse,
+ ObjectFreeze,
RegExpPrototypeExec,
SafeMap,
StringPrototypeCodePointAt,
@@ -15,7 +16,7 @@ let debug = require('internal/util/debuglog').debuglog('source_map', (fn) => {
debug = fn;
});
-const { validateBoolean } = require('internal/validators');
+const { validateBoolean, validateObject } = require('internal/validators');
const {
setSourceMapsEnabled: setSourceMapsNative,
} = internalBinding('errors');
@@ -23,7 +24,7 @@ const {
defaultPrepareStackTrace,
setInternalPrepareStackTrace,
} = require('internal/errors');
-const { getLazy } = require('internal/util');
+const { getLazy, isUnderNodeModules, kEmptyObject } = require('internal/util');
const getModuleSourceMapCache = getLazy(() => {
const { SourceMapCacheMap } = require('internal/source_map/source_map_cache_map');
@@ -45,30 +46,48 @@ const { fileURLToPath, pathToFileURL, URL, URLParse } = require('internal/url');
let SourceMap;
// This is configured with --enable-source-maps during pre-execution.
-let sourceMapsEnabled = false;
-function getSourceMapsEnabled() {
- return sourceMapsEnabled;
+let sourceMapsSupport = ObjectFreeze({
+ __proto__: null,
+ enabled: false,
+ nodeModules: false,
+ generatedCode: false,
+});
+function getSourceMapsSupport() {
+ // Return a read-only object.
+ return sourceMapsSupport;
}
/**
* Enables or disables source maps programmatically.
- * @param {boolean} val
+ * @param {boolean} enabled
+ * @param {object} options
+ * @param {boolean} [options.nodeModules]
+ * @param {boolean} [options.generatedCode]
*/
-function setSourceMapsEnabled(val) {
- validateBoolean(val, 'val');
+function setSourceMapsSupport(enabled, options = kEmptyObject) {
+ validateBoolean(enabled, 'enabled');
+ validateObject(options, 'options');
+
+ const { nodeModules = false, generatedCode = false } = options;
+ validateBoolean(nodeModules, 'options.nodeModules');
+ validateBoolean(generatedCode, 'options.generatedCode');
- setSourceMapsNative(val);
- if (val) {
+ setSourceMapsNative(enabled);
+ if (enabled) {
const {
prepareStackTraceWithSourceMaps,
} = require('internal/source_map/prepare_stack_trace');
setInternalPrepareStackTrace(prepareStackTraceWithSourceMaps);
- } else if (sourceMapsEnabled !== undefined) {
- // Reset prepare stack trace callback only when disabling source maps.
+ } else {
setInternalPrepareStackTrace(defaultPrepareStackTrace);
}
- sourceMapsEnabled = val;
+ sourceMapsSupport = ObjectFreeze({
+ __proto__: null,
+ enabled,
+ nodeModules: nodeModules,
+ generatedCode: generatedCode,
+ });
}
/**
@@ -130,14 +149,18 @@ function extractSourceMapURLMagicComment(content) {
* @param {string | undefined} sourceMapURL - the source map url
*/
function maybeCacheSourceMap(filename, content, moduleInstance, isGeneratedSource, sourceURL, sourceMapURL) {
- const sourceMapsEnabled = getSourceMapsEnabled();
- if (!(process.env.NODE_V8_COVERAGE || sourceMapsEnabled)) return;
+ const support = getSourceMapsSupport();
+ if (!(process.env.NODE_V8_COVERAGE || support.enabled)) return;
const { normalizeReferrerURL } = require('internal/modules/helpers');
filename = normalizeReferrerURL(filename);
if (filename === undefined) {
// This is most likely an invalid filename in sourceURL of [eval]-wrapper.
return;
}
+ if (!support.nodeModules && isUnderNodeModules(filename)) {
+ // Skip file under node_modules if not enabled.
+ return;
+ }
if (sourceMapURL === undefined) {
sourceMapURL = extractSourceMapURLMagicComment(content);
@@ -185,8 +208,8 @@ function maybeCacheSourceMap(filename, content, moduleInstance, isGeneratedSourc
* @param {string} content - the eval'd source code
*/
function maybeCacheGeneratedSourceMap(content) {
- const sourceMapsEnabled = getSourceMapsEnabled();
- if (!(process.env.NODE_V8_COVERAGE || sourceMapsEnabled)) return;
+ const support = getSourceMapsSupport();
+ if (!(process.env.NODE_V8_COVERAGE || support.enabled || support.generated)) return;
const sourceURL = extractSourceURLMagicComment(content);
if (sourceURL === null) {
@@ -352,6 +375,10 @@ function findSourceMap(sourceURL) {
return undefined;
}
+ if (!getSourceMapsSupport().nodeModules && isUnderNodeModules(sourceURL)) {
+ return undefined;
+ }
+
SourceMap ??= require('internal/source_map/source_map').SourceMap;
try {
if (RegExpPrototypeExec(kLeadingProtocol, sourceURL) === null) {
@@ -377,8 +404,8 @@ function findSourceMap(sourceURL) {
module.exports = {
findSourceMap,
- getSourceMapsEnabled,
- setSourceMapsEnabled,
+ getSourceMapsSupport,
+ setSourceMapsSupport,
maybeCacheSourceMap,
maybeCacheGeneratedSourceMap,
sourceMapCacheToObject,
diff --git a/lib/module.js b/lib/module.js
index a0317d06e0edb0..1217172afb3ccb 100644
--- a/lib/module.js
+++ b/lib/module.js
@@ -1,9 +1,15 @@
'use strict';
-const { findSourceMap } = require('internal/source_map/source_map_cache');
+const {
+ findSourceMap,
+ getSourceMapsSupport,
+ setSourceMapsSupport,
+} = require('internal/source_map/source_map_cache');
const { Module } = require('internal/modules/cjs/loader');
const { register } = require('internal/modules/esm/loader');
-const { SourceMap } = require('internal/source_map/source_map');
+const {
+ SourceMap,
+} = require('internal/source_map/source_map');
const {
constants,
enableCompileCache,
@@ -15,9 +21,7 @@ const {
} = require('internal/modules/package_json_reader');
const { stripTypeScriptTypes } = require('internal/modules/typescript');
-Module.findSourceMap = findSourceMap;
Module.register = register;
-Module.SourceMap = SourceMap;
Module.constants = constants;
Module.enableCompileCache = enableCompileCache;
Module.findPackageJSON = findPackageJSON;
@@ -25,4 +29,10 @@ Module.flushCompileCache = flushCompileCache;
Module.getCompileCacheDir = getCompileCacheDir;
Module.stripTypeScriptTypes = stripTypeScriptTypes;
+// SourceMap APIs
+Module.findSourceMap = findSourceMap;
+Module.SourceMap = SourceMap;
+Module.getSourceMapsSupport = getSourceMapsSupport;
+Module.setSourceMapsSupport = setSourceMapsSupport;
+
module.exports = Module;
diff --git a/lib/path.js b/lib/path.js
index 8b9d6d0b6b9621..1a59a66f66bb2a 100644
--- a/lib/path.js
+++ b/lib/path.js
@@ -26,6 +26,7 @@ const {
ArrayPrototypeSlice,
FunctionPrototypeBind,
StringPrototypeCharCodeAt,
+ StringPrototypeIncludes,
StringPrototypeIndexOf,
StringPrototypeLastIndexOf,
StringPrototypeRepeat,
@@ -414,6 +415,23 @@ const win32 = {
if (tail.length > 0 &&
isPathSeparator(StringPrototypeCharCodeAt(path, len - 1)))
tail += '\\';
+ if (!isAbsolute && device === undefined && StringPrototypeIncludes(path, ':')) {
+ // If the original path was not absolute and if we have not been able to
+ // resolve it relative to a particular device, we need to ensure that the
+ // `tail` has not become something that Windows might interpret as an
+ // absolute path. See CVE-2024-36139.
+ if (tail.length >= 2 &&
+ isWindowsDeviceRoot(StringPrototypeCharCodeAt(tail, 0)) &&
+ StringPrototypeCharCodeAt(tail, 1) === CHAR_COLON) {
+ return `.\\${tail}`;
+ }
+ let index = StringPrototypeIndexOf(path, ':');
+ do {
+ if (index === len - 1 || isPathSeparator(StringPrototypeCharCodeAt(path, index + 1))) {
+ return `.\\${tail}`;
+ }
+ } while ((index = StringPrototypeIndexOf(path, ':', index + 1)) !== -1);
+ }
if (device === undefined) {
return isAbsolute ? `\\${tail}` : tail;
}
diff --git a/lib/punycode.js b/lib/punycode.js
index 7dfe552a5c9efa..e303a5373b8839 100644
--- a/lib/punycode.js
+++ b/lib/punycode.js
@@ -1,11 +1,16 @@
'use strict';
-
-process.emitWarning(
- 'The `punycode` module is deprecated. Please use a userland ' +
- 'alternative instead.',
- 'DeprecationWarning',
- 'DEP0040',
-);
+const {
+ isInsideNodeModules,
+} = internalBinding('util');
+
+if (!isInsideNodeModules(100, true)) {
+ process.emitWarning(
+ 'The `punycode` module is deprecated. Please use a userland ' +
+ 'alternative instead.',
+ 'DeprecationWarning',
+ 'DEP0040',
+ );
+}
/** Highest positive signed 32-bit float value */
const maxInt = 2147483647; // aka. 0x7FFFFFFF or 2^31-1
diff --git a/src/crypto/crypto_context.cc b/src/crypto/crypto_context.cc
index 8f1e6dc7110b11..c7574e67f03f03 100644
--- a/src/crypto/crypto_context.cc
+++ b/src/crypto/crypto_context.cc
@@ -1164,7 +1164,7 @@ void SecureContext::LoadPKCS12(const FunctionCallbackInfo& args) {
X509* ca = sk_X509_value(extra_certs.get(), i);
X509_STORE_add_cert(sc->GetCertStoreOwnedByThisSecureContext(), ca);
- SSL_CTX_add_client_CA(sc->ctx_.get(), ca);
+ CHECK_EQ(1, SSL_CTX_add_client_CA(sc->ctx_.get(), ca));
}
ret = true;
diff --git a/src/env.cc b/src/env.cc
index f0f97244fdef63..0eda889802710d 100644
--- a/src/env.cc
+++ b/src/env.cc
@@ -223,7 +223,12 @@ void AsyncHooks::InstallPromiseHooks(Local ctx) {
: PersistentToLocal::Strong(js_promise_hooks_[3]));
}
+void Environment::PurgeTrackedEmptyContexts() {
+ std::erase_if(contexts_, [&](auto&& el) { return el.IsEmpty(); });
+}
+
void Environment::TrackContext(Local context) {
+ PurgeTrackedEmptyContexts();
size_t id = contexts_.size();
contexts_.resize(id + 1);
contexts_[id].Reset(isolate_, context);
@@ -232,7 +237,7 @@ void Environment::TrackContext(Local context) {
void Environment::UntrackContext(Local context) {
HandleScope handle_scope(isolate_);
- std::erase_if(contexts_, [&](auto&& el) { return el.IsEmpty(); });
+ PurgeTrackedEmptyContexts();
for (auto it = contexts_.begin(); it != contexts_.end(); it++) {
if (Local saved_context = PersistentToLocal::Weak(isolate_, *it);
saved_context == context) {
diff --git a/src/env.h b/src/env.h
index ec5b608cede6a1..1929450b8fe393 100644
--- a/src/env.h
+++ b/src/env.h
@@ -1085,6 +1085,7 @@ class Environment final : public MemoryRetainer {
const char* errmsg);
void TrackContext(v8::Local context);
void UntrackContext(v8::Local context);
+ void PurgeTrackedEmptyContexts();
std::list loaded_addons_;
v8::Isolate* const isolate_;
diff --git a/src/node_contextify.cc b/src/node_contextify.cc
index 77d35675827c67..ab6659d8cdccc6 100644
--- a/src/node_contextify.cc
+++ b/src/node_contextify.cc
@@ -118,8 +118,9 @@ Local Uint32ToName(Local context, uint32_t index) {
} // anonymous namespace
-BaseObjectPtr ContextifyContext::New(
- Environment* env, Local |