From aed74ccb4c1bbd0f121bc3bb31781c9d6adfcd36 Mon Sep 17 00:00:00 2001 From: cjihrig Date: Wed, 15 May 2019 13:24:08 -0400 Subject: [PATCH 1/5] deps: upgrade to libuv 1.29.1 Notable changes: - uv_get_constrained_memory() has been added. - A race condition in uv_async_send() has been fixed. - uv_get_free_memory() and uv_get_total_memory() now read from /proc/meminfo, which should improve correctness when called from inside an lxc container. - A failed assertion in uv_fs_poll_stop() has been fixed. - A bug in MAC addresses for IP-aliases has been fixed. Fixes: https://github.com/nodejs/node/issues/27170 Fixes: https://github.com/nodejs/node/issues/27493 PR-URL: https://github.com/nodejs/node/pull/27718 Reviewed-By: Refael Ackermann Reviewed-By: Ali Ijaz Sheikh Reviewed-By: Ben Noordhuis Reviewed-By: Santiago Gimeno Reviewed-By: Rich Trott Reviewed-By: James M Snell --- deps/uv/.mailmap | 1 + deps/uv/AUTHORS | 6 + deps/uv/CMakeLists.txt | 3 +- deps/uv/ChangeLog | 56 ++++++++ deps/uv/Makefile.am | 3 +- deps/uv/README.md | 13 +- deps/uv/configure.ac | 2 +- deps/uv/docs/src/async.rst | 4 + deps/uv/docs/src/fs.rst | 5 +- deps/uv/docs/src/misc.rst | 13 ++ deps/uv/docs/src/sphinx-plugins/manpage.py | 1 - deps/uv/include/uv.h | 1 + deps/uv/include/uv/unix.h | 9 +- deps/uv/include/uv/version.h | 4 +- deps/uv/src/fs-poll.c | 2 +- deps/uv/src/unix/aix.c | 5 + deps/uv/src/unix/async.c | 37 ++++- deps/uv/src/unix/core.c | 29 +++- deps/uv/src/unix/darwin.c | 5 + deps/uv/src/unix/freebsd.c | 5 + deps/uv/src/unix/fs.c | 4 +- deps/uv/src/unix/fsevents.c | 3 +- deps/uv/src/unix/ibmi.c | 142 ++++++++++++++++++- deps/uv/src/unix/internal.h | 1 + deps/uv/src/unix/kqueue.c | 12 +- deps/uv/src/unix/linux-core.c | 156 ++++++++++++++++++--- deps/uv/src/unix/linux-syscalls.c | 5 +- deps/uv/src/unix/netbsd.c | 5 + deps/uv/src/unix/openbsd.c | 5 + deps/uv/src/unix/os390.c | 5 + deps/uv/src/unix/process.c | 4 +- deps/uv/src/unix/sunos.c | 5 + deps/uv/src/win/util.c | 13 +- deps/uv/test/test-fs-poll.c | 39 ++++++ deps/uv/test/test-fs.c | 50 +++++-- deps/uv/test/test-get-memory.c | 6 +- deps/uv/test/test-list.h | 6 +- deps/uv/test/test-spawn.c | 6 + deps/uv/uv.gyp | 1 - 39 files changed, 596 insertions(+), 76 deletions(-) diff --git a/deps/uv/.mailmap b/deps/uv/.mailmap index 8a559787b49a75..3d95997e4fe4d6 100644 --- a/deps/uv/.mailmap +++ b/deps/uv/.mailmap @@ -46,3 +46,4 @@ Yuki Okumura jBarz jBarz ptlomholt +zlargon diff --git a/deps/uv/AUTHORS b/deps/uv/AUTHORS index 890e6b0d1a46be..af91fde4b239c0 100644 --- a/deps/uv/AUTHORS +++ b/deps/uv/AUTHORS @@ -376,3 +376,9 @@ Rich Trott Milad Farazmand zlargon Yury Selivanov +Oscar Waddell +FX Coudert +George Zhao +Kyle Edwards +ken-cunningham-webuse +Kelvin Jin diff --git a/deps/uv/CMakeLists.txt b/deps/uv/CMakeLists.txt index 7b25511939e62e..b3890ddf57d219 100644 --- a/deps/uv/CMakeLists.txt +++ b/deps/uv/CMakeLists.txt @@ -297,8 +297,7 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux") src/unix/linux-inotify.c src/unix/linux-syscalls.c src/unix/procfs-exepath.c - src/unix/sysinfo-loadavg.c - src/unix/sysinfo-memory.c) + src/unix/sysinfo-loadavg.c) endif() if(CMAKE_SYSTEM_NAME STREQUAL "NetBSD") diff --git a/deps/uv/ChangeLog b/deps/uv/ChangeLog index 3095f9fc1029a2..51ed5a223a1d83 100644 --- a/deps/uv/ChangeLog +++ b/deps/uv/ChangeLog @@ -1,3 +1,59 @@ +2019.05.22, Version 1.29.1 (Stable), d16e6094e1eb3b0b5981ef1dd7e03ec4d466944d + +Changes since version 1.29.0: + +* unix: simplify uv/posix.h include logic (cjihrig) + +* test: increase test timeout (cjihrig) + +* linux: fix sscanf() overflows reading from /proc (Ben Noordhuis) + + +2019.05.16, Version 1.29.0 (Stable), 43957efd92c167b352b4c948b617ca7afbee0ed1 + +Changes since version 1.28.0: + +* ibmi: read memory and CPU usage info (Xu Meng) + +* doc: update the cmake testing instruction (zlargon) + +* unix: fix race condition in uv_async_send() (Ben Noordhuis) + +* linux: use O_CLOEXEC instead of EPOLL_CLOEXEC (Ben Noordhuis) + +* doc: mark uv_async_send() as async-signal-safe (Ben Noordhuis) + +* linux: init st_flags and st_gen when using statx (Oscar Waddell) + +* linux: read free/total memory from /proc/meminfo (Ben Noordhuis) + +* test: test zero-sized uv_fs_sendfile() writes (Ben Noordhuis) + +* unix: don't assert on UV_PROCESS_WINDOWS_* flags (Ben Noordhuis) + +* linux: set correct mac address for IP-aliases (Santiago Gimeno) + +* win,util: fix null pointer dereferencing (Tobias Nießen) + +* unix,win: fix `uv_fs_poll_stop()` when active (Anna Henningsen) + +* doc: add missing uv_fs_type entries (Michele Caini) + +* doc: fix build with sphinx 2.x (FX Coudert) + +* unix: don't make statx system call on Android (George Zhao) + +* unix: fix clang scan-build warning (Kyle Edwards) + +* unix: fall back to kqueue on older macOS systems (ken-cunningham-webuse) + +* unix,win: add uv_get_constrained_memory() (Kelvin Jin) + +* darwin: fix thread cancellation fd leak (Ben Noordhuis) + +* linux: fix thread cancellation fd leak (Ben Noordhuis) + + 2019.04.16, Version 1.28.0 (Stable), 7bf8fabfa934660ee0fe889f78e151198a1165fc Changes since version 1.27.0: diff --git a/deps/uv/Makefile.am b/deps/uv/Makefile.am index 22069625e328c4..07224f32b48e96 100644 --- a/deps/uv/Makefile.am +++ b/deps/uv/Makefile.am @@ -425,8 +425,7 @@ libuv_la_SOURCES += src/unix/linux-core.c \ src/unix/linux-syscalls.h \ src/unix/procfs-exepath.c \ src/unix/proctitle.c \ - src/unix/sysinfo-loadavg.c \ - src/unix/sysinfo-memory.c + src/unix/sysinfo-loadavg.c test_run_tests_LDFLAGS += -lutil endif diff --git a/deps/uv/README.md b/deps/uv/README.md index 4e92a8174af681..11874cdb287fba 100644 --- a/deps/uv/README.md +++ b/deps/uv/README.md @@ -172,11 +172,14 @@ $ make install To build with [CMake](https://cmake.org/): ```bash -$ mkdir -p out/cmake ; cd out/cmake ; cmake -DBUILD_TESTING=ON ../.. -$ make all test -# Or manually: -$ ./uv_run_tests # shared library build -$ ./uv_run_tests_a # static library build +$ mkdir -p out/cmake ; cd out/cmake # create build directory +$ cmake ../.. -DBUILD_TESTING=ON # generate project with test +$ cmake --build . # build +$ ctest -C Debug --output-on-failure # run tests + +# Or manually run tests: +$ ./out/cmake/uv_run_tests # shared library build +$ ./out/cmake/uv_run_tests_a # static library build ``` To build with GYP, first run: diff --git a/deps/uv/configure.ac b/deps/uv/configure.ac index 75275aa8785f43..ad99f9ad380cdb 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.28.0], [https://github.com/libuv/libuv/issues]) +AC_INIT([libuv], [1.29.1], [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/async.rst b/deps/uv/docs/src/async.rst index 02e6a58e7838db..bf611692f460a2 100644 --- a/deps/uv/docs/src/async.rst +++ b/deps/uv/docs/src/async.rst @@ -50,6 +50,10 @@ API It's safe to call this function from any thread. The callback will be called on the loop thread. + .. note:: + :c:func:`uv_async_send` is `async-signal-safe `_. + It's safe to call this function from a signal handler. + .. warning:: libuv will coalesce calls to :c:func:`uv_async_send`, that is, not every call to it will yield an execution of the callback. For example: if :c:func:`uv_async_send` is called 5 diff --git a/deps/uv/docs/src/fs.rst b/deps/uv/docs/src/fs.rst index 177db5708426dc..996624b6951475 100644 --- a/deps/uv/docs/src/fs.rst +++ b/deps/uv/docs/src/fs.rst @@ -96,7 +96,10 @@ Data types UV_FS_FCHOWN, UV_FS_REALPATH, UV_FS_COPYFILE, - UV_FS_LCHOWN + UV_FS_LCHOWN, + UV_FS_OPENDIR, + UV_FS_READDIR, + UV_FS_CLOSEDIR } uv_fs_type; .. c:type:: uv_dirent_t diff --git a/deps/uv/docs/src/misc.rst b/deps/uv/docs/src/misc.rst index 4ad4e40af9f7be..ef70e14bff8cac 100644 --- a/deps/uv/docs/src/misc.rst +++ b/deps/uv/docs/src/misc.rst @@ -461,6 +461,19 @@ API Gets memory information (in bytes). +.. c:function:: uint64_t uv_get_constrained_memory(void) + + Gets the amount of memory available to the process (in bytes) based on + limits imposed by the OS. If there is no such constraint, or the constraint + is unknown, `0` is returned. Note that it is not unusual for this value to + be less than or greater than :c:func:`uv_get_total_memory`. + + .. note:: + This function currently only returns a non-zero value on Linux, based + on cgroups if it is present. + + .. versionadded:: 1.29.0 + .. c:function:: uint64_t uv_hrtime(void) Returns the current high-resolution real time. This is expressed in diff --git a/deps/uv/docs/src/sphinx-plugins/manpage.py b/deps/uv/docs/src/sphinx-plugins/manpage.py index 1d1dc379f410ee..bb597e280111c4 100644 --- a/deps/uv/docs/src/sphinx-plugins/manpage.py +++ b/deps/uv/docs/src/sphinx-plugins/manpage.py @@ -39,7 +39,6 @@ def man_role(name, rawtext, text, lineno, inliner, options={}, content=[]): def setup(app): - app.info('Initializing manpage plugin') app.add_role('man', man_role) app.add_config_value('man_url_regex', None, 'env') return diff --git a/deps/uv/include/uv.h b/deps/uv/include/uv.h index df15b836789070..f97801cec2f41b 100644 --- a/deps/uv/include/uv.h +++ b/deps/uv/include/uv.h @@ -1561,6 +1561,7 @@ UV_EXTERN int uv_chdir(const char* dir); UV_EXTERN uint64_t uv_get_free_memory(void); UV_EXTERN uint64_t uv_get_total_memory(void); +UV_EXTERN uint64_t uv_get_constrained_memory(void); UV_EXTERN uint64_t uv_hrtime(void); diff --git a/deps/uv/include/uv/unix.h b/deps/uv/include/uv/unix.h index 26df0ec17a3069..f73b7b04408d0c 100644 --- a/deps/uv/include/uv/unix.h +++ b/deps/uv/include/uv/unix.h @@ -49,8 +49,6 @@ # include "uv/linux.h" #elif defined (__MVS__) # include "uv/os390.h" -#elif defined(__PASE__) -# include "uv/posix.h" #elif defined(_AIX) # include "uv/aix.h" #elif defined(__sun) @@ -63,9 +61,10 @@ defined(__OpenBSD__) || \ defined(__NetBSD__) # include "uv/bsd.h" -#elif defined(__CYGWIN__) || defined(__MSYS__) -# include "uv/posix.h" -#elif defined(__GNU__) +#elif defined(__PASE__) || \ + defined(__CYGWIN__) || \ + defined(__MSYS__) || \ + defined(__GNU__) # include "uv/posix.h" #endif diff --git a/deps/uv/include/uv/version.h b/deps/uv/include/uv/version.h index fcb813b3121815..6d3072c73596e6 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 28 -#define UV_VERSION_PATCH 0 +#define UV_VERSION_MINOR 29 +#define UV_VERSION_PATCH 1 #define UV_VERSION_IS_RELEASE 1 #define UV_VERSION_SUFFIX "" diff --git a/deps/uv/src/fs-poll.c b/deps/uv/src/fs-poll.c index 40cb147e8de504..89864e23fbcc58 100644 --- a/deps/uv/src/fs-poll.c +++ b/deps/uv/src/fs-poll.c @@ -241,7 +241,7 @@ static void timer_close_cb(uv_handle_t* timer) { handle = ctx->parent_handle; if (ctx == handle->poll_ctx) { handle->poll_ctx = ctx->previous; - if (handle->poll_ctx == NULL) + if (handle->poll_ctx == NULL && uv__is_closing(handle)) uv__make_close_pending((uv_handle_t*)handle); } else { for (last = handle->poll_ctx, it = last->previous; diff --git a/deps/uv/src/unix/aix.c b/deps/uv/src/unix/aix.c index ec1fb8b01e1823..1f36926c02e2a8 100644 --- a/deps/uv/src/unix/aix.c +++ b/deps/uv/src/unix/aix.c @@ -344,6 +344,11 @@ uint64_t uv_get_total_memory(void) { } +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + void uv_loadavg(double avg[3]) { perfstat_cpu_total_t ps_total; int result = perfstat_cpu_total(NULL, &ps_total, sizeof(ps_total), 1); diff --git a/deps/uv/src/unix/async.c b/deps/uv/src/unix/async.c index 0b450ae0da7f37..a5c47bca05908e 100644 --- a/deps/uv/src/unix/async.c +++ b/deps/uv/src/unix/async.c @@ -61,14 +61,43 @@ int uv_async_send(uv_async_t* handle) { if (ACCESS_ONCE(int, handle->pending) != 0) return 0; - if (cmpxchgi(&handle->pending, 0, 1) == 0) - uv__async_send(handle->loop); + /* Tell the other thread we're busy with the handle. */ + if (cmpxchgi(&handle->pending, 0, 1) != 0) + return 0; + + /* Wake up the other thread's event loop. */ + uv__async_send(handle->loop); + + /* Tell the other thread we're done. */ + if (cmpxchgi(&handle->pending, 1, 2) != 1) + abort(); return 0; } +/* Only call this from the event loop thread. */ +static int uv__async_spin(uv_async_t* handle) { + int rc; + + for (;;) { + /* rc=0 -- handle is not pending. + * rc=1 -- handle is pending, other thread is still working with it. + * rc=2 -- handle is pending, other thread is done. + */ + rc = cmpxchgi(&handle->pending, 2, 0); + + if (rc != 1) + return rc; + + /* Other thread is busy with this handle, spin until it's done. */ + cpu_relax(); + } +} + + void uv__async_close(uv_async_t* handle) { + uv__async_spin(handle); QUEUE_REMOVE(&handle->queue); uv__handle_stop(handle); } @@ -109,8 +138,8 @@ static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { QUEUE_REMOVE(q); QUEUE_INSERT_TAIL(&loop->async_handles, q); - if (cmpxchgi(&h->pending, 1, 0) == 0) - continue; + if (0 == uv__async_spin(h)) + continue; /* Not pending. */ if (h->async_cb == NULL) continue; diff --git a/deps/uv/src/unix/core.c b/deps/uv/src/unix/core.c index e7e9f4b8c10a72..3bada900ebdc41 100644 --- a/deps/uv/src/unix/core.c +++ b/deps/uv/src/unix/core.c @@ -88,6 +88,10 @@ #include #endif +#if defined(__linux__) +#include +#endif + static int uv__run_pending(uv_loop_t* loop); /* Verify that uv_buf_t is ABI-compatible with struct iovec. */ @@ -510,6 +514,29 @@ int uv__accept(int sockfd) { } +/* close() on macos has the "interesting" quirk that it fails with EINTR + * without closing the file descriptor when a thread is in the cancel state. + * That's why libuv calls close$NOCANCEL() instead. + * + * glibc on linux has a similar issue: close() is a cancellation point and + * will unwind the thread when it's in the cancel state. Work around that + * by making the system call directly. Musl libc is unaffected. + */ +int uv__close_nocancel(int fd) { +#if defined(__APPLE__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdollar-in-identifier-extension" + extern int close$NOCANCEL(int); + return close$NOCANCEL(fd); +#pragma GCC diagnostic pop +#elif defined(__linux__) + return syscall(SYS_close, fd); +#else + return close(fd); +#endif +} + + int uv__close_nocheckstdio(int fd) { int saved_errno; int rc; @@ -517,7 +544,7 @@ int uv__close_nocheckstdio(int fd) { assert(fd > -1); /* Catch uninitialized io_watcher.fd bugs. */ saved_errno = errno; - rc = close(fd); + rc = uv__close_nocancel(fd); if (rc == -1) { rc = UV__ERR(errno); if (rc == UV_EINTR || rc == UV__ERR(EINPROGRESS)) diff --git a/deps/uv/src/unix/darwin.c b/deps/uv/src/unix/darwin.c index 31ad8a9e48a077..e4cd8ff7e0cf99 100644 --- a/deps/uv/src/unix/darwin.c +++ b/deps/uv/src/unix/darwin.c @@ -117,6 +117,11 @@ uint64_t uv_get_total_memory(void) { } +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + void uv_loadavg(double avg[3]) { struct loadavg info; size_t size = sizeof(info); diff --git a/deps/uv/src/unix/freebsd.c b/deps/uv/src/unix/freebsd.c index 0f729cfd4776a6..7de88d6a52faf6 100644 --- a/deps/uv/src/unix/freebsd.c +++ b/deps/uv/src/unix/freebsd.c @@ -137,6 +137,11 @@ uint64_t uv_get_total_memory(void) { } +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + void uv_loadavg(double avg[3]) { struct loadavg info; size_t size = sizeof(info); diff --git a/deps/uv/src/unix/fs.c b/deps/uv/src/unix/fs.c index c6d2259adc4080..24a130f5433d6e 100644 --- a/deps/uv/src/unix/fs.c +++ b/deps/uv/src/unix/fs.c @@ -145,7 +145,7 @@ extern char *mkdtemp(char *template); /* See issue #740 on AIX < 7 */ static int uv__fs_close(int fd) { int rc; - rc = close(fd); + rc = uv__close_nocancel(fd); if (rc == -1) if (errno == EINTR || errno == EINPROGRESS) rc = 0; /* The close is in progress, not an error. */ @@ -1165,6 +1165,8 @@ static int uv__fs_statx(int fd, buf->st_ctim.tv_nsec = statxbuf.stx_ctime.tv_nsec; buf->st_birthtim.tv_sec = statxbuf.stx_btime.tv_sec; buf->st_birthtim.tv_nsec = statxbuf.stx_btime.tv_nsec; + buf->st_flags = 0; + buf->st_gen = 0; return 0; #else diff --git a/deps/uv/src/unix/fsevents.c b/deps/uv/src/unix/fsevents.c index c430562b37298a..ddacda31fef87e 100644 --- a/deps/uv/src/unix/fsevents.c +++ b/deps/uv/src/unix/fsevents.c @@ -21,9 +21,10 @@ #include "uv.h" #include "internal.h" -#if TARGET_OS_IPHONE +#if TARGET_OS_IPHONE || MAC_OS_X_VERSION_MAX_ALLOWED < 1070 /* iOS (currently) doesn't provide the FSEvents-API (nor CoreServices) */ +/* macOS prior to 10.7 doesn't provide the full FSEvents API so use kqueue */ int uv__fsevents_init(uv_fs_event_t* handle) { return 0; diff --git a/deps/uv/src/unix/ibmi.c b/deps/uv/src/unix/ibmi.c index b1ab549c23f4b9..8b355033e29ce5 100644 --- a/deps/uv/src/unix/ibmi.c +++ b/deps/uv/src/unix/ibmi.c @@ -55,19 +55,155 @@ #include #include +#include + + +typedef struct { + int bytes_available; + int bytes_returned; + char current_date_and_time[8]; + char system_name[8]; + char elapsed_time[6]; + char restricted_state_flag; + char reserved; + int percent_processing_unit_used; + int jobs_in_system; + int percent_permanent_addresses; + int percent_temporary_addresses; + int system_asp; + int percent_system_asp_used; + int total_auxiliary_storage; + int current_unprotected_storage_used; + int maximum_unprotected_storage_used; + int percent_db_capability; + int main_storage_size; + int number_of_partitions; + int partition_identifier; + int reserved1; + int current_processing_capacity; + char processor_sharing_attribute; + char reserved2[3]; + int number_of_processors; + int active_jobs_in_system; + int active_threads_in_system; + int maximum_jobs_in_system; + int percent_temporary_256mb_segments_used; + int percent_temporary_4gb_segments_used; + int percent_permanent_256mb_segments_used; + int percent_permanent_4gb_segments_used; + int percent_current_interactive_performance; + int percent_uncapped_cpu_capacity_used; + int percent_shared_processor_pool_used; + long main_storage_size_long; +} SSTS0200; + + +static int get_ibmi_system_status(SSTS0200* rcvr) { + /* rcvrlen is input parameter 2 to QWCRSSTS */ + unsigned int rcvrlen = sizeof(*rcvr); + + /* format is input parameter 3 to QWCRSSTS ("SSTS0200" in EBCDIC) */ + unsigned char format[] = {0xE2, 0xE2, 0xE3, 0xE2, 0xF0, 0xF2, 0xF0, 0xF0}; + + /* reset_status is input parameter 4 to QWCRSSTS ("*NO " in EBCDIC) */ + unsigned char reset_status[] = { + 0x5C, 0xD5, 0xD6, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 + }; + + /* errcode is input parameter 5 to QWCRSSTS */ + struct _errcode { + int bytes_provided; + int bytes_available; + char msgid[7]; + } errcode; + + /* qwcrssts_pointer is the 16-byte tagged system pointer to QWCRSSTS */ + ILEpointer __attribute__((aligned(16))) qwcrssts_pointer; + + /* qwcrssts_argv is the array of argument pointers to QWCRSSTS */ + void* qwcrssts_argv[6]; + + /* Set the IBM i pointer to the QSYS/QWCRSSTS *PGM object */ + int rc = _RSLOBJ2(&qwcrssts_pointer, RSLOBJ_TS_PGM, "QWCRSSTS", "QSYS"); + + if (rc != 0) + return rc; + + /* initialize the QWCRSSTS returned info structure */ + memset(rcvr, 0, sizeof(*rcvr)); + + /* initialize the QWCRSSTS error code structure */ + memset(&errcode, 0, sizeof(errcode)); + errcode.bytes_provided = sizeof(errcode); + + /* initialize the array of argument pointers for the QWCRSSTS API */ + qwcrssts_argv[0] = rcvr; + qwcrssts_argv[1] = &rcvrlen; + qwcrssts_argv[2] = &format; + qwcrssts_argv[3] = &reset_status; + qwcrssts_argv[4] = &errcode; + qwcrssts_argv[5] = NULL; + + /* Call the IBM i QWCRSSTS API from PASE */ + rc = _PGMCALL(&qwcrssts_pointer, (void**)&qwcrssts_argv, 0); + + return rc; +} + + uint64_t uv_get_free_memory(void) { - return (uint64_t) sysconf(_SC_PAGESIZE) * sysconf(_SC_AVPHYS_PAGES); + SSTS0200 rcvr; + + if (get_ibmi_system_status(&rcvr)) + return 0; + + /* The amount of main storage, in kilobytes, in the system. */ + uint64_t main_storage_size = rcvr.main_storage_size; + + /* The current amount of storage in use for temporary objects. + * in millions (M) of bytes. + */ + uint64_t current_unprotected_storage_used = + rcvr.current_unprotected_storage_used * 1024ULL; + + uint64_t free_storage_size = + (main_storage_size - current_unprotected_storage_used) * 1024ULL; + + return free_storage_size < 0 ? 0 : free_storage_size; } uint64_t uv_get_total_memory(void) { - return (uint64_t) sysconf(_SC_PAGESIZE) * sysconf(_SC_PHYS_PAGES); + SSTS0200 rcvr; + + if (get_ibmi_system_status(&rcvr)) + return 0; + + return (uint64_t)rcvr.main_storage_size * 1024ULL; +} + + +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ } void uv_loadavg(double avg[3]) { + SSTS0200 rcvr; + + if (get_ibmi_system_status(&rcvr)) { avg[0] = avg[1] = avg[2] = 0; return; + } + + /* The average (in tenths) of the elapsed time during which the processing + * units were in use. For example, a value of 411 in binary would be 41.1%. + * This percentage could be greater than 100% for an uncapped partition. + */ + double processing_unit_used_percent = + rcvr.percent_processing_unit_used / 1000.0; + + avg[0] = avg[1] = avg[2] = processing_unit_used_percent; } @@ -110,4 +246,4 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { *count = numcpus; return 0; -} \ No newline at end of file +} diff --git a/deps/uv/src/unix/internal.h b/deps/uv/src/unix/internal.h index a0846970320da5..8c8ddc868ec800 100644 --- a/deps/uv/src/unix/internal.h +++ b/deps/uv/src/unix/internal.h @@ -183,6 +183,7 @@ int uv__nonblock_ioctl(int fd, int set); int uv__nonblock_fcntl(int fd, int set); int uv__close(int fd); /* preserves errno */ int uv__close_nocheckstdio(int fd); +int uv__close_nocancel(int fd); int uv__socket(int domain, int type, int protocol); ssize_t uv__recvmsg(int fd, struct msghdr *msg, int flags); void uv__make_close_pending(uv_handle_t* handle); diff --git a/deps/uv/src/unix/kqueue.c b/deps/uv/src/unix/kqueue.c index 092005161f4546..c04e7a485cf992 100644 --- a/deps/uv/src/unix/kqueue.c +++ b/deps/uv/src/unix/kqueue.c @@ -59,7 +59,7 @@ int uv__kqueue_init(uv_loop_t* loop) { } -#if defined(__APPLE__) +#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 static int uv__has_forked_with_cfrunloop; #endif @@ -70,7 +70,7 @@ int uv__io_fork(uv_loop_t* loop) { if (err) return err; -#if defined(__APPLE__) +#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 if (loop->cf_state != NULL) { /* We cannot start another CFRunloop and/or thread in the child process; CF aborts if you try or if you try to touch the thread @@ -86,7 +86,7 @@ int uv__io_fork(uv_loop_t* loop) { uv__free(loop->cf_state); loop->cf_state = NULL; } -#endif +#endif /* #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */ return err; } @@ -458,7 +458,7 @@ int uv_fs_event_start(uv_fs_event_t* handle, if (uv__is_active(handle)) return UV_EINVAL; -#if defined(__APPLE__) +#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 /* Nullify field to perform checks later */ handle->cf_cb = NULL; handle->realpath = NULL; @@ -482,7 +482,7 @@ int uv_fs_event_start(uv_fs_event_t* handle, } return r; } -#endif /* defined(__APPLE__) */ +#endif /* #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */ /* TODO open asynchronously - but how do we report back errors? */ fd = open(path, O_RDONLY); @@ -513,7 +513,7 @@ int uv_fs_event_stop(uv_fs_event_t* handle) { uv__handle_stop(handle); -#if defined(__APPLE__) +#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 if (!uv__has_forked_with_cfrunloop) r = uv__fsevents_close(handle); #endif diff --git a/deps/uv/src/unix/linux-core.c b/deps/uv/src/unix/linux-core.c index f8973bb36be7ae..b539beb86ae576 100644 --- a/deps/uv/src/unix/linux-core.c +++ b/deps/uv/src/unix/linux-core.c @@ -26,6 +26,7 @@ #include "uv.h" #include "internal.h" +#include #include #include #include @@ -79,16 +80,20 @@ static int read_times(FILE* statfile_fp, unsigned int numcpus, uv_cpu_info_t* ci); static void read_speeds(unsigned int numcpus, uv_cpu_info_t* ci); -static unsigned long read_cpufreq(unsigned int cpunum); +static uint64_t read_cpufreq(unsigned int cpunum); int uv__platform_loop_init(uv_loop_t* loop) { int fd; - fd = epoll_create1(EPOLL_CLOEXEC); + /* It was reported that EPOLL_CLOEXEC is not defined on Android API < 21, + * a.k.a. Lollipop. Since EPOLL_CLOEXEC is an alias for O_CLOEXEC on all + * architectures, we just use that instead. + */ + fd = epoll_create1(O_CLOEXEC); /* epoll_create1() can fail either because it's not implemented (old kernel) - * or because it doesn't understand the EPOLL_CLOEXEC flag. + * or because it doesn't understand the O_CLOEXEC flag. */ if (fd == -1 && (errno == ENOSYS || errno == EINVAL)) { fd = epoll_create(256); @@ -715,20 +720,20 @@ static int read_models(unsigned int numcpus, uv_cpu_info_t* ci) { static int read_times(FILE* statfile_fp, unsigned int numcpus, uv_cpu_info_t* ci) { - unsigned long clock_ticks; struct uv_cpu_times_s ts; - unsigned long user; - unsigned long nice; - unsigned long sys; - unsigned long idle; - unsigned long dummy; - unsigned long irq; - unsigned int num; - unsigned int len; + uint64_t clock_ticks; + uint64_t user; + uint64_t nice; + uint64_t sys; + uint64_t idle; + uint64_t dummy; + uint64_t irq; + uint64_t num; + uint64_t len; char buf[1024]; clock_ticks = sysconf(_SC_CLK_TCK); - assert(clock_ticks != (unsigned long) -1); + assert(clock_ticks != (uint64_t) -1); assert(clock_ticks != 0); rewind(statfile_fp); @@ -761,7 +766,8 @@ static int read_times(FILE* statfile_fp, * fields, they're not allowed in C89 mode. */ if (6 != sscanf(buf + len, - "%lu %lu %lu %lu %lu %lu", + "%" PRIu64 " %" PRIu64 " %" PRIu64 + "%" PRIu64 " %" PRIu64 " %" PRIu64, &user, &nice, &sys, @@ -783,8 +789,8 @@ static int read_times(FILE* statfile_fp, } -static unsigned long read_cpufreq(unsigned int cpunum) { - unsigned long val; +static uint64_t read_cpufreq(unsigned int cpunum) { + uint64_t val; char buf[1024]; FILE* fp; @@ -797,7 +803,7 @@ static unsigned long read_cpufreq(unsigned int cpunum) { if (fp == NULL) return 0; - if (fscanf(fp, "%lu", &val) != 1) + if (fscanf(fp, "%" PRIu64, &val) != 1) val = 0; fclose(fp); @@ -900,7 +906,10 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { address = *addresses; for (i = 0; i < (*count); i++) { - if (strcmp(address->name, ent->ifa_name) == 0) { + size_t namelen = strlen(ent->ifa_name); + /* Alias interface share the same physical address */ + if (strncmp(address->name, ent->ifa_name, namelen) == 0 && + (address->name[namelen] == 0 || address->name[namelen] == ':')) { sll = (struct sockaddr_ll*)ent->ifa_addr; memcpy(address->phys_addr, sll->sll_addr, sizeof(address->phys_addr)); } @@ -932,3 +941,114 @@ void uv__set_process_title(const char* title) { prctl(PR_SET_NAME, title); /* Only copies first 16 characters. */ #endif } + + +static uint64_t uv__read_proc_meminfo(const char* what) { + uint64_t rc; + ssize_t n; + char* p; + int fd; + char buf[4096]; /* Large enough to hold all of /proc/meminfo. */ + + rc = 0; + fd = uv__open_cloexec("/proc/meminfo", O_RDONLY); + + if (fd == -1) + return 0; + + n = read(fd, buf, sizeof(buf) - 1); + + if (n <= 0) + goto out; + + buf[n] = '\0'; + p = strstr(buf, what); + + if (p == NULL) + goto out; + + p += strlen(what); + + if (1 != sscanf(p, "%" PRIu64 " kB", &rc)) + goto out; + + rc *= 1024; + +out: + + if (uv__close_nocheckstdio(fd)) + abort(); + + return rc; +} + + +uint64_t uv_get_free_memory(void) { + struct sysinfo info; + uint64_t rc; + + rc = uv__read_proc_meminfo("MemFree:"); + + if (rc != 0) + return rc; + + if (0 == sysinfo(&info)) + return (uint64_t) info.freeram * info.mem_unit; + + return 0; +} + + +uint64_t uv_get_total_memory(void) { + struct sysinfo info; + uint64_t rc; + + rc = uv__read_proc_meminfo("MemTotal:"); + + if (rc != 0) + return rc; + + if (0 == sysinfo(&info)) + return (uint64_t) info.totalram * info.mem_unit; + + return 0; +} + + +static uint64_t uv__read_cgroups_uint64(const char* cgroup, const char* param) { + char filename[256]; + uint64_t rc; + int fd; + ssize_t n; + char buf[32]; /* Large enough to hold an encoded uint64_t. */ + + snprintf(filename, 256, "/sys/fs/cgroup/%s/%s", cgroup, param); + + rc = 0; + fd = uv__open_cloexec(filename, O_RDONLY); + + if (fd < 0) + return 0; + + n = read(fd, buf, sizeof(buf) - 1); + + if (n > 0) { + buf[n] = '\0'; + sscanf(buf, "%" PRIu64, &rc); + } + + if (uv__close_nocheckstdio(fd)) + abort(); + + return rc; +} + + +uint64_t uv_get_constrained_memory(void) { + /* + * This might return 0 if there was a problem getting the memory limit from + * cgroups. This is OK because a return value of 0 signifies that the memory + * limit is unknown. + */ + return uv__read_cgroups_uint64("memory", "memory.limit_in_bytes"); +} diff --git a/deps/uv/src/unix/linux-syscalls.c b/deps/uv/src/unix/linux-syscalls.c index beaba4ed9afd32..5637cf98a7b338 100644 --- a/deps/uv/src/unix/linux-syscalls.c +++ b/deps/uv/src/unix/linux-syscalls.c @@ -358,7 +358,10 @@ int uv__statx(int dirfd, int flags, unsigned int mask, struct uv__statx* statxbuf) { -#if defined(__NR_statx) + /* __NR_statx make Android box killed by SIGSYS. + * That looks like a seccomp2 sandbox filter rejecting the system call. + */ +#if defined(__NR_statx) && !defined(__ANDROID__) return syscall(__NR_statx, dirfd, path, flags, mask, statxbuf); #else return errno = ENOSYS, -1; diff --git a/deps/uv/src/unix/netbsd.c b/deps/uv/src/unix/netbsd.c index a2a4e521542af3..c649bb375f32a1 100644 --- a/deps/uv/src/unix/netbsd.c +++ b/deps/uv/src/unix/netbsd.c @@ -126,6 +126,11 @@ uint64_t uv_get_total_memory(void) { } +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + int uv_resident_set_memory(size_t* rss) { kvm_t *kd = NULL; struct kinfo_proc2 *kinfo = NULL; diff --git a/deps/uv/src/unix/openbsd.c b/deps/uv/src/unix/openbsd.c index bffb58bcd9ff00..ffae7683d8b620 100644 --- a/deps/uv/src/unix/openbsd.c +++ b/deps/uv/src/unix/openbsd.c @@ -136,6 +136,11 @@ uint64_t uv_get_total_memory(void) { } +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + int uv_resident_set_memory(size_t* rss) { struct kinfo_proc kinfo; size_t page_size = getpagesize(); diff --git a/deps/uv/src/unix/os390.c b/deps/uv/src/unix/os390.c index 70e389ece3bc73..273ded7ca5e81c 100644 --- a/deps/uv/src/unix/os390.c +++ b/deps/uv/src/unix/os390.c @@ -356,6 +356,11 @@ uint64_t uv_get_total_memory(void) { } +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + int uv_resident_set_memory(size_t* rss) { char* ascb; char* rax; diff --git a/deps/uv/src/unix/process.c b/deps/uv/src/unix/process.c index 101c9c53dfafe3..b284308dd0694f 100644 --- a/deps/uv/src/unix/process.c +++ b/deps/uv/src/unix/process.c @@ -315,7 +315,7 @@ static void uv__process_child_init(const uv_process_options_t* options, use_fd = open("/dev/null", fd == 0 ? O_RDONLY : O_RDWR); close_fd = use_fd; - if (use_fd == -1) { + if (use_fd < 0) { uv__write_int(error_fd, UV__ERR(errno)); _exit(127); } @@ -431,6 +431,8 @@ int uv_spawn(uv_loop_t* loop, UV_PROCESS_SETGID | UV_PROCESS_SETUID | UV_PROCESS_WINDOWS_HIDE | + UV_PROCESS_WINDOWS_HIDE_CONSOLE | + UV_PROCESS_WINDOWS_HIDE_GUI | UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS))); uv__handle_init(loop, (uv_handle_t*)process, UV_PROCESS); diff --git a/deps/uv/src/unix/sunos.c b/deps/uv/src/unix/sunos.c index fb6b070fd18f2e..f323d1defdef98 100644 --- a/deps/uv/src/unix/sunos.c +++ b/deps/uv/src/unix/sunos.c @@ -380,6 +380,11 @@ uint64_t uv_get_total_memory(void) { } +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + void uv_loadavg(double avg[3]) { (void) getloadavg(avg, 3); } diff --git a/deps/uv/src/win/util.c b/deps/uv/src/win/util.c index 2c10728427b64d..7ca83213a67cca 100644 --- a/deps/uv/src/win/util.c +++ b/deps/uv/src/win/util.c @@ -320,6 +320,11 @@ uint64_t uv_get_total_memory(void) { } +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + uv_pid_t uv_os_getpid(void) { return GetCurrentProcessId(); } @@ -703,9 +708,11 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos_ptr, int* cpu_count_ptr) { return 0; error: - /* This is safe because the cpu_infos array is zeroed on allocation. */ - for (i = 0; i < cpu_count; i++) - uv__free(cpu_infos[i].model); + if (cpu_infos != NULL) { + /* This is safe because the cpu_infos array is zeroed on allocation. */ + for (i = 0; i < cpu_count; i++) + uv__free(cpu_infos[i].model); + } uv__free(cpu_infos); uv__free(sppi); diff --git a/deps/uv/test/test-fs-poll.c b/deps/uv/test/test-fs-poll.c index e19a68780fa706..9dfd5fdd6aa282 100644 --- a/deps/uv/test/test-fs-poll.c +++ b/deps/uv/test/test-fs-poll.c @@ -37,6 +37,10 @@ static void poll_cb_fail(uv_fs_poll_t* handle, int status, const uv_stat_t* prev, const uv_stat_t* curr); +static void poll_cb_noop(uv_fs_poll_t* handle, + int status, + const uv_stat_t* prev, + const uv_stat_t* curr); static uv_fs_poll_t poll_handle; static uv_timer_t timer_handle; @@ -84,6 +88,12 @@ static void poll_cb_fail(uv_fs_poll_t* handle, ASSERT(0 && "fail_cb called"); } +static void poll_cb_noop(uv_fs_poll_t* handle, + int status, + const uv_stat_t* prev, + const uv_stat_t* curr) { +} + static void poll_cb(uv_fs_poll_t* handle, int status, @@ -259,3 +269,32 @@ TEST_IMPL(fs_poll_close_request_multi_stop_start) { MAKE_VALGRIND_HAPPY(); return 0; } + +TEST_IMPL(fs_poll_close_request_stop_when_active) { + /* Regression test for https://github.com/libuv/libuv/issues/2287. */ + uv_loop_t loop; + uv_fs_poll_t poll_handle; + + remove(FIXTURE); + + ASSERT(0 == uv_loop_init(&loop)); + + /* Set up all handles. */ + ASSERT(0 == uv_fs_poll_init(&loop, &poll_handle)); + ASSERT(0 == uv_fs_poll_start(&poll_handle, poll_cb_noop, FIXTURE, 100)); + uv_run(&loop, UV_RUN_ONCE); + + /* Close the timer handle, and do not crash. */ + ASSERT(0 == uv_fs_poll_stop(&poll_handle)); + uv_run(&loop, UV_RUN_ONCE); + + /* Clean up after the test. */ + uv_close((uv_handle_t*) &poll_handle, close_cb); + uv_run(&loop, UV_RUN_ONCE); + ASSERT(close_cb_called == 1); + + ASSERT(0 == uv_loop_close(&loop)); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/deps/uv/test/test-fs.c b/deps/uv/test/test-fs.c index 35f7d0c3f165df..c3153c717b7cd3 100644 --- a/deps/uv/test/test-fs.c +++ b/deps/uv/test/test-fs.c @@ -611,6 +611,15 @@ static void sendfile_cb(uv_fs_t* req) { } +static void sendfile_nodata_cb(uv_fs_t* req) { + ASSERT(req == &sendfile_req); + ASSERT(req->fs_type == UV_FS_SENDFILE); + ASSERT(req->result == 0); + sendfile_cb_count++; + uv_fs_req_cleanup(req); +} + + static void open_noent_cb(uv_fs_t* req) { ASSERT(req->fs_type == UV_FS_OPEN); ASSERT(req->result == UV_ENOENT); @@ -1049,7 +1058,7 @@ TEST_IMPL(fs_async_dir) { } -TEST_IMPL(fs_async_sendfile) { +static int test_sendfile(void (*setup)(int), uv_fs_cb cb, off_t expected_size) { int f, r; struct stat s1, s2; @@ -1062,14 +1071,8 @@ TEST_IMPL(fs_async_sendfile) { f = open("test_file", O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR); ASSERT(f != -1); - r = write(f, "begin\n", 6); - ASSERT(r == 6); - - r = lseek(f, 65536, SEEK_CUR); - ASSERT(r == 65542); - - r = write(f, "end\n", 4); - ASSERT(r != -1); + if (setup != NULL) + setup(f); r = close(f); ASSERT(r == 0); @@ -1087,7 +1090,7 @@ TEST_IMPL(fs_async_sendfile) { uv_fs_req_cleanup(&open_req2); r = uv_fs_sendfile(loop, &sendfile_req, open_req2.result, open_req1.result, - 0, 131072, sendfile_cb); + 0, 131072, cb); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); @@ -1100,9 +1103,10 @@ TEST_IMPL(fs_async_sendfile) { ASSERT(r == 0); uv_fs_req_cleanup(&close_req); - stat("test_file", &s1); - stat("test_file2", &s2); - ASSERT(65546 == s2.st_size && s1.st_size == s2.st_size); + ASSERT(0 == stat("test_file", &s1)); + ASSERT(0 == stat("test_file2", &s2)); + ASSERT(s1.st_size == s2.st_size); + ASSERT(s2.st_size == expected_size); /* Cleanup. */ unlink("test_file"); @@ -1113,6 +1117,23 @@ TEST_IMPL(fs_async_sendfile) { } +static void sendfile_setup(int f) { + ASSERT(6 == write(f, "begin\n", 6)); + ASSERT(65542 == lseek(f, 65536, SEEK_CUR)); + ASSERT(4 == write(f, "end\n", 4)); +} + + +TEST_IMPL(fs_async_sendfile) { + return test_sendfile(sendfile_setup, sendfile_cb, 65546); +} + + +TEST_IMPL(fs_async_sendfile_nodata) { + return test_sendfile(NULL, sendfile_nodata_cb, 0); +} + + TEST_IMPL(fs_mkdtemp) { int r; const char* path_template = "test_dir_XXXXXX"; @@ -1171,6 +1192,7 @@ TEST_IMPL(fs_fstat) { ASSERT(req.result == sizeof(test_buf)); uv_fs_req_cleanup(&req); + memset(&req.statbuf, 0xaa, sizeof(req.statbuf)); r = uv_fs_fstat(NULL, &req, file, NULL); ASSERT(r == 0); ASSERT(req.result == 0); @@ -1257,6 +1279,8 @@ TEST_IMPL(fs_fstat) { s->st_birthtim.tv_sec == t.st_ctim.tv_sec); ASSERT(s->st_birthtim.tv_nsec == 0 || s->st_birthtim.tv_nsec == t.st_ctim.tv_nsec); + ASSERT(s->st_flags == 0); + ASSERT(s->st_gen == 0); #endif uv_fs_req_cleanup(&req); diff --git a/deps/uv/test/test-get-memory.c b/deps/uv/test/test-get-memory.c index 2396939bcb1557..0e0864038b3219 100644 --- a/deps/uv/test/test-get-memory.c +++ b/deps/uv/test/test-get-memory.c @@ -25,10 +25,12 @@ TEST_IMPL(get_memory) { uint64_t free_mem = uv_get_free_memory(); uint64_t total_mem = uv_get_total_memory(); + uint64_t constrained_mem = uv_get_constrained_memory(); - printf("free_mem=%llu, total_mem=%llu\n", + printf("free_mem=%llu, total_mem=%llu, constrained_mem=%llu\n", (unsigned long long) free_mem, - (unsigned long long) total_mem); + (unsigned long long) total_mem, + (unsigned long long) constrained_mem); ASSERT(free_mem > 0); ASSERT(total_mem > 0); diff --git a/deps/uv/test/test-list.h b/deps/uv/test/test-list.h index ace501c9796f00..8886b07c8a7473 100644 --- a/deps/uv/test/test-list.h +++ b/deps/uv/test/test-list.h @@ -290,6 +290,7 @@ TEST_DECLARE (fs_poll_getpath) TEST_DECLARE (fs_poll_close_request) TEST_DECLARE (fs_poll_close_request_multi_start_stop) TEST_DECLARE (fs_poll_close_request_multi_stop_start) +TEST_DECLARE (fs_poll_close_request_stop_when_active) TEST_DECLARE (kill) TEST_DECLARE (kill_invalid_signum) TEST_DECLARE (fs_file_noent) @@ -300,6 +301,7 @@ TEST_DECLARE (fs_file_sync) TEST_DECLARE (fs_file_write_null_buffer) TEST_DECLARE (fs_async_dir) TEST_DECLARE (fs_async_sendfile) +TEST_DECLARE (fs_async_sendfile_nodata) TEST_DECLARE (fs_mkdtemp) TEST_DECLARE (fs_fstat) TEST_DECLARE (fs_access) @@ -843,6 +845,7 @@ TASK_LIST_START TEST_ENTRY (fs_poll_close_request) TEST_ENTRY (fs_poll_close_request_multi_start_stop) TEST_ENTRY (fs_poll_close_request_multi_stop_start) + TEST_ENTRY (fs_poll_close_request_stop_when_active) TEST_ENTRY (kill) TEST_ENTRY (kill_invalid_signum) @@ -886,6 +889,7 @@ TASK_LIST_START TEST_ENTRY (fs_file_write_null_buffer) TEST_ENTRY (fs_async_dir) TEST_ENTRY (fs_async_sendfile) + TEST_ENTRY (fs_async_sendfile_nodata) TEST_ENTRY (fs_mkdtemp) TEST_ENTRY (fs_fstat) TEST_ENTRY (fs_access) @@ -959,7 +963,7 @@ TASK_LIST_START TEST_ENTRY (strscpy) TEST_ENTRY (threadpool_queue_work_simple) TEST_ENTRY (threadpool_queue_work_einval) - TEST_ENTRY (threadpool_multiple_event_loops) + TEST_ENTRY_CUSTOM (threadpool_multiple_event_loops, 0, 0, 60000) TEST_ENTRY (threadpool_cancel_getaddrinfo) TEST_ENTRY (threadpool_cancel_getnameinfo) TEST_ENTRY (threadpool_cancel_work) diff --git a/deps/uv/test/test-spawn.c b/deps/uv/test/test-spawn.c index e5fc308a0e4345..fea1165d89e08e 100644 --- a/deps/uv/test/test-spawn.c +++ b/deps/uv/test/test-spawn.c @@ -1406,6 +1406,12 @@ TEST_IMPL(spawn_setuid_fails) { options.flags |= UV_PROCESS_SETUID; options.uid = 0; + /* These flags should be ignored on Unices. */ + options.flags |= UV_PROCESS_WINDOWS_HIDE; + options.flags |= UV_PROCESS_WINDOWS_HIDE_CONSOLE; + options.flags |= UV_PROCESS_WINDOWS_HIDE_GUI; + options.flags |= UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS; + r = uv_spawn(uv_default_loop(), &process, &options); #if defined(__CYGWIN__) ASSERT(r == UV_EINVAL); diff --git a/deps/uv/uv.gyp b/deps/uv/uv.gyp index 0836dd27f2b72a..46de9b769e76e4 100644 --- a/deps/uv/uv.gyp +++ b/deps/uv/uv.gyp @@ -241,7 +241,6 @@ 'src/unix/linux-syscalls.h', 'src/unix/procfs-exepath.c', 'src/unix/sysinfo-loadavg.c', - 'src/unix/sysinfo-memory.c', ], 'link_settings': { 'libraries': [ '-ldl', '-lrt' ], From 6f7005465a0c9bc0ce9764e1c0d4299eb22a3480 Mon Sep 17 00:00:00 2001 From: Gus Caplan Date: Tue, 19 Mar 2019 18:00:16 -0500 Subject: [PATCH 2/5] src, lib: take control of prepareStackTrace Refs https://crbug.com/v8/7848 PR-URL: https://github.com/nodejs/node/pull/23926 Reviewed-By: Refael Ackermann Reviewed-By: Joyee Cheung --- lib/internal/bootstrap/node.js | 8 ++++++ lib/internal/errors.js | 28 +++++++++++++++++- src/api/environment.cc | 38 +++++++++++++++++++++++++ src/env.h | 1 + src/node_binding.cc | 1 + src/node_errors.cc | 18 ++++++++++++ test/parallel/test-bootstrap-modules.js | 1 + 7 files changed, 94 insertions(+), 1 deletion(-) diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js index 321bfabe027d65..8dee38a0e71698 100644 --- a/lib/internal/bootstrap/node.js +++ b/lib/internal/bootstrap/node.js @@ -35,6 +35,8 @@ // passed by node::RunBootstrapping() /* global process, require, internalBinding, isMainThread, ownsProcessState */ +setupPrepareStackTrace(); + const { JSON, Object, Symbol } = primordials; const config = internalBinding('config'); const { deprecate } = require('internal/util'); @@ -290,6 +292,12 @@ process.emitWarning = emitWarning; // Note: only after this point are the timers effective } +function setupPrepareStackTrace() { + const { setPrepareStackTraceCallback } = internalBinding('errors'); + const { prepareStackTrace } = require('internal/errors'); + setPrepareStackTraceCallback(prepareStackTrace); +} + function setupProcessObject() { const EventEmitter = require('events'); const origProcProto = Object.getPrototypeOf(process); diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 52587f1f4e0971..266358310bf3f7 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -19,6 +19,31 @@ const codes = {}; const { kMaxLength } = internalBinding('buffer'); +const MainContextError = Error; +const ErrorToString = Error.prototype.toString; +// Polyfill of V8's Error.prepareStackTrace API. +// https://crbug.com/v8/7848 +const prepareStackTrace = (globalThis, error, trace) => { + // `globalThis` is the global that contains the constructor which + // created `error`. + if (typeof globalThis.Error.prepareStackTrace === 'function') { + return globalThis.Error.prepareStackTrace(error, trace); + } + // We still have legacy usage that depends on the main context's `Error` + // being used, even when the error is from a different context. + // TODO(devsnek): evaluate if this can be eventually deprecated/removed. + if (typeof MainContextError.prepareStackTrace === 'function') { + return MainContextError.prepareStackTrace(error, trace); + } + + const errorString = ErrorToString.call(error); + if (trace.length === 0) { + return errorString; + } + return `${errorString}\n at ${trace.join('\n at ')}`; +}; + + let excludedStackFn; // Lazily loaded @@ -598,7 +623,8 @@ module.exports = { uvExceptionWithHostPort, SystemError, // This is exported only to facilitate testing. - E + E, + prepareStackTrace, }; // To declare an error message, use the E(sym, val, def) function above. The sym diff --git a/src/api/environment.cc b/src/api/environment.cc index 548b685dfe424a..5dfac00647eddd 100644 --- a/src/api/environment.cc +++ b/src/api/environment.cc @@ -13,6 +13,8 @@ #endif namespace node { +using errors::TryCatchScope; +using v8::Array; using v8::Context; using v8::EscapableHandleScope; using v8::Function; @@ -45,6 +47,41 @@ static bool ShouldAbortOnUncaughtException(Isolate* isolate) { !env->inside_should_not_abort_on_uncaught_scope(); } +static MaybeLocal PrepareStackTraceCallback(Local context, + Local exception, + Local trace) { + Environment* env = Environment::GetCurrent(context); + if (env == nullptr) { + MaybeLocal s = exception->ToString(context); + return s.IsEmpty() ? + MaybeLocal() : + MaybeLocal(s.ToLocalChecked()); + } + Local prepare = env->prepare_stack_trace_callback(); + if (prepare.IsEmpty()) { + MaybeLocal s = exception->ToString(context); + return s.IsEmpty() ? + MaybeLocal() : + MaybeLocal(s.ToLocalChecked()); + } + Local args[] = { + context->Global(), + exception, + trace, + }; + // This TryCatch + Rethrow is required by V8 due to details around exception + // handling there. For C++ callbacks, V8 expects a scheduled exception (which + // is what ReThrow gives us). Just returning the empty MaybeLocal would leave + // us with a pending exception. + TryCatchScope try_catch(env); + MaybeLocal result = prepare->Call( + context, Undefined(env->isolate()), arraysize(args), args); + if (try_catch.HasCaught() && !try_catch.HasTerminated()) { + try_catch.ReThrow(); + } + return result; +} + void* NodeArrayBufferAllocator::Allocate(size_t size) { if (zero_fill_field_ || per_process::cli_options->zero_fill_all_buffers) return UncheckedCalloc(size); @@ -166,6 +203,7 @@ void SetIsolateUpForNode(v8::Isolate* isolate, IsolateSettingCategories cat) { isolate->SetAbortOnUncaughtExceptionCallback( ShouldAbortOnUncaughtException); isolate->SetFatalErrorHandler(OnFatalError); + isolate->SetPrepareStackTraceCallback(PrepareStackTraceCallback); break; case IsolateSettingCategories::kMisc: isolate->SetMicrotasksPolicy(MicrotasksPolicy::kExplicit); diff --git a/src/env.h b/src/env.h index 7b7e9f61320a36..5544ac44db5f8a 100644 --- a/src/env.h +++ b/src/env.h @@ -402,6 +402,7 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2; V(native_module_require, v8::Function) \ V(performance_entry_callback, v8::Function) \ V(performance_entry_template, v8::Function) \ + V(prepare_stack_trace_callback, v8::Function) \ V(process_object, v8::Object) \ V(primordials, v8::Object) \ V(promise_reject_callback, v8::Function) \ diff --git a/src/node_binding.cc b/src/node_binding.cc index 9a9bfa70a8a378..99c2406036e187 100644 --- a/src/node_binding.cc +++ b/src/node_binding.cc @@ -48,6 +48,7 @@ V(contextify) \ V(credentials) \ V(domain) \ + V(errors) \ V(fs) \ V(fs_event_wrap) \ V(heap_utils) \ diff --git a/src/node_errors.cc b/src/node_errors.cc index 1cd90523830fc9..603c9e415c3f54 100644 --- a/src/node_errors.cc +++ b/src/node_errors.cc @@ -17,6 +17,7 @@ using v8::Boolean; using v8::Context; using v8::Exception; using v8::Function; +using v8::FunctionCallbackInfo; using v8::HandleScope; using v8::Int32; using v8::Isolate; @@ -767,6 +768,21 @@ void PerIsolateMessageListener(Local message, Local error) { } } +void SetPrepareStackTraceCallback(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + CHECK(args[0]->IsFunction()); + env->set_prepare_stack_trace_callback(args[0].As()); +} + +void Initialize(Local target, + Local unused, + Local context, + void* priv) { + Environment* env = Environment::GetCurrent(context); + env->SetMethod( + target, "setPrepareStackTraceCallback", SetPrepareStackTraceCallback); +} + } // namespace errors void DecorateErrorStack(Environment* env, @@ -880,3 +896,5 @@ void FatalException(Isolate* isolate, } } // namespace node + +NODE_MODULE_CONTEXT_AWARE_INTERNAL(errors, node::errors::Initialize) diff --git a/test/parallel/test-bootstrap-modules.js b/test/parallel/test-bootstrap-modules.js index 50b0a53724da94..f96aba36863b7c 100644 --- a/test/parallel/test-bootstrap-modules.js +++ b/test/parallel/test-bootstrap-modules.js @@ -9,6 +9,7 @@ const common = require('../common'); const assert = require('assert'); const expectedModules = new Set([ + 'Internal Binding errors', 'Internal Binding async_wrap', 'Internal Binding buffer', 'Internal Binding config', From 7438a557af191595b23f103cae73ee79c23bdb9e Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Tue, 21 May 2019 21:46:19 +0200 Subject: [PATCH 3/5] src: remove util-inl.h include in node.h `node.h` may only include public APIs, which `util-inl.h` is not. There does not seem to be any reason for including it, so remove it, because otherwise native addon compilation is broken due to us not shipping the `util-inl.h` header. Refs: https://github.com/nodejs/node/pull/27631 Fixes: https://github.com/nodejs/node/issues/27803 PR-URL: https://github.com/nodejs/node/pull/27804 Reviewed-By: Colin Ihrig Reviewed-By: Ruben Bridgewater Reviewed-By: Sam Roberts Reviewed-By: Jeremiah Senkpiel Reviewed-By: Richard Lau --- src/node.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/node.h b/src/node.h index 5af0655a84b808..5098dc9c7e77df 100644 --- a/src/node.h +++ b/src/node.h @@ -99,14 +99,6 @@ # endif #endif -#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS -// Internally, do not include util-inl.h into files unless they need it's -// inline definitions. -#else -// Externally, it must be included for backwards API compatibility. -# include -#endif - // Forward-declare libuv loop struct uv_loop_s; From c478884725caa881830ad735df55066bc1735511 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= Date: Tue, 21 May 2019 08:21:34 +0200 Subject: [PATCH 4/5] deps: V8: cherry-pick 94c87fe Original commit message: [ic] Fix handling of +0/-0 when constant field tracking is enabled ... and ensure that runtime behaviour is in sync with the IC code. Bug: chromium:950747, v8:9113 Change-Id: Ied66c9514cbe3a4d75fc71d4fc3b19ea1538f9b2 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1561319 Reviewed-by: Toon Verwaest Commit-Queue: Igor Sheludko Cr-Commit-Position: refs/heads/master@{#60768} PR-URL: https://github.com/nodejs/node/pull/27792 Fixes: https://github.com/nodejs/node/issues/27784 Refs: https://github.com/v8/v8/commit/94c87fe0746fc95618ae091351f2f8c342212917 Reviewed-By: Ruben Bridgewater Reviewed-By: Ben Noordhuis Reviewed-By: Anna Henningsen Reviewed-By: Gus Caplan --- common.gypi | 2 +- deps/v8/src/code-stub-assembler.cc | 97 +++++---- deps/v8/src/code-stub-assembler.h | 9 +- deps/v8/src/ic/accessor-assembler.cc | 46 +++-- deps/v8/src/lookup.cc | 8 +- deps/v8/src/objects-inl.h | 10 + deps/v8/src/objects.cc | 48 +++-- deps/v8/src/objects.h | 4 + .../test/cctest/test-field-type-tracking.cc | 186 ++++++++++++++++++ .../mjsunit/regress/regress-crbug-950747.js | 11 ++ 10 files changed, 328 insertions(+), 93 deletions(-) create mode 100644 deps/v8/test/mjsunit/regress/regress-crbug-950747.js diff --git a/common.gypi b/common.gypi index 83efbb8898956b..223043e55194c3 100644 --- a/common.gypi +++ b/common.gypi @@ -38,7 +38,7 @@ # Reset this number to 0 on major V8 upgrades. # Increment by one for each non-official patch applied to deps/v8. - 'v8_embedder_string': '-node.17', + 'v8_embedder_string': '-node.18', ##### V8 defaults for Node.js ##### diff --git a/deps/v8/src/code-stub-assembler.cc b/deps/v8/src/code-stub-assembler.cc index e4dba15750e282..8f4930e560017e 100644 --- a/deps/v8/src/code-stub-assembler.cc +++ b/deps/v8/src/code-stub-assembler.cc @@ -12367,7 +12367,7 @@ Node* CodeStubAssembler::StrictEqual(Node* lhs, Node* rhs, // This algorithm differs from the Strict Equality Comparison Algorithm in its // treatment of signed zeroes and NaNs. void CodeStubAssembler::BranchIfSameValue(Node* lhs, Node* rhs, Label* if_true, - Label* if_false) { + Label* if_false, SameValueMode mode) { VARIABLE(var_lhs_value, MachineRepresentation::kFloat64); VARIABLE(var_rhs_value, MachineRepresentation::kFloat64); Label do_fcmp(this); @@ -12413,10 +12413,12 @@ void CodeStubAssembler::BranchIfSameValue(Node* lhs, Node* rhs, Label* if_true, if_lhsisbigint(this); Node* const lhs_map = LoadMap(lhs); GotoIf(IsHeapNumberMap(lhs_map), &if_lhsisheapnumber); - Node* const lhs_instance_type = LoadMapInstanceType(lhs_map); - GotoIf(IsStringInstanceType(lhs_instance_type), &if_lhsisstring); - Branch(IsBigIntInstanceType(lhs_instance_type), &if_lhsisbigint, - if_false); + if (mode != SameValueMode::kNumbersOnly) { + Node* const lhs_instance_type = LoadMapInstanceType(lhs_map); + GotoIf(IsStringInstanceType(lhs_instance_type), &if_lhsisstring); + GotoIf(IsBigIntInstanceType(lhs_instance_type), &if_lhsisbigint); + } + Goto(if_false); BIND(&if_lhsisheapnumber); { @@ -12426,53 +12428,62 @@ void CodeStubAssembler::BranchIfSameValue(Node* lhs, Node* rhs, Label* if_true, Goto(&do_fcmp); } - BIND(&if_lhsisstring); - { - // Now we can only yield true if {rhs} is also a String - // with the same sequence of characters. - GotoIfNot(IsString(rhs), if_false); - Node* const result = CallBuiltin(Builtins::kStringEqual, - NoContextConstant(), lhs, rhs); - Branch(IsTrue(result), if_true, if_false); - } - - BIND(&if_lhsisbigint); - { - GotoIfNot(IsBigInt(rhs), if_false); - Node* const result = CallRuntime(Runtime::kBigIntEqualToBigInt, - NoContextConstant(), lhs, rhs); - Branch(IsTrue(result), if_true, if_false); + if (mode != SameValueMode::kNumbersOnly) { + BIND(&if_lhsisstring); + { + // Now we can only yield true if {rhs} is also a String + // with the same sequence of characters. + GotoIfNot(IsString(rhs), if_false); + Node* const result = CallBuiltin( + Builtins::kStringEqual, NoContextConstant(), lhs, rhs); + Branch(IsTrue(result), if_true, if_false); + } + + BIND(&if_lhsisbigint); + { + GotoIfNot(IsBigInt(rhs), if_false); + Node* const result = + CallRuntime(Runtime::kBigIntEqualToBigInt, + NoContextConstant(), lhs, rhs); + Branch(IsTrue(result), if_true, if_false); + } } }); } BIND(&do_fcmp); { - Node* const lhs_value = var_lhs_value.value(); - Node* const rhs_value = var_rhs_value.value(); + TNode lhs_value = UncheckedCast(var_lhs_value.value()); + TNode rhs_value = UncheckedCast(var_rhs_value.value()); + BranchIfSameNumberValue(lhs_value, rhs_value, if_true, if_false); + } +} - Label if_equal(this), if_notequal(this); - Branch(Float64Equal(lhs_value, rhs_value), &if_equal, &if_notequal); +void CodeStubAssembler::BranchIfSameNumberValue(TNode lhs_value, + TNode rhs_value, + Label* if_true, + Label* if_false) { + Label if_equal(this), if_notequal(this); + Branch(Float64Equal(lhs_value, rhs_value), &if_equal, &if_notequal); - BIND(&if_equal); - { - // We still need to handle the case when {lhs} and {rhs} are -0.0 and - // 0.0 (or vice versa). Compare the high word to - // distinguish between the two. - Node* const lhs_hi_word = Float64ExtractHighWord32(lhs_value); - Node* const rhs_hi_word = Float64ExtractHighWord32(rhs_value); - - // If x is +0 and y is -0, return false. - // If x is -0 and y is +0, return false. - Branch(Word32Equal(lhs_hi_word, rhs_hi_word), if_true, if_false); - } + BIND(&if_equal); + { + // We still need to handle the case when {lhs} and {rhs} are -0.0 and + // 0.0 (or vice versa). Compare the high word to + // distinguish between the two. + Node* const lhs_hi_word = Float64ExtractHighWord32(lhs_value); + Node* const rhs_hi_word = Float64ExtractHighWord32(rhs_value); - BIND(&if_notequal); - { - // Return true iff both {rhs} and {lhs} are NaN. - GotoIf(Float64Equal(lhs_value, lhs_value), if_false); - Branch(Float64Equal(rhs_value, rhs_value), if_false, if_true); - } + // If x is +0 and y is -0, return false. + // If x is -0 and y is +0, return false. + Branch(Word32Equal(lhs_hi_word, rhs_hi_word), if_true, if_false); + } + + BIND(&if_notequal); + { + // Return true iff both {rhs} and {lhs} are NaN. + GotoIf(Float64Equal(lhs_value, lhs_value), if_false); + Branch(Float64Equal(rhs_value, rhs_value), if_false, if_true); } } diff --git a/deps/v8/src/code-stub-assembler.h b/deps/v8/src/code-stub-assembler.h index 86cc275c1450f4..c90b5d0e7e49f6 100644 --- a/deps/v8/src/code-stub-assembler.h +++ b/deps/v8/src/code-stub-assembler.h @@ -3095,7 +3095,14 @@ class V8_EXPORT_PRIVATE CodeStubAssembler // ECMA#sec-samevalue // Similar to StrictEqual except that NaNs are treated as equal and minus zero // differs from positive zero. - void BranchIfSameValue(Node* lhs, Node* rhs, Label* if_true, Label* if_false); + enum class SameValueMode { kNumbersOnly, kFull }; + void BranchIfSameValue(Node* lhs, Node* rhs, Label* if_true, Label* if_false, + SameValueMode mode = SameValueMode::kFull); + // A part of BranchIfSameValue() that handles two double values. + // Treats NaN == NaN and +0 != -0. + void BranchIfSameNumberValue(TNode lhs_value, + TNode rhs_value, Label* if_true, + Label* if_false); enum HasPropertyLookupMode { kHasProperty, kForInHasProperty }; diff --git a/deps/v8/src/ic/accessor-assembler.cc b/deps/v8/src/ic/accessor-assembler.cc index bc636e41643bcb..fba761725be12e 100644 --- a/deps/v8/src/ic/accessor-assembler.cc +++ b/deps/v8/src/ic/accessor-assembler.cc @@ -1202,23 +1202,23 @@ void AccessorAssembler::OverwriteExistingFastDataProperty( BIND(&inobject); { - Node* field_offset = TimesTaggedSize(field_index); + TNode field_offset = Signed(TimesTaggedSize(field_index)); Label tagged_rep(this), double_rep(this); Branch( Word32Equal(representation, Int32Constant(Representation::kDouble)), &double_rep, &tagged_rep); BIND(&double_rep); { - Node* double_value = ChangeNumberToFloat64(value); + TNode double_value = ChangeNumberToFloat64(value); if (FLAG_unbox_double_fields) { if (do_transitioning_store) { StoreMap(object, object_map); } else if (FLAG_track_constant_fields) { Label if_mutable(this); GotoIfNot(IsPropertyDetailsConst(details), &if_mutable); - Node* current_value = - LoadObjectField(object, field_offset, MachineType::Float64()); - Branch(Float64Equal(current_value, double_value), &done, slow); + TNode current_value = + LoadObjectField(CAST(object), field_offset); + BranchIfSameNumberValue(current_value, double_value, &done, slow); BIND(&if_mutable); } StoreObjectFieldNoWriteBarrier(object, field_offset, double_value, @@ -1234,8 +1234,9 @@ void AccessorAssembler::OverwriteExistingFastDataProperty( if (FLAG_track_constant_fields) { Label if_mutable(this); GotoIfNot(IsPropertyDetailsConst(details), &if_mutable); - Node* current_value = LoadHeapNumberValue(mutable_heap_number); - Branch(Float64Equal(current_value, double_value), &done, slow); + TNode current_value = + LoadHeapNumberValue(mutable_heap_number); + BranchIfSameNumberValue(current_value, double_value, &done, slow); BIND(&if_mutable); } StoreHeapNumberValue(mutable_heap_number, double_value); @@ -1251,9 +1252,10 @@ void AccessorAssembler::OverwriteExistingFastDataProperty( } else if (FLAG_track_constant_fields) { Label if_mutable(this); GotoIfNot(IsPropertyDetailsConst(details), &if_mutable); - Node* current_value = - LoadObjectField(object, field_offset, MachineType::AnyTagged()); - Branch(WordEqual(current_value, value), &done, slow); + TNode current_value = + LoadObjectField(CAST(object), field_offset); + BranchIfSameValue(current_value, value, &done, slow, + SameValueMode::kNumbersOnly); BIND(&if_mutable); } StoreObjectField(object, field_offset, value); @@ -1303,12 +1305,13 @@ void AccessorAssembler::OverwriteExistingFastDataProperty( { Node* mutable_heap_number = LoadPropertyArrayElement(properties, backing_store_index); - Node* double_value = ChangeNumberToFloat64(value); + TNode double_value = ChangeNumberToFloat64(value); if (FLAG_track_constant_fields) { Label if_mutable(this); GotoIfNot(IsPropertyDetailsConst(details), &if_mutable); - Node* current_value = LoadHeapNumberValue(mutable_heap_number); - Branch(Float64Equal(current_value, double_value), &done, slow); + TNode current_value = + LoadHeapNumberValue(mutable_heap_number); + BranchIfSameNumberValue(current_value, double_value, &done, slow); BIND(&if_mutable); } StoreHeapNumberValue(mutable_heap_number, double_value); @@ -1319,9 +1322,10 @@ void AccessorAssembler::OverwriteExistingFastDataProperty( if (FLAG_track_constant_fields) { Label if_mutable(this); GotoIfNot(IsPropertyDetailsConst(details), &if_mutable); - Node* current_value = + TNode current_value = LoadPropertyArrayElement(properties, backing_store_index); - Branch(WordEqual(current_value, value), &done, slow); + BranchIfSameValue(current_value, value, &done, slow, + SameValueMode::kNumbersOnly); BIND(&if_mutable); } StorePropertyArrayElement(properties, backing_store_index, value); @@ -1813,7 +1817,7 @@ void AccessorAssembler::StoreNamedField(Node* handler_word, Node* object, } Node* index = DecodeWord(handler_word); - Node* offset = IntPtrMul(index, IntPtrConstant(kTaggedSize)); + TNode offset = Signed(TimesTaggedSize(index)); if (representation.IsDouble()) { if (!FLAG_unbox_double_fields || !is_inobject) { // Load the mutable heap number. @@ -1831,14 +1835,14 @@ void AccessorAssembler::StoreNamedField(Node* handler_word, Node* object, &done); { if (store_value_as_double) { - Node* current_value = - LoadObjectField(property_storage, offset, MachineType::Float64()); - GotoIfNot(Float64Equal(current_value, value), bailout); + TNode current_value = + LoadObjectField(CAST(property_storage), offset); + BranchIfSameNumberValue(current_value, UncheckedCast(value), + &done, bailout); } else { Node* current_value = LoadObjectField(property_storage, offset); - GotoIfNot(WordEqual(current_value, value), bailout); + Branch(WordEqual(current_value, value), &done, bailout); } - Goto(&done); } BIND(&done); } diff --git a/deps/v8/src/lookup.cc b/deps/v8/src/lookup.cc index cc5d13dd6b1c8e..0e8a202a07890e 100644 --- a/deps/v8/src/lookup.cc +++ b/deps/v8/src/lookup.cc @@ -934,10 +934,14 @@ bool LookupIterator::IsConstFieldValueEqualTo(Object value) const { // Uninitialized double field. return true; } - return bit_cast(bits) == value->Number(); + return Object::SameNumberValue(bit_cast(bits), value->Number()); } else { Object current_value = holder->RawFastPropertyAt(field_index); - return current_value->IsUninitialized(isolate()) || current_value == value; + if (current_value->IsUninitialized(isolate()) || current_value == value) { + return true; + } + return current_value->IsNumber() && value->IsNumber() && + Object::SameNumberValue(current_value->Number(), value->Number()); } } diff --git a/deps/v8/src/objects-inl.h b/deps/v8/src/objects-inl.h index 6f146241b13ee2..33b2cb9bca3033 100644 --- a/deps/v8/src/objects-inl.h +++ b/deps/v8/src/objects-inl.h @@ -383,6 +383,16 @@ double Object::Number() const { : HeapNumber::unchecked_cast(*this)->value(); } +// static +bool Object::SameNumberValue(double value1, double value2) { + // SameNumberValue(NaN, NaN) is true. + if (value1 != value2) { + return std::isnan(value1) && std::isnan(value2); + } + // SameNumberValue(0.0, -0.0) is false. + return (std::signbit(value1) == std::signbit(value2)); +} + bool Object::IsNaN() const { return this->IsHeapNumber() && std::isnan(HeapNumber::cast(*this)->value()); } diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc index 8337b3d4cf47ba..98b838e478d0a2 100644 --- a/deps/v8/src/objects.cc +++ b/deps/v8/src/objects.cc @@ -594,7 +594,7 @@ namespace { // TODO(bmeurer): Maybe we should introduce a marker interface Number, // where we put all these methods at some point? -ComparisonResult NumberCompare(double x, double y) { +ComparisonResult StrictNumberCompare(double x, double y) { if (std::isnan(x) || std::isnan(y)) { return ComparisonResult::kUndefined; } else if (x < y) { @@ -606,19 +606,20 @@ ComparisonResult NumberCompare(double x, double y) { } } -bool NumberEquals(double x, double y) { +// See Number case of ES6#sec-strict-equality-comparison +// Returns false if x or y is NaN, treats -0.0 as equal to 0.0. +bool StrictNumberEquals(double x, double y) { // Must check explicitly for NaN's on Windows, but -0 works fine. - if (std::isnan(x)) return false; - if (std::isnan(y)) return false; + if (std::isnan(x) || std::isnan(y)) return false; return x == y; } -bool NumberEquals(const Object x, const Object y) { - return NumberEquals(x->Number(), y->Number()); +bool StrictNumberEquals(const Object x, const Object y) { + return StrictNumberEquals(x->Number(), y->Number()); } -bool NumberEquals(Handle x, Handle y) { - return NumberEquals(*x, *y); +bool StrictNumberEquals(Handle x, Handle y) { + return StrictNumberEquals(*x, *y); } ComparisonResult Reverse(ComparisonResult result) { @@ -663,7 +664,7 @@ Maybe Object::Compare(Isolate* isolate, Handle x, bool x_is_number = x->IsNumber(); bool y_is_number = y->IsNumber(); if (x_is_number && y_is_number) { - return Just(NumberCompare(x->Number(), y->Number())); + return Just(StrictNumberCompare(x->Number(), y->Number())); } else if (!x_is_number && !y_is_number) { return Just(BigInt::CompareToBigInt(Handle::cast(x), Handle::cast(y))); @@ -683,11 +684,12 @@ Maybe Object::Equals(Isolate* isolate, Handle x, while (true) { if (x->IsNumber()) { if (y->IsNumber()) { - return Just(NumberEquals(x, y)); + return Just(StrictNumberEquals(x, y)); } else if (y->IsBoolean()) { - return Just(NumberEquals(*x, Handle::cast(y)->to_number())); + return Just( + StrictNumberEquals(*x, Handle::cast(y)->to_number())); } else if (y->IsString()) { - return Just(NumberEquals( + return Just(StrictNumberEquals( x, String::ToNumber(isolate, Handle::cast(y)))); } else if (y->IsBigInt()) { return Just(BigInt::EqualToNumber(Handle::cast(y), x)); @@ -705,10 +707,11 @@ Maybe Object::Equals(Isolate* isolate, Handle x, Handle::cast(y))); } else if (y->IsNumber()) { x = String::ToNumber(isolate, Handle::cast(x)); - return Just(NumberEquals(x, y)); + return Just(StrictNumberEquals(x, y)); } else if (y->IsBoolean()) { x = String::ToNumber(isolate, Handle::cast(x)); - return Just(NumberEquals(*x, Handle::cast(y)->to_number())); + return Just( + StrictNumberEquals(*x, Handle::cast(y)->to_number())); } else if (y->IsBigInt()) { return Just(BigInt::EqualToString(isolate, Handle::cast(y), Handle::cast(x))); @@ -724,10 +727,12 @@ Maybe Object::Equals(Isolate* isolate, Handle x, if (y->IsOddball()) { return Just(x.is_identical_to(y)); } else if (y->IsNumber()) { - return Just(NumberEquals(Handle::cast(x)->to_number(), *y)); + return Just( + StrictNumberEquals(Handle::cast(x)->to_number(), *y)); } else if (y->IsString()) { y = String::ToNumber(isolate, Handle::cast(y)); - return Just(NumberEquals(Handle::cast(x)->to_number(), *y)); + return Just( + StrictNumberEquals(Handle::cast(x)->to_number(), *y)); } else if (y->IsBigInt()) { x = Oddball::ToNumber(isolate, Handle::cast(x)); return Just(BigInt::EqualToNumber(Handle::cast(y), x)); @@ -776,7 +781,7 @@ Maybe Object::Equals(Isolate* isolate, Handle x, bool Object::StrictEquals(Object that) { if (this->IsNumber()) { if (!that->IsNumber()) return false; - return NumberEquals(*this, that); + return StrictNumberEquals(*this, that); } else if (this->IsString()) { if (!that->IsString()) return false; return String::cast(*this)->Equals(String::cast(that)); @@ -1624,14 +1629,7 @@ bool Object::SameValue(Object other) { if (other == *this) return true; if (IsNumber() && other->IsNumber()) { - double this_value = Number(); - double other_value = other->Number(); - // SameValue(NaN, NaN) is true. - if (this_value != other_value) { - return std::isnan(this_value) && std::isnan(other_value); - } - // SameValue(0.0, -0.0) is false. - return (std::signbit(this_value) == std::signbit(other_value)); + return SameNumberValue(Number(), other->Number()); } if (IsString() && other->IsString()) { return String::cast(*this)->Equals(String::cast(other)); diff --git a/deps/v8/src/objects.h b/deps/v8/src/objects.h index d7cb4f39d0e1e2..6c0f313261d1e4 100644 --- a/deps/v8/src/objects.h +++ b/deps/v8/src/objects.h @@ -854,6 +854,10 @@ class Object { // to implement the Object.is function. V8_EXPORT_PRIVATE bool SameValue(Object other); + // A part of SameValue which handles Number vs. Number case. + // Treats NaN == NaN and +0 != -0. + inline static bool SameNumberValue(double number1, double number2); + // Checks whether this object has the same value as the given one. // +0 and -0 are treated equal. Everything else is the same as SameValue. // This function is implemented according to ES6, section 7.2.4 and is used diff --git a/deps/v8/test/cctest/test-field-type-tracking.cc b/deps/v8/test/cctest/test-field-type-tracking.cc index 49b0f920115400..0253a16735c312 100644 --- a/deps/v8/test/cctest/test-field-type-tracking.cc +++ b/deps/v8/test/cctest/test-field-type-tracking.cc @@ -2883,6 +2883,192 @@ TEST(HoleyMutableHeapNumber) { CHECK_EQ(kHoleNanInt64, MutableHeapNumber::cast(*obj)->value_as_bits()); } +namespace { + +template +MaybeHandle Call(Isolate* isolate, Handle function, + Args... args) { + Handle argv[] = {args...}; + return Execution::Call(isolate, function, + isolate->factory()->undefined_value(), sizeof...(args), + argv); +} + +void TestStoreToConstantField(const char* store_func_source, + Handle value1, Handle value2, + Representation expected_rep, + PropertyConstness expected_constness, + int store_repetitions) { + Isolate* isolate = CcTest::i_isolate(); + CompileRun(store_func_source); + + Handle store_func = GetGlobal("store"); + + const PropertyConstness kExpectedInitialFieldConstness = + FLAG_track_constant_fields ? PropertyConstness::kConst + : PropertyConstness::kMutable; + + Handle initial_map = Map::Create(isolate, 4); + + // Store value1 to obj1 and check that it got property with expected + // representation and constness. + Handle obj1 = isolate->factory()->NewJSObjectFromMap(initial_map); + for (int i = 0; i < store_repetitions; i++) { + Call(isolate, store_func, obj1, value1).Check(); + } + + Handle map(obj1->map(), isolate); + CHECK(!map->is_dictionary_map()); + CHECK(!map->is_deprecated()); + CHECK_EQ(1, map->NumberOfOwnDescriptors()); + + CHECK(map->instance_descriptors()->GetDetails(0).representation().Equals( + expected_rep)); + CHECK_EQ(kExpectedInitialFieldConstness, + map->instance_descriptors()->GetDetails(0).constness()); + + // Store value2 to obj2 and check that it got same map and property details + // did not change. + Handle obj2 = isolate->factory()->NewJSObjectFromMap(initial_map); + Call(isolate, store_func, obj2, value2).Check(); + + CHECK_EQ(*map, obj2->map()); + CHECK(!map->is_dictionary_map()); + CHECK(!map->is_deprecated()); + CHECK_EQ(1, map->NumberOfOwnDescriptors()); + + CHECK(map->instance_descriptors()->GetDetails(0).representation().Equals( + expected_rep)); + CHECK_EQ(kExpectedInitialFieldConstness, + map->instance_descriptors()->GetDetails(0).constness()); + + // Store value2 to obj1 and check that property became mutable. + Call(isolate, store_func, obj1, value2).Check(); + + CHECK_EQ(*map, obj1->map()); + CHECK(!map->is_dictionary_map()); + CHECK(!map->is_deprecated()); + CHECK_EQ(1, map->NumberOfOwnDescriptors()); + + CHECK(map->instance_descriptors()->GetDetails(0).representation().Equals( + expected_rep)); + CHECK_EQ(expected_constness, + map->instance_descriptors()->GetDetails(0).constness()); +} + +void TestStoreToConstantField_PlusMinusZero(const char* store_func_source, + int store_repetitions) { + Isolate* isolate = CcTest::i_isolate(); + CompileRun(store_func_source); + + Handle minus_zero = isolate->factory()->NewNumber(-0.0); + Handle plus_zero = isolate->factory()->NewNumber(0.0); + + // +0 and -0 are treated as not equal upon stores. + const PropertyConstness kExpectedFieldConstness = PropertyConstness::kMutable; + + TestStoreToConstantField(store_func_source, minus_zero, plus_zero, + Representation::Double(), kExpectedFieldConstness, + store_repetitions); +} + +void TestStoreToConstantField_NaN(const char* store_func_source, + int store_repetitions) { + Isolate* isolate = CcTest::i_isolate(); + CompileRun(store_func_source); + + uint64_t nan_bits = uint64_t{0x7FF8000000000001}; + double nan_double1 = bit_cast(nan_bits); + double nan_double2 = bit_cast(nan_bits | 0x12300); + CHECK(std::isnan(nan_double1)); + CHECK(std::isnan(nan_double2)); + CHECK_NE(nan_double1, nan_double2); + CHECK_NE(bit_cast(nan_double1), bit_cast(nan_double2)); + + Handle nan1 = isolate->factory()->NewNumber(nan_double1); + Handle nan2 = isolate->factory()->NewNumber(nan_double2); + + // NaNs with different bit patters are treated as equal upon stores. + const PropertyConstness kExpectedFieldConstness = + FLAG_track_constant_fields ? PropertyConstness::kConst + : PropertyConstness::kMutable; + + TestStoreToConstantField(store_func_source, nan1, nan2, + Representation::Double(), kExpectedFieldConstness, + store_repetitions); +} + +} // namespace + +TEST(StoreToConstantField_PlusMinusZero) { + FLAG_allow_natives_syntax = true; + CcTest::InitializeVM(); + v8::HandleScope scope(CcTest::isolate()); + + const char* store_func_source = + "function store(o, v) {" + " %SetNamedProperty(o, 'v', v);" + "}"; + + TestStoreToConstantField_PlusMinusZero(store_func_source, 1); + TestStoreToConstantField_PlusMinusZero(store_func_source, 3); + + TestStoreToConstantField_NaN(store_func_source, 1); + TestStoreToConstantField_NaN(store_func_source, 2); +} + +TEST(StoreToConstantField_ObjectDefineProperty) { + CcTest::InitializeVM(); + v8::HandleScope scope(CcTest::isolate()); + + const char* store_func_source = + "function store(o, v) {" + " Object.defineProperty(o, 'v', " + " {value: v, " + " writable: true, " + " configurable: true, " + " enumerable: true});" + "}"; + + TestStoreToConstantField_PlusMinusZero(store_func_source, 1); + TestStoreToConstantField_PlusMinusZero(store_func_source, 3); + + TestStoreToConstantField_NaN(store_func_source, 1); + TestStoreToConstantField_NaN(store_func_source, 2); +} + +TEST(StoreToConstantField_ReflectSet) { + CcTest::InitializeVM(); + v8::HandleScope scope(CcTest::isolate()); + + const char* store_func_source = + "function store(o, v) {" + " Reflect.set(o, 'v', v);" + "}"; + + TestStoreToConstantField_PlusMinusZero(store_func_source, 1); + TestStoreToConstantField_PlusMinusZero(store_func_source, 3); + + TestStoreToConstantField_NaN(store_func_source, 1); + TestStoreToConstantField_NaN(store_func_source, 2); +} + +TEST(StoreToConstantField_StoreIC) { + CcTest::InitializeVM(); + v8::HandleScope scope(CcTest::isolate()); + + const char* store_func_source = + "function store(o, v) {" + " o.v = v;" + "}"; + + TestStoreToConstantField_PlusMinusZero(store_func_source, 1); + TestStoreToConstantField_PlusMinusZero(store_func_source, 3); + + TestStoreToConstantField_NaN(store_func_source, 1); + TestStoreToConstantField_NaN(store_func_source, 2); +} + } // namespace test_field_type_tracking } // namespace compiler } // namespace internal diff --git a/deps/v8/test/mjsunit/regress/regress-crbug-950747.js b/deps/v8/test/mjsunit/regress/regress-crbug-950747.js new file mode 100644 index 00000000000000..21a91bb5d8797f --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-crbug-950747.js @@ -0,0 +1,11 @@ +// Copyright 2019 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +let o = {}; +Reflect.set(o, "a", 0.1); + +let o1 = {}; +o1.a = {}; + +Reflect.set(o, "a", 0.1); From 2817fde844de39b0e8c17c352433ee6124b5ef09 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Wed, 22 May 2019 10:48:30 +0200 Subject: [PATCH 5/5] 2019-05-22, Version v12.3.1 (Current) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Notable changes * deps: * Fix handling of +0/-0 when constant field tracking is enabled (Michaël Zasso) https://github.com/nodejs/node/pull/27792 * Fix `os.freemem()` and `os.totalmem` correctness (cjihrig) https://github.com/nodejs/node/pull/27718 * src: * Fix v12.3.0 regression that prevents native addons from compiling https://github.com/nodejs/node/pull/27804 PR-URL: https://github.com/nodejs/node/pull/27814 --- CHANGELOG.md | 3 ++- doc/changelogs/CHANGELOG_V12.md | 19 +++++++++++++++++++ src/node_version.h | 2 +- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fbdb98d6aa23d..b7bf473720886a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,7 +29,8 @@ release. -12.3.0
+12.3.1
+12.3.0
12.2.0
12.1.0
12.0.0
diff --git a/doc/changelogs/CHANGELOG_V12.md b/doc/changelogs/CHANGELOG_V12.md index 71b67ca2e8c869..9eb0f5e401fcaa 100644 --- a/doc/changelogs/CHANGELOG_V12.md +++ b/doc/changelogs/CHANGELOG_V12.md @@ -9,6 +9,7 @@ +12.3.1
12.3.0
12.2.0
12.1.0
@@ -31,6 +32,24 @@ * [io.js](CHANGELOG_IOJS.md) * [Archive](CHANGELOG_ARCHIVE.md) + +## 2019-05-22, Version 12.3.1 (Current), @BridgeAR + +### Notable changes + +* **deps**: + * Fix handling of +0/-0 when constant field tracking is enabled (Michaël Zasso) [#27792](https://github.com/nodejs/node/pull/27792) + * Fix `os.freemem()` and `os.totalmem` correctness (cjihrig) [#27718](https://github.com/nodejs/node/pull/27718) +* **src**: + * Fix v12.3.0 regression that prevents native addons from compiling [#27804](https://github.com/nodejs/node/pull/27804) + +### Commits + +* [[`c478884725`](https://github.com/nodejs/node/commit/c478884725)] - **deps**: V8: cherry-pick 94c87fe (Michaël Zasso) [#27792](https://github.com/nodejs/node/pull/27792) +* [[`aed74ccb4c`](https://github.com/nodejs/node/commit/aed74ccb4c)] - **deps**: upgrade to libuv 1.29.1 (cjihrig) [#27718](https://github.com/nodejs/node/pull/27718) +* [[`7438a557af`](https://github.com/nodejs/node/commit/7438a557af)] - **src**: remove util-inl.h include in node.h (Anna Henningsen) [#27804](https://github.com/nodejs/node/pull/27804) +* [[`6f7005465a`](https://github.com/nodejs/node/commit/6f7005465a)] - **src, lib**: take control of prepareStackTrace (Gus Caplan) [#23926](https://github.com/nodejs/node/pull/23926) + ## 2019-05-21, Version 12.3.0 (Current), @BridgeAR diff --git a/src/node_version.h b/src/node_version.h index 7e3435e8bb0606..a29fa9a53435bd 100644 --- a/src/node_version.h +++ b/src/node_version.h @@ -29,7 +29,7 @@ #define NODE_VERSION_IS_LTS 0 #define NODE_VERSION_LTS_CODENAME "" -#define NODE_VERSION_IS_RELEASE 0 +#define NODE_VERSION_IS_RELEASE 1 #ifndef NODE_STRINGIFY #define NODE_STRINGIFY(n) NODE_STRINGIFY_HELPER(n)