diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4cd6fceaddbfcd..8bec924fac1fc6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -31,7 +31,8 @@ release.
-14.1.0
+14.2.0
+14.1.0
14.0.0
|
diff --git a/common.gypi b/common.gypi
index fec140bbae983b..5e4f9b6dba1dce 100644
--- a/common.gypi
+++ b/common.gypi
@@ -26,6 +26,7 @@
'uv_library%': 'static_library',
'clang%': 0,
+ 'error_on_warn%': 'false',
'openssl_fips%': '',
'openssl_no_asm%': 0,
@@ -35,7 +36,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.32',
+ 'v8_embedder_string': '-node.33',
##### V8 defaults for Node.js #####
@@ -218,7 +219,14 @@
# Forcibly disable -Werror. We support a wide range of compilers, it's
# simply not feasible to squelch all warnings, never mind that the
# libraries in deps/ are not under our control.
- 'cflags!': ['-Werror'],
+ 'conditions': [
+ [ 'error_on_warn=="false"', {
+ 'cflags!': ['-Werror'],
+ }, '(_target_name!="<(node_lib_target_name)" or '
+ '_target_name!="<(node_core_target_name)")', {
+ 'cflags!': ['-Werror'],
+ }],
+ ],
'msvs_settings': {
'VCCLCompilerTool': {
'BufferSecurityCheck': 'true',
diff --git a/configure.py b/configure.py
index 2e7deb5a15ef11..ac26f62916cd06 100755
--- a/configure.py
+++ b/configure.py
@@ -117,6 +117,11 @@
choices=valid_os,
help='operating system to build for ({0})'.format(', '.join(valid_os)))
+parser.add_option('--error-on-warn',
+ action='store_true',
+ dest='error_on_warn',
+ help='Turn compiler warnings into errors for node core sources.')
+
parser.add_option('--gdb',
action='store_true',
dest='gdb',
@@ -1018,6 +1023,7 @@ def configure_node(o):
o['variables']['node_install_npm'] = b(not options.without_npm)
o['variables']['debug_node'] = b(options.debug_node)
o['default_configuration'] = 'Debug' if options.debug else 'Release'
+ o['variables']['error_on_warn'] = b(options.error_on_warn)
host_arch = host_arch_win() if os.name == 'nt' else host_arch_cc()
target_arch = options.dest_cpu or host_arch
diff --git a/deps/uvwasi/include/fd_table.h b/deps/uvwasi/include/fd_table.h
index f29b1adf88d6f0..474a0231e03baf 100644
--- a/deps/uvwasi/include/fd_table.h
+++ b/deps/uvwasi/include/fd_table.h
@@ -13,6 +13,7 @@ struct uvwasi_fd_wrap_t {
uv_file fd;
char* path;
char* real_path;
+ char* normalized_path;
uvwasi_filetype_t type;
uvwasi_rights_t rights_base;
uvwasi_rights_t rights_inheriting;
diff --git a/deps/uvwasi/include/uvwasi.h b/deps/uvwasi/include/uvwasi.h
index 39ee2f0ceb6609..f2d2d1a9fd8945 100644
--- a/deps/uvwasi/include/uvwasi.h
+++ b/deps/uvwasi/include/uvwasi.h
@@ -11,7 +11,7 @@ extern "C" {
#define UVWASI_VERSION_MAJOR 0
#define UVWASI_VERSION_MINOR 0
-#define UVWASI_VERSION_PATCH 6
+#define UVWASI_VERSION_PATCH 8
#define UVWASI_VERSION_HEX ((UVWASI_VERSION_MAJOR << 16) | \
(UVWASI_VERSION_MINOR << 8) | \
(UVWASI_VERSION_PATCH))
@@ -66,7 +66,7 @@ typedef struct uvwasi_options_s {
const uvwasi_mem_t* allocator;
} uvwasi_options_t;
-// Embedder API.
+/* Embedder API. */
uvwasi_errno_t uvwasi_init(uvwasi_t* uvwasi, uvwasi_options_t* options);
void uvwasi_destroy(uvwasi_t* uvwasi);
uvwasi_errno_t uvwasi_embedder_remap_fd(uvwasi_t* uvwasi,
@@ -75,7 +75,7 @@ uvwasi_errno_t uvwasi_embedder_remap_fd(uvwasi_t* uvwasi,
const char* uvwasi_embedder_err_code_to_string(uvwasi_errno_t code);
-// WASI system call API.
+/* WASI system call API. */
uvwasi_errno_t uvwasi_args_get(uvwasi_t* uvwasi, char** argv, char* argv_buf);
uvwasi_errno_t uvwasi_args_sizes_get(uvwasi_t* uvwasi,
size_t* argc,
diff --git a/deps/uvwasi/include/wasi_types.h b/deps/uvwasi/include/wasi_types.h
index ec1013663f6a76..2f93b412624c06 100644
--- a/deps/uvwasi/include/wasi_types.h
+++ b/deps/uvwasi/include/wasi_types.h
@@ -4,7 +4,7 @@
#include
#include
-/* API: https://github.com/WebAssembly/WASI/blob/master/phases/unstable/docs/wasi_unstable_preview0.md */
+/* API: https://github.com/WebAssembly/WASI/blob/master/phases/snapshot/docs.md */
typedef uint8_t uvwasi_advice_t;
#define UVWASI_ADVICE_NORMAL 0
diff --git a/deps/uvwasi/src/clocks.c b/deps/uvwasi/src/clocks.c
index fd42b9e50e4d8e..b59cbd6bb192c0 100644
--- a/deps/uvwasi/src/clocks.c
+++ b/deps/uvwasi/src/clocks.c
@@ -153,7 +153,7 @@ uvwasi_errno_t uvwasi__clock_gettime_thread_cputime(uvwasi_timestamp_t* time) {
UVWASI__WIN_TIME_AND_RETURN(GetCurrentThread(), *time);
#elif defined(__APPLE__)
UVWASI__OSX_THREADTIME_AND_RETURN(*time);
-#elif defined(CLOCK_THREAD_CPUTIME_ID) && !defined(__sun)
+#elif defined(CLOCK_THREAD_CPUTIME_ID) && !defined(__sun) && !defined(__PASE__)
UVWASI__CLOCK_GETTIME_AND_RETURN(CLOCK_THREAD_CPUTIME_ID, *time);
#else
# if defined(RUSAGE_LWP)
@@ -185,7 +185,7 @@ uvwasi_errno_t uvwasi__clock_getres_thread_cputime(uvwasi_timestamp_t* time) {
UVWASI__WIN_GETRES_AND_RETURN(*time);
#elif defined(__APPLE__)
UVWASI__SLOW_GETRES_AND_RETURN(*time);
-#elif defined(CLOCK_THREAD_CPUTIME_ID) && !defined(__sun)
+#elif defined(CLOCK_THREAD_CPUTIME_ID) && !defined(__sun) && !defined(__PASE__)
UVWASI__CLOCK_GETTIME_AND_RETURN(CLOCK_THREAD_CPUTIME_ID, *time);
#elif defined(RUSAGE_THREAD) || defined(RUSAGE_LWP)
UVWASI__SLOW_GETRES_AND_RETURN(*time);
diff --git a/deps/uvwasi/src/fd_table.c b/deps/uvwasi/src/fd_table.c
index f6e530d9591df2..3d134e3b7e5e2e 100644
--- a/deps/uvwasi/src/fd_table.c
+++ b/deps/uvwasi/src/fd_table.c
@@ -8,6 +8,7 @@
#include "uv.h"
#include "fd_table.h"
+#include "path_resolver.h"
#include "wasi_types.h"
#include "wasi_rights.h"
#include "uv_mapping.h"
@@ -75,20 +76,33 @@ uvwasi_errno_t uvwasi_fd_table_insert(uvwasi_t* uvwasi,
char* mp_copy;
size_t rp_len;
char* rp_copy;
+ char* np_copy;
mp_len = strlen(mapped_path);
rp_len = strlen(real_path);
+ /* Reserve room for the mapped path, real path, and normalized mapped path. */
entry = (struct uvwasi_fd_wrap_t*)
- uvwasi__malloc(uvwasi, sizeof(*entry) + mp_len + rp_len + 2);
- if (entry == NULL) return UVWASI_ENOMEM;
+ uvwasi__malloc(uvwasi, sizeof(*entry) + mp_len + mp_len + rp_len + 3);
+ if (entry == NULL)
+ return UVWASI_ENOMEM;
mp_copy = (char*)(entry + 1);
rp_copy = mp_copy + mp_len + 1;
+ np_copy = rp_copy + rp_len + 1;
memcpy(mp_copy, mapped_path, mp_len);
mp_copy[mp_len] = '\0';
memcpy(rp_copy, real_path, rp_len);
rp_copy[rp_len] = '\0';
+ /* Calculate the normalized version of the mapped path, as it will be used for
+ any path calculations on this fd. Use the length of the mapped path as an
+ upper bound for the normalized path length. */
+ err = uvwasi__normalize_path(mp_copy, mp_len, np_copy, mp_len);
+ if (err) {
+ uvwasi__free(uvwasi, entry);
+ goto exit;
+ }
+
uv_rwlock_wrlock(&table->rwlock);
/* Check that there is room for a new item. If there isn't, grow the table. */
@@ -138,6 +152,7 @@ uvwasi_errno_t uvwasi_fd_table_insert(uvwasi_t* uvwasi,
entry->fd = fd;
entry->path = mp_copy;
entry->real_path = rp_copy;
+ entry->normalized_path = np_copy;
entry->type = type;
entry->rights_base = rights_base;
entry->rights_inheriting = rights_inheriting;
diff --git a/deps/uvwasi/src/path_resolver.c b/deps/uvwasi/src/path_resolver.c
new file mode 100644
index 00000000000000..ee0e60f7e8f4ea
--- /dev/null
+++ b/deps/uvwasi/src/path_resolver.c
@@ -0,0 +1,492 @@
+#include
+
+#include "uv.h"
+#include "uvwasi.h"
+#include "uvwasi_alloc.h"
+#include "uv_mapping.h"
+#include "path_resolver.h"
+
+#define UVWASI__MAX_SYMLINK_FOLLOWS 32
+
+#ifndef _WIN32
+# define IS_SLASH(c) ((c) == '/')
+#else
+# define IS_SLASH(c) ((c) == '/' || (c) == '\\')
+#endif /* _WIN32 */
+
+
+static int uvwasi__is_absolute_path(const char* path, size_t path_len) {
+ /* It's expected that only Unix style paths will be generated by WASI. */
+ return path != NULL && path_len > 0 && path[0] == '/';
+}
+
+
+static char* uvwasi__strchr_slash(const char* s) {
+ /* strchr() that identifies /, as well as \ on Windows. */
+ do {
+ if (IS_SLASH(*s))
+ return (char*) s;
+ } while (*s++);
+
+ return NULL;
+}
+
+
+uvwasi_errno_t uvwasi__normalize_path(const char* path,
+ size_t path_len,
+ char* normalized_path,
+ size_t normalized_len) {
+ const char* cur;
+ char* ptr;
+ char* next;
+ char* last;
+ size_t cur_len;
+ int is_absolute;
+
+ if (path_len > normalized_len)
+ return UVWASI_ENOBUFS;
+
+ is_absolute = uvwasi__is_absolute_path(path, path_len);
+ normalized_path[0] = '\0';
+ ptr = normalized_path;
+ for (cur = path; cur != NULL; cur = next + 1) {
+ next = uvwasi__strchr_slash(cur);
+ cur_len = (next == NULL) ? strlen(cur) : (size_t) (next - cur);
+
+ if (cur_len == 0) {
+ if (ptr == normalized_path && next != NULL && is_absolute) {
+ *ptr = '/';
+ ptr++;
+ }
+
+ *ptr = '\0';
+ } else if (cur_len == 1 && cur[0] == '.') {
+ /* No-op. Just consume the '.' */
+ } else if (cur_len == 2 && cur[0] == '.' && cur[1] == '.') {
+ /* Identify the path segment that preceded the current one. */
+ last = ptr;
+ while (!IS_SLASH(*last) && last != normalized_path) {
+ last--;
+ }
+
+ /* If the result is currently empty, or the last prior path is also '..'
+ then output '..'. Otherwise, remove the last path segment. */
+ if (ptr == normalized_path ||
+ (last == ptr - 2 && last[0] == '.' && last[1] == '.') ||
+ (last == ptr - 3 && last[0] == '/' &&
+ last[1] == '.' && last[2] == '.')) {
+ if (ptr != normalized_path && *(ptr - 1) != '/') {
+ *ptr = '/';
+ ptr++;
+ }
+
+ *ptr = '.';
+ ptr++;
+ *ptr = '.';
+ ptr++;
+ } else {
+ /* Strip the last segment, but make sure not to strip the '/' if that
+ is the entire path. */
+ if (last == normalized_path && *last == '/')
+ ptr = last + 1;
+ else
+ ptr = last;
+ }
+
+ *ptr = '\0';
+ } else {
+ if (ptr != normalized_path && *(ptr - 1) != '/') {
+ *ptr = '/';
+ ptr++;
+ }
+
+ memcpy(ptr, cur, cur_len);
+ ptr += cur_len;
+ *ptr = '\0';
+ }
+
+ if (next == NULL)
+ break;
+ }
+
+ /* Normalized the path to the empty string. Return either '/' or '.'. */
+ if (ptr == normalized_path) {
+ if (1 == is_absolute)
+ *ptr = '/';
+ else
+ *ptr = '.';
+
+ ptr++;
+ *ptr = '\0';
+ }
+
+ return UVWASI_ESUCCESS;
+}
+
+
+static int uvwasi__is_path_sandboxed(const char* path,
+ size_t path_len,
+ const char* fd_path,
+ size_t fd_path_len) {
+ char* ptr;
+ int remaining_len;
+
+ if (1 == uvwasi__is_absolute_path(fd_path, fd_path_len))
+ return path == strstr(path, fd_path) ? 1 : 0;
+
+ /* Handle relative fds that normalized to '.' */
+ if (fd_path_len == 1 && fd_path[0] == '.') {
+ /* If the fd's path is '.', then any path does not begin with '..' is OK. */
+ if ((path_len == 2 && path[0] == '.' && path[1] == '.') ||
+ (path_len > 2 && path[0] == '.' && path[1] == '.' && path[2] == '/')) {
+ return 0;
+ }
+
+ return 1;
+ }
+
+ if (path != strstr(path, fd_path))
+ return 0;
+
+ /* Fail if the remaining path starts with '..', '../', '/..', or '/../'. */
+ ptr = (char*) path + fd_path_len;
+ remaining_len = path_len - fd_path_len;
+ if (remaining_len < 2)
+ return 1;
+
+ /* Strip a leading slash so the check is only for '..' and '../'. */
+ if (*ptr == '/') {
+ ptr++;
+ remaining_len--;
+ }
+
+ if ((remaining_len == 2 && ptr[0] == '.' && ptr[1] == '.') ||
+ (remaining_len > 2 && ptr[0] == '.' && ptr[1] == '.' && ptr[2] == '/')) {
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static uvwasi_errno_t uvwasi__normalize_absolute_path(
+ const uvwasi_t* uvwasi,
+ const struct uvwasi_fd_wrap_t* fd,
+ const char* path,
+ size_t path_len,
+ char** normalized_path,
+ size_t* normalized_len
+ ) {
+ /* This function resolves an absolute path to the provided file descriptor.
+ If the file descriptor's path is relative, then this operation will fail
+ with UVWASI_ENOTCAPABLE since it doesn't make sense to resolve an absolute
+ path to a relative prefix. If the file desciptor's path is also absolute,
+ then we just need to verify that the normalized path still starts with
+ the file descriptor's path. */
+ uvwasi_errno_t err;
+ char* abs_path;
+ int abs_size;
+
+ *normalized_path = NULL;
+ *normalized_len = 0;
+ abs_size = path_len + 1;
+ abs_path = uvwasi__malloc(uvwasi, abs_size);
+ if (abs_path == NULL) {
+ err = UVWASI_ENOMEM;
+ goto exit;
+ }
+
+ /* Normalize the input path first. */
+ err = uvwasi__normalize_path(path, path_len, abs_path, path_len);
+ if (err != UVWASI_ESUCCESS)
+ goto exit;
+
+ /* Once the input is normalized, ensure that it is still sandboxed. */
+ if (0 == uvwasi__is_path_sandboxed(abs_path,
+ path_len,
+ fd->normalized_path,
+ strlen(fd->normalized_path))) {
+ err = UVWASI_ENOTCAPABLE;
+ goto exit;
+ }
+
+ *normalized_path = abs_path;
+ *normalized_len = abs_size - 1;
+ return UVWASI_ESUCCESS;
+
+exit:
+ uvwasi__free(uvwasi, abs_path);
+ return err;
+}
+
+
+static uvwasi_errno_t uvwasi__normalize_relative_path(
+ const uvwasi_t* uvwasi,
+ const struct uvwasi_fd_wrap_t* fd,
+ const char* path,
+ size_t path_len,
+ char** normalized_path,
+ size_t* normalized_len
+ ) {
+ /* This function resolves a relative path to the provided file descriptor.
+ The relative path is concatenated to the file descriptor's path, and then
+ normalized. */
+ uvwasi_errno_t err;
+ char* combined;
+ char* normalized;
+ int combined_size;
+ int fd_path_len;
+ int norm_len;
+ int r;
+
+ *normalized_path = NULL;
+ *normalized_len = 0;
+
+ /* The max combined size is the path length + the file descriptor's path
+ length + 2 for a terminating NULL and a possible path separator. */
+ fd_path_len = strlen(fd->normalized_path);
+ combined_size = path_len + fd_path_len + 2;
+ combined = uvwasi__malloc(uvwasi, combined_size);
+ if (combined == NULL)
+ return UVWASI_ENOMEM;
+
+ normalized = uvwasi__malloc(uvwasi, combined_size);
+ if (normalized == NULL) {
+ err = UVWASI_ENOMEM;
+ goto exit;
+ }
+
+ r = snprintf(combined, combined_size, "%s/%s", fd->normalized_path, path);
+ if (r <= 0) {
+ err = uvwasi__translate_uv_error(uv_translate_sys_error(errno));
+ goto exit;
+ }
+
+ /* Normalize the input path. */
+ err = uvwasi__normalize_path(combined,
+ combined_size - 1,
+ normalized,
+ combined_size - 1);
+ if (err != UVWASI_ESUCCESS)
+ goto exit;
+
+ norm_len = strlen(normalized);
+
+ /* Once the path is normalized, ensure that it is still sandboxed. */
+ if (0 == uvwasi__is_path_sandboxed(normalized,
+ norm_len,
+ fd->normalized_path,
+ fd_path_len)) {
+ err = UVWASI_ENOTCAPABLE;
+ goto exit;
+ }
+
+ err = UVWASI_ESUCCESS;
+ *normalized_path = normalized;
+ *normalized_len = norm_len;
+
+exit:
+ if (err != UVWASI_ESUCCESS)
+ uvwasi__free(uvwasi, normalized);
+
+ uvwasi__free(uvwasi, combined);
+ return err;
+}
+
+
+static uvwasi_errno_t uvwasi__resolve_path_to_host(
+ const uvwasi_t* uvwasi,
+ const struct uvwasi_fd_wrap_t* fd,
+ const char* path,
+ size_t path_len,
+ char** resolved_path,
+ size_t* resolved_len
+ ) {
+ /* Return the normalized path, but resolved to the host's real path. */
+ char* res_path;
+ char* stripped_path;
+ int real_path_len;
+ int fake_path_len;
+ int stripped_len;
+#ifdef _WIN32
+ size_t i;
+#endif /* _WIN32 */
+
+ real_path_len = strlen(fd->real_path);
+ fake_path_len = strlen(fd->normalized_path);
+
+ /* If the fake path is '.' just ignore it. */
+ if (fake_path_len == 1 && fd->normalized_path[0] == '.') {
+ fake_path_len = 0;
+ }
+
+ stripped_len = path_len - fake_path_len;
+
+ /* The resolved path's length is calculated as: the length of the fd's real
+ path, + 1 for a path separator, and the length of the input path (with the
+ fake path stripped off). */
+ *resolved_len = stripped_len + real_path_len + 1;
+ *resolved_path = uvwasi__malloc(uvwasi, *resolved_len + 1);
+
+ if (*resolved_path == NULL)
+ return UVWASI_ENOMEM;
+
+ res_path = *resolved_path;
+ stripped_path = (char*) path + fake_path_len;
+ memcpy(res_path, fd->real_path, real_path_len);
+ res_path += real_path_len;
+
+ if (stripped_len > 1 ||
+ (stripped_len == 1 && stripped_path[0] != '/')) {
+ if (stripped_path[0] != '/') {
+ *res_path = '/';
+ res_path++;
+ }
+
+ memcpy(res_path, stripped_path, stripped_len);
+ res_path += stripped_len;
+ }
+
+ *res_path = '\0';
+
+#ifdef _WIN32
+ /* Replace / with \ on Windows. */
+ for (i = real_path_len; i < *resolved_len; i++) {
+ if (res_path[i] == '/')
+ res_path[i] = '\\';
+ }
+#endif /* _WIN32 */
+
+ return UVWASI_ESUCCESS;
+}
+
+
+uvwasi_errno_t uvwasi__resolve_path(const uvwasi_t* uvwasi,
+ const struct uvwasi_fd_wrap_t* fd,
+ const char* path,
+ size_t path_len,
+ char* resolved_path,
+ uvwasi_lookupflags_t flags) {
+ uv_fs_t req;
+ uvwasi_errno_t err;
+ const char* input;
+ char* host_path;
+ char* normalized_path;
+ char* link_target;
+ size_t input_len;
+ size_t host_path_len;
+ size_t normalized_len;
+ int follow_count;
+ int r;
+
+ input = path;
+ input_len = path_len;
+ link_target = NULL;
+ follow_count = 0;
+ host_path = NULL;
+
+start:
+ normalized_path = NULL;
+ err = UVWASI_ESUCCESS;
+
+ if (1 == uvwasi__is_absolute_path(input, input_len)) {
+ err = uvwasi__normalize_absolute_path(uvwasi,
+ fd,
+ input,
+ input_len,
+ &normalized_path,
+ &normalized_len);
+ } else {
+ err = uvwasi__normalize_relative_path(uvwasi,
+ fd,
+ input,
+ input_len,
+ &normalized_path,
+ &normalized_len);
+ }
+
+ if (err != UVWASI_ESUCCESS)
+ goto exit;
+
+ uvwasi__free(uvwasi, host_path);
+ err = uvwasi__resolve_path_to_host(uvwasi,
+ fd,
+ normalized_path,
+ normalized_len,
+ &host_path,
+ &host_path_len);
+ if (err != UVWASI_ESUCCESS)
+ goto exit;
+
+ /* TODO(cjihrig): Currently performing a bounds check here. The TODO is to
+ stop allocating resolved_path in every caller and instead return the
+ path allocated in this function. */
+ if (host_path_len > PATH_MAX_BYTES) {
+ err = UVWASI_ENOBUFS;
+ goto exit;
+ }
+
+ if ((flags & UVWASI_LOOKUP_SYMLINK_FOLLOW) == UVWASI_LOOKUP_SYMLINK_FOLLOW) {
+ r = uv_fs_readlink(NULL, &req, host_path, NULL);
+
+ if (r != 0) {
+#ifdef _WIN32
+ /* uv_fs_readlink() returns UV__UNKNOWN on Windows. Try to get a better
+ error using uv_fs_stat(). */
+ if (r == UV__UNKNOWN) {
+ uv_fs_req_cleanup(&req);
+ r = uv_fs_stat(NULL, &req, host_path, NULL);
+
+ if (r == 0) {
+ if (uvwasi__stat_to_filetype(&req.statbuf) !=
+ UVWASI_FILETYPE_SYMBOLIC_LINK) {
+ r = UV_EINVAL;
+ }
+ }
+
+ /* Fall through. */
+ }
+#endif /* _WIN32 */
+
+ /* Don't report UV_EINVAL or UV_ENOENT. They mean that either the file
+ does not exist, or it is not a symlink. Both are OK. */
+ if (r != UV_EINVAL && r != UV_ENOENT)
+ err = uvwasi__translate_uv_error(r);
+
+ uv_fs_req_cleanup(&req);
+ goto exit;
+ }
+
+ /* Clean up memory and follow the link, unless it's time to return ELOOP. */
+ follow_count++;
+ if (follow_count >= UVWASI__MAX_SYMLINK_FOLLOWS) {
+ uv_fs_req_cleanup(&req);
+ err = UVWASI_ELOOP;
+ goto exit;
+ }
+
+ input_len = strlen(req.ptr);
+ uvwasi__free(uvwasi, link_target);
+ link_target = uvwasi__malloc(uvwasi, input_len + 1);
+ if (link_target == NULL) {
+ uv_fs_req_cleanup(&req);
+ err = UVWASI_ENOMEM;
+ goto exit;
+ }
+
+ memcpy(link_target, req.ptr, input_len + 1);
+ input = link_target;
+ uvwasi__free(uvwasi, normalized_path);
+ uv_fs_req_cleanup(&req);
+ goto start;
+ }
+
+exit:
+ if (err == UVWASI_ESUCCESS)
+ memcpy(resolved_path, host_path, host_path_len + 1);
+
+ uvwasi__free(uvwasi, link_target);
+ uvwasi__free(uvwasi, normalized_path);
+ uvwasi__free(uvwasi, host_path);
+ return err;
+}
diff --git a/deps/uvwasi/src/path_resolver.h b/deps/uvwasi/src/path_resolver.h
new file mode 100644
index 00000000000000..d5f95eafbfbf83
--- /dev/null
+++ b/deps/uvwasi/src/path_resolver.h
@@ -0,0 +1,28 @@
+#ifndef __UVWASI_PATH_RESOLVER_H__
+#define __UVWASI_PATH_RESOLVER_H__
+
+#include "uvwasi.h"
+
+/* TODO(cjihrig): PATH_MAX_BYTES shouldn't be stack allocated. On Windows, paths
+ can be 32k long, and this PATH_MAX_BYTES is an artificial limitation. */
+#ifdef _WIN32
+/* MAX_PATH is in characters, not bytes. Make sure we have enough headroom. */
+# define PATH_MAX_BYTES (MAX_PATH * 4)
+#else
+# include
+# define PATH_MAX_BYTES (PATH_MAX)
+#endif
+
+uvwasi_errno_t uvwasi__normalize_path(const char* path,
+ size_t path_len,
+ char* normalized_path,
+ size_t normalized_len);
+
+uvwasi_errno_t uvwasi__resolve_path(const uvwasi_t* uvwasi,
+ const struct uvwasi_fd_wrap_t* fd,
+ const char* path,
+ size_t path_len,
+ char* resolved_path,
+ uvwasi_lookupflags_t flags);
+
+#endif /* __UVWASI_PATH_RESOLVER_H__ */
diff --git a/deps/uvwasi/src/uvwasi.c b/deps/uvwasi/src/uvwasi.c
index c80fc7715c1dc0..0ee66be36a3951 100644
--- a/deps/uvwasi/src/uvwasi.c
+++ b/deps/uvwasi/src/uvwasi.c
@@ -7,14 +7,11 @@
# include
# include
# include
-# define IS_SLASH(c) ((c) == '/')
#else
# include
-# define IS_SLASH(c) ((c) == '/' || (c) == '\\')
#endif /* _WIN32 */
#define UVWASI__READDIR_NUM_ENTRIES 1
-#define UVWASI__MAX_SYMLINK_FOLLOWS 32
#include "uvwasi.h"
#include "uvwasi_alloc.h"
@@ -22,18 +19,9 @@
#include "uv_mapping.h"
#include "fd_table.h"
#include "clocks.h"
+#include "path_resolver.h"
#include "wasi_rights.h"
-/* TODO(cjihrig): PATH_MAX_BYTES shouldn't be stack allocated. On Windows, paths
- can be 32k long, and this PATH_MAX_BYTES is an artificial limitation. */
-#ifdef _WIN32
-/* MAX_PATH is in characters, not bytes. Make sure we have enough headroom. */
-# define PATH_MAX_BYTES (MAX_PATH * 4)
-#else
-# include
-# define PATH_MAX_BYTES (PATH_MAX)
-#endif
-
/* IBMi PASE does not support posix_fadvise() */
#ifdef __PASE__
# undef POSIX_FADV_NORMAL
@@ -84,322 +72,6 @@ static const uvwasi_mem_t default_allocator = {
};
-static int uvwasi__is_absolute_path(const char* path, size_t path_len) {
- /* It's expected that only Unix style paths will be generated by WASI. */
- return path != NULL && path_len > 0 && path[0] == '/';
-}
-
-
-static char* uvwasi__strchr_slash(const char* s) {
- /* strchr() that identifies /, as well as \ on Windows. */
- do {
- if (IS_SLASH(*s))
- return (char*) s;
- } while (*s++);
-
- return NULL;
-}
-
-
-static uvwasi_errno_t uvwasi__normalize_path(const char* path,
- size_t path_len,
- char* normalized_path,
- size_t normalized_len) {
- const char* cur;
- char* ptr;
- char* next;
- size_t cur_len;
-
- if (path_len > normalized_len)
- return UVWASI_ENOBUFS;
-
- normalized_path[0] = '\0';
- ptr = normalized_path;
- for (cur = path; cur != NULL; cur = next + 1) {
- next = uvwasi__strchr_slash(cur);
- cur_len = (next == NULL) ? strlen(cur) : (size_t) (next - cur);
-
- if (cur_len == 0 || (cur_len == 1 && cur[0] == '.'))
- continue;
-
- if (cur_len == 2 && cur[0] == '.' && cur[1] == '.') {
- while (!IS_SLASH(*ptr) && ptr != normalized_path)
- ptr--;
- *ptr = '\0';
- continue;
- }
-
- *ptr = '/';
- ptr++;
- memcpy(ptr, cur, cur_len);
- ptr += cur_len;
- *ptr = '\0';
-
- if (next == NULL)
- break;
- }
-
- return UVWASI_ESUCCESS;
-}
-
-
-static uvwasi_errno_t uvwasi__resolve_path_to_host(
- const uvwasi_t* uvwasi,
- const struct uvwasi_fd_wrap_t* fd,
- const char* path,
- size_t path_len,
- char** resolved_path,
- size_t* resolved_len
- ) {
- /* Return the normalized path, but resolved to the host's real path. */
- int real_path_len;
- int fake_path_len;
-#ifdef _WIN32
- size_t i;
-#endif /* _WIN32 */
-
- real_path_len = strlen(fd->real_path);
- fake_path_len = strlen(fd->path);
- *resolved_len = path_len - fake_path_len + real_path_len;
- *resolved_path = uvwasi__malloc(uvwasi, *resolved_len + 1);
-
- if (*resolved_path == NULL)
- return UVWASI_ENOMEM;
-
- memcpy(*resolved_path, fd->real_path, real_path_len);
- memcpy(*resolved_path + real_path_len,
- path + fake_path_len,
- path_len - fake_path_len + 1);
-
-#ifdef _WIN32
- /* Replace / with \ on Windows. */
- for (i = real_path_len; i < *resolved_len; i++) {
- if ((*resolved_path)[i] == '/')
- (*resolved_path)[i] = '\\';
- }
-#endif /* _WIN32 */
-
- return UVWASI_ESUCCESS;
-}
-
-
-static uvwasi_errno_t uvwasi__normalize_absolute_path(
- const uvwasi_t* uvwasi,
- const struct uvwasi_fd_wrap_t* fd,
- const char* path,
- size_t path_len,
- char** normalized_path,
- size_t* normalized_len
- ) {
- uvwasi_errno_t err;
- char* abs_path;
- int abs_size;
-
- *normalized_path = NULL;
- *normalized_len = 0;
- abs_size = path_len + 1;
- abs_path = uvwasi__malloc(uvwasi, abs_size);
- if (abs_path == NULL) {
- err = UVWASI_ENOMEM;
- goto exit;
- }
-
- /* Normalize the input path first. */
- err = uvwasi__normalize_path(path,
- path_len,
- abs_path,
- path_len);
- if (err != UVWASI_ESUCCESS)
- goto exit;
-
- /* Once the input is normalized, ensure that it is still sandboxed. */
- if (abs_path != strstr(abs_path, fd->path)) {
- err = UVWASI_ENOTCAPABLE;
- goto exit;
- }
-
- *normalized_path = abs_path;
- *normalized_len = abs_size - 1;
- return UVWASI_ESUCCESS;
-
-exit:
- uvwasi__free(uvwasi, abs_path);
- return err;
-}
-
-
-static uvwasi_errno_t uvwasi__normalize_relative_path(
- const uvwasi_t* uvwasi,
- const struct uvwasi_fd_wrap_t* fd,
- const char* path,
- size_t path_len,
- char** normalized_path,
- size_t* normalized_len
- ) {
- uvwasi_errno_t err;
- char* abs_path;
- int abs_size;
- int r;
-
- *normalized_path = NULL;
- *normalized_len = 0;
- abs_size = path_len + strlen(fd->path) + 2;
- abs_path = uvwasi__malloc(uvwasi, abs_size);
- if (abs_path == NULL) {
- err = UVWASI_ENOMEM;
- goto exit;
- }
-
- /* Resolve the relative path to an absolute path based on fd's fake path. */
- r = snprintf(abs_path, abs_size, "%s/%s", fd->path, path);
- if (r <= 0) {
- err = uvwasi__translate_uv_error(uv_translate_sys_error(errno));
- goto exit;
- }
-
- err = uvwasi__normalize_absolute_path(uvwasi,
- fd,
- abs_path,
- abs_size - 1,
- normalized_path,
- normalized_len);
-exit:
- uvwasi__free(uvwasi, abs_path);
- return err;
-}
-
-
-static uvwasi_errno_t uvwasi__resolve_path(const uvwasi_t* uvwasi,
- const struct uvwasi_fd_wrap_t* fd,
- const char* path,
- size_t path_len,
- char* resolved_path,
- uvwasi_lookupflags_t flags) {
- uv_fs_t req;
- uvwasi_errno_t err;
- const char* input;
- char* host_path;
- char* normalized_path;
- char* link_target;
- size_t input_len;
- size_t host_path_len;
- size_t normalized_len;
- int follow_count;
- int r;
-
- input = path;
- input_len = path_len;
- link_target = NULL;
- follow_count = 0;
- host_path = NULL;
-
-start:
- normalized_path = NULL;
- err = UVWASI_ESUCCESS;
-
- if (1 == uvwasi__is_absolute_path(input, input_len)) {
- err = uvwasi__normalize_absolute_path(uvwasi,
- fd,
- input,
- input_len,
- &normalized_path,
- &normalized_len);
- } else {
- err = uvwasi__normalize_relative_path(uvwasi,
- fd,
- input,
- input_len,
- &normalized_path,
- &normalized_len);
- }
-
- if (err != UVWASI_ESUCCESS)
- goto exit;
-
- uvwasi__free(uvwasi, host_path);
- err = uvwasi__resolve_path_to_host(uvwasi,
- fd,
- normalized_path,
- normalized_len,
- &host_path,
- &host_path_len);
- if (err != UVWASI_ESUCCESS)
- goto exit;
-
- /* TODO(cjihrig): Currently performing a bounds check here. The TODO is to
- stop allocating resolved_path in every caller and instead return the
- path allocated in this function. */
- if (host_path_len > PATH_MAX_BYTES) {
- err = UVWASI_ENOBUFS;
- goto exit;
- }
-
- if ((flags & UVWASI_LOOKUP_SYMLINK_FOLLOW) == UVWASI_LOOKUP_SYMLINK_FOLLOW) {
- r = uv_fs_readlink(NULL, &req, host_path, NULL);
-
- if (r != 0) {
-#ifdef _WIN32
- /* uv_fs_readlink() returns UV__UNKNOWN on Windows. Try to get a better
- error using uv_fs_stat(). */
- if (r == UV__UNKNOWN) {
- uv_fs_req_cleanup(&req);
- r = uv_fs_stat(NULL, &req, host_path, NULL);
-
- if (r == 0) {
- if (uvwasi__stat_to_filetype(&req.statbuf) !=
- UVWASI_FILETYPE_SYMBOLIC_LINK) {
- r = UV_EINVAL;
- }
- }
-
- // Fall through.
- }
-#endif /* _WIN32 */
-
- /* Don't report UV_EINVAL or UV_ENOENT. They mean that either the file
- does not exist, or it is not a symlink. Both are OK. */
- if (r != UV_EINVAL && r != UV_ENOENT)
- err = uvwasi__translate_uv_error(r);
-
- uv_fs_req_cleanup(&req);
- goto exit;
- }
-
- /* Clean up memory and follow the link, unless it's time to return ELOOP. */
- follow_count++;
- if (follow_count >= UVWASI__MAX_SYMLINK_FOLLOWS) {
- uv_fs_req_cleanup(&req);
- err = UVWASI_ELOOP;
- goto exit;
- }
-
- input_len = strlen(req.ptr);
- uvwasi__free(uvwasi, link_target);
- link_target = uvwasi__malloc(uvwasi, input_len + 1);
- if (link_target == NULL) {
- uv_fs_req_cleanup(&req);
- err = UVWASI_ENOMEM;
- goto exit;
- }
-
- memcpy(link_target, req.ptr, input_len + 1);
- input = link_target;
- uvwasi__free(uvwasi, normalized_path);
- uv_fs_req_cleanup(&req);
- goto start;
- }
-
-exit:
- if (err == UVWASI_ESUCCESS)
- memcpy(resolved_path, host_path, host_path_len + 1);
-
- uvwasi__free(uvwasi, link_target);
- uvwasi__free(uvwasi, normalized_path);
- uvwasi__free(uvwasi, host_path);
- return err;
-}
-
-
static uvwasi_errno_t uvwasi__lseek(uv_file fd,
uvwasi_filedelta_t offset,
uvwasi_whence_t whence,
diff --git a/deps/uvwasi/uvwasi.gyp b/deps/uvwasi/uvwasi.gyp
index 6963cbf20a7923..42769095ecbafd 100644
--- a/deps/uvwasi/uvwasi.gyp
+++ b/deps/uvwasi/uvwasi.gyp
@@ -11,6 +11,7 @@
'sources': [
'src/clocks.c',
'src/fd_table.c',
+ 'src/path_resolver.c',
'src/uv_mapping.c',
'src/uvwasi.c',
'src/wasi_rights.c',
diff --git a/deps/v8/src/objects/backing-store.cc b/deps/v8/src/objects/backing-store.cc
index 8d604ba35d8d06..c9a5c2e4c8ec20 100644
--- a/deps/v8/src/objects/backing-store.cc
+++ b/deps/v8/src/objects/backing-store.cc
@@ -164,7 +164,10 @@ void BackingStore::Clear() {
BackingStore::~BackingStore() {
GlobalBackingStoreRegistry::Unregister(this);
- if (buffer_start_ == nullptr) return; // nothing to deallocate
+ if (buffer_start_ == nullptr) {
+ Clear();
+ return;
+ }
if (is_wasm_memory_) {
DCHECK(free_on_destruct_);
diff --git a/deps/v8/test/cctest/test-api-array-buffer.cc b/deps/v8/test/cctest/test-api-array-buffer.cc
index e8e026f156053d..eeddbcca1003be 100644
--- a/deps/v8/test/cctest/test-api-array-buffer.cc
+++ b/deps/v8/test/cctest/test-api-array-buffer.cc
@@ -729,6 +729,46 @@ TEST(BackingStore_HoldAllocatorAlive_UntilIsolateShutdown) {
CHECK(allocator_weak.expired());
}
+class NullptrAllocator final : public v8::ArrayBuffer::Allocator {
+ public:
+ void* Allocate(size_t length) override {
+ CHECK_EQ(length, 0);
+ return nullptr;
+ }
+ void* AllocateUninitialized(size_t length) override {
+ CHECK_EQ(length, 0);
+ return nullptr;
+ }
+ void Free(void* data, size_t length) override { CHECK_EQ(data, nullptr); }
+};
+
+TEST(BackingStore_ReleaseAllocator_NullptrBackingStore) {
+ std::shared_ptr allocator =
+ std::make_shared();
+ std::weak_ptr allocator_weak(allocator);
+
+ v8::Isolate::CreateParams create_params;
+ create_params.array_buffer_allocator_shared = allocator;
+ v8::Isolate* isolate = v8::Isolate::New(create_params);
+ isolate->Enter();
+
+ allocator.reset();
+ create_params.array_buffer_allocator_shared.reset();
+ CHECK(!allocator_weak.expired());
+
+ {
+ std::shared_ptr backing_store =
+ v8::ArrayBuffer::NewBackingStore(isolate, 0);
+ // This should release a reference to the allocator, even though the
+ // buffer is empty/nullptr.
+ backing_store.reset();
+ }
+
+ isolate->Exit();
+ isolate->Dispose();
+ CHECK(allocator_weak.expired());
+}
+
TEST(BackingStore_HoldAllocatorAlive_AfterIsolateShutdown) {
std::shared_ptr allocator =
std::make_shared();
diff --git a/doc/api/assert.md b/doc/api/assert.md
index 9731ddaa13f315..9ad01a48f0fd9a 100644
--- a/doc/api/assert.md
+++ b/doc/api/assert.md
@@ -149,6 +149,144 @@ try {
}
```
+## Class: `assert.CallTracker`
+
+
+> Stability: 1 - Experimental
+
+This feature is currently experimental and behavior might still change.
+
+### `new assert.CallTracker()`
+
+
+Creates a new [`CallTracker`][] object which can be used to track if functions
+were called a specific number of times. The `tracker.verify()` must be called
+for the verification to take place. The usual pattern would be to call it in a
+[`process.on('exit')`][] handler.
+
+```js
+const assert = require('assert');
+
+const tracker = new assert.CallTracker();
+
+function func() {}
+
+// callsfunc() must be called exactly 1 time before tracker.verify().
+const callsfunc = tracker.calls(func, 1);
+
+callsfunc();
+
+// Calls tracker.verify() and verifies if all tracker.calls() functions have
+// been called exact times.
+process.on('exit', () => {
+ tracker.verify();
+});
+```
+
+### `tracker.calls([fn][, exact])`
+
+
+* `fn` {Function} **Default** A no-op function.
+* `exact` {number} **Default** `1`.
+* Returns: {Function} that wraps `fn`.
+
+The wrapper function is expected to be called exactly `exact` times. If the
+function has not been called exactly `exact` times when
+[`tracker.verify()`][] is called, then [`tracker.verify()`][] will throw an
+error.
+
+```js
+const assert = require('assert');
+
+// Creates call tracker.
+const tracker = new assert.CallTracker();
+
+function func() {}
+
+// Returns a function that wraps func() that must be called exact times
+// before tracker.verify().
+const callsfunc = tracker.calls(func);
+```
+
+### `tracker.report()`
+
+
+* Returns: {Array} of objects containing information about the wrapper functions
+returned by [`tracker.calls()`][].
+* Object {Object}
+ * `message` {string}
+ * `actual` {number} The actual number of times the function was called.
+ * `expected` {number} The number of times the function was expected to be
+ called.
+ * `operator` {string} The name of the function that is wrapped.
+ * `stack` {Object} A stack trace of the function.
+
+The arrays contains information about the expected and actual number of calls of
+the functions that have not been called the expected number of times.
+
+```js
+const assert = require('assert');
+
+// Creates call tracker.
+const tracker = new assert.CallTracker();
+
+function func() {}
+
+function foo() {}
+
+// Returns a function that wraps func() that must be called exact times
+// before tracker.verify().
+const callsfunc = tracker.calls(func, 2);
+
+// Returns an array containing information on callsfunc()
+tracker.report();
+// [
+// {
+// message: 'Expected the func function to be executed 2 time(s) but was
+// executed 0 time(s).',
+// actual: 0,
+// expected: 2,
+// operator: 'func',
+// stack: stack trace
+// }
+// ]
+```
+
+### `tracker.verify()`
+
+
+Iterates through the list of functions passed to
+[`tracker.calls()`][] and will throw an error for functions that
+have not been called the expected number of times.
+
+```js
+const assert = require('assert');
+
+// Creates call tracker.
+const tracker = new assert.CallTracker();
+
+function func() {}
+
+// Returns a function that wraps func() that must be called exact times
+// before tracker.verify().
+const callsfunc = tracker.calls(func, 2);
+
+callsfunc();
+
+// Will throw an error since callsfunc() was only called once.
+tracker.verify();
+```
+
## `assert(value[, message])`
* `options` {Object}
@@ -107,6 +110,8 @@ changes:
**Default:** `'auto'`.
* `inspectOptions` {Object} Specifies options that are passed along to
[`util.inspect()`][].
+ * `groupIndentation` {number} Set group indentation.
+ **Default:** `2`.
Creates a new `Console` with one or two writable stream instances. `stdout` is a
writable stream to print log or info output. `stderr` is used for warning or
@@ -306,7 +311,8 @@ added: v8.5.0
* `...label` {any}
-Increases indentation of subsequent lines by two spaces.
+Increases indentation of subsequent lines by spaces for `groupIndentation`
+length.
If one or more `label`s are provided, those are printed first without the
additional indentation.
@@ -323,7 +329,8 @@ An alias for [`console.group()`][].
added: v8.5.0
-->
-Decreases indentation of subsequent lines by two spaces.
+Decreases indentation of subsequent lines by spaces for `groupIndentation`
+length.
### `console.info([data][, ...args])`
+> Stability: 0 - Deprecated: Use [`request.destroy()`][] instead.
+
Marks the request as aborting. Calling this will cause remaining data
in the response to be dropped and the socket to be destroyed.
diff --git a/doc/api/http2.md b/doc/api/http2.md
index c4365f4e20e869..6f3b70be3a03f4 100644
--- a/doc/api/http2.md
+++ b/doc/api/http2.md
@@ -913,8 +913,9 @@ the value is `undefined`, the stream is not yet ready for use.
All [`Http2Stream`][] instances are destroyed either when:
* An `RST_STREAM` frame for the stream is received by the connected peer,
- and pending data has been read.
-* The `http2stream.close()` method is called, and pending data has been read.
+ and (for client streams only) pending data has been read.
+* The `http2stream.close()` method is called, and (for client streams only)
+ pending data has been read.
* The `http2stream.destroy()` or `http2session.destroy()` methods are called.
When an `Http2Stream` instance is destroyed, an attempt will be made to send an
diff --git a/doc/api/os.md b/doc/api/os.md
index e2cce1f8a5580f..d70bd2d260b3ac 100644
--- a/doc/api/os.md
+++ b/doc/api/os.md
@@ -389,7 +389,7 @@ operating system response.
Throws a [`SystemError`][] if a user has no `username` or `homedir`.
-## `os.version()`
+## `os.version()`
diff --git a/doc/api/stream.md b/doc/api/stream.md
index e8993849090199..6229704eb91bc4 100644
--- a/doc/api/stream.md
+++ b/doc/api/stream.md
@@ -1442,7 +1442,7 @@ If the loop terminates with a `break` or a `throw`, the stream will be
destroyed. In other terms, iterating over a stream will consume the stream
fully. The stream will be read in chunks of size equal to the `highWaterMark`
option. In the code example above, data will be in a single chunk if the file
-has less then 64kb of data because no `highWaterMark` option is provided to
+has less then 64KB of data because no `highWaterMark` option is provided to
[`fs.createReadStream()`][].
### Duplex and Transform Streams
@@ -1700,7 +1700,8 @@ added:
-->
* `iterable` {Iterable} Object implementing the `Symbol.asyncIterator` or
- `Symbol.iterator` iterable protocol.
+ `Symbol.iterator` iterable protocol. Emits an 'error' event if a null
+ value is passed.
* `options` {Object} Options provided to `new stream.Readable([options])`.
By default, `Readable.from()` will set `options.objectMode` to `true`, unless
this is explicitly opted out by setting `options.objectMode` to `false`.
@@ -1829,7 +1830,7 @@ changes:
* `options` {Object}
* `highWaterMark` {number} Buffer level when
[`stream.write()`][stream-write] starts returning `false`. **Default:**
- `16384` (16kb), or `16` for `objectMode` streams.
+ `16384` (16KB), or `16` for `objectMode` streams.
* `decodeStrings` {boolean} Whether to encode `string`s passed to
[`stream.write()`][stream-write] to `Buffer`s (with the encoding
specified in the [`stream.write()`][stream-write] call) before passing
@@ -2111,7 +2112,7 @@ changes:
* `options` {Object}
* `highWaterMark` {number} The maximum [number of bytes][hwm-gotcha] to store
in the internal buffer before ceasing to read from the underlying resource.
- **Default:** `16384` (16kb), or `16` for `objectMode` streams.
+ **Default:** `16384` (16KB), or `16` for `objectMode` streams.
* `encoding` {string} If specified, then buffers will be decoded to
strings using the specified encoding. **Default:** `null`.
* `objectMode` {boolean} Whether this stream should behave
diff --git a/doc/api/util.md b/doc/api/util.md
index 757c6a48f6ece9..567ac5f7523260 100644
--- a/doc/api/util.md
+++ b/doc/api/util.md
@@ -1259,6 +1259,25 @@ util.types.isAnyArrayBuffer(new ArrayBuffer()); // Returns true
util.types.isAnyArrayBuffer(new SharedArrayBuffer()); // Returns true
```
+### `util.types.isArrayBufferView(value)`
+
+
+* `value` {any}
+* Returns: {boolean}
+
+Returns `true` if the value is an instance of one of the [`ArrayBuffer`][]
+views, such as typed array objects or [`DataView`][]. Equivalent to
+[`ArrayBuffer.isView()`][].
+
+```js
+util.types.isArrayBufferView(new Int8Array()); // true
+util.types.isArrayBufferView(Buffer.from('hello world')); // true
+util.types.isArrayBufferView(new DataView(new ArrayBuffer(16))); // true
+util.types.isArrayBufferView(new ArrayBuffer()); // false
+```
+
### `util.types.isArgumentsObject(value)`
+```js
+const assert = require('assert');
+
+const tracker = new assert.CallTracker();
+
+function func() {}
+const callsfunc = tracker.calls(func);
+
+console.log(tracker.report());
+/*
+[
+ {
+ message: 'Expected the func function to be executed 1 time(s) but was executed 0 time(s).',
+ actual: 0,
+ expected: 1,
+ operator: 'func',
+ stack: Error
+ ...
+ }
+]
+*/
+```
+
+Contributed by ConorDavenport - [#31982](https://github.com/nodejs/node/pull/31982).
+
+#### Console `groupIndentation` option
+
+The Console constructor (`require('console').Console`) now supports different group indentations.
+
+This is useful in case you want different grouping width than 2 spaces.
+
+```js
+const { Console } = require('console');
+const customConsole = new Console({
+ stdout: process.stdout,
+ stderr: process.stderr,
+ groupIndentation: 10
+});
+
+customConsole.log('foo');
+// 'foo'
+customConsole.group();
+customConsole.log('foo');
+// 'foo'
+```
+
+Contributed by rickyes - [#32964](https://github.com/nodejs/node/pull/32964).
+
+### Commits
+
+#### Semver-minor commits
+
+* [[`c87ed21fdf`](https://github.com/nodejs/node/commit/c87ed21fdf)] - **(SEMVER-MINOR)** **assert**: port common.mustCall() to assert (ConorDavenport) [#31982](https://github.com/nodejs/node/pull/31982)
+* [[`c49e3ea20c`](https://github.com/nodejs/node/commit/c49e3ea20c)] - **(SEMVER-MINOR)** **console**: support console constructor groupIndentation option (rickyes) [#32964](https://github.com/nodejs/node/pull/32964)
+* [[`bc9e413dae`](https://github.com/nodejs/node/commit/bc9e413dae)] - **(SEMVER-MINOR)** **worker**: add stack size resource limit option (Anna Henningsen) [#33085](https://github.com/nodejs/node/pull/33085)
+
+#### Semver-patch commits
+
+* [[`f62d92b900`](https://github.com/nodejs/node/commit/f62d92b900)] - **build**: add --error-on-warn configure flag (Daniel Bevenius) [#32685](https://github.com/nodejs/node/pull/32685)
+* [[`db293c47dd`](https://github.com/nodejs/node/commit/db293c47dd)] - **cluster**: fix error on worker disconnect/destroy (Santiago Gimeno) [#32793](https://github.com/nodejs/node/pull/32793)
+* [[`83e165bf88`](https://github.com/nodejs/node/commit/83e165bf88)] - **crypto**: check DiffieHellman p and g params (Ben Noordhuis) [#32739](https://github.com/nodejs/node/pull/32739)
+* [[`e07cca6af6`](https://github.com/nodejs/node/commit/e07cca6af6)] - **crypto**: generator must be int32 in DiffieHellman() (Ben Noordhuis) [#32739](https://github.com/nodejs/node/pull/32739)
+* [[`637442fec9`](https://github.com/nodejs/node/commit/637442fec9)] - **crypto**: key size must be int32 in DiffieHellman() (Ben Noordhuis) [#32739](https://github.com/nodejs/node/pull/32739)
+* [[`c5a4534d5c`](https://github.com/nodejs/node/commit/c5a4534d5c)] - **deps**: V8: backport e29c62b74854 (Anna Henningsen) [#33125](https://github.com/nodejs/node/pull/33125)
+* [[`8325c29e92`](https://github.com/nodejs/node/commit/8325c29e92)] - **deps**: update to uvwasi 0.0.8 (Colin Ihrig) [#33078](https://github.com/nodejs/node/pull/33078)
+* [[`2174159598`](https://github.com/nodejs/node/commit/2174159598)] - **esm**: improve commonjs hint on module not found (Daniele Belardi) [#31906](https://github.com/nodejs/node/pull/31906)
+* [[`74b0e8c3a8`](https://github.com/nodejs/node/commit/74b0e8c3a8)] - **http**: ensure client request emits close (Robert Nagy) [#33178](https://github.com/nodejs/node/pull/33178)
+* [[`a4ec01c55b`](https://github.com/nodejs/node/commit/a4ec01c55b)] - **http**: simplify sending header (Robert Nagy) [#33200](https://github.com/nodejs/node/pull/33200)
+* [[`451993ea94`](https://github.com/nodejs/node/commit/451993ea94)] - **http**: set default timeout in agent keepSocketAlive (Owen Smith) [#33127](https://github.com/nodejs/node/pull/33127)
+* [[`3cb1713a59`](https://github.com/nodejs/node/commit/3cb1713a59)] - **http2,doc**: minor fixes (Alba Mendez) [#28044](https://github.com/nodejs/node/pull/28044)
+* [[`eab4be1b93`](https://github.com/nodejs/node/commit/eab4be1b93)] - **lib**: cosmetic change to builtinLibs list for maintainability (James M Snell) [#33106](https://github.com/nodejs/node/pull/33106)
+* [[`542da430ff`](https://github.com/nodejs/node/commit/542da430ff)] - **lib**: fix validateport error message when allowZero is false (rickyes) [#32861](https://github.com/nodejs/node/pull/32861)
+* [[`5eccf1e9ad`](https://github.com/nodejs/node/commit/5eccf1e9ad)] - **module**: no type module resolver side effects (Guy Bedford) [#33086](https://github.com/nodejs/node/pull/33086)
+* [[`466213d726`](https://github.com/nodejs/node/commit/466213d726)] - **n-api**: simplify uv\_idle wrangling (Ben Noordhuis) [#32997](https://github.com/nodejs/node/pull/32997)
+* [[`ed45b51642`](https://github.com/nodejs/node/commit/ed45b51642)] - **path**: fix comment grammar (thecodrr) [#32942](https://github.com/nodejs/node/pull/32942)
+* [[`bb2d2f6e0e`](https://github.com/nodejs/node/commit/bb2d2f6e0e)] - **src**: remove unused v8 Message namespace (Adrian Estrada) [#33180](https://github.com/nodejs/node/pull/33180)
+* [[`de643bc325`](https://github.com/nodejs/node/commit/de643bc325)] - **src**: use unique\_ptr for CachedData in ContextifyScript::New (Anna Henningsen) [#33113](https://github.com/nodejs/node/pull/33113)
+* [[`f61928ba35`](https://github.com/nodejs/node/commit/f61928ba35)] - **src**: return undefined when validation err == 0 (James M Snell) [#33107](https://github.com/nodejs/node/pull/33107)
+* [[`f4e5ab14da`](https://github.com/nodejs/node/commit/f4e5ab14da)] - **src**: crypto::UseSNIContext to use BaseObjectPtr (James M Snell) [#33107](https://github.com/nodejs/node/pull/33107)
+* [[`541ea035bf`](https://github.com/nodejs/node/commit/541ea035bf)] - **src**: separate out NgLibMemoryManagerBase (James M Snell) [#33104](https://github.com/nodejs/node/pull/33104)
+* [[`10a87c81cf`](https://github.com/nodejs/node/commit/10a87c81cf)] - **src**: remove unnecessary fully qualified names (rickyes) [#33077](https://github.com/nodejs/node/pull/33077)
+* [[`45032a39e8`](https://github.com/nodejs/node/commit/45032a39e8)] - **stream**: fix stream.finished on Duplex (Robert Nagy) [#33133](https://github.com/nodejs/node/pull/33133)
+* [[`4cfa7e0716`](https://github.com/nodejs/node/commit/4cfa7e0716)] - **stream**: simplify Readable push/unshift logic (himself65) [#32899](https://github.com/nodejs/node/pull/32899)
+* [[`bc40ed31b3`](https://github.com/nodejs/node/commit/bc40ed31b3)] - **stream**: add null check in Readable.from (Pranshu Srivastava) [#32873](https://github.com/nodejs/node/pull/32873)
+* [[`b183d0a18a`](https://github.com/nodejs/node/commit/b183d0a18a)] - **stream**: let Duplex re-use Writable properties (Robert Nagy) [#33079](https://github.com/nodejs/node/pull/33079)
+* [[`ec24577406`](https://github.com/nodejs/node/commit/ec24577406)] - **v8**: use AliasedBuffers for passing heap statistics around (Joyee Cheung) [#32929](https://github.com/nodejs/node/pull/32929)
+* [[`d39254ada6`](https://github.com/nodejs/node/commit/d39254ada6)] - **vm**: fix vm.measureMemory() and introduce execution option (Joyee Cheung) [#32988](https://github.com/nodejs/node/pull/32988)
+* [[`4423304ac4`](https://github.com/nodejs/node/commit/4423304ac4)] - **vm**: throw error when duplicated exportNames in SyntheticModule (himself65) [#32810](https://github.com/nodejs/node/pull/32810)
+* [[`3866dc1311`](https://github.com/nodejs/node/commit/3866dc1311)] - **wasi**: use free() to release preopen array (Anna Henningsen) [#33110](https://github.com/nodejs/node/pull/33110)
+* [[`d7d9960d38`](https://github.com/nodejs/node/commit/d7d9960d38)] - **wasi**: update start() behavior to match spec (Colin Ihrig) [#33073](https://github.com/nodejs/node/pull/33073)
+* [[`8d5ac1bbf0`](https://github.com/nodejs/node/commit/8d5ac1bbf0)] - **wasi**: rename \_\_wasi\_unstable\_reactor\_start() (Colin Ihrig) [#33073](https://github.com/nodejs/node/pull/33073)
+* [[`c6d632a72a`](https://github.com/nodejs/node/commit/c6d632a72a)] - **worker**: unify custom error creation (Anna Henningsen) [#33084](https://github.com/nodejs/node/pull/33084)
+
+#### Documentation commits
+
+* [[`6925b358f9`](https://github.com/nodejs/node/commit/6925b358f9)] - **doc**: mark assert.CallTracker experimental (Ruben Bridgewater) [#33124](https://github.com/nodejs/node/pull/33124)
+* [[`413f5d3581`](https://github.com/nodejs/node/commit/413f5d3581)] - **doc**: add missing deprecation not (Robert Nagy) [#33203](https://github.com/nodejs/node/pull/33203)
+* [[`7893bde07e`](https://github.com/nodejs/node/commit/7893bde07e)] - **doc**: fix a typo in crypto.generateKeyPairSync() (himself65) [#33187](https://github.com/nodejs/node/pull/33187)
+* [[`d02ced8af6`](https://github.com/nodejs/node/commit/d02ced8af6)] - **doc**: add util.types.isArrayBufferView() (Kevin Locke) [#33092](https://github.com/nodejs/node/pull/33092)
+* [[`36d50027af`](https://github.com/nodejs/node/commit/36d50027af)] - **doc**: clarify when not to run CI on docs (Juan José Arboleda) [#33101](https://github.com/nodejs/node/pull/33101)
+* [[`a99013718c`](https://github.com/nodejs/node/commit/a99013718c)] - **doc**: fix the spelling error in stream.md (白一梓) [#31561](https://github.com/nodejs/node/pull/31561)
+* [[`23962191c1`](https://github.com/nodejs/node/commit/23962191c1)] - **doc**: correct Nodejs to Node.js spelling (Nick Schonning) [#33088](https://github.com/nodejs/node/pull/33088)
+* [[`de15edcfc0`](https://github.com/nodejs/node/commit/de15edcfc0)] - **doc**: improve worker pool example (Ranjan Purbey) [#33082](https://github.com/nodejs/node/pull/33082)
+* [[`289a5c8dfb`](https://github.com/nodejs/node/commit/289a5c8dfb)] - **doc**: some grammar fixes (Chris Holland) [#33081](https://github.com/nodejs/node/pull/33081)
+* [[`82e459d9af`](https://github.com/nodejs/node/commit/82e459d9af)] - **doc**: don't check links in tmp dirs (Ben Noordhuis) [#32996](https://github.com/nodejs/node/pull/32996)
+* [[`c5a2f9a02a`](https://github.com/nodejs/node/commit/c5a2f9a02a)] - **doc**: fix markdown parsing on doc/api/os.md (Juan José Arboleda) [#33067](https://github.com/nodejs/node/pull/33067)
+
+#### Other commits
+
+* [[`60ebbc4386`](https://github.com/nodejs/node/commit/60ebbc4386)] - **test**: update c8 ignore comment (Benjamin Coe) [#33151](https://github.com/nodejs/node/pull/33151)
+* [[`e276524fcc`](https://github.com/nodejs/node/commit/e276524fcc)] - **test**: skip memory usage tests when ASAN is enabled (Anna Henningsen) [#33129](https://github.com/nodejs/node/pull/33129)
+* [[`89ed7a5862`](https://github.com/nodejs/node/commit/89ed7a5862)] - **test**: move test-process-title to sequential (Anna Henningsen) [#33150](https://github.com/nodejs/node/pull/33150)
+* [[`af7da46d9b`](https://github.com/nodejs/node/commit/af7da46d9b)] - **test**: fix out-of-bound reads from invalid sizeof usage (Anna Henningsen) [#33115](https://github.com/nodejs/node/pull/33115)
+* [[`9ccb6b2e8c`](https://github.com/nodejs/node/commit/9ccb6b2e8c)] - **test**: add missing calls to napi\_async\_destroy (Anna Henningsen) [#33114](https://github.com/nodejs/node/pull/33114)
+* [[`3c2f608a8d`](https://github.com/nodejs/node/commit/3c2f608a8d)] - **test**: correct typo in test name (Colin Ihrig) [#33083](https://github.com/nodejs/node/pull/33083)
+* [[`92c7e0620f`](https://github.com/nodejs/node/commit/92c7e0620f)] - **test**: check args on SourceTextModule cachedData (Juan José Arboleda) [#32956](https://github.com/nodejs/node/pull/32956)
+* [[`f79ef96fea`](https://github.com/nodejs/node/commit/f79ef96fea)] - **test**: mark test flaky on freebsd (Sam Roberts) [#32849](https://github.com/nodejs/node/pull/32849)
+* [[`aced1f5d70`](https://github.com/nodejs/node/commit/aced1f5d70)] - **test**: flaky test-stdout-close-catch on freebsd (Sam Roberts) [#32849](https://github.com/nodejs/node/pull/32849)
+* [[`6734cc43df`](https://github.com/nodejs/node/commit/6734cc43df)] - **tools**: bump remark-preset-lint-node to 1.15.0 (Rich Trott) [#33157](https://github.com/nodejs/node/pull/33157)
+* [[`a87d371014`](https://github.com/nodejs/node/commit/a87d371014)] - **tools**: fix redundant-move warning in inspector (Daniel Bevenius) [#32685](https://github.com/nodejs/node/pull/32685)
+* [[`12426f59f5`](https://github.com/nodejs/node/commit/12426f59f5)] - **tools**: update remark-preset-lint-node@1.14.0 (Rich Trott) [#33072](https://github.com/nodejs/node/pull/33072)
+* [[`8c40ffc329`](https://github.com/nodejs/node/commit/8c40ffc329)] - **tools**: update broken types in type parser (Colin Ihrig) [#33068](https://github.com/nodejs/node/pull/33068)
+
## 2020-04-29, Version 14.1.0 (Current), @BethGriggs
diff --git a/doc/guides/backporting-to-release-lines.md b/doc/guides/backporting-to-release-lines.md
index 55fbcb7e5bc344..6fcbdc43f72396 100644
--- a/doc/guides/backporting-to-release-lines.md
+++ b/doc/guides/backporting-to-release-lines.md
@@ -31,9 +31,9 @@ release line. All commands will use the `v10.x-staging` branch as the target
branch. In order to submit a backport pull request to another branch, simply
replace that with the staging branch for the targeted release line.
-1. Checkout the staging branch for the targeted release line
-2. Make sure that the local staging branch is up to date with the remote
-3. Create a new branch off of the staging branch
+1. Checkout the staging branch for the targeted release line.
+2. Make sure that the local staging branch is up to date with the remote.
+3. Create a new branch off of the staging branch, as shown below.
```shell
# Assuming your fork of Node.js is checked out in $NODE_DIR,
@@ -68,17 +68,17 @@ replace that with the staging branch for the targeted release line.
using `git add`, and then commit the changes. That can be done with
`git cherry-pick --continue`.
6. Leave the commit message as is. If you think it should be modified, comment
- in the Pull Request. The `Backport-PR-URL` metadata does need to be added to
+ in the pull request. The `Backport-PR-URL` metadata does need to be added to
the commit, but this will be done later.
7. Make sure `make -j4 test` passes.
-8. Push the changes to your fork
+8. Push the changes to your fork.
9. Open a pull request:
1. Be sure to target the `v10.x-staging` branch in the pull request.
1. Include the backport target in the pull request title in the following
format: `[v10.x backport] `.
Example: `[v10.x backport] process: improve performance of nextTick`
1. Check the checkbox labeled "Allow edits from maintainers".
- 1. In the description add a reference to the original PR.
+ 1. In the description add a reference to the original pull request.
1. Amend the commit message and include a `Backport-PR-URL:` metadata and
re-push the change to your fork.
1. Run a [`node-test-pull-request`][] CI job (with `REBASE_ONTO` set to the
@@ -86,8 +86,8 @@ replace that with the staging branch for the targeted release line.
10. If during the review process conflicts arise, use the following to rebase:
`git pull --rebase upstream v10.x-staging`
-After the PR lands replace the `backport-requested-v10.x` label on the original
-PR with `backported-to-v10.x`.
+After the pull request lands, replace the `backport-requested-v10.x` label
+on the original pull request with `backported-to-v10.x`.
[Release Schedule]: https://github.com/nodejs/Release#release-schedule1
[Release Plan]: https://github.com/nodejs/Release#release-plan
diff --git a/doc/guides/collaborator-guide.md b/doc/guides/collaborator-guide.md
index c08834ea4a1352..128f06857b8f0a 100644
--- a/doc/guides/collaborator-guide.md
+++ b/doc/guides/collaborator-guide.md
@@ -177,8 +177,10 @@ All pull requests must pass continuous integration tests. Code changes must pass
on [project CI server](https://ci.nodejs.org/). Pull requests that only change
documentation and comments can use GitHub Actions results.
-Do not land any pull requests without passing (green or yellow) CI runs. If
-there are CI failures unrelated to the change in the pull request, try "Resume
+Do not land any pull requests without a passing (green or yellow) CI run.
+For documentation-only changes, GitHub Actions CI is sufficient.
+For all other code changes, Jenkins CI must pass as well. If there are
+Jenkins CI failures unrelated to the change in the pull request, try "Resume
Build". It is in the left navigation of the relevant `node-test-pull-request`
job. It will preserve all the green results from the current job but re-run
everything else. Start a fresh CI if more than seven days have elapsed since
diff --git a/doc/guides/diagnostic-tooling-support-tiers.md b/doc/guides/diagnostic-tooling-support-tiers.md
index 254f90ecadd5fd..5d935a29369a88 100644
--- a/doc/guides/diagnostic-tooling-support-tiers.md
+++ b/doc/guides/diagnostic-tooling-support-tiers.md
@@ -25,7 +25,7 @@ the following tiers.
* The tool must have a guide or other documentation in the Node.js GitHub
organization or website;
* The tool must be working on all supported platforms;
- * The tool must only be using APIs exposed by Nodejs as opposed to
+ * The tool must only be using APIs exposed by Node.js as opposed to
its dependencies; and
* The tool must be open source.
diff --git a/lib/_http_agent.js b/lib/_http_agent.js
index 2618c6c3cb8223..b9d2c0c2c04a7c 100644
--- a/lib/_http_agent.js
+++ b/lib/_http_agent.js
@@ -138,11 +138,6 @@ function Agent(options) {
socket._httpMessage = null;
this.removeSocket(socket, options);
- const agentTimeout = this.options.timeout || 0;
- if (socket.timeout !== agentTimeout) {
- socket.setTimeout(agentTimeout);
- }
-
socket.once('error', freeSocketErrorListener);
freeSockets.push(socket);
});
@@ -247,7 +242,12 @@ Agent.prototype.addRequest = function addRequest(req, options, port/* legacy */,
} else if (sockLen < this.maxSockets) {
debug('call onSocket', sockLen, freeLen);
// If we are under maxSockets create a new one.
- this.createSocket(req, options, handleSocketCreation(this, req, true));
+ this.createSocket(req, options, (err, socket) => {
+ if (err)
+ req.onSocket(socket, err);
+ else
+ setRequestSocket(this, req, socket);
+ });
} else {
debug('wait for socket');
// We are over limit so we'll add it to the queue.
@@ -393,8 +393,12 @@ Agent.prototype.removeSocket = function removeSocket(s, options) {
debug('removeSocket, have a request, make a socket');
const req = this.requests[name][0];
// If we have pending requests and a socket gets closed make a new one
- const socketCreationHandler = handleSocketCreation(this, req, false);
- this.createSocket(req, options, socketCreationHandler);
+ this.createSocket(req, options, (err, socket) => {
+ if (err)
+ req.onSocket(socket, err);
+ else
+ socket.emit('free');
+ });
}
};
@@ -402,6 +406,11 @@ Agent.prototype.keepSocketAlive = function keepSocketAlive(socket) {
socket.setKeepAlive(true, this.keepAliveMsecs);
socket.unref();
+ const agentTimeout = this.options.timeout || 0;
+ if (socket.timeout !== agentTimeout) {
+ socket.setTimeout(agentTimeout);
+ }
+
return true;
};
@@ -422,19 +431,6 @@ Agent.prototype.destroy = function destroy() {
}
};
-function handleSocketCreation(agent, request, informRequest) {
- return function handleSocketCreation_Inner(err, socket) {
- if (err) {
- process.nextTick(emitErrorNT, request, err);
- return;
- }
- if (informRequest)
- setRequestSocket(agent, request, socket);
- else
- socket.emit('free');
- };
-}
-
function setRequestSocket(agent, req, socket) {
req.onSocket(socket);
const agentTimeout = agent.options.timeout || 0;
@@ -444,10 +440,6 @@ function setRequestSocket(agent, req, socket) {
socket.setTimeout(req.timeout);
}
-function emitErrorNT(emitter, err) {
- emitter.emit('error', err);
-}
-
module.exports = {
Agent,
globalAgent: new Agent()
diff --git a/lib/_http_client.js b/lib/_http_client.js
index 73a6409c411cc1..c78289138e756a 100644
--- a/lib/_http_client.js
+++ b/lib/_http_client.js
@@ -371,10 +371,12 @@ function _destroy(req, socket, err) {
// TODO (ronag): Check if socket was used at all (e.g. headersSent) and
// re-use it in that case. `req.socket` just checks whether the socket was
// assigned to the request and *might* have been used.
- if (!req.agent || req.socket) {
+ if (socket && (!req.agent || req.socket)) {
socket.destroy(err);
} else {
- socket.emit('free');
+ if (socket) {
+ socket.emit('free');
+ }
if (!req.aborted && !err) {
err = connResetException('socket hang up');
}
@@ -776,15 +778,18 @@ function listenSocketTimeout(req) {
}
}
-ClientRequest.prototype.onSocket = function onSocket(socket) {
+ClientRequest.prototype.onSocket = function onSocket(socket, err) {
// TODO(ronag): Between here and onSocketNT the socket
// has no 'error' handler.
- process.nextTick(onSocketNT, this, socket);
+ process.nextTick(onSocketNT, this, socket, err);
};
-function onSocketNT(req, socket) {
+function onSocketNT(req, socket, err) {
if (req.destroyed) {
_destroy(req, socket, req[kError]);
+ } else if (err) {
+ req.destroyed = true;
+ _destroy(req, null, err);
} else {
tickOnSocket(req, socket);
}
diff --git a/lib/_http_outgoing.js b/lib/_http_outgoing.js
index a22621ea4319dc..5fd462b0194387 100644
--- a/lib/_http_outgoing.js
+++ b/lib/_http_outgoing.js
@@ -304,19 +304,11 @@ OutgoingMessage.prototype._send = function _send(data, encoding, callback) {
data = this._header + data;
} else {
const header = this._header;
- if (this.outputData.length === 0) {
- this.outputData = [{
- data: header,
- encoding: 'latin1',
- callback: null
- }];
- } else {
- this.outputData.unshift({
- data: header,
- encoding: 'latin1',
- callback: null
- });
- }
+ this.outputData.unshift({
+ data: header,
+ encoding: 'latin1',
+ callback: null
+ });
this.outputSize += header.length;
this._onPendingData(header.length);
}
diff --git a/lib/_stream_duplex.js b/lib/_stream_duplex.js
index 07a16175ffcd73..ced361c8d60898 100644
--- a/lib/_stream_duplex.js
+++ b/lib/_stream_duplex.js
@@ -71,7 +71,20 @@ function Duplex(options) {
}
ObjectDefineProperties(Duplex.prototype, {
- writable: ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writable'),
+ writable:
+ ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writable'),
+ writableHighWaterMark:
+ ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writableHighWaterMark'),
+ writableBuffer:
+ ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writableBuffer'),
+ writableLength:
+ ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writableLength'),
+ writableFinished:
+ ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writableFinished'),
+ writableCorked:
+ ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writableCorked'),
+ writableEnded:
+ ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writableEnded'),
destroyed: {
get() {
@@ -89,41 +102,5 @@ ObjectDefineProperties(Duplex.prototype, {
this._writableState.destroyed = value;
}
}
- },
-
- writableHighWaterMark: {
- get() {
- return this._writableState && this._writableState.highWaterMark;
- }
- },
-
- writableBuffer: {
- get() {
- return this._writableState && this._writableState.getBuffer();
- }
- },
-
- writableLength: {
- get() {
- return this._writableState && this._writableState.length;
- }
- },
-
- writableFinished: {
- get() {
- return this._writableState ? this._writableState.finished : false;
- }
- },
-
- writableCorked: {
- get() {
- return this._writableState ? this._writableState.corked : 0;
- }
- },
-
- writableEnded: {
- get() {
- return this._writableState ? this._writableState.ending : false;
- }
}
});
diff --git a/lib/_stream_readable.js b/lib/_stream_readable.js
index ec9f86d7eacf4a..f39c49867805d3 100644
--- a/lib/_stream_readable.js
+++ b/lib/_stream_readable.js
@@ -230,13 +230,15 @@ function readableAddChunk(stream, chunk, encoding, addToFront) {
if (!state.objectMode) {
if (typeof chunk === 'string') {
encoding = encoding || state.defaultEncoding;
- if (addToFront && state.encoding && state.encoding !== encoding) {
- // When unshifting, if state.encoding is set, we have to save
- // the string in the BufferList with the state encoding.
- chunk = Buffer.from(chunk, encoding).toString(state.encoding);
- } else if (encoding !== state.encoding) {
- chunk = Buffer.from(chunk, encoding);
- encoding = '';
+ if (state.encoding !== encoding) {
+ if (addToFront && state.encoding) {
+ // When unshifting, if state.encoding is set, we have to save
+ // the string in the BufferList with the state encoding.
+ chunk = Buffer.from(chunk, encoding).toString(state.encoding);
+ } else {
+ chunk = Buffer.from(chunk, encoding);
+ encoding = '';
+ }
}
} else if (chunk instanceof Buffer) {
encoding = '';
diff --git a/lib/assert.js b/lib/assert.js
index 45c4644279de33..6ad672d698602d 100644
--- a/lib/assert.js
+++ b/lib/assert.js
@@ -51,6 +51,7 @@ const { NativeModule } = require('internal/bootstrap/loaders');
const { isError } = require('internal/util');
const errorCache = new Map();
+const CallTracker = require('internal/assert/calltracker');
let isDeepEqual;
let isDeepStrictEqual;
@@ -928,6 +929,8 @@ assert.doesNotMatch = function doesNotMatch(string, regexp, message) {
internalMatch(string, regexp, message, doesNotMatch);
};
+assert.CallTracker = CallTracker;
+
// Expose a strict only variant of assert
function strict(...args) {
innerOk(strict, args.length, ...args);
diff --git a/lib/internal/assert/assertion_error.js b/lib/internal/assert/assertion_error.js
index 691f057ad281e3..3c2150c69e4e09 100644
--- a/lib/internal/assert/assertion_error.js
+++ b/lib/internal/assert/assertion_error.js
@@ -312,6 +312,7 @@ class AssertionError extends Error {
message,
operator,
stackStartFn,
+ details,
// Compatibility with older versions.
stackStartFunction
} = options;
@@ -426,9 +427,22 @@ class AssertionError extends Error {
configurable: true
});
this.code = 'ERR_ASSERTION';
- this.actual = actual;
- this.expected = expected;
- this.operator = operator;
+ if (details) {
+ this.actual = undefined;
+ this.expected = undefined;
+ this.operator = undefined;
+ for (let i = 0; i < details.length; i++) {
+ this['message ' + i] = details[i].message;
+ this['actual ' + i] = details[i].actual;
+ this['expected ' + i] = details[i].expected;
+ this['operator ' + i] = details[i].operator;
+ this['stack trace ' + i] = details[i].stack;
+ }
+ } else {
+ this.actual = actual;
+ this.expected = expected;
+ this.operator = operator;
+ }
// eslint-disable-next-line no-restricted-syntax
Error.captureStackTrace(this, stackStartFn || stackStartFunction);
// Create error message including the error code in the name.
diff --git a/lib/internal/assert/calltracker.js b/lib/internal/assert/calltracker.js
new file mode 100644
index 00000000000000..74f517f3f9e99b
--- /dev/null
+++ b/lib/internal/assert/calltracker.js
@@ -0,0 +1,93 @@
+'use strict';
+
+const {
+ Error,
+ SafeSet,
+} = primordials;
+
+const {
+ codes: {
+ ERR_UNAVAILABLE_DURING_EXIT,
+ },
+} = require('internal/errors');
+const AssertionError = require('internal/assert/assertion_error');
+const {
+ validateUint32,
+} = require('internal/validators');
+
+const noop = () => {};
+
+class CallTracker {
+
+ #callChecks = new SafeSet()
+
+ calls(fn, exact = 1) {
+ if (process._exiting)
+ throw new ERR_UNAVAILABLE_DURING_EXIT();
+ if (typeof fn === 'number') {
+ exact = fn;
+ fn = noop;
+ } else if (fn === undefined) {
+ fn = noop;
+ }
+
+ validateUint32(exact, 'exact', true);
+
+ const context = {
+ exact,
+ actual: 0,
+ // eslint-disable-next-line no-restricted-syntax
+ stackTrace: new Error(),
+ name: fn.name || 'calls'
+ };
+ const callChecks = this.#callChecks;
+ callChecks.add(context);
+
+ return function() {
+ context.actual++;
+ if (context.actual === context.exact) {
+ // Once function has reached its call count remove it from
+ // callChecks set to prevent memory leaks.
+ callChecks.delete(context);
+ }
+ // If function has been called more than expected times, add back into
+ // callchecks.
+ if (context.actual === context.exact + 1) {
+ callChecks.add(context);
+ }
+ return fn.apply(this, arguments);
+ };
+ }
+
+ report() {
+ const errors = [];
+ for (const context of this.#callChecks) {
+ // If functions have not been called exact times
+ if (context.actual !== context.exact) {
+ const message = `Expected the ${context.name} function to be ` +
+ `executed ${context.exact} time(s) but was ` +
+ `executed ${context.actual} time(s).`;
+ errors.push({
+ message,
+ actual: context.actual,
+ expected: context.exact,
+ operator: context.name,
+ stack: context.stackTrace
+ });
+ }
+ }
+ return errors;
+ }
+
+ verify() {
+ const errors = this.report();
+ if (errors.length > 0) {
+ throw new AssertionError({
+ message: 'Function(s) were not called the expected number of times',
+ details: errors,
+ });
+ }
+ }
+}
+
+module.exports = CallTracker;
diff --git a/lib/internal/cluster/child.js b/lib/internal/cluster/child.js
index 250a82ecabaa34..74f30c0d2ece90 100644
--- a/lib/internal/cluster/child.js
+++ b/lib/internal/cluster/child.js
@@ -228,16 +228,23 @@ function _disconnect(masterInitiated) {
// Extend generic Worker with methods specific to worker processes.
Worker.prototype.disconnect = function() {
- _disconnect.call(this);
+ if (![ 'disconnecting', 'destroying' ].includes(this.state)) {
+ this.state = 'disconnecting';
+ _disconnect.call(this);
+ }
+
return this;
};
Worker.prototype.destroy = function() {
- this.exitedAfterDisconnect = true;
+ if (this.state === 'destroying')
+ return;
+ this.exitedAfterDisconnect = true;
if (!this.isConnected()) {
process.exit(0);
} else {
+ this.state = 'destroying';
send({ act: 'exitedAfterDisconnect' }, () => process.disconnect());
process.once('disconnect', () => process.exit(0));
}
diff --git a/lib/internal/console/constructor.js b/lib/internal/console/constructor.js
index dc8dcaa1d054ed..c5195c77c6e169 100644
--- a/lib/internal/console/constructor.js
+++ b/lib/internal/console/constructor.js
@@ -32,6 +32,7 @@ const {
ERR_INCOMPATIBLE_OPTION_PAIR,
},
} = require('internal/errors');
+const { validateInteger } = require('internal/validators');
const { previewEntries } = internalBinding('util');
const { Buffer: { isBuffer } } = require('buffer');
const {
@@ -52,12 +53,14 @@ const kTraceInstant = 'n'.charCodeAt(0);
const kSecond = 1000;
const kMinute = 60 * kSecond;
const kHour = 60 * kMinute;
+const kMaxGroupIndentation = 1000;
// Lazy loaded for startup performance.
let cliTable;
// Track amount of indentation required via `console.group()`.
const kGroupIndent = Symbol('kGroupIndent');
+const kGroupIndentationWidth = Symbol('kGroupIndentWidth');
const kFormatForStderr = Symbol('kFormatForStderr');
const kFormatForStdout = Symbol('kFormatForStdout');
const kGetInspectOptions = Symbol('kGetInspectOptions');
@@ -93,7 +96,8 @@ function Console(options /* or: stdout, stderr, ignoreErrors = true */) {
stderr = stdout,
ignoreErrors = true,
colorMode = 'auto',
- inspectOptions
+ inspectOptions,
+ groupIndentation,
} = options;
if (!stdout || typeof stdout.write !== 'function') {
@@ -106,6 +110,11 @@ function Console(options /* or: stdout, stderr, ignoreErrors = true */) {
if (typeof colorMode !== 'boolean' && colorMode !== 'auto')
throw new ERR_INVALID_ARG_VALUE('colorMode', colorMode);
+ if (groupIndentation !== undefined) {
+ validateInteger(groupIndentation, 'groupIndentation',
+ 0, kMaxGroupIndentation);
+ }
+
if (typeof inspectOptions === 'object' && inspectOptions !== null) {
if (inspectOptions.colors !== undefined &&
options.colorMode !== undefined) {
@@ -130,7 +139,7 @@ function Console(options /* or: stdout, stderr, ignoreErrors = true */) {
}
this[kBindStreamsEager](stdout, stderr);
- this[kBindProperties](ignoreErrors, colorMode);
+ this[kBindProperties](ignoreErrors, colorMode, groupIndentation);
}
const consolePropAttributes = {
@@ -181,7 +190,8 @@ Console.prototype[kBindStreamsLazy] = function(object) {
});
};
-Console.prototype[kBindProperties] = function(ignoreErrors, colorMode) {
+Console.prototype[kBindProperties] = function(ignoreErrors, colorMode,
+ groupIndentation = 2) {
ObjectDefineProperties(this, {
'_stdoutErrorHandler': {
...consolePropAttributes,
@@ -200,7 +210,11 @@ Console.prototype[kBindProperties] = function(ignoreErrors, colorMode) {
[kCounts]: { ...consolePropAttributes, value: new Map() },
[kColorMode]: { ...consolePropAttributes, value: colorMode },
[kIsConsole]: { ...consolePropAttributes, value: true },
- [kGroupIndent]: { ...consolePropAttributes, value: '' }
+ [kGroupIndent]: { ...consolePropAttributes, value: '' },
+ [kGroupIndentationWidth]: {
+ ...consolePropAttributes,
+ value: groupIndentation
+ },
});
};
@@ -403,12 +417,13 @@ const consoleMethods = {
if (data.length > 0) {
this.log(...data);
}
- this[kGroupIndent] += ' ';
+ this[kGroupIndent] += ' '.repeat(this[kGroupIndentationWidth]);
},
groupEnd() {
this[kGroupIndent] =
- this[kGroupIndent].slice(0, this[kGroupIndent].length - 2);
+ this[kGroupIndent].slice(0, this[kGroupIndent].length -
+ this[kGroupIndentationWidth]);
},
// https://console.spec.whatwg.org/#table
diff --git a/lib/internal/crypto/diffiehellman.js b/lib/internal/crypto/diffiehellman.js
index ae6b68b73b5767..8f86911757fe1f 100644
--- a/lib/internal/crypto/diffiehellman.js
+++ b/lib/internal/crypto/diffiehellman.js
@@ -14,7 +14,10 @@ const {
ERR_INVALID_ARG_TYPE,
ERR_INVALID_OPT_VALUE
} = require('internal/errors').codes;
-const { validateString } = require('internal/validators');
+const {
+ validateString,
+ validateInt32,
+} = require('internal/validators');
const { isArrayBufferView } = require('internal/util/types');
const { KeyObject } = require('internal/crypto/keys');
const {
@@ -51,6 +54,13 @@ function DiffieHellman(sizeOrKey, keyEncoding, generator, genEncoding) {
);
}
+ // Sizes < 0 don't make sense but they _are_ accepted (and subsequently
+ // rejected with ERR_OSSL_BN_BITS_TOO_SMALL) by OpenSSL. The glue code
+ // in node_crypto.cc accepts values that are IsInt32() for that reason
+ // and that's why we do that here too.
+ if (typeof sizeOrKey === 'number')
+ validateInt32(sizeOrKey, 'sizeOrKey');
+
if (keyEncoding && !Buffer.isEncoding(keyEncoding) &&
keyEncoding !== 'buffer') {
genEncoding = generator;
@@ -67,7 +77,9 @@ function DiffieHellman(sizeOrKey, keyEncoding, generator, genEncoding) {
if (!generator)
generator = DH_GENERATOR;
- else if (typeof generator !== 'number')
+ else if (typeof generator === 'number')
+ validateInt32(generator, 'generator');
+ else
generator = toBuf(generator, genEncoding);
this[kHandle] = new _DiffieHellman(sizeOrKey, generator);
diff --git a/lib/internal/errors.js b/lib/internal/errors.js
index e9e11bf0fb195b..c4e9fb3d59b4a3 100644
--- a/lib/internal/errors.js
+++ b/lib/internal/errors.js
@@ -1318,8 +1318,12 @@ E('ERR_SERVER_NOT_RUNNING', 'Server is not running.', Error);
E('ERR_SOCKET_ALREADY_BOUND', 'Socket is already bound', Error);
E('ERR_SOCKET_BAD_BUFFER_SIZE',
'Buffer size must be a positive integer', TypeError);
-E('ERR_SOCKET_BAD_PORT',
- '%s should be >= 0 and < 65536. Received %s.', RangeError);
+E('ERR_SOCKET_BAD_PORT', (name, port, allowZero = true) => {
+ assert(typeof allowZero === 'boolean',
+ "The 'allowZero' argument must be of type boolean.");
+ const operator = allowZero ? '>=' : '>';
+ return `${name} should be ${operator} 0 and < 65536. Received ${port}.`;
+}, RangeError);
E('ERR_SOCKET_BAD_TYPE',
'Bad socket type specified. Valid types are: udp4, udp6', TypeError);
E('ERR_SOCKET_BUFFER_SIZE',
@@ -1380,6 +1384,8 @@ E('ERR_TRANSFORM_ALREADY_TRANSFORMING',
E('ERR_TRANSFORM_WITH_LENGTH_0',
'Calling transform done when writableState.length != 0', Error);
E('ERR_TTY_INIT_FAILED', 'TTY initialization failed', SystemError);
+E('ERR_UNAVAILABLE_DURING_EXIT', 'Cannot call function in process exit ' +
+ 'handler', Error);
E('ERR_UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET',
'`process.setupUncaughtExceptionCapture()` was called while a capture ' +
'callback was already active',
diff --git a/lib/internal/modules/cjs/helpers.js b/lib/internal/modules/cjs/helpers.js
index 2d73219a77772c..2f62c8623e27be 100644
--- a/lib/internal/modules/cjs/helpers.js
+++ b/lib/internal/modules/cjs/helpers.js
@@ -110,11 +110,39 @@ function stripBOM(content) {
}
const builtinLibs = [
- 'assert', 'async_hooks', 'buffer', 'child_process', 'cluster', 'crypto',
- 'dgram', 'dns', 'domain', 'events', 'fs', 'http', 'http2', 'https', 'net',
- 'os', 'path', 'perf_hooks', 'punycode', 'querystring', 'readline', 'repl',
- 'stream', 'string_decoder', 'tls', 'trace_events', 'tty', 'url', 'util',
- 'v8', 'vm', 'worker_threads', 'zlib'
+ 'assert',
+ 'async_hooks',
+ 'buffer',
+ 'child_process',
+ 'cluster',
+ 'crypto',
+ 'dgram',
+ 'dns',
+ 'domain',
+ 'events',
+ 'fs',
+ 'http',
+ 'http2',
+ 'https',
+ 'net',
+ 'os',
+ 'path',
+ 'perf_hooks',
+ 'punycode',
+ 'querystring',
+ 'readline',
+ 'repl',
+ 'stream',
+ 'string_decoder',
+ 'tls',
+ 'trace_events',
+ 'tty',
+ 'url',
+ 'util',
+ 'v8',
+ 'vm',
+ 'worker_threads',
+ 'zlib',
];
if (internalBinding('config').experimentalWasi) {
diff --git a/lib/internal/modules/esm/resolve.js b/lib/internal/modules/esm/resolve.js
index 5bb2f37e53eeb5..c66e0554ad2746 100644
--- a/lib/internal/modules/esm/resolve.js
+++ b/lib/internal/modules/esm/resolve.js
@@ -2,17 +2,22 @@
const {
ArrayIsArray,
+ ArrayPrototypeJoin,
+ ArrayPrototypeShift,
JSONParse,
JSONStringify,
ObjectFreeze,
ObjectGetOwnPropertyNames,
ObjectPrototypeHasOwnProperty,
+ RegExp,
SafeMap,
SafeSet,
StringPrototypeEndsWith,
StringPrototypeIncludes,
StringPrototypeIndexOf,
+ StringPrototypeReplace,
StringPrototypeSlice,
+ StringPrototypeSplit,
StringPrototypeStartsWith,
StringPrototypeSubstr,
} = primordials;
@@ -29,8 +34,8 @@ const {
Stats,
} = require('fs');
const { getOptionValue } = require('internal/options');
-const { sep } = require('path');
-
+const { sep, relative } = require('path');
+const { Module: CJSModule } = require('internal/modules/cjs/loader');
const preserveSymlinks = getOptionValue('--preserve-symlinks');
const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
const typeFlag = getOptionValue('--input-type');
@@ -439,11 +444,6 @@ function packageMainResolve(packageJSONUrl, packageConfig, base, conditions) {
throw new ERR_PACKAGE_PATH_NOT_EXPORTED(packageJSONUrl, '.');
}
- if (packageConfig.main !== undefined) {
- const resolved = new URL(packageConfig.main, packageJSONUrl);
- const path = fileURLToPath(resolved);
- if (tryStatSync(path).isFile()) return resolved;
- }
if (getOptionValue('--experimental-specifier-resolution') === 'node') {
if (packageConfig.main !== undefined) {
return finalizeResolution(
@@ -453,9 +453,7 @@ function packageMainResolve(packageJSONUrl, packageConfig, base, conditions) {
new URL('index', packageJSONUrl), base);
}
}
- if (packageConfig.type !== 'module') {
- return legacyMainResolve(packageJSONUrl, packageConfig);
- }
+ return legacyMainResolve(packageJSONUrl, packageConfig);
}
throw new ERR_MODULE_NOT_FOUND(
@@ -618,9 +616,11 @@ function packageResolve(specifier, base, conditions) {
throw new ERR_MODULE_NOT_FOUND(packageName, fileURLToPath(base));
}
-function shouldBeTreatedAsRelativeOrAbsolutePath(specifier) {
- if (specifier === '') return false;
- if (specifier[0] === '/') return true;
+function isBareSpecifier(specifier) {
+ return specifier[0] && specifier[0] !== '/' && specifier[0] !== '.';
+}
+
+function isRelativeSpecifier(specifier) {
if (specifier[0] === '.') {
if (specifier.length === 1 || specifier[1] === '/') return true;
if (specifier[1] === '.') {
@@ -630,6 +630,12 @@ function shouldBeTreatedAsRelativeOrAbsolutePath(specifier) {
return false;
}
+function shouldBeTreatedAsRelativeOrAbsolutePath(specifier) {
+ if (specifier === '') return false;
+ if (specifier[0] === '/') return true;
+ return isRelativeSpecifier(specifier);
+}
+
/**
* @param {string} specifier
* @param {URL} base
@@ -652,6 +658,51 @@ function moduleResolve(specifier, base, conditions) {
return finalizeResolution(resolved, base);
}
+/**
+ * Try to resolve an import as a CommonJS module
+ * @param {string} specifier
+ * @param {string} parentURL
+ * @returns {boolean|string}
+ */
+function resolveAsCommonJS(specifier, parentURL) {
+ try {
+ const parent = fileURLToPath(parentURL);
+ const tmpModule = new CJSModule(parent, null);
+ tmpModule.paths = CJSModule._nodeModulePaths(parent);
+
+ let found = CJSModule._resolveFilename(specifier, tmpModule, false);
+
+ // If it is a relative specifier return the relative path
+ // to the parent
+ if (isRelativeSpecifier(specifier)) {
+ found = relative(parent, found);
+ // Add '.separator if the path does not start with '..separator'
+ // This should be a safe assumption because when loading
+ // esm modules there should be always a file specified so
+ // there should not be a specifier like '..' or '.'
+ if (!StringPrototypeStartsWith(found, `..${sep}`)) {
+ found = `.${sep}${found}`;
+ }
+ } else if (isBareSpecifier(specifier)) {
+ // If it is a bare specifier return the relative path within the
+ // module
+ const pkg = StringPrototypeSplit(specifier, '/')[0];
+ const index = StringPrototypeIndexOf(found, pkg);
+ if (index !== -1) {
+ found = StringPrototypeSlice(found, index);
+ }
+ }
+ // Normalize the path separator to give a valid suggestion
+ // on Windows
+ if (process.platform === 'win32') {
+ found = StringPrototypeReplace(found, new RegExp(`\\${sep}`, 'g'), '/');
+ }
+ return found;
+ } catch {
+ return false;
+ }
+}
+
function defaultResolve(specifier, context = {}, defaultResolveUnused) {
let { parentURL, conditions } = context;
let parsed;
@@ -692,7 +743,27 @@ function defaultResolve(specifier, context = {}, defaultResolveUnused) {
}
conditions = getConditionsSet(conditions);
- let url = moduleResolve(specifier, parentURL, conditions);
+ let url;
+ try {
+ url = moduleResolve(specifier, parentURL, conditions);
+ } catch (error) {
+ // Try to give the user a hint of what would have been the
+ // resolved CommonJS module
+ if (error.code === 'ERR_MODULE_NOT_FOUND') {
+ const found = resolveAsCommonJS(specifier, parentURL);
+ if (found) {
+ // Modify the stack and message string to include the hint
+ const lines = StringPrototypeSplit(error.stack, '\n');
+ const hint = `Did you mean to import ${found}?`;
+ error.stack =
+ ArrayPrototypeShift(lines) + '\n' +
+ hint + '\n' +
+ ArrayPrototypeJoin(lines, '\n');
+ error.message += `\n${hint}`;
+ }
+ }
+ throw error;
+ }
if (isMain ? !preserveSymlinksMain : !preserveSymlinks) {
const urlPath = fileURLToPath(url);
diff --git a/lib/internal/streams/end-of-stream.js b/lib/internal/streams/end-of-stream.js
index 7d5689ddadb7fc..bc2d0ccfcaf142 100644
--- a/lib/internal/streams/end-of-stream.js
+++ b/lib/internal/streams/end-of-stream.js
@@ -147,10 +147,17 @@ function eos(stream, opts, callback) {
if (opts.error !== false) stream.on('error', onerror);
stream.on('close', onclose);
- const closed = (wState && wState.closed) || (rState && rState.closed) ||
- (wState && wState.errorEmitted) || (rState && rState.errorEmitted) ||
- (wState && wState.finished) || (rState && rState.endEmitted) ||
- (rState && stream.req && stream.aborted);
+ const closed = (
+ (wState && wState.closed) ||
+ (rState && rState.closed) ||
+ (wState && wState.errorEmitted) ||
+ (rState && rState.errorEmitted) ||
+ (rState && stream.req && stream.aborted) ||
+ (
+ (!writable || (wState && wState.finished)) &&
+ (!readable || (rState && rState.endEmitted))
+ )
+ );
if (closed) {
// TODO(ronag): Re-throw error if errorEmitted?
@@ -158,6 +165,7 @@ function eos(stream, opts, callback) {
// before being closed? i.e. if closed but not errored, ended or finished.
// TODO(ronag): Throw some kind of error? Does it make sense
// to call finished() on a "finished" stream?
+ // TODO(ronag): willEmitClose?
process.nextTick(() => {
callback();
});
diff --git a/lib/internal/streams/from.js b/lib/internal/streams/from.js
index ca567914bbf0fe..6752679ae3bc2b 100644
--- a/lib/internal/streams/from.js
+++ b/lib/internal/streams/from.js
@@ -7,7 +7,8 @@ const {
const { Buffer } = require('buffer');
const {
- ERR_INVALID_ARG_TYPE
+ ERR_INVALID_ARG_TYPE,
+ ERR_STREAM_NULL_VALUES
} = require('internal/errors').codes;
function from(Readable, iterable, opts) {
@@ -73,15 +74,20 @@ function from(Readable, iterable, opts) {
needToClose = false;
const { value, done } = await iterator.next();
needToClose = !done;
- const resolved = await value;
if (done) {
readable.push(null);
} else if (readable.destroyed) {
await close();
- } else if (readable.push(resolved)) {
- next();
} else {
- reading = false;
+ const res = await value;
+ if (res === null) {
+ reading = false;
+ throw new ERR_STREAM_NULL_VALUES();
+ } else if (readable.push(res)) {
+ next();
+ } else {
+ reading = false;
+ }
}
} catch (err) {
readable.destroy(err);
diff --git a/lib/internal/util/comparisons.js b/lib/internal/util/comparisons.js
index 3cc593ae27f403..9d4cdf9f183f16 100644
--- a/lib/internal/util/comparisons.js
+++ b/lib/internal/util/comparisons.js
@@ -147,7 +147,7 @@ function isIdenticalTypedArrayType(a, b) {
return check(b);
}
}
- /* c8 ignore next */
+ /* c8 ignore next 4 */
assert.fail(
`Unknown TypedArray type checking ${a[SymbolToStringTag]} ${a}\n` +
`and ${b[SymbolToStringTag]} ${b}`
diff --git a/lib/internal/validators.js b/lib/internal/validators.js
index 790c923386fa91..deab53d4b8221e 100644
--- a/lib/internal/validators.js
+++ b/lib/internal/validators.js
@@ -190,7 +190,7 @@ function validatePort(port, name = 'Port', { allowZero = true } = {}) {
+port !== (+port >>> 0) ||
port > 0xFFFF ||
(port === 0 && !allowZero)) {
- throw new ERR_SOCKET_BAD_PORT(name, port);
+ throw new ERR_SOCKET_BAD_PORT(name, port, allowZero);
}
return port | 0;
}
diff --git a/lib/internal/vm/module.js b/lib/internal/vm/module.js
index ed0dedd1e31b4f..992753ef680dbb 100644
--- a/lib/internal/vm/module.js
+++ b/lib/internal/vm/module.js
@@ -22,6 +22,7 @@ const {
} = require('internal/util');
const {
ERR_INVALID_ARG_TYPE,
+ ERR_INVALID_ARG_VALUE,
ERR_VM_MODULE_ALREADY_LINKED,
ERR_VM_MODULE_DIFFERENT_CONTEXT,
ERR_VM_MODULE_CANNOT_CREATE_CACHED_DATA,
@@ -379,8 +380,17 @@ class SyntheticModule extends Module {
constructor(exportNames, evaluateCallback, options = {}) {
if (!ArrayIsArray(exportNames) ||
exportNames.some((e) => typeof e !== 'string')) {
- throw new ERR_INVALID_ARG_TYPE('exportNames', 'Array of strings',
+ throw new ERR_INVALID_ARG_TYPE('exportNames',
+ 'Array of unique strings',
exportNames);
+ } else {
+ exportNames.forEach((name, i) => {
+ if (exportNames.indexOf(name, i + 1) !== -1) {
+ throw new ERR_INVALID_ARG_VALUE(`exportNames.${name}`,
+ name,
+ 'is duplicated');
+ }
+ });
}
if (typeof evaluateCallback !== 'function') {
throw new ERR_INVALID_ARG_TYPE('evaluateCallback', 'function',
diff --git a/lib/internal/worker.js b/lib/internal/worker.js
index 5f92a7898e93a9..48886aee3fef5d 100644
--- a/lib/internal/worker.js
+++ b/lib/internal/worker.js
@@ -54,6 +54,7 @@ const {
kMaxYoungGenerationSizeMb,
kMaxOldGenerationSizeMb,
kCodeRangeSizeMb,
+ kStackSizeMb,
kTotalResourceLimitCount
} = internalBinding('worker');
@@ -379,6 +380,8 @@ function parseResourceLimits(obj) {
ret[kMaxYoungGenerationSizeMb] = obj.maxYoungGenerationSizeMb;
if (typeof obj.codeRangeSizeMb === 'number')
ret[kCodeRangeSizeMb] = obj.codeRangeSizeMb;
+ if (typeof obj.stackSizeMb === 'number')
+ ret[kStackSizeMb] = obj.stackSizeMb;
return ret;
}
@@ -386,7 +389,8 @@ function makeResourceLimits(float64arr) {
return {
maxYoungGenerationSizeMb: float64arr[kMaxYoungGenerationSizeMb],
maxOldGenerationSizeMb: float64arr[kMaxOldGenerationSizeMb],
- codeRangeSizeMb: float64arr[kCodeRangeSizeMb]
+ codeRangeSizeMb: float64arr[kCodeRangeSizeMb],
+ stackSizeMb: float64arr[kStackSizeMb]
};
}
diff --git a/lib/path.js b/lib/path.js
index c5db4437a52408..7532b795bf63f7 100644
--- a/lib/path.js
+++ b/lib/path.js
@@ -385,14 +385,14 @@ const win32 = {
return '.';
// Make sure that the joined path doesn't start with two slashes, because
- // normalize() will mistake it for an UNC path then.
+ // normalize() will mistake it for a UNC path then.
//
// This step is skipped when it is very clear that the user actually
- // intended to point at an UNC path. This is assumed when the first
+ // intended to point at a UNC path. This is assumed when the first
// non-empty string arguments starts with exactly two slashes followed by
// at least one more non-slash character.
//
- // Note that for normalize() to treat a path as an UNC path it needs to
+ // Note that for normalize() to treat a path as a UNC path it needs to
// have at least 2 components, so we don't filter for that here.
// This means that the user can use join to construct UNC paths from
// a server name and a share name; for example:
diff --git a/lib/v8.js b/lib/v8.js
index 5841f204986fd7..11ca7fa640f27b 100644
--- a/lib/v8.js
+++ b/lib/v8.js
@@ -73,12 +73,12 @@ class Deserializer extends _Deserializer { }
const {
cachedDataVersionTag,
setFlagsFromString: _setFlagsFromString,
- heapStatisticsArrayBuffer,
- heapSpaceStatisticsArrayBuffer,
- heapCodeStatisticsArrayBuffer,
- updateHeapStatisticsArrayBuffer,
- updateHeapSpaceStatisticsArrayBuffer,
- updateHeapCodeStatisticsArrayBuffer,
+ heapStatisticsBuffer,
+ heapSpaceStatisticsBuffer,
+ heapCodeStatisticsBuffer,
+ updateHeapStatisticsBuffer,
+ updateHeapSpaceStatisticsBuffer,
+ updateHeapCodeStatisticsBuffer,
// Properties for heap statistics buffer extraction.
kTotalHeapSizeIndex,
@@ -95,7 +95,6 @@ const {
// Properties for heap spaces statistics buffer extraction.
kHeapSpaces,
- kHeapSpaceStatisticsPropertiesCount,
kSpaceSizeIndex,
kSpaceUsedSizeIndex,
kSpaceAvailableSizeIndex,
@@ -109,15 +108,6 @@ const {
const kNumberOfHeapSpaces = kHeapSpaces.length;
-const heapStatisticsBuffer =
- new Float64Array(heapStatisticsArrayBuffer);
-
-const heapSpaceStatisticsBuffer =
- new Float64Array(heapSpaceStatisticsArrayBuffer);
-
-const heapCodeStatisticsBuffer =
- new Float64Array(heapCodeStatisticsArrayBuffer);
-
function setFlagsFromString(flags) {
validateString(flags, 'flags');
_setFlagsFromString(flags);
@@ -126,7 +116,7 @@ function setFlagsFromString(flags) {
function getHeapStatistics() {
const buffer = heapStatisticsBuffer;
- updateHeapStatisticsArrayBuffer();
+ updateHeapStatisticsBuffer();
return {
'total_heap_size': buffer[kTotalHeapSizeIndex],
@@ -146,16 +136,15 @@ function getHeapStatistics() {
function getHeapSpaceStatistics() {
const heapSpaceStatistics = new Array(kNumberOfHeapSpaces);
const buffer = heapSpaceStatisticsBuffer;
- updateHeapSpaceStatisticsArrayBuffer();
for (let i = 0; i < kNumberOfHeapSpaces; i++) {
- const propertyOffset = i * kHeapSpaceStatisticsPropertiesCount;
+ updateHeapSpaceStatisticsBuffer(i);
heapSpaceStatistics[i] = {
space_name: kHeapSpaces[i],
- space_size: buffer[propertyOffset + kSpaceSizeIndex],
- space_used_size: buffer[propertyOffset + kSpaceUsedSizeIndex],
- space_available_size: buffer[propertyOffset + kSpaceAvailableSizeIndex],
- physical_space_size: buffer[propertyOffset + kPhysicalSpaceSizeIndex]
+ space_size: buffer[kSpaceSizeIndex],
+ space_used_size: buffer[kSpaceUsedSizeIndex],
+ space_available_size: buffer[kSpaceAvailableSizeIndex],
+ physical_space_size: buffer[kPhysicalSpaceSizeIndex]
};
}
@@ -165,7 +154,7 @@ function getHeapSpaceStatistics() {
function getHeapCodeStatistics() {
const buffer = heapCodeStatisticsBuffer;
- updateHeapCodeStatisticsArrayBuffer();
+ updateHeapCodeStatisticsBuffer();
return {
'code_and_metadata_size': buffer[kCodeAndMetadataSizeIndex],
'bytecode_and_metadata_size': buffer[kBytecodeAndMetadataSizeIndex],
diff --git a/lib/vm.js b/lib/vm.js
index cffca355720dae..fd81e9da8b0515 100644
--- a/lib/vm.js
+++ b/lib/vm.js
@@ -385,20 +385,27 @@ const measureMemoryModes = {
detailed: constants.measureMemory.mode.DETAILED,
};
+const measureMemoryExecutions = {
+ default: constants.measureMemory.execution.DEFAULT,
+ eager: constants.measureMemory.execution.EAGER,
+};
+
function measureMemory(options = {}) {
emitExperimentalWarning('vm.measureMemory');
validateObject(options, 'options');
- const { mode = 'summary', context } = options;
+ const { mode = 'summary', execution = 'default' } = options;
if (mode !== 'summary' && mode !== 'detailed') {
throw new ERR_INVALID_ARG_VALUE(
'options.mode', options.mode,
'must be either \'summary\' or \'detailed\'');
}
- if (context !== undefined &&
- (typeof context !== 'object' || context === null || !_isContext(context))) {
- throw new ERR_INVALID_ARG_TYPE('options.context', 'vm.Context', context);
+ if (execution !== 'default' && execution !== 'eager') {
+ throw new ERR_INVALID_ARG_VALUE(
+ 'options.execution', options.execution,
+ 'must be either \'default\' or \'eager\'');
}
- const result = _measureMemory(measureMemoryModes[mode], context);
+ const result = _measureMemory(measureMemoryModes[mode],
+ measureMemoryExecutions[execution]);
if (result === undefined) {
return PromiseReject(new ERR_CONTEXT_NOT_INITIALIZED());
}
diff --git a/lib/wasi.js b/lib/wasi.js
index a6d90f0f57365f..2de467ff1ac066 100644
--- a/lib/wasi.js
+++ b/lib/wasi.js
@@ -80,7 +80,17 @@ class WASI {
validateObject(exports, 'instance.exports');
- const { memory } = exports;
+ const { _initialize, _start, memory } = exports;
+
+ if (typeof _start !== 'function') {
+ throw new ERR_INVALID_ARG_TYPE(
+ 'instance.exports._start', 'function', _start);
+ }
+
+ if (_initialize !== undefined) {
+ throw new ERR_INVALID_ARG_TYPE(
+ 'instance.exports._initialize', 'undefined', _initialize);
+ }
if (!(memory instanceof WebAssembly.Memory)) {
throw new ERR_INVALID_ARG_TYPE(
@@ -95,10 +105,7 @@ class WASI {
this[kSetMemory](memory);
try {
- if (exports._start)
- exports._start();
- else if (exports.__wasi_unstable_reactor_start)
- exports.__wasi_unstable_reactor_start();
+ exports._start();
} catch (err) {
if (err !== kExitCode) {
throw err;
diff --git a/node.gyp b/node.gyp
index c3ffd2ff2147e2..3dadad15c9e193 100644
--- a/node.gyp
+++ b/node.gyp
@@ -96,6 +96,7 @@
'lib/zlib.js',
'lib/internal/assert.js',
'lib/internal/assert/assertion_error.js',
+ 'lib/internal/assert/calltracker.js',
'lib/internal/async_hooks.js',
'lib/internal/buffer.js',
'lib/internal/cli_table.js',
@@ -375,6 +376,9 @@
'msvs_disabled_warnings!': [4244],
'conditions': [
+ [ 'error_on_warn=="true"', {
+ 'cflags': ['-Werror'],
+ }],
[ 'node_intermediate_lib_type=="static_library" and '
'node_shared=="true" and OS=="aix"', {
# For AIX, shared lib is linked by static lib and .exp. In the
@@ -749,6 +753,9 @@
'msvs_disabled_warnings!': [4244],
'conditions': [
+ [ 'error_on_warn=="true"', {
+ 'cflags': ['-Werror'],
+ }],
[ 'node_builtin_modules_path!=""', {
'defines': [ 'NODE_BUILTIN_MODULES_PATH="<(node_builtin_modules_path)"' ]
}],
diff --git a/src/heap_utils.cc b/src/heap_utils.cc
index efdd68fde9d160..2e979e49e87922 100644
--- a/src/heap_utils.cc
+++ b/src/heap_utils.cc
@@ -237,7 +237,7 @@ class HeapSnapshotStream : public AsyncWrap,
HeapSnapshotStream(
Environment* env,
HeapSnapshotPointer&& snapshot,
- v8::Local obj) :
+ Local |