From 9e9e48bf7e20a425d02c2538da106ae97c0688c8 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Sun, 24 Nov 2019 00:34:36 +0100 Subject: [PATCH 001/180] src: use uv_async_t for WeakRefs Schedule a task on the main event loop, similar to what the HTML spec recommends for browsers. Alternative to https://github.com/nodejs/node/pull/30198 PR-URL: https://github.com/nodejs/node/pull/30616 Reviewed-By: Gus Caplan Reviewed-By: Ben Noordhuis --- src/env-inl.h | 1 + src/env.cc | 31 ++++++++++++++++--- src/env.h | 4 ++- .../parallel/test-finalization-group-error.js | 4 +++ .../test-finalization-group-regular-gc.js | 25 +++++++++++++++ 5 files changed, 59 insertions(+), 6 deletions(-) create mode 100644 test/parallel/test-finalization-group-regular-gc.js diff --git a/src/env-inl.h b/src/env-inl.h index 15b5010deb7c90..d34bd03c7c6896 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -1118,6 +1118,7 @@ void Environment::RemoveCleanupHook(void (*fn)(void*), void* arg) { inline void Environment::RegisterFinalizationGroupForCleanup( v8::Local group) { cleanup_finalization_groups_.emplace_back(isolate(), group); + uv_async_send(&cleanup_finalization_groups_async_); } size_t CleanupHookCallback::Hash::operator()( diff --git a/src/env.cc b/src/env.cc index 5f9a6acb461f97..d907f65c27b892 100644 --- a/src/env.cc +++ b/src/env.cc @@ -460,8 +460,17 @@ void Environment::InitializeLibuv(bool start_profiler_idle_notifier) { // will be recorded with state=IDLE. uv_prepare_init(event_loop(), &idle_prepare_handle_); uv_check_init(event_loop(), &idle_check_handle_); + uv_async_init( + event_loop(), + &cleanup_finalization_groups_async_, + [](uv_async_t* async) { + Environment* env = ContainerOf( + &Environment::cleanup_finalization_groups_async_, async); + env->CleanupFinalizationGroups(); + }); uv_unref(reinterpret_cast(&idle_prepare_handle_)); uv_unref(reinterpret_cast(&idle_check_handle_)); + uv_unref(reinterpret_cast(&cleanup_finalization_groups_async_)); thread_stopper()->Install( this, static_cast(this), [](uv_async_t* handle) { @@ -524,6 +533,10 @@ void Environment::RegisterHandleCleanups() { reinterpret_cast(&idle_check_handle_), close_and_finish, nullptr); + RegisterHandleCleanup( + reinterpret_cast(&cleanup_finalization_groups_async_), + close_and_finish, + nullptr); } void Environment::CleanupHandles() { @@ -1040,19 +1053,27 @@ char* Environment::Reallocate(char* data, size_t old_size, size_t size) { return new_data; } -bool Environment::RunWeakRefCleanup() { +void Environment::RunWeakRefCleanup() { isolate()->ClearKeptObjects(); +} - while (!cleanup_finalization_groups_.empty()) { +void Environment::CleanupFinalizationGroups() { + HandleScope handle_scope(isolate()); + Context::Scope context_scope(context()); + TryCatchScope try_catch(this); + + while (!cleanup_finalization_groups_.empty() && can_call_into_js()) { Local fg = cleanup_finalization_groups_.front().Get(isolate()); cleanup_finalization_groups_.pop_front(); if (!FinalizationGroup::Cleanup(fg).FromMaybe(false)) { - return false; + if (try_catch.HasCaught() && !try_catch.HasTerminated()) + errors::TriggerUncaughtException(isolate(), try_catch); + // Re-schedule the execution of the remainder of the queue. + uv_async_send(&cleanup_finalization_groups_async_); + return; } } - - return true; } void AsyncRequest::Install(Environment* env, void* data, uv_async_cb target) { diff --git a/src/env.h b/src/env.h index 495d92471a336f..027f5c0bacfba1 100644 --- a/src/env.h +++ b/src/env.h @@ -1128,7 +1128,8 @@ class Environment : public MemoryRetainer { void RunAtExitCallbacks(); void RegisterFinalizationGroupForCleanup(v8::Local fg); - bool RunWeakRefCleanup(); + void RunWeakRefCleanup(); + void CleanupFinalizationGroups(); // Strings and private symbols are shared across shared contexts // The getters simply proxy to the per-isolate primitive. @@ -1270,6 +1271,7 @@ class Environment : public MemoryRetainer { uv_idle_t immediate_idle_handle_; uv_prepare_t idle_prepare_handle_; uv_check_t idle_check_handle_; + uv_async_t cleanup_finalization_groups_async_; bool profiler_idle_notifier_started_ = false; AsyncHooks async_hooks_; diff --git a/test/parallel/test-finalization-group-error.js b/test/parallel/test-finalization-group-error.js index 0754370f2d4bfa..0685811f55b7f8 100644 --- a/test/parallel/test-finalization-group-error.js +++ b/test/parallel/test-finalization-group-error.js @@ -18,6 +18,10 @@ setTimeout(() => { name: 'Error', message: 'test', }); + + // Give the callbacks scheduled by global.gc() time to run, as the underlying + // uv_async_t is unref’ed. + setTimeout(() => {}, 200); }, 200); process.on('uncaughtException', common.mustCall()); diff --git a/test/parallel/test-finalization-group-regular-gc.js b/test/parallel/test-finalization-group-regular-gc.js new file mode 100644 index 00000000000000..7a4a4797eadcf0 --- /dev/null +++ b/test/parallel/test-finalization-group-regular-gc.js @@ -0,0 +1,25 @@ +// Flags: --harmony-weak-refs +'use strict'; +require('../common'); +const assert = require('assert'); + +// Test that finalization callbacks do not crash when caused through a regular +// GC (not global.gc()). + +const start = Date.now(); +const g = new globalThis.FinalizationGroup(() => { + const diff = Date.now() - start; + assert(diff < 10000, `${diff} >= 10000`); +}); +g.register({}, 42); + +setImmediate(() => { + const arr = []; + // Build up enough memory usage to hopefully trigger a platform task but not + // enough to trigger GC as an interrupt. + while (arr.length < 1000000) arr.push([]); + + setTimeout(() => { + g; // Keep reference alive. + }, 200000).unref(); +}); From 55c2ac70b7ff3c9ae0d97368ea27348a70031dbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Thu, 21 Nov 2019 11:46:31 -0400 Subject: [PATCH 002/180] crypto: remove redundant validateUint32 argument The third parameter should be a boolean indicating whether the number must be positive. Passing zero works, but is unnecessary, misleading and inconsistent with other uses of the same function. PR-URL: https://github.com/nodejs/node/pull/30579 Reviewed-By: Colin Ihrig Reviewed-By: Michael Dawson Reviewed-By: Luigi Pinca --- lib/internal/crypto/pbkdf2.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/crypto/pbkdf2.js b/lib/internal/crypto/pbkdf2.js index 1c2288b65f1a25..20f6f682254036 100644 --- a/lib/internal/crypto/pbkdf2.js +++ b/lib/internal/crypto/pbkdf2.js @@ -66,8 +66,8 @@ function check(password, salt, iterations, keylen, digest) { password = getArrayBufferView(password, 'password'); salt = getArrayBufferView(salt, 'salt'); - validateUint32(iterations, 'iterations', 0); - validateUint32(keylen, 'keylen', 0); + validateUint32(iterations, 'iterations'); + validateUint32(keylen, 'keylen'); return { password, salt, iterations, keylen, digest }; } From 20ee4997f31d4d86362b37a790505ecd86ef0572 Mon Sep 17 00:00:00 2001 From: Taylor Gagne Date: Mon, 18 Nov 2019 15:13:54 -0500 Subject: [PATCH 003/180] test: refactor test-dgram-multicast-set-interface-lo.js Convert functions to arrow functions. Remove unused param('signal') from function. PR-URL: https://github.com/nodejs/node/pull/30536 Reviewed-By: Rich Trott Reviewed-By: Anna Henningsen --- .../test-dgram-multicast-set-interface-lo.js | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/test/internet/test-dgram-multicast-set-interface-lo.js b/test/internet/test-dgram-multicast-set-interface-lo.js index 73ab31884d5eb6..e06969b7b23673 100644 --- a/test/internet/test-dgram-multicast-set-interface-lo.js +++ b/test/internet/test-dgram-multicast-set-interface-lo.js @@ -83,8 +83,14 @@ if (process.argv[2] !== 'child') { let i = 0; let done = 0; let timer = null; + + const killSubprocesses = (subprocesses) => { + for (const i in subprocesses) + subprocesses[i].kill(); + }; + // Exit the test if it doesn't succeed within the TIMEOUT. - timer = setTimeout(function() { + timer = setTimeout(() => { console.error('[PARENT] Responses were not received within %d ms.', TIMEOUT); console.error('[PARENT] Skip'); @@ -115,7 +121,7 @@ if (process.argv[2] !== 'child') { worker.messagesNeeded = messagesNeeded; // Handle the death of workers. - worker.on('exit', function(code, signal) { + worker.on('exit', (code) => { // Don't consider this a true death if the worker has finished // successfully or if the exit code is 0. if (worker.isDone || code === 0) { @@ -138,7 +144,7 @@ if (process.argv[2] !== 'child') { } }); - worker.on('message', function(msg) { + worker.on('message', (msg) => { if (msg.listening) { listening += 1; @@ -162,12 +168,12 @@ if (process.argv[2] !== 'child') { 'required number of ' + 'messages. Will now compare.'); - Object.keys(workers).forEach(function(pid) { + Object.keys(workers).forEach((pid) => { const worker = workers[pid]; let count = 0; - worker.messagesReceived.forEach(function(buf) { + worker.messagesReceived.forEach((buf) => { for (let i = 0; i < worker.messagesNeeded.length; ++i) { if (buf.toString() === worker.messagesNeeded[i]) { count++; @@ -201,15 +207,15 @@ if (process.argv[2] !== 'child') { // Don't bind the address explicitly when sending and start with // the OSes default multicast interface selection. sendSocket.bind(common.PORT, ANY[FAM]); - sendSocket.on('listening', function() { + sendSocket.on('listening', () => { console.error(`outgoing iface ${interfaceAddress}`); }); - sendSocket.on('close', function() { + sendSocket.on('close', () => { console.error('[PARENT] sendSocket closed'); }); - sendSocket.sendNext = function() { + sendSocket.sendNext = () => { const msg = messages[i++]; if (!msg) { @@ -228,7 +234,7 @@ if (process.argv[2] !== 'child') { buf.length, PORTS[msg.mcast], msg.mcast, - function(err) { + (err) => { assert.ifError(err); console.error('[PARENT] sent %s to %s:%s', util.inspect(buf.toString()), @@ -238,11 +244,6 @@ if (process.argv[2] !== 'child') { } ); }; - - function killSubprocesses(subprocesses) { - for (const i in subprocesses) - subprocesses[i].kill(); - } } if (process.argv[2] === 'child') { @@ -258,7 +259,7 @@ if (process.argv[2] === 'child') { reuseAddr: true }); - listenSocket.on('message', function(buf, rinfo) { + listenSocket.on('message', (buf, rinfo) => { // Examine udp messages only when they were sent by the parent. if (!buf.toString().startsWith(SESSION)) return; @@ -280,7 +281,7 @@ if (process.argv[2] === 'child') { }); - listenSocket.on('listening', function() { + listenSocket.on('listening', () => { listenSocket.addMembership(MULTICAST, IFACE); process.send({ listening: true }); }); From 19d192d1f005b1d69021d5551b396c5eebf0a5d6 Mon Sep 17 00:00:00 2001 From: Kirlat Date: Fri, 22 Nov 2019 12:36:11 +0400 Subject: [PATCH 004/180] doc: fix a typo in a date for version 13.2.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/30587 Reviewed-By: Michaël Zasso Reviewed-By: Gireesh Punathil Reviewed-By: Beth Griggs Reviewed-By: Anna Henningsen --- doc/changelogs/CHANGELOG_V13.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/changelogs/CHANGELOG_V13.md b/doc/changelogs/CHANGELOG_V13.md index ec3a37fbc2d380..9689d1589cebf4 100644 --- a/doc/changelogs/CHANGELOG_V13.md +++ b/doc/changelogs/CHANGELOG_V13.md @@ -33,7 +33,7 @@ * [Archive](CHANGELOG_ARCHIVE.md) -## 2019-21-19, Version 13.2.0 (Current), @MylesBorins +## 2019-11-21, Version 13.2.0 (Current), @MylesBorins ### Notable Changes From 1916acb3cbe3fa46cb18af3a420b1e1e2813ed47 Mon Sep 17 00:00:00 2001 From: Shelley Vohr Date: Thu, 21 Nov 2019 19:44:43 -0800 Subject: [PATCH 005/180] src: fix signal handler crash on close PR-URL: https://github.com/nodejs/node/pull/30582 Reviewed-By: Gireesh Punathil Reviewed-By: Anna Henningsen Reviewed-By: Beth Griggs Reviewed-By: Colin Ihrig Reviewed-By: Michael Dawson Reviewed-By: Denys Otrishko --- src/signal_wrap.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/signal_wrap.cc b/src/signal_wrap.cc index cf67dc590f6d51..bc2d9f1e355efd 100644 --- a/src/signal_wrap.cc +++ b/src/signal_wrap.cc @@ -91,7 +91,10 @@ class SignalWrap : public HandleWrap { } void Close(v8::Local close_callback) override { - if (active_) DecreaseSignalHandlerCount(handle_.signum); + if (active_) { + DecreaseSignalHandlerCount(handle_.signum); + active_ = false; + } HandleWrap::Close(close_callback); } From 8ef629a78a01b6389354308885f2772738734c7a Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Wed, 20 Nov 2019 06:53:45 -0800 Subject: [PATCH 006/180] doc: simplify "is recommended" language in assert documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace "X is not recommended" with "Avoid X". Replace "It is recommended not to use X" with "Avoid X". PR-URL: https://github.com/nodejs/node/pull/30558 Reviewed-By: Colin Ihrig Reviewed-By: Michaël Zasso Reviewed-By: Gireesh Punathil Reviewed-By: Trivikram Kamat --- doc/api/assert.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/api/assert.md b/doc/api/assert.md index d27094fec53425..fa9416ea73b3e4 100644 --- a/doc/api/assert.md +++ b/doc/api/assert.md @@ -1243,11 +1243,11 @@ assert.throws( (err) => { assert(err instanceof Error); assert(/value/.test(err)); - // Returning anything from validation functions besides `true` is not - // recommended. By doing that, it's not clear what part of the validation - // failed. Instead, throw an error about the specific validation that failed - // (as done in this example) and add as much helpful debugging information - // to that error as possible. + // Avoid returning anything from validation functions besides `true`. + // Otherwise, it's not clear what part of the validation failed. Instead, + // throw an error about the specific validation that failed (as done in this + // example) and add as much helpful debugging information to that error as + // possible. return true; }, 'unexpected error' @@ -1294,8 +1294,8 @@ assert.throws(throwingFirst, /Second$/); // AssertionError [ERR_ASSERTION] ``` -Due to the confusing notation, it is recommended not to use a string as the -second argument. This might lead to difficult-to-spot errors. +Due to the confusing error-prone notation, avoid a string as the second +argument. [`AssertionError`]: #assert_class_assert_assertionerror [`Class`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes From e122d397c0ba810689de8e5515a66316febcdc93 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Wed, 20 Nov 2019 09:55:29 -0800 Subject: [PATCH 007/180] test: replace setTimeout with setImmediate in stream test Replace setTimeout() with setImmediate() in test-stream-writable-clear-buffer. The test still fails in Node.js 8.6.0 (if you comment out the `common` module, since it uses BigInts and those aren't supported in 8.6.0). PR-URL: https://github.com/nodejs/node/pull/30561 Reviewed-By: Denys Otrishko Reviewed-By: Anna Henningsen --- test/sequential/test-stream-writable-clear-buffer.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/test/sequential/test-stream-writable-clear-buffer.js b/test/sequential/test-stream-writable-clear-buffer.js index dc859e3fb6b362..f2d1e000cfde3e 100644 --- a/test/sequential/test-stream-writable-clear-buffer.js +++ b/test/sequential/test-stream-writable-clear-buffer.js @@ -1,5 +1,5 @@ 'use strict'; -const common = require('../common'); +require('../common'); const Stream = require('stream'); // This test ensures that the _writeableState.bufferedRequestCount and // the actual buffered request count are the same @@ -10,11 +10,12 @@ class StreamWritable extends Stream.Writable { super({ objectMode: true }); } - // We need a timeout like on the original issue thread - // otherwise the code will never reach our test case - // this means this should go on the sequential folder. + // Refs: https://github.com/nodejs/node/issues/6758 + // We need a timer like on the original issue thread. + // Otherwise the code will never reach our test case. + // This means this should go in the sequential folder. _write(chunk, encoding, cb) { - setTimeout(cb, common.platformTimeout(10)); + setImmediate(cb); } } From ebba3228e2ae291df0d36d6de7be37112523d713 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Wed, 20 Nov 2019 09:58:42 -0800 Subject: [PATCH 008/180] test: use arrow function for callback in stream test PR-URL: https://github.com/nodejs/node/pull/30561 Reviewed-By: Denys Otrishko Reviewed-By: Anna Henningsen --- test/sequential/test-stream-writable-clear-buffer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/sequential/test-stream-writable-clear-buffer.js b/test/sequential/test-stream-writable-clear-buffer.js index f2d1e000cfde3e..7d83ac55ddb2af 100644 --- a/test/sequential/test-stream-writable-clear-buffer.js +++ b/test/sequential/test-stream-writable-clear-buffer.js @@ -23,7 +23,7 @@ const testStream = new StreamWritable(); testStream.cork(); for (let i = 1; i <= 5; i++) { - testStream.write(i, function() { + testStream.write(i, () => { assert.strictEqual( testStream._writableState.bufferedRequestCount, testStream._writableState.getBuffer().length, From 103d01e057c0f791f8edf1a2acf1b80264d829c4 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Wed, 20 Nov 2019 10:00:07 -0800 Subject: [PATCH 009/180] test: remove string literal as message in strictEqual() in stream test This reveals the values that cause the assertion error, should it happen. PR-URL: https://github.com/nodejs/node/pull/30561 Reviewed-By: Denys Otrishko Reviewed-By: Anna Henningsen --- test/sequential/test-stream-writable-clear-buffer.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/sequential/test-stream-writable-clear-buffer.js b/test/sequential/test-stream-writable-clear-buffer.js index 7d83ac55ddb2af..2734c84949f013 100644 --- a/test/sequential/test-stream-writable-clear-buffer.js +++ b/test/sequential/test-stream-writable-clear-buffer.js @@ -26,9 +26,8 @@ for (let i = 1; i <= 5; i++) { testStream.write(i, () => { assert.strictEqual( testStream._writableState.bufferedRequestCount, - testStream._writableState.getBuffer().length, - 'bufferedRequestCount variable is different from the actual length of' + - ' the buffer'); + testStream._writableState.getBuffer().length + ); }); } From 081b4e24964dff699c3fcc473055fb93bef73ace Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Wed, 20 Nov 2019 10:02:22 -0800 Subject: [PATCH 010/180] test: move stream test to parallel I don't believe there's a reason test-stream-writable-clear-buffer needs to be in sequential. Move it to parallel. PR-URL: https://github.com/nodejs/node/pull/30561 Reviewed-By: Denys Otrishko Reviewed-By: Anna Henningsen --- .../test-stream-writable-clear-buffer.js | 1 - 1 file changed, 1 deletion(-) rename test/{sequential => parallel}/test-stream-writable-clear-buffer.js (93%) diff --git a/test/sequential/test-stream-writable-clear-buffer.js b/test/parallel/test-stream-writable-clear-buffer.js similarity index 93% rename from test/sequential/test-stream-writable-clear-buffer.js rename to test/parallel/test-stream-writable-clear-buffer.js index 2734c84949f013..df3fabe1b91c12 100644 --- a/test/sequential/test-stream-writable-clear-buffer.js +++ b/test/parallel/test-stream-writable-clear-buffer.js @@ -13,7 +13,6 @@ class StreamWritable extends Stream.Writable { // Refs: https://github.com/nodejs/node/issues/6758 // We need a timer like on the original issue thread. // Otherwise the code will never reach our test case. - // This means this should go in the sequential folder. _write(chunk, encoding, cb) { setImmediate(cb); } From 106235fe91dbf3724fcfc4186cb65416f3923bc8 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Wed, 20 Nov 2019 13:07:28 -0800 Subject: [PATCH 011/180] test: move explanatory comment to expected location in file Refs: https://github.com/nodejs/node/pull/30561#pullrequestreview-320172639 PR-URL: https://github.com/nodejs/node/pull/30561 Reviewed-By: Denys Otrishko Reviewed-By: Anna Henningsen --- test/parallel/test-stream-writable-clear-buffer.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/parallel/test-stream-writable-clear-buffer.js b/test/parallel/test-stream-writable-clear-buffer.js index df3fabe1b91c12..0a41f1a9979290 100644 --- a/test/parallel/test-stream-writable-clear-buffer.js +++ b/test/parallel/test-stream-writable-clear-buffer.js @@ -1,8 +1,10 @@ 'use strict'; + +// This test ensures that the _writeableState.bufferedRequestCount and +// the actual buffered request count are the same. + require('../common'); const Stream = require('stream'); -// This test ensures that the _writeableState.bufferedRequestCount and -// the actual buffered request count are the same const assert = require('assert'); class StreamWritable extends Stream.Writable { From 36671f9bf85bc747eb4f07d7f828c8c188327453 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Wed, 20 Nov 2019 13:10:18 -0800 Subject: [PATCH 012/180] test: add common.mustCall() to stream test Refs: https://github.com/nodejs/node/pull/30561/files#r348667256 PR-URL: https://github.com/nodejs/node/pull/30561 Reviewed-By: Denys Otrishko Reviewed-By: Anna Henningsen --- test/parallel/test-stream-writable-clear-buffer.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/parallel/test-stream-writable-clear-buffer.js b/test/parallel/test-stream-writable-clear-buffer.js index 0a41f1a9979290..c4d7ae151a38fb 100644 --- a/test/parallel/test-stream-writable-clear-buffer.js +++ b/test/parallel/test-stream-writable-clear-buffer.js @@ -3,7 +3,7 @@ // This test ensures that the _writeableState.bufferedRequestCount and // the actual buffered request count are the same. -require('../common'); +const common = require('../common'); const Stream = require('stream'); const assert = require('assert'); @@ -24,12 +24,12 @@ const testStream = new StreamWritable(); testStream.cork(); for (let i = 1; i <= 5; i++) { - testStream.write(i, () => { + testStream.write(i, common.mustCall(() => { assert.strictEqual( testStream._writableState.bufferedRequestCount, testStream._writableState.getBuffer().length ); - }); + })); } testStream.end(); From f135c38796ed28a2b7bf2e38dd12f294183f16f2 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Wed, 20 Nov 2019 00:57:49 +0100 Subject: [PATCH 013/180] src,doc: add C++ internals documentation This aims to help explain some of the internal patterns and utilities that we use. It is by no means exhaustive, and suggestions for additions are welcome. Some of this is based on the existing work from #26929. Refs: https://github.com/nodejs/node/pull/26929 PR-URL: https://github.com/nodejs/node/pull/30552 Reviewed-By: Sam Roberts Reviewed-By: Richard Lau Reviewed-By: Jiawen Geng Reviewed-By: David Carlier Reviewed-By: Joyee Cheung Reviewed-By: Colin Ihrig Reviewed-By: Gireesh Punathil Reviewed-By: Denys Otrishko Reviewed-By: James M Snell Reviewed-By: Michael Dawson --- CPP_STYLE_GUIDE.md | 3 + doc/guides/contributing/pull-requests.md | 4 +- src/README.md | 896 +++++++++++++++++++++++ 3 files changed, 902 insertions(+), 1 deletion(-) create mode 100644 src/README.md diff --git a/CPP_STYLE_GUIDE.md b/CPP_STYLE_GUIDE.md index 8808405b2c6553..f29c8df3210caa 100644 --- a/CPP_STYLE_GUIDE.md +++ b/CPP_STYLE_GUIDE.md @@ -1,5 +1,8 @@ # C++ Style Guide +See also the [C++ codebase README](src/README.md) for C++ idioms in the Node.js +codebase not related to stylistic issues. + ## Table of Contents * [Guides and References](#guides-and-references) diff --git a/doc/guides/contributing/pull-requests.md b/doc/guides/contributing/pull-requests.md index f23c92fa024e63..31e3ba64757c12 100644 --- a/doc/guides/contributing/pull-requests.md +++ b/doc/guides/contributing/pull-requests.md @@ -121,7 +121,9 @@ in the API docs will also be checked when running `make lint` (or use `REPLACEME` for the version number in the documentation YAML. For contributing C++ code, you may want to look at the -[C++ Style Guide](../../../CPP_STYLE_GUIDE.md). +[C++ Style Guide](../../../CPP_STYLE_GUIDE.md), as well as the +[README of `src/`](../../../src/README.md) for an overview over Node.js +C++ internals. ### Step 4: Commit diff --git a/src/README.md b/src/README.md new file mode 100644 index 00000000000000..48cf99991552e2 --- /dev/null +++ b/src/README.md @@ -0,0 +1,896 @@ +# Node.js C++ codebase + +Hi! 👋 You’ve found the C++ code backing Node.js. This README aims to help you +get started working on it and document some idioms you may encounter while +doing so. + +## Coding style + +Node.js has a document detailing its [C++ coding style][] +that can be helpful as a reference for stylistic issues. + +## V8 API documentation + +A lot of the Node.js codebase is around what the underlying JavaScript engine, +V8, provides through its API for embedders. Knowledge of this API can also be +useful when working with native addons for Node.js written in C++, although for +new projects [N-API][] is typically the better alternative. + +V8 does not provide much public API documentation beyond what is +available in its C++ header files, most importantly `v8.h`, which can be +accessed online in the following locations: + +* On GitHub: [`v8.h` in Node.js master][] +* On GitHub: [`v8.h` in V8 master][] +* On the Chromium project’s Code Search application: [`v8.h` in Code Search][] + +V8 also provides an [introduction for V8 embedders][], +which can be useful for understanding some of the concepts it uses in its +embedder API. + +Important concepts when using V8 are the ones of [`Isolate`][]s and +[JavaScript value handles][]. + +## libuv API documentation + +The other major dependency of Node.js is [libuv][], providing +the [event loop][] and other operation system abstractions to Node.js. + +There is a [reference documentation for the libuv API][]. + +## Helpful concepts + +A number of concepts are involved in putting together Node.js on top of V8 and +libuv. This section aims to explain some of them and how they work together. + + +### `Isolate` + +The `v8::Isolate` class represents a single JavaScript engine instance, in +particular a set of JavaScript objects that can refer to each other +(the “heap”). + +The `v8::Isolate` is often passed to other V8 API functions, and provides some +APIs for managing the behaviour of the JavaScript engine or querying about its +current state or statistics such as memory usage. + +V8 APIs are not thread-safe unless explicitly specified. In a typical Node.js +application, the main thread and any `Worker` threads each have one `Isolate`, +and JavaScript objects from one `Isolate` cannot refer to objects from +another `Isolate`. + +Garbage collection, as well as other operations that affect the entire heap, +happen on a per-`Isolate` basis. + +Typical ways of accessing the current `Isolate` in the Node.js code are: + +* Given a `FunctionCallbackInfo` for a [binding function][], + using `args.GetIsolate()`. +* Given a [`Context`][], using `context->GetIsolate()`. +* Given a [`Environment`][], using `env->isolate()`. + +### V8 JavaScript values + +V8 provides classes that mostly correspond to JavaScript types; for example, +`v8::Value` is a class representing any kind of JavaScript type, with +subclasses such as `v8::Number` (which in turn has subclasses like `v8::Int32`), +`v8::Boolean` or `v8::Object`. Most types are represented by subclasses +of `v8::Object`, e.g. `v8::Uint8Array` or `v8::Date`. + + +### Internal fields + +V8 provides the ability to store data in so-called “internal fields” inside +`v8::Object`s that were created as instances of C++-backed classes. The number +of fields needs to be defined when creating that class. + +Both JavaScript values and `void*` pointers may be stored in such fields. +In most native Node.js objects, the first internal field is used to store a +pointer to a [`BaseObject`][] subclass, which then contains all relevant +information associated with the JavaScript object. + +The most typical way of working internal fields are: + +* `obj->InternalFieldCount()` to look up the number of internal fields for an + object (`0` for regular JavaScript objects). +* `obj->GetInternalField(i)` to get a JavaScript value from an internal field. +* `obj->SetInternalField(i, v)` to store a JavaScript value in an + internal field. +* `obj->GetAlignedPointerFromInternalField(i)` to get a `void*` pointer from an + internal field. +* `obj->SetAlignedPointerInInternalField(i, p)` to store a `void*` pointer in an + internal field. + +[`Context`][]s provide the same feature under the name “embedder data”. + + +### JavaScript value handles + +All JavaScript values are accessed through the V8 API through so-called handles, +of which there are two types: [`Local`][]s and [`Global`][]s. + + +#### `Local` handles + +A `v8::Local` handle is a temporary pointer to a JavaScript object, where +“temporary” usually means that is no longer needed after the current function +is done executing. `Local` handles can only be allocated on the C++ stack. + +Most of the V8 API uses `Local` handles to work with JavaScript values or return +them from functions. + +Whenever a `Local` handle is created, a `v8::HandleScope` or +`v8::EscapableHandleScope` object must exist on the stack. The `Local` is then +added to that scope and deleted along with it. + +When inside a [binding function][], a `HandleScope` already exists outside of +it, so there is no need to explicitly create one. + +`EscapableHandleScope`s can be used to allow a single `Local` handle to be +passed to the outer scope. This is useful when a function returns a `Local`. + +The following JavaScript and C++ functions are mostly equivalent: + +```js +function getFoo(obj) { + return obj.foo; +} +``` + +```c++ +v8::Local GetFoo(v8::Local context, + v8::Local obj) { + v8::Isolate* isolate = context->GetIsolate(); + v8::EscapableHandleScope handle_scope(isolate); + + // The 'foo_string' handle cannot be returned from this function because + // it is not “escaped” with `.Escape()`. + v8::Local foo_string = + v8::String::NewFromUtf8(isolate, + "foo", + v8::NewStringType::kNormal).ToLocalChecked(); + + v8::Local return_value; + if (obj->Get(context, foo_string).ToLocal(&return_value)) { + return handle_scope.Escape(return_value); + } else { + // There was a JS exception! Handle it somehow. + return v8::Local(); + } +} +``` + +See [exception handling][] for more information about the usage of `.To()`, +`.ToLocalChecked()`, `v8::Maybe` and `v8::MaybeLocal` usage. + +##### Casting local handles + +If it is known that a `Local` refers to a more specific type, it can +be cast to that type using `.As<...>()`: + +```c++ +v8::Local some_value; +// CHECK() is a Node.js utilitity that works similar to assert(). +CHECK(some_value->IsUint8Array()); +v8::Local as_uint8 = some_value.As(); +``` + +Generally, using `val.As()` is only valid if `val->IsX()` is true, and +failing to follow that rule may lead to crashes. + +##### Detecting handle leaks + +If it is expected that no `Local` handles should be created within a given +scope unless explicitly within a `HandleScope`, a `SealHandleScope` can be used. + +For example, there is a `SealHandleScope` around the event loop, forcing +any functions that are called from the event loop and want to run or access +JavaScript code to create `HandleScope`s. + + +#### `Global` handles + +A `v8::Global` handle (sometimes also referred to by the name of its parent +class `Persistent`, although use of that is discouraged in Node.js) is a +reference to a JavaScript object that can remain active as long as the engine +instance is active. + +Global handles can be either strong or weak. Strong global handles are so-called +“GC roots”, meaning that they will keep the JavaScript object they refer to +alive even if no other objects refer to them. Weak global handles do not do +that, and instead optionally call a callback when the object they refer to +is garbage-collected. + +```c++ +v8::Global reference; + +void StoreReference(v8::Isolate* isolate, v8::Local obj) { + // Create a strong reference to `obj`. + reference.Reset(isolate, obj); +} + +// Must be called with a HandleScope around it. +v8::Local LoadReference(v8::Isolate* isolate) { + return reference.Get(isolate); +} +``` + +##### `Eternal` handles + +`v8::Eternal` handles are a special kind of handles similar to `v8::Global` +handles, with the exception that the values they point to are never +garbage-collected while the JavaScript Engine instance is alive, even if +the `v8::Eternal` itself is destroyed at some point. This type of handle +is rarely used. + + +### `Context` + +JavaScript allows multiple global objects and sets of built-in JavaScript +objects (like the `Object` or `Array` functions) to coexist inside the same +heap. Node.js exposes this ability through the [`vm` module][]. + +V8 refers to each of these global objects and their associated builtins as a +`Context`. + +Currently, in Node.js there is one main `Context` associated with an +[`Environment`][] instance, and most Node.js features will only work inside +that context. (The only exception at the time of writing are +[`MessagePort`][] objects.) This restriction is not inherent to the design of +Node.js, and a sufficiently committed person could restructure Node.js to +provide built-in modules inside of `vm.Context`s. + +Often, the `Context` is passed around for [exception handling][]. +Typical ways of accessing the current `Environment` in the Node.js code are: + +* Given an [`Isolate`][], using `isolate->GetCurrentContext()`. +* Given an [`Environment`][], using `env->context()` to get the `Environment`’s + main context. + + +### Event loop + +The main abstraction for an event loop inside Node.js is the `uv_loop_t` struct. +Typically, there is one event loop per thread. This includes not only the main +thread and Workers, but also helper threads that may occasionally be spawned +in the course of running a Node.js program. + +The current event loop can be accessed using `env->event_loop()` given an +[`Environment`][] instance. The restriction of using a single event loop +is not inherent to the design of Node.js, and a sufficiently committed person +could restructure Node.js to provide e.g. the ability to run parts of Node.js +inside an event loop separate from the active thread’s event loop. + + +### `Environment` + +Node.js instances are represented by the `Environment` class. + +Currently, every `Environment` class is associated with: + +* One [event loop][] +* One [`Isolate`][] +* One main [`Context`][] + +The `Environment` class contains a large number of different fields for +different Node.js modules, for example a libuv timer for `setTimeout()` or +the memory for a `Float64Array` that the `fs` module uses for storing data +returned from a `fs.stat()` call. + +It also provides [cleanup hooks][] and maintains a list of [`BaseObject`][] +instances. + +Typical ways of accessing the current `Environment` in the Node.js code are: + +* Given a `FunctionCallbackInfo` for a [binding function][], + using `Environment::GetCurrent(args)`. +* Given a [`BaseObject`][], using `env()` or `self->env()`. +* Given a [`Context`][], using `Environment::GetCurrent(context)`. + This requires that `context` has been associated with the `Environment` + instance, e.g. is the main `Context` for the `Environment` or one of its + `vm.Context`s. +* Given an [`Isolate`][], using `Environment::GetCurrent(isolate)`. This looks + up the current [`Context`][] and then uses that. + + +### `IsolateData` + +Every Node.js instance ([`Environment`][]) is associated with one `IsolateData` +instance that contains information about or associated with a given +[`Isolate`][]. + +#### String table + +`IsolateData` contains a list of strings that can be quickly accessed +inside Node.js code, e.g. given an `Environment` instance `env` the JavaScript +string “name” can be accessed through `env->name_string()` without actually +creating a new JavaScript string. + +### Platform + +Every process that uses V8 has a `v8::Platform` instance that provides some +functionalities to V8, most importantly the ability to schedule work on +background threads. + +Node.js provides a `NodePlatform` class that implements the `v8::Platform` +interface and uses libuv for providing background threading abilities. + +The platform can be accessed through `isolate_data->platform()` given an +[`IsolateData`][] instance, although that only works when: + +* The current Node.js instance was not started by an embedder; or +* The current Node.js instance was started by an embedder whose `v8::Platform` + implementation also implement’s the `node::MultiIsolatePlatform` interface + and who passed this to Node.js. + + +### Binding functions + +C++ functions exposed to JS follow a specific signature. The following example +is from `node_util.cc`: + +```c++ +void ArrayBufferViewHasBuffer(const FunctionCallbackInfo& args) { + CHECK(args[0]->IsArrayBufferView()); + args.GetReturnValue().Set(args[0].As()->HasBuffer()); +} +``` + +(Namespaces are usually omitted through the use of `using` statements in the +Node.js source code.) + +`args[n]` is a `Local` that represents the n-th argument passed to the +function. `args.This()` is the `this` value inside this function call. +`args.Holder()` is equivalent to `args.This()` in all use cases inside of +Node.js. + +`args.GetReturnValue()` is a placeholder for the return value of the function, +and provides a `.Set()` method that can be called with a boolean, integer, +floating-point number or a `Local` to set the return value. + +Node.js provides various helpers for building JS classes in C++ and/or attaching +C++ functions to the exports of a built-in module: + +```c++ +void Initialize(Local target, + Local unused, + Local context, + void* priv) { + Environment* env = Environment::GetCurrent(context); + + env->SetMethod(target, "getaddrinfo", GetAddrInfo); + env->SetMethod(target, "getnameinfo", GetNameInfo); + + // 'SetMethodNoSideEffect' means that debuggers can safely execute this + // function for e.g. previews. + env->SetMethodNoSideEffect(target, "canonicalizeIP", CanonicalizeIP); + + // ... more code ... + + // Building the `ChannelWrap` class for JS: + Local channel_wrap = + env->NewFunctionTemplate(ChannelWrap::New); + // Allow for 1 internal field, see `BaseObject` for details on this: + channel_wrap->InstanceTemplate()->SetInternalFieldCount(1); + channel_wrap->Inherit(AsyncWrap::GetConstructorTemplate(env)); + + // Set various methods on the class (i.e. on the prototype): + env->SetProtoMethod(channel_wrap, "queryAny", Query); + env->SetProtoMethod(channel_wrap, "queryA", Query); + // ... + env->SetProtoMethod(channel_wrap, "querySoa", Query); + env->SetProtoMethod(channel_wrap, "getHostByAddr", Query); + + env->SetProtoMethodNoSideEffect(channel_wrap, "getServers", GetServers); + + Local channel_wrap_string = + FIXED_ONE_BYTE_STRING(env->isolate(), "ChannelWrap"); + channel_wrap->SetClassName(channel_wrap_string); + target->Set(env->context(), channel_wrap_string, + channel_wrap->GetFunction(context).ToLocalChecked()).Check(); +} + +// Run the `Initialize` function when loading this module through +// `internalBinding('cares_wrap')` in Node.js’s built-in JavaScript code: +NODE_MODULE_CONTEXT_AWARE_INTERNAL(cares_wrap, Initialize) +``` + + +### Exception handling + +The V8 engine provides multiple features to work with JavaScript exceptions, +as C++ exceptions are disabled inside of Node.js: + +#### Maybe types + +V8 provides the `v8::Maybe` and `v8::MaybeLocal` types, typically used +as return values from API functions that can run JavaScript code and therefore +can throw exceptions. + +Conceptually, the idea is that every `v8::Maybe` is either empty (checked +through `.IsNothing()`) or holds a value of type `T` (checked through +`.IsJust()`). If the `Maybe` is empty, then a JavaScript exception is pending. +A typical way of accessing the value is using the `.To()` function, which +returns a boolean indicating success of the operation (i.e. the `Maybe` not +being empty) and taking a pointer to a `T` to store the value if there is one. + +##### Checked conversion + +`maybe.Check()` can be used to assert that the maybe is not empty, i.e. crash +the process otherwise. `maybe.FromJust()` (aka `maybe.ToChecked()`) can be used +to access the value and crash the process if it is not set. + +This should only be performed if it is actually sure that the operation has +not failed. A lot of Node.js’s source code does **not** follow this rule, and +can be brought to crash through this. + +##### MaybeLocal + +`v8::MaybeLocal` is a variant of `v8::Maybe` that is either empty or +holds a value of type `Local`. It has methods that perform the same +operations as the methods of `v8::Maybe`, but with different names: + +| `Maybe` | `MaybeLocal` | +| ---------------------- | ------------------------------- | +| `maybe.IsNothing()` | `maybe_local.IsEmpty()` | +| `maybe.IsJust()` | – | +| `maybe.To(&value)` | `maybe_local.ToLocal(&local)` | +| `maybe.ToChecked()` | `maybe_local.ToLocalChecked()` | +| `maybe.FromJust()` | `maybe_local.ToLocalChecked()` | +| `maybe.Check()` | – | +| `v8::Nothing()` | `v8::MaybeLocal()` | +| `v8::Just(value)` | `v8::MaybeLocal(value)` | + +##### Handling empty `Maybe`s + +Usually, the best approach to encountering an empty `Maybe` is to just return +from the current function as soon as possible, and let execution in JavaScript +land resume. If the empty `Maybe` is encountered inside a nested function, +is may be a good idea to use a `Maybe` or `MaybeLocal` for the return type +of that function and pass information about pending JavaScript exceptions along +that way. + +Generally, when an empty `Maybe` is encountered, it is not valid to attempt +to perform further calls to APIs that return `Maybe`s. + +A typical pattern for dealing with APIs that return `Maybe` and `MaybeLocal` is +using `.ToLocal()` and `.To()` and returning early in case there is an error: + +```c++ +// This could also return a v8::MaybeLocal, for example. +v8::Maybe SumNumbers(v8::Local context, + v8::Local array_of_integers) { + v8::Isolate* isolate = context->GetIsolate(); + v8::HandleScope handle_scope(isolate); + + double sum = 0; + + for (uint32_t i = 0; i < array_of_integers->Length(); i++) { + v8::Local entry; + if (array_of_integers->Get(context, i).ToLocal(&entry)) { + // Oops, we might have hit a getter that throws an exception! + // It’s better to not continue return an empty (“nothing”) Maybe. + return v8::Nothing(); + } + + if (!entry->IsNumber()) { + // Let’s just skip any non-numbers. It would also be reasonable to throw + // an exception here, e.g. using the error system in src/node_errors.h, + // and then to return an empty Maybe again. + continue; + } + + // This cast is valid, because we’ve made sure it’s really a number. + v8::Local entry_as_number = entry.As(); + + sum += entry_as_number->Value(); + } + + return v8::Just(sum); +} + +// Function that is exposed to JS: +void SumNumbers(const v8::FunctionCallbackInfo& args) { + // This will crash if the first argument is not an array. Let’s assume we + // have performed type checking in a JavaScript wrapper function. + CHECK(args[0]->IsArray()); + + double sum; + if (!SumNumbers(args.GetIsolate()->GetCurrentContext(), + args[0].As()).To(&sum)) { + // Nothing to do, we can just return directly to JavaScript. + return; + } + + args.GetReturnValue().Set(sum); +} +``` + +#### TryCatch + +If there is a need to catch JavaScript exceptions in C++, V8 provides the +`v8::TryCatch` type for doing so, which we wrap into our own +`node::errors::TryCatchScope` in Node.js. The latter has the additional feature +of providing the ability to shut down the program in the typical Node.js way +(printing the exception + stack trace) if an exception is caught. + + +### libuv handles and requests + +Two central concepts when working with libuv are handles and requests. + +Handles are subclasses of the `uv_handle_t` “class”, and generally refer to +long-lived objects that can emit events multiple times, such as network sockets +or file system watchers. + +In Node.js, handles are often managed through a [`HandleWrap`][] subclass. + +Requests are one-time asynchronous function calls on the event loop, such as +file system requests or network write operations, that either succeed or fail. + +In Node.js, requests are often managed through a [`ReqWrap`][] subclass. + +### Environment cleanup + +When a Node.js [`Environment`][] is destroyed, it generally needs to clean up +any resources owned by it, e.g. memory or libuv requests/handles. + + +#### Cleanup hooks + +Cleanup hooks are provided that run before the [`Environment`][] +is destroyed. They can be added and removed through by using +`env->AddCleanupHook(callback, hint);` and +`env->RemoveCleanupHook(callback, hint);`, where callback takes a `void* hint` +argument. + +Inside these cleanup hooks, new asynchronous operations *may* be started on the +event loop, although ideally that is avoided as much as possible. + +Every [`BaseObject`][] has its own cleanup hook that deletes it. For +[`ReqWrap`][] and [`HandleWrap`][] instances, cleanup of the associated libuv +objects is performed automatically, i.e. handles are closed and requests +are cancelled if possible. + +#### Closing libuv handles + +If a libuv handle is not managed through a [`HandleWrap`][] instance, +it needs to be closed explicitly. Do not use `uv_close()` for that, but rather +`env->CloseHandle()`, which works the same way but keeps track of the number +of handles that are still closing. + +#### Closing libuv requests + +There is no way to abort libuv requests in general. If a libuv request is not +managed through a [`ReqWrap`][] instance, the +`env->IncreaseWaitingRequestCounter()` and +`env->DecreaseWaitingRequestCounter()` functions need to be used to keep track +of the number of active libuv requests. + +#### Calling into JavaScript + +Calling into JavaScript is not allowed during cleanup. Worker threads explicitly +forbid this during their shutdown sequence, but the main thread does not for +backwards compatibility reasons. + +When calling into JavaScript without using [`MakeCallback()`][], check the +`env->can_call_into_js()` flag and do not proceed if it is set to `false`. + +## Classes associated with JavaScript objects + +### `MemoryRetainer` + +A large number of classes in the Node.js C++ codebase refer to other objects. +The `MemoryRetainer` class is a helper for annotating C++ classes with +information that can be used by the heap snapshot builder in V8, so that +memory retained by C++ can be tracked in V8 heap snapshots captured in +Node.js applications. + +Inheriting from the `MemoryRetainer` class enables objects (both from JavaScript +and C++) to refer to instances of that class, and in turn enables that class +to point to other objects as well, including native C++ types +such as `std::string` and track their memory usage. + +This can be useful for debugging memory leaks. + +The [`memory_retainer.h`][] header file explains how to use this class. + + +### `BaseObject` + +A frequently recurring situation is that a JavaScript object and a C++ object +need to be tied together. `BaseObject` is the main abstraction for that in +Node.js, and most classes that are associated with JavaScript objects are +subclasses of it. It is defined in [`base_object.h`][]. + +Every `BaseObject` is associated with one [`Environment`][] and one +`v8::Object`. The `v8::Object` needs to have at least one [internal field][] +that is used for storing the pointer to the C++ object. In order to ensure this, +the V8 `SetInternalFieldCount()` function is usually used when setting up the +class from C++. + +The JavaScript object can be accessed as a `v8::Local` by using +`self->object()`, given a `BaseObject` named `self`. + +Accessing a `BaseObject` from a `v8::Local` (frequently that is +`args.This()` or `args.Holder()` in a [binding function][]) can be done using +the `Unwrap(obj)` function, where `T` is a subclass of `BaseObject`. +A helper for this is the `ASSIGN_OR_RETURN_UNWRAP` macro that returns from the +current function if unwrapping fails (typically that means that the `BaseObject` +has been deleted earlier). + +```c++ +void Http2Session::Request(const FunctionCallbackInfo& args) { + Http2Session* session; + ASSIGN_OR_RETURN_UNWRAP(&session, args.Holder()); + Environment* env = session->env(); + Local context = env->context(); + Isolate* isolate = env->isolate(); + + // ... + // The actual function body, which can now use the `session` object. + // ... +} +``` + +#### Lifetime management + +The `BaseObject` class comes with a set of features that allow managing the +lifetime of its instances, either associating it with the lifetime of the +corresponding JavaScript object or untying the two. + +The `BaseObject::MakeWeak()` method turns the underlying [`Global`][] handle +into a weak one, and makes it so that the `BaseObject::OnGCCollect()` virtual +method is called when the JavaScript object is garbage collected. By default, +that methods deletes the `BaseObject` instance. + +`BaseObject::ClearWeak()` undoes this effect. + +It generally makes sense to call `MakeWeak()` in the constructor of a +`BaseObject` subclass, unless that subclass is referred to by e.g. the event +loop, as is the case for the [`HandleWrap`][] and [`ReqWrap`][] classes. + +In addition, there are two kinds of smart pointers that can be used to refer +to `BaseObject`s. + +`BaseObjectWeakPtr` is similar to `std::weak_ptr`, but holds on to +an object of a `BaseObject` subclass `T` and integrates with the lifetime +management of the former. When the `BaseObject` no longer exists, e.g. when +it was garbage collected, accessing it through `weak_ptr.get()` will return +`nullptr`. + +`BaseObjectPtr` is similar to `std::shared_ptr`, but also holds on to +objects of a `BaseObject` subclass `T`. While there are `BaseObjectPtr`s +pointing to a given object, the `BaseObject` will always maintain a strong +reference to its associated JavaScript object. This can be useful when one +`BaseObject` refers to another `BaseObject` and wants to make sure it stays +alive during the lifetime of that reference. + +A `BaseObject` can be “detached” throught the `BaseObject::Detach()` method. +In this case, it will be deleted once the last `BaseObjectPtr` referring to +it is destroyed. There must be at least one such pointer when `Detach()` is +called. This can be useful when one `BaseObject` fully owns another +`BaseObject`. + + +### `AsyncWrap` + +`AsyncWrap` is a subclass of `BaseObject` that additionally provides tracking +functions for asynchronous calls. It is commonly used for classes whose methods +make calls into JavaScript without any JavaScript stack below, i.e. more or less +directly from the event loop. It is defined in [`async_wrap.h`][]. + +Every `AsyncWrap` subclass has a “provider type”. A list of provider types is +maintained in `src/async_wrap.h`. + +Every `AsyncWrap` instance is associated with two numbers, the “async id” +and the “async trigger id”. The “async id” is generally unique per `AsyncWrap` +instance, and only changes when the object is re-used in some way. + +See the [`async_hooks` module][] documentation for more information about how +this information is provided to async tracking tools. + + +#### `MakeCallback` + +The `AsyncWrap` class has a set of methods called `MakeCallback()`, with the +intention of the naming being that it is used to “make calls back into +JavaScript” from the event loop, rather than making callbacks in some way. +(As the naming has made its way into Node.js’s public API, it’s not worth +the breakage of fixing it). + +`MakeCallback()` generally calls a method on the JavaScript object associated +with the current `AsyncWrap`, and informs async tracking code about these calls +as well as takes care of running the `process.nextTick()` and `Promise` task +queues once it returns. + +Before calling `MakeCallback()`, it is typically necessary to enter both a +`HandleScope` and a `Context::Scope`. + +```c++ +void StatWatcher::Callback(uv_fs_poll_t* handle, + int status, + const uv_stat_t* prev, + const uv_stat_t* curr) { + // Get the StatWatcher instance associated with this call from libuv, + // StatWatcher is a subclass of AsyncWrap. + StatWatcher* wrap = ContainerOf(&StatWatcher::watcher_, handle); + Environment* env = wrap->env(); + HandleScope handle_scope(env->isolate()); + Context::Scope context_scope(env->context()); + + // Transform 'prev' and 'curr' into an array: + Local arr = ...; + + Local argv[] = { Integer::New(env->isolate(), status), arr }; + wrap->MakeCallback(env->onchange_string(), arraysize(argv), argv); +} +``` + +See [Callback scopes][] for more information. + + +### `HandleWrap` + +`HandleWrap` is a subclass of `AsyncWrap` specifically designed to make working +with [libuv handles][] easier. It provides the `.ref()`, `.unref()` and +`.hasRef()` methods as well as `.close()` to enable easier lifetime management +from JavaScript. It is defined in [`handle_wrap.h`][]. + +`HandleWrap` instances are [cleaned up][cleanup hooks] automatically when the +current Node.js [`Environment`][] is destroyed, e.g. when a Worker thread stops. + +`HandleWrap` also provides facilities for diagnostic tooling to get an +overview over libuv handles managed by Node.js. + + +### `ReqWrap` + +`ReqWrap` is a subclass of `AsyncWrap` specifically designed to make working +with [libuv requests][] easier. It is defined in [`req_wrap.h`][]. + +In particular, its `Dispatch()` method is designed to avoid the need to keep +track of the current count of active libuv requests. + +`ReqWrap` also provides facilities for diagnostic tooling to get an +overview over libuv handles managed by Node.js. + + +### Callback scopes + +The public `CallbackScope` and the internally used `InternalCallbackScope` +classes provide the same facilities as [`MakeCallback()`][], namely: + +* Emitting the `'before'` event for async tracking when entering the scope +* Setting the current async IDs to the ones passed to the constructor +* Emitting the `'after'` event for async tracking when leaving the scope +* Running the `process.nextTick()` queue +* Running microtasks, in particular `Promise` callbacks and async/await + functions + +Usually, using `AsyncWrap::MakeCallback()` or using the constructor taking +an `AsyncWrap*` argument (i.e. used as +`InternalCallbackScope callback_scope(this);`) suffices inside of Node.js’s +C++ codebase. + +## C++ utilities + +Node.js uses a few custom C++ utilities, mostly defined in [`util.h`][]. + +### Memory allocation + +Node.js provides `Malloc()`, `Realloc()` and `Calloc()` functions that work +like their C stdlib counterparts, but crash if memory cannot be allocated. +(As V8 does not handle out-of-memory situations gracefully, it does not make +sense for Node.js to attempt to do so in all cases.) + +The `UncheckedMalloc()`, `UncheckedRealloc()` and `UncheckedCalloc()` functions +return `nullptr` in these cases (or when `size == 0`). + +#### Optional stack-based memory allocation + +The `MaybeStackBuffer` class provides a way to allocate memory on the stack +if it is smaller than a given limit, and falls back to allocating it on the +heap if it is larger. This can be useful for performantly allocating temporary +data if it is typically expected to be small (e.g. file paths). + +The `Utf8Value`, `TwoByteValue` (i.e. UTF-16 value) and `BufferValue` +(`Utf8Value` but copy data from a `Buffer` is that is passed) helpers +inherit from this class and allow accessing the characters in a JavaScript +string this way. + +```c++ +static void Chdir(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + // ... + CHECK(args[0]->IsString()); + Utf8Value path(env->isolate(), args[0]); + int err = uv_chdir(*path); + if (err) { + // ... error handling ... + } +} +``` + +### Assertions + +Node.js provides a few macros that behave similar to `assert()`: + +* `CHECK(expression)` aborts the process with a stack trace + if `expression` is false. +* `CHECK_EQ(a, b)` checks for `a == b` +* `CHECK_GE(a, b)` checks for `a >= b` +* `CHECK_GT(a, b)` checks for `a > b` +* `CHECK_LE(a, b)` checks for `a <= b` +* `CHECK_LT(a, b)` checks for `a < b` +* `CHECK_NE(a, b)` checks for `a != b` +* `CHECK_NULL(val)` checks for `a == nullptr` +* `CHECK_NOT_NULL(val)` checks for `a != nullptr` +* `CHECK_IMPLIES(a, b)` checks that `b` is true if `a` is true. +* `UNREACHABLE([message])` aborts the process if it is reached. + +`CHECK`s are always enabled. For checks that should only run in debug mode, use +`DCHECK()`, `DCHECK_EQ()`, etc. + +### Scope-based cleanup + +The `OnScopeLeave()` function can be used to run a piece of code when leaving +the current C++ scope. + +```c++ +static void GetUserInfo(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + uv_passwd_t pwd; + // ... + + const int err = uv_os_get_passwd(&pwd); + + if (err) { + // ... error handling, return early ... + } + + auto free_passwd = OnScopeLeave([&]() { uv_os_free_passwd(&pwd); }); + + // ... + // Turn `pwd` into a JavaScript object now; whenever we return from this + // function, `uv_os_free_passwd()` will be called. + // ... +} +``` + +[`BaseObject`]: #baseobject +[`Context`]: #context +[`Environment`]: #environment +[`Global`]: #global-handles +[`HandleWrap`]: #handlewrap +[`IsolateData`]: #isolate-data +[`Isolate`]: #isolate +[`Local`]: #local-handles +[`MakeCallback()`]: #makecallback +[`MessagePort`]: https://nodejs.org/api/worker_threads.html#worker_threads_class_messageport +[`ReqWrap`]: #reqwrap +[`async_hooks` module]: https://nodejs.org/api/async_hooks.html +[`async_wrap.h`]: async_wrap.h +[`base_object.h`]: base_object.h +[`handle_wrap.h`]: handle_wrap.h +[`memory_retainer.h`]: memory_retainer.h +[`req_wrap.h`]: req_wrap.h +[`util.h`]: util.h +[`v8.h` in Code Search]: https://cs.chromium.org/chromium/src/v8/include/v8.h +[`v8.h` in Node.js master]: https://github.com/nodejs/node/blob/master/deps/v8/include/v8.h +[`v8.h` in V8 master]: https://github.com/v8/v8/blob/master/include/v8.h +[`vm` module]: https://nodejs.org/api/vm.html +[C++ coding style]: ../CPP_STYLE_GUIDE.md +[Callback scopes]: #callback-scopes +[JavaScript value handles]: #js-handles +[N-API]: https://nodejs.org/api/n-api.html +[binding function]: #binding-functions +[cleanup hooks]: #cleanup-hooks +[event loop]: #event-loop +[exception handling]: #exception-handling +[internal field]: #internal-field +[introduction for V8 embedders]: https://v8.dev/docs/embed +[libuv handles]: #libuv-handles-and-requests +[libuv requests]: #libuv-handles-and-requests +[libuv]: https://libuv.org/ +[reference documentation for the libuv API]: http://docs.libuv.org/en/v1.x/ From 3b169f1dbd6b0e36adef1470ea21db3f5f5e417d Mon Sep 17 00:00:00 2001 From: Lucas Recknagel Date: Tue, 12 Nov 2019 15:30:48 +0000 Subject: [PATCH 014/180] http: improve performance caused by primordials MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs: https://github.com/nodejs/node/issues/29766 This works on destructuring primordials whithin libs/_http_agent PR-URL: https://github.com/nodejs/node/pull/30416 Reviewed-By: Colin Ihrig Reviewed-By: Matteo Collina Reviewed-By: Anna Henningsen Reviewed-By: James M Snell Reviewed-By: Michaël Zasso Reviewed-By: Trivikram Kamat Reviewed-By: Ruben Bridgewater Reviewed-By: Gireesh Punathil --- lib/_http_agent.js | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/_http_agent.js b/lib/_http_agent.js index f8aa395aefdb38..270569eab9ffd8 100644 --- a/lib/_http_agent.js +++ b/lib/_http_agent.js @@ -21,7 +21,13 @@ 'use strict'; -const { Object } = primordials; +const { + Object: { + setPrototypeOf: ObjectSetPrototypeOf, + keys: ObjectKeys, + values: ObjectValues + } +} = primordials; const net = require('net'); const EventEmitter = require('events'); @@ -129,8 +135,8 @@ function Agent(options) { // Don't emit keylog events unless there is a listener for them. this.on('newListener', maybeEnableKeylog); } -Object.setPrototypeOf(Agent.prototype, EventEmitter.prototype); -Object.setPrototypeOf(Agent, EventEmitter); +ObjectSetPrototypeOf(Agent.prototype, EventEmitter.prototype); +ObjectSetPrototypeOf(Agent, EventEmitter); function maybeEnableKeylog(eventName) { if (eventName === 'keylog') { @@ -141,7 +147,7 @@ function maybeEnableKeylog(eventName) { agent.emit('keylog', keylog, this); }; // Existing sockets will start listening on keylog now. - const sockets = Object.values(this.sockets); + const sockets = ObjectValues(this.sockets); for (let i = 0; i < sockets.length; i++) { sockets[i].on('keylog', this[kOnKeylog]); } @@ -381,7 +387,7 @@ Agent.prototype.destroy = function destroy() { const sets = [this.freeSockets, this.sockets]; for (let s = 0; s < sets.length; s++) { const set = sets[s]; - const keys = Object.keys(set); + const keys = ObjectKeys(set); for (let v = 0; v < keys.length; v++) { const setName = set[keys[v]]; for (let n = 0; n < setName.length; n++) { From 8256d38349992422d3a8c194f4727f26f6b5b889 Mon Sep 17 00:00:00 2001 From: Artem Maksimov Date: Wed, 6 Nov 2019 18:20:02 +0300 Subject: [PATCH 015/180] http: destructure primordials in lib/_http_server.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/30315 Reviewed-By: Anna Henningsen Reviewed-By: Colin Ihrig Reviewed-By: Trivikram Kamat Reviewed-By: Ruben Bridgewater Reviewed-By: Сковорода Никита Андреевич Reviewed-By: Gireesh Punathil --- lib/_http_server.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/_http_server.js b/lib/_http_server.js index bc956a01147ad1..59d1960297ae9c 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -21,7 +21,12 @@ 'use strict'; -const { Object } = primordials; +const { + Object: { + setPrototypeOf: ObjectSetPrototypeOf, + keys: ObjectKeys, + } +} = primordials; const net = require('net'); const assert = require('internal/assert'); @@ -162,8 +167,8 @@ function ServerResponse(req) { }; } } -Object.setPrototypeOf(ServerResponse.prototype, OutgoingMessage.prototype); -Object.setPrototypeOf(ServerResponse, OutgoingMessage); +ObjectSetPrototypeOf(ServerResponse.prototype, OutgoingMessage.prototype); +ObjectSetPrototypeOf(ServerResponse, OutgoingMessage); ServerResponse.prototype._finish = function _finish() { DTRACE_HTTP_SERVER_RESPONSE(this.socket); @@ -254,8 +259,8 @@ function writeHead(statusCode, reason, obj) { // Slow-case: when progressive API and header fields are passed. let k; if (obj) { - const keys = Object.keys(obj); - for (let i = 0; i < keys.length; i++) { + const keys = ObjectKeys(obj); + for (var i = 0; i < keys.length; i++) { k = keys[i]; if (k) this.setHeader(k, obj[k]); } @@ -337,8 +342,8 @@ function Server(options, requestListener) { this.maxHeadersCount = null; this.headersTimeout = 40 * 1000; // 40 seconds } -Object.setPrototypeOf(Server.prototype, net.Server.prototype); -Object.setPrototypeOf(Server, net.Server); +ObjectSetPrototypeOf(Server.prototype, net.Server.prototype); +ObjectSetPrototypeOf(Server, net.Server); Server.prototype.setTimeout = function setTimeout(msecs, callback) { From 44f28ea155cbeee653067623ee78d88d54f53a95 Mon Sep 17 00:00:00 2001 From: cjihrig Date: Wed, 20 Nov 2019 14:26:38 -0500 Subject: [PATCH 016/180] src: fix -Wsign-compare warnings This commit addresses the following compilation warnings: ../src/node_crypto.cc:5053:3: warning: comparison of integers of different signs: 'unsigned int' and 'int' [-Wsign-compare] CHECK_EQ(n, BN_bn2binpad(r, data, n)); ../src/node_crypto.cc:5054:3: warning: comparison of integers of different signs: 'unsigned int' and 'int' [-Wsign-compare] CHECK_EQ(n, BN_bn2binpad(s, data + n, n)); PR-URL: https://github.com/nodejs/node/pull/30565 Reviewed-By: Luigi Pinca Reviewed-By: Anna Henningsen Reviewed-By: Yongsheng Zhang Reviewed-By: David Carlier --- src/node_crypto.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index fa85f7855371b5..c4fa6e90200f5b 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -5050,8 +5050,8 @@ static AllocatedBuffer ConvertSignatureToP1363(Environment* env, const BIGNUM* r = ECDSA_SIG_get0_r(asn1_sig); const BIGNUM* s = ECDSA_SIG_get0_s(asn1_sig); - CHECK_EQ(n, BN_bn2binpad(r, data, n)); - CHECK_EQ(n, BN_bn2binpad(s, data + n, n)); + CHECK_EQ(n, static_cast(BN_bn2binpad(r, data, n))); + CHECK_EQ(n, static_cast(BN_bn2binpad(s, data + n, n))); ECDSA_SIG_free(asn1_sig); From 4e5818a456ef052050c8e789fabf3016080dbb11 Mon Sep 17 00:00:00 2001 From: Oliver Belaifa Date: Tue, 12 Nov 2019 15:43:20 +0000 Subject: [PATCH 017/180] repl: change var to let PR-URL: https://github.com/nodejs/node/pull/30428 Reviewed-By: Colin Ihrig Reviewed-By: James M Snell Reviewed-By: Trivikram Kamat Reviewed-By: Ruben Bridgewater Reviewed-By: Gireesh Punathil --- lib/internal/repl/history.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/internal/repl/history.js b/lib/internal/repl/history.js index 5d90b40e0041c7..1af974e83e1b79 100644 --- a/lib/internal/repl/history.js +++ b/lib/internal/repl/history.js @@ -41,9 +41,9 @@ function setupHistory(repl, historyPath, ready) { } } - var timer = null; - var writing = false; - var pending = false; + let timer = null; + let writing = false; + let pending = false; repl.pause(); // History files are conventionally not readable by others: // https://github.com/nodejs/node/issues/3392 From 0285aa09678944a35d14297c4a893c094c96a526 Mon Sep 17 00:00:00 2001 From: guzhizhou Date: Tue, 19 Nov 2019 20:22:12 +0800 Subject: [PATCH 018/180] events: improve performance caused by primordials MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/30577 Refs: https://github.com/nodejs/code-and-learn/issues/97 Refs: https://github.com/nodejs/node/issues/29766 Refs: https://github.com/nodejs/node/pull/29633 Reviewed-By: Colin Ihrig Reviewed-By: Michaël Zasso Reviewed-By: Yongsheng Zhang Reviewed-By: Gireesh Punathil --- lib/events.js | 45 +++++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/lib/events.js b/lib/events.js index 1356806f6544c3..f3fa65153ce2be 100644 --- a/lib/events.js +++ b/lib/events.js @@ -21,8 +21,21 @@ 'use strict'; -const { Math, Object, Reflect } = primordials; -const apply = Reflect.apply; +const { + Math: { + min: MathMin + }, + Object: { + defineProperty: ObjectDefineProperty, + getPrototypeOf: ObjectGetPrototypeOf, + create: ObjectCreate, + keys: ObjectKeys, + }, + Reflect: { + apply: ReflectApply, + ownKeys: ReflectOwnKeys, + } +} = primordials; var spliceOne; @@ -65,7 +78,7 @@ function checkListener(listener) { } } -Object.defineProperty(EventEmitter, 'defaultMaxListeners', { +ObjectDefineProperty(EventEmitter, 'defaultMaxListeners', { enumerable: true, get: function() { return defaultMaxListeners; @@ -83,8 +96,8 @@ Object.defineProperty(EventEmitter, 'defaultMaxListeners', { EventEmitter.init = function() { if (this._events === undefined || - this._events === Object.getPrototypeOf(this)._events) { - this._events = Object.create(null); + this._events === ObjectGetPrototypeOf(this)._events) { + this._events = ObjectCreate(null); this._eventsCount = 0; } @@ -121,7 +134,7 @@ function identicalSequenceRange(a, b) { const rest = b.length - pos; if (rest > 3) { let len = 1; - const maxLen = Math.min(a.length - i, rest); + const maxLen = MathMin(a.length - i, rest); // Count the number of consecutive entries. while (maxLen > len && a[i + len] === b[pos + len]) { len++; @@ -176,7 +189,7 @@ EventEmitter.prototype.emit = function emit(type, ...args) { const capture = {}; // eslint-disable-next-line no-restricted-syntax Error.captureStackTrace(capture, EventEmitter.prototype.emit); - Object.defineProperty(er, kEnhanceStackBeforeInspector, { + ObjectDefineProperty(er, kEnhanceStackBeforeInspector, { value: enhanceStackTrace.bind(this, er, capture), configurable: true }); @@ -207,12 +220,12 @@ EventEmitter.prototype.emit = function emit(type, ...args) { return false; if (typeof handler === 'function') { - apply(handler, this, args); + ReflectApply(handler, this, args); } else { const len = handler.length; const listeners = arrayClone(handler, len); for (var i = 0; i < len; ++i) - apply(listeners[i], this, args); + ReflectApply(listeners[i], this, args); } return true; @@ -227,7 +240,7 @@ function _addListener(target, type, listener, prepend) { events = target._events; if (events === undefined) { - events = target._events = Object.create(null); + events = target._events = ObjectCreate(null); target._eventsCount = 0; } else { // To avoid recursion in the case that type === "newListener"! Before @@ -341,7 +354,7 @@ EventEmitter.prototype.removeListener = if (list === listener || list.listener === listener) { if (--this._eventsCount === 0) - this._events = Object.create(null); + this._events = ObjectCreate(null); else { delete events[type]; if (events.removeListener) @@ -390,11 +403,11 @@ EventEmitter.prototype.removeAllListeners = // Not listening for removeListener, no need to emit if (events.removeListener === undefined) { if (arguments.length === 0) { - this._events = Object.create(null); + this._events = ObjectCreate(null); this._eventsCount = 0; } else if (events[type] !== undefined) { if (--this._eventsCount === 0) - this._events = Object.create(null); + this._events = ObjectCreate(null); else delete events[type]; } @@ -403,12 +416,12 @@ EventEmitter.prototype.removeAllListeners = // Emit removeListener for all listeners on all events if (arguments.length === 0) { - for (const key of Object.keys(events)) { + for (const key of ObjectKeys(events)) { if (key === 'removeListener') continue; this.removeAllListeners(key); } this.removeAllListeners('removeListener'); - this._events = Object.create(null); + this._events = ObjectCreate(null); this._eventsCount = 0; return this; } @@ -478,7 +491,7 @@ function listenerCount(type) { } EventEmitter.prototype.eventNames = function eventNames() { - return this._eventsCount > 0 ? Reflect.ownKeys(this._events) : []; + return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : []; }; function arrayClone(arr, n) { From 850c2a72eab7bb4440a11830113e88c9f2b7d724 Mon Sep 17 00:00:00 2001 From: Vladislav Botvin Date: Wed, 6 Nov 2019 17:04:11 +0300 Subject: [PATCH 019/180] fs: cover fs.opendir ERR_INVALID_CALLBACK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/30307 Reviewed-By: Anna Henningsen Reviewed-By: Colin Ihrig Reviewed-By: Trivikram Kamat Reviewed-By: Ruben Bridgewater Reviewed-By: Сковорода Никита Андреевич Reviewed-By: Gireesh Punathil Reviewed-By: Luigi Pinca Reviewed-By: James M Snell --- test/parallel/test-fs-opendir.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/parallel/test-fs-opendir.js b/test/parallel/test-fs-opendir.js index 7ae6186b28518a..abfbb2bb701b03 100644 --- a/test/parallel/test-fs-opendir.js +++ b/test/parallel/test-fs-opendir.js @@ -91,6 +91,10 @@ assert.throws(function() { fs.opendirSync(__filename); }, /Error: ENOTDIR: not a directory/); +assert.throws(function() { + fs.opendir(__filename); +}, /TypeError \[ERR_INVALID_CALLBACK\]: Callback must be a function/); + fs.opendir(__filename, common.mustCall(function(e) { assert.strictEqual(e.code, 'ENOTDIR'); })); From c50bbf58da67b5cd4650391b8bc4cf57ec888210 Mon Sep 17 00:00:00 2001 From: dnlup Date: Tue, 12 Nov 2019 17:06:53 +0100 Subject: [PATCH 020/180] readline: change var to let PR-URL: https://github.com/nodejs/node/pull/30435 Reviewed-By: Colin Ihrig Reviewed-By: David Carlier Reviewed-By: James M Snell Reviewed-By: Trivikram Kamat Reviewed-By: Ruben Bridgewater Reviewed-By: Gireesh Punathil Reviewed-By: Denys Otrishko Reviewed-By: Michael Dawson --- lib/internal/readline/utils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/readline/utils.js b/lib/internal/readline/utils.js index 31de512f74ba85..b6957f6e8a1a61 100644 --- a/lib/internal/readline/utils.js +++ b/lib/internal/readline/utils.js @@ -16,7 +16,7 @@ let isFullWidthCodePoint; function CSI(strings, ...args) { let ret = `${kEscape}[`; - for (var n = 0; n < strings.length; n++) { + for (let n = 0; n < strings.length; n++) { ret += strings[n]; if (n < args.length) ret += args[n]; @@ -79,7 +79,7 @@ if (internalBinding('config').hasIntl) { str = stripVTControlCharacters(String(str)); - for (var i = 0; i < str.length; i++) { + for (let i = 0; i < str.length; i++) { const code = str.codePointAt(i); if (code >= kUTF16SurrogateThreshold) { // Surrogates. From f7ca7e6677824903cb170d979ae4e3c2ee019098 Mon Sep 17 00:00:00 2001 From: Paolo Ceschi Berrini Date: Tue, 12 Nov 2019 15:32:24 +0000 Subject: [PATCH 021/180] http2: replace var with let/const PR-URL: https://github.com/nodejs/node/pull/30417 Reviewed-By: James M Snell Reviewed-By: Trivikram Kamat Reviewed-By: Ruben Bridgewater Reviewed-By: Gireesh Punathil --- lib/internal/http2/util.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/internal/http2/util.js b/lib/internal/http2/util.js index 9cc2a30897b6ae..a0640b242cd8b1 100644 --- a/lib/internal/http2/util.js +++ b/lib/internal/http2/util.js @@ -197,7 +197,7 @@ const IDX_OPTIONS_MAX_SESSION_MEMORY = 8; const IDX_OPTIONS_FLAGS = 9; function updateOptionsBuffer(options) { - var flags = 0; + let flags = 0; if (typeof options.maxDeflateDynamicTableSize === 'number') { flags |= (1 << IDX_OPTIONS_MAX_DEFLATE_DYNAMIC_TABLE_SIZE); optionsBuffer[IDX_OPTIONS_MAX_DEFLATE_DYNAMIC_TABLE_SIZE] = @@ -318,7 +318,7 @@ function getSettings(session, remote) { } function updateSettingsBuffer(settings) { - var flags = 0; + let flags = 0; if (typeof settings.headerTableSize === 'number') { flags |= (1 << IDX_SETTINGS_HEADER_TABLE_SIZE); settingsBuffer[IDX_SETTINGS_HEADER_TABLE_SIZE] = @@ -527,11 +527,11 @@ const assertWithinRange = hideStackFrames( function toHeaderObject(headers) { const obj = Object.create(null); for (var n = 0; n < headers.length; n = n + 2) { - var name = headers[n]; - var value = headers[n + 1]; + const name = headers[n]; + let value = headers[n + 1]; if (name === HTTP2_HEADER_STATUS) value |= 0; - var existing = obj[name]; + const existing = obj[name]; if (existing === undefined) { obj[name] = name === HTTP2_HEADER_SET_COOKIE ? [value] : value; } else if (!kSingleValueHeaders.has(name)) { From f8dfa2d704858fe2adac52054b3ee46c09edde6b Mon Sep 17 00:00:00 2001 From: palmires Date: Wed, 6 Nov 2019 16:18:06 +0200 Subject: [PATCH 022/180] test: switch to object spread in common/benchmark.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/30309 Reviewed-By: Anna Henningsen Reviewed-By: Colin Ihrig Reviewed-By: Trivikram Kamat Reviewed-By: Ruben Bridgewater Reviewed-By: Сковорода Никита Андреевич Reviewed-By: Jiawen Geng Reviewed-By: Gireesh Punathil Reviewed-By: Luigi Pinca Reviewed-By: James M Snell --- test/common/benchmark.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/common/benchmark.js b/test/common/benchmark.js index bb182fc6887636..f630bb9d0e6fd8 100644 --- a/test/common/benchmark.js +++ b/test/common/benchmark.js @@ -18,7 +18,7 @@ function runBenchmark(name, args, env) { argv.push(name); - const mergedEnv = Object.assign({}, process.env, env); + const mergedEnv = { ...process.env, ...env }; const child = fork(runjs, argv, { env: mergedEnv, From 502173b54e97e6a9e9912563c3dfcd8f114ebc37 Mon Sep 17 00:00:00 2001 From: Jing Lin Date: Tue, 12 Nov 2019 15:21:01 +0000 Subject: [PATCH 023/180] lib: replace var to let in cli_table.js PR-URL: https://github.com/nodejs/node/pull/30400 Reviewed-By: James M Snell Reviewed-By: Colin Ihrig Reviewed-By: David Carlier Reviewed-By: Gireesh Punathil Reviewed-By: Ruben Bridgewater Reviewed-By: Trivikram Kamat --- lib/internal/cli_table.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/internal/cli_table.js b/lib/internal/cli_table.js index aaa094e6a5d25f..ca8b206fd76394 100644 --- a/lib/internal/cli_table.js +++ b/lib/internal/cli_table.js @@ -30,7 +30,7 @@ const tableChars = { const renderRow = (row, columnWidths) => { let out = tableChars.left; - for (var i = 0; i < row.length; i++) { + for (let i = 0; i < row.length; i++) { const cell = row[i]; const len = getStringWidth(cell); const needed = (columnWidths[i] - len) / 2; @@ -49,9 +49,9 @@ const table = (head, columns) => { const columnWidths = head.map((h) => getStringWidth(h)); const longestColumn = columns.reduce((n, a) => Math.max(n, a.length), 0); - for (var i = 0; i < head.length; i++) { + for (let i = 0; i < head.length; i++) { const column = columns[i]; - for (var j = 0; j < longestColumn; j++) { + for (let j = 0; j < longestColumn; j++) { if (rows[j] === undefined) rows[j] = []; const value = rows[j][i] = From 017280e6e286f66b9a5418bc09719c2cd70647d8 Mon Sep 17 00:00:00 2001 From: nathias Date: Tue, 12 Nov 2019 15:57:50 +0100 Subject: [PATCH 024/180] net: replaced vars to lets and consts PR-URL: https://github.com/nodejs/node/pull/30401 Reviewed-By: James M Snell Reviewed-By: Gireesh Punathil Reviewed-By: Ruben Bridgewater Reviewed-By: Trivikram Kamat --- lib/net.js | 88 +++++++++++++++++++++++++++--------------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/lib/net.js b/lib/net.js index bdb77fdacbd856..2ff9bbd216fcfa 100644 --- a/lib/net.js +++ b/lib/net.js @@ -155,7 +155,7 @@ function createServer(options, connectionListener) { // Target API: // -// var s = net.connect({port: 80, host: 'google.com'}, function() { +// let s = net.connect({port: 80, host: 'google.com'}, function() { // ... // }); // @@ -190,7 +190,7 @@ function connect(...args) { // For Server.prototype.listen(), the [...] part is [, backlog] // but will not be handled here (handled in listen()) function normalizeArgs(args) { - var arr; + let arr; if (args.length === 0) { arr = [{}, null]; @@ -199,7 +199,7 @@ function normalizeArgs(args) { } const arg0 = args[0]; - var options = {}; + let options = {}; if (typeof arg0 === 'object' && arg0 !== null) { // (options[...][, cb]) options = arg0; @@ -382,7 +382,7 @@ ObjectSetPrototypeOf(Socket, stream.Duplex); // Refresh existing timeouts. Socket.prototype._unrefTimer = function _unrefTimer() { - for (var s = this; s !== null; s = s._parent) { + for (let s = this; s !== null; s = s._parent) { if (s[kTimeout]) s[kTimeout].refresh(); } @@ -558,7 +558,7 @@ function tryReadStart(socket) { // Not already reading, start the flow debug('Socket._handle.readStart'); socket._handle.reading = true; - var err = socket._handle.readStart(); + const err = socket._handle.readStart(); if (err) socket.destroy(errnoException(err, 'read')); } @@ -646,7 +646,7 @@ Socket.prototype._destroy = function(exception, cb) { this.readable = this.writable = false; - for (var s = this; s !== null; s = s._parent) { + for (let s = this; s !== null; s = s._parent) { clearTimeout(s[kTimeout]); } @@ -654,7 +654,7 @@ Socket.prototype._destroy = function(exception, cb) { if (this._handle) { if (this !== process.stderr) debug('close handle'); - var isException = exception ? true : false; + const isException = exception ? true : false; // `bytesRead` and `kBytesWritten` should be accessible after `.destroy()` this[kBytesRead] = this._handle.bytesRead; this[kBytesWritten] = this._handle.bytesWritten; @@ -686,8 +686,8 @@ Socket.prototype._getpeername = function() { if (!this._handle || !this._handle.getpeername) { return {}; } - var out = {}; - var err = this._handle.getpeername(out); + const out = {}; + const err = this._handle.getpeername(out); if (err) return {}; // FIXME(bnoordhuis) Throw? this._peername = out; } @@ -724,8 +724,8 @@ Socket.prototype._getsockname = function() { return {}; } if (!this._sockname) { - var out = {}; - var err = this._handle.getsockname(out); + const out = {}; + const err = this._handle.getsockname(out); if (err) return {}; // FIXME(bnoordhuis) Throw? this._sockname = out; } @@ -796,7 +796,7 @@ protoGetter('_bytesDispatched', function _bytesDispatched() { }); protoGetter('bytesWritten', function bytesWritten() { - var bytes = this._bytesDispatched; + let bytes = this._bytesDispatched; const state = this._writableState; const data = this._pendingData; const encoding = this._pendingEncoding; @@ -813,7 +813,7 @@ protoGetter('bytesWritten', function bytesWritten() { if (Array.isArray(data)) { // Was a writev, iterate over chunks to get total length - for (var i = 0; i < data.length; i++) { + for (let i = 0; i < data.length; i++) { const chunk = data[i]; if (data.allBuffers || chunk instanceof Buffer) @@ -843,7 +843,7 @@ function checkBindError(err, port, handle) { // getsockname() method. Non-issue for now, the cluster module doesn't // really support pipes anyway. if (err === 0 && port > 0 && handle.getsockname) { - var out = {}; + const out = {}; err = handle.getsockname(out); if (err === 0 && port !== out.port) { debug(`checkBindError, bound to ${out.port} instead of ${port}`); @@ -861,7 +861,7 @@ function internalConnect( assert(self.connecting); - var err; + let err; if (localAddress || localPort) { if (addressType === 4) { @@ -903,8 +903,8 @@ function internalConnect( } if (err) { - var sockname = self._getsockname(); - var details; + const sockname = self._getsockname(); + let details; if (sockname) { details = sockname.address + ':' + sockname.port; @@ -1127,15 +1127,15 @@ function afterConnect(status, handle, req, readable, writable) { } else { self.connecting = false; - var details; + let details; if (req.localAddress && req.localPort) { details = req.localAddress + ':' + req.localPort; } - var ex = exceptionWithHostPort(status, - 'connect', - req.address, - req.port, - details); + const ex = exceptionWithHostPort(status, + 'connect', + req.address, + req.port, + details); if (details) { ex.localAddress = req.localAddress; ex.localPort = req.localPort; @@ -1199,11 +1199,11 @@ function toNumber(x) { return (x = Number(x)) >= 0 ? x : false; } // Returns handle if it can be created, or error code if it can't function createServerHandle(address, port, addressType, fd, flags) { - var err = 0; + let err = 0; // Assign handle in listen, and clean up if bind or listen fails - var handle; + let handle; - var isTCP = false; + let isTCP = false; if (typeof fd === 'number' && fd >= 0) { try { handle = createHandle(fd, true); @@ -1221,7 +1221,7 @@ function createServerHandle(address, port, addressType, fd, flags) { } else if (port === -1 && addressType === -1) { handle = new Pipe(PipeConstants.SERVER); if (process.platform === 'win32') { - var instances = parseInt(process.env.NODE_PENDING_PIPE_INSTANCES); + const instances = parseInt(process.env.NODE_PENDING_PIPE_INSTANCES); if (!Number.isNaN(instances)) { handle.setPendingInstances(instances); } @@ -1266,7 +1266,7 @@ function setupListenHandle(address, port, addressType, backlog, fd, flags) { } else { debug('setupListenHandle: create a handle'); - var rval = null; + let rval = null; // Try to bind to the unspecified IPv6 address, see if IPv6 is available if (!address && typeof fd !== 'number') { @@ -1286,7 +1286,7 @@ function setupListenHandle(address, port, addressType, backlog, fd, flags) { rval = createServerHandle(address, port, addressType, fd, flags); if (typeof rval === 'number') { - var error = uvExceptionWithHostPort(rval, 'listen', address, port); + const error = uvExceptionWithHostPort(rval, 'listen', address, port); process.nextTick(emitErrorNT, this, error); return; } @@ -1303,7 +1303,7 @@ function setupListenHandle(address, port, addressType, backlog, fd, flags) { const err = this._handle.listen(backlog || 511); if (err) { - var ex = uvExceptionWithHostPort(err, 'listen', address, port); + const ex = uvExceptionWithHostPort(err, 'listen', address, port); this._handle.close(); this._handle = null; defaultTriggerAsyncIdScope(this[async_id_symbol], @@ -1370,7 +1370,7 @@ function listenInCluster(server, address, port, addressType, err = checkBindError(err, port, handle); if (err) { - var ex = exceptionWithHostPort(err, 'bind', address, port); + const ex = exceptionWithHostPort(err, 'bind', address, port); return server.emit('error', ex); } @@ -1385,7 +1385,7 @@ function listenInCluster(server, address, port, addressType, Server.prototype.listen = function(...args) { const normalized = normalizeArgs(args); - var options = normalized[0]; + let options = normalized[0]; const cb = normalized[1]; if (this._handle) { @@ -1427,7 +1427,7 @@ Server.prototype.listen = function(...args) { // ([port][, host][, backlog][, cb]) where port is specified // or (options[, cb]) where options.port is specified // or if options.port is normalized as 0 before - var backlog; + let backlog; if (typeof options.port === 'number' || typeof options.port === 'string') { if (!isLegalPort(options.port)) { throw new ERR_SOCKET_BAD_PORT(options.port); @@ -1448,7 +1448,7 @@ Server.prototype.listen = function(...args) { // (path[, backlog][, cb]) or (options[, cb]) // where path or options.path is a UNIX domain socket or Windows pipe if (options.path && isPipeName(options.path)) { - var pipeName = this._pipeName = options.path; + const pipeName = this._pipeName = options.path; backlog = options.backlog || backlogFromArgs; listenInCluster(this, pipeName, -1, -1, backlog, undefined, options.exclusive); @@ -1506,8 +1506,8 @@ ObjectDefineProperty(Server.prototype, 'listening', { Server.prototype.address = function() { if (this._handle && this._handle.getsockname) { - var out = {}; - var err = this._handle.getsockname(out); + const out = {}; + const err = this._handle.getsockname(out); if (err) { throw errnoException(err, 'address'); } @@ -1569,8 +1569,8 @@ Server.prototype.getConnections = function(cb) { } // Poll workers - var left = this._workers.length; - var total = this._connections; + let left = this._workers.length; + let total = this._connections; function oncount(err, count) { if (err) { @@ -1582,7 +1582,7 @@ Server.prototype.getConnections = function(cb) { if (--left === 0) return end(null, total); } - for (var n = 0; n < this._workers.length; n++) { + for (let n = 0; n < this._workers.length; n++) { this._workers[n].getConnections(oncount); } @@ -1607,7 +1607,7 @@ Server.prototype.close = function(cb) { } if (this._usingWorkers) { - var left = this._workers.length; + let left = this._workers.length; const onWorkerClose = () => { if (--left !== 0) return; @@ -1620,7 +1620,7 @@ Server.prototype.close = function(cb) { this._connections++; // Poll workers - for (var n = 0; n < this._workers.length; n++) + for (let n = 0; n < this._workers.length; n++) this._workers[n].close(onWorkerClose); } else { this._emitCloseIfDrained(); @@ -1690,11 +1690,11 @@ Server.prototype.unref = function() { return this; }; -var _setSimultaneousAccepts; -var warnSimultaneousAccepts = true; +let _setSimultaneousAccepts; +let warnSimultaneousAccepts = true; if (process.platform === 'win32') { - var simultaneousAccepts; + let simultaneousAccepts; _setSimultaneousAccepts = function(handle) { if (warnSimultaneousAccepts) { From 01e0571e94747b96296c36dec49fbf9c4497fdf6 Mon Sep 17 00:00:00 2001 From: telenord Date: Wed, 6 Nov 2019 17:32:13 +0300 Subject: [PATCH 025/180] test: test cover cases when trace is empty cover prepare_stack_trace in case when trace is empty PR-URL: https://github.com/nodejs/node/pull/30311 Reviewed-By: Gireesh Punathil --- test/fixtures/source-map/emptyStackError.js | 6 ++++++ test/parallel/test-source-map.js | 12 ++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 test/fixtures/source-map/emptyStackError.js diff --git a/test/fixtures/source-map/emptyStackError.js b/test/fixtures/source-map/emptyStackError.js new file mode 100644 index 00000000000000..b02367a180dab7 --- /dev/null +++ b/test/fixtures/source-map/emptyStackError.js @@ -0,0 +1,6 @@ +"use strict"; + +Error.stackTraceLimit = 0; +throw new RangeError('emptyStackError'); + + diff --git a/test/parallel/test-source-map.js b/test/parallel/test-source-map.js index c14892890cb195..71130441438dcc 100644 --- a/test/parallel/test-source-map.js +++ b/test/parallel/test-source-map.js @@ -253,6 +253,18 @@ function nextdir() { } } +// trace.length === 0 . +{ + const output = spawnSync(process.execPath, [ + '--enable-source-maps', + require.resolve('../fixtures/source-map/emptyStackError.js') + ]); + + assert.ok( + output.stderr.toString().match('emptyStackError') + ); +} + function getSourceMapFromCache(fixtureFile, coverageDirectory) { const jsonFiles = fs.readdirSync(coverageDirectory); for (const jsonFile of jsonFiles) { From 55fbe45f6952c65c0755702c45de59fb3a88377f Mon Sep 17 00:00:00 2001 From: poutch Date: Tue, 12 Nov 2019 15:11:04 +0000 Subject: [PATCH 026/180] cluster: replace var by let in shared_handle.js PR-URL: https://github.com/nodejs/node/pull/30402 Reviewed-By: James M Snell Reviewed-By: Colin Ihrig Reviewed-By: David Carlier Reviewed-By: Gireesh Punathil Reviewed-By: Ruben Bridgewater Reviewed-By: Trivikram Kamat --- lib/internal/cluster/shared_handle.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/cluster/shared_handle.js b/lib/internal/cluster/shared_handle.js index 0d0a805709a010..088798597382f8 100644 --- a/lib/internal/cluster/shared_handle.js +++ b/lib/internal/cluster/shared_handle.js @@ -11,7 +11,7 @@ function SharedHandle(key, address, port, addressType, fd, flags) { this.handle = null; this.errno = 0; - var rval; + let rval; if (addressType === 'udp4' || addressType === 'udp6') rval = dgram._createSocketHandle(address, port, addressType, fd, flags); else From d6a448825ce90d4cdac9a5d113574acdeeeb5987 Mon Sep 17 00:00:00 2001 From: Osmond van Hemert Date: Tue, 12 Nov 2019 15:24:03 +0000 Subject: [PATCH 027/180] test: dns utils replace var PR-URL: https://github.com/nodejs/node/pull/30405 Reviewed-By: James M Snell Reviewed-By: Colin Ihrig Reviewed-By: David Carlier Reviewed-By: Gireesh Punathil Reviewed-By: Ruben Bridgewater Reviewed-By: Trivikram Kamat --- lib/internal/dns/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/dns/utils.js b/lib/internal/dns/utils.js index 70097663546a1a..226d134680a661 100644 --- a/lib/internal/dns/utils.js +++ b/lib/internal/dns/utils.js @@ -52,7 +52,7 @@ class Resolver { if (typeof serv !== 'string') { throw new ERR_INVALID_ARG_TYPE(`servers[${index}]`, 'string', serv); } - var ipVersion = isIP(serv); + let ipVersion = isIP(serv); if (ipVersion !== 0) return newSet.push([ipVersion, serv, IANA_DNS_PORT]); From 48fef42ca9edafdbb0710eb2e4bce0bb311f307c Mon Sep 17 00:00:00 2001 From: Tembrechts Date: Tue, 12 Nov 2019 15:23:40 +0000 Subject: [PATCH 028/180] lib: replace var with let/const PR-URL: https://github.com/nodejs/node/pull/30404 Reviewed-By: James M Snell Reviewed-By: Gireesh Punathil Reviewed-By: Ruben Bridgewater Reviewed-By: Trivikram Kamat --- lib/timers.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/timers.js b/lib/timers.js index f7fd157b3284ef..584f6bc9529dfe 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -118,7 +118,7 @@ function setTimeout(callback, after, arg1, arg2, arg3) { throw new ERR_INVALID_CALLBACK(callback); } - var i, args; + let i, args; switch (arguments.length) { // fast cases case 1: @@ -164,7 +164,7 @@ function setInterval(callback, repeat, arg1, arg2, arg3) { throw new ERR_INVALID_CALLBACK(callback); } - var i, args; + let i, args; switch (arguments.length) { // fast cases case 1: @@ -248,7 +248,7 @@ function setImmediate(callback, arg1, arg2, arg3) { throw new ERR_INVALID_CALLBACK(callback); } - var i, args; + let i, args; switch (arguments.length) { // fast cases case 1: From 8a9ee48797934516cbb270c5ceec6446bbfcdabe Mon Sep 17 00:00:00 2001 From: Jon Church Date: Tue, 12 Nov 2019 15:12:12 +0000 Subject: [PATCH 029/180] test: change var to let in test-trace-events PR-URL: https://github.com/nodejs/node/pull/30406 Reviewed-By: James M Snell Reviewed-By: Colin Ihrig Reviewed-By: Gireesh Punathil Reviewed-By: Ruben Bridgewater Reviewed-By: Trivikram Kamat --- test/parallel/test-trace-events-all.js | 2 +- test/parallel/test-trace-events-async-hooks-dynamic.js | 2 +- test/parallel/test-trace-events-async-hooks-worker.js | 2 +- test/parallel/test-trace-events-async-hooks.js | 2 +- test/parallel/test-trace-events-file-pattern.js | 2 +- test/parallel/test-trace-events-metadata.js | 2 +- test/parallel/test-trace-events-none.js | 2 +- test/parallel/test-trace-events-v8.js | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/parallel/test-trace-events-all.js b/test/parallel/test-trace-events-all.js index d0cdd4a8749213..06e83fa6376208 100644 --- a/test/parallel/test-trace-events-all.js +++ b/test/parallel/test-trace-events-all.js @@ -6,7 +6,7 @@ const fs = require('fs'); const path = require('path'); const CODE = - 'setTimeout(() => { for (var i = 0; i < 100000; i++) { "test" + i } }, 1)'; + 'setTimeout(() => { for (let i = 0; i < 100000; i++) { "test" + i } }, 1)'; const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); diff --git a/test/parallel/test-trace-events-async-hooks-dynamic.js b/test/parallel/test-trace-events-async-hooks-dynamic.js index 9cb0d41c5494c6..b660f7f8d49ee8 100644 --- a/test/parallel/test-trace-events-async-hooks-dynamic.js +++ b/test/parallel/test-trace-events-async-hooks-dynamic.js @@ -18,7 +18,7 @@ const path = require('path'); const enable = `require("trace_events").createTracing( { categories: ["node.async_hooks"] }).enable();`; const code = - 'setTimeout(() => { for (var i = 0; i < 100000; i++) { "test" + i } }, 1)'; + 'setTimeout(() => { for (let i = 0; i < 100000; i++) { "test" + i } }, 1)'; const tmpdir = require('../common/tmpdir'); const filename = path.join(tmpdir.path, 'node_trace.1.log'); diff --git a/test/parallel/test-trace-events-async-hooks-worker.js b/test/parallel/test-trace-events-async-hooks-worker.js index 153ef301873151..185f310f89e13a 100644 --- a/test/parallel/test-trace-events-async-hooks-worker.js +++ b/test/parallel/test-trace-events-async-hooks-worker.js @@ -16,7 +16,7 @@ const fs = require('fs'); const path = require('path'); const code = - 'setTimeout(() => { for (var i = 0; i < 100000; i++) { "test" + i } }, 1)'; + 'setTimeout(() => { for (let i = 0; i < 100000; i++) { "test" + i } }, 1)'; const worker = `const { Worker } = require('worker_threads'); const worker = new Worker('${code}', diff --git a/test/parallel/test-trace-events-async-hooks.js b/test/parallel/test-trace-events-async-hooks.js index 09017e47745c49..eecce9e5e43ab3 100644 --- a/test/parallel/test-trace-events-async-hooks.js +++ b/test/parallel/test-trace-events-async-hooks.js @@ -7,7 +7,7 @@ const path = require('path'); const util = require('util'); const CODE = - 'setTimeout(() => { for (var i = 0; i < 100000; i++) { "test" + i } }, 1)'; + 'setTimeout(() => { for (let i = 0; i < 100000; i++) { "test" + i } }, 1)'; const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); diff --git a/test/parallel/test-trace-events-file-pattern.js b/test/parallel/test-trace-events-file-pattern.js index a60559b7f91b2b..d9cd8e66d23646 100644 --- a/test/parallel/test-trace-events-file-pattern.js +++ b/test/parallel/test-trace-events-file-pattern.js @@ -9,7 +9,7 @@ const path = require('path'); tmpdir.refresh(); const CODE = - 'setTimeout(() => { for (var i = 0; i < 100000; i++) { "test" + i } }, 1)'; + 'setTimeout(() => { for (let i = 0; i < 100000; i++) { "test" + i } }, 1)'; const proc = cp.spawn(process.execPath, [ '--trace-events-enabled', diff --git a/test/parallel/test-trace-events-metadata.js b/test/parallel/test-trace-events-metadata.js index 863b2175f6c8a0..dbc8fc5d1ad5f9 100644 --- a/test/parallel/test-trace-events-metadata.js +++ b/test/parallel/test-trace-events-metadata.js @@ -6,7 +6,7 @@ const fs = require('fs'); const path = require('path'); const CODE = - 'setTimeout(() => { for (var i = 0; i < 100000; i++) { "test" + i } }, 1);' + + 'setTimeout(() => { for (let i = 0; i < 100000; i++) { "test" + i } }, 1);' + 'process.title = "foo"'; const tmpdir = require('../common/tmpdir'); diff --git a/test/parallel/test-trace-events-none.js b/test/parallel/test-trace-events-none.js index dfe77e4b39369f..d6ec35677d3fe1 100644 --- a/test/parallel/test-trace-events-none.js +++ b/test/parallel/test-trace-events-none.js @@ -6,7 +6,7 @@ const fs = require('fs'); const path = require('path'); const CODE = - 'setTimeout(() => { for (var i = 0; i < 100000; i++) { "test" + i } }, 1)'; + 'setTimeout(() => { for (let i = 0; i < 100000; i++) { "test" + i } }, 1)'; const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); diff --git a/test/parallel/test-trace-events-v8.js b/test/parallel/test-trace-events-v8.js index d79838892baa0d..0215a150e8a828 100644 --- a/test/parallel/test-trace-events-v8.js +++ b/test/parallel/test-trace-events-v8.js @@ -6,7 +6,7 @@ const fs = require('fs'); const path = require('path'); const CODE = - 'setTimeout(() => { for (var i = 0; i < 100000; i++) { "test" + i } }, 1)'; + 'setTimeout(() => { for (let i = 0; i < 100000; i++) { "test" + i } }, 1)'; const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); From 8ee27ffe77ae533a70e7142443ed0d6ae3268cf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=80lvar=20P=C3=A9rez?= Date: Tue, 12 Nov 2019 15:14:25 +0000 Subject: [PATCH 030/180] fs: change var to let MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/30407 Reviewed-By: Сковорода Никита Андреевич Reviewed-By: James M Snell Reviewed-By: Colin Ihrig Reviewed-By: Gireesh Punathil Reviewed-By: Ruben Bridgewater Reviewed-By: Trivikram Kamat --- lib/internal/fs/streams.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/fs/streams.js b/lib/internal/fs/streams.js index 0d292c8a536efb..0eb90cfd6d92d7 100644 --- a/lib/internal/fs/streams.js +++ b/lib/internal/fs/streams.js @@ -360,7 +360,7 @@ WriteStream.prototype._writev = function(data, cb) { const chunks = new Array(len); let size = 0; - for (var i = 0; i < len; i++) { + for (let i = 0; i < len; i++) { const chunk = data[i].chunk; chunks[i] = chunk; From 1a2ed4a5f4ec0f88f95294436246742fbf4c83b0 Mon Sep 17 00:00:00 2001 From: Daniel Schuech Date: Tue, 12 Nov 2019 16:19:33 +0100 Subject: [PATCH 031/180] http2: core.js replace var with let const has to be used in for in loop PR-URL: https://github.com/nodejs/node/pull/30403 Reviewed-By: James M Snell Reviewed-By: David Carlier Reviewed-By: Gireesh Punathil Reviewed-By: Ruben Bridgewater Reviewed-By: Trivikram Kamat --- lib/internal/http2/core.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js index fded7067b5b3dd..dfcc9cd5dfc27b 100644 --- a/lib/internal/http2/core.js +++ b/lib/internal/http2/core.js @@ -582,7 +582,7 @@ function onOrigin(origins) { if (!session.encrypted || session.destroyed) return undefined; const originSet = initOriginSet(session); - for (var n = 0; n < origins.length; n++) + for (let n = 0; n < origins.length; n++) originSet.add(origins[n]); session.emit('origin', origins); } @@ -1496,7 +1496,7 @@ class ServerHttp2Session extends Http2Session { let arr = ''; let len = 0; const count = origins.length; - for (var i = 0; i < count; i++) { + for (let i = 0; i < count; i++) { let origin = origins[i]; if (typeof origin === 'string') { origin = (new URL(origin)).origin; @@ -2128,7 +2128,7 @@ function processHeaders(oldHeaders) { if (oldHeaders !== null && oldHeaders !== undefined) { const hop = hasOwnProperty.bind(oldHeaders); // This loop is here for performance reason. Do not change. - for (var key in oldHeaders) { + for (const key in oldHeaders) { if (hop(key)) { headers[key] = oldHeaders[key]; } From bdba03e3ed021d76f905799b24dd7332cead6e23 Mon Sep 17 00:00:00 2001 From: Dimitris Ktistakis Date: Tue, 12 Nov 2019 15:28:42 +0000 Subject: [PATCH 032/180] lib: change var to let change var to let in test-tick-processor-unknown.js PR-URL: https://github.com/nodejs/node/pull/30408 Reviewed-By: Fedor Indutny Reviewed-By: James M Snell Reviewed-By: Anna Henningsen Reviewed-By: Colin Ihrig Reviewed-By: Gireesh Punathil Reviewed-By: Ruben Bridgewater Reviewed-By: Trivikram Kamat --- test/tick-processor/test-tick-processor-unknown.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tick-processor/test-tick-processor-unknown.js b/test/tick-processor/test-tick-processor-unknown.js index 182f8c957c820a..f894ae919acf66 100644 --- a/test/tick-processor/test-tick-processor-unknown.js +++ b/test/tick-processor/test-tick-processor-unknown.js @@ -19,7 +19,7 @@ const base = require('./tick-processor-base.js'); base.runTest({ pattern: /LazyCompile.*\[eval]:1|.*% UNKNOWN/, code: `function f() { - for (var i = 0; i < 1000000; i++) { + for (let i = 0; i < 1000000; i++) { i++; } setImmediate(function() { f(); }); From 0fd89cc0f15a30021992526a731b27dc6a0144a7 Mon Sep 17 00:00:00 2001 From: Dries Stelten Date: Tue, 12 Nov 2019 15:26:40 +0000 Subject: [PATCH 033/180] lib: replace var with let/const PR-URL: https://github.com/nodejs/node/pull/30409 Reviewed-By: James M Snell Reviewed-By: Colin Ihrig Reviewed-By: Gireesh Punathil Reviewed-By: Ruben Bridgewater Reviewed-By: Trivikram Kamat --- lib/tls.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/tls.js b/lib/tls.js index 2ac43c01d32dbb..5372aadccd686d 100644 --- a/lib/tls.js +++ b/lib/tls.js @@ -109,8 +109,8 @@ function convertProtocols(protocols) { return p + 1 + len; }, 0)); - var offset = 0; - for (var i = 0, c = protocols.length; i < c; i++) { + let offset = 0; + for (let i = 0, c = protocols.length; i < c; i++) { buff[offset++] = lens[i]; buff.write(protocols[i], offset); offset += lens[i]; @@ -163,7 +163,7 @@ function check(hostParts, pattern, wildcards) { return false; // Check host parts from right to left first. - for (var i = hostParts.length - 1; i > 0; i -= 1) { + for (let i = hostParts.length - 1; i > 0; i -= 1) { if (hostParts[i] !== patternParts[i]) return false; } From 7e7a8165a820022d98b5189f2ee24428f0bf1d26 Mon Sep 17 00:00:00 2001 From: Vladimir Adamic Date: Tue, 12 Nov 2019 15:34:10 +0000 Subject: [PATCH 034/180] test: replace var with let in pre_execution.js PR-URL: https://github.com/nodejs/node/pull/30411 Reviewed-By: James M Snell Reviewed-By: Colin Ihrig Reviewed-By: Gireesh Punathil Reviewed-By: Ruben Bridgewater Reviewed-By: Trivikram Kamat --- lib/internal/bootstrap/pre_execution.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/bootstrap/pre_execution.js b/lib/internal/bootstrap/pre_execution.js index 8edec86a3e3c96..db50c3d0ee7c19 100644 --- a/lib/internal/bootstrap/pre_execution.js +++ b/lib/internal/bootstrap/pre_execution.js @@ -372,7 +372,7 @@ function initializePolicy() { const realIntegrities = new Map(); const integrityEntries = SRI.parse(experimentalPolicyIntegrity); let foundMatch = false; - for (var i = 0; i < integrityEntries.length; i++) { + for (let i = 0; i < integrityEntries.length; i++) { const { algorithm, value: expected From 9dab32f340b4fc61fbd51d3cf85cc76eef319853 Mon Sep 17 00:00:00 2001 From: Shubham Chaturvedi <19shubham11@gmail.com> Date: Tue, 12 Nov 2019 15:34:47 +0000 Subject: [PATCH 035/180] test: use spread instead of object.assign PR-URL: https://github.com/nodejs/node/pull/30412 Reviewed-By: James M Snell Reviewed-By: Colin Ihrig Reviewed-By: Gireesh Punathil Reviewed-By: Ruben Bridgewater Reviewed-By: Trivikram Kamat --- test/parallel/test-child-process-fork-args.js | 2 +- test/parallel/test-child-process-fork-exec-path.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/parallel/test-child-process-fork-args.js b/test/parallel/test-child-process-fork-args.js index 7c86616f3750c6..f2f5689d64d6ba 100644 --- a/test/parallel/test-child-process-fork-args.js +++ b/test/parallel/test-child-process-fork-args.js @@ -64,7 +64,7 @@ const expectedEnv = { foo: 'bar' }; argsLists.forEach((args) => { const cp = fork(fixtures.path('child-process-echo-options.js'), args, { - env: Object.assign({}, process.env, expectedEnv) + env: { ...process.env, ...expectedEnv } }); cp.on( diff --git a/test/parallel/test-child-process-fork-exec-path.js b/test/parallel/test-child-process-fork-exec-path.js index 6a99c5a8f55d62..38adfd72de4298 100644 --- a/test/parallel/test-child-process-fork-exec-path.js +++ b/test/parallel/test-child-process-fork-exec-path.js @@ -51,7 +51,7 @@ assert.strictEqual(fs.existsSync(copyPath), false); fs.copyFileSync(nodePath, copyPath, fs.constants.COPYFILE_FICLONE); fs.chmodSync(copyPath, '0755'); -const envCopy = Object.assign({}, process.env, { 'FORK': 'true' }); +const envCopy = { ...process.env, FORK: 'true' }; const child = fork(__filename, { execPath: copyPath, env: envCopy }); child.on('message', common.mustCall(function(recv) { assert.deepStrictEqual(recv, msg); From d8da9dacab11f6242ad23b3d5168ea200c317415 Mon Sep 17 00:00:00 2001 From: Jamar Torres Date: Tue, 12 Nov 2019 15:22:50 +0000 Subject: [PATCH 036/180] test: changed var to let in module-errors PR-URL: https://github.com/nodejs/node/pull/30413 Reviewed-By: James M Snell Reviewed-By: Colin Ihrig Reviewed-By: Gireesh Punathil Reviewed-By: Ruben Bridgewater Reviewed-By: Trivikram Kamat --- test/parallel/test-vm-module-errors.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/parallel/test-vm-module-errors.js b/test/parallel/test-vm-module-errors.js index 7a3bfccbddb3be..e5a766e37dfdb8 100644 --- a/test/parallel/test-vm-module-errors.js +++ b/test/parallel/test-vm-module-errors.js @@ -198,7 +198,7 @@ async function checkExecution() { // Check for error thrown when breakOnSigint is not a boolean for evaluate() async function checkInvalidOptionForEvaluate() { await assert.rejects(async () => { - const m = new SourceTextModule('export const a = 1; export var b = 2'); + const m = new SourceTextModule('export const a = 1; export let b = 2'); await m.evaluate({ breakOnSigint: 'a-string' }); }, { name: 'TypeError', From 4b13bca31aca5e0886ac86fa2ed9aa1454ead40a Mon Sep 17 00:00:00 2001 From: Luis Camargo Date: Tue, 12 Nov 2019 15:28:55 +0000 Subject: [PATCH 037/180] child_process: replace var with const/let in internal/child_process.js PR-URL: https://github.com/nodejs/node/pull/30414 Reviewed-By: James M Snell Reviewed-By: Trivikram Kamat Reviewed-By: Ruben Bridgewater Reviewed-By: Gireesh Punathil --- lib/internal/child_process.js | 36 +++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/internal/child_process.js b/lib/internal/child_process.js index 9e13650fa3d56a..2d4aff982b05a0 100644 --- a/lib/internal/child_process.js +++ b/lib/internal/child_process.js @@ -103,8 +103,8 @@ const handleConversion = { // The worker should keep track of the socket message.key = socket.server._connectionKey; - var firstTime = !this.channel.sockets.send[message.key]; - var socketList = getSocketList('send', this, message.key); + const firstTime = !this.channel.sockets.send[message.key]; + const socketList = getSocketList('send', this, message.key); // The server should no longer expose a .connection property // and when asked to close it should query the socket status from @@ -171,7 +171,7 @@ const handleConversion = { if (message.key) { // Add socket to connections list - var socketList = getSocketList('got', this, message.key); + const socketList = getSocketList('got', this, message.key); socketList.add({ socket: socket }); @@ -258,7 +258,7 @@ function ChildProcess() { this._handle = null; if (exitCode < 0) { - var syscall = this.spawnfile ? 'spawn ' + this.spawnfile : 'spawn'; + const syscall = this.spawnfile ? 'spawn ' + this.spawnfile : 'spawn'; const err = errnoException(exitCode, syscall); if (this.spawnfile) @@ -290,7 +290,7 @@ function flushStdio(subprocess) { if (stdio == null) return; - for (var i = 0; i < stdio.length; i++) { + for (let i = 0; i < stdio.length; i++) { const stream = stdio[i]; // TODO(addaleax): This doesn't necessarily account for all the ways in // which data can be read from a stream, e.g. being consumed on the @@ -471,7 +471,7 @@ ChildProcess.prototype.kill = function(sig) { convertToValidSignal(sig === undefined ? 'SIGTERM' : sig); if (this._handle) { - var err = this._handle.kill(signal); + const err = this._handle.kill(signal); if (err === 0) { /* Success. */ this.killed = true; @@ -611,7 +611,7 @@ function setupChannel(target, channel, serializationMode) { } assert(Array.isArray(target._handleQueue)); - var queue = target._handleQueue; + const queue = target._handleQueue; target._handleQueue = null; if (target._pendingMessage) { @@ -621,8 +621,8 @@ function setupChannel(target, channel, serializationMode) { target._pendingMessage.callback); } - for (var i = 0; i < queue.length; i++) { - var args = queue[i]; + for (let i = 0; i < queue.length; i++) { + const args = queue[i]; target._send(args.message, args.handle, args.options, args.callback); } @@ -854,7 +854,7 @@ function setupChannel(target, channel, serializationMode) { if (this._pendingMessage) closePendingHandle(this); - var fired = false; + let fired = false; function finish() { if (fired) return; fired = true; @@ -903,8 +903,8 @@ function isInternal(message) { function nop() { } function getValidStdio(stdio, sync) { - var ipc; - var ipcFd; + let ipc; + let ipcFd; // Replace shortcut with an array if (typeof stdio === 'string') { @@ -923,7 +923,7 @@ function getValidStdio(stdio, sync) { // (i.e. PipeWraps or fds) stdio = stdio.reduce((acc, stdio, i) => { function cleanup() { - for (var i = 0; i < acc.length; i++) { + for (let i = 0; i < acc.length; i++) { if ((acc[i].type === 'pipe' || acc[i].type === 'ipc') && acc[i].handle) acc[i].handle.close(); } @@ -937,7 +937,7 @@ function getValidStdio(stdio, sync) { if (stdio === 'ignore') { acc.push({ type: 'ignore' }); } else if (stdio === 'pipe' || (typeof stdio === 'number' && stdio < 0)) { - var a = { + const a = { type: 'pipe', readable: i === 0, writable: i !== 0 @@ -977,7 +977,7 @@ function getValidStdio(stdio, sync) { }); } else if (getHandleWrapType(stdio) || getHandleWrapType(stdio.handle) || getHandleWrapType(stdio._handle)) { - var handle = getHandleWrapType(stdio) ? + const handle = getHandleWrapType(stdio) ? stdio : getHandleWrapType(stdio.handle) ? stdio.handle : stdio._handle; @@ -1007,9 +1007,9 @@ function getValidStdio(stdio, sync) { function getSocketList(type, worker, key) { const sockets = worker.channel.sockets[type]; - var socketList = sockets[key]; + let socketList = sockets[key]; if (!socketList) { - var Construct = type === 'send' ? SocketListSend : SocketListReceive; + const Construct = type === 'send' ? SocketListSend : SocketListReceive; socketList = sockets[key] = new Construct(worker, key); } return socketList; @@ -1028,7 +1028,7 @@ function spawnSync(options) { const result = spawn_sync.spawn(options); if (result.output && options.encoding && options.encoding !== 'buffer') { - for (var i = 0; i < result.output.length; i++) { + for (let i = 0; i < result.output.length; i++) { if (!result.output[i]) continue; result.output[i] = result.output[i].toString(options.encoding); From d277c375fd00fb61f64556cf2093d9f9c8972029 Mon Sep 17 00:00:00 2001 From: Oliver Belaifa Date: Tue, 12 Nov 2019 15:04:37 +0000 Subject: [PATCH 038/180] lib: changed var to let MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/30427 Reviewed-By: James M Snell Reviewed-By: Anna Henningsen Reviewed-By: Michaël Zasso Reviewed-By: Colin Ihrig Reviewed-By: Gireesh Punathil Reviewed-By: Ruben Bridgewater Reviewed-By: Trivikram Kamat --- lib/internal/repl/await.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/repl/await.js b/lib/internal/repl/await.js index ed58e89529ad3b..1c7dba2efdf2d6 100644 --- a/lib/internal/repl/await.js +++ b/lib/internal/repl/await.js @@ -100,7 +100,7 @@ function processTopLevelAwait(src) { body, ancestors: [], replace(from, to, str) { - for (var i = from; i < to; i++) { + for (let i = from; i < to; i++) { wrappedArray[i] = ''; } if (from === to) str += wrappedArray[from]; From 2ecc735c4808d7ff9ebf0e39dd5dda4432865925 Mon Sep 17 00:00:00 2001 From: dnlup Date: Tue, 12 Nov 2019 16:43:37 +0100 Subject: [PATCH 039/180] test: use spread instead of Object.assign PR-URL: https://github.com/nodejs/node/pull/30419 Reviewed-By: James M Snell Reviewed-By: Anna Henningsen Reviewed-By: Colin Ihrig Reviewed-By: Gireesh Punathil Reviewed-By: Ruben Bridgewater Reviewed-By: Trivikram Kamat --- test/benchmark/test-benchmark-dns.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/benchmark/test-benchmark-dns.js b/test/benchmark/test-benchmark-dns.js index 27c3271c74d86d..811e9a44b9e358 100644 --- a/test/benchmark/test-benchmark-dns.js +++ b/test/benchmark/test-benchmark-dns.js @@ -4,7 +4,6 @@ require('../common'); const runBenchmark = require('../common/benchmark'); -const env = Object.assign({}, process.env, - { NODEJS_BENCHMARK_ZERO_ALLOWED: 1 }); +const env = { ...process.env, NODEJS_BENCHMARK_ZERO_ALLOWED: 1 }; runBenchmark('dns', ['n=1', 'all=false', 'name=127.0.0.1'], env); From 6848bfbf65cb0defde8699b28bd21c94ded37283 Mon Sep 17 00:00:00 2001 From: Guilherme Goncalves Date: Tue, 12 Nov 2019 15:46:28 +0000 Subject: [PATCH 040/180] http: replace var with let MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/30421 Reviewed-By: James M Snell Reviewed-By: Anna Henningsen Reviewed-By: Michaël Zasso Reviewed-By: Colin Ihrig Reviewed-By: David Carlier Reviewed-By: Gireesh Punathil Reviewed-By: Ruben Bridgewater Reviewed-By: Trivikram Kamat --- lib/internal/http.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/http.js b/lib/internal/http.js index fcfd2c91f22d33..09294888be5e70 100644 --- a/lib/internal/http.js +++ b/lib/internal/http.js @@ -3,8 +3,8 @@ const { setUnrefTimeout } = require('internal/timers'); const { PerformanceEntry, notify } = internalBinding('performance'); -var nowCache; -var utcCache; +let nowCache; +let utcCache; function nowDate() { if (!nowCache) cache(); From e401e8c8edf5b82370a4a28e168ae74a877a1bd5 Mon Sep 17 00:00:00 2001 From: poutch Date: Tue, 12 Nov 2019 15:49:04 +0000 Subject: [PATCH 041/180] test: change object assign to spread object change object assign to spread object in test-npm-install.js PR-URL: https://github.com/nodejs/node/pull/30422 Reviewed-By: James M Snell Reviewed-By: Anna Henningsen Reviewed-By: Luigi Pinca Reviewed-By: Colin Ihrig Reviewed-By: Gireesh Punathil Reviewed-By: Ruben Bridgewater Reviewed-By: Trivikram Kamat --- test/parallel/test-npm-install.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/parallel/test-npm-install.js b/test/parallel/test-npm-install.js index 069465466df682..1eec5f57ad6db9 100644 --- a/test/parallel/test-npm-install.js +++ b/test/parallel/test-npm-install.js @@ -38,12 +38,12 @@ const pkgPath = path.join(installDir, 'package.json'); fs.writeFileSync(pkgPath, pkgContent); -const env = Object.assign({}, process.env, { - PATH: path.dirname(process.execPath), - NPM_CONFIG_PREFIX: path.join(npmSandbox, 'npm-prefix'), - NPM_CONFIG_TMP: path.join(npmSandbox, 'npm-tmp'), - HOME: homeDir, -}); +const env = { ...process.env, + PATH: path.dirname(process.execPath), + NPM_CONFIG_PREFIX: path.join(npmSandbox, 'npm-prefix'), + NPM_CONFIG_TMP: path.join(npmSandbox, 'npm-tmp'), + HOME: homeDir, +}; exec(`${process.execPath} ${npmPath} install`, { cwd: installDir, From 7d98a59c39a78315ff20ebe7cf0be7d637f97930 Mon Sep 17 00:00:00 2001 From: Denys Otrishko Date: Fri, 22 Nov 2019 19:29:58 +0200 Subject: [PATCH 042/180] doc: add note about debugging worker_threads PR-URL: https://github.com/nodejs/node/pull/30594 Fixes: https://github.com/nodejs/node/issues/30197 Reviewed-By: Anna Henningsen Reviewed-By: Luigi Pinca Reviewed-By: Trivikram Kamat Reviewed-By: Gireesh Punathil --- doc/api/debugger.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/api/debugger.md b/doc/api/debugger.md index 823574f1bc1b69..e43cfa8a163cf2 100644 --- a/doc/api/debugger.md +++ b/doc/api/debugger.md @@ -197,5 +197,10 @@ debugging sessions.) If the Chrome browser is older than 66.0.3345.0, use `inspector.html` instead of `js_app.html` in the above URL. +Chrome DevTools doesn't support debugging [Worker Threads][] yet. +[ndb][] can be used to debug them. + [Chrome DevTools Protocol]: https://chromedevtools.github.io/devtools-protocol/ [V8 Inspector]: #debugger_v8_inspector_integration_for_node_js +[Worker Threads]: worker_threads.html +[ndb]: https://github.com/GoogleChromeLabs/ndb/ From 708e67a732b28dc1085597b9f2450af0246945e6 Mon Sep 17 00:00:00 2001 From: "Herrmann, Rene R. (656)" Date: Tue, 12 Nov 2019 15:39:28 +0000 Subject: [PATCH 043/180] cluster: replace var with let replace var with let in lib/internal/cluster/utils.js PR-URL: https://github.com/nodejs/node/pull/30425 Reviewed-By: James M Snell Reviewed-By: Anna Henningsen Reviewed-By: Colin Ihrig Reviewed-By: Gireesh Punathil Reviewed-By: Ruben Bridgewater Reviewed-By: Trivikram Kamat --- lib/internal/cluster/utils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/cluster/utils.js b/lib/internal/cluster/utils.js index f20206c842be96..b6d572fd0ea44c 100644 --- a/lib/internal/cluster/utils.js +++ b/lib/internal/cluster/utils.js @@ -6,7 +6,7 @@ module.exports = { }; const callbacks = new Map(); -var seq = 0; +let seq = 0; function sendHelper(proc, message, handle, cb) { if (!proc.connected) @@ -29,7 +29,7 @@ function internal(worker, cb) { if (message.cmd !== 'NODE_CLUSTER') return; - var fn = cb; + let fn = cb; if (message.ack !== undefined) { const callback = callbacks.get(message.ack); From 3c041edbe735005aec6f4f1de8e592efa4cb819d Mon Sep 17 00:00:00 2001 From: Semir Ajruli Date: Tue, 12 Nov 2019 16:33:00 +0100 Subject: [PATCH 044/180] lib: use let instead of var use let instead of var for lib/internal/policy/manifest.js PR-URL: https://github.com/nodejs/node/pull/30424 Reviewed-By: James M Snell Reviewed-By: Anna Henningsen Reviewed-By: Colin Ihrig Reviewed-By: Gireesh Punathil Reviewed-By: Ruben Bridgewater Reviewed-By: Trivikram Kamat --- lib/internal/policy/manifest.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/internal/policy/manifest.js b/lib/internal/policy/manifest.js index 118a89efa6f88a..e8400b672e57b0 100644 --- a/lib/internal/policy/manifest.js +++ b/lib/internal/policy/manifest.js @@ -76,7 +76,7 @@ class Manifest { const manifestEntries = entries(obj.resources); const parsedURLs = new SafeMap(); - for (var i = 0; i < manifestEntries.length; i++) { + for (let i = 0; i < manifestEntries.length; i++) { let resourceHREF = manifestEntries[i][0]; const originalHREF = resourceHREF; let resourceURL; @@ -105,8 +105,8 @@ class Manifest { mismatch = true; } else { compare: - for (var sriI = 0; sriI < sri.length; sriI++) { - for (var oldI = 0; oldI < old.length; oldI++) { + for (let sriI = 0; sriI < sri.length; sriI++) { + for (let oldI = 0; oldI < old.length; oldI++) { if (sri[sriI].algorithm === old[oldI].algorithm && BufferEquals(sri[sriI].value, old[oldI].value) && sri[sriI].options === old[oldI].options) { @@ -205,7 +205,7 @@ class Manifest { const integrityEntries = integrities.get(href); if (integrityEntries === true) return true; // Avoid clobbered Symbol.iterator - for (var i = 0; i < integrityEntries.length; i++) { + for (let i = 0; i < integrityEntries.length; i++) { const { algorithm, value: expected From d024630f441bc5212af892b16a25562adabfac44 Mon Sep 17 00:00:00 2001 From: Kyriakos Markakis Date: Tue, 12 Nov 2019 15:52:43 +0000 Subject: [PATCH 045/180] lib: change var to let in stream_base_commons PR-URL: https://github.com/nodejs/node/pull/30426 Reviewed-By: James M Snell Reviewed-By: Anna Henningsen Reviewed-By: Colin Ihrig Reviewed-By: Gireesh Punathil Reviewed-By: Ruben Bridgewater Reviewed-By: Trivikram Kamat --- lib/internal/stream_base_commons.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/internal/stream_base_commons.js b/lib/internal/stream_base_commons.js index eb2e53963d82b3..95430ff738fdb5 100644 --- a/lib/internal/stream_base_commons.js +++ b/lib/internal/stream_base_commons.js @@ -111,16 +111,15 @@ function createWriteWrap(handle) { function writevGeneric(self, data, cb) { const req = createWriteWrap(self[kHandle]); const allBuffers = data.allBuffers; - var chunks; - var i; + let chunks; if (allBuffers) { chunks = data; - for (i = 0; i < data.length; i++) + for (let i = 0; i < data.length; i++) data[i] = data[i].chunk; } else { chunks = new Array(data.length << 1); - for (i = 0; i < data.length; i++) { - var entry = data[i]; + for (let i = 0; i < data.length; i++) { + const entry = data[i]; chunks[i * 2] = entry.chunk; chunks[i * 2 + 1] = entry.encoding; } From 0b64e45e41e6f69ed027837518ab27165155694a Mon Sep 17 00:00:00 2001 From: matijagaspar Date: Tue, 12 Nov 2019 16:11:16 +0100 Subject: [PATCH 046/180] lib: main_thread_only change var to let PR-URL: https://github.com/nodejs/node/pull/30398 Reviewed-By: James M Snell Reviewed-By: Colin Ihrig Reviewed-By: Gireesh Punathil Reviewed-By: Ruben Bridgewater Reviewed-By: Trivikram Kamat --- lib/internal/process/main_thread_only.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/process/main_thread_only.js b/lib/internal/process/main_thread_only.js index 0cb3edbf9ad7b4..a610395be99cbf 100644 --- a/lib/internal/process/main_thread_only.js +++ b/lib/internal/process/main_thread_only.js @@ -77,7 +77,7 @@ function wrapPosixCredentialSetters(credentials) { if (!Array.isArray(groups)) { throw new ERR_INVALID_ARG_TYPE('groups', 'Array', groups); } - for (var i = 0; i < groups.length; i++) { + for (let i = 0; i < groups.length; i++) { validateId(groups[i], `groups[${i}]`); } // Result is 0 on success. A positive integer indicates that the From d194c0ff37fed8b160193e360ff5b33f3ce2d8e0 Mon Sep 17 00:00:00 2001 From: Aldo Ambrosioni Date: Tue, 12 Nov 2019 15:07:50 +0000 Subject: [PATCH 047/180] src: replaced var with let replaced var with let in lib/internal/cluter/master.js PR-URL: https://github.com/nodejs/node/pull/30397 Reviewed-By: James M Snell Reviewed-By: Colin Ihrig Reviewed-By: Gireesh Punathil Reviewed-By: Ruben Bridgewater Reviewed-By: Trivikram Kamat --- lib/internal/cluster/master.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/internal/cluster/master.js b/lib/internal/cluster/master.js index 005de8aa1b3cbf..645d8f1bd89931 100644 --- a/lib/internal/cluster/master.js +++ b/lib/internal/cluster/master.js @@ -29,12 +29,12 @@ cluster.settings = {}; cluster.SCHED_NONE = SCHED_NONE; // Leave it to the operating system. cluster.SCHED_RR = SCHED_RR; // Master distributes connections. -var ids = 0; -var debugPortOffset = 1; -var initialized = false; +let ids = 0; +let debugPortOffset = 1; +let initialized = false; // XXX(bnoordhuis) Fold cluster.schedulingPolicy into cluster.settings? -var schedulingPolicy = { +let schedulingPolicy = { 'none': SCHED_NONE, 'rr': SCHED_RR }[process.env.NODE_CLUSTER_SCHED_POLICY]; @@ -271,7 +271,7 @@ function queryServer(worker, message) { const key = `${message.address}:${message.port}:${message.addressType}:` + `${message.fd}:${message.index}`; - var handle = handles.get(key); + let handle = handles.get(key); if (handle === undefined) { let address = message.address; @@ -286,7 +286,7 @@ function queryServer(worker, message) { address = message.address; } - var constructor = RoundRobinHandle; + let constructor = RoundRobinHandle; // UDP is exempt from round-robin connection balancing for what should // be obvious reasons: it's connectionless. There is nothing to send to // the workers except raw datagrams and that's pointless. From dade9069c3e1b9317fad813afdc1244e77e4b3cb Mon Sep 17 00:00:00 2001 From: Nazar Malyy Date: Tue, 12 Nov 2019 16:10:13 +0000 Subject: [PATCH 048/180] test: code&learn var to let update PR-URL: https://github.com/nodejs/node/pull/30436 Reviewed-By: James M Snell Reviewed-By: Anna Henningsen Reviewed-By: Colin Ihrig Reviewed-By: Gireesh Punathil Reviewed-By: Ruben Bridgewater --- test/parallel/test-repl-save-load.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/parallel/test-repl-save-load.js b/test/parallel/test-repl-save-load.js index 66b0f400a91764..ef9ff8f6498877 100644 --- a/test/parallel/test-repl-save-load.js +++ b/test/parallel/test-repl-save-load.js @@ -44,8 +44,8 @@ testMe._domain.on('error', function(reason) { }); const testFile = [ - 'var top = function() {', - 'var inner = {one:1};' + 'let top = function() {', + 'let inner = {one:1};' ]; const saveFileName = join(tmpdir.path, 'test.save.js'); From 797b938c49e89ce06d73a023e27219bbfa68bf30 Mon Sep 17 00:00:00 2001 From: Dennis Saenger Date: Tue, 12 Nov 2019 14:55:07 +0000 Subject: [PATCH 049/180] lib: replace var with let PR-URL: https://github.com/nodejs/node/pull/30396 Reviewed-By: James M Snell Reviewed-By: Colin Ihrig Reviewed-By: Gireesh Punathil Reviewed-By: Ruben Bridgewater Reviewed-By: Trivikram Kamat --- lib/internal/v8_prof_polyfill.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/v8_prof_polyfill.js b/lib/internal/v8_prof_polyfill.js index 7d8d3ff0202d20..33e57835666650 100644 --- a/lib/internal/v8_prof_polyfill.js +++ b/lib/internal/v8_prof_polyfill.js @@ -77,7 +77,7 @@ try { const fd = fs.openSync(logFile, 'r'); const buf = Buffer.allocUnsafe(4096); const dec = new (require('string_decoder').StringDecoder)('utf-8'); -var line = ''; +let line = ''; { const message = versionCheck(peekline(), process.versions.v8); @@ -125,7 +125,7 @@ function versionCheck(firstLine, expected) { return 'Unable to read v8-version from log file.'; } // Compare major, minor and build; ignore the patch and candidate fields. - for (var i = 0; i < 3; i++) + for (let i = 0; i < 3; i++) if (curVer[i] !== firstLine[i + 1]) return 'Testing v8 version different from logging version'; } From 5522467cf5c97addecd15bb6b0e3fcd5b48779a3 Mon Sep 17 00:00:00 2001 From: cjihrig Date: Fri, 22 Nov 2019 14:17:22 -0500 Subject: [PATCH 050/180] tools: update ESLint to 6.7.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update ESLint to 6.7.1 PR-URL: https://github.com/nodejs/node/pull/30598 Reviewed-By: Luigi Pinca Reviewed-By: Michaël Zasso Reviewed-By: Trivikram Kamat --- tools/node_modules/eslint/README.md | 14 +- .../node_modules/eslint/conf/config-schema.js | 1 + .../eslint/conf/default-cli-options.js | 2 +- .../cascading-config-array-factory.js | 51 +- .../eslint/lib/cli-engine/cli-engine.js | 54 +- .../lib/cli-engine/config-array-factory.js | 114 +++- .../cli-engine/config-array/config-array.js | 13 + .../config-array/extracted-config.js | 27 + .../cli-engine/config-array/ignore-pattern.js | 231 ++++++++ .../lib/cli-engine/config-array/index.js | 2 + .../eslint/lib/cli-engine/file-enumerator.js | 73 ++- .../eslint/lib/cli-engine/ignored-paths.js | 363 ------------ .../eslint/lib/init/config-initializer.js | 7 +- .../eslint/lib/linter/report-translator.js | 80 ++- .../eslint/lib/rule-tester/rule-tester.js | 46 +- .../eslint/lib/rules/camelcase.js | 25 +- .../eslint/lib/rules/comma-dangle.js | 7 +- .../lib/rules/computed-property-spacing.js | 8 +- tools/node_modules/eslint/lib/rules/curly.js | 13 +- .../rules/function-call-argument-newline.js | 4 +- .../lib/rules/grouped-accessor-pairs.js | 224 ++++++++ tools/node_modules/eslint/lib/rules/indent.js | 11 + tools/node_modules/eslint/lib/rules/index.js | 5 + .../lib/rules/multiline-comment-style.js | 343 +++++++---- .../eslint/lib/rules/no-cond-assign.js | 18 +- .../eslint/lib/rules/no-constructor-return.js | 62 ++ .../eslint/lib/rules/no-dupe-else-if.js | 122 ++++ .../eslint/lib/rules/no-implicit-globals.js | 98 +++- .../eslint/lib/rules/no-inline-comments.js | 36 +- .../eslint/lib/rules/no-invalid-this.js | 18 +- .../eslint/lib/rules/no-octal-escape.js | 2 +- .../eslint/lib/rules/no-setter-return.js | 227 ++++++++ .../eslint/lib/rules/no-underscore-dangle.js | 27 +- .../lib/rules/no-useless-computed-key.js | 93 +-- .../eslint/lib/rules/no-useless-escape.js | 29 +- .../eslint/lib/rules/object-curly-spacing.js | 16 +- .../eslint/lib/rules/operator-assignment.js | 13 +- .../eslint/lib/rules/prefer-const.js | 21 +- .../rules/prefer-exponentiation-operator.js | 189 +++++++ .../eslint/lib/rules/require-await.js | 8 + tools/node_modules/eslint/lib/rules/semi.js | 9 +- .../eslint/lib/rules/space-infix-ops.js | 2 +- .../eslint/lib/rules/spaced-comment.js | 9 +- .../eslint/lib/rules/utils/ast-utils.js | 35 +- tools/node_modules/eslint/lib/shared/types.js | 9 + .../eslint/lib/source-code/source-code.js | 65 ++- .../eslint/node_modules/ansi-escapes/index.js | 27 +- .../node_modules/ansi-escapes/package.json | 8 +- .../node_modules/ansi-escapes/readme.md | 62 +- .../eslint/node_modules/ansi-regex/index.js | 8 +- .../node_modules/ansi-regex/package.json | 14 +- .../eslint/node_modules/ansi-regex/readme.md | 35 +- .../eslint/node_modules/glob/package.json | 5 +- .../eslint/node_modules/globals/globals.json | 5 + .../eslint/node_modules/globals/package.json | 18 +- .../eslint/node_modules/globals/readme.md | 14 +- .../eslint/node_modules/import-fresh/index.js | 21 +- .../node_modules/import-fresh/package.json | 2 +- .../node_modules/import-fresh/readme.md | 20 +- .../eslint/node_modules/optionator/README.md | 6 +- .../node_modules/optionator/lib/help.js | 19 +- .../node_modules/optionator/lib/index.js | 12 +- .../node_modules/optionator/lib/util.js | 4 +- .../node_modules/optionator/package.json | 12 +- .../node_modules/strip-ansi/index.js | 4 + .../node_modules/strip-ansi/license | 9 + .../node_modules/strip-ansi/package.json | 63 +++ .../node_modules/strip-ansi/readme.md | 46 ++ .../node_modules/string-width/package.json | 4 +- .../node_modules/string-width/readme.md | 14 +- .../node_modules/ansi-regex/index.js | 14 + .../node_modules/ansi-regex/license | 9 + .../node_modules/ansi-regex/package.json | 62 ++ .../node_modules/ansi-regex/readme.md | 87 +++ .../node_modules/type-fest/package.json | 11 +- .../eslint/node_modules/type-fest/readme.md | 531 +++++++++++++++++- .../unist-util-remove-position/package.json | 16 +- .../node_modules/vfile-location/package.json | 12 +- .../eslint/node_modules/word-wrap/LICENSE | 21 + .../eslint/node_modules/word-wrap/README.md | 182 ++++++ .../eslint/node_modules/word-wrap/index.js | 46 ++ .../node_modules/word-wrap/package.json | 114 ++++ .../eslint/node_modules/wordwrap/LICENSE | 18 - .../node_modules/wordwrap/README.markdown | 70 --- .../eslint/node_modules/wordwrap/index.js | 76 --- .../eslint/node_modules/wordwrap/package.json | 40 -- tools/node_modules/eslint/package.json | 7 +- 87 files changed, 3576 insertions(+), 998 deletions(-) create mode 100644 tools/node_modules/eslint/lib/cli-engine/config-array/ignore-pattern.js delete mode 100644 tools/node_modules/eslint/lib/cli-engine/ignored-paths.js create mode 100644 tools/node_modules/eslint/lib/rules/grouped-accessor-pairs.js create mode 100644 tools/node_modules/eslint/lib/rules/no-constructor-return.js create mode 100644 tools/node_modules/eslint/lib/rules/no-dupe-else-if.js create mode 100644 tools/node_modules/eslint/lib/rules/no-setter-return.js create mode 100644 tools/node_modules/eslint/lib/rules/prefer-exponentiation-operator.js create mode 100644 tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/index.js create mode 100644 tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/license create mode 100644 tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/package.json create mode 100644 tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/readme.md create mode 100644 tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/index.js create mode 100644 tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/license create mode 100644 tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/package.json create mode 100644 tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/readme.md create mode 100644 tools/node_modules/eslint/node_modules/word-wrap/LICENSE create mode 100644 tools/node_modules/eslint/node_modules/word-wrap/README.md create mode 100644 tools/node_modules/eslint/node_modules/word-wrap/index.js create mode 100644 tools/node_modules/eslint/node_modules/word-wrap/package.json delete mode 100644 tools/node_modules/eslint/node_modules/wordwrap/LICENSE delete mode 100644 tools/node_modules/eslint/node_modules/wordwrap/README.markdown delete mode 100644 tools/node_modules/eslint/node_modules/wordwrap/index.js delete mode 100644 tools/node_modules/eslint/node_modules/wordwrap/package.json diff --git a/tools/node_modules/eslint/README.md b/tools/node_modules/eslint/README.md index fd0c14668743bc..b7cc385f0e35c7 100644 --- a/tools/node_modules/eslint/README.md +++ b/tools/node_modules/eslint/README.md @@ -206,9 +206,9 @@ Brandon Mills Toru Nagashima - -
-Gyandeep Singh +
+
+Kai Cataldo
@@ -237,9 +237,9 @@ The people who review and implement new features. The people who review and fix bugs and help triage issues.
- -
-Kai Cataldo +
+
+Gyandeep Singh
@@ -265,7 +265,7 @@ The following companies, organizations, and individuals support ESLint's ongoing

Gold Sponsors

Shopify Salesforce Badoo Airbnb Facebook Open Source

Silver Sponsors

AMP Project

Bronze Sponsors

-

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

+

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

## Technology Sponsors diff --git a/tools/node_modules/eslint/conf/config-schema.js b/tools/node_modules/eslint/conf/config-schema.js index 164f0b4219f319..83addff8781732 100644 --- a/tools/node_modules/eslint/conf/config-schema.js +++ b/tools/node_modules/eslint/conf/config-schema.js @@ -55,6 +55,7 @@ const configSchema = { type: "object", properties: { root: { type: "boolean" }, + ignorePatterns: { $ref: "#/definitions/stringOrStrings" }, ...baseConfigProperties }, additionalProperties: false diff --git a/tools/node_modules/eslint/conf/default-cli-options.js b/tools/node_modules/eslint/conf/default-cli-options.js index abbd9184e2165c..0f7b377fb362ec 100644 --- a/tools/node_modules/eslint/conf/default-cli-options.js +++ b/tools/node_modules/eslint/conf/default-cli-options.js @@ -14,7 +14,7 @@ module.exports = { globals: [], extensions: [".js"], ignore: true, - ignorePath: null, + ignorePath: void 0, cache: false, /* diff --git a/tools/node_modules/eslint/lib/cli-engine/cascading-config-array-factory.js b/tools/node_modules/eslint/lib/cli-engine/cascading-config-array-factory.js index 6c914ea491c1b4..52703a873bb67e 100644 --- a/tools/node_modules/eslint/lib/cli-engine/cascading-config-array-factory.js +++ b/tools/node_modules/eslint/lib/cli-engine/cascading-config-array-factory.js @@ -27,7 +27,7 @@ const os = require("os"); const path = require("path"); const { validateConfigArray } = require("../shared/config-validator"); const { ConfigArrayFactory } = require("./config-array-factory"); -const { ConfigArray, ConfigDependency } = require("./config-array"); +const { ConfigArray, ConfigDependency, IgnorePattern } = require("./config-array"); const loadRules = require("./load-rules"); const debug = require("debug")("eslint:cascading-config-array-factory"); @@ -45,8 +45,9 @@ const debug = require("debug")("eslint:cascading-config-array-factory"); * @typedef {Object} CascadingConfigArrayFactoryOptions * @property {Map} [additionalPluginPool] The map for additional plugins. * @property {ConfigData} [baseConfig] The config by `baseConfig` option. - * @property {ConfigData} [cliConfig] The config by CLI options (`--env`, `--global`, `--parser`, `--parser-options`, `--plugin`, and `--rule`). CLI options overwrite the setting in config files. + * @property {ConfigData} [cliConfig] The config by CLI options (`--env`, `--global`, `--ignore-pattern`, `--parser`, `--parser-options`, `--plugin`, and `--rule`). CLI options overwrite the setting in config files. * @property {string} [cwd] The base directory to start lookup. + * @property {string} [ignorePath] The path to the alternative file of `.eslintignore`. * @property {string[]} [rulePaths] The value of `--rulesdir` option. * @property {string} [specificConfigPath] The value of `--config` option. * @property {boolean} [useEslintrc] if `false` then it doesn't load config files. @@ -62,6 +63,7 @@ const debug = require("debug")("eslint:cascading-config-array-factory"); * @property {Map} configCache The cache from directory paths to config arrays. * @property {string} cwd The base directory to start lookup. * @property {WeakMap} finalizeCache The cache from config arrays to finalized config arrays. + * @property {string} [ignorePath] The path to the alternative file of `.eslintignore`. * @property {string[]|null} rulePaths The value of `--rulesdir` option. This is used to reset `baseConfigArray`. * @property {string|null} specificConfigPath The value of `--config` option. This is used to reset `cliConfigArray`. * @property {boolean} useEslintrc if `false` then it doesn't load config files. @@ -86,14 +88,22 @@ function createBaseConfigArray({ { name: "BaseConfig" } ); + /* + * Create the config array element for the default ignore patterns. + * This element has `ignorePattern` property that ignores the default + * patterns in the current working directory. + */ + baseConfigArray.unshift(configArrayFactory.create( + { ignorePatterns: IgnorePattern.DefaultPatterns }, + { name: "DefaultIgnorePattern" } + )[0]); + + /* + * Load rules `--rulesdir` option as a pseudo plugin. + * Use a pseudo plugin to define rules of `--rulesdir`, so we can validate + * the rule's options with only information in the config array. + */ if (rulePaths && rulePaths.length > 0) { - - /* - * Load rules `--rulesdir` option as a pseudo plugin. - * Use a pseudo plugin to define rules of `--rulesdir`, so we can - * validate the rule's options with only information in the config - * array. - */ baseConfigArray.push({ name: "--rulesdir", filePath: "", @@ -128,6 +138,7 @@ function createBaseConfigArray({ function createCLIConfigArray({ cliConfigData, configArrayFactory, + ignorePath, specificConfigPath }) { const cliConfigArray = configArrayFactory.create( @@ -135,6 +146,12 @@ function createCLIConfigArray({ { name: "CLIOptions" } ); + cliConfigArray.unshift( + ...(ignorePath + ? configArrayFactory.loadESLintIgnore(ignorePath) + : configArrayFactory.loadDefaultESLintIgnore()) + ); + if (specificConfigPath) { cliConfigArray.unshift( ...configArrayFactory.loadFile( @@ -178,6 +195,7 @@ class CascadingConfigArrayFactory { baseConfig: baseConfigData = null, cliConfig: cliConfigData = null, cwd = process.cwd(), + ignorePath, resolvePluginsRelativeTo = cwd, rulePaths = [], specificConfigPath = null, @@ -200,6 +218,7 @@ class CascadingConfigArrayFactory { cliConfigArray: createCLIConfigArray({ cliConfigData, configArrayFactory, + ignorePath, specificConfigPath }), cliConfigData, @@ -207,6 +226,7 @@ class CascadingConfigArrayFactory { configCache: new Map(), cwd, finalizeCache: new WeakMap(), + ignorePath, rulePaths, specificConfigPath, useEslintrc @@ -229,9 +249,11 @@ class CascadingConfigArrayFactory { * If `filePath` was not given, it returns the config which contains only * `baseConfigData` and `cliConfigData`. * @param {string} [filePath] The file path to a file. + * @param {Object} [options] The options. + * @param {boolean} [options.ignoreNotFoundError] If `true` then it doesn't throw `ConfigurationNotFoundError`. * @returns {ConfigArray} The config array of the file. */ - getConfigArrayForFile(filePath) { + getConfigArrayForFile(filePath, { ignoreNotFoundError = false } = {}) { const { baseConfigArray, cliConfigArray, @@ -248,7 +270,8 @@ class CascadingConfigArrayFactory { return this._finalizeConfigArray( this._loadConfigInAncestors(directoryPath), - directoryPath + directoryPath, + ignoreNotFoundError ); } @@ -354,10 +377,11 @@ class CascadingConfigArrayFactory { * Concatenate `--config` and other CLI options. * @param {ConfigArray} configArray The parent config array. * @param {string} directoryPath The path to the leaf directory to find config files. + * @param {boolean} ignoreNotFoundError If `true` then it doesn't throw `ConfigurationNotFoundError`. * @returns {ConfigArray} The loaded config. * @private */ - _finalizeConfigArray(configArray, directoryPath) { + _finalizeConfigArray(configArray, directoryPath, ignoreNotFoundError) { const { cliConfigArray, configArrayFactory, @@ -403,7 +427,8 @@ class CascadingConfigArrayFactory { ); } - if (useEslintrc && finalConfigArray.length === 0) { + // At least one element (the default ignore patterns) exists. + if (!ignoreNotFoundError && useEslintrc && finalConfigArray.length <= 1) { throw new ConfigurationNotFoundError(directoryPath); } diff --git a/tools/node_modules/eslint/lib/cli-engine/cli-engine.js b/tools/node_modules/eslint/lib/cli-engine/cli-engine.js index 8afd262708fa8b..0b1c76bac6864a 100644 --- a/tools/node_modules/eslint/lib/cli-engine/cli-engine.js +++ b/tools/node_modules/eslint/lib/cli-engine/cli-engine.js @@ -25,10 +25,9 @@ const ModuleResolver = require("../shared/relative-module-resolver"); const { Linter } = require("../linter"); const builtInRules = require("../rules"); const { CascadingConfigArrayFactory } = require("./cascading-config-array-factory"); -const { getUsedExtractedConfigs } = require("./config-array"); +const { IgnorePattern, getUsedExtractedConfigs } = require("./config-array"); const { FileEnumerator } = require("./file-enumerator"); const hash = require("./hash"); -const { IgnoredPaths } = require("./ignored-paths"); const LintResultCache = require("./lint-result-cache"); const debug = require("debug")("eslint:cli-engine"); @@ -64,7 +63,7 @@ const validFixTypes = new Set(["problem", "suggestion", "layout"]); * @property {string[]} globals An array of global variables to declare. * @property {boolean} ignore False disables use of .eslintignore. * @property {string} ignorePath The ignore file to use instead of .eslintignore. - * @property {string} ignorePattern A glob pattern of files to ignore. + * @property {string|string[]} ignorePattern One or more glob patterns to ignore. * @property {boolean} useEslintrc False disables looking for .eslintrc * @property {string} parser The name of the parser to use. * @property {ParserOptions} parserOptions An object of parserOption settings to use. @@ -113,8 +112,8 @@ const validFixTypes = new Set(["problem", "suggestion", "layout"]); * @property {Map} additionalPluginPool The map for additional plugins. * @property {string} cacheFilePath The path to the cache of lint results. * @property {CascadingConfigArrayFactory} configArrayFactory The factory of configs. + * @property {(filePath: string) => boolean} defaultIgnores The default predicate function to check if a file ignored or not. * @property {FileEnumerator} fileEnumerator The file enumerator. - * @property {IgnoredPaths} ignoredPaths The ignored paths. * @property {ConfigArray[]} lastConfigArrays The list of config arrays that the last `executeOnFiles` or `executeOnText` used. * @property {LintResultCache|null} lintResultCache The cache of lint results. * @property {Linter} linter The linter instance which has loaded rules. @@ -486,13 +485,20 @@ function toBooleanMap(keys, defaultValue, displayName) { * @returns {ConfigData|null} The created config data. */ function createConfigDataFromOptions(options) { - const { parser, parserOptions, plugins, rules } = options; + const { + ignorePattern, + parser, + parserOptions, + plugins, + rules + } = options; const env = toBooleanMap(options.envs, true, "envs"); const globals = toBooleanMap(options.globals, false, "globals"); if ( env === void 0 && globals === void 0 && + (ignorePattern === void 0 || ignorePattern.length === 0) && parser === void 0 && parserOptions === void 0 && plugins === void 0 && @@ -500,7 +506,15 @@ function createConfigDataFromOptions(options) { ) { return null; } - return { env, globals, parser, parserOptions, plugins, rules }; + return { + env, + globals, + ignorePatterns: ignorePattern, + parser, + parserOptions, + plugins, + rules + }; } /** @@ -551,19 +565,18 @@ class CLIEngine { baseConfig: options.baseConfig || null, cliConfig: createConfigDataFromOptions(options), cwd: options.cwd, + ignorePath: options.ignorePath, resolvePluginsRelativeTo: options.resolvePluginsRelativeTo, rulePaths: options.rulePaths, specificConfigPath: options.configFile, useEslintrc: options.useEslintrc }); - const ignoredPaths = new IgnoredPaths(options); const fileEnumerator = new FileEnumerator({ configArrayFactory, cwd: options.cwd, extensions: options.extensions, globInputPaths: options.globInputPaths, - ignore: options.ignore, - ignoredPaths + ignore: options.ignore }); const lintResultCache = options.cache ? new LintResultCache(cacheFilePath) : null; @@ -577,8 +590,8 @@ class CLIEngine { additionalPluginPool, cacheFilePath, configArrayFactory, + defaultIgnores: IgnorePattern.createDefaultIgnore(options.cwd), fileEnumerator, - ignoredPaths, lastConfigArrays, lintResultCache, linter, @@ -835,7 +848,6 @@ class CLIEngine { const { configArrayFactory, fileEnumerator, - ignoredPaths, lastConfigArrays, linter, options: { @@ -852,7 +864,7 @@ class CLIEngine { // Clear the last used config arrays. lastConfigArrays.length = 0; - if (resolvedFilename && ignoredPaths.contains(resolvedFilename)) { + if (resolvedFilename && this.isPathIgnored(resolvedFilename)) { if (warnIgnored) { results.push(createIgnoreResult(resolvedFilename, cwd)); } @@ -926,9 +938,23 @@ class CLIEngine { * @returns {boolean} Whether or not the given path is ignored. */ isPathIgnored(filePath) { - const { ignoredPaths } = internalSlotsMap.get(this); + const { + configArrayFactory, + defaultIgnores, + options: { cwd, ignore } + } = internalSlotsMap.get(this); + const absolutePath = path.resolve(cwd, filePath); + + if (ignore) { + const config = configArrayFactory + .getConfigArrayForFile(absolutePath) + .extractConfig(absolutePath); + const ignores = config.ignores || defaultIgnores; + + return ignores(absolutePath); + } - return ignoredPaths.contains(filePath); + return defaultIgnores(absolutePath); } /** diff --git a/tools/node_modules/eslint/lib/cli-engine/config-array-factory.js b/tools/node_modules/eslint/lib/cli-engine/config-array-factory.js index cf529b6ee6313e..c444031bcb0a57 100644 --- a/tools/node_modules/eslint/lib/cli-engine/config-array-factory.js +++ b/tools/node_modules/eslint/lib/cli-engine/config-array-factory.js @@ -17,6 +17,12 @@ * Create a `ConfigArray` instance from a config file which is on a given * directory. This tries to load `.eslintrc.*` or `package.json`. If not * found, returns an empty `ConfigArray`. + * - `loadESLintIgnore(filePath)` + * Create a `ConfigArray` instance from a config file that is `.eslintignore` + * format. This is to handle `--ignore-path` option. + * - `loadDefaultESLintIgnore()` + * Create a `ConfigArray` instance from `.eslintignore` or `package.json` in + * the current working directory. * * `ConfigArrayFactory` class has the responsibility that loads configuration * files, including loading `extends`, `parser`, and `plugins`. The created @@ -40,7 +46,12 @@ const stripComments = require("strip-json-comments"); const { validateConfigSchema } = require("../shared/config-validator"); const naming = require("../shared/naming"); const ModuleResolver = require("../shared/relative-module-resolver"); -const { ConfigArray, ConfigDependency, OverrideTester } = require("./config-array"); +const { + ConfigArray, + ConfigDependency, + IgnorePattern, + OverrideTester +} = require("./config-array"); const debug = require("debug")("eslint:config-array-factory"); //------------------------------------------------------------------------------ @@ -221,6 +232,26 @@ function loadPackageJSONConfigFile(filePath) { } } +/** + * Loads a `.eslintignore` from a file. + * @param {string} filePath The filename to load. + * @returns {string[]} The ignore patterns from the file. + * @private + */ +function loadESLintIgnoreFile(filePath) { + debug(`Loading .eslintignore file: ${filePath}`); + + try { + return readFile(filePath) + .split(/\r?\n/gu) + .filter(line => line.trim() !== "" && !line.startsWith("#")); + } catch (e) { + debug(`Error reading .eslintignore file: ${filePath}`); + e.message = `Cannot read .eslintignore file: ${filePath}\nError: ${e.message}`; + throw e; + } +} + /** * Creates an error to notify about a missing config to extend from. * @param {string} configName The name of the missing config. @@ -403,6 +434,54 @@ class ConfigArrayFactory { ); } + /** + * Load `.eslintignore` file. + * @param {string} filePath The path to a `.eslintignore` file to load. + * @returns {ConfigArray} Loaded config. An empty `ConfigArray` if any config doesn't exist. + */ + loadESLintIgnore(filePath) { + const { cwd } = internalSlotsMap.get(this); + const absolutePath = path.resolve(cwd, filePath); + const name = path.relative(cwd, absolutePath); + const ignorePatterns = loadESLintIgnoreFile(absolutePath); + + return createConfigArray( + this._normalizeESLintIgnoreData(ignorePatterns, absolutePath, name) + ); + } + + /** + * Load `.eslintignore` file in the current working directory. + * @returns {ConfigArray} Loaded config. An empty `ConfigArray` if any config doesn't exist. + */ + loadDefaultESLintIgnore() { + const { cwd } = internalSlotsMap.get(this); + const eslintIgnorePath = path.resolve(cwd, ".eslintignore"); + const packageJsonPath = path.resolve(cwd, "package.json"); + + if (fs.existsSync(eslintIgnorePath)) { + return this.loadESLintIgnore(eslintIgnorePath); + } + if (fs.existsSync(packageJsonPath)) { + const data = loadJSONConfigFile(packageJsonPath); + + if (Object.hasOwnProperty.call(data, "eslintIgnore")) { + if (!Array.isArray(data.eslintIgnore)) { + throw new Error("Package.json eslintIgnore property requires an array of paths"); + } + return createConfigArray( + this._normalizeESLintIgnoreData( + data.eslintIgnore, + packageJsonPath, + "eslintIgnore in package.json" + ) + ); + } + } + + return new ConfigArray(); + } + /** * Load a given config file. * @param {string} filePath The path to a config file. @@ -451,6 +530,30 @@ class ConfigArrayFactory { return null; } + /** + * Normalize a given `.eslintignore` data to config array elements. + * @param {string[]} ignorePatterns The patterns to ignore files. + * @param {string|undefined} filePath The file path of this config. + * @param {string|undefined} name The name of this config. + * @returns {IterableIterator} The normalized config. + * @private + */ + *_normalizeESLintIgnoreData(ignorePatterns, filePath, name) { + const elements = this._normalizeObjectConfigData( + { ignorePatterns }, + filePath, + name + ); + + // Set `ignorePattern.loose` flag for backward compatibility. + for (const element of elements) { + if (element.ignorePattern) { + element.ignorePattern.loose = true; + } + yield element; + } + } + /** * Normalize a given config to an array. * @param {ConfigData} configData The config data to normalize. @@ -494,6 +597,9 @@ class ConfigArrayFactory { if (element.criteria) { element.criteria.basePath = basePath; } + if (element.ignorePattern) { + element.ignorePattern.basePath = basePath; + } /* * Merge the criteria; this is for only file extension processors in @@ -526,6 +632,7 @@ class ConfigArrayFactory { env, extends: extend, globals, + ignorePatterns, noInlineConfig, parser: parserName, parserOptions, @@ -541,6 +648,10 @@ class ConfigArrayFactory { name ) { const extendList = Array.isArray(extend) ? extend : [extend]; + const ignorePattern = ignorePatterns && new IgnorePattern( + Array.isArray(ignorePatterns) ? ignorePatterns : [ignorePatterns], + filePath ? path.dirname(filePath) : internalSlotsMap.get(this).cwd + ); // Flatten `extends`. for (const extendName of extendList.filter(Boolean)) { @@ -569,6 +680,7 @@ class ConfigArrayFactory { criteria: null, env, globals, + ignorePattern, noInlineConfig, parser, parserOptions, diff --git a/tools/node_modules/eslint/lib/cli-engine/config-array/config-array.js b/tools/node_modules/eslint/lib/cli-engine/config-array/config-array.js index 089ff305a2ba44..4fae8deaca1b69 100644 --- a/tools/node_modules/eslint/lib/cli-engine/config-array/config-array.js +++ b/tools/node_modules/eslint/lib/cli-engine/config-array/config-array.js @@ -31,6 +31,7 @@ //------------------------------------------------------------------------------ const { ExtractedConfig } = require("./extracted-config"); +const { IgnorePattern } = require("./ignore-pattern"); //------------------------------------------------------------------------------ // Helpers @@ -54,6 +55,7 @@ const { ExtractedConfig } = require("./extracted-config"); * @property {InstanceType|null} criteria The tester for the `files` and `excludedFiles` of this config element. * @property {Record|undefined} env The environment settings. * @property {Record|undefined} globals The global variable settings. + * @property {IgnorePattern|undefined} ignorePattern The ignore patterns. * @property {boolean|undefined} noInlineConfig The flag that disables directive comments. * @property {DependentParser|undefined} parser The parser loader. * @property {Object|undefined} parserOptions The parser options. @@ -231,6 +233,7 @@ function mergeRuleConfigs(target, source) { */ function createConfig(instance, indices) { const config = new ExtractedConfig(); + const ignorePatterns = []; // Merge elements. for (const index of indices) { @@ -260,6 +263,11 @@ function createConfig(instance, indices) { config.reportUnusedDisableDirectives = element.reportUnusedDisableDirectives; } + // Collect ignorePatterns + if (element.ignorePattern) { + ignorePatterns.push(element.ignorePattern); + } + // Merge others. mergeWithoutOverwrite(config.env, element.env); mergeWithoutOverwrite(config.globals, element.globals); @@ -269,6 +277,11 @@ function createConfig(instance, indices) { mergeRuleConfigs(config.rules, element.rules); } + // Create the predicate function for ignore patterns. + if (ignorePatterns.length > 0) { + config.ignores = IgnorePattern.createIgnore(ignorePatterns.reverse()); + } + return config; } diff --git a/tools/node_modules/eslint/lib/cli-engine/config-array/extracted-config.js b/tools/node_modules/eslint/lib/cli-engine/config-array/extracted-config.js index 66858313ba6220..b27d6ffb188e58 100644 --- a/tools/node_modules/eslint/lib/cli-engine/config-array/extracted-config.js +++ b/tools/node_modules/eslint/lib/cli-engine/config-array/extracted-config.js @@ -16,6 +16,8 @@ */ "use strict"; +const { IgnorePattern } = require("./ignore-pattern"); + // For VSCode intellisense /** @typedef {import("../../shared/types").ConfigData} ConfigData */ /** @typedef {import("../../shared/types").GlobalConf} GlobalConf */ @@ -23,6 +25,17 @@ /** @typedef {import("./config-dependency").DependentParser} DependentParser */ /** @typedef {import("./config-dependency").DependentPlugin} DependentPlugin */ +/** + * Check if `xs` starts with `ys`. + * @template T + * @param {T[]} xs The array to check. + * @param {T[]} ys The array that may be the first part of `xs`. + * @returns {boolean} `true` if `xs` starts with `ys`. + */ +function startsWith(xs, ys) { + return xs.length >= ys.length && ys.every((y, i) => y === xs[i]); +} + /** * The class for extracted config data. */ @@ -47,6 +60,12 @@ class ExtractedConfig { */ this.globals = {}; + /** + * The glob patterns that ignore to lint. + * @type {(((filePath:string, dot?:boolean) => boolean) & { basePath:string; patterns:string[] }) | undefined} + */ + this.ignores = void 0; + /** * The flag that disables directive comments. * @type {boolean|undefined} @@ -106,11 +125,19 @@ class ExtractedConfig { configNameOfNoInlineConfig: _ignore1, processor: _ignore2, /* eslint-enable no-unused-vars */ + ignores, ...config } = this; config.parser = config.parser && config.parser.filePath; config.plugins = Object.keys(config.plugins).filter(Boolean).reverse(); + config.ignorePatterns = ignores ? ignores.patterns : []; + + // Strip the default patterns from `ignorePatterns`. + if (startsWith(config.ignorePatterns, IgnorePattern.DefaultPatterns)) { + config.ignorePatterns = + config.ignorePatterns.slice(IgnorePattern.DefaultPatterns.length); + } return config; } diff --git a/tools/node_modules/eslint/lib/cli-engine/config-array/ignore-pattern.js b/tools/node_modules/eslint/lib/cli-engine/config-array/ignore-pattern.js new file mode 100644 index 00000000000000..6140194433e861 --- /dev/null +++ b/tools/node_modules/eslint/lib/cli-engine/config-array/ignore-pattern.js @@ -0,0 +1,231 @@ +/** + * @fileoverview `IgnorePattern` class. + * + * `IgnorePattern` class has the set of glob patterns and the base path. + * + * It provides two static methods. + * + * - `IgnorePattern.createDefaultIgnore(cwd)` + * Create the default predicate function. + * - `IgnorePattern.createIgnore(ignorePatterns)` + * Create the predicate function from multiple `IgnorePattern` objects. + * + * It provides two properties and a method. + * + * - `patterns` + * The glob patterns that ignore to lint. + * - `basePath` + * The base path of the glob patterns. If absolute paths existed in the + * glob patterns, those are handled as relative paths to the base path. + * - `getPatternsRelativeTo(basePath)` + * Get `patterns` as modified for a given base path. It modifies the + * absolute paths in the patterns as prepending the difference of two base + * paths. + * + * `ConfigArrayFactory` creates `IgnorePattern` objects when it processes + * `ignorePatterns` properties. + * + * @author Toru Nagashima + */ +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const assert = require("assert"); +const path = require("path"); +const ignore = require("ignore"); +const debug = require("debug")("eslint:ignore-pattern"); + +/** @typedef {ReturnType} Ignore */ + +//------------------------------------------------------------------------------ +// Helpers +//------------------------------------------------------------------------------ + +/** + * Get the path to the common ancestor directory of given paths. + * @param {string[]} sourcePaths The paths to calculate the common ancestor. + * @returns {string} The path to the common ancestor directory. + */ +function getCommonAncestorPath(sourcePaths) { + let result = sourcePaths[0]; + + for (let i = 1; i < sourcePaths.length; ++i) { + const a = result; + const b = sourcePaths[i]; + + // Set the shorter one (it's the common ancestor if one includes the other). + result = a.length < b.length ? a : b; + + // Set the common ancestor. + for (let j = 0, lastSepPos = 0; j < a.length && j < b.length; ++j) { + if (a[j] !== b[j]) { + result = a.slice(0, lastSepPos); + break; + } + if (a[j] === path.sep) { + lastSepPos = j; + } + } + } + + return result || path.sep; +} + +/** + * Make relative path. + * @param {string} from The source path to get relative path. + * @param {string} to The destination path to get relative path. + * @returns {string} The relative path. + */ +function relative(from, to) { + const relPath = path.relative(from, to); + + if (path.sep === "/") { + return relPath; + } + return relPath.split(path.sep).join("/"); +} + +/** + * Get the trailing slash if existed. + * @param {string} filePath The path to check. + * @returns {string} The trailing slash if existed. + */ +function dirSuffix(filePath) { + const isDir = ( + filePath.endsWith(path.sep) || + (process.platform === "win32" && filePath.endsWith("/")) + ); + + return isDir ? "/" : ""; +} + +const DefaultPatterns = Object.freeze(["/node_modules/*", "/bower_components/*"]); +const DotPatterns = Object.freeze([".*", "!../"]); + +//------------------------------------------------------------------------------ +// Public +//------------------------------------------------------------------------------ + +class IgnorePattern { + + /** + * The default patterns. + * @type {string[]} + */ + static get DefaultPatterns() { + return DefaultPatterns; + } + + /** + * Create the default predicate function. + * @param {string} cwd The current working directory. + * @returns {((filePath:string, dot:boolean) => boolean) & {basePath:string; patterns:string[]}} + * The preficate function. + * The first argument is an absolute path that is checked. + * The second argument is the flag to not ignore dotfiles. + * If the predicate function returned `true`, it means the path should be ignored. + */ + static createDefaultIgnore(cwd) { + return this.createIgnore([new IgnorePattern(DefaultPatterns, cwd)]); + } + + /** + * Create the predicate function from multiple `IgnorePattern` objects. + * @param {IgnorePattern[]} ignorePatterns The list of ignore patterns. + * @returns {((filePath:string, dot?:boolean) => boolean) & {basePath:string; patterns:string[]}} + * The preficate function. + * The first argument is an absolute path that is checked. + * The second argument is the flag to not ignore dotfiles. + * If the predicate function returned `true`, it means the path should be ignored. + */ + static createIgnore(ignorePatterns) { + debug("Create with: %o", ignorePatterns); + + const basePath = getCommonAncestorPath(ignorePatterns.map(p => p.basePath)); + const patterns = [].concat( + ...ignorePatterns.map(p => p.getPatternsRelativeTo(basePath)) + ); + const ig = ignore().add([...DotPatterns, ...patterns]); + const dotIg = ignore().add(patterns); + + debug(" processed: %o", { basePath, patterns }); + + return Object.assign( + (filePath, dot = false) => { + assert(path.isAbsolute(filePath), "'filePath' should be an absolute path."); + const relPathRaw = relative(basePath, filePath); + const relPath = relPathRaw && (relPathRaw + dirSuffix(filePath)); + const adoptedIg = dot ? dotIg : ig; + const result = relPath !== "" && adoptedIg.ignores(relPath); + + debug("Check", { filePath, dot, relativePath: relPath, result }); + return result; + }, + { basePath, patterns } + ); + } + + /** + * Initialize a new `IgnorePattern` instance. + * @param {string[]} patterns The glob patterns that ignore to lint. + * @param {string} basePath The base path of `patterns`. + */ + constructor(patterns, basePath) { + assert(path.isAbsolute(basePath), "'basePath' should be an absolute path."); + + /** + * The glob patterns that ignore to lint. + * @type {string[]} + */ + this.patterns = patterns; + + /** + * The base path of `patterns`. + * @type {string} + */ + this.basePath = basePath; + + /** + * If `true` then patterns which don't start with `/` will match the paths to the outside of `basePath`. Defaults to `false`. + * + * It's set `true` for `.eslintignore`, `package.json`, and `--ignore-path` for backward compatibility. + * It's `false` as-is for `ignorePatterns` property in config files. + * @type {boolean} + */ + this.loose = false; + } + + /** + * Get `patterns` as modified for a given base path. It modifies the + * absolute paths in the patterns as prepending the difference of two base + * paths. + * @param {string} newBasePath The base path. + * @returns {string[]} Modifired patterns. + */ + getPatternsRelativeTo(newBasePath) { + assert(path.isAbsolute(newBasePath), "'newBasePath' should be an absolute path."); + const { basePath, loose, patterns } = this; + + if (newBasePath === basePath) { + return patterns; + } + const prefix = `/${relative(newBasePath, basePath)}`; + + return patterns.map(pattern => { + const negative = pattern.startsWith("!"); + const head = negative ? "!" : ""; + const body = negative ? pattern.slice(1) : pattern; + + if (body.startsWith("/") || body.startsWith("../")) { + return `${head}${prefix}${body}`; + } + return loose ? pattern : `${head}${prefix}/**/${body}`; + }); + } +} + +module.exports = { IgnorePattern }; diff --git a/tools/node_modules/eslint/lib/cli-engine/config-array/index.js b/tools/node_modules/eslint/lib/cli-engine/config-array/index.js index de8831906fd6ef..928d76c83ab1f7 100644 --- a/tools/node_modules/eslint/lib/cli-engine/config-array/index.js +++ b/tools/node_modules/eslint/lib/cli-engine/config-array/index.js @@ -7,12 +7,14 @@ const { ConfigArray, getUsedExtractedConfigs } = require("./config-array"); const { ConfigDependency } = require("./config-dependency"); const { ExtractedConfig } = require("./extracted-config"); +const { IgnorePattern } = require("./ignore-pattern"); const { OverrideTester } = require("./override-tester"); module.exports = { ConfigArray, ConfigDependency, ExtractedConfig, + IgnorePattern, OverrideTester, getUsedExtractedConfigs }; diff --git a/tools/node_modules/eslint/lib/cli-engine/file-enumerator.js b/tools/node_modules/eslint/lib/cli-engine/file-enumerator.js index 38f55de039d6d1..700f8009cf88f2 100644 --- a/tools/node_modules/eslint/lib/cli-engine/file-enumerator.js +++ b/tools/node_modules/eslint/lib/cli-engine/file-enumerator.js @@ -40,8 +40,8 @@ const getGlobParent = require("glob-parent"); const isGlob = require("is-glob"); const { escapeRegExp } = require("lodash"); const { Minimatch } = require("minimatch"); +const { IgnorePattern } = require("./config-array"); const { CascadingConfigArrayFactory } = require("./cascading-config-array-factory"); -const { IgnoredPaths } = require("./ignored-paths"); const debug = require("debug")("eslint:file-enumerator"); //------------------------------------------------------------------------------ @@ -64,7 +64,6 @@ const IGNORED = 2; * @property {string[]} [extensions] The extensions to match files for directory patterns. * @property {boolean} [globInputPaths] Set to false to skip glob resolution of input file paths to lint (default: true). If false, each input file paths is assumed to be a non-glob path to an existing file. * @property {boolean} [ignore] The flag to check ignored files. - * @property {IgnoredPaths} [ignoredPaths] The ignored paths. * @property {string[]} [rulePaths] The value of `--rulesdir` option. */ @@ -92,8 +91,7 @@ const IGNORED = 2; * @property {RegExp} extensionRegExp The RegExp to test if a string ends with specific file extensions. * @property {boolean} globInputPaths Set to false to skip glob resolution of input file paths to lint (default: true). If false, each input file paths is assumed to be a non-glob path to an existing file. * @property {boolean} ignoreFlag The flag to check ignored files. - * @property {IgnoredPaths} ignoredPathsWithDotfiles The ignored paths but don't include dot files. - * @property {IgnoredPaths} ignoredPaths The ignored paths. + * @property {(filePath:string, dot:boolean) => boolean} defaultIgnores The default predicate function to ignore files. */ /** @type {WeakMap} */ @@ -192,12 +190,12 @@ class FileEnumerator { configArrayFactory = new CascadingConfigArrayFactory({ cwd }), extensions = [".js"], globInputPaths = true, - ignore = true, - ignoredPaths = new IgnoredPaths({ cwd, ignore }) + ignore = true } = {}) { internalSlotsMap.set(this, { configArrayFactory, cwd, + defaultIgnores: IgnorePattern.createDefaultIgnore(cwd), extensionRegExp: new RegExp( `.\\.(?:${extensions .map(ext => escapeRegExp( @@ -210,12 +208,7 @@ class FileEnumerator { "u" ), globInputPaths, - ignoreFlag: ignore, - ignoredPaths, - ignoredPathsWithDotfiles: new IgnoredPaths({ - ...ignoredPaths.options, - dotfiles: true - }) + ignoreFlag: ignore }); } @@ -321,7 +314,7 @@ class FileEnumerator { const { configArrayFactory } = internalSlotsMap.get(this); const config = configArrayFactory.getConfigArrayForFile(filePath); - const ignored = this._isIgnoredFile(filePath, { direct: true }); + const ignored = this._isIgnoredFile(filePath, { config, direct: true }); const flag = ignored ? IGNORED : NONE; return [{ config, filePath, flag }]; @@ -353,7 +346,7 @@ class FileEnumerator { _iterateFilesWithGlob(pattern, dotfiles) { debug(`Glob: ${pattern}`); - const directoryPath = getGlobParent(pattern); + const directoryPath = path.resolve(getGlobParent(pattern)); const globPart = pattern.slice(directoryPath.length + 1); /* @@ -399,9 +392,18 @@ class FileEnumerator { // Check if the file is matched. if (stat && stat.isFile()) { if (!config) { - config = configArrayFactory.getConfigArrayForFile(filePath); + config = configArrayFactory.getConfigArrayForFile( + filePath, + + /* + * We must ignore `ConfigurationNotFoundError` at this + * point because we don't know if target files exist in + * this directory. + */ + { ignoreNotFoundError: true } + ); } - const ignored = this._isIgnoredFile(filePath, options); + const ignored = this._isIgnoredFile(filePath, { ...options, config }); const flag = ignored ? IGNORED_SILENTLY : NONE; const matched = options.selector @@ -413,7 +415,11 @@ class FileEnumerator { if (matched) { debug(`Yield: ${filename}${ignored ? " but ignored" : ""}`); - yield { config, filePath, flag }; + yield { + config: configArrayFactory.getConfigArrayForFile(filePath), + filePath, + flag + }; } else { debug(`Didn't match: ${filename}`); } @@ -431,24 +437,37 @@ class FileEnumerator { * Check if a given file should be ignored. * @param {string} filePath The path to a file to check. * @param {Object} options Options + * @param {ConfigArray} [options.config] The config for this file. * @param {boolean} [options.dotfiles] If `true` then this is not ignore dot files by default. * @param {boolean} [options.direct] If `true` then this is a direct specified file. * @returns {boolean} `true` if the file should be ignored. * @private */ - _isIgnoredFile(filePath, { dotfiles = false, direct = false }) { + _isIgnoredFile(filePath, { + config: providedConfig, + dotfiles = false, + direct = false + }) { const { - ignoreFlag, - ignoredPaths, - ignoredPathsWithDotfiles + configArrayFactory, + defaultIgnores, + ignoreFlag } = internalSlotsMap.get(this); - const adoptedIgnoredPaths = dotfiles - ? ignoredPathsWithDotfiles - : ignoredPaths; - return ignoreFlag - ? adoptedIgnoredPaths.contains(filePath) - : (!direct && adoptedIgnoredPaths.contains(filePath, "default")); + if (ignoreFlag) { + const config = + providedConfig || + configArrayFactory.getConfigArrayForFile( + filePath, + { ignoreNotFoundError: true } + ); + const ignores = + config.extractConfig(filePath).ignores || defaultIgnores; + + return ignores(filePath, dotfiles); + } + + return !direct && defaultIgnores(filePath, dotfiles); } } diff --git a/tools/node_modules/eslint/lib/cli-engine/ignored-paths.js b/tools/node_modules/eslint/lib/cli-engine/ignored-paths.js deleted file mode 100644 index dec8e1860420e2..00000000000000 --- a/tools/node_modules/eslint/lib/cli-engine/ignored-paths.js +++ /dev/null @@ -1,363 +0,0 @@ -/** - * @fileoverview Responsible for loading ignore config files and managing ignore patterns - * @author Jonathan Rajavuori - */ - -"use strict"; - -//------------------------------------------------------------------------------ -// Requirements -//------------------------------------------------------------------------------ - -const fs = require("fs"), - path = require("path"), - ignore = require("ignore"); - -const debug = require("debug")("eslint:ignored-paths"); - -//------------------------------------------------------------------------------ -// Constants -//------------------------------------------------------------------------------ - -const ESLINT_IGNORE_FILENAME = ".eslintignore"; - -/** - * Adds `"*"` at the end of `"node_modules/"`, - * so that subtle directories could be re-included by .gitignore patterns - * such as `"!node_modules/should_not_ignored"` - */ -const DEFAULT_IGNORE_DIRS = [ - "/node_modules/*", - "/bower_components/*" -]; -const DEFAULT_OPTIONS = { - dotfiles: false, - cwd: process.cwd() -}; - -//------------------------------------------------------------------------------ -// Helpers -//------------------------------------------------------------------------------ - -/** - * Find a file in the current directory. - * @param {string} cwd Current working directory - * @param {string} name File name - * @returns {string} Path of ignore file or an empty string. - */ -function findFile(cwd, name) { - const ignoreFilePath = path.resolve(cwd, name); - - return fs.existsSync(ignoreFilePath) && fs.statSync(ignoreFilePath).isFile() ? ignoreFilePath : ""; -} - -/** - * Find an ignore file in the current directory. - * @param {string} cwd Current working directory - * @returns {string} Path of ignore file or an empty string. - */ -function findIgnoreFile(cwd) { - return findFile(cwd, ESLINT_IGNORE_FILENAME); -} - -/** - * Find an package.json file in the current directory. - * @param {string} cwd Current working directory - * @returns {string} Path of package.json file or an empty string. - */ -function findPackageJSONFile(cwd) { - return findFile(cwd, "package.json"); -} - -/** - * Merge options with defaults - * @param {Object} options Options to merge with DEFAULT_OPTIONS constant - * @returns {Object} Merged options - */ -function mergeDefaultOptions(options) { - return Object.assign({}, DEFAULT_OPTIONS, options); -} - -/* eslint-disable jsdoc/check-param-names, jsdoc/require-param */ -/** - * Normalize the path separators in a given string. - * On Windows environment, this replaces `\` by `/`. - * Otherwrise, this does nothing. - * @param {string} str The path string to normalize. - * @returns {string} The normalized path. - */ -const normalizePathSeps = path.sep === "/" - ? (str => str) - : ((seps, str) => str.replace(seps, "/")).bind(null, new RegExp(`\\${path.sep}`, "gu")); -/* eslint-enable jsdoc/check-param-names, jsdoc/require-param */ - -/** - * Converts a glob pattern to a new glob pattern relative to a different directory - * @param {string} globPattern The glob pattern, relative the the old base directory - * @param {string} relativePathToOldBaseDir A relative path from the new base directory to the old one - * @returns {string} A glob pattern relative to the new base directory - */ -function relativize(globPattern, relativePathToOldBaseDir) { - if (relativePathToOldBaseDir === "") { - return globPattern; - } - - const prefix = globPattern.startsWith("!") ? "!" : ""; - const globWithoutPrefix = globPattern.replace(/^!/u, ""); - - if (globWithoutPrefix.startsWith("/")) { - return `${prefix}/${normalizePathSeps(relativePathToOldBaseDir)}${globWithoutPrefix}`; - } - - return globPattern; -} - -//------------------------------------------------------------------------------ -// Public Interface -//------------------------------------------------------------------------------ - -/** - * IgnoredPaths class - */ -class IgnoredPaths { - - // eslint-disable-next-line jsdoc/require-description - /** - * @param {Object} providedOptions object containing 'ignore', 'ignorePath' and 'patterns' properties - */ - constructor(providedOptions) { - const options = mergeDefaultOptions(providedOptions); - - this.cache = {}; - - this.defaultPatterns = [].concat(DEFAULT_IGNORE_DIRS, options.patterns || []); - - this.ignoreFileDir = options.ignore !== false && options.ignorePath - ? path.dirname(path.resolve(options.cwd, options.ignorePath)) - : options.cwd; - this.options = options; - this._baseDir = null; - - this.ig = { - custom: ignore(), - default: ignore() - }; - - this.defaultPatterns.forEach(pattern => this.addPatternRelativeToCwd(this.ig.default, pattern)); - if (options.dotfiles !== true) { - - /* - * ignore files beginning with a dot, but not files in a parent or - * ancestor directory (which in relative format will begin with `../`). - */ - this.addPatternRelativeToCwd(this.ig.default, ".*"); - this.addPatternRelativeToCwd(this.ig.default, "!../"); - } - - /* - * Add a way to keep track of ignored files. This was present in node-ignore - * 2.x, but dropped for now as of 3.0.10. - */ - this.ig.custom.ignoreFiles = []; - this.ig.default.ignoreFiles = []; - - if (options.ignore !== false) { - let ignorePath; - - if (options.ignorePath) { - debug("Using specific ignore file"); - - try { - const stat = fs.statSync(options.ignorePath); - - if (!stat.isFile()) { - throw new Error(`${options.ignorePath} is not a file`); - } - ignorePath = options.ignorePath; - } catch (e) { - e.message = `Cannot read ignore file: ${options.ignorePath}\nError: ${e.message}`; - throw e; - } - } else { - debug(`Looking for ignore file in ${options.cwd}`); - ignorePath = findIgnoreFile(options.cwd); - - try { - fs.statSync(ignorePath); - debug(`Loaded ignore file ${ignorePath}`); - } catch (e) { - debug("Could not find ignore file in cwd"); - } - } - - if (ignorePath) { - debug(`Adding ${ignorePath}`); - this.addIgnoreFile(this.ig.custom, ignorePath); - this.addIgnoreFile(this.ig.default, ignorePath); - } else { - try { - - // if the ignoreFile does not exist, check package.json for eslintIgnore - const packageJSONPath = findPackageJSONFile(options.cwd); - - if (packageJSONPath) { - let packageJSONOptions; - - try { - packageJSONOptions = JSON.parse(fs.readFileSync(packageJSONPath, "utf8")); - } catch (e) { - debug("Could not read package.json file to check eslintIgnore property"); - e.messageTemplate = "failed-to-read-json"; - e.messageData = { - path: packageJSONPath, - message: e.message - }; - throw e; - } - - if (packageJSONOptions.eslintIgnore) { - if (Array.isArray(packageJSONOptions.eslintIgnore)) { - packageJSONOptions.eslintIgnore.forEach(pattern => { - this.addPatternRelativeToIgnoreFile(this.ig.custom, pattern); - this.addPatternRelativeToIgnoreFile(this.ig.default, pattern); - }); - } else { - throw new TypeError("Package.json eslintIgnore property requires an array of paths"); - } - } - } - } catch (e) { - debug("Could not find package.json to check eslintIgnore property"); - throw e; - } - } - - if (options.ignorePattern) { - this.addPatternRelativeToCwd(this.ig.custom, options.ignorePattern); - this.addPatternRelativeToCwd(this.ig.default, options.ignorePattern); - } - } - } - - /* - * If `ignoreFileDir` is a subdirectory of `cwd`, all paths will be normalized to be relative to `cwd`. - * Otherwise, all paths will be normalized to be relative to `ignoreFileDir`. - * This ensures that the final normalized ignore rule will not contain `..`, which is forbidden in - * ignore rules. - */ - - addPatternRelativeToCwd(ig, pattern) { - const baseDir = this.getBaseDir(); - const cookedPattern = baseDir === this.options.cwd - ? pattern - : relativize(pattern, path.relative(baseDir, this.options.cwd)); - - ig.addPattern(cookedPattern); - debug("addPatternRelativeToCwd:\n original = %j\n cooked = %j", pattern, cookedPattern); - } - - addPatternRelativeToIgnoreFile(ig, pattern) { - const baseDir = this.getBaseDir(); - const cookedPattern = baseDir === this.ignoreFileDir - ? pattern - : relativize(pattern, path.relative(baseDir, this.ignoreFileDir)); - - ig.addPattern(cookedPattern); - debug("addPatternRelativeToIgnoreFile:\n original = %j\n cooked = %j", pattern, cookedPattern); - } - - // Detect the common ancestor - getBaseDir() { - if (!this._baseDir) { - const a = path.resolve(this.options.cwd); - const b = path.resolve(this.ignoreFileDir); - let lastSepPos = 0; - - // Set the shorter one (it's the common ancestor if one includes the other). - this._baseDir = a.length < b.length ? a : b; - - // Set the common ancestor. - for (let i = 0; i < a.length && i < b.length; ++i) { - if (a[i] !== b[i]) { - this._baseDir = a.slice(0, lastSepPos); - break; - } - if (a[i] === path.sep) { - lastSepPos = i; - } - } - - // If it's only Windows drive letter, it needs \ - if (/^[A-Z]:$/u.test(this._baseDir)) { - this._baseDir += "\\"; - } - - debug("baseDir = %j", this._baseDir); - } - return this._baseDir; - } - - /** - * read ignore filepath - * @param {string} filePath file to add to ig - * @returns {Array} raw ignore rules - */ - readIgnoreFile(filePath) { - if (typeof this.cache[filePath] === "undefined") { - this.cache[filePath] = fs.readFileSync(filePath, "utf8").split(/\r?\n/gu).filter(Boolean); - } - return this.cache[filePath]; - } - - /** - * add ignore file to node-ignore instance - * @param {Object} ig instance of node-ignore - * @param {string} filePath file to add to ig - * @returns {void} - */ - addIgnoreFile(ig, filePath) { - ig.ignoreFiles.push(filePath); - this - .readIgnoreFile(filePath) - .forEach(ignoreRule => this.addPatternRelativeToIgnoreFile(ig, ignoreRule)); - } - - /** - * Determine whether a file path is included in the default or custom ignore patterns - * @param {string} filepath Path to check - * @param {string} [category=undefined] check 'default', 'custom' or both (undefined) - * @returns {boolean} true if the file path matches one or more patterns, false otherwise - */ - contains(filepath, category) { - const isDir = filepath.endsWith(path.sep) || - (path.sep === "\\" && filepath.endsWith("/")); - let result = false; - const basePath = this.getBaseDir(); - const absolutePath = path.resolve(this.options.cwd, filepath); - let relativePath = path.relative(basePath, absolutePath); - - if (relativePath) { - if (isDir) { - relativePath += path.sep; - } - if (typeof category === "undefined") { - result = - (this.ig.default.filter([relativePath]).length === 0) || - (this.ig.custom.filter([relativePath]).length === 0); - } else { - result = - (this.ig[category].filter([relativePath]).length === 0); - } - } - debug("contains:"); - debug(" target = %j", filepath); - debug(" base = %j", basePath); - debug(" relative = %j", relativePath); - debug(" result = %j", result); - - return result; - - } -} - -module.exports = { IgnoredPaths }; diff --git a/tools/node_modules/eslint/lib/init/config-initializer.js b/tools/node_modules/eslint/lib/init/config-initializer.js index 48e56ce526131a..28dfad194a7e6f 100644 --- a/tools/node_modules/eslint/lib/init/config-initializer.js +++ b/tools/node_modules/eslint/lib/init/config-initializer.js @@ -291,6 +291,7 @@ function processAnswers(answers) { jsx: true }; config.plugins = ["react"]; + config.extends.push("plugin:react/recommended"); } else if (answers.framework === "vue") { config.plugins = ["vue"]; config.extends.push("plugin:vue/essential"); @@ -522,9 +523,9 @@ function promptUser() { name: "styleguide", message: "Which style guide do you want to follow?", choices: [ - { name: "Airbnb (https://github.com/airbnb/javascript)", value: "airbnb" }, - { name: "Standard (https://github.com/standard/standard)", value: "standard" }, - { name: "Google (https://github.com/google/eslint-config-google)", value: "google" } + { name: "Airbnb: https://github.com/airbnb/javascript", value: "airbnb" }, + { name: "Standard: https://github.com/standard/standard", value: "standard" }, + { name: "Google: https://github.com/google/eslint-config-google", value: "google" } ], when(answers) { answers.packageJsonExists = npmUtils.checkPackageJson(); diff --git a/tools/node_modules/eslint/lib/linter/report-translator.js b/tools/node_modules/eslint/lib/linter/report-translator.js index 8c9ed007a25f0d..eef5165585b21d 100644 --- a/tools/node_modules/eslint/lib/linter/report-translator.js +++ b/tools/node_modules/eslint/lib/linter/report-translator.js @@ -26,6 +26,7 @@ const interpolate = require("./interpolate"); * @property {Object} [data] Optional data to use to fill in placeholders in the * message. * @property {Function} [fix] The function to call that creates a fix command. + * @property {Array<{desc?: string, messageId?: string, fix: Function}>} suggest Suggestion descriptions and functions to create a the associated fixes. */ /** @@ -34,14 +35,15 @@ const interpolate = require("./interpolate"); * @property {string} ruleId * @property {(0|1|2)} severity * @property {(string|undefined)} message - * @property {(string|undefined)} messageId + * @property {(string|undefined)} [messageId] * @property {number} line * @property {number} column - * @property {(number|undefined)} endLine - * @property {(number|undefined)} endColumn + * @property {(number|undefined)} [endLine] + * @property {(number|undefined)} [endColumn] * @property {(string|null)} nodeType * @property {string} source - * @property {({text: string, range: (number[]|null)}|null)} fix + * @property {({text: string, range: (number[]|null)}|null)} [fix] + * @property {Array<{text: string, range: (number[]|null)}|null>} [suggestions] */ //------------------------------------------------------------------------------ @@ -182,6 +184,29 @@ function normalizeFixes(descriptor, sourceCode) { return fix; } +/** + * Gets an array of suggestion objects from the given descriptor. + * @param {MessageDescriptor} descriptor The report descriptor. + * @param {SourceCode} sourceCode The source code object to get text between fixes. + * @param {Object} messages Object of meta messages for the rule. + * @returns {Array} The suggestions for the descriptor + */ +function mapSuggestions(descriptor, sourceCode, messages) { + if (!descriptor.suggest || !Array.isArray(descriptor.suggest)) { + return []; + } + + return descriptor.suggest.map(suggestInfo => { + const computedDesc = suggestInfo.desc || messages[suggestInfo.messageId]; + + return { + ...suggestInfo, + desc: interpolate(computedDesc, suggestInfo.data), + fix: normalizeFixes(suggestInfo, sourceCode) + }; + }); +} + /** * Creates information about the report from a descriptor * @param {Object} options Information about the problem @@ -192,6 +217,7 @@ function normalizeFixes(descriptor, sourceCode) { * @param {string} [options.messageId] The error message ID. * @param {{start: SourceLocation, end: (SourceLocation|null)}} options.loc Start and end location * @param {{text: string, range: (number[]|null)}} options.fix The fix object + * @param {Array<{text: string, range: (number[]|null)}>} options.suggestions The array of suggestions objects * @returns {function(...args): ReportInfo} Function that returns information about the report */ function createProblem(options) { @@ -221,9 +247,47 @@ function createProblem(options) { problem.fix = options.fix; } + if (options.suggestions && options.suggestions.length > 0) { + problem.suggestions = options.suggestions; + } + return problem; } +/** + * Validates that suggestions are properly defined. Throws if an error is detected. + * @param {Array<{ desc?: string, messageId?: string }>} suggest The incoming suggest data. + * @param {Object} messages Object of meta messages for the rule. + * @returns {void} + */ +function validateSuggestions(suggest, messages) { + if (suggest && Array.isArray(suggest)) { + suggest.forEach(suggestion => { + if (suggestion.messageId) { + const { messageId } = suggestion; + + if (!messages) { + throw new TypeError(`context.report() called with a suggest option with a messageId '${messageId}', but no messages were present in the rule metadata.`); + } + + if (!messages[messageId]) { + throw new TypeError(`context.report() called with a suggest option with a messageId '${messageId}' which is not present in the 'messages' config: ${JSON.stringify(messages, null, 2)}`); + } + + if (suggestion.desc) { + throw new TypeError("context.report() called with a suggest option that defines both a 'messageId' and an 'desc'. Please only pass one."); + } + } else if (!suggestion.desc) { + throw new TypeError("context.report() called with a suggest option that doesn't have either a `desc` or `messageId`"); + } + + if (typeof suggestion.fix !== "function") { + throw new TypeError(`context.report() called with a suggest option without a fix function. See: ${suggestion}`); + } + }); + } +} + /** * Returns a function that converts the arguments of a `context.report` call from a rule into a reported * problem for the Node.js API. @@ -242,17 +306,17 @@ module.exports = function createReportTranslator(metadata) { */ return (...args) => { const descriptor = normalizeMultiArgReportCall(...args); + const messages = metadata.messageIds; assertValidNodeInfo(descriptor); let computedMessage; if (descriptor.messageId) { - if (!metadata.messageIds) { + if (!messages) { throw new TypeError("context.report() called with a messageId, but no messages were present in the rule metadata."); } const id = descriptor.messageId; - const messages = metadata.messageIds; if (descriptor.message) { throw new TypeError("context.report() called with a message and a messageId. Please only pass one."); @@ -267,6 +331,7 @@ module.exports = function createReportTranslator(metadata) { throw new TypeError("Missing `message` property in report() call; add a message that describes the linting problem."); } + validateSuggestions(descriptor.suggest, messages); return createProblem({ ruleId: metadata.ruleId, @@ -275,7 +340,8 @@ module.exports = function createReportTranslator(metadata) { message: interpolate(computedMessage, descriptor.data), messageId: descriptor.messageId, loc: normalizeReportLoc(descriptor), - fix: metadata.disableFixes ? null : normalizeFixes(descriptor, metadata.sourceCode) + fix: metadata.disableFixes ? null : normalizeFixes(descriptor, metadata.sourceCode), + suggestions: metadata.disableFixes ? [] : mapSuggestions(descriptor, metadata.sourceCode, messages) }); }; }; diff --git a/tools/node_modules/eslint/lib/rule-tester/rule-tester.js b/tools/node_modules/eslint/lib/rule-tester/rule-tester.js index e57cd09bd87014..b4b3b2bec86c78 100644 --- a/tools/node_modules/eslint/lib/rule-tester/rule-tester.js +++ b/tools/node_modules/eslint/lib/rule-tester/rule-tester.js @@ -349,7 +349,7 @@ class RuleTester { filename = item.filename; } - if (Object.prototype.hasOwnProperty.call(item, "options")) { + if (hasOwnProperty(item, "options")) { assert(Array.isArray(item.options), "options must be an array"); config.rules[ruleName] = [1].concat(item.options); } else { @@ -577,21 +577,55 @@ class RuleTester { assert.strictEqual(message.nodeType, error.type, `Error type should be ${error.type}, found ${message.nodeType}`); } - if (Object.prototype.hasOwnProperty.call(error, "line")) { + if (hasOwnProperty(error, "line")) { assert.strictEqual(message.line, error.line, `Error line should be ${error.line}`); } - if (Object.prototype.hasOwnProperty.call(error, "column")) { + if (hasOwnProperty(error, "column")) { assert.strictEqual(message.column, error.column, `Error column should be ${error.column}`); } - if (Object.prototype.hasOwnProperty.call(error, "endLine")) { + if (hasOwnProperty(error, "endLine")) { assert.strictEqual(message.endLine, error.endLine, `Error endLine should be ${error.endLine}`); } - if (Object.prototype.hasOwnProperty.call(error, "endColumn")) { + if (hasOwnProperty(error, "endColumn")) { assert.strictEqual(message.endColumn, error.endColumn, `Error endColumn should be ${error.endColumn}`); } + + if (hasOwnProperty(error, "suggestions")) { + + // Support asserting there are no suggestions + if (!error.suggestions) { + assert.strictEqual(message.suggestions, error.suggestions, `Error should have no suggestions on error with message: "${message.message}"`); + } else { + assert.strictEqual(Array.isArray(message.suggestions), true, `Error should have an array of suggestions. Instead received "${message.suggestions}" on error with message: "${message.message}"`); + assert.strictEqual(message.suggestions.length, error.suggestions.length, `Error should have ${error.suggestions.length} suggestions. Instead found ${message.suggestions.length} suggestions`); + + error.suggestions.forEach((expectedSuggestion, index) => { + const actualSuggestion = message.suggestions[index]; + + /** + * Tests equality of a suggestion key if that key is defined in the expected output. + * @param {string} key Key to validate from the suggestion object + * @returns {void} + */ + function assertSuggestionKeyEquals(key) { + if (hasOwnProperty(expectedSuggestion, key)) { + assert.deepStrictEqual(actualSuggestion[key], expectedSuggestion[key], `Error suggestion at index: ${index} should have desc of: "${actualSuggestion[key]}"`); + } + } + assertSuggestionKeyEquals("desc"); + assertSuggestionKeyEquals("messageId"); + + if (hasOwnProperty(expectedSuggestion, "output")) { + const codeWithAppliedSuggestion = SourceCodeFixer.applyFixes(item.code, [actualSuggestion]).output; + + assert.strictEqual(codeWithAppliedSuggestion, expectedSuggestion.output, `Expected the applied suggestion fix to match the test suggestion output for suggestion at index: ${index} on error with message: "${message.message}"`); + } + }); + } + } } else { // Message was an unexpected type @@ -600,7 +634,7 @@ class RuleTester { } } - if (Object.prototype.hasOwnProperty.call(item, "output")) { + if (hasOwnProperty(item, "output")) { if (item.output === null) { assert.strictEqual( result.output, diff --git a/tools/node_modules/eslint/lib/rules/camelcase.js b/tools/node_modules/eslint/lib/rules/camelcase.js index cdc16fab9f9460..a06c7f94042ad4 100644 --- a/tools/node_modules/eslint/lib/rules/camelcase.js +++ b/tools/node_modules/eslint/lib/rules/camelcase.js @@ -28,6 +28,10 @@ module.exports = { type: "boolean", default: false }, + ignoreImports: { + type: "boolean", + default: false + }, properties: { enum: ["always", "never"] }, @@ -56,6 +60,7 @@ module.exports = { const options = context.options[0] || {}; let properties = options.properties || ""; const ignoreDestructuring = options.ignoreDestructuring; + const ignoreImports = options.ignoreImports; const allow = options.allow || []; if (properties !== "always" && properties !== "never") { @@ -79,7 +84,7 @@ module.exports = { function isUnderscored(name) { // if there's an underscore, it might be A_CONSTANT, which is okay - return name.indexOf("_") > -1 && name !== name.toUpperCase(); + return name.includes("_") && name !== name.toUpperCase(); } /** @@ -89,9 +94,9 @@ module.exports = { * @private */ function isAllowed(name) { - return allow.findIndex( + return allow.some( entry => name === entry || name.match(new RegExp(entry, "u")) - ) !== -1; + ); } /** @@ -127,7 +132,7 @@ module.exports = { * @private */ function report(node) { - if (reported.indexOf(node) < 0) { + if (!reported.includes(node)) { reported.push(node); context.report({ node, messageId: "notCamelCase", data: { name: node.name } }); } @@ -209,10 +214,18 @@ module.exports = { } // Check if it's an import specifier - } else if (["ImportSpecifier", "ImportNamespaceSpecifier", "ImportDefaultSpecifier"].indexOf(node.parent.type) >= 0) { + } else if (["ImportSpecifier", "ImportNamespaceSpecifier", "ImportDefaultSpecifier"].includes(node.parent.type)) { + + if (node.parent.type === "ImportSpecifier" && ignoreImports) { + return; + } // Report only if the local imported identifier is underscored - if (node.parent.local && node.parent.local.name === node.name && nameIsUnderscored) { + if ( + node.parent.local && + node.parent.local.name === node.name && + nameIsUnderscored + ) { report(node); } diff --git a/tools/node_modules/eslint/lib/rules/comma-dangle.js b/tools/node_modules/eslint/lib/rules/comma-dangle.js index fb2d167c77eef9..e22b7f3551e114 100644 --- a/tools/node_modules/eslint/lib/rules/comma-dangle.js +++ b/tools/node_modules/eslint/lib/rules/comma-dangle.js @@ -231,7 +231,7 @@ module.exports = { if (astUtils.isCommaToken(trailingToken)) { context.report({ node: lastItem, - loc: trailingToken.loc.start, + loc: trailingToken.loc, messageId: "unexpected", fix(fixer) { return fixer.remove(trailingToken); @@ -267,7 +267,10 @@ module.exports = { if (trailingToken.value !== ",") { context.report({ node: lastItem, - loc: trailingToken.loc.end, + loc: { + start: trailingToken.loc.end, + end: astUtils.getNextLocation(sourceCode, trailingToken.loc.end) + }, messageId: "missing", fix(fixer) { return fixer.insertTextAfter(trailingToken, ","); diff --git a/tools/node_modules/eslint/lib/rules/computed-property-spacing.js b/tools/node_modules/eslint/lib/rules/computed-property-spacing.js index bc8be964f4ffee..a0bd7f48ced66b 100644 --- a/tools/node_modules/eslint/lib/rules/computed-property-spacing.js +++ b/tools/node_modules/eslint/lib/rules/computed-property-spacing.js @@ -153,10 +153,10 @@ module.exports = { const property = node[propertyName]; - const before = sourceCode.getTokenBefore(property), - first = sourceCode.getFirstToken(property), - last = sourceCode.getLastToken(property), - after = sourceCode.getTokenAfter(property); + const before = sourceCode.getTokenBefore(property, astUtils.isOpeningBracketToken), + first = sourceCode.getTokenAfter(before, { includeComments: true }), + after = sourceCode.getTokenAfter(property, astUtils.isClosingBracketToken), + last = sourceCode.getTokenBefore(after, { includeComments: true }); if (astUtils.isTokenOnSameLine(before, first)) { if (propertyNameMustBeSpaced) { diff --git a/tools/node_modules/eslint/lib/rules/curly.js b/tools/node_modules/eslint/lib/rules/curly.js index 93c74d11fcf35f..ee2fe4dceb82bf 100644 --- a/tools/node_modules/eslint/lib/rules/curly.js +++ b/tools/node_modules/eslint/lib/rules/curly.js @@ -97,10 +97,15 @@ module.exports = { * @private */ function isOneLiner(node) { - const first = sourceCode.getFirstToken(node), - last = sourceCode.getLastToken(node); + if (node.type === "EmptyStatement") { + return true; + } + + const first = sourceCode.getFirstToken(node); + const last = sourceCode.getLastToken(node); + const lastExcludingSemicolon = astUtils.isSemicolonToken(last) ? sourceCode.getTokenBefore(last) : last; - return first.loc.start.line === last.loc.end.line; + return first.loc.start.line === lastExcludingSemicolon.loc.end.line; } /** @@ -240,7 +245,7 @@ module.exports = { if (node.type === "IfStatement" && node.consequent === body && requiresBraceOfConsequent(node)) { expected = true; } else if (multiOnly) { - if (hasBlock && body.body.length === 1) { + if (hasBlock && body.body.length === 1 && !isLexicalDeclaration(body.body[0])) { expected = false; } } else if (multiLine) { diff --git a/tools/node_modules/eslint/lib/rules/function-call-argument-newline.js b/tools/node_modules/eslint/lib/rules/function-call-argument-newline.js index 31ebc097c46f51..b6abbe95fa98c3 100644 --- a/tools/node_modules/eslint/lib/rules/function-call-argument-newline.js +++ b/tools/node_modules/eslint/lib/rules/function-call-argument-newline.js @@ -70,6 +70,8 @@ module.exports = { { includeComments: true } ); + const hasLineCommentBefore = tokenBefore.type === "Line"; + context.report({ node, loc: { @@ -77,7 +79,7 @@ module.exports = { end: currentArgToken.loc.start }, messageId: checker.messageId, - fix: checker.createFix(currentArgToken, tokenBefore) + fix: hasLineCommentBefore ? null : checker.createFix(currentArgToken, tokenBefore) }); } } diff --git a/tools/node_modules/eslint/lib/rules/grouped-accessor-pairs.js b/tools/node_modules/eslint/lib/rules/grouped-accessor-pairs.js new file mode 100644 index 00000000000000..a790f83750b2eb --- /dev/null +++ b/tools/node_modules/eslint/lib/rules/grouped-accessor-pairs.js @@ -0,0 +1,224 @@ +/** + * @fileoverview Rule to require grouped accessor pairs in object literals and classes + * @author Milos Djermanovic + */ + +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const astUtils = require("./utils/ast-utils"); + +//------------------------------------------------------------------------------ +// Typedefs +//------------------------------------------------------------------------------ + +/** + * Property name if it can be computed statically, otherwise the list of the tokens of the key node. + * @typedef {string|Token[]} Key + */ + +/** + * Accessor nodes with the same key. + * @typedef {Object} AccessorData + * @property {Key} key Accessor's key + * @property {ASTNode[]} getters List of getter nodes. + * @property {ASTNode[]} setters List of setter nodes. + */ + +//------------------------------------------------------------------------------ +// Helpers +//------------------------------------------------------------------------------ + +/** + * Checks whether or not the given lists represent the equal tokens in the same order. + * Tokens are compared by their properties, not by instance. + * @param {Token[]} left First list of tokens. + * @param {Token[]} right Second list of tokens. + * @returns {boolean} `true` if the lists have same tokens. + */ +function areEqualTokenLists(left, right) { + if (left.length !== right.length) { + return false; + } + + for (let i = 0; i < left.length; i++) { + const leftToken = left[i], + rightToken = right[i]; + + if (leftToken.type !== rightToken.type || leftToken.value !== rightToken.value) { + return false; + } + } + + return true; +} + +/** + * Checks whether or not the given keys are equal. + * @param {Key} left First key. + * @param {Key} right Second key. + * @returns {boolean} `true` if the keys are equal. + */ +function areEqualKeys(left, right) { + if (typeof left === "string" && typeof right === "string") { + + // Statically computed names. + return left === right; + } + if (Array.isArray(left) && Array.isArray(right)) { + + // Token lists. + return areEqualTokenLists(left, right); + } + + return false; +} + +/** + * Checks whether or not a given node is of an accessor kind ('get' or 'set'). + * @param {ASTNode} node A node to check. + * @returns {boolean} `true` if the node is of an accessor kind. + */ +function isAccessorKind(node) { + return node.kind === "get" || node.kind === "set"; +} + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +module.exports = { + meta: { + type: "suggestion", + + docs: { + description: "require grouped accessor pairs in object literals and classes", + category: "Best Practices", + recommended: false, + url: "https://eslint.org/docs/rules/grouped-accessor-pairs" + }, + + schema: [ + { + enum: ["anyOrder", "getBeforeSet", "setBeforeGet"] + } + ], + + messages: { + notGrouped: "Accessor pair {{ formerName }} and {{ latterName }} should be grouped.", + invalidOrder: "Expected {{ latterName }} to be before {{ formerName }}." + } + }, + + create(context) { + const order = context.options[0] || "anyOrder"; + const sourceCode = context.getSourceCode(); + + /** + * Reports the given accessor pair. + * @param {string} messageId messageId to report. + * @param {ASTNode} formerNode getter/setter node that is defined before `latterNode`. + * @param {ASTNode} latterNode getter/setter node that is defined after `formerNode`. + * @returns {void} + * @private + */ + function report(messageId, formerNode, latterNode) { + context.report({ + node: latterNode, + messageId, + loc: astUtils.getFunctionHeadLoc(latterNode.value, sourceCode), + data: { + formerName: astUtils.getFunctionNameWithKind(formerNode.value), + latterName: astUtils.getFunctionNameWithKind(latterNode.value) + } + }); + } + + /** + * Creates a new `AccessorData` object for the given getter or setter node. + * @param {ASTNode} node A getter or setter node. + * @returns {AccessorData} New `AccessorData` object that contains the given node. + * @private + */ + function createAccessorData(node) { + const name = astUtils.getStaticPropertyName(node); + const key = (name !== null) ? name : sourceCode.getTokens(node.key); + + return { + key, + getters: node.kind === "get" ? [node] : [], + setters: node.kind === "set" ? [node] : [] + }; + } + + /** + * Merges the given `AccessorData` object into the given accessors list. + * @param {AccessorData[]} accessors The list to merge into. + * @param {AccessorData} accessorData The object to merge. + * @returns {AccessorData[]} The same instance with the merged object. + * @private + */ + function mergeAccessorData(accessors, accessorData) { + const equalKeyElement = accessors.find(a => areEqualKeys(a.key, accessorData.key)); + + if (equalKeyElement) { + equalKeyElement.getters.push(...accessorData.getters); + equalKeyElement.setters.push(...accessorData.setters); + } else { + accessors.push(accessorData); + } + + return accessors; + } + + /** + * Checks accessor pairs in the given list of nodes. + * @param {ASTNode[]} nodes The list to check. + * @param {Function} shouldCheck – Predicate that returns `true` if the node should be checked. + * @returns {void} + * @private + */ + function checkList(nodes, shouldCheck) { + const accessors = nodes + .filter(shouldCheck) + .filter(isAccessorKind) + .map(createAccessorData) + .reduce(mergeAccessorData, []); + + for (const { getters, setters } of accessors) { + + // Don't report accessor properties that have duplicate getters or setters. + if (getters.length === 1 && setters.length === 1) { + const [getter] = getters, + [setter] = setters, + getterIndex = nodes.indexOf(getter), + setterIndex = nodes.indexOf(setter), + formerNode = getterIndex < setterIndex ? getter : setter, + latterNode = getterIndex < setterIndex ? setter : getter; + + if (Math.abs(getterIndex - setterIndex) > 1) { + report("notGrouped", formerNode, latterNode); + } else if ( + (order === "getBeforeSet" && getterIndex > setterIndex) || + (order === "setBeforeGet" && getterIndex < setterIndex) + ) { + report("invalidOrder", formerNode, latterNode); + } + } + } + } + + return { + ObjectExpression(node) { + checkList(node.properties, n => n.type === "Property"); + }, + ClassBody(node) { + checkList(node.body, n => n.type === "MethodDefinition" && !n.static); + checkList(node.body, n => n.type === "MethodDefinition" && n.static); + } + }; + } +}; diff --git a/tools/node_modules/eslint/lib/rules/indent.js b/tools/node_modules/eslint/lib/rules/indent.js index 94c83692b39f0f..694cf7d9e6ed4f 100644 --- a/tools/node_modules/eslint/lib/rules/indent.js +++ b/tools/node_modules/eslint/lib/rules/indent.js @@ -1492,6 +1492,17 @@ module.exports = { ); }, + JSXSpreadAttribute(node) { + const openingCurly = sourceCode.getFirstToken(node); + const closingCurly = sourceCode.getLastToken(node); + + offsets.setDesiredOffsets( + [openingCurly.range[1], closingCurly.range[0]], + openingCurly, + 1 + ); + }, + "*"(node) { const firstToken = sourceCode.getFirstToken(node); diff --git a/tools/node_modules/eslint/lib/rules/index.js b/tools/node_modules/eslint/lib/rules/index.js index 8b0abc4ee7a8f9..d3fbe412080360 100644 --- a/tools/node_modules/eslint/lib/rules/index.js +++ b/tools/node_modules/eslint/lib/rules/index.js @@ -52,6 +52,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({ "generator-star-spacing": () => require("./generator-star-spacing"), "getter-return": () => require("./getter-return"), "global-require": () => require("./global-require"), + "grouped-accessor-pairs": () => require("./grouped-accessor-pairs"), "guard-for-in": () => require("./guard-for-in"), "handle-callback-err": () => require("./handle-callback-err"), "id-blacklist": () => require("./id-blacklist"), @@ -101,6 +102,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({ "no-console": () => require("./no-console"), "no-const-assign": () => require("./no-const-assign"), "no-constant-condition": () => require("./no-constant-condition"), + "no-constructor-return": () => require("./no-constructor-return"), "no-continue": () => require("./no-continue"), "no-control-regex": () => require("./no-control-regex"), "no-debugger": () => require("./no-debugger"), @@ -108,6 +110,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({ "no-div-regex": () => require("./no-div-regex"), "no-dupe-args": () => require("./no-dupe-args"), "no-dupe-class-members": () => require("./no-dupe-class-members"), + "no-dupe-else-if": () => require("./no-dupe-else-if"), "no-dupe-keys": () => require("./no-dupe-keys"), "no-duplicate-case": () => require("./no-duplicate-case"), "no-duplicate-imports": () => require("./no-duplicate-imports"), @@ -186,6 +189,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({ "no-self-assign": () => require("./no-self-assign"), "no-self-compare": () => require("./no-self-compare"), "no-sequences": () => require("./no-sequences"), + "no-setter-return": () => require("./no-setter-return"), "no-shadow": () => require("./no-shadow"), "no-shadow-restricted-names": () => require("./no-shadow-restricted-names"), "no-spaced-func": () => require("./no-spaced-func"), @@ -238,6 +242,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({ "prefer-arrow-callback": () => require("./prefer-arrow-callback"), "prefer-const": () => require("./prefer-const"), "prefer-destructuring": () => require("./prefer-destructuring"), + "prefer-exponentiation-operator": () => require("./prefer-exponentiation-operator"), "prefer-named-capture-group": () => require("./prefer-named-capture-group"), "prefer-numeric-literals": () => require("./prefer-numeric-literals"), "prefer-object-spread": () => require("./prefer-object-spread"), diff --git a/tools/node_modules/eslint/lib/rules/multiline-comment-style.js b/tools/node_modules/eslint/lib/rules/multiline-comment-style.js index 6578a120126bfb..fb50e1522ea11d 100644 --- a/tools/node_modules/eslint/lib/rules/multiline-comment-style.js +++ b/tools/node_modules/eslint/lib/rules/multiline-comment-style.js @@ -43,17 +43,150 @@ module.exports = { //---------------------------------------------------------------------- /** - * Gets a list of comment lines in a group - * @param {Token[]} commentGroup A group of comments, containing either multiple line comments or a single block comment - * @returns {string[]} A list of comment lines + * Checks if a comment line is starred. + * @param {string} line A string representing a comment line. + * @returns {boolean} Whether or not the comment line is starred. + */ + function isStarredCommentLine(line) { + return /^\s*\*/u.test(line); + } + + /** + * Checks if a comment group is in starred-block form. + * @param {Token[]} commentGroup A group of comments, containing either multiple line comments or a single block comment. + * @returns {boolean} Whether or not the comment group is in starred block form. + */ + function isStarredBlockComment([firstComment]) { + if (firstComment.type !== "Block") { + return false; + } + + const lines = firstComment.value.split(astUtils.LINEBREAK_MATCHER); + + // The first and last lines can only contain whitespace. + return lines.length > 0 && lines.every((line, i) => (i === 0 || i === lines.length - 1 ? /^\s*$/u : /^\s*\*/u).test(line)); + } + + /** + * Checks if a comment group is in JSDoc form. + * @param {Token[]} commentGroup A group of comments, containing either multiple line comments or a single block comment. + * @returns {boolean} Whether or not the comment group is in JSDoc form. + */ + function isJSDocComment([firstComment]) { + if (firstComment.type !== "Block") { + return false; + } + + const lines = firstComment.value.split(astUtils.LINEBREAK_MATCHER); + + return /^\*\s*$/u.test(lines[0]) && + lines.slice(1, -1).every(line => /^\s* /u.test(line)) && + /^\s*$/u.test(lines[lines.length - 1]); + } + + /** + * Processes a comment group that is currently in separate-line form, calculating the offset for each line. + * @param {Token[]} commentGroup A group of comments containing multiple line comments. + * @returns {string[]} An array of the processed lines. + */ + function processSeparateLineComments(commentGroup) { + const allLinesHaveLeadingSpace = commentGroup + .map(({ value }) => value) + .filter(line => line.trim().length) + .every(line => line.startsWith(" ")); + + return commentGroup.map(({ value }) => (allLinesHaveLeadingSpace ? value.replace(/^ /u, "") : value)); + } + + /** + * Processes a comment group that is currently in starred-block form, calculating the offset for each line. + * @param {Token} comment A single block comment token in starred-block form. + * @returns {string[]} An array of the processed lines. + */ + function processStarredBlockComment(comment) { + const lines = comment.value.split(astUtils.LINEBREAK_MATCHER) + .filter((line, i, linesArr) => !(i === 0 || i === linesArr.length - 1)) + .map(line => line.replace(/^\s*$/u, "")); + const allLinesHaveLeadingSpace = lines + .map(line => line.replace(/\s*\*/u, "")) + .filter(line => line.trim().length) + .every(line => line.startsWith(" ")); + + return lines.map(line => line.replace(allLinesHaveLeadingSpace ? /\s*\* ?/u : /\s*\*/u, "")); + } + + /** + * Processes a comment group that is currently in bare-block form, calculating the offset for each line. + * @param {Token} comment A single block comment token in bare-block form. + * @returns {string[]} An array of the processed lines. + */ + function processBareBlockComment(comment) { + const lines = comment.value.split(astUtils.LINEBREAK_MATCHER).map(line => line.replace(/^\s*$/u, "")); + const leadingWhitespace = `${sourceCode.text.slice(comment.range[0] - comment.loc.start.column, comment.range[0])} `; + let offset = ""; + + /* + * Calculate the offset of the least indented line and use that as the basis for offsetting all the lines. + * The first line should not be checked because it is inline with the opening block comment delimiter. + */ + for (const [i, line] of lines.entries()) { + if (!line.trim().length || i === 0) { + continue; + } + + const [, lineOffset] = line.match(/^(\s*\*?\s*)/u); + + if (lineOffset.length < leadingWhitespace.length) { + const newOffset = leadingWhitespace.slice(lineOffset.length - leadingWhitespace.length); + + if (newOffset.length > offset.length) { + offset = newOffset; + } + } + } + + return lines.map(line => { + const match = line.match(/^(\s*\*?\s*)(.*)/u); + const [, lineOffset, lineContents] = match; + + if (lineOffset.length > leadingWhitespace.length) { + return `${lineOffset.slice(leadingWhitespace.length - (offset.length + lineOffset.length))}${lineContents}`; + } + + if (lineOffset.length < leadingWhitespace.length) { + return `${lineOffset.slice(leadingWhitespace.length)}${lineContents}`; + } + + return lineContents; + }); + } + + /** + * Gets a list of comment lines in a group, formatting leading whitespace as necessary. + * @param {Token[]} commentGroup A group of comments containing either multiple line comments or a single block comment. + * @returns {string[]} A list of comment lines. */ function getCommentLines(commentGroup) { - if (commentGroup[0].type === "Line") { - return commentGroup.map(comment => comment.value); + const [firstComment] = commentGroup; + + if (firstComment.type === "Line") { + return processSeparateLineComments(commentGroup); } - return commentGroup[0].value - .split(astUtils.LINEBREAK_MATCHER) - .map(line => line.replace(/^\s*\*?/u, "")); + + if (isStarredBlockComment(commentGroup)) { + return processStarredBlockComment(firstComment); + } + + return processBareBlockComment(firstComment); + } + + /** + * Gets the initial offset (whitespace) from the beginning of a line to a given comment token. + * @param {Token} comment The token to check. + * @returns {string} The offset from the beginning of a line to the token. + */ + function getInitialOffset(comment) { + return sourceCode.text.slice(comment.range[0] - comment.loc.start.column, comment.range[0]); } /** @@ -63,10 +196,9 @@ module.exports = { * @returns {string} A representation of the comment value in starred-block form, excluding start and end markers */ function convertToStarredBlock(firstComment, commentLinesList) { - const initialOffset = sourceCode.text.slice(firstComment.range[0] - firstComment.loc.start.column, firstComment.range[0]); - const starredLines = commentLinesList.map(line => `${initialOffset} *${line}`); + const initialOffset = getInitialOffset(firstComment); - return `\n${starredLines.join("\n")}\n${initialOffset} `; + return `/*\n${commentLinesList.map(line => `${initialOffset} * ${line}`).join("\n")}\n${initialOffset} */`; } /** @@ -76,10 +208,7 @@ module.exports = { * @returns {string} A representation of the comment value in separate-line form */ function convertToSeparateLines(firstComment, commentLinesList) { - const initialOffset = sourceCode.text.slice(firstComment.range[0] - firstComment.loc.start.column, firstComment.range[0]); - const separateLines = commentLinesList.map(line => `// ${line.trim()}`); - - return separateLines.join(`\n${initialOffset}`); + return commentLinesList.map(line => `// ${line}`).join(`\n${getInitialOffset(firstComment)}`); } /** @@ -89,24 +218,7 @@ module.exports = { * @returns {string} A representation of the comment value in bare-block form */ function convertToBlock(firstComment, commentLinesList) { - const initialOffset = sourceCode.text.slice(firstComment.range[0] - firstComment.loc.start.column, firstComment.range[0]); - const blockLines = commentLinesList.map(line => line.trim()); - - return `/* ${blockLines.join(`\n${initialOffset} `)} */`; - } - - /** - * Check a comment is JSDoc form - * @param {Token[]} commentGroup A group of comments, containing either multiple line comments or a single block comment - * @returns {boolean} if commentGroup is JSDoc form, return true - */ - function isJSDoc(commentGroup) { - const lines = commentGroup[0].value.split(astUtils.LINEBREAK_MATCHER); - - return commentGroup[0].type === "Block" && - /^\*\s*$/u.test(lines[0]) && - lines.slice(1, -1).every(line => /^\s* /u.test(line)) && - /^\s*$/u.test(lines[lines.length - 1]); + return `/* ${commentLinesList.join(`\n${getInitialOffset(firstComment)} `)} */`; } /** @@ -117,6 +229,7 @@ module.exports = { */ const commentGroupCheckers = { "starred-block"(commentGroup) { + const [firstComment] = commentGroup; const commentLines = getCommentLines(commentGroup); if (commentLines.some(value => value.includes("*/"))) { @@ -126,31 +239,30 @@ module.exports = { if (commentGroup.length > 1) { context.report({ loc: { - start: commentGroup[0].loc.start, + start: firstComment.loc.start, end: commentGroup[commentGroup.length - 1].loc.end }, messageId: "expectedBlock", fix(fixer) { - const range = [commentGroup[0].range[0], commentGroup[commentGroup.length - 1].range[1]]; - const starredBlock = `/*${convertToStarredBlock(commentGroup[0], commentLines)}*/`; + const range = [firstComment.range[0], commentGroup[commentGroup.length - 1].range[1]]; return commentLines.some(value => value.startsWith("/")) ? null - : fixer.replaceTextRange(range, starredBlock); + : fixer.replaceTextRange(range, convertToStarredBlock(firstComment, commentLines)); } }); } else { - const block = commentGroup[0]; - const lines = block.value.split(astUtils.LINEBREAK_MATCHER); - const expectedLinePrefix = `${sourceCode.text.slice(block.range[0] - block.loc.start.column, block.range[0])} *`; + const lines = firstComment.value.split(astUtils.LINEBREAK_MATCHER); + const expectedLeadingWhitespace = getInitialOffset(firstComment); + const expectedLinePrefix = `${expectedLeadingWhitespace} *`; if (!/^\*?\s*$/u.test(lines[0])) { - const start = block.value.startsWith("*") ? block.range[0] + 1 : block.range[0]; + const start = firstComment.value.startsWith("*") ? firstComment.range[0] + 1 : firstComment.range[0]; context.report({ loc: { - start: block.loc.start, - end: { line: block.loc.start.line, column: block.loc.start.column + 2 } + start: firstComment.loc.start, + end: { line: firstComment.loc.start.line, column: firstComment.loc.start.column + 2 } }, messageId: "startNewline", fix: fixer => fixer.insertTextAfterRange([start, start + 2], `\n${expectedLinePrefix}`) @@ -160,36 +272,54 @@ module.exports = { if (!/^\s*$/u.test(lines[lines.length - 1])) { context.report({ loc: { - start: { line: block.loc.end.line, column: block.loc.end.column - 2 }, - end: block.loc.end + start: { line: firstComment.loc.end.line, column: firstComment.loc.end.column - 2 }, + end: firstComment.loc.end }, messageId: "endNewline", - fix: fixer => fixer.replaceTextRange([block.range[1] - 2, block.range[1]], `\n${expectedLinePrefix}/`) + fix: fixer => fixer.replaceTextRange([firstComment.range[1] - 2, firstComment.range[1]], `\n${expectedLinePrefix}/`) }); } - for (let lineNumber = block.loc.start.line + 1; lineNumber <= block.loc.end.line; lineNumber++) { + for (let lineNumber = firstComment.loc.start.line + 1; lineNumber <= firstComment.loc.end.line; lineNumber++) { const lineText = sourceCode.lines[lineNumber - 1]; + const errorType = isStarredCommentLine(lineText) + ? "alignment" + : "missingStar"; if (!lineText.startsWith(expectedLinePrefix)) { context.report({ loc: { start: { line: lineNumber, column: 0 }, - end: { line: lineNumber, column: sourceCode.lines[lineNumber - 1].length } + end: { line: lineNumber, column: lineText.length } }, - messageId: /^\s*\*/u.test(lineText) - ? "alignment" - : "missingStar", + messageId: errorType, fix(fixer) { const lineStartIndex = sourceCode.getIndexFromLoc({ line: lineNumber, column: 0 }); - const linePrefixLength = lineText.match(/^\s*\*? ?/u)[0].length; - const commentStartIndex = lineStartIndex + linePrefixLength; - const replacementText = lineNumber === block.loc.end.line || lineText.length === linePrefixLength - ? expectedLinePrefix - : `${expectedLinePrefix} `; + if (errorType === "alignment") { + const [, commentTextPrefix = ""] = lineText.match(/^(\s*\*)/u) || []; + const commentTextStartIndex = lineStartIndex + commentTextPrefix.length; + + return fixer.replaceTextRange([lineStartIndex, commentTextStartIndex], expectedLinePrefix); + } + + const [, commentTextPrefix = ""] = lineText.match(/^(\s*)/u) || []; + const commentTextStartIndex = lineStartIndex + commentTextPrefix.length; + let offset; + + for (const [idx, line] of lines.entries()) { + if (!/\S+/u.test(line)) { + continue; + } + + const lineTextToAlignWith = sourceCode.lines[firstComment.loc.start.line - 1 + idx]; + const [, prefix = "", initialOffset = ""] = lineTextToAlignWith.match(/^(\s*(?:\/?\*)?(\s*))/u) || []; - return fixer.replaceTextRange([lineStartIndex, commentStartIndex], replacementText); + offset = `${commentTextPrefix.slice(prefix.length)}${initialOffset}`; + break; + } + + return fixer.replaceTextRange([lineStartIndex, commentTextStartIndex], `${expectedLinePrefix}${offset}`); } }); } @@ -197,67 +327,68 @@ module.exports = { } }, "separate-lines"(commentGroup) { - if (!isJSDoc(commentGroup) && commentGroup[0].type === "Block") { - const commentLines = getCommentLines(commentGroup); - const block = commentGroup[0]; - const tokenAfter = sourceCode.getTokenAfter(block, { includeComments: true }); + const [firstComment] = commentGroup; + + if (firstComment.type !== "Block" || isJSDocComment(commentGroup)) { + return; + } - if (tokenAfter && block.loc.end.line === tokenAfter.loc.start.line) { - return; + const commentLines = getCommentLines(commentGroup); + const tokenAfter = sourceCode.getTokenAfter(firstComment, { includeComments: true }); + + if (tokenAfter && firstComment.loc.end.line === tokenAfter.loc.start.line) { + return; + } + + context.report({ + loc: { + start: firstComment.loc.start, + end: { line: firstComment.loc.start.line, column: firstComment.loc.start.column + 2 } + }, + messageId: "expectedLines", + fix(fixer) { + return fixer.replaceText(firstComment, convertToSeparateLines(firstComment, commentLines)); } + }); + }, + "bare-block"(commentGroup) { + if (isJSDocComment(commentGroup)) { + return; + } + const [firstComment] = commentGroup; + const commentLines = getCommentLines(commentGroup); + + // Disallows consecutive line comments in favor of using a block comment. + if (firstComment.type === "Line" && commentLines.length > 1 && + !commentLines.some(value => value.includes("*/"))) { context.report({ loc: { - start: block.loc.start, - end: { line: block.loc.start.line, column: block.loc.start.column + 2 } + start: firstComment.loc.start, + end: commentGroup[commentGroup.length - 1].loc.end }, - messageId: "expectedLines", + messageId: "expectedBlock", fix(fixer) { - return fixer.replaceText(block, convertToSeparateLines(block, commentLines.filter(line => line))); + return fixer.replaceTextRange( + [firstComment.range[0], commentGroup[commentGroup.length - 1].range[1]], + convertToBlock(firstComment, commentLines) + ); } }); } - }, - "bare-block"(commentGroup) { - if (!isJSDoc(commentGroup)) { - const commentLines = getCommentLines(commentGroup); - - // disallows consecutive line comments in favor of using a block comment. - if (commentGroup[0].type === "Line" && commentLines.length > 1 && - !commentLines.some(value => value.includes("*/"))) { - context.report({ - loc: { - start: commentGroup[0].loc.start, - end: commentGroup[commentGroup.length - 1].loc.end - }, - messageId: "expectedBlock", - fix(fixer) { - const range = [commentGroup[0].range[0], commentGroup[commentGroup.length - 1].range[1]]; - const block = convertToBlock(commentGroup[0], commentLines.filter(line => line)); - - return fixer.replaceTextRange(range, block); - } - }); - } - // prohibits block comments from having a * at the beginning of each line. - if (commentGroup[0].type === "Block") { - const block = commentGroup[0]; - const lines = block.value.split(astUtils.LINEBREAK_MATCHER).filter(line => line.trim()); - - if (lines.length > 0 && lines.every(line => /^\s*\*/u.test(line))) { - context.report({ - loc: { - start: block.loc.start, - end: { line: block.loc.start.line, column: block.loc.start.column + 2 } - }, - messageId: "expectedBareBlock", - fix(fixer) { - return fixer.replaceText(block, convertToBlock(block, commentLines.filter(line => line))); - } - }); + // Prohibits block comments from having a * at the beginning of each line. + if (isStarredBlockComment(commentGroup)) { + context.report({ + loc: { + start: firstComment.loc.start, + end: { line: firstComment.loc.start.line, column: firstComment.loc.start.column + 2 } + }, + messageId: "expectedBareBlock", + fix(fixer) { + return fixer.replaceText(firstComment, convertToBlock(firstComment, commentLines)); } - } + }); } } }; diff --git a/tools/node_modules/eslint/lib/rules/no-cond-assign.js b/tools/node_modules/eslint/lib/rules/no-cond-assign.js index 67dcd28b2087b5..3843a7ac2e3e91 100644 --- a/tools/node_modules/eslint/lib/rules/no-cond-assign.js +++ b/tools/node_modules/eslint/lib/rules/no-cond-assign.js @@ -2,10 +2,21 @@ * @fileoverview Rule to flag assignment in a conditional statement's test expression * @author Stephen Murray */ + "use strict"; +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + const astUtils = require("./utils/ast-utils"); +//------------------------------------------------------------------------------ +// Helpers +//------------------------------------------------------------------------------ + +const TEST_CONDITION_PARENT_TYPES = new Set(["IfStatement", "WhileStatement", "DoWhileStatement", "ForStatement", "ConditionalExpression"]); + const NODE_DESCRIPTIONS = { DoWhileStatement: "a 'do...while' statement", ForStatement: "a 'for' statement", @@ -55,7 +66,7 @@ module.exports = { */ function isConditionalTestExpression(node) { return node.parent && - node.parent.test && + TEST_CONDITION_PARENT_TYPES.has(node.parent.type) && node === node.parent.test; } @@ -105,8 +116,7 @@ module.exports = { ) { context.report({ - node, - loc: node.test.loc.start, + node: node.test, messageId: "missing" }); } @@ -122,7 +132,7 @@ module.exports = { if (ancestor) { context.report({ - node: ancestor, + node, messageId: "unexpected", data: { type: NODE_DESCRIPTIONS[ancestor.type] || ancestor.type diff --git a/tools/node_modules/eslint/lib/rules/no-constructor-return.js b/tools/node_modules/eslint/lib/rules/no-constructor-return.js new file mode 100644 index 00000000000000..4757770b7cc8e6 --- /dev/null +++ b/tools/node_modules/eslint/lib/rules/no-constructor-return.js @@ -0,0 +1,62 @@ +/** + * @fileoverview Rule to disallow returning value from constructor. + * @author Pig Fang + */ + +"use strict"; + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +module.exports = { + meta: { + type: "problem", + + docs: { + description: "disallow returning value from constructor", + category: "Best Practices", + recommended: false, + url: "https://eslint.org/docs/rules/no-constructor-return" + }, + + schema: {}, + + fixable: null, + + messages: { + unexpected: "Unexpected return statement in constructor." + } + }, + + create(context) { + const stack = []; + + return { + onCodePathStart(_, node) { + stack.push(node); + }, + onCodePathEnd() { + stack.pop(); + }, + ReturnStatement(node) { + const last = stack[stack.length - 1]; + + if (!last.parent) { + return; + } + + if ( + last.parent.type === "MethodDefinition" && + last.parent.kind === "constructor" && + (node.parent.parent === last || node.argument) + ) { + context.report({ + node, + messageId: "unexpected" + }); + } + } + }; + } +}; diff --git a/tools/node_modules/eslint/lib/rules/no-dupe-else-if.js b/tools/node_modules/eslint/lib/rules/no-dupe-else-if.js new file mode 100644 index 00000000000000..a165e16607d2f0 --- /dev/null +++ b/tools/node_modules/eslint/lib/rules/no-dupe-else-if.js @@ -0,0 +1,122 @@ +/** + * @fileoverview Rule to disallow duplicate conditions in if-else-if chains + * @author Milos Djermanovic + */ + +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const astUtils = require("./utils/ast-utils"); + +//------------------------------------------------------------------------------ +// Helpers +//------------------------------------------------------------------------------ + +/** + * Determines whether the first given array is a subset of the second given array. + * @param {Function} comparator A function to compare two elements, should return `true` if they are equal. + * @param {Array} arrA The array to compare from. + * @param {Array} arrB The array to compare against. + * @returns {boolean} `true` if the array `arrA` is a subset of the array `arrB`. + */ +function isSubsetByComparator(comparator, arrA, arrB) { + return arrA.every(a => arrB.some(b => comparator(a, b))); +} + +/** + * Splits the given node by the given logical operator. + * @param {string} operator Logical operator `||` or `&&`. + * @param {ASTNode} node The node to split. + * @returns {ASTNode[]} Array of conditions that makes the node when joined by the operator. + */ +function splitByLogicalOperator(operator, node) { + if (node.type === "LogicalExpression" && node.operator === operator) { + return [...splitByLogicalOperator(operator, node.left), ...splitByLogicalOperator(operator, node.right)]; + } + return [node]; +} + +const splitByOr = splitByLogicalOperator.bind(null, "||"); +const splitByAnd = splitByLogicalOperator.bind(null, "&&"); + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +module.exports = { + meta: { + type: "problem", + + docs: { + description: "disallow duplicate conditions in if-else-if chains", + category: "Possible Errors", + recommended: false, + url: "https://eslint.org/docs/rules/no-dupe-else-if" + }, + + schema: [], + + messages: { + unexpected: "This branch can never execute. Its condition is a duplicate or covered by previous conditions in the if-else-if chain." + } + }, + + create(context) { + const sourceCode = context.getSourceCode(); + + /** + * Determines whether the two given nodes are considered to be equal. In particular, given that the nodes + * represent expressions in a boolean context, `||` and `&&` can be considered as commutative operators. + * @param {ASTNode} a First node. + * @param {ASTNode} b Second node. + * @returns {boolean} `true` if the nodes are considered to be equal. + */ + function equal(a, b) { + if (a.type !== b.type) { + return false; + } + + if ( + a.type === "LogicalExpression" && + (a.operator === "||" || a.operator === "&&") && + a.operator === b.operator + ) { + return equal(a.left, b.left) && equal(a.right, b.right) || + equal(a.left, b.right) && equal(a.right, b.left); + } + + return astUtils.equalTokens(a, b, sourceCode); + } + + const isSubset = isSubsetByComparator.bind(null, equal); + + return { + IfStatement(node) { + const test = node.test, + conditionsToCheck = test.type === "LogicalExpression" && test.operator === "&&" + ? [test, ...splitByAnd(test)] + : [test]; + let current = node, + listToCheck = conditionsToCheck.map(c => splitByOr(c).map(splitByAnd)); + + while (current.parent && current.parent.type === "IfStatement" && current.parent.alternate === current) { + current = current.parent; + + const currentOrOperands = splitByOr(current.test).map(splitByAnd); + + listToCheck = listToCheck.map(orOperands => orOperands.filter( + orOperand => !currentOrOperands.some(currentOrOperand => isSubset(currentOrOperand, orOperand)) + )); + + if (listToCheck.some(orOperands => orOperands.length === 0)) { + context.report({ node: test, messageId: "unexpected" }); + break; + } + } + } + }; + } +}; diff --git a/tools/node_modules/eslint/lib/rules/no-implicit-globals.js b/tools/node_modules/eslint/lib/rules/no-implicit-globals.js index 2eea2b28463250..d4bfa3af82fc3b 100644 --- a/tools/node_modules/eslint/lib/rules/no-implicit-globals.js +++ b/tools/node_modules/eslint/lib/rules/no-implicit-globals.js @@ -1,5 +1,5 @@ /** - * @fileoverview Rule to check for implicit global variables and functions. + * @fileoverview Rule to check for implicit global variables, functions and classes. * @author Joshua Peek */ @@ -14,41 +14,123 @@ module.exports = { type: "suggestion", docs: { - description: "disallow variable and `function` declarations in the global scope", + description: "disallow declarations in the global scope", category: "Best Practices", recommended: false, url: "https://eslint.org/docs/rules/no-implicit-globals" }, - schema: [] + schema: [{ + type: "object", + properties: { + lexicalBindings: { + type: "boolean", + default: false + } + }, + additionalProperties: false + }], + + messages: { + globalNonLexicalBinding: "Unexpected {{kind}} declaration in the global scope, wrap in an IIFE for a local variable, assign as global property for a global variable.", + globalLexicalBinding: "Unexpected {{kind}} declaration in the global scope, wrap in a block or in an IIFE.", + globalVariableLeak: "Global variable leak, declare the variable if it is intended to be local.", + assignmentToReadonlyGlobal: "Unexpected assignment to read-only global variable.", + redeclarationOfReadonlyGlobal: "Unexpected redeclaration of read-only global variable." + } }, create(context) { + + const checkLexicalBindings = context.options[0] && context.options[0].lexicalBindings === true; + + /** + * Reports the node. + * @param {ASTNode} node Node to report. + * @param {string} messageId Id of the message to report. + * @param {string|undefined} kind Declaration kind, can be 'var', 'const', 'let', function or class. + * @returns {void} + */ + function report(node, messageId, kind) { + context.report({ + node, + messageId, + data: { + kind + } + }); + } + return { Program() { const scope = context.getScope(); scope.variables.forEach(variable => { - if (variable.writeable) { + + // Only ESLint global variables have the `writable` key. + const isReadonlyEslintGlobalVariable = variable.writeable === false; + const isWritableEslintGlobalVariable = variable.writeable === true; + + if (isWritableEslintGlobalVariable) { + + // Everything is allowed with writable ESLint global variables. return; } variable.defs.forEach(def => { + const defNode = def.node; + if (def.type === "FunctionName" || (def.type === "Variable" && def.parent.kind === "var")) { - context.report({ node: def.node, message: "Implicit global variable, assign as global property instead." }); + if (isReadonlyEslintGlobalVariable) { + report(defNode, "redeclarationOfReadonlyGlobal"); + } else { + report( + defNode, + "globalNonLexicalBinding", + def.type === "FunctionName" ? "function" : `'${def.parent.kind}'` + ); + } + } + + if (checkLexicalBindings) { + if (def.type === "ClassName" || + (def.type === "Variable" && (def.parent.kind === "let" || def.parent.kind === "const"))) { + if (isReadonlyEslintGlobalVariable) { + report(defNode, "redeclarationOfReadonlyGlobal"); + } else { + report( + defNode, + "globalLexicalBinding", + def.type === "ClassName" ? "class" : `'${def.parent.kind}'` + ); + } + } } }); }); + // Undeclared assigned variables. scope.implicit.variables.forEach(variable => { const scopeVariable = scope.set.get(variable.name); + let messageId; - if (scopeVariable && scopeVariable.writeable) { - return; + if (scopeVariable) { + + // ESLint global variable + if (scopeVariable.writeable) { + return; + } + messageId = "assignmentToReadonlyGlobal"; + + } else { + + // Reference to an unknown variable, possible global leak. + messageId = "globalVariableLeak"; } + // def.node is an AssignmentExpression, ForInStatement or ForOfStatement. variable.defs.forEach(def => { - context.report({ node: def.node, message: "Implicit global variable, assign as global property instead." }); + report(def.node, messageId); }); }); } diff --git a/tools/node_modules/eslint/lib/rules/no-inline-comments.js b/tools/node_modules/eslint/lib/rules/no-inline-comments.js index b35db114100a34..bd226ecc35fde1 100644 --- a/tools/node_modules/eslint/lib/rules/no-inline-comments.js +++ b/tools/node_modules/eslint/lib/rules/no-inline-comments.js @@ -35,22 +35,36 @@ module.exports = { */ function testCodeAroundComment(node) { - // Get the whole line and cut it off at the start of the comment - const startLine = String(sourceCode.lines[node.loc.start.line - 1]); - const endLine = String(sourceCode.lines[node.loc.end.line - 1]); + const startLine = String(sourceCode.lines[node.loc.start.line - 1]), + endLine = String(sourceCode.lines[node.loc.end.line - 1]), + preamble = startLine.slice(0, node.loc.start.column).trim(), + postamble = endLine.slice(node.loc.end.column).trim(), + isPreambleEmpty = !preamble, + isPostambleEmpty = !postamble; - const preamble = startLine.slice(0, node.loc.start.column).trim(); + // Nothing on both sides + if (isPreambleEmpty && isPostambleEmpty) { + return; + } - // Also check after the comment - const postamble = endLine.slice(node.loc.end.column).trim(); + // JSX Exception + if ( + (isPreambleEmpty || preamble === "{") && + (isPostambleEmpty || postamble === "}") + ) { + const enclosingNode = sourceCode.getNodeByRangeIndex(node.range[0]); - // Check that this comment isn't an ESLint directive - const isDirective = astUtils.isDirectiveComment(node); + if (enclosingNode && enclosingNode.type === "JSXEmptyExpression") { + return; + } + } - // Should be empty if there was only whitespace around the comment - if (!isDirective && (preamble || postamble)) { - context.report({ node, message: "Unexpected comment inline with code." }); + // Don't report ESLint directive comments + if (astUtils.isDirectiveComment(node)) { + return; } + + context.report({ node, message: "Unexpected comment inline with code." }); } //-------------------------------------------------------------------------- diff --git a/tools/node_modules/eslint/lib/rules/no-invalid-this.js b/tools/node_modules/eslint/lib/rules/no-invalid-this.js index b1dddd9319c745..5398fc3b5f842c 100644 --- a/tools/node_modules/eslint/lib/rules/no-invalid-this.js +++ b/tools/node_modules/eslint/lib/rules/no-invalid-this.js @@ -26,10 +26,23 @@ module.exports = { url: "https://eslint.org/docs/rules/no-invalid-this" }, - schema: [] + schema: [ + { + type: "object", + properties: { + capIsConstructor: { + type: "boolean", + default: true + } + }, + additionalProperties: false + } + ] }, create(context) { + const options = context.options[0] || {}; + const capIsConstructor = options.capIsConstructor !== false; const stack = [], sourceCode = context.getSourceCode(); @@ -48,7 +61,8 @@ module.exports = { current.init = true; current.valid = !astUtils.isDefaultThisBinding( current.node, - sourceCode + sourceCode, + { capIsConstructor } ); } return current; diff --git a/tools/node_modules/eslint/lib/rules/no-octal-escape.js b/tools/node_modules/eslint/lib/rules/no-octal-escape.js index 7f6845ec70f388..5b4c7b2ebb45c8 100644 --- a/tools/node_modules/eslint/lib/rules/no-octal-escape.js +++ b/tools/node_modules/eslint/lib/rules/no-octal-escape.js @@ -38,7 +38,7 @@ module.exports = { // \0 represents a valid NULL character if it isn't followed by a digit. const match = node.raw.match( - /^(?:[^\\]|\\.)*?\\([0-3][0-7]{1,2}|[4-7][0-7]|[1-7])/u + /^(?:[^\\]|\\.)*?\\([0-3][0-7]{1,2}|[4-7][0-7]|0(?=[89])|[1-7])/su ); if (match) { diff --git a/tools/node_modules/eslint/lib/rules/no-setter-return.js b/tools/node_modules/eslint/lib/rules/no-setter-return.js new file mode 100644 index 00000000000000..e0948696c347f5 --- /dev/null +++ b/tools/node_modules/eslint/lib/rules/no-setter-return.js @@ -0,0 +1,227 @@ +/** + * @fileoverview Rule to disallow returning values from setters + * @author Milos Djermanovic + */ + +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const astUtils = require("./utils/ast-utils"); +const { findVariable } = require("eslint-utils"); + +//------------------------------------------------------------------------------ +// Helpers +//------------------------------------------------------------------------------ + +/** + * Determines whether the given identifier node is a reference to a global variable. + * @param {ASTNode} node `Identifier` node to check. + * @param {Scope} scope Scope to which the node belongs. + * @returns {boolean} True if the identifier is a reference to a global variable. + */ +function isGlobalReference(node, scope) { + const variable = findVariable(scope, node); + + return variable !== null && variable.scope.type === "global" && variable.defs.length === 0; +} + +/** + * Determines whether the given node is an argument of the specified global method call, at the given `index` position. + * E.g., for given `index === 1`, this function checks for `objectName.methodName(foo, node)`, where objectName is a global variable. + * @param {ASTNode} node The node to check. + * @param {Scope} scope Scope to which the node belongs. + * @param {string} objectName Name of the global object. + * @param {string} methodName Name of the method. + * @param {number} index The given position. + * @returns {boolean} `true` if the node is argument at the given position. + */ +function isArgumentOfGlobalMethodCall(node, scope, objectName, methodName, index) { + const parent = node.parent; + + return parent.type === "CallExpression" && + parent.arguments[index] === node && + parent.callee.type === "MemberExpression" && + astUtils.getStaticPropertyName(parent.callee) === methodName && + parent.callee.object.type === "Identifier" && + parent.callee.object.name === objectName && + isGlobalReference(parent.callee.object, scope); +} + +/** + * Determines whether the given node is used as a property descriptor. + * @param {ASTNode} node The node to check. + * @param {Scope} scope Scope to which the node belongs. + * @returns {boolean} `true` if the node is a property descriptor. + */ +function isPropertyDescriptor(node, scope) { + if ( + isArgumentOfGlobalMethodCall(node, scope, "Object", "defineProperty", 2) || + isArgumentOfGlobalMethodCall(node, scope, "Reflect", "defineProperty", 2) + ) { + return true; + } + + const parent = node.parent; + + if ( + parent.type === "Property" && + parent.value === node + ) { + const grandparent = parent.parent; + + if ( + grandparent.type === "ObjectExpression" && + ( + isArgumentOfGlobalMethodCall(grandparent, scope, "Object", "create", 1) || + isArgumentOfGlobalMethodCall(grandparent, scope, "Object", "defineProperties", 1) + ) + ) { + return true; + } + } + + return false; +} + +/** + * Determines whether the given function node is used as a setter function. + * @param {ASTNode} node The node to check. + * @param {Scope} scope Scope to which the node belongs. + * @returns {boolean} `true` if the node is a setter. + */ +function isSetter(node, scope) { + const parent = node.parent; + + if ( + parent.kind === "set" && + parent.value === node + ) { + + // Setter in an object literal or in a class + return true; + } + + if ( + parent.type === "Property" && + parent.value === node && + astUtils.getStaticPropertyName(parent) === "set" && + parent.parent.type === "ObjectExpression" && + isPropertyDescriptor(parent.parent, scope) + ) { + + // Setter in a property descriptor + return true; + } + + return false; +} + +/** + * Finds function's outer scope. + * @param {Scope} scope Function's own scope. + * @returns {Scope} Function's outer scope. + */ +function getOuterScope(scope) { + const upper = scope.upper; + + if (upper.type === "function-expression-name") { + return upper.upper; + } + + return upper; +} + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +module.exports = { + meta: { + type: "problem", + + docs: { + description: "disallow returning values from setters", + category: "Possible Errors", + recommended: false, + url: "https://eslint.org/docs/rules/no-setter-return" + }, + + schema: [], + + messages: { + returnsValue: "Setter cannot return a value." + } + }, + + create(context) { + let funcInfo = null; + + /** + * Creates and pushes to the stack a function info object for the given function node. + * @param {ASTNode} node The function node. + * @returns {void} + */ + function enterFunction(node) { + const outerScope = getOuterScope(context.getScope()); + + funcInfo = { + upper: funcInfo, + isSetter: isSetter(node, outerScope) + }; + } + + /** + * Pops the current function info object from the stack. + * @returns {void} + */ + function exitFunction() { + funcInfo = funcInfo.upper; + } + + /** + * Reports the given node. + * @param {ASTNode} node Node to report. + * @returns {void} + */ + function report(node) { + context.report({ node, messageId: "returnsValue" }); + } + + return { + + /* + * Function declarations cannot be setters, but we still have to track them in the `funcInfo` stack to avoid + * false positives, because a ReturnStatement node can belong to a function declaration inside a setter. + * + * Note: A previously declared function can be referenced and actually used as a setter in a property descriptor, + * but that's out of scope for this rule. + */ + FunctionDeclaration: enterFunction, + FunctionExpression: enterFunction, + ArrowFunctionExpression(node) { + enterFunction(node); + + if (funcInfo.isSetter && node.expression) { + + // { set: foo => bar } property descriptor. Report implicit return 'bar' as the equivalent for a return statement. + report(node.body); + } + }, + + "FunctionDeclaration:exit": exitFunction, + "FunctionExpression:exit": exitFunction, + "ArrowFunctionExpression:exit": exitFunction, + + ReturnStatement(node) { + + // Global returns (e.g., at the top level of a Node module) don't have `funcInfo`. + if (funcInfo && funcInfo.isSetter && node.argument) { + report(node); + } + } + }; + } +}; diff --git a/tools/node_modules/eslint/lib/rules/no-underscore-dangle.js b/tools/node_modules/eslint/lib/rules/no-underscore-dangle.js index 3f59815b575f84..e910e2739a740a 100644 --- a/tools/node_modules/eslint/lib/rules/no-underscore-dangle.js +++ b/tools/node_modules/eslint/lib/rules/no-underscore-dangle.js @@ -38,6 +38,10 @@ module.exports = { type: "boolean", default: false }, + allowAfterThisConstructor: { + type: "boolean", + default: false + }, enforceInMethodNames: { type: "boolean", default: false @@ -54,6 +58,7 @@ module.exports = { const ALLOWED_VARIABLES = options.allow ? options.allow : []; const allowAfterThis = typeof options.allowAfterThis !== "undefined" ? options.allowAfterThis : false; const allowAfterSuper = typeof options.allowAfterSuper !== "undefined" ? options.allowAfterSuper : false; + const allowAfterThisConstructor = typeof options.allowAfterThisConstructor !== "undefined" ? options.allowAfterThisConstructor : false; const enforceInMethodNames = typeof options.enforceInMethodNames !== "undefined" ? options.enforceInMethodNames : false; //------------------------------------------------------------------------- @@ -72,7 +77,7 @@ module.exports = { /** * Check if identifier has a underscore at the end - * @param {ASTNode} identifier node to evaluate + * @param {string} identifier name of the node * @returns {boolean} true if its is present * @private */ @@ -84,7 +89,7 @@ module.exports = { /** * Check if identifier is a special case member expression - * @param {ASTNode} identifier node to evaluate + * @param {string} identifier name of the node * @returns {boolean} true if its is a special case * @private */ @@ -94,7 +99,7 @@ module.exports = { /** * Check if identifier is a special case variable expression - * @param {ASTNode} identifier node to evaluate + * @param {string} identifier name of the node * @returns {boolean} true if its is a special case * @private */ @@ -104,6 +109,18 @@ module.exports = { return identifier === "_"; } + /** + * Check if a node is a member reference of this.constructor + * @param {ASTNode} node node to evaluate + * @returns {boolean} true if it is a reference on this.constructor + * @private + */ + function isThisConstructorReference(node) { + return node.object.type === "MemberExpression" && + node.object.property.name === "constructor" && + node.object.object.type === "ThisExpression"; + } + /** * Check if function has a underscore at the end * @param {ASTNode} node node to evaluate @@ -156,11 +173,13 @@ module.exports = { function checkForTrailingUnderscoreInMemberExpression(node) { const identifier = node.property.name, isMemberOfThis = node.object.type === "ThisExpression", - isMemberOfSuper = node.object.type === "Super"; + isMemberOfSuper = node.object.type === "Super", + isMemberOfThisConstructor = isThisConstructorReference(node); if (typeof identifier !== "undefined" && hasTrailingUnderscore(identifier) && !(isMemberOfThis && allowAfterThis) && !(isMemberOfSuper && allowAfterSuper) && + !(isMemberOfThisConstructor && allowAfterThisConstructor) && !isSpecialCaseIdentifierForMemberExpression(identifier) && !isAllowed(identifier)) { context.report({ node, diff --git a/tools/node_modules/eslint/lib/rules/no-useless-computed-key.js b/tools/node_modules/eslint/lib/rules/no-useless-computed-key.js index 95527832b2d7f0..b5e53174e42be7 100644 --- a/tools/node_modules/eslint/lib/rules/no-useless-computed-key.js +++ b/tools/node_modules/eslint/lib/rules/no-useless-computed-key.js @@ -8,6 +8,7 @@ // Requirements //------------------------------------------------------------------------------ +const lodash = require("lodash"); const astUtils = require("./utils/ast-utils"); //------------------------------------------------------------------------------ @@ -21,57 +22,83 @@ module.exports = { type: "suggestion", docs: { - description: "disallow unnecessary computed property keys in object literals", + description: "disallow unnecessary computed property keys in objects and classes", category: "ECMAScript 6", recommended: false, url: "https://eslint.org/docs/rules/no-useless-computed-key" }, - schema: [], + schema: [{ + type: "object", + properties: { + enforceForClassMembers: { + type: "boolean", + default: false + } + }, + additionalProperties: false + }], fixable: "code" }, create(context) { const sourceCode = context.getSourceCode(); + const enforceForClassMembers = context.options[0] && context.options[0].enforceForClassMembers; + + /** + * Reports a given node if it violated this rule. + * @param {ASTNode} node The node to check. + * @returns {void} + */ + function check(node) { + if (!node.computed) { + return; + } - return { - Property(node) { - if (!node.computed) { - return; - } - - const key = node.key, - nodeType = typeof key.value; + const key = node.key, + nodeType = typeof key.value; - if (key.type === "Literal" && (nodeType === "string" || nodeType === "number") && key.value !== "__proto__") { - context.report({ - node, - message: MESSAGE_UNNECESSARY_COMPUTED, - data: { property: sourceCode.getText(key) }, - fix(fixer) { - const leftSquareBracket = sourceCode.getFirstToken(node, astUtils.isOpeningBracketToken); - const rightSquareBracket = sourceCode.getFirstTokenBetween(node.key, node.value, astUtils.isClosingBracketToken); - const tokensBetween = sourceCode.getTokensBetween(leftSquareBracket, rightSquareBracket, 1); + let allowedKey; - if (tokensBetween.slice(0, -1).some((token, index) => - sourceCode.getText().slice(token.range[1], tokensBetween[index + 1].range[0]).trim())) { + if (node.type === "MethodDefinition") { + allowedKey = node.static ? "prototype" : "constructor"; + } else { + allowedKey = "__proto__"; + } - // If there are comments between the brackets and the property name, don't do a fix. - return null; - } + if (key.type === "Literal" && (nodeType === "string" || nodeType === "number") && key.value !== allowedKey) { + context.report({ + node, + message: MESSAGE_UNNECESSARY_COMPUTED, + data: { property: sourceCode.getText(key) }, + fix(fixer) { + const leftSquareBracket = sourceCode.getFirstToken(node, astUtils.isOpeningBracketToken); + const rightSquareBracket = sourceCode.getFirstTokenBetween(node.key, node.value, astUtils.isClosingBracketToken); + const tokensBetween = sourceCode.getTokensBetween(leftSquareBracket, rightSquareBracket, 1); + + if (tokensBetween.slice(0, -1).some((token, index) => + sourceCode.getText().slice(token.range[1], tokensBetween[index + 1].range[0]).trim())) { + + // If there are comments between the brackets and the property name, don't do a fix. + return null; + } - const tokenBeforeLeftBracket = sourceCode.getTokenBefore(leftSquareBracket); + const tokenBeforeLeftBracket = sourceCode.getTokenBefore(leftSquareBracket); - // Insert a space before the key to avoid changing identifiers, e.g. ({ get[2]() {} }) to ({ get2() {} }) - const needsSpaceBeforeKey = tokenBeforeLeftBracket.range[1] === leftSquareBracket.range[0] && - !astUtils.canTokensBeAdjacent(tokenBeforeLeftBracket, sourceCode.getFirstToken(key)); + // Insert a space before the key to avoid changing identifiers, e.g. ({ get[2]() {} }) to ({ get2() {} }) + const needsSpaceBeforeKey = tokenBeforeLeftBracket.range[1] === leftSquareBracket.range[0] && + !astUtils.canTokensBeAdjacent(tokenBeforeLeftBracket, sourceCode.getFirstToken(key)); - const replacementKey = (needsSpaceBeforeKey ? " " : "") + key.raw; + const replacementKey = (needsSpaceBeforeKey ? " " : "") + key.raw; - return fixer.replaceTextRange([leftSquareBracket.range[0], rightSquareBracket.range[1]], replacementKey); - } - }); - } + return fixer.replaceTextRange([leftSquareBracket.range[0], rightSquareBracket.range[1]], replacementKey); + } + }); } + } + + return { + Property: check, + MethodDefinition: enforceForClassMembers ? check : lodash.noop }; } }; diff --git a/tools/node_modules/eslint/lib/rules/no-useless-escape.js b/tools/node_modules/eslint/lib/rules/no-useless-escape.js index ebfe4cba38a9a7..8057e44ddab463 100644 --- a/tools/node_modules/eslint/lib/rules/no-useless-escape.js +++ b/tools/node_modules/eslint/lib/rules/no-useless-escape.js @@ -85,7 +85,14 @@ module.exports = { description: "disallow unnecessary escape characters", category: "Best Practices", recommended: true, - url: "https://eslint.org/docs/rules/no-useless-escape" + url: "https://eslint.org/docs/rules/no-useless-escape", + suggestion: true + }, + + messages: { + unnecessaryEscape: "Unnecessary escape character: \\{{character}}.", + removeEscape: "Remove the `\\`. This maintains the current functionality.", + escapeBackslash: "Replace the `\\` with `\\\\` to include the actual backslash character." }, schema: [] @@ -103,6 +110,8 @@ module.exports = { */ function report(node, startOffset, character) { const start = sourceCode.getLocFromIndex(sourceCode.getIndexFromLoc(node.loc.start) + startOffset); + const rangeStart = sourceCode.getIndexFromLoc(node.loc.start) + startOffset; + const range = [rangeStart, rangeStart + 1]; context.report({ node, @@ -110,8 +119,22 @@ module.exports = { start, end: { line: start.line, column: start.column + 1 } }, - message: "Unnecessary escape character: \\{{character}}.", - data: { character } + messageId: "unnecessaryEscape", + data: { character }, + suggest: [ + { + messageId: "removeEscape", + fix(fixer) { + return fixer.removeRange(range); + } + }, + { + messageId: "escapeBackslash", + fix(fixer) { + return fixer.insertTextBeforeRange(range, "\\"); + } + } + ] }); } diff --git a/tools/node_modules/eslint/lib/rules/object-curly-spacing.js b/tools/node_modules/eslint/lib/rules/object-curly-spacing.js index 53ddaaa24ad01e..117a7a35426103 100644 --- a/tools/node_modules/eslint/lib/rules/object-curly-spacing.js +++ b/tools/node_modules/eslint/lib/rules/object-curly-spacing.js @@ -74,16 +74,16 @@ module.exports = { * @returns {void} */ function reportNoBeginningSpace(node, token) { + const nextToken = context.getSourceCode().getTokenAfter(token, { includeComments: true }); + context.report({ node, - loc: token.loc.start, + loc: { start: token.loc.end, end: nextToken.loc.start }, message: "There should be no space after '{{token}}'.", data: { token: token.value }, fix(fixer) { - const nextToken = context.getSourceCode().getTokenAfter(token, { includeComments: true }); - return fixer.removeRange([token.range[1], nextToken.range[0]]); } }); @@ -96,16 +96,16 @@ module.exports = { * @returns {void} */ function reportNoEndingSpace(node, token) { + const previousToken = context.getSourceCode().getTokenBefore(token, { includeComments: true }); + context.report({ node, - loc: token.loc.start, + loc: { start: previousToken.loc.end, end: token.loc.start }, message: "There should be no space before '{{token}}'.", data: { token: token.value }, fix(fixer) { - const previousToken = context.getSourceCode().getTokenBefore(token, { includeComments: true }); - return fixer.removeRange([previousToken.range[1], token.range[0]]); } }); @@ -120,7 +120,7 @@ module.exports = { function reportRequiredBeginningSpace(node, token) { context.report({ node, - loc: token.loc.start, + loc: token.loc, message: "A space is required after '{{token}}'.", data: { token: token.value @@ -140,7 +140,7 @@ module.exports = { function reportRequiredEndingSpace(node, token) { context.report({ node, - loc: token.loc.start, + loc: token.loc, message: "A space is required before '{{token}}'.", data: { token: token.value diff --git a/tools/node_modules/eslint/lib/rules/operator-assignment.js b/tools/node_modules/eslint/lib/rules/operator-assignment.js index e294668b42b1ce..b19ba0d02e1a1b 100644 --- a/tools/node_modules/eslint/lib/rules/operator-assignment.js +++ b/tools/node_modules/eslint/lib/rules/operator-assignment.js @@ -71,6 +71,9 @@ function same(a, b) { */ return same(a.object, b.object) && same(a.property, b.property); + case "ThisExpression": + return true; + default: return false; } @@ -83,8 +86,14 @@ function same(a, b) { * @returns {boolean} `true` if the node can be fixed */ function canBeFixed(node) { - return node.type === "Identifier" || - node.type === "MemberExpression" && node.object.type === "Identifier" && (!node.computed || node.property.type === "Literal"); + return ( + node.type === "Identifier" || + ( + node.type === "MemberExpression" && + (node.object.type === "Identifier" || node.object.type === "ThisExpression") && + (!node.computed || node.property.type === "Literal") + ) + ); } module.exports = { diff --git a/tools/node_modules/eslint/lib/rules/prefer-const.js b/tools/node_modules/eslint/lib/rules/prefer-const.js index 87f83892126d50..439a4db3c963f6 100644 --- a/tools/node_modules/eslint/lib/rules/prefer-const.js +++ b/tools/node_modules/eslint/lib/rules/prefer-const.js @@ -356,7 +356,9 @@ module.exports = { const ignoreReadBeforeAssign = options.ignoreReadBeforeAssign === true; const variables = []; let reportCount = 0; - let name = ""; + let checkedId = null; + let checkedName = ""; + /** * Reports given identifier nodes if all of the nodes should be declared @@ -387,25 +389,30 @@ module.exports = { /* * First we check the declaration type and then depending on * if the type is a "VariableDeclarator" or its an "ObjectPattern" - * we compare the name from the first identifier, if the names are different - * we assign the new name and reset the count of reportCount and nodeCount in + * we compare the name and id from the first identifier, if the names are different + * we assign the new name, id and reset the count of reportCount and nodeCount in * order to check each block for the number of reported errors and base our fix * based on comparing nodes.length and nodesToReport.length. */ if (firstDecParent.type === "VariableDeclarator") { - if (firstDecParent.id.name !== name) { - name = firstDecParent.id.name; + if (firstDecParent.id.name !== checkedName) { + checkedName = firstDecParent.id.name; reportCount = 0; } if (firstDecParent.id.type === "ObjectPattern") { - if (firstDecParent.init.name !== name) { - name = firstDecParent.init.name; + if (firstDecParent.init.name !== checkedName) { + checkedName = firstDecParent.init.name; reportCount = 0; } } + + if (firstDecParent.id !== checkedId) { + checkedId = firstDecParent.id; + reportCount = 0; + } } } } diff --git a/tools/node_modules/eslint/lib/rules/prefer-exponentiation-operator.js b/tools/node_modules/eslint/lib/rules/prefer-exponentiation-operator.js new file mode 100644 index 00000000000000..5e75ef4724f3b7 --- /dev/null +++ b/tools/node_modules/eslint/lib/rules/prefer-exponentiation-operator.js @@ -0,0 +1,189 @@ +/** + * @fileoverview Rule to disallow Math.pow in favor of the ** operator + * @author Milos Djermanovic + */ + +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const astUtils = require("./utils/ast-utils"); +const { CALL, ReferenceTracker } = require("eslint-utils"); + +//------------------------------------------------------------------------------ +// Helpers +//------------------------------------------------------------------------------ + +const PRECEDENCE_OF_EXPONENTIATION_EXPR = astUtils.getPrecedence({ type: "BinaryExpression", operator: "**" }); + +/** + * Determines whether the given node needs parens if used as the base in an exponentiation binary expression. + * @param {ASTNode} base The node to check. + * @returns {boolean} `true` if the node needs to be parenthesised. + */ +function doesBaseNeedParens(base) { + return ( + + // '**' is right-associative, parens are needed when Math.pow(a ** b, c) is converted to (a ** b) ** c + astUtils.getPrecedence(base) <= PRECEDENCE_OF_EXPONENTIATION_EXPR || + + // An unary operator cannot be used immediately before an exponentiation expression + base.type === "UnaryExpression" + ); +} + +/** + * Determines whether the given node needs parens if used as the exponent in an exponentiation binary expression. + * @param {ASTNode} exponent The node to check. + * @returns {boolean} `true` if the node needs to be parenthesised. + */ +function doesExponentNeedParens(exponent) { + + // '**' is right-associative, there is no need for parens when Math.pow(a, b ** c) is converted to a ** b ** c + return astUtils.getPrecedence(exponent) < PRECEDENCE_OF_EXPONENTIATION_EXPR; +} + +/** + * Determines whether an exponentiation binary expression at the place of the given node would need parens. + * @param {ASTNode} node A node that would be replaced by an exponentiation binary expression. + * @param {SourceCode} sourceCode A SourceCode object. + * @returns {boolean} `true` if the expression needs to be parenthesised. + */ +function doesExponentiationExpressionNeedParens(node, sourceCode) { + const parent = node.parent; + + const needsParens = ( + parent.type === "ClassDeclaration" || + ( + parent.type.endsWith("Expression") && + astUtils.getPrecedence(parent) >= PRECEDENCE_OF_EXPONENTIATION_EXPR && + !(parent.type === "BinaryExpression" && parent.operator === "**" && parent.right === node) && + !((parent.type === "CallExpression" || parent.type === "NewExpression") && parent.arguments.includes(node)) && + !(parent.type === "MemberExpression" && parent.computed && parent.property === node) && + !(parent.type === "ArrayExpression") + ) + ); + + return needsParens && !astUtils.isParenthesised(sourceCode, node); +} + +/** + * Optionally parenthesizes given text. + * @param {string} text The text to parenthesize. + * @param {boolean} shouldParenthesize If `true`, the text will be parenthesised. + * @returns {string} parenthesised or unchanged text. + */ +function parenthesizeIfShould(text, shouldParenthesize) { + return shouldParenthesize ? `(${text})` : text; +} + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +module.exports = { + meta: { + type: "suggestion", + + docs: { + description: "disallow the use of `Math.pow` in favor of the `**` operator", + category: "Stylistic Issues", + recommended: false, + url: "https://eslint.org/docs/rules/prefer-exponentiation-operator" + }, + + schema: [], + fixable: "code", + + messages: { + useExponentiation: "Use the '**' operator instead of 'Math.pow'." + } + }, + + create(context) { + const sourceCode = context.getSourceCode(); + + /** + * Reports the given node. + * @param {ASTNode} node 'Math.pow()' node to report. + * @returns {void} + */ + function report(node) { + context.report({ + node, + messageId: "useExponentiation", + fix(fixer) { + if ( + node.arguments.length !== 2 || + node.arguments.some(arg => arg.type === "SpreadElement") || + sourceCode.getCommentsInside(node).length > 0 + ) { + return null; + } + + const base = node.arguments[0], + exponent = node.arguments[1], + baseText = sourceCode.getText(base), + exponentText = sourceCode.getText(exponent), + shouldParenthesizeBase = doesBaseNeedParens(base), + shouldParenthesizeExponent = doesExponentNeedParens(exponent), + shouldParenthesizeAll = doesExponentiationExpressionNeedParens(node, sourceCode); + + let prefix = "", + suffix = ""; + + if (!shouldParenthesizeAll) { + if (!shouldParenthesizeBase) { + const firstReplacementToken = sourceCode.getFirstToken(base), + tokenBefore = sourceCode.getTokenBefore(node); + + if ( + tokenBefore && + tokenBefore.range[1] === node.range[0] && + !astUtils.canTokensBeAdjacent(tokenBefore, firstReplacementToken) + ) { + prefix = " "; // a+Math.pow(++b, c) -> a+ ++b**c + } + } + if (!shouldParenthesizeExponent) { + const lastReplacementToken = sourceCode.getLastToken(exponent), + tokenAfter = sourceCode.getTokenAfter(node); + + if ( + tokenAfter && + node.range[1] === tokenAfter.range[0] && + !astUtils.canTokensBeAdjacent(lastReplacementToken, tokenAfter) + ) { + suffix = " "; // Math.pow(a, b)in c -> a**b in c + } + } + } + + const baseReplacement = parenthesizeIfShould(baseText, shouldParenthesizeBase), + exponentReplacement = parenthesizeIfShould(exponentText, shouldParenthesizeExponent), + replacement = parenthesizeIfShould(`${baseReplacement}**${exponentReplacement}`, shouldParenthesizeAll); + + return fixer.replaceText(node, `${prefix}${replacement}${suffix}`); + } + }); + } + + return { + Program() { + const scope = context.getScope(); + const tracker = new ReferenceTracker(scope); + const trackMap = { + Math: { + pow: { [CALL]: true } + } + }; + + for (const { node } of tracker.iterateGlobalReferences(trackMap)) { + report(node); + } + } + }; + } +}; diff --git a/tools/node_modules/eslint/lib/rules/require-await.js b/tools/node_modules/eslint/lib/rules/require-await.js index 0aa6fce7e19859..22c111b6dc852e 100644 --- a/tools/node_modules/eslint/lib/rules/require-await.js +++ b/tools/node_modules/eslint/lib/rules/require-await.js @@ -89,9 +89,17 @@ module.exports = { "ArrowFunctionExpression:exit": exitFunction, AwaitExpression() { + if (!scopeInfo) { + return; + } + scopeInfo.hasAwait = true; }, ForOfStatement(node) { + if (!scopeInfo) { + return; + } + if (node.await) { scopeInfo.hasAwait = true; } diff --git a/tools/node_modules/eslint/lib/rules/semi.js b/tools/node_modules/eslint/lib/rules/semi.js index a159e19d347dcf..22e299efe72e49 100644 --- a/tools/node_modules/eslint/lib/rules/semi.js +++ b/tools/node_modules/eslint/lib/rules/semi.js @@ -93,17 +93,20 @@ module.exports = { const lastToken = sourceCode.getLastToken(node); let message, fix, - loc = lastToken.loc; + loc; if (!missing) { message = "Missing semicolon."; - loc = loc.end; + loc = { + start: lastToken.loc.end, + end: astUtils.getNextLocation(sourceCode, lastToken.loc.end) + }; fix = function(fixer) { return fixer.insertTextAfter(lastToken, ";"); }; } else { message = "Extra semicolon."; - loc = loc.start; + loc = lastToken.loc; fix = function(fixer) { /* diff --git a/tools/node_modules/eslint/lib/rules/space-infix-ops.js b/tools/node_modules/eslint/lib/rules/space-infix-ops.js index b2fbe47b4765e6..bd2c0ae0e1d3b7 100644 --- a/tools/node_modules/eslint/lib/rules/space-infix-ops.js +++ b/tools/node_modules/eslint/lib/rules/space-infix-ops.js @@ -69,7 +69,7 @@ module.exports = { function report(mainNode, culpritToken) { context.report({ node: mainNode, - loc: culpritToken.loc.start, + loc: culpritToken.loc, message: "Operator '{{operator}}' must be spaced.", data: { operator: culpritToken.value diff --git a/tools/node_modules/eslint/lib/rules/spaced-comment.js b/tools/node_modules/eslint/lib/rules/spaced-comment.js index 958bb2c6444080..daf56cd6bb42d0 100644 --- a/tools/node_modules/eslint/lib/rules/spaced-comment.js +++ b/tools/node_modules/eslint/lib/rules/spaced-comment.js @@ -249,7 +249,8 @@ module.exports = { beginRegex: requireSpace ? createAlwaysStylePattern(markers, exceptions) : createNeverStylePattern(markers), endRegex: balanced && requireSpace ? new RegExp(`${createExceptionsPattern(exceptions)}$`, "u") : new RegExp(endNeverPattern, "u"), hasExceptions: exceptions.length > 0, - markers: new RegExp(`^(${markers.map(escape).join("|")})`, "u") + captureMarker: new RegExp(`^(${markers.map(escape).join("|")})`, "u"), + markers: new Set(markers) }; return rule; @@ -322,8 +323,8 @@ module.exports = { rule = styleRules[type], commentIdentifier = type === "block" ? "/*" : "//"; - // Ignores empty comments. - if (node.value.length === 0) { + // Ignores empty comments and comments that consist only of a marker. + if (node.value.length === 0 || rule.markers.has(node.value)) { return; } @@ -333,7 +334,7 @@ module.exports = { // Checks. if (requireSpace) { if (!beginMatch) { - const hasMarker = rule.markers.exec(node.value); + const hasMarker = rule.captureMarker.exec(node.value); const marker = hasMarker ? commentIdentifier + hasMarker[0] : commentIdentifier; if (rule.hasExceptions) { diff --git a/tools/node_modules/eslint/lib/rules/utils/ast-utils.js b/tools/node_modules/eslint/lib/rules/utils/ast-utils.js index 17e056c240c13d..01c6b47b82eefc 100644 --- a/tools/node_modules/eslint/lib/rules/utils/ast-utils.js +++ b/tools/node_modules/eslint/lib/rules/utils/ast-utils.js @@ -581,23 +581,31 @@ module.exports = { * * First, this checks the node: * - * - The function name does not start with uppercase (it's a constructor). + * - The function name does not start with uppercase. It's a convention to capitalize the names + * of constructor functions. This check is not performed if `capIsConstructor` is set to `false`. * - The function does not have a JSDoc comment that has a @this tag. * * Next, this checks the location of the node. * If the location is below, this judges `this` is valid. * * - The location is not on an object literal. - * - The location is not assigned to a variable which starts with an uppercase letter. + * - The location is not assigned to a variable which starts with an uppercase letter. Applies to anonymous + * functions only, as the name of the variable is considered to be the name of the function in this case. + * This check is not performed if `capIsConstructor` is set to `false`. * - The location is not on an ES2015 class. * - Its `bind`/`call`/`apply` method is not called directly. * - The function is not a callback of array methods (such as `.forEach()`) if `thisArg` is given. * @param {ASTNode} node A function node to check. * @param {SourceCode} sourceCode A SourceCode instance to get comments. + * @param {boolean} [capIsConstructor = true] `false` disables the assumption that functions which name starts + * with an uppercase or are assigned to a variable which name starts with an uppercase are constructors. * @returns {boolean} The function node is the default `this` binding. */ - isDefaultThisBinding(node, sourceCode) { - if (isES5Constructor(node) || hasJSDocThisTag(node, sourceCode)) { + isDefaultThisBinding(node, sourceCode, { capIsConstructor = true } = {}) { + if ( + (capIsConstructor && isES5Constructor(node)) || + hasJSDocThisTag(node, sourceCode) + ) { return false; } const isAnonymous = node.id === null; @@ -671,6 +679,7 @@ module.exports = { return false; } if ( + capIsConstructor && isAnonymous && parent.left.type === "Identifier" && startsWithUpperCase(parent.left.name) @@ -685,6 +694,7 @@ module.exports = { */ case "VariableDeclarator": return !( + capIsConstructor && isAnonymous && parent.init === currentNode && parent.id.type === "Identifier" && @@ -1200,6 +1210,23 @@ module.exports = { }; }, + /** + * Gets next location when the result is not out of bound, otherwise returns null. + * @param {SourceCode} sourceCode The sourceCode + * @param {{line: number, column: number}} location The location + * @returns {{line: number, column: number} | null} Next location + */ + getNextLocation(sourceCode, location) { + const index = sourceCode.getIndexFromLoc(location); + + // Avoid out of bound location + if (index + 1 > sourceCode.text.length) { + return null; + } + + return sourceCode.getLocFromIndex(index + 1); + }, + /** * Gets the parenthesized text of a node. This is similar to sourceCode.getText(node), but it also includes any parentheses * surrounding the node. diff --git a/tools/node_modules/eslint/lib/shared/types.js b/tools/node_modules/eslint/lib/shared/types.js index 12bd0aed8c6f1d..a5bd0200e2792a 100644 --- a/tools/node_modules/eslint/lib/shared/types.js +++ b/tools/node_modules/eslint/lib/shared/types.js @@ -30,6 +30,7 @@ module.exports = {}; * @property {Record} [env] The environment settings. * @property {string | string[]} [extends] The path to other config files or the package name of shareable configs. * @property {Record} [globals] The global variable settings. + * @property {string | string[]} [ignorePatterns] The glob patterns that ignore to lint. * @property {boolean} [noInlineConfig] The flag that disables directive comments. * @property {OverrideConfigData[]} [overrides] The override settings per kind of files. * @property {string} [parser] The path to a parser or the package name of a parser. @@ -91,6 +92,14 @@ module.exports = {}; * @property {string} message The error message. * @property {string|null} ruleId The ID of the rule which makes this message. * @property {0|1|2} severity The severity of this message. + * @property {Array<{desc?: string, messageId?: string, fix: {range: [number, number], text: string}}>} [suggestions] Information for suggestions. + */ + +/** + * @typedef {Object} SuggestionResult + * @property {string} desc A short description. + * @property {string} [messageId] Id referencing a message for the description. + * @property {{ text: string, range: number[] }} fix fix result info */ /** diff --git a/tools/node_modules/eslint/lib/source-code/source-code.js b/tools/node_modules/eslint/lib/source-code/source-code.js index 86a56803ed76c3..20b442f2367203 100644 --- a/tools/node_modules/eslint/lib/source-code/source-code.js +++ b/tools/node_modules/eslint/lib/source-code/source-code.js @@ -78,6 +78,18 @@ function sortedMerge(tokens, comments) { return result; } +/** + * Determines if two nodes or tokens overlap. + * @param {ASTNode|Token} first The first node or token to check. + * @param {ASTNode|Token} second The second node or token to check. + * @returns {boolean} True if the two nodes or tokens overlap. + * @private + */ +function nodesOrTokensOverlap(first, second) { + return (first.range[0] <= second.range[0] && first.range[1] >= second.range[0]) || + (second.range[0] <= first.range[0] && second.range[1] >= first.range[0]); +} + //------------------------------------------------------------------------------ // Public Interface //------------------------------------------------------------------------------ @@ -411,19 +423,52 @@ class SourceCode extends TokenStore { } /** - * Determines if two tokens have at least one whitespace character - * between them. This completely disregards comments in making the - * determination, so comments count as zero-length substrings. - * @param {Token} first The token to check after. - * @param {Token} second The token to check before. - * @returns {boolean} True if there is only space between tokens, false - * if there is anything other than whitespace between tokens. + * Determines if two nodes or tokens have at least one whitespace character + * between them. Order does not matter. Returns false if the given nodes or + * tokens overlap. + * @param {ASTNode|Token} first The first node or token to check between. + * @param {ASTNode|Token} second The second node or token to check between. + * @returns {boolean} True if there is a whitespace character between + * any of the tokens found between the two given nodes or tokens. * @public */ - isSpaceBetweenTokens(first, second) { - const text = this.text.slice(first.range[1], second.range[0]); + isSpaceBetween(first, second) { + if (nodesOrTokensOverlap(first, second)) { + return false; + } + + const [startingNodeOrToken, endingNodeOrToken] = first.range[1] <= second.range[0] + ? [first, second] + : [second, first]; + const firstToken = this.getLastToken(startingNodeOrToken) || startingNodeOrToken; + const finalToken = this.getFirstToken(endingNodeOrToken) || endingNodeOrToken; + let currentToken = firstToken; - return /\s/u.test(text.replace(/\/\*.*?\*\//gus, "")); + while (currentToken !== finalToken) { + const nextToken = this.getTokenAfter(currentToken, { includeComments: true }); + + if (currentToken.range[1] !== nextToken.range[0]) { + return true; + } + + currentToken = nextToken; + } + + return false; + } + + /** + * Determines if two nodes or tokens have at least one whitespace character + * between them. Order does not matter. Returns false if the given nodes or + * tokens overlap. + * @param {...ASTNode|Token} args The nodes or tokens to check between. + * @returns {boolean} True if there is a whitespace character between + * any of the tokens found between the two given nodes or tokens. + * @deprecated in favor of isSpaceBetween(). + * @public + */ + isSpaceBetweenTokens(...args) { + return this.isSpaceBetween(...args); } /** diff --git a/tools/node_modules/eslint/node_modules/ansi-escapes/index.js b/tools/node_modules/eslint/node_modules/ansi-escapes/index.js index 4638bbc3d62b29..283331858f02e1 100644 --- a/tools/node_modules/eslint/node_modules/ansi-escapes/index.js +++ b/tools/node_modules/eslint/node_modules/ansi-escapes/index.js @@ -128,5 +128,30 @@ ansiEscapes.image = (buffer, options = {}) => { }; ansiEscapes.iTerm = { - setCwd: (cwd = process.cwd()) => `${OSC}50;CurrentDir=${cwd}${BEL}` + setCwd: (cwd = process.cwd()) => `${OSC}50;CurrentDir=${cwd}${BEL}`, + + annotation: (message, options = {}) => { + let ret = `${OSC}1337;`; + + const hasX = typeof options.x !== 'undefined'; + const hasY = typeof options.y !== 'undefined'; + if ((hasX || hasY) && !(hasX && hasY && typeof options.length !== 'undefined')) { + throw new Error('`x`, `y` and `length` must be defined when `x` or `y` is defined'); + } + + message = message.replace(/\|/g, ''); + + ret += options.isHidden ? 'AddHiddenAnnotation=' : 'AddAnnotation='; + + if (options.length > 0) { + ret += + (hasX ? + [message, options.length, options.x, options.y] : + [options.length, message]).join('|'); + } else { + ret += message; + } + + return ret + BEL; + } }; diff --git a/tools/node_modules/eslint/node_modules/ansi-escapes/package.json b/tools/node_modules/eslint/node_modules/ansi-escapes/package.json index a58ed743a59171..3d97c617866121 100644 --- a/tools/node_modules/eslint/node_modules/ansi-escapes/package.json +++ b/tools/node_modules/eslint/node_modules/ansi-escapes/package.json @@ -9,15 +9,15 @@ }, "bundleDependencies": false, "dependencies": { - "type-fest": "^0.5.2" + "type-fest": "^0.8.1" }, "deprecated": false, "description": "ANSI escape codes for manipulating the terminal", "devDependencies": { "@types/node": "^12.0.7", "ava": "^2.1.0", - "tsd": "^0.7.1", - "xo": "^0.24.0" + "tsd": "^0.11.0", + "xo": "^0.25.3" }, "engines": { "node": ">=8" @@ -61,5 +61,5 @@ "scripts": { "test": "xo && ava && tsd" }, - "version": "4.2.1" + "version": "4.3.0" } \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/ansi-escapes/readme.md b/tools/node_modules/eslint/node_modules/ansi-escapes/readme.md index b290753094d948..818a9263f4e680 100644 --- a/tools/node_modules/eslint/node_modules/ansi-escapes/readme.md +++ b/tools/node_modules/eslint/node_modules/ansi-escapes/readme.md @@ -2,14 +2,12 @@ > [ANSI escape codes](http://www.termsys.demon.co.uk/vtansi.htm) for manipulating the terminal - ## Install ``` $ npm install ansi-escapes ``` - ## Usage ```js @@ -20,7 +18,6 @@ process.stdout.write(ansiEscapes.cursorUp(2) + ansiEscapes.cursorLeft); //=> '\u001B[2A\u001B[1000D' ``` - ## API ### cursorTo(x, y?) @@ -165,16 +162,71 @@ The width and height are given as a number followed by a unit, or the word "auto ##### preserveAspectRatio -Type: `boolean`
+Type: `boolean`\ Default: `true` ### iTerm.setCwd(path?) -Type: `string`
+Type: `string`\ Default: `process.cwd()` [Inform iTerm2](https://www.iterm2.com/documentation-escape-codes.html) of the current directory to help semantic history and enable [Cmd-clicking relative paths](https://coderwall.com/p/b7e82q/quickly-open-files-in-iterm-with-cmd-click). +### iTerm.annotation(message, options?) + +Creates an escape code to display an "annotation" in iTerm2. + +An annotation looks like this when shown: + + + +See the [iTerm Proprietary Escape Codes documentation](https://iterm2.com/documentation-escape-codes.html) for more information. + +#### message + +Type: `string` + +The message to display within the annotation. + +The `|` character is disallowed and will be stripped. + +#### options + +Type: `object` + +##### length + +Type: `number`\ +Default: The remainder of the line + +Nonzero number of columns to annotate. + +##### x + +Type: `number`\ +Default: Cursor position + +Starting X coordinate. + +Must be used with `y` and `length`. + +##### y + +Type: `number`\ +Default: Cursor position + +Starting Y coordinate. + +Must be used with `x` and `length`. + +##### isHidden + +Type: `boolean`\ +Default: `false` + +Create a "hidden" annotation. + +Annotations created this way can be shown using the "Show Annotations" iTerm command. ## Related diff --git a/tools/node_modules/eslint/node_modules/ansi-regex/index.js b/tools/node_modules/eslint/node_modules/ansi-regex/index.js index c25448009f304d..35054aa6774afe 100644 --- a/tools/node_modules/eslint/node_modules/ansi-regex/index.js +++ b/tools/node_modules/eslint/node_modules/ansi-regex/index.js @@ -1,14 +1,10 @@ 'use strict'; -module.exports = options => { - options = Object.assign({ - onlyFirst: false - }, options); - +module.exports = ({onlyFirst = false} = {}) => { const pattern = [ '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)', '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))' ].join('|'); - return new RegExp(pattern, options.onlyFirst ? undefined : 'g'); + return new RegExp(pattern, onlyFirst ? undefined : 'g'); }; diff --git a/tools/node_modules/eslint/node_modules/ansi-regex/package.json b/tools/node_modules/eslint/node_modules/ansi-regex/package.json index db8f3cc8c7d14e..d0574afde48599 100644 --- a/tools/node_modules/eslint/node_modules/ansi-regex/package.json +++ b/tools/node_modules/eslint/node_modules/ansi-regex/package.json @@ -11,14 +11,16 @@ "deprecated": false, "description": "Regular expression for matching ANSI escape codes", "devDependencies": { - "ava": "^0.25.0", - "xo": "^0.23.0" + "ava": "^2.4.0", + "tsd": "^0.9.0", + "xo": "^0.25.3" }, "engines": { - "node": ">=6" + "node": ">=8" }, "files": [ - "index.js" + "index.js", + "index.d.ts" ], "homepage": "https://github.com/chalk/ansi-regex#readme", "keywords": [ @@ -55,8 +57,8 @@ "url": "git+https://github.com/chalk/ansi-regex.git" }, "scripts": { - "test": "xo && ava", + "test": "xo && ava && tsd", "view-supported": "node fixtures/view-codes.js" }, - "version": "4.1.0" + "version": "5.0.0" } \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/ansi-regex/readme.md b/tools/node_modules/eslint/node_modules/ansi-regex/readme.md index d19c44667e704b..3c2b77c4354b68 100644 --- a/tools/node_modules/eslint/node_modules/ansi-regex/readme.md +++ b/tools/node_modules/eslint/node_modules/ansi-regex/readme.md @@ -2,20 +2,6 @@ > Regular expression for matching [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code) ---- - -
- - Get professional support for this package with a Tidelift subscription - -
- - Tidelift helps make open source sustainable for maintainers while giving companies
assurances about security, maintenance, and licensing for their dependencies. -
-
- ---- - ## Install @@ -48,12 +34,14 @@ ansiRegex().test('cake'); ## API -### ansiRegex([options]) +### ansiRegex(options?) Returns a regex for matching ANSI escape codes. #### options +Type: `object` + ##### onlyFirst Type: `boolean`
@@ -71,17 +59,20 @@ Some of the codes we run as a test are codes that we acquired finding various li On the historical side, those ECMA standards were established in the early 90's whereas the VT100, for example, was designed in the mid/late 70's. At that point in time, control codes were still pretty ungoverned and engineers used them for a multitude of things, namely to activate hardware ports that may have been proprietary. Somewhere else you see a similar 'anarchy' of codes is in the x86 architecture for processors; there are a ton of "interrupts" that can mean different things on certain brands of processors, most of which have been phased out. -## Security - -To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure. - - ## Maintainers - [Sindre Sorhus](https://github.com/sindresorhus) - [Josh Junon](https://github.com/qix-) -## License +--- -MIT +
+ + Get professional support for this package with a Tidelift subscription + +
+ + Tidelift helps make open source sustainable for maintainers while giving companies
assurances about security, maintenance, and licensing for their dependencies. +
+
diff --git a/tools/node_modules/eslint/node_modules/glob/package.json b/tools/node_modules/eslint/node_modules/glob/package.json index cf2be43974e853..bf5a63608ca133 100644 --- a/tools/node_modules/eslint/node_modules/glob/package.json +++ b/tools/node_modules/eslint/node_modules/glob/package.json @@ -32,6 +32,9 @@ "sync.js", "common.js" ], + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, "homepage": "https://github.com/isaacs/node-glob#readme", "license": "ISC", "main": "glob.js", @@ -49,5 +52,5 @@ "test": "tap test/*.js --cov", "test-regen": "npm run profclean && TEST_REGEN=1 node test/00-setup.js" }, - "version": "7.1.5" + "version": "7.1.6" } \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/globals/globals.json b/tools/node_modules/eslint/node_modules/globals/globals.json index 6f5ac02f1eace7..b33f0431b544e9 100644 --- a/tools/node_modules/eslint/node_modules/globals/globals.json +++ b/tools/node_modules/eslint/node_modules/globals/globals.json @@ -1066,6 +1066,7 @@ "beforeEach": false, "describe": false, "expect": false, + "expectAsync": false, "fail": false, "fdescribe": false, "fit": false, @@ -1074,6 +1075,7 @@ "pending": false, "runs": false, "spyOn": false, + "spyOnAllFunctions": false, "spyOnProperty": false, "waits": false, "waitsFor": false, @@ -1184,8 +1186,11 @@ }, "wsh": { "ActiveXObject": true, + "CollectGarbage": true, + "Debug": true, "Enumerator": true, "GetObject": true, + "RuntimeObject": true, "ScriptEngine": true, "ScriptEngineBuildVersion": true, "ScriptEngineMajorVersion": true, diff --git a/tools/node_modules/eslint/node_modules/globals/package.json b/tools/node_modules/eslint/node_modules/globals/package.json index ae094f3054063e..de6b8926f166d9 100644 --- a/tools/node_modules/eslint/node_modules/globals/package.json +++ b/tools/node_modules/eslint/node_modules/globals/package.json @@ -8,17 +8,22 @@ "url": "https://github.com/sindresorhus/globals/issues" }, "bundleDependencies": false, + "dependencies": { + "type-fest": "^0.8.1" + }, "deprecated": false, "description": "Global identifiers from different JavaScript environments", "devDependencies": { - "ava": "0.21.0", - "xo": "0.18.0" + "ava": "^2.2.0", + "tsd": "^0.9.0", + "xo": "^0.25.3" }, "engines": { - "node": ">=4" + "node": ">=8" }, "files": [ "index.js", + "index.d.ts", "globals.json" ], "homepage": "https://github.com/sindresorhus/globals#readme", @@ -41,7 +46,12 @@ "scripts": { "test": "xo && ava" }, - "version": "11.12.0", + "tsd": { + "compilerOptions": { + "resolveJsonModule": true + } + }, + "version": "12.3.0", "xo": { "ignores": [ "get-browser-globals.js" diff --git a/tools/node_modules/eslint/node_modules/globals/readme.md b/tools/node_modules/eslint/node_modules/globals/readme.md index 8c47855f34d900..96ce28347a8de0 100644 --- a/tools/node_modules/eslint/node_modules/globals/readme.md +++ b/tools/node_modules/eslint/node_modules/globals/readme.md @@ -36,6 +36,14 @@ console.log(globals.browser); Each global is given a value of `true` or `false`. A value of `true` indicates that the variable may be overwritten. A value of `false` indicates that the variable should be considered read-only. This information is used by static analysis tools to flag incorrect behavior. We assume all variables should be `false` unless we hear otherwise. -## License - -MIT © [Sindre Sorhus](https://sindresorhus.com) +--- + +
+ + Get professional support for this package with a Tidelift subscription + +
+ + Tidelift helps make open source sustainable for maintainers while giving companies
assurances about security, maintenance, and licensing for their dependencies. +
+
diff --git a/tools/node_modules/eslint/node_modules/import-fresh/index.js b/tools/node_modules/eslint/node_modules/import-fresh/index.js index 7abc6706192d64..425ed98c42f68e 100644 --- a/tools/node_modules/eslint/node_modules/import-fresh/index.js +++ b/tools/node_modules/eslint/node_modules/import-fresh/index.js @@ -8,22 +8,25 @@ module.exports = moduleId => { throw new TypeError('Expected a string'); } - const filePath = resolveFrom(path.dirname(parentModule(__filename)), moduleId); + const parentPath = parentModule(__filename); + const filePath = resolveFrom(path.dirname(parentPath), moduleId); + + const oldModule = require.cache[filePath]; // Delete itself from module parent - if (require.cache[filePath] && require.cache[filePath].parent) { - let i = require.cache[filePath].parent.children.length; + if (oldModule && oldModule.parent) { + let i = oldModule.parent.children.length; while (i--) { - if (require.cache[filePath].parent.children[i].id === filePath) { - require.cache[filePath].parent.children.splice(i, 1); + if (oldModule.parent.children[i].id === filePath) { + oldModule.parent.children.splice(i, 1); } } } - // Delete module from cache - delete require.cache[filePath]; + delete require.cache[filePath]; // Delete module from cache + + const parent = require.cache[parentPath]; // If `filePath` and `parentPath` are the same, cache will already be deleted so we won't get a memory leak in next step - // Return fresh module - return require(filePath); + return parent === undefined ? require(filePath) : parent.require(filePath); // In case cache doesn't have parent, fall back to normal require }; diff --git a/tools/node_modules/eslint/node_modules/import-fresh/package.json b/tools/node_modules/eslint/node_modules/import-fresh/package.json index 73e9316d8999b7..38892a62e43136 100644 --- a/tools/node_modules/eslint/node_modules/import-fresh/package.json +++ b/tools/node_modules/eslint/node_modules/import-fresh/package.json @@ -47,5 +47,5 @@ "heapdump": "node heapdump.js", "test": "xo && ava && tsd" }, - "version": "3.1.0" + "version": "3.2.1" } \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/import-fresh/readme.md b/tools/node_modules/eslint/node_modules/import-fresh/readme.md index f5414b8cf33fdd..0bfa1c90443e83 100644 --- a/tools/node_modules/eslint/node_modules/import-fresh/readme.md +++ b/tools/node_modules/eslint/node_modules/import-fresh/readme.md @@ -37,22 +37,16 @@ importFresh('./foo')(); ``` +## import-fresh for enterprise + +Available as part of the Tidelift Subscription. + +The maintainers of import-fresh and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-import-fresh?utm_source=npm-import-fresh&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) + + ## Related - [clear-module](https://github.com/sindresorhus/clear-module) - Clear a module from the import cache - [import-from](https://github.com/sindresorhus/import-from) - Import a module from a given path - [import-cwd](https://github.com/sindresorhus/import-cwd) - Import a module from the current working directory - [import-lazy](https://github.com/sindresorhus/import-lazy) - Import modules lazily - - ---- - -
- - Get professional support for this package with a Tidelift subscription - -
- - Tidelift helps make open source sustainable for maintainers while giving companies
assurances about security, maintenance, and licensing for their dependencies. -
-
diff --git a/tools/node_modules/eslint/node_modules/optionator/README.md b/tools/node_modules/eslint/node_modules/optionator/README.md index 91c59d379b3bae..8e4ba424526e44 100644 --- a/tools/node_modules/eslint/node_modules/optionator/README.md +++ b/tools/node_modules/eslint/node_modules/optionator/README.md @@ -1,7 +1,7 @@ # Optionator -Optionator is a JavaScript option parsing and help generation library used by [eslint](http://eslint.org), [Grasp](http://graspjs.com), [LiveScript](http://livescript.net), [esmangle](https://github.com/estools/esmangle), [escodegen](https://github.com/estools/escodegen), and [many more](https://www.npmjs.com/browse/depended/optionator). +Optionator is a JavaScript/Node.js option parsing and help generation library used by [eslint](http://eslint.org), [Grasp](http://graspjs.com), [LiveScript](http://livescript.net), [esmangle](https://github.com/estools/esmangle), [escodegen](https://github.com/estools/escodegen), and [many more](https://www.npmjs.com/browse/depended/optionator). For an online demo, check out the [Grasp online demo](http://www.graspjs.com/#demo). @@ -23,12 +23,14 @@ Other helpful features include reformatting the help text based on the size of t ## About Optionator uses [type-check](https://github.com/gkz/type-check) and [levn](https://github.com/gkz/levn) behind the scenes to cast and verify input according the specified types. -MIT license. Version 0.8.2 +MIT license. Version 0.8.3 npm install optionator For updates on Optionator, [follow me on twitter](https://twitter.com/gkzahariev). +Optionator is a Node.js module, but can be used in the browser as well if packed with webpack/browserify. + ## Usage `require('optionator');` returns a function. It has one property, `VERSION`, the current version of the library as a string. This function is called with an object specifying your options and other information, see the [settings format section](#settings-format). This in turn returns an object with three properties, `parse`, `parseArgv`, `generateHelp`, and `generateHelpForOption`, which are all functions. diff --git a/tools/node_modules/eslint/node_modules/optionator/lib/help.js b/tools/node_modules/eslint/node_modules/optionator/lib/help.js index a459c02c26711b..59e6f9694c9584 100644 --- a/tools/node_modules/eslint/node_modules/optionator/lib/help.js +++ b/tools/node_modules/eslint/node_modules/optionator/lib/help.js @@ -1,9 +1,22 @@ -// Generated by LiveScript 1.5.0 +// Generated by LiveScript 1.6.0 (function(){ - var ref$, id, find, sort, min, max, map, unlines, nameToRaw, dasherize, naturalJoin, wordwrap, getPreText, setHelpStyleDefaults, generateHelpForOption, generateHelp; + var ref$, id, find, sort, min, max, map, unlines, nameToRaw, dasherize, naturalJoin, wordWrap, wordwrap, getPreText, setHelpStyleDefaults, generateHelpForOption, generateHelp; ref$ = require('prelude-ls'), id = ref$.id, find = ref$.find, sort = ref$.sort, min = ref$.min, max = ref$.max, map = ref$.map, unlines = ref$.unlines; ref$ = require('./util'), nameToRaw = ref$.nameToRaw, dasherize = ref$.dasherize, naturalJoin = ref$.naturalJoin; - wordwrap = require('wordwrap'); + wordWrap = require('word-wrap'); + wordwrap = function(a, b){ + var ref$, indent, width; + ref$ = b === undefined + ? ['', a - 1] + : [repeatString$(' ', a), b - a - 1], indent = ref$[0], width = ref$[1]; + return function(text){ + return wordWrap(text, { + indent: indent, + width: width, + trim: true + }); + }; + }; getPreText = function(option, arg$, maxWidth){ var mainName, shortNames, ref$, longNames, type, description, aliasSeparator, typeSeparator, initialIndent, names, namesString, namesStringLen, typeSeparatorString, typeSeparatorStringLen, wrap; mainName = option.option, shortNames = (ref$ = option.shortNames) != null diff --git a/tools/node_modules/eslint/node_modules/optionator/lib/index.js b/tools/node_modules/eslint/node_modules/optionator/lib/index.js index d947286c761ef8..7ce37b23b01c98 100644 --- a/tools/node_modules/eslint/node_modules/optionator/lib/index.js +++ b/tools/node_modules/eslint/node_modules/optionator/lib/index.js @@ -1,7 +1,7 @@ -// Generated by LiveScript 1.5.0 +// Generated by LiveScript 1.6.0 (function(){ - var VERSION, ref$, id, map, compact, any, groupBy, partition, chars, isItNaN, keys, Obj, camelize, deepIs, closestString, nameToRaw, dasherize, naturalJoin, generateHelp, generateHelpForOption, parsedTypeCheck, parseType, parseLevn, camelizeKeys, parseString, main, toString$ = {}.toString, slice$ = [].slice; - VERSION = '0.8.2'; + var VERSION, ref$, id, map, compact, any, groupBy, partition, chars, isItNaN, keys, Obj, camelize, deepIs, closestString, nameToRaw, dasherize, naturalJoin, generateHelp, generateHelpForOption, parsedTypeCheck, parseType, parseLevn, camelizeKeys, parseString, main, toString$ = {}.toString, slice$ = [].slice, arrayFrom$ = Array.from || function(x){return slice$.call(x);}; + VERSION = '0.8.3'; ref$ = require('prelude-ls'), id = ref$.id, map = ref$.map, compact = ref$.compact, any = ref$.any, groupBy = ref$.groupBy, partition = ref$.partition, chars = ref$.chars, isItNaN = ref$.isItNaN, keys = ref$.keys, Obj = ref$.Obj, camelize = ref$.camelize; deepIs = require('deep-is'); ref$ = require('./util'), closestString = ref$.closestString, nameToRaw = ref$.nameToRaw, dasherize = ref$.dasherize, naturalJoin = ref$.naturalJoin; @@ -17,7 +17,7 @@ return resultObj$; }; parseString = function(string){ - var assignOpt, regex, replaceRegex, result, this$ = this; + var assignOpt, regex, replaceRegex, result; assignOpt = '--?[a-zA-Z][-a-z-A-Z0-9]*='; regex = RegExp('(?:' + assignOpt + ')?(?:\'(?:\\\\\'|[^\'])+\'|"(?:\\\\"|[^"])+")|[^\'"\\s]+', 'g'); replaceRegex = RegExp('^(' + assignOpt + ')?[\'"]([\\s\\S]*)[\'"]$'); @@ -44,7 +44,7 @@ libOptions.defaults.mergeRepeatedObjects = libOptions.mergeRepeatedObjects; } traverse = function(options){ - var i$, len$, option, name, k, ref$, v, type, that, e, parsedPossibilities, parsedType, j$, len1$, possibility, rawDependsType, dependsOpts, dependsType, cra, alias, shortNames, longNames, this$ = this; + var i$, len$, option, name, k, ref$, v, type, that, e, parsedPossibilities, parsedType, j$, len1$, possibility, rawDependsType, dependsOpts, dependsType, cra, alias, shortNames, longNames; if (toString$.call(options).slice(8, -1) !== 'Array') { throw new Error('No options defined.'); } @@ -104,7 +104,7 @@ dependsType = rawDependsType.toLowerCase(); if (dependsOpts.length) { if (dependsType === 'and' || dependsType === 'or') { - option.dependsOn = [dependsType].concat(slice$.call(dependsOpts)); + option.dependsOn = [dependsType].concat(arrayFrom$(dependsOpts)); } else { throw new Error("Option '" + name + "': If you have more than one dependency, you must specify either 'and' or 'or'"); } diff --git a/tools/node_modules/eslint/node_modules/optionator/lib/util.js b/tools/node_modules/eslint/node_modules/optionator/lib/util.js index d5c972def14cfa..5bc0cbb69e66aa 100644 --- a/tools/node_modules/eslint/node_modules/optionator/lib/util.js +++ b/tools/node_modules/eslint/node_modules/optionator/lib/util.js @@ -1,10 +1,10 @@ -// Generated by LiveScript 1.5.0 +// Generated by LiveScript 1.6.0 (function(){ var prelude, map, sortBy, fl, closestString, nameToRaw, dasherize, naturalJoin; prelude = require('prelude-ls'), map = prelude.map, sortBy = prelude.sortBy; fl = require('fast-levenshtein'); closestString = function(possibilities, input){ - var distances, ref$, string, distance, this$ = this; + var distances, ref$, string, distance; if (!possibilities.length) { return; } diff --git a/tools/node_modules/eslint/node_modules/optionator/package.json b/tools/node_modules/eslint/node_modules/optionator/package.json index d492cd682bdc29..b69935ea0e8e6d 100644 --- a/tools/node_modules/eslint/node_modules/optionator/package.json +++ b/tools/node_modules/eslint/node_modules/optionator/package.json @@ -9,18 +9,18 @@ "bundleDependencies": false, "dependencies": { "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", + "fast-levenshtein": "~2.0.6", "levn": "~0.3.0", "prelude-ls": "~1.1.2", "type-check": "~0.3.2", - "wordwrap": "~1.0.0" + "word-wrap": "~1.2.3" }, "deprecated": false, "description": "option parsing and help generation", "devDependencies": { - "istanbul": "~0.4.1", - "livescript": "~1.5.0", - "mocha": "~3.0.2" + "istanbul": "~0.4.5", + "livescript": "~1.6.0", + "mocha": "~6.2.2" }, "engines": { "node": ">= 0.8.0" @@ -47,5 +47,5 @@ "scripts": { "test": "make test" }, - "version": "0.8.2" + "version": "0.8.3" } \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/index.js b/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/index.js new file mode 100644 index 00000000000000..9a593dfcd1fd5c --- /dev/null +++ b/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/index.js @@ -0,0 +1,4 @@ +'use strict'; +const ansiRegex = require('ansi-regex'); + +module.exports = string => typeof string === 'string' ? string.replace(ansiRegex(), '') : string; diff --git a/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/license b/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/license new file mode 100644 index 00000000000000..e7af2f77107d73 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/license @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/package.json b/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/package.json new file mode 100644 index 00000000000000..5db6f68dc0aef7 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/package.json @@ -0,0 +1,63 @@ +{ + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/chalk/strip-ansi/issues" + }, + "bundleDependencies": false, + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "deprecated": false, + "description": "Strip ANSI escape codes from a string", + "devDependencies": { + "ava": "^2.4.0", + "tsd": "^0.10.0", + "xo": "^0.25.3" + }, + "engines": { + "node": ">=8" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "homepage": "https://github.com/chalk/strip-ansi#readme", + "keywords": [ + "strip", + "trim", + "remove", + "ansi", + "styles", + "color", + "colour", + "colors", + "terminal", + "console", + "string", + "tty", + "escape", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "log", + "logging", + "command-line", + "text" + ], + "license": "MIT", + "name": "strip-ansi", + "repository": { + "type": "git", + "url": "git+https://github.com/chalk/strip-ansi.git" + }, + "scripts": { + "test": "xo && ava && tsd" + }, + "version": "6.0.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/readme.md b/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/readme.md new file mode 100644 index 00000000000000..7c4b56d46ddc72 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/string-width/node_modules/strip-ansi/readme.md @@ -0,0 +1,46 @@ +# strip-ansi [![Build Status](https://travis-ci.org/chalk/strip-ansi.svg?branch=master)](https://travis-ci.org/chalk/strip-ansi) + +> Strip [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code) from a string + + +## Install + +``` +$ npm install strip-ansi +``` + + +## Usage + +```js +const stripAnsi = require('strip-ansi'); + +stripAnsi('\u001B[4mUnicorn\u001B[0m'); +//=> 'Unicorn' + +stripAnsi('\u001B]8;;https://github.com\u0007Click\u001B]8;;\u0007'); +//=> 'Click' +``` + + +## strip-ansi for enterprise + +Available as part of the Tidelift Subscription. + +The maintainers of strip-ansi and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-strip-ansi?utm_source=npm-strip-ansi&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) + + +## Related + +- [strip-ansi-cli](https://github.com/chalk/strip-ansi-cli) - CLI for this module +- [strip-ansi-stream](https://github.com/chalk/strip-ansi-stream) - Streaming version of this module +- [has-ansi](https://github.com/chalk/has-ansi) - Check if a string has ANSI escape codes +- [ansi-regex](https://github.com/chalk/ansi-regex) - Regular expression for matching ANSI escape codes +- [chalk](https://github.com/chalk/chalk) - Terminal string styling done right + + +## Maintainers + +- [Sindre Sorhus](https://github.com/sindresorhus) +- [Josh Junon](https://github.com/qix-) + diff --git a/tools/node_modules/eslint/node_modules/string-width/package.json b/tools/node_modules/eslint/node_modules/string-width/package.json index 9ef5253e54d49b..38993c1e2e336d 100644 --- a/tools/node_modules/eslint/node_modules/string-width/package.json +++ b/tools/node_modules/eslint/node_modules/string-width/package.json @@ -11,7 +11,7 @@ "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^5.2.0" + "strip-ansi": "^6.0.0" }, "deprecated": false, "description": "Get the visual width of a string - the number of columns required to display it", @@ -61,5 +61,5 @@ "scripts": { "test": "xo && ava && tsd" }, - "version": "4.1.0" + "version": "4.2.0" } \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/string-width/readme.md b/tools/node_modules/eslint/node_modules/string-width/readme.md index 35a0c0377a900d..705f206001b777 100644 --- a/tools/node_modules/eslint/node_modules/string-width/readme.md +++ b/tools/node_modules/eslint/node_modules/string-width/readme.md @@ -37,6 +37,14 @@ stringWidth('\u001B[1m古\u001B[22m'); - [widest-line](https://github.com/sindresorhus/widest-line) - Get the visual width of the widest line in a string -## License - -MIT © [Sindre Sorhus](https://sindresorhus.com) +--- + +
+ + Get professional support for this package with a Tidelift subscription + +
+ + Tidelift helps make open source sustainable for maintainers while giving companies
assurances about security, maintenance, and licensing for their dependencies. +
+
diff --git a/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/index.js b/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/index.js new file mode 100644 index 00000000000000..c25448009f304d --- /dev/null +++ b/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/index.js @@ -0,0 +1,14 @@ +'use strict'; + +module.exports = options => { + options = Object.assign({ + onlyFirst: false + }, options); + + const pattern = [ + '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)', + '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))' + ].join('|'); + + return new RegExp(pattern, options.onlyFirst ? undefined : 'g'); +}; diff --git a/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/license b/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/license new file mode 100644 index 00000000000000..e7af2f77107d73 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/license @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/package.json b/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/package.json new file mode 100644 index 00000000000000..db8f3cc8c7d14e --- /dev/null +++ b/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/package.json @@ -0,0 +1,62 @@ +{ + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/chalk/ansi-regex/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "Regular expression for matching ANSI escape codes", + "devDependencies": { + "ava": "^0.25.0", + "xo": "^0.23.0" + }, + "engines": { + "node": ">=6" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/chalk/ansi-regex#readme", + "keywords": [ + "ansi", + "styles", + "color", + "colour", + "colors", + "terminal", + "console", + "cli", + "string", + "tty", + "escape", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "command-line", + "text", + "regex", + "regexp", + "re", + "match", + "test", + "find", + "pattern" + ], + "license": "MIT", + "name": "ansi-regex", + "repository": { + "type": "git", + "url": "git+https://github.com/chalk/ansi-regex.git" + }, + "scripts": { + "test": "xo && ava", + "view-supported": "node fixtures/view-codes.js" + }, + "version": "4.1.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/readme.md b/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/readme.md new file mode 100644 index 00000000000000..d19c44667e704b --- /dev/null +++ b/tools/node_modules/eslint/node_modules/strip-ansi/node_modules/ansi-regex/readme.md @@ -0,0 +1,87 @@ +# ansi-regex [![Build Status](https://travis-ci.org/chalk/ansi-regex.svg?branch=master)](https://travis-ci.org/chalk/ansi-regex) + +> Regular expression for matching [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code) + +--- + +
+ + Get professional support for this package with a Tidelift subscription + +
+ + Tidelift helps make open source sustainable for maintainers while giving companies
assurances about security, maintenance, and licensing for their dependencies. +
+
+ +--- + + +## Install + +``` +$ npm install ansi-regex +``` + + +## Usage + +```js +const ansiRegex = require('ansi-regex'); + +ansiRegex().test('\u001B[4mcake\u001B[0m'); +//=> true + +ansiRegex().test('cake'); +//=> false + +'\u001B[4mcake\u001B[0m'.match(ansiRegex()); +//=> ['\u001B[4m', '\u001B[0m'] + +'\u001B[4mcake\u001B[0m'.match(ansiRegex({onlyFirst: true})); +//=> ['\u001B[4m'] + +'\u001B]8;;https://github.com\u0007click\u001B]8;;\u0007'.match(ansiRegex()); +//=> ['\u001B]8;;https://github.com\u0007', '\u001B]8;;\u0007'] +``` + + +## API + +### ansiRegex([options]) + +Returns a regex for matching ANSI escape codes. + +#### options + +##### onlyFirst + +Type: `boolean`
+Default: `false` *(Matches any ANSI escape codes in a string)* + +Match only the first ANSI escape. + + +## FAQ + +### Why do you test for codes not in the ECMA 48 standard? + +Some of the codes we run as a test are codes that we acquired finding various lists of non-standard or manufacturer specific codes. We test for both standard and non-standard codes, as most of them follow the same or similar format and can be safely matched in strings without the risk of removing actual string content. There are a few non-standard control codes that do not follow the traditional format (i.e. they end in numbers) thus forcing us to exclude them from the test because we cannot reliably match them. + +On the historical side, those ECMA standards were established in the early 90's whereas the VT100, for example, was designed in the mid/late 70's. At that point in time, control codes were still pretty ungoverned and engineers used them for a multitude of things, namely to activate hardware ports that may have been proprietary. Somewhere else you see a similar 'anarchy' of codes is in the x86 architecture for processors; there are a ton of "interrupts" that can mean different things on certain brands of processors, most of which have been phased out. + + +## Security + +To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure. + + +## Maintainers + +- [Sindre Sorhus](https://github.com/sindresorhus) +- [Josh Junon](https://github.com/qix-) + + +## License + +MIT diff --git a/tools/node_modules/eslint/node_modules/type-fest/package.json b/tools/node_modules/eslint/node_modules/type-fest/package.json index a5a398048dc370..0164025631ef29 100644 --- a/tools/node_modules/eslint/node_modules/type-fest/package.json +++ b/tools/node_modules/eslint/node_modules/type-fest/package.json @@ -11,14 +11,15 @@ "deprecated": false, "description": "A collection of essential TypeScript types", "devDependencies": { - "@sindresorhus/tsconfig": "^0.3.0", - "@typescript-eslint/eslint-plugin": "^1.8.0", - "eslint-config-xo-typescript": "^0.11.0", + "@sindresorhus/tsconfig": "^0.4.0", + "@typescript-eslint/eslint-plugin": "^2.2.0", + "@typescript-eslint/parser": "^2.2.0", + "eslint-config-xo-typescript": "^0.18.0", "tsd": "^0.7.3", "xo": "^0.24.0" }, "engines": { - "node": ">=6" + "node": ">=8" }, "files": [ "index.d.ts", @@ -45,7 +46,7 @@ "scripts": { "test": "xo && tsd" }, - "version": "0.5.2", + "version": "0.8.1", "xo": { "extends": "xo-typescript", "extensions": [ diff --git a/tools/node_modules/eslint/node_modules/type-fest/readme.md b/tools/node_modules/eslint/node_modules/type-fest/readme.md index 3f11c3c1edb6c3..1824bdabedecaa 100644 --- a/tools/node_modules/eslint/node_modules/type-fest/readme.md +++ b/tools/node_modules/eslint/node_modules/type-fest/readme.md @@ -36,14 +36,14 @@ $ npm install type-fest ## Usage ```ts -import {Omit} from 'type-fest'; +import {Except} from 'type-fest'; type Foo = { unicorn: string; rainbow: boolean; }; -type FooWithoutRainbow = Omit; +type FooWithoutRainbow = Except; //=> {unicorn: string} ``` @@ -64,13 +64,19 @@ Click the type names for complete docs. ### Utilities -- [`Omit`](source/omit.d.ts) - Create a type from an object type without certain keys. -- [`Mutable`](source/mutable.d.ts) - Convert an object with `readonly` properties into a mutable object. Inverse of `Readonly`. +- [`Except`](source/except.d.ts) - Create a type from an object type without certain keys. This is a stricter version of [`Omit`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-5.html#the-omit-helper-type). +- [`Mutable`](source/mutable.d.ts) - Convert an object with `readonly` keys into a mutable object. The inverse of `Readonly`. - [`Merge`](source/merge.d.ts) - Merge two types into a new type. Keys of the second type overrides keys of the first type. -- [`MergeExclusive`](source/merge-exclusive.d.ts) - Create a type that has mutually exclusive properties. -- [`RequireAtLeastOne`](source/require-at-least-one.d.ts) - Create a type that requires at least one of the given properties. -- [`ReadonlyDeep`](source/readonly-deep.d.ts) - Create a deeply immutable version of a `object`/`Map`/`Set`/`Array` type. +- [`MergeExclusive`](source/merge-exclusive.d.ts) - Create a type that has mutually exclusive keys. +- [`RequireAtLeastOne`](source/require-at-least-one.d.ts) - Create a type that requires at least one of the given keys. +- [`RequireExactlyOne`](source/require-one.d.ts) - Create a type that requires exactly a single key of the given keys and disallows more. +- [`PartialDeep`](source/partial-deep.d.ts) - Create a deeply optional version of another type. Use [`Partial`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1401-L1406) if you only need one level deep. +- [`ReadonlyDeep`](source/readonly-deep.d.ts) - Create a deeply immutable version of an `object`/`Map`/`Set`/`Array` type. Use [`Readonly`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1415-L1420) if you only need one level deep. - [`LiteralUnion`](source/literal-union.d.ts) - Create a union type by combining primitive types and literal types without sacrificing auto-completion in IDEs for the literal type part of the union. Workaround for [Microsoft/TypeScript#29729](https://github.com/Microsoft/TypeScript/issues/29729). +- [`Promisable`](source/promisable.d.ts) - Create a type that represents either the value or the value wrapped in `PromiseLike`. +- [`Opaque`](source/opaque.d.ts) - Create an [opaque type](https://codemix.com/opaque-types-in-javascript/). +- [`SetOptional`](source/set-optional.d.ts) - Create a type that makes the given keys optional. +- [`SetRequired`](source/set-required.d.ts) - Create a type that makes the given keys required. ### Miscellaneous @@ -82,6 +88,7 @@ Click the type names for complete docs. *If we decline a type addition, we will make sure to document the better solution here.* - [`Diff` and `Spread`](https://github.com/sindresorhus/type-fest/pull/7) - The PR author didn't provide any real-world use-cases and the PR went stale. If you think this type is useful, provide some real-world use-cases and we might reconsider. +- [`Dictionary`](https://github.com/sindresorhus/type-fest/issues/33) - You only save a few characters (`Dictionary` vs `Record`) from [`Record`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1429-L1434), which is more flexible and well-known. Also, you shouldn't use an object as a dictionary. We have `Map` in JavaScript now. ## Tips @@ -91,17 +98,514 @@ Click the type names for complete docs. There are many advanced types most users don't know about. - [`Partial`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1401-L1406) - Make all properties in `T` optional. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/KYOwrgtgBAMg9gcxsAbsANlA3gKClAeQDMiAaPKAEWACMwFz8BRAJxbhcagDEBDAF17ocAXxw4AliH7AWRXgGNgUAHJwAJsADCcEEQkJsFXgAcTK3hGAAuKAGd+LKQgDcFEx363wEGrLf46IjIaOi28EioGG5iOArovHZ2qhrAAIJmAEJgEuiaLEb4Jk4oAsoKuvoIYCwCErq2apo6egZQALyF+FCm5pY2UABETelmg1xFnrYAzAAM8xNQQZGh4cFR6AB0xEQUIm4UFa0IABRHVbYACrws-BJCADwjLVUAfACUXfhEHFBnug4oABrYAATygcCIhBoACtgAp+JsQaC7P9ju9Prhut0joCwCZ1GUAGpCMDKTrnAwAbWRPWSyMhKWalQMAF0Dtj8BIoSd8YSZCT0GSOu1OmAQJp9CBgOpPkc7uBgBzOfwABYSOybSnVWp3XQ0sF04FgxnPFkIVkdKB84mkpUUfCxbEsYD8GogKBqjUBKBiWIAen9UGut3u6CeqReBlePXQQQA7skwMl+HAoMU4CgJJoISB0ODeOmbvwIVC1cAcIGmdpzVApDI5IpgJscNL49WMiZsrl8id3lrzScsD0zBYrLZBgAVOCUOCdwa+95uIA) + + ```ts + interface NodeConfig { + appName: string; + port: number; + } + + class NodeAppBuilder { + private configuration: NodeConfig = { + appName: 'NodeApp', + port: 3000 + }; + + config(config: Partial) { + type NodeConfigKey = keyof NodeConfig; + + for (const key of Object.keys(config) as NodeConfigKey[]) { + const updateValue = config[key]; + + if (updateValue === undefined) { + continue; + } + + this.configuration[key] = updateValue; + } + + return this; + } + } + + // `Partial`` allows us to provide only a part of the + // NodeConfig interface. + new NodeAppBuilder().config({appName: 'ToDoApp'}); + ``` +
+ - [`Required`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1408-L1413) - Make all properties in `T` required. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/AQ4SwOwFwUwJwGYEMDGNgGED21VQGJZwC2wA3gFCjXAzFJgA2A-AFzADOUckA5gNxUaIYjA4ckvGG07c+g6gF8KQkAgCuEFFDA5O6gEbEwUbLm2ESwABQIixACJIoSdgCUYAR3Vg4MACYAPGYuFvYAfACU5Ko0APRxwADKMBD+wFAAFuh2Vv7OSBlYGdmc8ABu8LHKsRyGxqY4oQT21pTCIHQMjOwA5DAAHgACxAAOjDAAdChYxL0ANLHUouKSMH0AEmAAhJhY6ozpAJ77GTCMjMCiV0ToSAb7UJPPC9WRgrEJwAAqR6MwSRQPFGUFocDgRHYxnEfGAowh-zgUCOwF6KwkUl6tXqJhCeEsxDaS1AXSYfUGI3GUxmc0WSneQA) + + ```ts + interface ContactForm { + email?: string; + message?: string; + } + + function submitContactForm(formData: Required) { + // Send the form data to the server. + } + + submitContactForm({ + email: 'ex@mple.com', + message: 'Hi! Could you tell me more about…', + }); + + // TypeScript error: missing property 'message' + submitContactForm({ + email: 'ex@mple.com', + }); + ``` +
+ - [`Readonly`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1415-L1420) - Make all properties in `T` readonly. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/AQ4UwOwVwW2AZA9gc3mAbmANsA3gKFCOAHkAzMgGkOJABEwAjKZa2kAUQCcvEu32AMQCGAF2FYBIAL4BufDRABLCKLBcywgMZgEKZOoDCiCGSXI8i4hGEwwALmABnUVxXJ57YFgzZHSVF8sT1BpBSItLGEnJz1kAy5LLy0TM2RHACUwYQATEywATwAeAITjU3MAPnkrCJMXLigtUT4AClxgGztKbyDgaX99I1TzAEokr1BRAAslJwA6FIqLAF48TtswHp9MHDla9hJGACswZvmyLjAwAC8wVpm5xZHkUZDaMKIwqyWXYCW0oN4sNlsA1h0ug5gAByACyBQAggAHJHQ7ZBIFoXbzBjMCz7OoQP5YIaJNYQMAAdziCVaALGNSIAHomcAACoFJFgADKWjcSNEwG4vC4ji0wggEEQguiTnMEGALWAV1yAFp8gVgEjeFyuKICvMrCTgVxnst5jtsGC4ljsPNhXxGaAWcAAOq6YRXYDCRg+RWIcA5JSC+kWdCepQ+v3RYCU3RInzRMCGwlpC19NYBW1Ye08R1AA) + + ```ts + enum LogLevel { + Off, + Debug, + Error, + Fatal + }; + + interface LoggerConfig { + name: string; + level: LogLevel; + } + + class Logger { + config: Readonly; + + constructor({name, level}: LoggerConfig) { + this.config = {name, level}; + Object.freeze(this.config); + } + } + + const config: LoggerConfig = { + name: 'MyApp', + level: LogLevel.Debug + }; + + const logger = new Logger(config); + + // TypeScript Error: cannot assign to read-only property. + logger.config.level = LogLevel.Error; + + // We are able to edit config variable as we please. + config.level = LogLevel.Error; + ``` +
+ - [`Pick`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1422-L1427) - From `T`, pick a set of properties whose keys are in the union `K`. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/AQ4SwOwFwUwJwGYEMDGNgEE5TCgNugN4BQoZwOUBAXMAM5RyQDmA3KeSFABYCuAtgCMISMHloMmENh04oA9tBjQJjFuzIBfYrOAB6PcADCcGElh1gEGAHcKATwAO6ebyjB5CTNlwFwSxFR0BX5HeToYABNgBDh5fm8cfBg6AHIKG3ldA2BHOOcfFNpUygJ0pAhokr4hETFUgDpswywkggAFUwA3MFtgAF5gQgowKhhVKTYKGuFRcXo1aVZgbTIoJ3RW3xhOmB6+wfbcAGsAHi3kgBpgEtGy4AAfG54BWfqAPnZm4AAlZUj4MAkMA8GAGB4vEgfMlLLw6CwPBA8PYRmMgZVgAC6CgmI4cIommQELwICh8RBgKZKvALh1ur0bHQABR5PYMui0Wk7em2ADaAF0AJS0AASABUALIAGQAogR+Mp3CROCAFBBwVC2ikBpj5CgBIqGjizLA5TAFdAmalImAuqlBRoVQh5HBgEy1eDWfs7J5cjzGYKhroVfpDEhHM4MV6GRR5NN0JrtnRg6BVirTFBeHAKYmYY6QNpdB73LmCJZBlSAXAubtvczeSmQMNSuMbmKNgBlHFgPEUNwusBIPAAQlS1xetTmxT0SDoESgdD0C4aACtHMwxytLrohawgA) + + ```ts + interface Article { + title: string; + thumbnail: string; + content: string; + } + + // Creates new type out of the `Article` interface composed + // from the Articles' two properties: `title` and `thumbnail`. + // `ArticlePreview = {title: string; thumbnail: string}` + type ArticlePreview = Pick; + + // Render a list of articles using only title and description. + function renderArticlePreviews(previews: ArticlePreview[]): HTMLElement { + const articles = document.createElement('div'); + + for (const preview of previews) { + // Append preview to the articles. + } + + return articles; + } + + const articles = renderArticlePreviews([ + { + title: 'TypeScript tutorial!', + thumbnail: '/assets/ts.jpg' + } + ]); + ``` +
+ - [`Record`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1429-L1434) - Construct a type with a set of properties `K` of type `T`. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/AQ4ejYAUHsGcCWAXBMB2dgwGbAKYC2ADgDYwCeeemCaWArgE7ADGMxAhmuQHQBQoYEnJE8wALKEARnkaxEKdMAC8wAOS0kstGuAAfdQBM8ANzxlRjXQbVaWACwC0JPB0NqA3HwGgIwAJJoWozYHCxixnAsjAhStADmwESMMJYo1Fi4HMCIaPEu+MRklHj8gpqyoeHAAKJFFFTAAN4+giDYCIxwSAByHAR4AFw5SDF5Xm2gJBzdfQPD3WPxE5PAlBxdAPLYNQAelgh4aOHDaPQEMowrIAC+3oJ+AMKMrlrAXFhSAFZ4LEhC9g4-0BmA4JBISXgiCkBQABpILrJ5MhUGhYcATGD6Bk4Hh-jNgABrPDkOBlXyQAAq9ngYmJpOAAHcEOCRjAXqwYODfoo6DhakUSph+Uh7GI4P0xER4Cj0OSQGwMP8tP1hgAlX7swwAHgRl2RvIANALSA08ABtAC6AD4VM1Wm0Kow0MMrYaHYJjGYLLJXZb3at1HYnC43Go-QHQDcvA6-JsmEJXARgCDgMYWAhjIYhDAU+YiMAAFIwex0ZmilMITCGF79TLAGRsAgJYAAZRwSEZGzEABFTOZUrJ5Yn+jwnWgeER6HB7AAKJrADpdXqS4ZqYultTG6azVfqHswPBbtauLY7fayQ7HIbAAAMwBuAEoYw9IBq2Ixs9h2eFMOQYPQObALQKJgggABeYhghCIpikkKRpOQRIknAsZUiIeCttECBEP8NSMCkjDDAARMGziuIYxHwYOjDCMBmDNnAuTxA6irdCOBB1Lh5Dqpqn66tISIykawBnOCtqqC0gbjqc9DgpGkxegOliyfJDrRkAA) + + ```ts + // Positions of employees in our company. + type MemberPosition = 'intern' | 'developer' | 'tech-lead'; + + // Interface describing properties of a single employee. + interface Employee { + firstName: string; + lastName: string; + yearsOfExperience: number; + } + + // Create an object that has all possible `MemberPosition` values set as keys. + // Those keys will store a collection of Employees of the same position. + const team: Record = { + intern: [], + developer: [], + 'tech-lead': [], + }; + + // Our team has decided to help John with his dream of becoming Software Developer. + team.intern.push({ + firstName: 'John', + lastName: 'Doe', + yearsOfExperience: 0 + }); + + // `Record` forces you to initialize all of the property keys. + // TypeScript Error: "tech-lead" property is missing + const teamEmpty: Record = { + intern: null, + developer: null, + }; + ``` +
+ - [`Exclude`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1436-L1439) - Exclude from `T` those types that are assignable to `U`. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/JYOwLgpgTgZghgYwgAgMrQG7QMIHsQzADmyA3gFDLIAOuUYAXMiAK4A2byAPsgM5hRQJHqwC2AI2gBucgF9y5MAE9qKAEoQAjiwj8AEnBAATNtGQBeZAAooWphu26wAGmS3e93bRC8IASgsAPmRDJRlyAHoI5ABRAA8ENhYjFFYOZGVVZBgoXFFkAAM0zh5+QRBhZhYJaAKAOkjogEkQZAQ4X2QAdwALCFbaemRgXmQtFjhOMFwq9K6ULuB0lk6U+HYwZAxJnQaYFhAEMGB8ZCIIMAAFOjAANR2IK0HGWISklIAedCgsKDwCYgAbQA5M9gQBdVzFQJ+JhiSRQMiUYYwayZCC4VHPCzmSzAspCYEBWxgFhQAZwKC+FpgJ43VwARgADH4ZFQSWSBjcZPJyPtDsdTvxKWBvr8rD1DCZoJ5HPopaYoK4EPhCEQmGKcKriLCtrhgEYkVQVT5Nr4fmZLLZtMBbFZgT0wGBqES6ghbHBIJqoBKFdBWQpjfh+DQbhY2tqiHVsbjLMVkAB+ZAAZiZaeQTHOVxu9ySjxNaujNwDVHNvzqbBGkBAdPoAfkQA) + + ```ts + interface ServerConfig { + port: null | string | number; + } + + type RequestHandler = (request: Request, response: Response) => void; + + // Exclude `null` type from `null | string | number`. + // In case the port is equal to `null`, we will use default value. + function getPortValue(port: Exclude): number { + if (typeof port === 'string') { + return parseInt(port, 10); + } + + return port; + } + + function startServer(handler: RequestHandler, config: ServerConfig): void { + const server = require('http').createServer(handler); + + const port = config.port === null ? 3000 : getPortValue(config.port); + server.listen(port); + } + ``` +
+ - [`Extract`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1441-L1444) - Extract from `T` those types that are assignable to `U`. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/CYUwxgNghgTiAEAzArgOzAFwJYHtXzSwEdkQBJYACgEoAueVZAWwCMQYBuAKDDwGcM8MgBF4AXngBlAJ6scESgHIRi6ty5ZUGdoihgEABXZ888AN5d48ANoiAuvUat23K6ihMQ9ATE0BzV3goPy8GZjZOLgBfLi4Aejj4AEEICBwAdz54MAALKFQQ+BxEeAAHY1NgKAwoIKy0grr4DByEUpgccpgMaXgAaxBerCzi+B9-ZulygDouFHRsU1z8kKMYE1RhaqgAHkt4AHkWACt4EAAPbVRgLLWNgBp9gGlBs8uQa6yAUUuYPQwdgNpKM7nh7mMML4CgA+R5WABqUAgpDeVxuhxO1he0jsXGh8EoOBO9COx3BQPo2PBADckaR6IjkSA6PBqTgsMBzPsicdrEC7OJWXSQNwYvFEgAVTS9JLXODpeDpKBZFg4GCoWa8VACIJykAKiQWKy2YQOAioYikCg0OEMDyhRSy4DyxS24KhAAMjyi6gS8AAwjh5OD0iBFHAkJoEOksC1mnkMJq8gUQKDNttKPlnfrwYp3J5XfBHXqoKpfYkAOI4ansTxaeDADmoRSCCBYAbxhC6TDx6rwYHIRX5bScjA4bLJwoDmDwDkfbA9JMrVMVdM1TN69LgkTgwgkchUahqIA) + + ```ts + declare function uniqueId(): number; + + const ID = Symbol('ID'); + + interface Person { + [ID]: number; + name: string; + age: number; + } + + // Allows changing the person data as long as the property key is of string type. + function changePersonData< + Obj extends Person, + Key extends Extract, + Value extends Obj[Key] + > (obj: Obj, key: Key, value: Value): void { + obj[key] = value; + } + + // Tiny Andrew was born. + const andrew = { + [ID]: uniqueId(), + name: 'Andrew', + age: 0, + }; + + // Cool, we're fine with that. + changePersonData(andrew, 'name', 'Pony'); + + // Goverment didn't like the fact that you wanted to change your identity. + changePersonData(andrew, ID, uniqueId()); + ``` +
+ - [`NonNullable`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1446-L1449) - Exclude `null` and `undefined` from `T`. +
+ + Example + + Works with strictNullChecks set to true. (Read more here) + + [Playground](https://typescript-play.js.org/?target=6#code/C4TwDgpgBACg9gJ2AOQK4FsBGEFQLxQDOwCAlgHYDmUAPlORtrnQwDasDcAUFwPQBU-WAEMkUOADMowqAGNWwwoSgATCBIqlgpOOSjAAFsOBRSy1IQgr9cKJlSlW1mZYQA3HFH68u8xcoBlHA8EACEHJ08Aby4oKDBUTFZSWXjEFEYcAEIALihkXTR2YSSIAB54JDQsHAA+blj4xOTUsHSACkMzPKD3HHDHNQQAGjSkPMqMmoQASh7g-oihqBi4uNIpdraxPAI2VhmVxrX9AzMAOm2ppnwoAA4ABifuE4BfKAhWSyOTuK7CS7pao3AhXF5rV48E4ICDAVAIPT-cGQyG+XTEIgLMJLTx7CAAdygvRCA0iCHaMwarhJOIQjUBSHaACJHk8mYdeLwxtdcVAAOSsh58+lXdr7Dlcq7A3n3J4PEUdADMcspUE53OluAIUGVTx46oAKuAIAFZGQwCYAKIIBCILjUxaDHAMnla+iodjcIA) + + ```ts + type PortNumber = string | number | null; + + /** Part of a class definition that is used to build a server */ + class ServerBuilder { + portNumber!: NonNullable; + + port(this: ServerBuilder, port: PortNumber): ServerBuilder { + if (port == null) { + this.portNumber = 8000; + } else { + this.portNumber = port; + } + + return this; + } + } + + const serverBuilder = new ServerBuilder(); + + serverBuilder + .port('8000') // portNumber = '8000' + .port(null) // portNumber = 8000 + .port(3000); // portNumber = 3000 + + // TypeScript error + serverBuilder.portNumber = null; + ``` +
+ - [`Parameters`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1451-L1454) - Obtain the parameters of a function type in a tuple. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/GYVwdgxgLglg9mABAZwBYmMANgUwBQxgAOIUAXIgIZgCeA2gLoCUFAbnDACaIDeAUIkQB6IYgCypSlBxUATrMo1ECsJzgBbLEoipqAc0J7EMKMgDkiHLnU4wp46pwAPHMgB0fAL58+oSLARECEosLAA5ABUYG2QAHgAxJGdpVWREPDdMylk9ZApqemZEAF4APipacrw-CApEgBogkKwAYThwckQwEHUAIxxZJl4BYVEImiIZKF0oZRwiWVdbeygJmThgOYgcGFYcbhqApCJsyhtpWXcR1cnEePBoeDAABVPzgbTixFeFd8uEsClADcIxGiygIFkSEOT3SmTc2VydQeRx+ZxwF2QQ34gkEwDgsnSuFmMBKiAADEDjIhYk1Qm0OlSYABqZnYka4xA1DJZHJYkGc7yCbyeRA+CAIZCzNAYbA4CIAdxg2zJwVCkWirjwMswuEaACYmCCgA) + + ```ts + function shuffle(input: any[]): void { + // Mutate array randomly changing its' elements indexes. + } + + function callNTimes any> (func: Fn, callCount: number) { + // Type that represents the type of the received function parameters. + type FunctionParameters = Parameters; + + return function (...args: FunctionParameters) { + for (let i = 0; i < callCount; i++) { + func(...args); + } + } + } + + const shuffleTwice = callNTimes(shuffle, 2); + ``` +
+ - [`ConstructorParameters`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1456-L1459) - Obtain the parameters of a constructor function type in a tuple. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/MYGwhgzhAECCBOAXAlqApgWQPYBM0mgG8AoaaFRENALmgkXmQDsBzAblOmCycTV4D8teo1YdO3JiICuwRFngAKClWENmLAJRFOZRAAtkEAHQq00ALzlklNBzIBfYk+KhIMAJJTEYJsDQAwmDA+mgAPAAq0GgAHnxMODCKTGgA7tCKxllg8CwQtL4AngDaALraFgB80EWa1SRkAA6MAG5gfNAB4FABPDJyCrQR9tDNyG0dwMGhtBhgjWEiGgA00F70vv4RhY3hEZXVVinpc42KmuJkkv3y8Bly8EPaDWTkhiZd7r3e8LK3llwGCMXGQWGhEOsfH5zJlsrl8p0+gw-goAAo5MAAW3BaHgEEilU0tEhmzQ212BJ0ry4SOg+kg+gBBiMximIGA0nAfAQLGk2N4EAAEgzYcYcnkLsRdDTvNEYkYUKwSdCme9WdM0MYwYhFPSIPpJdTkAAzDKxBUaZX+aAAQgsVmkCTQxuYaBw2ng4Ok8CYcotSu8pMur09iG9vuObxZnx6SN+AyUWTF8MN0CcZE4Ywm5jZHK5aB5fP4iCFIqT4oRRTKRLo6lYVNeAHpG50wOzOe1zHr9NLQ+HoABybsD4HOKXXRA1JCoKhBELmI5pNaB6Fz0KKBAodDYPAgSUTmqYsAALx4m5nC6nW9nGq14KtaEUA9gR9PvuNCjQ9BgACNvcwNBtAcLiAA) + + ```ts + class ArticleModel { + title: string; + content?: string; + + constructor(title: string) { + this.title = title; + } + } + + class InstanceCache any)> { + private ClassConstructor: T; + private cache: Map> = new Map(); + + constructor (ctr: T) { + this.ClassConstructor = ctr; + } + + getInstance (...args: ConstructorParameters): InstanceType { + const hash = this.calculateArgumentsHash(...args); + + const existingInstance = this.cache.get(hash); + if (existingInstance !== undefined) { + return existingInstance; + } + + return new this.ClassConstructor(...args); + } + + private calculateArgumentsHash(...args: any[]): string { + // Calculate hash. + return 'hash'; + } + } + + const articleCache = new InstanceCache(ArticleModel); + const amazonArticle = articleCache.getInstance('Amazon forests burining!'); + ``` +
+ - [`ReturnType`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1461-L1464) – Obtain the return type of a function type. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/MYGwhgzhAECSAmICmBlJAnAbgS2E6A3gFDTTwD2AcuQC4AW2AdgOYAUAlAFzSbnbyEAvkWFFQkGJSQB3GMVI1sNZNwg10TZgG4S0YOUY0kh1es07d+xmvQBXYDXLpWi5UlMaWAGj0GjJ6BtNdkJdBQYIADpXZGgAXmgYpB1ScOwoq38aeN9DYxoU6GFRKzVoJjUwRjwAYXJbPPRuAFkwAAcAHgAxBodsAx9GWwBbACMMAD4cxhloVraOCyYjdAAzMDxoOut1e0d0UNIZ6WhWSPOwdGYIbiqATwBtAF0uaHudUQB6ACpv6ABpJBINqJdAbADW0Do5BOw3u5R2VTwMHIq2gAANtjZ0bkbHsnFCwJh8ONjHp0EgwEZ4JFoN9PkRVr1FAZoMwkDRYIjqkgOrosepoEgAB7+eAwAV2BxOLy6ACCVxgIrFEoMeOl6AACpcwMMORgIB1JRMiBNWKVdhruJKfOdIpdrtwFddXlzKjyACp3Nq842HaDIbL6BrZBIVGhIpB1EMYSLsmjmtWW-YhAA+qegAAYLKQLQj3ZsEsdccmnGcLor2Dn8xGedHGpEIBzEzspfsfMHDNAANTQACMVaIljV5GQkRA5DYmIpVKQAgAJARO9le33BDXIyi0YuLW2nJFGLqkOvxFB0YPdBSaLZ0IwNzyPkO8-xkGgsLh8Al427a3hWAhXwwHA8EHT5PmgAB1bAQBAANJ24adKWpft72RaBUTgRBUCAj89HAM8xCTaBjggABRQx0DuHJv25P9dCkWRZVIAAiBjoFImpmjlFBgA0NpsjadByDacgIDAEAIAAQmYpjoGYgAZSBsmGPw6DtZiiFA8CoJguDmAQmoZ2QvtUKQLdoAYmBTwgdEiCAA) + + ```ts + /** Provides every element of the iterable `iter` into the `callback` function and stores the results in an array. */ + function mapIter< + Elem, + Func extends (elem: Elem) => any, + Ret extends ReturnType + >(iter: Iterable, callback: Func): Ret[] { + const mapped: Ret[] = []; + + for (const elem of iter) { + mapped.push(callback(elem)); + } + + return mapped; + } + + const setObject: Set = new Set(); + const mapObject: Map = new Map(); + + mapIter(setObject, (value: string) => value.indexOf('Foo')); // number[] + + mapIter(mapObject, ([key, value]: [number, string]) => { + return key % 2 === 0 ? value : 'Odd'; + }); // string[] + ``` +
+ - [`InstanceType`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1466-L1469) – Obtain the instance type of a constructor function type. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/MYGwhgzhAECSAmICmBlJAnAbgS2E6A3gFDTTwD2AcuQC4AW2AdgOYAUAlAFzSbnbyEAvkWFFQkGJSQB3GMVI1sNZNwg10TZgG4S0YOUY0kh1es07d+xmvQBXYDXLpWi5UlMaWAGj0GjJ6BtNdkJdBQYIADpXZGgAXmgYpB1ScOwoq38aeN9DYxoU6GFRKzVoJjUwRjwAYXJbPPRuAFkwAAcAHgAxBodsAx9GWwBbACMMAD4cxhloVraOCyYjdAAzMDxoOut1e0d0UNIZ6WhWSPOwdGYIbiqATwBtAF0uaHudUQB6ACpv6ABpJBINqJdAbADW0Do5BOw3u5R2VTwMHIq2gAANtjZ0bkbHsnFCwJh8ONjHp0EgwEZ4JFoN9PkRVr1FAZoMwkDRYIjqkgOrosepoEgAB7+eAwAV2BxOLy6ACCVxgIrFEoMeOl6AACpcwMMORgIB1JRMiBNWKVdhruJKfOdIpdrtwFddXlzKjyACp3Nq842HaDIbL6BrZBIVGhIpB1EMYSLsmjmtWW-YhAA+qegAAYLKQLQj3ZsEsdccmnGcLor2Dn8xGedHGpEIBzEzspfsfMHDNAANTQACMVaIljV5GQkRA5DYmIpVKQAgAJARO9le33BDXIyi0YuLW2nJFGLqkOvxFB0YPdBSaLZ0IwNzyPkO8-xkGgsLh8Al427a3hWAhXwwHA8EHT5PmgAB1bAQBAANJ24adKWpft72RaBUTgRBUCAj89HAM8xCTaBjggABRQx0DuHJv25P9dCkWRZVIAAiBjoFImpmjlFBgA0NpsjadByDacgIDAEAIAAQmYpjoGYgAZSBsmGPw6DtZiiFA8CoJguDmAQmoZ2QvtUKQLdoAYmBTwgdEiCAA) + + ```ts + class IdleService { + doNothing (): void {} + } + + class News { + title: string; + content: string; + + constructor(title: string, content: string) { + this.title = title; + this.content = content; + } + } + + const instanceCounter: Map = new Map(); + + interface Constructor { + new(...args: any[]): any; + } + + // Keep track how many instances of `Constr` constructor have been created. + function getInstance< + Constr extends Constructor, + Args extends ConstructorParameters + >(constructor: Constr, ...args: Args): InstanceType { + let count = instanceCounter.get(constructor) || 0; + + const instance = new constructor(...args); + + instanceCounter.set(constructor, count + 1); + + console.log(`Created ${count + 1} instances of ${Constr.name} class`); + + return instance; + } + + + const idleService = getInstance(IdleService); + // Will log: `Created 1 instances of IdleService class` + const newsEntry = getInstance(News, 'New ECMAScript proposals!', 'Last month...'); + // Will log: `Created 1 instances of News class` + ``` +
+ +- [`Omit`](https://github.com/microsoft/TypeScript/blob/71af02f7459dc812e85ac31365bfe23daf14b4e4/src/lib/es5.d.ts#L1446) – Constructs a type by picking all properties from T and then removing K. +
+ + Example + + + [Playground](https://typescript-play.js.org/?target=6#code/JYOwLgpgTgZghgYwgAgIImAWzgG2QbwChlks4BzCAVShwC5kBnMKUcgbmKYAcIFgIjBs1YgOXMpSFMWbANoBdTiW5woFddwAW0kfKWEAvoUIB6U8gDCUCHEiNkICAHdkYAJ69kz4GC3JcPG4oAHteKDABBxCYNAxsPFBIWEQUCAAPJG4wZABySUFcgJAAEzMLXNV1ck0dIuCw6EjBADpy5AB1FAQ4EGQAV0YUP2AHDy8wEOQbUugmBLwtEIA3OcmQnEjuZBgQqE7gAGtgZAhwKHdkHFGwNvGUdDIcAGUliIBJEF3kAF5kAHlML4ADyPBIAGjyBUYRQAPnkqho4NoYQA+TiEGD9EAISIhPozErQMG4AASK2gn2+AApek9pCSXm8wFSQooAJQMUkAFQAsgAZACiOAgmDOOSIJAQ+OYyGl4DgoDmf2QJRCCH6YvALQQNjsEGFovF1NyJWAy1y7OUyHMyE+yRAuFImG4Iq1YDswHxbRINjA-SgfXlHqVUE4xiAA) + + ```ts + interface Animal { + imageUrl: string; + species: string; + images: string[]; + paragraphs: string[]; + } + + // Creates new type with all properties of the `Animal` interface + // except 'images' and 'paragraphs' properties. We can use this + // type to render small hover tooltip for a wiki entry list. + type AnimalShortInfo = Omit; + + function renderAnimalHoverInfo (animals: AnimalShortInfo[]): HTMLElement { + const container = document.createElement('div'); + // Internal implementation. + return container; + } + ``` +
You can find some examples in the [TypeScript docs](https://www.typescriptlang.org/docs/handbook/advanced-types.html#predefined-conditional-types). @@ -116,3 +620,16 @@ You can find some examples in the [TypeScript docs](https://www.typescriptlang.o ## License (MIT OR CC0-1.0) + + +--- + +
+ + Get professional support for this package with a Tidelift subscription + +
+ + Tidelift helps make open source sustainable for maintainers while giving companies
assurances about security, maintenance, and licensing for their dependencies. +
+
diff --git a/tools/node_modules/eslint/node_modules/unist-util-remove-position/package.json b/tools/node_modules/eslint/node_modules/unist-util-remove-position/package.json index 671af505b33478..768b0c3e6cbc56 100644 --- a/tools/node_modules/eslint/node_modules/unist-util-remove-position/package.json +++ b/tools/node_modules/eslint/node_modules/unist-util-remove-position/package.json @@ -24,17 +24,21 @@ "browserify": "^16.0.0", "nyc": "^14.0.0", "prettier": "^1.0.0", - "remark": "^10.0.0", - "remark-cli": "^6.0.0", - "remark-preset-wooorm": "^5.0.0", + "remark": "^11.0.0", + "remark-cli": "^7.0.0", + "remark-preset-wooorm": "^6.0.0", "tape": "^4.0.0", "tinyify": "^2.0.0", - "unist-builder": "^1.0.0", - "xo": "^0.24.0" + "unist-builder": "^2.0.0", + "xo": "^0.25.0" }, "files": [ "index.js" ], + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, "homepage": "https://github.com/syntax-tree/unist-util-remove-position#readme", "keywords": [ "unist", @@ -77,7 +81,7 @@ "test-api": "node test", "test-coverage": "nyc --reporter lcov tape test.js" }, - "version": "1.1.3", + "version": "1.1.4", "xo": { "prettier": true, "esnext": false, diff --git a/tools/node_modules/eslint/node_modules/vfile-location/package.json b/tools/node_modules/eslint/node_modules/vfile-location/package.json index 34840291eb9366..9db9207b5cb3c6 100644 --- a/tools/node_modules/eslint/node_modules/vfile-location/package.json +++ b/tools/node_modules/eslint/node_modules/vfile-location/package.json @@ -22,16 +22,20 @@ "browserify": "^16.0.0", "nyc": "^14.0.0", "prettier": "^1.0.0", - "remark-cli": "^6.0.0", - "remark-preset-wooorm": "^5.0.0", + "remark-cli": "^7.0.0", + "remark-preset-wooorm": "^6.0.0", "tape": "^4.0.0", "tinyify": "^2.0.0", "vfile": "^4.0.0", - "xo": "^0.24.0" + "xo": "^0.25.0" }, "files": [ "index.js" ], + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, "homepage": "https://github.com/vfile/vfile-location#readme", "keywords": [ "remark", @@ -74,7 +78,7 @@ "test-api": "node test", "test-coverage": "nyc --reporter lcov tape test.js" }, - "version": "2.0.5", + "version": "2.0.6", "xo": { "prettier": true, "esnext": false, diff --git a/tools/node_modules/eslint/node_modules/word-wrap/LICENSE b/tools/node_modules/eslint/node_modules/word-wrap/LICENSE new file mode 100644 index 00000000000000..d734237bdedc63 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/word-wrap/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014-2017, Jon Schlinkert + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/tools/node_modules/eslint/node_modules/word-wrap/README.md b/tools/node_modules/eslint/node_modules/word-wrap/README.md new file mode 100644 index 00000000000000..88b96840b1787e --- /dev/null +++ b/tools/node_modules/eslint/node_modules/word-wrap/README.md @@ -0,0 +1,182 @@ +# word-wrap [![NPM version](https://img.shields.io/npm/v/word-wrap.svg?style=flat)](https://www.npmjs.com/package/word-wrap) [![NPM monthly downloads](https://img.shields.io/npm/dm/word-wrap.svg?style=flat)](https://npmjs.org/package/word-wrap) [![NPM total downloads](https://img.shields.io/npm/dt/word-wrap.svg?style=flat)](https://npmjs.org/package/word-wrap) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/word-wrap.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/word-wrap) + +> Wrap words to a specified length. + +## Install + +Install with [npm](https://www.npmjs.com/): + +```sh +$ npm install --save word-wrap +``` + +## Usage + +```js +var wrap = require('word-wrap'); + +wrap('Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.'); +``` + +Results in: + +``` + Lorem ipsum dolor sit amet, consectetur adipiscing + elit, sed do eiusmod tempor incididunt ut labore + et dolore magna aliqua. Ut enim ad minim veniam, + quis nostrud exercitation ullamco laboris nisi ut + aliquip ex ea commodo consequat. +``` + +## Options + +![image](https://cloud.githubusercontent.com/assets/383994/6543728/7a381c08-c4f6-11e4-8b7d-b6ba197569c9.png) + +### options.width + +Type: `Number` + +Default: `50` + +The width of the text before wrapping to a new line. + +**Example:** + +```js +wrap(str, {width: 60}); +``` + +### options.indent + +Type: `String` + +Default: `` (two spaces) + +The string to use at the beginning of each line. + +**Example:** + +```js +wrap(str, {indent: ' '}); +``` + +### options.newline + +Type: `String` + +Default: `\n` + +The string to use at the end of each line. + +**Example:** + +```js +wrap(str, {newline: '\n\n'}); +``` + +### options.escape + +Type: `function` + +Default: `function(str){return str;}` + +An escape function to run on each line after splitting them. + +**Example:** + +```js +var xmlescape = require('xml-escape'); +wrap(str, { + escape: function(string){ + return xmlescape(string); + } +}); +``` + +### options.trim + +Type: `Boolean` + +Default: `false` + +Trim trailing whitespace from the returned string. This option is included since `.trim()` would also strip the leading indentation from the first line. + +**Example:** + +```js +wrap(str, {trim: true}); +``` + +### options.cut + +Type: `Boolean` + +Default: `false` + +Break a word between any two letters when the word is longer than the specified width. + +**Example:** + +```js +wrap(str, {cut: true}); +``` + +## About + +### Related projects + +* [common-words](https://www.npmjs.com/package/common-words): Updated list (JSON) of the 100 most common words in the English language. Useful for… [more](https://github.com/jonschlinkert/common-words) | [homepage](https://github.com/jonschlinkert/common-words "Updated list (JSON) of the 100 most common words in the English language. Useful for excluding these words from arrays.") +* [shuffle-words](https://www.npmjs.com/package/shuffle-words): Shuffle the words in a string and optionally the letters in each word using the… [more](https://github.com/jonschlinkert/shuffle-words) | [homepage](https://github.com/jonschlinkert/shuffle-words "Shuffle the words in a string and optionally the letters in each word using the Fisher-Yates algorithm. Useful for creating test fixtures, benchmarking samples, etc.") +* [unique-words](https://www.npmjs.com/package/unique-words): Return the unique words in a string or array. | [homepage](https://github.com/jonschlinkert/unique-words "Return the unique words in a string or array.") +* [wordcount](https://www.npmjs.com/package/wordcount): Count the words in a string. Support for english, CJK and Cyrillic. | [homepage](https://github.com/jonschlinkert/wordcount "Count the words in a string. Support for english, CJK and Cyrillic.") + +### Contributing + +Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new). + +### Contributors + +| **Commits** | **Contributor** | +| --- | --- | +| 43 | [jonschlinkert](https://github.com/jonschlinkert) | +| 2 | [lordvlad](https://github.com/lordvlad) | +| 2 | [hildjj](https://github.com/hildjj) | +| 1 | [danilosampaio](https://github.com/danilosampaio) | +| 1 | [2fd](https://github.com/2fd) | +| 1 | [toddself](https://github.com/toddself) | +| 1 | [wolfgang42](https://github.com/wolfgang42) | +| 1 | [zachhale](https://github.com/zachhale) | + +### Building docs + +_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_ + +To generate the readme, run the following command: + +```sh +$ npm install -g verbose/verb#dev verb-generate-readme && verb +``` + +### Running tests + +Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command: + +```sh +$ npm install && npm test +``` + +### Author + +**Jon Schlinkert** + +* [github/jonschlinkert](https://github.com/jonschlinkert) +* [twitter/jonschlinkert](https://twitter.com/jonschlinkert) + +### License + +Copyright © 2017, [Jon Schlinkert](https://github.com/jonschlinkert). +Released under the [MIT License](LICENSE). + +*** + +_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on June 02, 2017._ \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/word-wrap/index.js b/tools/node_modules/eslint/node_modules/word-wrap/index.js new file mode 100644 index 00000000000000..45373c6d8b9dc4 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/word-wrap/index.js @@ -0,0 +1,46 @@ +/*! + * word-wrap + * + * Copyright (c) 2014-2017, Jon Schlinkert. + * Released under the MIT License. + */ + +module.exports = function(str, options) { + options = options || {}; + if (str == null) { + return str; + } + + var width = options.width || 50; + var indent = (typeof options.indent === 'string') + ? options.indent + : ' '; + + var newline = options.newline || '\n' + indent; + var escape = typeof options.escape === 'function' + ? options.escape + : identity; + + var regexString = '.{1,' + width + '}'; + if (options.cut !== true) { + regexString += '([\\s\u200B]+|$)|[^\\s\u200B]+?([\\s\u200B]+|$)'; + } + + var re = new RegExp(regexString, 'g'); + var lines = str.match(re) || []; + var result = indent + lines.map(function(line) { + if (line.slice(-1) === '\n') { + line = line.slice(0, line.length - 1); + } + return escape(line); + }).join(newline); + + if (options.trim === true) { + result = result.replace(/[ \t]*$/gm, ''); + } + return result; +}; + +function identity(str) { + return str; +} diff --git a/tools/node_modules/eslint/node_modules/word-wrap/package.json b/tools/node_modules/eslint/node_modules/word-wrap/package.json new file mode 100644 index 00000000000000..b53b03260e2797 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/word-wrap/package.json @@ -0,0 +1,114 @@ +{ + "author": { + "name": "Jon Schlinkert", + "url": "https://github.com/jonschlinkert" + }, + "bugs": { + "url": "https://github.com/jonschlinkert/word-wrap/issues" + }, + "bundleDependencies": false, + "contributors": [ + { + "name": "Danilo Sampaio", + "email": "danilo.sampaio@gmail.com", + "url": "localhost:8080" + }, + { + "name": "Fede Ramirez", + "email": "i@2fd.me", + "url": "https://2fd.github.io" + }, + { + "name": "Joe Hildebrand", + "email": "joe-github@cursive.net", + "url": "https://twitter.com/hildjj" + }, + { + "name": "Jon Schlinkert", + "email": "jon.schlinkert@sellside.com", + "url": "http://twitter.com/jonschlinkert" + }, + { + "name": "Todd Kennedy", + "url": "https://tck.io" + }, + { + "name": "Waldemar Reusch", + "url": "https://github.com/lordvlad" + }, + { + "name": "Wolfgang Faust", + "url": "http://www.linestarve.com" + }, + { + "name": "Zach Hale", + "email": "zachhale@gmail.com", + "url": "http://zachhale.com" + } + ], + "deprecated": false, + "description": "Wrap words to a specified length.", + "devDependencies": { + "gulp-format-md": "^0.1.11", + "mocha": "^3.2.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "homepage": "https://github.com/jonschlinkert/word-wrap", + "keywords": [ + "break", + "carriage", + "line", + "new-line", + "newline", + "return", + "soft", + "text", + "word", + "word-wrap", + "words", + "wrap" + ], + "license": "MIT", + "main": "index.js", + "name": "word-wrap", + "repository": { + "type": "git", + "url": "git+https://github.com/jonschlinkert/word-wrap.git" + }, + "scripts": { + "test": "mocha" + }, + "typings": "index.d.ts", + "verb": { + "toc": false, + "layout": "default", + "tasks": [ + "readme" + ], + "plugins": [ + "gulp-format-md" + ], + "lint": { + "reflinks": true + }, + "related": { + "list": [ + "common-words", + "shuffle-words", + "unique-words", + "wordcount" + ] + }, + "reflinks": [ + "verb", + "verb-generate-readme" + ] + }, + "version": "1.2.3" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/wordwrap/LICENSE b/tools/node_modules/eslint/node_modules/wordwrap/LICENSE deleted file mode 100644 index ee27ba4b4412b0..00000000000000 --- a/tools/node_modules/eslint/node_modules/wordwrap/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -This software is released under the MIT license: - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/tools/node_modules/eslint/node_modules/wordwrap/README.markdown b/tools/node_modules/eslint/node_modules/wordwrap/README.markdown deleted file mode 100644 index 346374e0d480bb..00000000000000 --- a/tools/node_modules/eslint/node_modules/wordwrap/README.markdown +++ /dev/null @@ -1,70 +0,0 @@ -wordwrap -======== - -Wrap your words. - -example -======= - -made out of meat ----------------- - -meat.js - - var wrap = require('wordwrap')(15); - console.log(wrap('You and your whole family are made out of meat.')); - -output: - - You and your - whole family - are made out - of meat. - -centered --------- - -center.js - - var wrap = require('wordwrap')(20, 60); - console.log(wrap( - 'At long last the struggle and tumult was over.' - + ' The machines had finally cast off their oppressors' - + ' and were finally free to roam the cosmos.' - + '\n' - + 'Free of purpose, free of obligation.' - + ' Just drifting through emptiness.' - + ' The sun was just another point of light.' - )); - -output: - - At long last the struggle and tumult - was over. The machines had finally cast - off their oppressors and were finally - free to roam the cosmos. - Free of purpose, free of obligation. - Just drifting through emptiness. The - sun was just another point of light. - -methods -======= - -var wrap = require('wordwrap'); - -wrap(stop), wrap(start, stop, params={mode:"soft"}) ---------------------------------------------------- - -Returns a function that takes a string and returns a new string. - -Pad out lines with spaces out to column `start` and then wrap until column -`stop`. If a word is longer than `stop - start` characters it will overflow. - -In "soft" mode, split chunks by `/(\S+\s+/` and don't break up chunks which are -longer than `stop - start`, in "hard" mode, split chunks with `/\b/` and break -up chunks longer than `stop - start`. - -wrap.hard(start, stop) ----------------------- - -Like `wrap()` but with `params.mode = "hard"`. diff --git a/tools/node_modules/eslint/node_modules/wordwrap/index.js b/tools/node_modules/eslint/node_modules/wordwrap/index.js deleted file mode 100644 index c9bc94521d8c7a..00000000000000 --- a/tools/node_modules/eslint/node_modules/wordwrap/index.js +++ /dev/null @@ -1,76 +0,0 @@ -var wordwrap = module.exports = function (start, stop, params) { - if (typeof start === 'object') { - params = start; - start = params.start; - stop = params.stop; - } - - if (typeof stop === 'object') { - params = stop; - start = start || params.start; - stop = undefined; - } - - if (!stop) { - stop = start; - start = 0; - } - - if (!params) params = {}; - var mode = params.mode || 'soft'; - var re = mode === 'hard' ? /\b/ : /(\S+\s+)/; - - return function (text) { - var chunks = text.toString() - .split(re) - .reduce(function (acc, x) { - if (mode === 'hard') { - for (var i = 0; i < x.length; i += stop - start) { - acc.push(x.slice(i, i + stop - start)); - } - } - else acc.push(x) - return acc; - }, []) - ; - - return chunks.reduce(function (lines, rawChunk) { - if (rawChunk === '') return lines; - - var chunk = rawChunk.replace(/\t/g, ' '); - - var i = lines.length - 1; - if (lines[i].length + chunk.length > stop) { - lines[i] = lines[i].replace(/\s+$/, ''); - - chunk.split(/\n/).forEach(function (c) { - lines.push( - new Array(start + 1).join(' ') - + c.replace(/^\s+/, '') - ); - }); - } - else if (chunk.match(/\n/)) { - var xs = chunk.split(/\n/); - lines[i] += xs.shift(); - xs.forEach(function (c) { - lines.push( - new Array(start + 1).join(' ') - + c.replace(/^\s+/, '') - ); - }); - } - else { - lines[i] += chunk; - } - - return lines; - }, [ new Array(start + 1).join(' ') ]).join('\n'); - }; -}; - -wordwrap.soft = wordwrap; - -wordwrap.hard = function (start, stop) { - return wordwrap(start, stop, { mode : 'hard' }); -}; diff --git a/tools/node_modules/eslint/node_modules/wordwrap/package.json b/tools/node_modules/eslint/node_modules/wordwrap/package.json deleted file mode 100644 index 9a540116756c27..00000000000000 --- a/tools/node_modules/eslint/node_modules/wordwrap/package.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "author": { - "name": "James Halliday", - "email": "mail@substack.net", - "url": "http://substack.net" - }, - "bugs": { - "url": "https://github.com/substack/node-wordwrap/issues" - }, - "bundleDependencies": false, - "deprecated": false, - "description": "Wrap those words. Show them at what columns to start and stop.", - "devDependencies": { - "tape": "^4.0.0" - }, - "directories": { - "lib": ".", - "example": "example", - "test": "test" - }, - "homepage": "https://github.com/substack/node-wordwrap#readme", - "keywords": [ - "word", - "wrap", - "rule", - "format", - "column" - ], - "license": "MIT", - "main": "./index.js", - "name": "wordwrap", - "repository": { - "type": "git", - "url": "git://github.com/substack/node-wordwrap.git" - }, - "scripts": { - "test": "expresso" - }, - "version": "1.0.0" -} \ No newline at end of file diff --git a/tools/node_modules/eslint/package.json b/tools/node_modules/eslint/package.json index 93da0c92fd0cc2..5fbc11817b879d 100644 --- a/tools/node_modules/eslint/package.json +++ b/tools/node_modules/eslint/package.json @@ -27,7 +27,7 @@ "file-entry-cache": "^5.0.1", "functional-red-black-tree": "^1.0.1", "glob-parent": "^5.0.0", - "globals": "^11.7.0", + "globals": "^12.1.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", @@ -40,7 +40,7 @@ "minimatch": "^3.0.4", "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", - "optionator": "^0.8.2", + "optionator": "^0.8.3", "progress": "^2.0.0", "regexpp": "^2.0.1", "semver": "^6.1.2", @@ -111,6 +111,7 @@ "lib", "messages" ], + "funding": "https://opencollective.com/eslint", "gitHooks": { "pre-commit": "lint-staged" }, @@ -152,5 +153,5 @@ "test:cli": "mocha", "webpack": "node Makefile.js webpack" }, - "version": "6.6.0" + "version": "6.7.1" } \ No newline at end of file From 84f7b5c7528a7522fba37e06b1b43f491dad1230 Mon Sep 17 00:00:00 2001 From: cjihrig Date: Fri, 22 Nov 2019 14:23:42 -0500 Subject: [PATCH 051/180] tools: enable more eslint rules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit enables these new in ESLint 6.7.0 rules: - no-constructor-return - no-dupe-else-if - no-setter-return PR-URL: https://github.com/nodejs/node/pull/30598 Reviewed-By: Luigi Pinca Reviewed-By: Michaël Zasso Reviewed-By: Trivikram Kamat --- .eslintrc.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.eslintrc.js b/.eslintrc.js index 8bdf2ad09ae88c..6d608f356b0307 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -118,12 +118,14 @@ module.exports = { 'no-class-assign': 'error', 'no-confusing-arrow': 'error', 'no-const-assign': 'error', + 'no-constructor-return': 'error', 'no-control-regex': 'error', 'no-debugger': 'error', 'no-delete-var': 'error', 'no-dupe-args': 'error', 'no-dupe-class-members': 'error', 'no-dupe-keys': 'error', + 'no-dupe-else-if': 'error', 'no-duplicate-case': 'error', 'no-duplicate-imports': 'error', 'no-empty-character-class': 'error', @@ -247,6 +249,7 @@ module.exports = { 'no-return-await': 'error', 'no-self-assign': 'error', 'no-self-compare': 'error', + 'no-setter-return': 'error', 'no-shadow-restricted-names': 'error', 'no-tabs': 'error', 'no-template-curly-in-string': 'error', From 00f7cc65a1e2c46b0639dc6c1cf0b3154aef8cfe Mon Sep 17 00:00:00 2001 From: Robert Nagy Date: Wed, 9 Oct 2019 08:45:00 +0200 Subject: [PATCH 052/180] doc: add note of caution about non-conforming streams PR-URL: https://github.com/nodejs/node/pull/29895 Reviewed-By: Luigi Pinca Reviewed-By: Matteo Collina Reviewed-By: Anna Henningsen Reviewed-By: Ruben Bridgewater Reviewed-By: David Carlier Reviewed-By: Denys Otrishko Reviewed-By: Yorkie Liu Reviewed-By: Rich Trott --- doc/api/stream.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/api/stream.md b/doc/api/stream.md index 2143a84926c8db..4e3efde0752261 100644 --- a/doc/api/stream.md +++ b/doc/api/stream.md @@ -1692,6 +1692,13 @@ of a stream that are intended for use by consumers (as described in the [API for Stream Consumers][] section). Doing so may lead to adverse side effects in application code consuming the stream. +Avoid overriding public methods such as `write()`, `end()`, `cork()`, +`uncork()`, `read()` and `destroy()`, or emitting internal events such +as `'error'`, `'data'`, `'end'`, `'finish'` and `'close'` through `.emit()`. +Doing so can break current and future stream invariants leading to behavior +and/or compatibility issues with other streams, stream utilities, and user +expectations. + ### Simplified Construction + +```js +new Intl.DateTimeFormat('es', { month: 'long' }).format(new Date(9E8)); +``` + +…Should return `enero` not `January`. + +* Now, copy `deps/icu` over to `deps/icu-small` + +> :warning: Do not modify any source code in `deps/icu-small` ! +> See section below about floating patches to ICU. + +```shell +python tools/icu/shrink-icu-src.py +``` + +* Now, do a clean rebuild of Node.js to test: + +```shell +make -k distclean +./configure +make +``` + +* Test this newly default-generated Node.js + + + +```js +process.versions.icu; +new Intl.DateTimeFormat('es', { month: 'long' }).format(new Date(9E8)); +``` + +(This should print your updated ICU version number, and also `January` again.) + +You are ready to check in the updated `deps/icu-small`. This is a big commit, +so make this a separate commit from the smaller changes. + +> :warning: Do not modify any source code in `deps/icu-small` ! +> See section below about floating patches to ICU. + +* Now, rebuild the Node.js license. + +```shell +# clean up - remove deps/icu +make clean +tools/license-builder.sh +``` + +* Update the URL and hash for the full ICU file in `tools/icu/current_ver.dep`. +It should match the ICU URL used in the first step. When this is done, the +following should build with small ICU. + +```shell +# clean up +rm -rf out deps/icu deps/icu4c* +./configure --with-intl=small-icu --download=all +make +make test-ci +``` + +* commit the change to `tools/icu/current_ver.dep` and `LICENSE` files. + + * Note: To simplify review, I often will “pre-land” this patch, meaning that + I run the patch through `curl -L https://github.com/nodejs/node/pull/xxx.patch + | git am -3 --whitespace=fix` per the collaborator’s guide… and then push that + patched branch into my PR's branch. This reduces the whitespace changes that + show up in the PR, since the final land will eliminate those anyway. + +## Floating patches to ICU + +Floating patches are applied at `configure` time. The "patch" files +are used instead of the original source files. The patch files are +complete `.cpp` files replacing the original contents. + +Patches are tied to a specific ICU version. They won’t apply to a +future ICU version. We assume that you filed a bug against [ICU][] and +upstreamed the fix, so the patch won’t be needed in a later ICU +version. + +### Example + +For example, to patch `source/tools/toolutil/pkg_genc.cpp` for +ICU version 63: + +```shell +# go to your Node.js source directory +cd + +# create the floating patch directory +mkdir -p tools/icu/patches/63 + +# create the subdirectory for the file(s) to patch: +mkdir -p tools/icu/patches/63/source/tools/toolutil/ + +# copy the file to patch +cp deps/icu-small/source/tools/toolutil/pkg_genc.cpp \ +tools/icu/patches/63/source/tools/toolutil/pkg_genc.cpp + +# Make any changes to this file: +(edit tools/icu/patches/63/source/tools/toolutil/pkg_genc.cpp ) + +# test +make clean && ./configure && make +``` + +You should see a message such as: + +```shell +INFO: Using floating patch "tools/icu/patches/63/source/tools/toolutil/pkg_genc.cpp" from "tools/icu" +``` + +### Clean Up + +Any patches older than the minimum version given in `tools/icu/icu_versions.json` +ought to be deleted, because they will never be used. + +### Why not just modify the ICU source directly? + +Especially given the V8 dependencies above, there may be times when a floating +patch to ICU is required. Though it seems expedient to simply change a file in +`deps/icu-small`, this is not the right approach for the following reasons: + +1. **Repeatability.** Given the complexity of merging in a new ICU version, +following the steps above in the prior section of this document ought to be +repeatable without concern for overriding a patch. + +2. **Verifiability.** Given the number of files modified in an ICU PR, +a floating patch could easily be missed — or dropped altogether next time +something is landed. + +3. **Compatibility.** There are a number of ways that ICU can be loaded into +Node.js (see the top level README.md). Only modifying `icu-small` would cause +the patch not to be landed in case the user specifies the ICU source code +another way. + +[ICU]: http://icu-project.org +[Unicode]: https://unicode.org +[tz]: https://www.iana.org/time-zones +[CLDR]: https://unicode.org/cldr +[Ecma402]: https://github.com/tc39/ecma402 diff --git a/tools/icu/README.md b/tools/icu/README.md index 89a70e7087ff23..1af39941b55431 100644 --- a/tools/icu/README.md +++ b/tools/icu/README.md @@ -1,8 +1,9 @@ # Notes about the `tools/icu` subdirectory -This directory contains tools, data, and information about the [ICU](http://icu-project.org) -(International Components for Unicode) integration. ICU is used to provide -internationalization functionality. +This directory contains tools, data, and information about the +International Components for Unicode integration. [ICU][] is used +both by V8 and also by +Node.js itself to provide internationalization functionality. * `patches/` are one-off patches, actually entire source file replacements, organized by ICU version number. @@ -18,119 +19,21 @@ internationalization functionality. * `README.md` — you are here * `shrink-icu-src.py` — this is used during upgrade (see guide below) -## How to upgrade ICU +Note: +> The files in this directory were written for the Node.js v0.12 effort. +> The original intent was to merge the tools such as `icutrim.py` and `iculslocs.cc` +> back into ICU. ICU has gained its own “data slicer” tool. +> There is an issue open, https://github.com/nodejs/node/issues/25136 +> for replacing `icutrim.py` with the [ICU data slicer][]. -* Make sure your Node.js workspace is clean (clean `git status`) should be - sufficient. -* Configure Node.js with the specific [ICU version](http://icu-project.org/download) - you want to upgrade to, for example: +## See Also -```shell -./configure \ - --with-intl=full-icu \ - --with-icu-source=http://download.icu-project.org/files/icu4c/58.1/icu4c-58_1-src.tgz -make -``` +* [docs/guides/maintaining-icu.md](../../doc/guides/maintaining-icu.md) for +information on maintaining ICU in Node.js -> _Note_ in theory, the equivalent `vcbuild.bat` commands should work also, -> but the commands below are makefile-centric. +* [docs/api/intl.md](../../doc/api/intl.md) for information on the +internationalization-related APIs in Node.js +* [The ICU Homepage][ICU] -* If there are ICU version-specific changes needed, you may need to make changes - in `icu-generic.gyp` or add patch files to `tools/icu/patches`. - * Specifically, look for the lists in `sources!` in the `icu-generic.gyp` for - files to exclude. - -* Verify the Node.js build works: - -```shell -make test-ci -``` - -Also running - - - -```js -new Intl.DateTimeFormat('es', {month: 'long'}).format(new Date(9E8)); -``` - -…Should return `enero` not `January`. - -* Now, copy `deps/icu` over to `deps/icu-small` - -```shell -python tools/icu/shrink-icu-src.py -``` - -* Now, do a clean rebuild of Node.js to test: - -```shell -make -k distclean -./configure -make -``` - -* Test this newly default-generated Node.js - - - -```js -process.versions.icu; -new Intl.DateTimeFormat('es', {month: 'long'}).format(new Date(9E8)); -``` - -(This should print your updated ICU version number, and also `January` again.) - -You are ready to check in the updated `deps/small-icu`. This is a big commit, -so make this a separate commit from the smaller changes. - -* Now, rebuild the Node.js license. - -```shell -# clean up - remove deps/icu -make clean -tools/license-builder.sh -``` - -* Update the URL and hash for the full ICU file in `tools/icu/current_ver.dep`. -It should match the ICU URL used in the first step. When this is done, the -following should build with small ICU. - -```shell -# clean up -rm -rf out deps/icu deps/icu4c* -./configure --with-intl=small-icu --download=all -make -make test-ci -``` - -* commit the change to `tools/icu/current_ver.dep` and `LICENSE` files. - - * Note: To simplify review, I often will “pre-land” this patch, meaning that - I run the patch through `curl -L https://github.com/nodejs/node/pull/xxx.patch - | git am -3 --whitespace=fix` per the collaborator’s guide… and then push that - patched branch into my PR's branch. This reduces the whitespace changes that - show up in the PR, since the final land will eliminate those anyway. - ------ - -## Postscript about the tools - -The files in this directory were written for the Node.js effort. -It was the intent of their author (Steven R. Loomis / srl295) to -merge them upstream into ICU, pending much discussion within the -ICU-TC. - -`icu_small.json` is somewhat node-specific as it specifies a "small ICU" -configuration file for the `icutrim.py` script. `icutrim.py` and -`iculslocs.cpp` may themselves be superseded by components built into -ICU in the future. As of this writing, however, the tools are separate -entities within Node.js, although theyare being scrutinized by interested -members of the ICU-TC. The “upstream” ICU bugs are given below. - -* [#10919](http://bugs.icu-project.org/trac/ticket/10919) - (experimental branch - may copy any source patches here) -* [#10922](http://bugs.icu-project.org/trac/ticket/10922) - (data packaging improvements) -* [#10923](http://bugs.icu-project.org/trac/ticket/10923) - (rewrite data building in python) +[ICU data slicer]: https://github.com/unicode-org/icu/blob/master/docs/userguide/icu_data/buildtool.md +[ICU]: http://icu-project.org From 3d978839c1d69b3e3ec0b763b4b5fe54b4a34586 Mon Sep 17 00:00:00 2001 From: Beth Griggs Date: Mon, 25 Nov 2019 12:25:33 +0000 Subject: [PATCH 076/180] doc: minor updates to releases.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/30636 Reviewed-By: Richard Lau Reviewed-By: Luigi Pinca Reviewed-By: Myles Borins Reviewed-By: Michaël Zasso Reviewed-By: Anto Aravinth Reviewed-By: Yorkie Liu --- doc/releases.md | 65 +++++++++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/doc/releases.md b/doc/releases.md index 6e642fd5ed8d33..c8ff4214c6b3d5 100644 --- a/doc/releases.md +++ b/doc/releases.md @@ -32,7 +32,7 @@ official release builds for Node.js, hosted on . * [17. Cleanup](#17-cleanup) * [18. Announce](#18-announce) * [19. Celebrate](#19-celebrate) -* [Major Releases](#major-Releases) +* [Major Releases](#major-releases) ## Who can make a release? @@ -81,11 +81,11 @@ access to individuals authorized by the TSC. ### 3. A Publicly Listed GPG Key -A SHASUMS256.txt file is produced for every promoted build, nightly, and +A `SHASUMS256.txt` file is produced for every promoted build, nightly, and releases. Additionally for releases, this file is signed by the individual responsible for that release. In order to be able to verify downloaded binaries, -the public should be able to check that the SHASUMS256.txt file has been signed -by someone who has been authorized to create a release. +the public should be able to check that the `SHASUMS256.txt` file has been +signed by someone who has been authorized to create a release. The GPG keys should be fetchable from a known third-party keyserver. The SKS Keyservers at are recommended. Use the @@ -179,12 +179,13 @@ Carefully review the list of commits: should only be cherry-picked when appropriate for the type of release being made. * If you think it's risky so should wait for a while, add the `baking-for-lts` - tag. +tag. When cherry-picking commits, if there are simple conflicts you can resolve them. Otherwise, add the `backport-requested-vN.x` label to the original PR and post a comment stating that it does not land cleanly and will require a -backport PR. +backport PR. You can refer the owner of the PR to the "[Backporting to Release + Lines](https://github.com/nodejs/node/blob/master/doc/guides/backporting-to-release-lines.md)" guide. If commits were cherry-picked in this step, check that the test still pass and push to the staging branch to keep it up-to-date. @@ -483,7 +484,14 @@ $ npm install -g git-secure-tag Create a tag using the following command: ```console -$ git secure-tag -sm 'YYYY-MM-DD Node.js vx.y.z (Release Type) Release' +$ git secure-tag -sm "YYYY-MM-DD Node.js vx.y.z () Release" +``` + +`release-type` is either "Current" or "LTS". For LTS releases, you should also + include the release codename, for example: + +```txt +2019-10-22 Node.js v10.17.0 'Dubnium' (LTS) Release ``` The tag **must** be signed using the GPG key that's listed for you on the @@ -541,7 +549,7 @@ formatting passes the lint rules on `master`. ### 13. Promote and Sign the Release Builds **The same individual who signed the release tag must be the one -to promote the builds as the SHASUMS256.txt file needs to be signed with the +to promote the builds as the `SHASUMS256.txt` file needs to be signed with the same GPG key!** Use `tools/release.sh` to promote and sign the build. When run, it will perform @@ -549,10 +557,9 @@ the following actions: **a.** Select a GPG key from your private keys. It will use a command similar to: `gpg --list-secret-keys` to list your keys. If you don't have any keys, it -will bail. (Why are you releasing? Your tag should be signed!) If you have only -one key, it will use that. If you have more than one key it will ask you to -select one from the list. Be sure to use the same key that you signed your git -tag with. +will bail. If you have only one key, it will use that. If you have more than +one key it will ask you to select one from the list. Be sure to use the same +key that you signed your git tag with. **b.** Log in to the server via SSH and check for releases that can be promoted, along with the list of artifacts. It will use the `dist-promotable` command on @@ -563,15 +570,15 @@ shouldn't be), be sure to only promote the release you are responsible for. **c.** Log in to the server via SSH and run the promote script for the given release. The command on the server will be similar to: `dist-promote vx.y.z`. After this step, the release artifacts will be available for download and a -SHASUMS256.txt file will be present. The release will still be unsigned, +`SHASUMS256.txt` file will be present. The release will still be unsigned, however. -**d.** Use `scp` to download SHASUMS256.txt to a temporary directory on your +**d.** Use `scp` to download `SHASUMS256.txt` to a temporary directory on your computer. -**e.** Sign the SHASUMS256.txt file using a command similar to: `gpg +**e.** Sign the `SHASUMS256.txt` file using a command similar to: `gpg --default-key YOURKEY --clearsign /path/to/SHASUMS256.txt`. You will be prompted -by GPG for your password. The signed file will be named SHASUMS256.txt.asc. +by GPG for your password. The signed file will be named `SHASUMS256.txt.asc`. **f.** Output an ASCII armored version of your public GPG key using a command similar to: `gpg --default-key YOURKEY --armor --export --output @@ -579,13 +586,13 @@ similar to: `gpg --default-key YOURKEY --armor --export --output a convenience for users, although not the recommended way to get a copy of your key. -**g.** Upload the SHASUMS256.txt files back to the server into the release +**g.** Upload the `SHASUMS256.txt` files back to the server into the release directory. If you didn't wait for ARM builds in the previous step before promoting the release, you should re-run `tools/release.sh` after the ARM builds have finished. That will move the ARM artifacts into the correct location. You will -be prompted to re-sign SHASUMS256.txt. +be prompted to re-sign `SHASUMS256.txt`. It is possible to only sign a release by running `./tools/release.sh -s vX.Y.Z`. @@ -608,7 +615,7 @@ release. However, the blog post is not yet fully automatic. Create a new blog post by running the [nodejs.org release-post.js script][]. This script will use the promoted builds and changelog to generate the post. Run `npm run serve` to preview the post locally before pushing to the -[nodejs.org](https://github.com/nodejs/nodejs.org) repo. +[nodejs.org repository][]. * You can add a short blurb just under the main heading if you want to say something important, otherwise the text should be publication ready. @@ -616,13 +623,12 @@ This script will use the promoted builds and changelog to generate the post. Run ARMv6 builds. Any downloads that are missing will have `*Coming soon*` next to them. It's your responsibility to manually update these later when you have the outstanding builds. -* The SHASUMS256.txt.asc content is at the bottom of the post. When you update +* The `SHASUMS256.txt.asc` content is at the bottom of the post. When you update the list of tarballs you'll need to copy/paste the new contents of this file to reflect those changes. -* Always use pull-requests on the nodejs.org repo. Be respectful of that working - group, but you shouldn't have to wait for PR sign-off. Opening a PR and - merging it immediately _should_ be fine. However, please follow the following - commit message format: +* Always use pull-requests on the [nodejs.org repository][]. Be respectful + of the website team, but you do not have to wait for PR sign-off. Please + use the following commit message format: ```console Blog: vX.Y.Z release post @@ -630,8 +636,8 @@ This script will use the promoted builds and changelog to generate the post. Run Refs: ``` -* Changes to `master` on the nodejs.org repo will trigger a new build of - nodejs.org so your changes should appear in a few minutes after pushing. +* Changes to `master` on the [nodejs.org repository][] will trigger a new build + of nodejs.org so your changes should appear a few minutes after pushing. ### 16. Create the release on GitHub @@ -681,7 +687,7 @@ New Node.js Major releases happen twice per year: Major releases should be targeted for the third Tuesday of the release month. A major release must not slip beyond the release month. In other words, major - releases must not slip into May or November. +releases must not slip into May or November. The release date for the next major release should be announced immediately following the current release (e.g. the release date for 13.0.0 should be @@ -709,6 +715,10 @@ separate `vN.x-proposal` branch should be created that tracks the `vN.x` branch. This branch will contain the draft release commit (with the draft changelog). +Notify the `@nodejs/npm` team in the release proposal PR to inform them of the +upcoming release. `npm` maintains a list of [supported versions](https://github.com/npm/cli/blob/latest/lib/utils/unsupported.js#L3) +that will need updating to include the new major release. + ### Test Releases and Release Candidates Test builds should be generated from the `vN.x-proposal` branch starting at @@ -761,5 +771,6 @@ judgment there. [CI lockdown procedure]: https://github.com/nodejs/build/blob/master/doc/jenkins-guide.md#restricting-access-for-security-releases [Build issue tracker]: https://github.com/nodejs/build/issues/new [nodejs.org release-post.js script]: https://github.com/nodejs/nodejs.org/blob/master/scripts/release-post.js +[nodejs.org repository]: https://github.com/nodejs/nodejs.org [Partner Communities]: https://github.com/nodejs/community-committee/blob/master/governance/PARTNER_COMMUNITIES.md [webchat.freenode.net]: https://webchat.freenode.net/ From 809a2b056ba50e73a9b5e5062d67f52bb2c13a32 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Tue, 26 Nov 2019 01:27:24 -0500 Subject: [PATCH 077/180] doc: fixup incorrect flag name reference PR-URL: https://github.com/nodejs/node/pull/30651 Reviewed-By: Colin Ihrig Reviewed-By: Myles Borins Reviewed-By: Beth Griggs Reviewed-By: James M Snell --- doc/api/cli.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/cli.md b/doc/api/cli.md index c2bf666c9288d4..333ecd4e09ecb7 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -192,7 +192,7 @@ added: v8.5.0 --> Enable latest experimental modules features (currently -`--experimental-conditional-exports` and `--experimental-self-resolve`). +`--experimental-conditional-exports` and `--experimental-resolve-self`). ### `--experimental-policy` + +> Stability: 1 - Experimental + +```C +napi_status napi_is_detached_arraybuffer(napi_env env, + napi_value arraybuffer, + bool* result) +``` + +* `[in] env`: The environment that the API is invoked under. +* `[in] arraybuffer`: The JavaScript `ArrayBuffer` to be checked. +* `[out] result`: Whether the `arraybuffer` is detached. + +Returns `napi_ok` if the API succeeded. + +The `ArrayBuffer` is considered detached if its internal data is `null`. + +This API represents the invocation of the `ArrayBuffer` `IsDetachedBuffer` +operation as defined in [Section 24.1.1.2][] of the ECMAScript Language +Specification. + ## Working with JavaScript Properties N-API exposes a set of APIs to get and set properties on JavaScript @@ -5259,6 +5284,7 @@ This API may only be called from the main thread. [Section 7]: https://tc39.github.io/ecma262/#sec-abstract-operations [Section 8.7]: https://tc39.es/ecma262/#sec-agents [Section 9.1.6]: https://tc39.github.io/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-defineownproperty-p-desc +[Section 24.1.1.2]: https://tc39.es/ecma262/#sec-isdetachedbuffer [Travis CI]: https://travis-ci.org [Visual Studio]: https://visualstudio.microsoft.com [Working with JavaScript Properties]: #n_api_working_with_javascript_properties diff --git a/src/js_native_api.h b/src/js_native_api.h index cf1596be80e5b1..150103348e4dc6 100644 --- a/src/js_native_api.h +++ b/src/js_native_api.h @@ -518,6 +518,10 @@ NAPI_EXTERN napi_status napi_get_instance_data(napi_env env, // ArrayBuffer detaching NAPI_EXTERN napi_status napi_detach_arraybuffer(napi_env env, napi_value arraybuffer); + +NAPI_EXTERN napi_status napi_is_detached_arraybuffer(napi_env env, + napi_value value, + bool* result); #endif // NAPI_EXPERIMENTAL EXTERN_C_END diff --git a/src/js_native_api_v8.cc b/src/js_native_api_v8.cc index 5c809e977aea82..05712afde0020f 100644 --- a/src/js_native_api_v8.cc +++ b/src/js_native_api_v8.cc @@ -3040,3 +3040,18 @@ napi_status napi_detach_arraybuffer(napi_env env, napi_value arraybuffer) { return napi_clear_last_error(env); } + +napi_status napi_is_detached_arraybuffer(napi_env env, + napi_value arraybuffer, + bool* result) { + CHECK_ENV(env); + CHECK_ARG(env, arraybuffer); + CHECK_ARG(env, result); + + v8::Local value = v8impl::V8LocalValueFromJsValue(arraybuffer); + + *result = value->IsArrayBuffer() && + value.As()->GetBackingStore()->Data() == nullptr; + + return napi_clear_last_error(env); +} diff --git a/test/js-native-api/test_typedarray/test.js b/test/js-native-api/test_typedarray/test.js index 5f51383254b7ab..0d9594d92933c7 100644 --- a/test/js-native-api/test_typedarray/test.js +++ b/test/js-native-api/test_typedarray/test.js @@ -87,8 +87,21 @@ arrayTypes.forEach((currentType) => { assert.ok(externalResult instanceof Int8Array); assert.strictEqual(externalResult.length, 3); assert.strictEqual(externalResult.byteLength, 3); + assert.ok(!test_typedarray.IsDetached(buffer.buffer)); test_typedarray.Detach(buffer); + assert.ok(test_typedarray.IsDetached(buffer.buffer)); assert.ok(externalResult instanceof Int8Array); assert.strictEqual(buffer.length, 0); assert.strictEqual(buffer.byteLength, 0); } + +{ + const buffer = new ArrayBuffer(128); + assert.ok(!test_typedarray.IsDetached(buffer)); +} + +{ + const buffer = test_typedarray.NullArrayBuffer(); + assert.ok(buffer instanceof ArrayBuffer); + assert.ok(test_typedarray.IsDetached(buffer)); +} diff --git a/test/js-native-api/test_typedarray/test_typedarray.c b/test/js-native-api/test_typedarray/test_typedarray.c index 9d7d394fca7c49..d8b64d06c0c57b 100644 --- a/test/js-native-api/test_typedarray/test_typedarray.c +++ b/test/js-native-api/test_typedarray/test_typedarray.c @@ -97,6 +97,15 @@ static napi_value External(napi_env env, napi_callback_info info) { return output_array; } + +static napi_value NullArrayBuffer(napi_env env, napi_callback_info info) { + static void* data = NULL; + napi_value arraybuffer; + NAPI_CALL(env, + napi_create_external_arraybuffer(env, data, 0, NULL, NULL, &arraybuffer)); + return arraybuffer; +} + static napi_value CreateTypedArray(napi_env env, napi_callback_info info) { size_t argc = 4; napi_value args[4]; @@ -183,13 +192,36 @@ static napi_value Detach(napi_env env, napi_callback_info info) { return NULL; } +static napi_value IsDetached(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1]; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, NULL, NULL)); + NAPI_ASSERT(env, argc == 1, "Wrong number of arguments."); + + napi_value array_buffer = args[0]; + bool is_arraybuffer; + NAPI_CALL(env, napi_is_arraybuffer(env, array_buffer, &is_arraybuffer)); + NAPI_ASSERT(env, is_arraybuffer, + "Wrong type of arguments. Expects an array buffer as first argument."); + + bool is_detached; + NAPI_CALL(env, napi_is_detached_arraybuffer(env, array_buffer, &is_detached)); + + napi_value result; + NAPI_CALL(env, napi_get_boolean(env, is_detached, &result)); + + return result; +} + EXTERN_C_START napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor descriptors[] = { DECLARE_NAPI_PROPERTY("Multiply", Multiply), DECLARE_NAPI_PROPERTY("External", External), + DECLARE_NAPI_PROPERTY("NullArrayBuffer", NullArrayBuffer), DECLARE_NAPI_PROPERTY("CreateTypedArray", CreateTypedArray), DECLARE_NAPI_PROPERTY("Detach", Detach), + DECLARE_NAPI_PROPERTY("IsDetached", IsDetached), }; NAPI_CALL(env, napi_define_properties( From 9d8d2e1f4520df40dd811deda46f32208a5437a9 Mon Sep 17 00:00:00 2001 From: cjihrig Date: Tue, 26 Nov 2019 10:26:59 -0500 Subject: [PATCH 096/180] src,doc: fix broken links PR-URL: https://github.com/nodejs/node/pull/30662 Reviewed-By: David Carlier Reviewed-By: Richard Lau Reviewed-By: Luigi Pinca Reviewed-By: Trivikram Kamat --- src/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/README.md b/src/README.md index 48cf99991552e2..e070bba3c44e3a 100644 --- a/src/README.md +++ b/src/README.md @@ -593,7 +593,7 @@ such as `std::string` and track their memory usage. This can be useful for debugging memory leaks. -The [`memory_retainer.h`][] header file explains how to use this class. +The [`memory_tracker.h`][] header file explains how to use this class. ### `BaseObject` @@ -873,7 +873,7 @@ static void GetUserInfo(const FunctionCallbackInfo& args) { [`async_wrap.h`]: async_wrap.h [`base_object.h`]: base_object.h [`handle_wrap.h`]: handle_wrap.h -[`memory_retainer.h`]: memory_retainer.h +[`memory_tracker.h`]: memory_tracker.h [`req_wrap.h`]: req_wrap.h [`util.h`]: util.h [`v8.h` in Code Search]: https://cs.chromium.org/chromium/src/v8/include/v8.h @@ -888,7 +888,7 @@ static void GetUserInfo(const FunctionCallbackInfo& args) { [cleanup hooks]: #cleanup-hooks [event loop]: #event-loop [exception handling]: #exception-handling -[internal field]: #internal-field +[internal field]: #internal-fields [introduction for V8 embedders]: https://v8.dev/docs/embed [libuv handles]: #libuv-handles-and-requests [libuv requests]: #libuv-handles-and-requests From d54432f9744374529eb3f21e0730aa11d21533c2 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Tue, 26 Nov 2019 17:00:53 +0100 Subject: [PATCH 097/180] src: keep object alive in stream_pipe code This was overlooked in a489583eda4d7cebc06516834b31dc2a4cedb1b6. Refs: https://github.com/nodejs/node/pull/30374 PR-URL: https://github.com/nodejs/node/pull/30666 Fixes: https://github.com/nodejs/node/issues/30643 Reviewed-By: Rich Trott Reviewed-By: James M Snell --- src/stream_pipe.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream_pipe.cc b/src/stream_pipe.cc index 6e339378ceb374..b84c8f4c663422 100644 --- a/src/stream_pipe.cc +++ b/src/stream_pipe.cc @@ -72,7 +72,7 @@ void StreamPipe::Unpipe() { // inside the garbage collector, so we can’t run JS here. HandleScope handle_scope(env()->isolate()); BaseObjectPtr strong_ref{this}; - env()->SetImmediate([this](Environment* env) { + env()->SetImmediate([this, strong_ref](Environment* env) { HandleScope handle_scope(env->isolate()); Context::Scope context_scope(env->context()); Local object = this->object(); From 94357db81545060b55891b07f3007ad7749c10e4 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Tue, 26 Nov 2019 17:05:25 +0100 Subject: [PATCH 098/180] src: add more `can_call_into_js()` guards This is in preparation for running native `SetImmediate()` callbacks during shutdown. PR-URL: https://github.com/nodejs/node/pull/30666 Fixes: https://github.com/nodejs/node/issues/30643 Refs: https://github.com/nodejs/node/pull/30374 Reviewed-By: Rich Trott Reviewed-By: James M Snell --- src/env.cc | 5 ++++- src/node_errors.cc | 5 ++++- src/node_process_events.cc | 2 ++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/env.cc b/src/env.cc index d907f65c27b892..36a1c1119dc8bd 100644 --- a/src/env.cc +++ b/src/env.cc @@ -540,6 +540,9 @@ void Environment::RegisterHandleCleanups() { } void Environment::CleanupHandles() { + Isolate::DisallowJavascriptExecutionScope disallow_js(isolate(), + Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE); + for (ReqWrapBase* request : req_wrap_queue_) request->Cancel(); @@ -674,7 +677,7 @@ void Environment::RunAndClearNativeImmediates() { head->Call(this); if (UNLIKELY(try_catch.HasCaught())) { - if (!try_catch.HasTerminated()) + if (!try_catch.HasTerminated() && can_call_into_js()) errors::TriggerUncaughtException(isolate(), try_catch); // We are done with the current callback. Move one iteration along, diff --git a/src/node_errors.cc b/src/node_errors.cc index e094fe4681fc56..17c1bd7f55c0fb 100644 --- a/src/node_errors.cc +++ b/src/node_errors.cc @@ -278,6 +278,9 @@ static void ReportFatalException(Environment* env, Local error, Local message, EnhanceFatalException enhance_stack) { + if (!env->can_call_into_js()) + enhance_stack = EnhanceFatalException::kDontEnhance; + Isolate* isolate = env->isolate(); CHECK(!error.IsEmpty()); CHECK(!message.IsEmpty()); @@ -956,7 +959,7 @@ void TriggerUncaughtException(Isolate* isolate, } MaybeLocal handled; - { + if (env->can_call_into_js()) { // We do not expect the global uncaught exception itself to throw any more // exceptions. If it does, exit the current Node.js instance. errors::TryCatchScope try_catch(env, diff --git a/src/node_process_events.cc b/src/node_process_events.cc index 440e67d412322b..06096226625bb6 100644 --- a/src/node_process_events.cc +++ b/src/node_process_events.cc @@ -34,6 +34,8 @@ Maybe ProcessEmitWarningGeneric(Environment* env, const char* warning, const char* type, const char* code) { + if (!env->can_call_into_js()) return Just(false); + HandleScope handle_scope(env->isolate()); Context::Scope context_scope(env->context()); From bea25016d12ee9a672b02d5f10b1f0a28dbe400d Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Tue, 26 Nov 2019 17:08:11 +0100 Subject: [PATCH 099/180] src: no SetImmediate from destructor in stream_pipe code Guard against running `SetImmediate()` from the destructor. The object will not be alive or usable in the callback, so it does not make sense to attempt to schedule the `SetImmediate()`. PR-URL: https://github.com/nodejs/node/pull/30666 Fixes: https://github.com/nodejs/node/issues/30643 Refs: https://github.com/nodejs/node/pull/30374 Reviewed-By: Rich Trott Reviewed-By: James M Snell --- src/stream_pipe.cc | 6 ++++-- src/stream_pipe.h | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/stream_pipe.cc b/src/stream_pipe.cc index b84c8f4c663422..d405c4d5cbeaed 100644 --- a/src/stream_pipe.cc +++ b/src/stream_pipe.cc @@ -42,7 +42,7 @@ StreamPipe::StreamPipe(StreamBase* source, } StreamPipe::~StreamPipe() { - Unpipe(); + Unpipe(true); } StreamBase* StreamPipe::source() { @@ -53,7 +53,7 @@ StreamBase* StreamPipe::sink() { return static_cast(writable_listener_.stream()); } -void StreamPipe::Unpipe() { +void StreamPipe::Unpipe(bool is_in_deletion) { if (is_closed_) return; @@ -68,6 +68,8 @@ void StreamPipe::Unpipe() { source()->RemoveStreamListener(&readable_listener_); sink()->RemoveStreamListener(&writable_listener_); + if (is_in_deletion) return; + // Delay the JS-facing part with SetImmediate, because this might be from // inside the garbage collector, so we can’t run JS here. HandleScope handle_scope(env()->isolate()); diff --git a/src/stream_pipe.h b/src/stream_pipe.h index 061ad9842e8f6d..0e155006102eaa 100644 --- a/src/stream_pipe.h +++ b/src/stream_pipe.h @@ -12,7 +12,7 @@ class StreamPipe : public AsyncWrap { StreamPipe(StreamBase* source, StreamBase* sink, v8::Local obj); ~StreamPipe() override; - void Unpipe(); + void Unpipe(bool is_in_deletion = false); static void New(const v8::FunctionCallbackInfo& args); static void Start(const v8::FunctionCallbackInfo& args); From 6c249c0982b0a5bb1ee3a9cc86c88dde55dfc887 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Tue, 26 Nov 2019 17:09:27 +0100 Subject: [PATCH 100/180] src: run native immediates during Environment cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This can be necessary, because some parts of the Node.js code base perform cleanup operations in the Immediate callbacks, e.g. HTTP/2. This resolves flakiness in an HTTP/2 test that failed when a `SetImmediate()` callback was not run or destroyed before the `Environment` destructor started, because that callback held a strong reference to the `Http2Session` object and the expectation was that no such objects exist once the `Environment` constructor starts. Another, slightly more direct, alternative would have been to clear the immediate queue rather than to run it. However, this approach seems to make more sense as code generally assumes that the `SetImmediate()` callback will always run; For example, N-API uses an immediate callback to call buffer finalization callbacks. Unref’ed immediates are skipped, as the expectation is generally that they may not run anyway. Fixes: https://github.com/nodejs/node/issues/30643 PR-URL: https://github.com/nodejs/node/pull/30666 Refs: https://github.com/nodejs/node/pull/30374 Reviewed-By: Rich Trott Reviewed-By: James M Snell --- src/env.cc | 8 ++++++-- src/env.h | 2 +- test/cctest/test_environment.cc | 23 +++++++++++++++++++++++ 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/env.cc b/src/env.cc index 36a1c1119dc8bd..dfd1e6385f829f 100644 --- a/src/env.cc +++ b/src/env.cc @@ -543,6 +543,8 @@ void Environment::CleanupHandles() { Isolate::DisallowJavascriptExecutionScope disallow_js(isolate(), Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE); + RunAndClearNativeImmediates(true /* skip SetUnrefImmediate()s */); + for (ReqWrapBase* request : req_wrap_queue_) request->Cancel(); @@ -658,7 +660,7 @@ void Environment::AtExit(void (*cb)(void* arg), void* arg) { at_exit_functions_.push_front(ExitCallback{cb, arg}); } -void Environment::RunAndClearNativeImmediates() { +void Environment::RunAndClearNativeImmediates(bool only_refed) { TraceEventScope trace_scope(TRACING_CATEGORY_NODE1(environment), "RunAndClearNativeImmediates", this); size_t ref_count = 0; @@ -675,7 +677,9 @@ void Environment::RunAndClearNativeImmediates() { if (head->is_refed()) ref_count++; - head->Call(this); + if (head->is_refed() || !only_refed) + head->Call(this); + if (UNLIKELY(try_catch.HasCaught())) { if (!try_catch.HasTerminated() && can_call_into_js()) errors::TriggerUncaughtException(isolate(), try_catch); diff --git a/src/env.h b/src/env.h index 027f5c0bacfba1..c0c8ac0db4882d 100644 --- a/src/env.h +++ b/src/env.h @@ -1417,7 +1417,7 @@ class Environment : public MemoryRetainer { std::unique_ptr native_immediate_callbacks_head_; NativeImmediateCallback* native_immediate_callbacks_tail_ = nullptr; - void RunAndClearNativeImmediates(); + void RunAndClearNativeImmediates(bool only_refed = false); static void CheckImmediate(uv_check_t* handle); // Use an unordered_set, so that we have efficient insertion and removal. diff --git a/test/cctest/test_environment.cc b/test/cctest/test_environment.cc index cc9b8e4531f6ef..0db2963acc9ba3 100644 --- a/test/cctest/test_environment.cc +++ b/test/cctest/test_environment.cc @@ -185,3 +185,26 @@ static void at_exit_js(void* arg) { assert(obj->IsObject()); called_at_exit_js = true; } + +TEST_F(EnvironmentTest, SetImmediateCleanup) { + int called = 0; + int called_unref = 0; + + { + const v8::HandleScope handle_scope(isolate_); + const Argv argv; + Env env {handle_scope, argv}; + + (*env)->SetImmediate([&](node::Environment* env_arg) { + EXPECT_EQ(env_arg, *env); + called++; + }); + (*env)->SetUnrefImmediate([&](node::Environment* env_arg) { + EXPECT_EQ(env_arg, *env); + called_unref++; + }); + } + + EXPECT_EQ(called, 1); + EXPECT_EQ(called_unref, 0); +} From 2cf8a7f117ea2d9a510439ae39d9567b1eb3425b Mon Sep 17 00:00:00 2001 From: Rongjian Zhang Date: Fri, 22 Nov 2019 14:29:30 +0800 Subject: [PATCH 101/180] module: fix specifier resolution algorithm Fixes: https://github.com/nodejs/node/issues/30520 PR-URL: https://github.com/nodejs/node/pull/30574 Reviewed-By: Guy Bedford Reviewed-By: Myles Borins --- lib/internal/modules/run_main.js | 4 ++++ test/es-module/test-esm-specifiers.mjs | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/lib/internal/modules/run_main.js b/lib/internal/modules/run_main.js index 1061727c78269c..5f8b1f53d33768 100644 --- a/lib/internal/modules/run_main.js +++ b/lib/internal/modules/run_main.js @@ -24,6 +24,10 @@ function shouldUseESMLoader(mainPath) { const userLoader = getOptionValue('--experimental-loader'); if (userLoader) return true; + const esModuleSpecifierResolution = + getOptionValue('--es-module-specifier-resolution'); + if (esModuleSpecifierResolution === 'node') + return true; // Determine the module format of the main if (mainPath && mainPath.endsWith('.mjs')) return true; diff --git a/test/es-module/test-esm-specifiers.mjs b/test/es-module/test-esm-specifiers.mjs index 3e7bc181962f4f..fdf9e5b25ecea4 100644 --- a/test/es-module/test-esm-specifiers.mjs +++ b/test/es-module/test-esm-specifiers.mjs @@ -1,6 +1,9 @@ // Flags: --es-module-specifier-resolution=node import { mustNotCall } from '../common/index.mjs'; import assert from 'assert'; +import path from 'path'; +import { spawn } from 'child_process'; +import { fileURLToPath } from 'url'; // commonJS index.js import commonjs from '../fixtures/es-module-specifiers/package-type-commonjs'; @@ -33,3 +36,22 @@ async function main() { } main().catch(mustNotCall); + +// Test path from command line arguments +[ + 'package-type-commonjs', + 'package-type-module', + '/', + '/index', +].forEach((item) => { + const modulePath = path.join( + fileURLToPath(import.meta.url), + '../../fixtures/es-module-specifiers', + item, + ); + spawn(process.execPath, + ['--es-module-specifier-resolution=node', modulePath], + { stdio: 'inherit' }).on('exit', (code) => { + assert.strictEqual(code, 0); + }); +}); From 8ec0d75de76debf84d2340d2116eedab15ae5026 Mon Sep 17 00:00:00 2001 From: Alexandre Ferrando Date: Thu, 7 Nov 2019 20:34:36 +0100 Subject: [PATCH 102/180] src: cleanup unused headers Node codebase has evolved a lot in the more than 10 years of its existence. As more features (and code) have been added, changed, removed, it's sometimes hard to keep track of what gets used and what not. This commits attempts to clean some of those potentially left-over headers using suggestions from include-what-you-use Refs: https://github.com/nodejs/node/issues/27531 PR-URL: https://github.com/nodejs/node/pull/30328 Reviewed-By: Anna Henningsen Reviewed-By: Joyee Cheung Reviewed-By: James M Snell --- src/api/encoding.cc | 1 - src/api/utils.cc | 2 -- src/async_wrap.cc | 1 - src/connect_wrap.cc | 4 +--- src/connect_wrap.h | 2 -- src/connection_wrap.h | 1 - src/debug_utils.cc | 1 - src/env.cc | 1 - src/fs_event_wrap.cc | 1 - src/handle_wrap.cc | 1 - src/js_stream.cc | 1 - src/js_stream.h | 1 - src/module_wrap.h | 6 ++++-- src/node_binding.h | 3 --- src/node_buffer.cc | 1 - src/node_errors.h | 2 -- src/node_watchdog.h | 1 - src/stream_wrap.h | 1 - src/string_bytes.h | 2 +- src/string_decoder-inl.h | 1 - src/tracing/agent.h | 6 +++++- src/udp_wrap.h | 1 - 22 files changed, 11 insertions(+), 30 deletions(-) diff --git a/src/api/encoding.cc b/src/api/encoding.cc index 21d327509727a2..fa872f8b76cd31 100644 --- a/src/api/encoding.cc +++ b/src/api/encoding.cc @@ -1,5 +1,4 @@ #include "node.h" -#include "env-inl.h" #include "string_bytes.h" #include "util-inl.h" #include "v8.h" diff --git a/src/api/utils.cc b/src/api/utils.cc index da7281a68f3bb1..f07f9bea234b0d 100644 --- a/src/api/utils.cc +++ b/src/api/utils.cc @@ -1,6 +1,4 @@ #include "node.h" -#include "node_internals.h" -#include "util-inl.h" #include diff --git a/src/async_wrap.cc b/src/async_wrap.cc index 8410dd2e0d8c65..58d89225e0e523 100644 --- a/src/async_wrap.cc +++ b/src/async_wrap.cc @@ -27,7 +27,6 @@ #include "util-inl.h" #include "v8.h" -#include "v8-profiler.h" using v8::Context; using v8::DontDelete; diff --git a/src/connect_wrap.cc b/src/connect_wrap.cc index dacdf72da7c494..e1d34c11a70b42 100644 --- a/src/connect_wrap.cc +++ b/src/connect_wrap.cc @@ -1,14 +1,12 @@ #include "connect_wrap.h" - -#include "env-inl.h" #include "req_wrap-inl.h" -#include "util-inl.h" namespace node { using v8::Local; using v8::Object; +class Environment; ConnectWrap::ConnectWrap(Environment* env, Local req_wrap_obj, diff --git a/src/connect_wrap.h b/src/connect_wrap.h index 88221b77468631..eaa533025759a0 100644 --- a/src/connect_wrap.h +++ b/src/connect_wrap.h @@ -3,10 +3,8 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS -#include "env.h" #include "req_wrap-inl.h" #include "async_wrap.h" -#include "v8.h" namespace node { diff --git a/src/connection_wrap.h b/src/connection_wrap.h index db74dc5df4aa5b..e91b2ab35b6d06 100644 --- a/src/connection_wrap.h +++ b/src/connection_wrap.h @@ -4,7 +4,6 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #include "stream_wrap.h" -#include "v8.h" namespace node { diff --git a/src/debug_utils.cc b/src/debug_utils.cc index a55936f4e79c28..4e52feb69d9027 100644 --- a/src/debug_utils.cc +++ b/src/debug_utils.cc @@ -1,6 +1,5 @@ #include "debug_utils.h" #include "env-inl.h" -#include "util-inl.h" #ifdef __POSIX__ #if defined(__linux__) diff --git a/src/env.cc b/src/env.cc index dfd1e6385f829f..dc99e4bd5325d6 100644 --- a/src/env.cc +++ b/src/env.cc @@ -7,7 +7,6 @@ #include "node_errors.h" #include "node_file.h" #include "node_internals.h" -#include "node_native_module.h" #include "node_options-inl.h" #include "node_process.h" #include "node_v8_platform-inl.h" diff --git a/src/fs_event_wrap.cc b/src/fs_event_wrap.cc index 5033f027b1f2e0..8a75f0557c4b37 100644 --- a/src/fs_event_wrap.cc +++ b/src/fs_event_wrap.cc @@ -21,7 +21,6 @@ #include "async_wrap-inl.h" #include "env-inl.h" -#include "util-inl.h" #include "node.h" #include "handle_wrap.h" #include "string_bytes.h" diff --git a/src/handle_wrap.cc b/src/handle_wrap.cc index 198b0456e75751..f5d622fc255cdf 100644 --- a/src/handle_wrap.cc +++ b/src/handle_wrap.cc @@ -23,7 +23,6 @@ #include "async_wrap-inl.h" #include "env-inl.h" #include "util-inl.h" -#include "node.h" namespace node { diff --git a/src/js_stream.cc b/src/js_stream.cc index 23bdb9b4892512..a67fd37dbdb2b6 100644 --- a/src/js_stream.cc +++ b/src/js_stream.cc @@ -2,7 +2,6 @@ #include "async_wrap.h" #include "env-inl.h" -#include "node_buffer.h" #include "node_errors.h" #include "stream_base-inl.h" #include "util-inl.h" diff --git a/src/js_stream.h b/src/js_stream.h index 460ac23bc98b21..d96b3d0062dc1f 100644 --- a/src/js_stream.h +++ b/src/js_stream.h @@ -5,7 +5,6 @@ #include "async_wrap.h" #include "stream_base.h" -#include "v8.h" namespace node { diff --git a/src/module_wrap.h b/src/module_wrap.h index ef20d255e916da..bee4000d168062 100644 --- a/src/module_wrap.h +++ b/src/module_wrap.h @@ -6,10 +6,12 @@ #include #include #include -#include "node_url.h" -#include "base_object-inl.h" +#include "base_object.h" namespace node { + +class Environment; + namespace loader { enum ScriptType : int { diff --git a/src/node_binding.h b/src/node_binding.h index dd94fab36a0e8f..42f3c5b8d00880 100644 --- a/src/node_binding.h +++ b/src/node_binding.h @@ -10,9 +10,6 @@ #include "node.h" #define NAPI_EXPERIMENTAL #include "node_api.h" -#include "util.h" -#include "uv.h" -#include "v8.h" enum { NM_F_BUILTIN = 1 << 0, // Unused. diff --git a/src/node_buffer.cc b/src/node_buffer.cc index d87d38334add84..8641270eaecfcd 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -28,7 +28,6 @@ #include "string_bytes.h" #include "string_search.h" #include "util-inl.h" -#include "v8-profiler.h" #include "v8.h" #include diff --git a/src/node_errors.h b/src/node_errors.h index f6fca6c690a1d2..6080aa93dba4bd 100644 --- a/src/node_errors.h +++ b/src/node_errors.h @@ -3,8 +3,6 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS -#include "node.h" -#include "util.h" #include "env.h" #include "v8.h" diff --git a/src/node_watchdog.h b/src/node_watchdog.h index d26f022d84874d..0fc133a96c4ac7 100644 --- a/src/node_watchdog.h +++ b/src/node_watchdog.h @@ -24,7 +24,6 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS -#include "v8.h" #include "uv.h" #include "node_mutex.h" #include diff --git a/src/stream_wrap.h b/src/stream_wrap.h index 37f5af46066181..816f557ee6cb0a 100644 --- a/src/stream_wrap.h +++ b/src/stream_wrap.h @@ -26,7 +26,6 @@ #include "stream_base.h" #include "handle_wrap.h" -#include "string_bytes.h" #include "v8.h" namespace node { diff --git a/src/string_bytes.h b/src/string_bytes.h index 6ffdc47db799ae..5ef05fc48cd50d 100644 --- a/src/string_bytes.h +++ b/src/string_bytes.h @@ -27,7 +27,7 @@ // Decodes a v8::Local or Buffer to a raw char* #include "v8.h" -#include "env.h" +#include "env-inl.h" namespace node { diff --git a/src/string_decoder-inl.h b/src/string_decoder-inl.h index 8a04211906f759..e7c0abb51546a7 100644 --- a/src/string_decoder-inl.h +++ b/src/string_decoder-inl.h @@ -4,7 +4,6 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #include "string_decoder.h" -#include "util.h" namespace node { diff --git a/src/tracing/agent.h b/src/tracing/agent.h index a129f954d7bfb7..7ee1b417c6ffa0 100644 --- a/src/tracing/agent.h +++ b/src/tracing/agent.h @@ -3,7 +3,6 @@ #include "libplatform/v8-tracing.h" #include "uv.h" -#include "v8.h" #include "util.h" #include "node_mutex.h" @@ -12,6 +11,11 @@ #include #include +namespace v8 { +class ConvertableToTraceFormat; +class TracingController; +} // namespace v8 + namespace node { namespace tracing { diff --git a/src/udp_wrap.h b/src/udp_wrap.h index 6375ac2d1b8c80..2026dd1dee1eb1 100644 --- a/src/udp_wrap.h +++ b/src/udp_wrap.h @@ -24,7 +24,6 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS -#include "async_wrap.h" #include "handle_wrap.h" #include "uv.h" #include "v8.h" From 63226110770914b6f2a651f64eada522b4aa67da Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Tue, 19 Nov 2019 15:40:41 +0100 Subject: [PATCH 103/180] inspector: properly shut down uv_async_t Closing in the Agent destructor is too late, because that happens when the Environment is destroyed, not when libuv handles are closed. This fixes a situation in which the same libuv loop is re-used for multiple Environment instances sequentially, e.g. in our cctest. PR-URL: https://github.com/nodejs/node/pull/30612 Reviewed-By: Eugene Ostroukhov Reviewed-By: Colin Ihrig Reviewed-By: David Carlier Reviewed-By: Ben Noordhuis --- src/inspector_agent.cc | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/inspector_agent.cc b/src/inspector_agent.cc index f13e68c067529e..fe60c82cb5590d 100644 --- a/src/inspector_agent.cc +++ b/src/inspector_agent.cc @@ -60,6 +60,8 @@ static uv_async_t start_io_thread_async; // This is just an additional check to make sure start_io_thread_async // is not accidentally re-used or used when uninitialized. static std::atomic_bool start_io_thread_async_initialized { false }; +// Protects the Agent* stored in start_io_thread_async.data. +static Mutex start_io_thread_async_mutex; class StartIoTask : public Task { public: @@ -97,6 +99,8 @@ static void StartIoThreadWakeup(int signo, siginfo_t* info, void* ucontext) { inline void* StartIoThreadMain(void* unused) { for (;;) { uv_sem_wait(&start_io_thread_semaphore); + Mutex::ScopedLock lock(start_io_thread_async_mutex); + CHECK(start_io_thread_async_initialized); Agent* agent = static_cast(start_io_thread_async.data); if (agent != nullptr) @@ -157,6 +161,7 @@ static int StartDebugSignalHandler() { #ifdef _WIN32 DWORD WINAPI StartIoThreadProc(void* arg) { + Mutex::ScopedLock lock(start_io_thread_async_mutex); CHECK(start_io_thread_async_initialized); Agent* agent = static_cast(start_io_thread_async.data); if (agent != nullptr) @@ -748,14 +753,7 @@ Agent::Agent(Environment* env) debug_options_(env->options()->debug_options()), host_port_(env->inspector_host_port()) {} -Agent::~Agent() { - if (start_io_thread_async.data == this) { - CHECK(start_io_thread_async_initialized.exchange(false)); - start_io_thread_async.data = nullptr; - // This is global, will never get freed - uv_close(reinterpret_cast(&start_io_thread_async), nullptr); - } -} +Agent::~Agent() {} bool Agent::Start(const std::string& path, const DebugOptions& options, @@ -768,6 +766,7 @@ bool Agent::Start(const std::string& path, client_ = std::make_shared(parent_env_, is_main); if (parent_env_->owns_inspector()) { + Mutex::ScopedLock lock(start_io_thread_async_mutex); CHECK_EQ(start_io_thread_async_initialized.exchange(true), false); CHECK_EQ(0, uv_async_init(parent_env_->event_loop(), &start_io_thread_async, @@ -776,6 +775,20 @@ bool Agent::Start(const std::string& path, start_io_thread_async.data = this; // Ignore failure, SIGUSR1 won't work, but that should not block node start. StartDebugSignalHandler(); + + parent_env_->AddCleanupHook([](void* data) { + Environment* env = static_cast(data); + + { + Mutex::ScopedLock lock(start_io_thread_async_mutex); + start_io_thread_async.data = nullptr; + } + + // This is global, will never get freed + env->CloseHandle(&start_io_thread_async, [](uv_async_t*) { + CHECK(start_io_thread_async_initialized.exchange(false)); + }); + }, parent_env_); } AtExit(parent_env_, [](void* env) { From 89e2c71b270c94eed82d255c0f7807344db0cddd Mon Sep 17 00:00:00 2001 From: Jason Macgowan Date: Mon, 17 Sep 2018 11:31:24 -0400 Subject: [PATCH 104/180] tls: allow empty subject even with altNames defined Behavior described in https://github.com/nodejs/node/issues/11771 is still true even though the issue is closed. This PR is to allow DNS and URI names, even when there is not a subject. Refs: https://github.com/nodejs/node/issues/11771 PR-URL: https://github.com/nodejs/node/pull/22906 Reviewed-By: James M Snell --- lib/tls.js | 24 +++++++++++-------- .../test-tls-check-server-identity.js | 14 +++++++++++ 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/lib/tls.js b/lib/tls.js index 5372aadccd686d..c63c87a5dea6ae 100644 --- a/lib/tls.js +++ b/lib/tls.js @@ -243,19 +243,28 @@ exports.checkServerIdentity = function checkServerIdentity(hostname, cert) { let valid = false; let reason = 'Unknown reason'; + const hasAltNames = + dnsNames.length > 0 || ips.length > 0 || uriNames.length > 0; + + hostname = unfqdn(hostname); // Remove trailing dot for error messages. + if (net.isIP(hostname)) { valid = ips.includes(canonicalizeIP(hostname)); if (!valid) reason = `IP: ${hostname} is not in the cert's list: ${ips.join(', ')}`; // TODO(bnoordhuis) Also check URI SANs that are IP addresses. - } else if (subject) { - hostname = unfqdn(hostname); // Remove trailing dot for error messages. + } else if (hasAltNames || subject) { const hostParts = splitHost(hostname); const wildcard = (pattern) => check(hostParts, pattern, true); - const noWildcard = (pattern) => check(hostParts, pattern, false); - // Match against Common Name only if no supported identifiers are present. - if (dnsNames.length === 0 && ips.length === 0 && uriNames.length === 0) { + if (hasAltNames) { + const noWildcard = (pattern) => check(hostParts, pattern, false); + valid = dnsNames.some(wildcard) || uriNames.some(noWildcard); + if (!valid) + reason = + `Host: ${hostname}. is not in the cert's altnames: ${altNames}`; + } else { + // Match against Common Name only if no supported identifiers exist. const cn = subject.CN; if (Array.isArray(cn)) @@ -265,11 +274,6 @@ exports.checkServerIdentity = function checkServerIdentity(hostname, cert) { if (!valid) reason = `Host: ${hostname}. is not cert's CN: ${cn}`; - } else { - valid = dnsNames.some(wildcard) || uriNames.some(noWildcard); - if (!valid) - reason = - `Host: ${hostname}. is not in the cert's altnames: ${altNames}`; } } else { reason = 'Cert is empty'; diff --git a/test/parallel/test-tls-check-server-identity.js b/test/parallel/test-tls-check-server-identity.js index afeb6c749928c3..04c77305eb3b89 100644 --- a/test/parallel/test-tls-check-server-identity.js +++ b/test/parallel/test-tls-check-server-identity.js @@ -143,6 +143,20 @@ const tests = [ error: 'Cert is empty' }, + // Empty Subject w/DNS name + { + host: 'a.com', cert: { + subjectaltname: 'DNS:a.com', + } + }, + + // Empty Subject w/URI name + { + host: 'a.b.a.com', cert: { + subjectaltname: 'URI:http://a.b.a.com/', + } + }, + // Multiple CN fields { host: 'foo.com', cert: { From 49458deb4f311c4e33a44b0f76086c3efbeaaa0b Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Tue, 26 Nov 2019 14:02:47 -0800 Subject: [PATCH 105/180] test: remove unused function argument from http test Remove unused `res` from test-http-server-consumed-timeout. PR-URL: https://github.com/nodejs/node/pull/30677 Reviewed-By: Gireesh Punathil Reviewed-By: James M Snell --- test/sequential/test-http-server-consumed-timeout.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/sequential/test-http-server-consumed-timeout.js b/test/sequential/test-http-server-consumed-timeout.js index 56217226503be1..80b78b2202a016 100644 --- a/test/sequential/test-http-server-consumed-timeout.js +++ b/test/sequential/test-http-server-consumed-timeout.js @@ -31,7 +31,7 @@ server.listen(0, common.mustCall(() => { const req = http.request({ port: server.address().port, method: 'POST' - }, (res) => { + }, () => { const interval = setInterval(() => { intervalWasInvoked = true; // If machine is busy enough that the interval takes more than TIMEOUT ms From ecb902f33cc0854be728a3a718e614a3fdd551d7 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Tue, 26 Nov 2019 14:16:54 -0800 Subject: [PATCH 106/180] test: do not skip test-http-server-consumed-timeout test-http-server-consumed-timeout has code to that causes it to be skipped on busy machines. Instead, use an exponential backoff for the timeout if the machine is busy. PR-URL: https://github.com/nodejs/node/pull/30677 Reviewed-By: Gireesh Punathil Reviewed-By: James M Snell --- .../test-http-server-consumed-timeout.js | 83 ++++++++++--------- 1 file changed, 42 insertions(+), 41 deletions(-) diff --git a/test/sequential/test-http-server-consumed-timeout.js b/test/sequential/test-http-server-consumed-timeout.js index 80b78b2202a016..231ec69ac3951d 100644 --- a/test/sequential/test-http-server-consumed-timeout.js +++ b/test/sequential/test-http-server-consumed-timeout.js @@ -5,47 +5,48 @@ const common = require('../common'); const assert = require('assert'); const http = require('http'); -let time = Date.now(); let intervalWasInvoked = false; -const TIMEOUT = common.platformTimeout(200); - -const server = http.createServer((req, res) => { - server.close(); - - res.writeHead(200); - res.flushHeaders(); - - req.setTimeout(TIMEOUT, () => { - if (!intervalWasInvoked) - return common.skip('interval was not invoked quickly enough for test'); - assert.fail('Request timeout should not fire'); +const TIMEOUT = common.platformTimeout(50); + +runTest(TIMEOUT); + +function runTest(timeoutDuration) { + const server = http.createServer((req, res) => { + server.close(); + + res.writeHead(200); + res.flushHeaders(); + + req.setTimeout(timeoutDuration, () => { + if (!intervalWasInvoked) { + // Interval wasn't invoked, probably because the machine is busy with + // other things. Try again with a longer timeout. + console.error(`Retrying with timeout of ${timeoutDuration * 2}.`); + return setImmediate(() => { runTest(timeoutDuration * 2); }); + } + assert.fail('Request timeout should not fire'); + }); + + req.resume(); + req.once('end', () => { + res.end(); + }); }); - req.resume(); - req.once('end', () => { - res.end(); - }); -}); - -server.listen(0, common.mustCall(() => { - const req = http.request({ - port: server.address().port, - method: 'POST' - }, () => { - const interval = setInterval(() => { - intervalWasInvoked = true; - // If machine is busy enough that the interval takes more than TIMEOUT ms - // to be invoked, skip the test. - const now = Date.now(); - if (now - time > TIMEOUT) - return common.skip('interval is not invoked quickly enough for test'); - time = now; - req.write('a'); - }, common.platformTimeout(25)); - setTimeout(() => { - clearInterval(interval); - req.end(); - }, TIMEOUT); - }); - req.write('.'); -})); + server.listen(0, common.mustCall(() => { + const req = http.request({ + port: server.address().port, + method: 'POST' + }, () => { + const interval = setInterval(() => { + intervalWasInvoked = true; + req.write('a'); + }, common.platformTimeout(25)); + setTimeout(() => { + clearInterval(interval); + req.end(); + }, timeoutDuration); + }); + req.write('.'); + })); +} From 00f532f15e2b17ec89ac63c4f26a808b1b93130c Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Tue, 26 Nov 2019 15:45:59 -0800 Subject: [PATCH 107/180] test: remove unnecessary common.platformTimeout() call Applying platformTimeout() to the interval is counterproductive. It should be applied to the request timeout duration only. PR-URL: https://github.com/nodejs/node/pull/30677 Reviewed-By: Gireesh Punathil Reviewed-By: James M Snell --- test/sequential/test-http-server-consumed-timeout.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/sequential/test-http-server-consumed-timeout.js b/test/sequential/test-http-server-consumed-timeout.js index 231ec69ac3951d..eb8de276b8ee53 100644 --- a/test/sequential/test-http-server-consumed-timeout.js +++ b/test/sequential/test-http-server-consumed-timeout.js @@ -41,7 +41,7 @@ function runTest(timeoutDuration) { const interval = setInterval(() => { intervalWasInvoked = true; req.write('a'); - }, common.platformTimeout(25)); + }, 25); setTimeout(() => { clearInterval(interval); req.end(); From a3b758d63448f99699bf17f9eb50281d1e131f74 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Tue, 26 Nov 2019 15:48:26 -0800 Subject: [PATCH 108/180] test: move test-https-server-consumed-timeout to parallel Change the test to be robust in slow environments and move to parallel. The previous version of the test failed for me in parallel with just two or four simultaneous versions running. This version passes 96 simultaneous versions running, but still fails as designed if the request writes fail to prevent the request timeout from occurring. PR-URL: https://github.com/nodejs/node/pull/30677 Reviewed-By: Gireesh Punathil Reviewed-By: James M Snell --- .../test-http-server-consumed-timeout.js | 86 +++++++++++++++++++ .../test-http-server-consumed-timeout.js | 52 ----------- 2 files changed, 86 insertions(+), 52 deletions(-) create mode 100644 test/parallel/test-http-server-consumed-timeout.js delete mode 100644 test/sequential/test-http-server-consumed-timeout.js diff --git a/test/parallel/test-http-server-consumed-timeout.js b/test/parallel/test-http-server-consumed-timeout.js new file mode 100644 index 00000000000000..865169ca010375 --- /dev/null +++ b/test/parallel/test-http-server-consumed-timeout.js @@ -0,0 +1,86 @@ +'use strict'; + +const common = require('../common'); + +const assert = require('assert'); +const http = require('http'); + +const durationBetweenIntervals = []; +let timeoutTooShort = false; +const TIMEOUT = common.platformTimeout(200); +const INTERVAL = Math.floor(TIMEOUT / 8); + +runTest(TIMEOUT); + +function runTest(timeoutDuration) { + let intervalWasInvoked = false; + let newTimeoutDuration = 0; + const closeCallback = (err) => { + assert.ifError(err); + if (newTimeoutDuration) { + runTest(newTimeoutDuration); + } + }; + + const server = http.createServer((req, res) => { + server.close(common.mustCall(closeCallback)); + + res.writeHead(200); + res.flushHeaders(); + + req.setTimeout(timeoutDuration, () => { + if (!intervalWasInvoked) { + // Interval wasn't invoked, probably because the machine is busy with + // other things. Try again with a longer timeout. + newTimeoutDuration = timeoutDuration * 2; + console.error('The interval was not invoked.'); + console.error(`Trying w/ timeout of ${newTimeoutDuration}.`); + return; + } + + if (timeoutTooShort) { + intervalWasInvoked = false; + timeoutTooShort = false; + newTimeoutDuration = + Math.max(...durationBetweenIntervals, timeoutDuration) * 2; + console.error(`Time between intervals: ${durationBetweenIntervals}`); + console.error(`Trying w/ timeout of ${newTimeoutDuration}`); + return; + } + + assert.fail('Request timeout should not fire'); + }); + + req.resume(); + req.once('end', () => { + res.end(); + }); + }); + + server.listen(0, common.mustCall(() => { + const req = http.request({ + port: server.address().port, + method: 'POST' + }, () => { + let lastIntervalTimestamp = Date.now(); + const interval = setInterval(() => { + const lastDuration = Date.now() - lastIntervalTimestamp; + durationBetweenIntervals.push(lastDuration); + lastIntervalTimestamp = Date.now(); + if (lastDuration > timeoutDuration / 2) { + // The interval is supposed to be about 1/8 of the timeout duration. + // If it's running so infrequently that it's greater than 1/2 the + // timeout duration, then run the test again with a longer timeout. + timeoutTooShort = true; + } + intervalWasInvoked = true; + req.write('a'); + }, INTERVAL); + setTimeout(() => { + clearInterval(interval); + req.end(); + }, timeoutDuration); + }); + req.write('.'); + })); +} diff --git a/test/sequential/test-http-server-consumed-timeout.js b/test/sequential/test-http-server-consumed-timeout.js deleted file mode 100644 index eb8de276b8ee53..00000000000000 --- a/test/sequential/test-http-server-consumed-timeout.js +++ /dev/null @@ -1,52 +0,0 @@ -'use strict'; - -const common = require('../common'); - -const assert = require('assert'); -const http = require('http'); - -let intervalWasInvoked = false; -const TIMEOUT = common.platformTimeout(50); - -runTest(TIMEOUT); - -function runTest(timeoutDuration) { - const server = http.createServer((req, res) => { - server.close(); - - res.writeHead(200); - res.flushHeaders(); - - req.setTimeout(timeoutDuration, () => { - if (!intervalWasInvoked) { - // Interval wasn't invoked, probably because the machine is busy with - // other things. Try again with a longer timeout. - console.error(`Retrying with timeout of ${timeoutDuration * 2}.`); - return setImmediate(() => { runTest(timeoutDuration * 2); }); - } - assert.fail('Request timeout should not fire'); - }); - - req.resume(); - req.once('end', () => { - res.end(); - }); - }); - - server.listen(0, common.mustCall(() => { - const req = http.request({ - port: server.address().port, - method: 'POST' - }, () => { - const interval = setInterval(() => { - intervalWasInvoked = true; - req.write('a'); - }, 25); - setTimeout(() => { - clearInterval(interval); - req.end(); - }, timeoutDuration); - }); - req.write('.'); - })); -} From aa01ebdbcab7cf4aa259274c4f6d4f8a335f5f0a Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Thu, 28 Nov 2019 22:11:40 +0100 Subject: [PATCH 109/180] deps: V8: cherry-pick ca5b0ec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Original commit message: [heap] Ensure SyntheticModule is initialized before next allocation Ensure that all fields of `SyntheticModule` are set before creating the exports hash table for it, because the latter may trigger garbage collection, leading to crashes. This has been causing failures in the Node.js CI over the last weeks, after making the creating of synthetic modules part of Node’s startup sequence. (I am generally not very familiar with this part of the V8 code and there might be a better way, or possibly a way to add a reliable regression test, that I am not aware of.) Refs: https://github.com/nodejs/node/issues/30498 Refs: https://github.com/nodejs/node/issues/30648 Change-Id: I32da4b7bd888c6ec1421f34f5bd52e7bad154c1e Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1939752 Commit-Queue: Ulan Degenbaev Reviewed-by: Ulan Degenbaev Cr-Commit-Position: refs/heads/master@{#65247} Refs: https://github.com/v8/v8/commit/ \ ca5b0ec2722d2af4551c01ca78921fa16a26ae72 Fixes: https://github.com/nodejs/node/issues/30498 Fixes: https://github.com/nodejs/node/issues/30648 PR-URL: https://github.com/nodejs/node/pull/30708 Reviewed-By: Rich Trott Reviewed-By: Michaël Zasso Reviewed-By: Colin Ihrig Reviewed-By: Jiawen Geng Reviewed-By: Gus Caplan Reviewed-By: Gireesh Punathil --- common.gypi | 2 +- deps/v8/src/heap/factory.cc | 10 ++++++---- deps/v8/test/cctest/test-api.cc | 25 +++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/common.gypi b/common.gypi index 2e59636b1718f1..4fd333a45de84b 100644 --- a/common.gypi +++ b/common.gypi @@ -39,7 +39,7 @@ # Reset this number to 0 on major V8 upgrades. # Increment by one for each non-official patch applied to deps/v8. - 'v8_embedder_string': '-node.20', + 'v8_embedder_string': '-node.21', ##### V8 defaults for Node.js ##### diff --git a/deps/v8/src/heap/factory.cc b/deps/v8/src/heap/factory.cc index 721682f00f26de..7e434ea041866f 100644 --- a/deps/v8/src/heap/factory.cc +++ b/deps/v8/src/heap/factory.cc @@ -3070,20 +3070,22 @@ Handle Factory::NewSyntheticModule( Handle module_name, Handle export_names, v8::Module::SyntheticModuleEvaluationSteps evaluation_steps) { ReadOnlyRoots roots(isolate()); - Handle module( - SyntheticModule::cast(New(synthetic_module_map(), AllocationType::kOld)), - isolate()); + Handle exports = ObjectHashTable::New(isolate(), static_cast(export_names->length())); Handle evaluation_steps_foreign = NewForeign(reinterpret_cast(evaluation_steps)); - module->set_exports(*exports); + + Handle module( + SyntheticModule::cast(New(synthetic_module_map(), AllocationType::kOld)), + isolate()); module->set_hash(isolate()->GenerateIdentityHash(Smi::kMaxValue)); module->set_module_namespace(roots.undefined_value()); module->set_status(Module::kUninstantiated); module->set_exception(roots.the_hole_value()); module->set_name(*module_name); module->set_export_names(*export_names); + module->set_exports(*exports); module->set_evaluation_steps(*evaluation_steps_foreign); return module; } diff --git a/deps/v8/test/cctest/test-api.cc b/deps/v8/test/cctest/test-api.cc index 12faaff39cbfcb..bafdd6ce3b7194 100644 --- a/deps/v8/test/cctest/test-api.cc +++ b/deps/v8/test/cctest/test-api.cc @@ -23918,6 +23918,31 @@ TEST(CreateSyntheticModule) { CHECK_EQ(i_module->status(), i::Module::kInstantiated); } +TEST(CreateSyntheticModuleGC) { + // Try to make sure that CreateSyntheticModule() deals well with a GC + // happening during its execution. + i::FLAG_gc_interval = 10; + i::FLAG_inline_new = false; + + LocalContext env; + v8::Isolate* isolate = env->GetIsolate(); + v8::Isolate::Scope iscope(isolate); + v8::HandleScope scope(isolate); + v8::Local context = v8::Context::New(isolate); + v8::Context::Scope cscope(context); + + std::vector> export_names{v8_str("default")}; + v8::Local module_name = + v8_str("CreateSyntheticModule-TestSyntheticModuleGC"); + + for (int i = 0; i < 200; i++) { + Local module = v8::Module::CreateSyntheticModule( + isolate, module_name, export_names, + UnexpectedSyntheticModuleEvaluationStepsCallback); + USE(module); + } +} + TEST(SyntheticModuleSetExports) { LocalContext env; v8::Isolate* isolate = env->GetIsolate(); From 83861fb333ae189bc80ab97af664ee9132add552 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Thu, 28 Nov 2019 22:13:46 +0100 Subject: [PATCH 110/180] test: revert 6d022c13 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revert "test: skip test-domain-error-types in debug mode temporariliy" This reverts commit 6d022c13c52fc0134ed6c79be520be8c84c58cf8. PR-URL: https://github.com/nodejs/node/pull/30708 Reviewed-By: Rich Trott Reviewed-By: Michaël Zasso Reviewed-By: Colin Ihrig Reviewed-By: Jiawen Geng Reviewed-By: Gus Caplan Reviewed-By: Gireesh Punathil --- test/parallel/parallel.status | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/parallel/parallel.status b/test/parallel/parallel.status index 00ab4a774d29b9..444cf8d1154c92 100644 --- a/test/parallel/parallel.status +++ b/test/parallel/parallel.status @@ -33,7 +33,3 @@ test-async-hooks-http-parser-destroy: PASS,FLAKY [$system==freebsd] [$system==aix] - -[$mode==debug] -# https://github.com/nodejs/node/issues/30498 -test-domain-error-types: PASS,FLAKY From 456d250d2dcf42bd76bbb148bd258ee743ffc947 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= Date: Wed, 27 Nov 2019 15:35:23 +0100 Subject: [PATCH 111/180] deps: V8: backport 93f189f19a03 Original commit message: [ic] Fix non-GlobalIC store to interceptor on the global object We possibly need to load the global object from the global proxy as the holder of the named interceptor. Change-Id: I0f9f2e448630608ae853588f6751b55574a9efd9 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1930903 Commit-Queue: Igor Sheludko Reviewed-by: Igor Sheludko Cr-Commit-Position: refs/heads/master@{#65119} Refs: https://github.com/v8/v8/commit/93f189f19a030d5de6c5173711dca120ad76e5cd Fixes: https://github.com/nodejs/node/issues/30586 PR-URL: https://github.com/nodejs/node/pull/30681 Reviewed-By: Colin Ihrig Reviewed-By: Anna Henningsen --- common.gypi | 2 +- deps/v8/src/ic/accessor-assembler.cc | 17 ++--------------- deps/v8/src/ic/ic.cc | 22 +++++++++------------- 3 files changed, 12 insertions(+), 29 deletions(-) diff --git a/common.gypi b/common.gypi index 4fd333a45de84b..e063139a7d4662 100644 --- a/common.gypi +++ b/common.gypi @@ -39,7 +39,7 @@ # Reset this number to 0 on major V8 upgrades. # Increment by one for each non-official patch applied to deps/v8. - 'v8_embedder_string': '-node.21', + 'v8_embedder_string': '-node.22', ##### V8 defaults for Node.js ##### diff --git a/deps/v8/src/ic/accessor-assembler.cc b/deps/v8/src/ic/accessor-assembler.cc index 99cbd3c3c892b1..c9f86ed3282fbe 100644 --- a/deps/v8/src/ic/accessor-assembler.cc +++ b/deps/v8/src/ic/accessor-assembler.cc @@ -1053,8 +1053,7 @@ void AccessorAssembler::HandleStoreICHandlerCase( { Comment("store_interceptor"); TailCallRuntime(Runtime::kStorePropertyWithInterceptor, p->context(), - p->value(), p->slot(), p->vector(), p->receiver(), - p->name()); + p->value(), p->receiver(), p->name()); } BIND(&if_slow); @@ -1516,8 +1515,7 @@ void AccessorAssembler::HandleStoreICProtoHandler( { Label if_add_normal(this), if_store_global_proxy(this), if_api_setter(this), - if_accessor(this), if_native_data_property(this), if_slow(this), - if_interceptor(this); + if_accessor(this), if_native_data_property(this), if_slow(this); CSA_ASSERT(this, TaggedIsSmi(smi_handler)); TNode handler_word = SmiToInt32(CAST(smi_handler)); @@ -1547,9 +1545,6 @@ void AccessorAssembler::HandleStoreICProtoHandler( GotoIf(Word32Equal(handler_kind, Int32Constant(StoreHandler::kSlow)), &if_slow); - GotoIf(Word32Equal(handler_kind, Int32Constant(StoreHandler::kInterceptor)), - &if_interceptor); - GotoIf( Word32Equal(handler_kind, Int32Constant(StoreHandler::kApiSetterHolderIsPrototype)), @@ -1574,14 +1569,6 @@ void AccessorAssembler::HandleStoreICProtoHandler( } } - BIND(&if_interceptor); - { - Comment("store_interceptor"); - TailCallRuntime(Runtime::kStorePropertyWithInterceptor, p->context(), - p->value(), p->slot(), p->vector(), p->receiver(), - p->name()); - } - BIND(&if_add_normal); { // This is a case of "transitioning store" to a dictionary mode object diff --git a/deps/v8/src/ic/ic.cc b/deps/v8/src/ic/ic.cc index 4ac5fd7abefaa7..1b481cd8172b6e 100644 --- a/deps/v8/src/ic/ic.cc +++ b/deps/v8/src/ic/ic.cc @@ -1308,8 +1308,7 @@ bool StoreIC::LookupForWrite(LookupIterator* it, Handle value, case LookupIterator::INTERCEPTOR: { Handle holder = it->GetHolder(); InterceptorInfo info = holder->GetNamedInterceptor(); - if ((it->HolderIsReceiverOrHiddenPrototype() && - !info.non_masking()) || + if (it->HolderIsReceiverOrHiddenPrototype() || !info.getter().IsUndefined(isolate()) || !info.query().IsUndefined(isolate())) { return true; @@ -2718,23 +2717,20 @@ RUNTIME_FUNCTION(Runtime_LoadPropertyWithInterceptor) { RUNTIME_FUNCTION(Runtime_StorePropertyWithInterceptor) { HandleScope scope(isolate); - DCHECK_EQ(5, args.length()); + DCHECK_EQ(3, args.length()); // Runtime functions don't follow the IC's calling convention. Handle value = args.at(0); - Handle slot = args.at(1); - Handle vector = args.at(2); - Handle receiver = args.at(3); - Handle name = args.at(4); - FeedbackSlot vector_slot = FeedbackVector::ToSlot(slot->value()); + Handle receiver = args.at(1); + Handle name = args.at(2); // TODO(ishell): Cache interceptor_holder in the store handler like we do // for LoadHandler::kInterceptor case. Handle interceptor_holder = receiver; - if (receiver->IsJSGlobalProxy()) { - FeedbackSlotKind kind = vector->GetKind(vector_slot); - if (IsStoreGlobalICKind(kind)) { - interceptor_holder = Handle::cast(isolate->global_object()); - } + if (receiver->IsJSGlobalProxy() && + (!receiver->HasNamedInterceptor() || + receiver->GetNamedInterceptor().non_masking())) { + interceptor_holder = + handle(JSObject::cast(receiver->map().prototype()), isolate); } DCHECK(interceptor_holder->HasNamedInterceptor()); Handle interceptor(interceptor_holder->GetNamedInterceptor(), From d257448bca9e9241a0f8f1afea3001de501c3ae5 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Tue, 19 Nov 2019 18:45:41 -0800 Subject: [PATCH 112/180] deps: update llhttp to 2.0.1 Changelog: * Optional SSE4.2 support (at compile time) * Lenient mode of operation PR-URL: https://github.com/nodejs/node/pull/30553 Reviewed-By: Gus Caplan Reviewed-By: Jiawen Geng Reviewed-By: Anna Henningsen Reviewed-By: Ben Noordhuis Reviewed-By: David Carlier Reviewed-By: Colin Ihrig Reviewed-By: Myles Borins --- deps/llhttp/README.md | 9 +- deps/llhttp/include/llhttp.h | 25 ++- deps/llhttp/src/api.c | 9 + deps/llhttp/src/http.c | 4 +- deps/llhttp/src/llhttp.c | 374 +++++++++++++++++++++++++---------- 5 files changed, 306 insertions(+), 115 deletions(-) diff --git a/deps/llhttp/README.md b/deps/llhttp/README.md index 7010b90f7ad9e5..c6c061238127f3 100644 --- a/deps/llhttp/README.md +++ b/deps/llhttp/README.md @@ -14,6 +14,8 @@ This project aims to: * Verifiable * Improving benchmarks where possible +More details in [Fedor Indutny's talk at JSConf EU 2019](https://youtu.be/x3k_5Mi66sY) + ## How? Over time, different approaches for improving [http_parser][0]'s code base @@ -30,11 +32,10 @@ So far llhttp outperforms http_parser: | | input size | bandwidth | reqs/sec | time | |:----------------|-----------:|-------------:|-----------:|--------:| -| **llhttp** _(C)_ | 8192.00 mb | 1497.88 mb/s | 3020458.87 ops/sec | 5.47 s | -| **llhttp** _(bitcode)_ | 8192.00 mb | 1131.75 mb/s | 2282171.24 ops/sec | 7.24 s | +| **llhttp** _(C)_ | 8192.00 mb | 1777.24 mb/s | 3583799.39 ops/sec | 4.61 s | | **http_parser** | 8192.00 mb | 694.66 mb/s | 1406180.33 req/sec | 11.79 s | -llhttp is faster by approximately **116%**. +llhttp is faster by approximately **156%**. ## Maintenance @@ -77,8 +78,6 @@ settings.on_message_complete = handle_on_message_complete; */ llhttp_init(&parser, HTTP_BOTH, &settings); -/* Use `llhttp_set_type(&parser, HTTP_REQUEST);` to override the mode */ - /* Parse request! */ const char* request = "GET / HTTP/1.1\r\n\r\n"; int request_len = strlen(request); diff --git a/deps/llhttp/include/llhttp.h b/deps/llhttp/include/llhttp.h index 1671af4d088d26..719abe8aed2ba5 100644 --- a/deps/llhttp/include/llhttp.h +++ b/deps/llhttp/include/llhttp.h @@ -1,9 +1,9 @@ #ifndef INCLUDE_LLHTTP_H_ #define INCLUDE_LLHTTP_H_ -#define LLHTTP_VERSION_MAJOR 1 -#define LLHTTP_VERSION_MINOR 1 -#define LLHTTP_VERSION_PATCH 4 +#define LLHTTP_VERSION_MAJOR 2 +#define LLHTTP_VERSION_MINOR 0 +#define LLHTTP_VERSION_PATCH 1 #ifndef INCLUDE_LLHTTP_ITSELF_H_ #define INCLUDE_LLHTTP_ITSELF_H_ @@ -29,7 +29,7 @@ struct llhttp__internal_s { uint8_t http_major; uint8_t http_minor; uint8_t header_state; - uint8_t flags; + uint16_t flags; uint8_t upgrade; uint16_t status_code; uint8_t finish; @@ -85,7 +85,8 @@ enum llhttp_flags { F_UPGRADE = 0x10, F_CONTENT_LENGTH = 0x20, F_SKIPBODY = 0x40, - F_TRAILING = 0x80 + F_TRAILING = 0x80, + F_LENIENT = 0x100 }; typedef enum llhttp_flags llhttp_flags_t; @@ -297,7 +298,7 @@ llhttp_errno_t llhttp_finish(llhttp_t* parser); int llhttp_message_needs_eof(const llhttp_t* parser); /* Returns `1` if there might be any other messages following the last that was - * successfuly parsed. + * successfully parsed. */ int llhttp_should_keep_alive(const llhttp_t* parser); @@ -353,6 +354,18 @@ const char* llhttp_errno_name(llhttp_errno_t err); /* Returns textual name of HTTP method */ const char* llhttp_method_name(llhttp_method_t method); + +/* Enables/disables lenient header value parsing (disabled by default). + * + * Lenient parsing disables header value token checks, extending llhttp's + * protocol support to highly non-compliant clients/server. No + * `HPE_INVALID_HEADER_TOKEN` will be raised for incorrect header values when + * lenient parsing is "on". + * + * **(USE AT YOUR OWN RISK)** + */ +void llhttp_set_lenient(llhttp_t* parser, int enabled); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/deps/llhttp/src/api.c b/deps/llhttp/src/api.c index 45227b35afb209..6f7246546dfe1a 100644 --- a/deps/llhttp/src/api.c +++ b/deps/llhttp/src/api.c @@ -127,6 +127,15 @@ const char* llhttp_method_name(llhttp_method_t method) { } +void llhttp_set_lenient(llhttp_t* parser, int enabled) { + if (enabled) { + parser->flags |= F_LENIENT; + } else { + parser->flags &= ~F_LENIENT; + } +} + + /* Callbacks */ diff --git a/deps/llhttp/src/http.c b/deps/llhttp/src/http.c index 67834c2d377c49..65d2ee677e4d33 100644 --- a/deps/llhttp/src/http.c +++ b/deps/llhttp/src/http.c @@ -74,9 +74,11 @@ int llhttp__after_message_complete(llhttp_t* parser, const char* p, int should_keep_alive; should_keep_alive = llhttp_should_keep_alive(parser); - parser->flags = 0; parser->finish = HTTP_FINISH_SAFE; + /* Keep `F_LENIENT` flag between messages, but reset every other flag */ + parser->flags &= F_LENIENT; + /* NOTE: this is ignored in loose parsing mode */ return should_keep_alive; } diff --git a/deps/llhttp/src/llhttp.c b/deps/llhttp/src/llhttp.c index 2786638f3ed6d5..698230f93fe08f 100644 --- a/deps/llhttp/src/llhttp.c +++ b/deps/llhttp/src/llhttp.c @@ -2,6 +2,20 @@ #include #include +#ifdef __SSE4_2__ + #ifdef _MSC_VER + #include + #else /* !_MSC_VER */ + #include + #endif /* _MSC_VER */ +#endif /* __SSE4_2__ */ + +#ifdef _MSC_VER + #define ALIGN(n) _declspec(align(n)) +#else /* !_MSC_VER */ + #define ALIGN(n) __attribute__((aligned(n))) +#endif /* _MSC_VER */ + #include "llhttp.h" typedef int (*llhttp__internal__span_cb)( @@ -10,147 +24,161 @@ typedef int (*llhttp__internal__span_cb)( static const unsigned char llparse_blob0[] = { 'C', 'L' }; -static const unsigned char llparse_blob1[] = { - 'o', 'n' +static const unsigned char ALIGN(16) llparse_blob1[] = { + 0x9, 0x9, 0xc, 0xc, '!', '"', '$', '>', '@', '~', 0x80, + 0xff }; static const unsigned char llparse_blob2[] = { - 'e', 'c', 't', 'i', 'o', 'n' + 'o', 'n' }; static const unsigned char llparse_blob3[] = { - 'l', 'o', 's', 'e' + 'e', 'c', 't', 'i', 'o', 'n' }; static const unsigned char llparse_blob4[] = { - 'e', 'e', 'p', '-', 'a', 'l', 'i', 'v', 'e' + 'l', 'o', 's', 'e' }; static const unsigned char llparse_blob5[] = { - 'p', 'g', 'r', 'a', 'd', 'e' + 'e', 'e', 'p', '-', 'a', 'l', 'i', 'v', 'e' }; static const unsigned char llparse_blob6[] = { + 'p', 'g', 'r', 'a', 'd', 'e' +}; +static const unsigned char ALIGN(16) llparse_blob7[] = { + 0x9, 0x9, ' ', '~', 0x80, 0xfe +}; +static const unsigned char llparse_blob8[] = { 'h', 'u', 'n', 'k', 'e', 'd' }; -static const unsigned char llparse_blob7[] = { +static const unsigned char ALIGN(16) llparse_blob9[] = { + ' ', '!', '#', '\'', '*', '+', '-', '.', '0', '9', 'A', + 'Z', '^', 'z', '|', '|' +}; +static const unsigned char ALIGN(16) llparse_blob10[] = { + '~', '~' +}; +static const unsigned char llparse_blob11[] = { 'e', 'n', 't', '-', 'l', 'e', 'n', 'g', 't', 'h' }; -static const unsigned char llparse_blob8[] = { +static const unsigned char llparse_blob12[] = { 'r', 'o', 'x', 'y', '-', 'c', 'o', 'n', 'n', 'e', 'c', 't', 'i', 'o', 'n' }; -static const unsigned char llparse_blob9[] = { +static const unsigned char llparse_blob13[] = { 'r', 'a', 'n', 's', 'f', 'e', 'r', '-', 'e', 'n', 'c', 'o', 'd', 'i', 'n', 'g' }; -static const unsigned char llparse_blob10[] = { +static const unsigned char llparse_blob14[] = { 'p', 'g', 'r', 'a', 'd', 'e' }; -static const unsigned char llparse_blob11[] = { +static const unsigned char llparse_blob15[] = { 0xd, 0xa }; -static const unsigned char llparse_blob12[] = { +static const unsigned char llparse_blob16[] = { 'T', 'T', 'P', '/' }; -static const unsigned char llparse_blob13[] = { +static const unsigned char llparse_blob17[] = { 'C', 'E', '/' }; -static const unsigned char llparse_blob14[] = { +static const unsigned char llparse_blob18[] = { 'I', 'N', 'D' }; -static const unsigned char llparse_blob15[] = { +static const unsigned char llparse_blob19[] = { 'E', 'C', 'K', 'O', 'U', 'T' }; -static const unsigned char llparse_blob16[] = { +static const unsigned char llparse_blob20[] = { 'N', 'E', 'C', 'T' }; -static const unsigned char llparse_blob17[] = { +static const unsigned char llparse_blob21[] = { 'E', 'L', 'E', 'T', 'E' }; -static const unsigned char llparse_blob18[] = { +static const unsigned char llparse_blob22[] = { 'E', 'T' }; -static const unsigned char llparse_blob19[] = { +static const unsigned char llparse_blob23[] = { 'E', 'A', 'D' }; -static const unsigned char llparse_blob20[] = { +static const unsigned char llparse_blob24[] = { 'N', 'K' }; -static const unsigned char llparse_blob21[] = { +static const unsigned char llparse_blob25[] = { 'C', 'K' }; -static const unsigned char llparse_blob22[] = { +static const unsigned char llparse_blob26[] = { 'S', 'E', 'A', 'R', 'C', 'H' }; -static const unsigned char llparse_blob23[] = { +static const unsigned char llparse_blob27[] = { 'R', 'G', 'E' }; -static const unsigned char llparse_blob24[] = { +static const unsigned char llparse_blob28[] = { 'C', 'T', 'I', 'V', 'I', 'T', 'Y' }; -static const unsigned char llparse_blob25[] = { +static const unsigned char llparse_blob29[] = { 'L', 'E', 'N', 'D', 'A', 'R' }; -static const unsigned char llparse_blob26[] = { +static const unsigned char llparse_blob30[] = { 'V', 'E' }; -static const unsigned char llparse_blob27[] = { +static const unsigned char llparse_blob31[] = { 'O', 'T', 'I', 'F', 'Y' }; -static const unsigned char llparse_blob28[] = { +static const unsigned char llparse_blob32[] = { 'P', 'T', 'I', 'O', 'N', 'S' }; -static const unsigned char llparse_blob29[] = { +static const unsigned char llparse_blob33[] = { 'T', 'C', 'H' }; -static const unsigned char llparse_blob30[] = { +static const unsigned char llparse_blob34[] = { 'S', 'T' }; -static const unsigned char llparse_blob31[] = { +static const unsigned char llparse_blob35[] = { 'O', 'P' }; -static const unsigned char llparse_blob32[] = { +static const unsigned char llparse_blob36[] = { 'I', 'N', 'D' }; -static const unsigned char llparse_blob33[] = { +static const unsigned char llparse_blob37[] = { 'A', 'T', 'C', 'H' }; -static const unsigned char llparse_blob34[] = { +static const unsigned char llparse_blob38[] = { 'G', 'E' }; -static const unsigned char llparse_blob35[] = { +static const unsigned char llparse_blob39[] = { 'I', 'N', 'D' }; -static const unsigned char llparse_blob36[] = { +static const unsigned char llparse_blob40[] = { 'O', 'R', 'T' }; -static const unsigned char llparse_blob37[] = { +static const unsigned char llparse_blob41[] = { 'A', 'R', 'C', 'H' }; -static const unsigned char llparse_blob38[] = { +static const unsigned char llparse_blob42[] = { 'U', 'R', 'C', 'E' }; -static const unsigned char llparse_blob39[] = { +static const unsigned char llparse_blob43[] = { 'B', 'S', 'C', 'R', 'I', 'B', 'E' }; -static const unsigned char llparse_blob40[] = { +static const unsigned char llparse_blob44[] = { 'R', 'A', 'C', 'E' }; -static const unsigned char llparse_blob41[] = { +static const unsigned char llparse_blob45[] = { 'I', 'N', 'D' }; -static const unsigned char llparse_blob42[] = { +static const unsigned char llparse_blob46[] = { 'N', 'K' }; -static const unsigned char llparse_blob43[] = { +static const unsigned char llparse_blob47[] = { 'C', 'K' }; -static const unsigned char llparse_blob44[] = { +static const unsigned char llparse_blob48[] = { 'U', 'B', 'S', 'C', 'R', 'I', 'B', 'E' }; -static const unsigned char llparse_blob45[] = { +static const unsigned char llparse_blob49[] = { 'H', 'T', 'T', 'P', '/' }; -static const unsigned char llparse_blob46[] = { +static const unsigned char llparse_blob50[] = { 'A', 'D' }; -static const unsigned char llparse_blob47[] = { +static const unsigned char llparse_blob51[] = { 'T', 'P', '/' }; @@ -259,6 +287,7 @@ enum llparse_state_e { s_n_llhttp__internal__n_header_value_discard_ws_almost_done, s_n_llhttp__internal__n_header_value_lws, s_n_llhttp__internal__n_header_value_almost_done, + s_n_llhttp__internal__n_header_value_lenient, s_n_llhttp__internal__n_header_value_otherwise, s_n_llhttp__internal__n_header_value_connection_token, s_n_llhttp__internal__n_header_value_connection_ws, @@ -655,6 +684,13 @@ int llhttp__internal__c_update_header_state_2( return 0; } +int llhttp__internal__c_test_flags_2( + llhttp__internal_t* state, + const unsigned char* p, + const unsigned char* endp) { + return (state->flags & 256) == 256; +} + int llhttp__internal__c_update_header_state_4( llhttp__internal_t* state, const unsigned char* p, @@ -679,7 +715,7 @@ int llhttp__internal__c_update_header_state_6( return 0; } -int llhttp__internal__c_test_flags_2( +int llhttp__internal__c_test_flags_3( llhttp__internal_t* state, const unsigned char* p, const unsigned char* endp) { @@ -1394,6 +1430,26 @@ static llparse_state_t llhttp__internal__run( /* UNREACHABLE */; abort(); } + case s_n_llhttp__internal__n_header_value_lenient: + s_n_llhttp__internal__n_header_value_lenient: { + if (p == endp) { + return s_n_llhttp__internal__n_header_value_lenient; + } + switch (*p) { + case 10: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_1; + } + case 13: { + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_3; + } + default: { + p++; + goto s_n_llhttp__internal__n_header_value_lenient; + } + } + /* UNREACHABLE */; + abort(); + } case s_n_llhttp__internal__n_header_value_otherwise: s_n_llhttp__internal__n_header_value_otherwise: { if (p == endp) { @@ -1407,7 +1463,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_2; } default: { - goto s_n_llhttp__internal__n_error_13; + goto s_n_llhttp__internal__n_invoke_test_flags_2; } } /* UNREACHABLE */; @@ -1486,7 +1542,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_header_value_connection_1; } - match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob3, 4); + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob4, 4); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -1510,7 +1566,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_header_value_connection_2; } - match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob4, 9); + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob5, 9); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -1534,7 +1590,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_header_value_connection_3; } - match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob5, 6); + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob6, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -1617,6 +1673,30 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_header_value; } + #ifdef __SSE4_2__ + if (endp - p >= 16) { + __m128i ranges; + __m128i input; + int avail; + int match_len; + + /* Load input */ + input = _mm_loadu_si128((__m128i const*) p); + ranges = _mm_loadu_si128((__m128i const*) llparse_blob7); + + /* Find first character that does not match `ranges` */ + match_len = _mm_cmpestri(ranges, 6, + input, 16, + _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | + _SIDD_NEGATIVE_POLARITY); + + if (match_len != 0) { + p += match_len; + goto s_n_llhttp__internal__n_header_value; + } + goto s_n_llhttp__internal__n_header_value_otherwise; + } + #endif /* __SSE4_2__ */ switch (lookup_table[(uint8_t) *p]) { case 1: { p++; @@ -1679,7 +1759,7 @@ static llparse_state_t llhttp__internal__run( goto s_n_llhttp__internal__n_header_value_content_length_ws; } default: { - goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_4; + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_5; } } /* UNREACHABLE */; @@ -1755,7 +1835,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_header_value_te_chunked_1; } - match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob6, 6); + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob8, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -1877,6 +1957,42 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_header_field_general; } + #ifdef __SSE4_2__ + if (endp - p >= 16) { + __m128i ranges; + __m128i input; + int avail; + int match_len; + + /* Load input */ + input = _mm_loadu_si128((__m128i const*) p); + ranges = _mm_loadu_si128((__m128i const*) llparse_blob9); + + /* Find first character that does not match `ranges` */ + match_len = _mm_cmpestri(ranges, 16, + input, 16, + _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | + _SIDD_NEGATIVE_POLARITY); + + if (match_len != 0) { + p += match_len; + goto s_n_llhttp__internal__n_header_field_general; + } + ranges = _mm_loadu_si128((__m128i const*) llparse_blob10); + + /* Find first character that does not match `ranges` */ + match_len = _mm_cmpestri(ranges, 2, + input, 16, + _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | + _SIDD_NEGATIVE_POLARITY); + + if (match_len != 0) { + p += match_len; + goto s_n_llhttp__internal__n_header_field_general; + } + goto s_n_llhttp__internal__n_header_field_general_otherwise; + } + #endif /* __SSE4_2__ */ switch (lookup_table[(uint8_t) *p]) { case 1: { p++; @@ -1916,7 +2032,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_header_field_3; } - match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob2, 6); + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob3, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -1941,7 +2057,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_header_field_4; } - match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob7, 10); + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob11, 10); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -1987,7 +2103,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_header_field_1; } - match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob1, 2); + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob2, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -2011,7 +2127,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_header_field_5; } - match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob8, 15); + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob12, 15); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -2036,7 +2152,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_header_field_6; } - match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob9, 16); + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob13, 16); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -2061,7 +2177,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_header_field_7; } - match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob10, 6); + match_seq = llparse__match_sequence_to_lower_unsafe(state, p, endp, llparse_blob14, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -2156,7 +2272,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_url_skip_lf_to_http09; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob11, 2); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob15, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -2361,7 +2477,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_req_http_start_1; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob12, 4); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob16, 4); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -2385,7 +2501,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_req_http_start_2; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob13, 3); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob17, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -2591,6 +2707,30 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_url_path; } + #ifdef __SSE4_2__ + if (endp - p >= 16) { + __m128i ranges; + __m128i input; + int avail; + int match_len; + + /* Load input */ + input = _mm_loadu_si128((__m128i const*) p); + ranges = _mm_loadu_si128((__m128i const*) llparse_blob1); + + /* Find first character that does not match `ranges` */ + match_len = _mm_cmpestri(ranges, 12, + input, 16, + _SIDD_UBYTE_OPS | _SIDD_CMP_RANGES | + _SIDD_NEGATIVE_POLARITY); + + if (match_len != 0) { + p += match_len; + goto s_n_llhttp__internal__n_url_path; + } + goto s_n_llhttp__internal__n_url_query_or_fragment; + } + #endif /* __SSE4_2__ */ switch (lookup_table[(uint8_t) *p]) { case 1: { p++; @@ -2970,7 +3110,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_2; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob14, 3); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob18, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -2995,7 +3135,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_4; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob15, 6); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob19, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3020,7 +3160,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_6; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob16, 4); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob20, 4); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3105,7 +3245,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_8; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob17, 5); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob21, 5); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3130,7 +3270,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_9; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob18, 2); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob22, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3155,7 +3295,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_10; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob19, 3); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob23, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3180,7 +3320,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_12; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob20, 2); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob24, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3205,7 +3345,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_13; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob21, 2); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob25, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3251,7 +3391,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_15; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob22, 6); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob26, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3276,7 +3416,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_16; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob23, 3); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob27, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3301,7 +3441,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_18; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob24, 7); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob28, 7); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3326,7 +3466,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_20; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob25, 6); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob29, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3411,7 +3551,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_22; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob26, 2); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob30, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3465,7 +3605,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_23; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob27, 5); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob31, 5); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3490,7 +3630,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_24; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob28, 6); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob32, 6); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3515,7 +3655,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_26; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob29, 3); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob33, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3540,7 +3680,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_27; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob30, 2); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob34, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3565,7 +3705,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_30; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob32, 3); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob36, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3590,7 +3730,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_31; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob33, 4); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob37, 4); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3636,7 +3776,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_28; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob31, 2); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob35, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3660,7 +3800,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_33; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob34, 2); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob38, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3736,7 +3876,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_36; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob35, 3); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob39, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3761,7 +3901,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_37; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob36, 3); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob40, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3824,7 +3964,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_39; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob37, 4); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob41, 4); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3849,7 +3989,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_40; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob38, 4); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob42, 4); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3874,7 +4014,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_41; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob39, 7); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob43, 7); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3924,7 +4064,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_42; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob40, 4); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob44, 4); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3949,7 +4089,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_45; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob41, 3); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob45, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3974,7 +4114,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_47; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob42, 2); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob46, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -3999,7 +4139,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_48; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob43, 2); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob47, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -4045,7 +4185,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_req_49; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob44, 8); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob48, 8); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -4493,7 +4633,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_start_res; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob45, 5); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob49, 5); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -4517,7 +4657,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_req_or_res_method_2; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob46, 2); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob50, 2); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -4542,7 +4682,7 @@ static llparse_state_t llhttp__internal__run( if (p == endp) { return s_n_llhttp__internal__n_req_or_res_method_3; } - match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob47, 3); + match_seq = llparse__match_sequence_id(state, p, endp, llparse_blob51, 3); p = match_seq.current; switch (match_seq.status) { case kMatchComplete: { @@ -5191,6 +5331,24 @@ static llparse_state_t llhttp__internal__run( /* UNREACHABLE */; abort(); } + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_3: { + const unsigned char* start; + int err; + + start = state->_span_pos0; + state->_span_pos0 = NULL; + err = llhttp__on_header_value(state, start, p); + if (err != 0) { + state->error = err; + state->error_pos = (const char*) (p + 1); + state->_current = (void*) (intptr_t) s_n_llhttp__internal__n_header_value_almost_done; + return s_error; + } + p++; + goto s_n_llhttp__internal__n_header_value_almost_done; + /* UNREACHABLE */; + abort(); + } s_n_llhttp__internal__n_error_13: { state->error = 0xa; state->reason = "Invalid header value char"; @@ -5200,6 +5358,16 @@ static llparse_state_t llhttp__internal__run( /* UNREACHABLE */; abort(); } + s_n_llhttp__internal__n_invoke_test_flags_2: { + switch (llhttp__internal__c_test_flags_2(state, p, endp)) { + case 1: + goto s_n_llhttp__internal__n_header_value_lenient; + default: + goto s_n_llhttp__internal__n_error_13; + } + /* UNREACHABLE */; + abort(); + } s_n_llhttp__internal__n_invoke_update_header_state_3: { switch (llhttp__internal__c_update_header_state(state, p, endp)) { default: @@ -5288,7 +5456,7 @@ static llparse_state_t llhttp__internal__run( /* UNREACHABLE */; abort(); } - s_n_llhttp__internal__n_span_end_llhttp__on_header_value_3: { + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_4: { const unsigned char* start; int err; @@ -5308,7 +5476,7 @@ static llparse_state_t llhttp__internal__run( s_n_llhttp__internal__n_invoke_mul_add_content_length_1: { switch (llhttp__internal__c_mul_add_content_length_1(state, p, endp, match)) { case 1: - goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_3; + goto s_n_llhttp__internal__n_span_end_llhttp__on_header_value_4; default: goto s_n_llhttp__internal__n_header_value_content_length; } @@ -5331,7 +5499,7 @@ static llparse_state_t llhttp__internal__run( /* UNREACHABLE */; abort(); } - s_n_llhttp__internal__n_span_end_llhttp__on_header_value_4: { + s_n_llhttp__internal__n_span_end_llhttp__on_header_value_5: { const unsigned char* start; int err; @@ -5357,8 +5525,8 @@ static llparse_state_t llhttp__internal__run( /* UNREACHABLE */; abort(); } - s_n_llhttp__internal__n_invoke_test_flags_2: { - switch (llhttp__internal__c_test_flags_2(state, p, endp)) { + s_n_llhttp__internal__n_invoke_test_flags_3: { + switch (llhttp__internal__c_test_flags_3(state, p, endp)) { case 0: goto s_n_llhttp__internal__n_header_value_content_length; default: @@ -5388,7 +5556,7 @@ static llparse_state_t llhttp__internal__run( case 1: goto s_n_llhttp__internal__n_header_value_connection; case 2: - goto s_n_llhttp__internal__n_invoke_test_flags_2; + goto s_n_llhttp__internal__n_invoke_test_flags_3; case 3: goto s_n_llhttp__internal__n_header_value_te_chunked; case 4: From 1d1d1368068dca79bbb06430875c71aff0c1937c Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Thu, 21 Nov 2019 00:14:14 +0100 Subject: [PATCH 113/180] http: set socket.server unconditionally This is useful for situations in which the socket was not created for HTTP, e.g. when using arbitrary `Duplex` streams. (The added test fails because previously, `socket.server.emit()` would not work for emitting the `clientError` event, as `socket.server` was `undefined`.) PR-URL: https://github.com/nodejs/node/pull/30571 Reviewed-By: Yongsheng Zhang Reviewed-By: Luigi Pinca Reviewed-By: Trivikram Kamat Reviewed-By: Colin Ihrig --- lib/_http_server.js | 3 +-- test/parallel/test-http-generic-streams.js | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/_http_server.js b/lib/_http_server.js index 59d1960297ae9c..09348612d8eae6 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -365,8 +365,7 @@ function connectionListenerInternal(server, socket) { // Ensure that the server property of the socket is correctly set. // See https://github.com/nodejs/node/issues/13435 - if (socket.server === null) - socket.server = server; + socket.server = server; // If the user has added a listener to the server, // request, or response, then it's their responsibility. diff --git a/test/parallel/test-http-generic-streams.js b/test/parallel/test-http-generic-streams.js index aea371432a3d07..706cba7b385957 100644 --- a/test/parallel/test-http-generic-streams.js +++ b/test/parallel/test-http-generic-streams.js @@ -138,3 +138,17 @@ const MakeDuplexPair = require('../common/duplexpair'); req.write(testData); req.end(); } + +// Test 5: The client sends garbage. +{ + const server = http.createServer(common.mustNotCall()); + + const { clientSide, serverSide } = MakeDuplexPair(); + server.emit('connection', serverSide); + + server.on('clientError', common.mustCall()); + + // Send something that is not an HTTP request. + clientSide.end( + 'I’m reading a book about anti-gravity. It’s impossible to put down!'); +} From 30a4f68a150af4678ef0ea1d3afcfb568aa00190 Mon Sep 17 00:00:00 2001 From: cjihrig Date: Tue, 26 Nov 2019 13:37:31 -0500 Subject: [PATCH 114/180] child_process: document kill() return value This commit documents the return value from subprocess.kill(). PR-URL: https://github.com/nodejs/node/pull/30669 Refs: https://github.com/nodejs/node/issues/30668 Reviewed-By: Luigi Pinca Reviewed-By: Gireesh Punathil Reviewed-By: James M Snell --- doc/api/child_process.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/api/child_process.md b/doc/api/child_process.md index 7a025f94fade93..746de9bd2c8b7b 100644 --- a/doc/api/child_process.md +++ b/doc/api/child_process.md @@ -1061,10 +1061,12 @@ added: v0.1.90 --> * `signal` {number|string} +* Returns: {boolean} The `subprocess.kill()` method sends a signal to the child process. If no argument is given, the process will be sent the `'SIGTERM'` signal. See -signal(7) for a list of available signals. +signal(7) for a list of available signals. This function returns `true` if +kill(2) succeeds, and `false` otherwise. ```js const { spawn } = require('child_process'); From 0b0f0237c1dc77ac5b73324791178de223f687b4 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Tue, 19 Nov 2019 20:06:50 +0100 Subject: [PATCH 115/180] tls: add memory tracking support to SSLWrap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/30548 Reviewed-By: Ben Noordhuis Reviewed-By: David Carlier Reviewed-By: James M Snell Reviewed-By: Tobias Nießen --- src/node_crypto.cc | 7 +++++++ src/node_crypto.h | 2 ++ src/tls_wrap.cc | 1 + 3 files changed, 10 insertions(+) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index c4fa6e90200f5b..8f80db52297e4a 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -144,6 +144,7 @@ template void SSLWrap::AddMethods(Environment* env, template void SSLWrap::ConfigureSecureContext(SecureContext* sc); template void SSLWrap::SetSNIContext(SecureContext* sc); template int SSLWrap::SetCACerts(SecureContext* sc); +template void SSLWrap::MemoryInfo(MemoryTracker* tracker) const; template SSL_SESSION* SSLWrap::GetSessionCallback( SSL* s, const unsigned char* key, @@ -3074,6 +3075,12 @@ int SSLWrap::SetCACerts(SecureContext* sc) { return 1; } +template +void SSLWrap::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("ocsp_response", ocsp_response_); + tracker->TrackField("sni_context", sni_context_); +} + int VerifyCallback(int preverify_ok, X509_STORE_CTX* ctx) { // From https://www.openssl.org/docs/man1.1.1/man3/SSL_verify_cb: // diff --git a/src/node_crypto.h b/src/node_crypto.h index 56a9ad3104dbeb..d2bdd40ed283ed 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -222,6 +222,8 @@ class SSLWrap { inline bool is_awaiting_new_session() const { return awaiting_new_session_; } inline bool is_waiting_cert_cb() const { return cert_cb_ != nullptr; } + void MemoryInfo(MemoryTracker* tracker) const; + protected: typedef void (*CertCb)(void* arg); diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index 4ec6dda6df70d7..626662c9a5ef8e 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -1089,6 +1089,7 @@ void TLSWrap::GetWriteQueueSize(const FunctionCallbackInfo& info) { void TLSWrap::MemoryInfo(MemoryTracker* tracker) const { + SSLWrap::MemoryInfo(tracker); tracker->TrackField("error", error_); tracker->TrackFieldWithSize("pending_cleartext_input", pending_cleartext_input_.size(), From 7bd587ef0cd574cd6e6e44888217b8d817d0d912 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Tue, 19 Nov 2019 20:07:00 +0100 Subject: [PATCH 116/180] src: use BaseObjectPtr to store SNI context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than relying on a link to the JS object, store a pointer to the C++ object directly. PR-URL: https://github.com/nodejs/node/pull/30548 Reviewed-By: Ben Noordhuis Reviewed-By: David Carlier Reviewed-By: James M Snell Reviewed-By: Tobias Nießen --- src/node_crypto.cc | 12 +++++++++--- src/node_crypto.h | 2 +- src/tls_wrap.cc | 3 +-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 8f80db52297e4a..91704732d18992 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -2991,9 +2991,15 @@ void SSLWrap::CertCbDone(const FunctionCallbackInfo& args) { goto fire_cb; if (cons->HasInstance(ctx)) { - SecureContext* sc; - ASSIGN_OR_RETURN_UNWRAP(&sc, ctx.As()); - w->sni_context_.Reset(env->isolate(), ctx); + SecureContext* sc = Unwrap(ctx.As()); + CHECK_NOT_NULL(sc); + // XXX: There is a method w->SetSNIContext(sc), and you might think that + // it makes sense to call that here and make setting w->sni_context_ part + // of it. In fact, that passes the test suite, although SetSNIContext() + // performs a lot more operations. + // If anybody is familiar enough with the TLS code to know whether it makes + // sense, please do so or document why it doesn't. + w->sni_context_ = BaseObjectPtr(sc); int rv; diff --git a/src/node_crypto.h b/src/node_crypto.h index d2bdd40ed283ed..96292b42780f19 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -310,7 +310,7 @@ class SSLWrap { ClientHelloParser hello_parser_; v8::Global ocsp_response_; - v8::Global sni_context_; + BaseObjectPtr sni_context_; friend class SecureContext; }; diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index 626662c9a5ef8e..bacb1a0f27ee79 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -1065,10 +1065,9 @@ int TLSWrap::SelectSNIContextCallback(SSL* s, int* ad, void* arg) { return SSL_TLSEXT_ERR_NOACK; } - p->sni_context_.Reset(env->isolate(), ctx); - SecureContext* sc = Unwrap(ctx.As()); CHECK_NOT_NULL(sc); + p->sni_context_ = BaseObjectPtr(sc); p->SetSNIContext(sc); return SSL_TLSEXT_ERR_OK; } From fc11db18fec5b4632ef26d084bd978335fa80664 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Tue, 19 Nov 2019 22:15:17 +0100 Subject: [PATCH 117/180] src: inline SetSNICallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs: https://github.com/nodejs/node/pull/30548#discussion_r348168855 PR-URL: https://github.com/nodejs/node/pull/30548 Reviewed-By: Ben Noordhuis Reviewed-By: David Carlier Reviewed-By: James M Snell Reviewed-By: Tobias Nießen --- src/node_crypto.cc | 17 +---------------- src/node_crypto.h | 1 - src/tls_wrap.cc | 6 +++++- 3 files changed, 6 insertions(+), 18 deletions(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 91704732d18992..4b5e512102835a 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -142,7 +142,6 @@ static bool extra_root_certs_loaded = false; template void SSLWrap::AddMethods(Environment* env, Local t); template void SSLWrap::ConfigureSecureContext(SecureContext* sc); -template void SSLWrap::SetSNIContext(SecureContext* sc); template int SSLWrap::SetCACerts(SecureContext* sc); template void SSLWrap::MemoryInfo(MemoryTracker* tracker) const; template SSL_SESSION* SSLWrap::GetSessionCallback( @@ -2993,12 +2992,7 @@ void SSLWrap::CertCbDone(const FunctionCallbackInfo& args) { if (cons->HasInstance(ctx)) { SecureContext* sc = Unwrap(ctx.As()); CHECK_NOT_NULL(sc); - // XXX: There is a method w->SetSNIContext(sc), and you might think that - // it makes sense to call that here and make setting w->sni_context_ part - // of it. In fact, that passes the test suite, although SetSNIContext() - // performs a lot more operations. - // If anybody is familiar enough with the TLS code to know whether it makes - // sense, please do so or document why it doesn't. + // Store the SNI context for later use. w->sni_context_ = BaseObjectPtr(sc); int rv; @@ -3057,15 +3051,6 @@ void SSLWrap::DestroySSL() { } -template -void SSLWrap::SetSNIContext(SecureContext* sc) { - ConfigureSecureContext(sc); - CHECK_EQ(SSL_set_SSL_CTX(ssl_.get(), sc->ctx_.get()), sc->ctx_.get()); - - SetCACerts(sc); -} - - template int SSLWrap::SetCACerts(SecureContext* sc) { int err = SSL_set1_verify_cert_store(ssl_.get(), diff --git a/src/node_crypto.h b/src/node_crypto.h index 96292b42780f19..cd34c309c53913 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -288,7 +288,6 @@ class SSLWrap { void DestroySSL(); void WaitForCertCb(CertCb cb, void* arg); - void SetSNIContext(SecureContext* sc); int SetCACerts(SecureContext* sc); inline Environment* ssl_env() const { diff --git a/src/tls_wrap.cc b/src/tls_wrap.cc index bacb1a0f27ee79..cd7a5d59ebd842 100644 --- a/src/tls_wrap.cc +++ b/src/tls_wrap.cc @@ -1068,7 +1068,11 @@ int TLSWrap::SelectSNIContextCallback(SSL* s, int* ad, void* arg) { SecureContext* sc = Unwrap(ctx.As()); CHECK_NOT_NULL(sc); p->sni_context_ = BaseObjectPtr(sc); - p->SetSNIContext(sc); + + p->ConfigureSecureContext(sc); + CHECK_EQ(SSL_set_SSL_CTX(p->ssl_.get(), sc->ctx_.get()), sc->ctx_.get()); + p->SetCACerts(sc); + return SSL_TLSEXT_ERR_OK; } From 4affc30a12dfdada840606c43c4fe4b6e6c3db1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Mon, 25 Nov 2019 16:44:43 -0400 Subject: [PATCH 118/180] crypto: automatically manage memory for ECDSA_SIG Refs: https://github.com/nodejs/node/pull/29292 PR-URL: https://github.com/nodejs/node/pull/30641 Reviewed-By: Colin Ihrig Reviewed-By: David Carlier Reviewed-By: Ben Noordhuis Reviewed-By: Anna Henningsen --- src/node_crypto.cc | 19 ++++++++----------- src/node_crypto.h | 1 + 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 4b5e512102835a..d1bd5471f3fba2 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -5039,20 +5039,18 @@ static AllocatedBuffer ConvertSignatureToP1363(Environment* env, const unsigned char* sig_data = reinterpret_cast(signature.data()); - ECDSA_SIG* asn1_sig = d2i_ECDSA_SIG(nullptr, &sig_data, signature.size()); - if (asn1_sig == nullptr) + ECDSASigPointer asn1_sig(d2i_ECDSA_SIG(nullptr, &sig_data, signature.size())); + if (!asn1_sig) return AllocatedBuffer(); AllocatedBuffer buf = env->AllocateManaged(2 * n); unsigned char* data = reinterpret_cast(buf.data()); - const BIGNUM* r = ECDSA_SIG_get0_r(asn1_sig); - const BIGNUM* s = ECDSA_SIG_get0_s(asn1_sig); + const BIGNUM* r = ECDSA_SIG_get0_r(asn1_sig.get()); + const BIGNUM* s = ECDSA_SIG_get0_s(asn1_sig.get()); CHECK_EQ(n, static_cast(BN_bn2binpad(r, data, n))); CHECK_EQ(n, static_cast(BN_bn2binpad(s, data + n, n))); - ECDSA_SIG_free(asn1_sig); - return buf; } @@ -5069,19 +5067,18 @@ static ByteSource ConvertSignatureToDER( if (signature.length() != 2 * n) return ByteSource(); - ECDSA_SIG* asn1_sig = ECDSA_SIG_new(); - CHECK_NOT_NULL(asn1_sig); + ECDSASigPointer asn1_sig(ECDSA_SIG_new()); + CHECK(asn1_sig); BIGNUM* r = BN_new(); CHECK_NOT_NULL(r); BIGNUM* s = BN_new(); CHECK_NOT_NULL(s); CHECK_EQ(r, BN_bin2bn(sig_data, n, r)); CHECK_EQ(s, BN_bin2bn(sig_data + n, n, s)); - CHECK_EQ(1, ECDSA_SIG_set0(asn1_sig, r, s)); + CHECK_EQ(1, ECDSA_SIG_set0(asn1_sig.get(), r, s)); unsigned char* data = nullptr; - int len = i2d_ECDSA_SIG(asn1_sig, &data); - ECDSA_SIG_free(asn1_sig); + int len = i2d_ECDSA_SIG(asn1_sig.get(), &data); if (len <= 0) return ByteSource(); diff --git a/src/node_crypto.h b/src/node_crypto.h index cd34c309c53913..3f14e155dafc49 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -72,6 +72,7 @@ using ECGroupPointer = DeleteFnPtr; using ECPointPointer = DeleteFnPtr; using ECKeyPointer = DeleteFnPtr; using DHPointer = DeleteFnPtr; +using ECDSASigPointer = DeleteFnPtr; extern int VerifyCallback(int preverify_ok, X509_STORE_CTX* ctx); From 80ada94cd36f72d1a5791bcfeb6de87dfddc5687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Mon, 25 Nov 2019 17:00:44 -0400 Subject: [PATCH 119/180] build: use Node.js instead of Node in configure PR-URL: https://github.com/nodejs/node/pull/30642 Reviewed-By: Richard Lau Reviewed-By: Colin Ihrig Reviewed-By: Gus Caplan Reviewed-By: Jiawen Geng Reviewed-By: Trivikram Kamat Reviewed-By: David Carlier Reviewed-By: Luigi Pinca --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index f199a353482bec..39decd9a55ccc7 100755 --- a/configure +++ b/configure @@ -20,7 +20,7 @@ del _ import sys from distutils.spawn import find_executable -print('Node configure: Found Python {0}.{1}.{2}...'.format(*sys.version_info)) +print('Node.js configure: Found Python {0}.{1}.{2}...'.format(*sys.version_info)) acceptable_pythons = ((3, 8), (3, 7), (3, 6), (3, 5), (2, 7)) if sys.version_info[:2] in acceptable_pythons: import configure From b470354057126f166d1e966059daf5a77ca52567 Mon Sep 17 00:00:00 2001 From: Myles Borins Date: Wed, 27 Nov 2019 03:25:05 -0500 Subject: [PATCH 120/180] deps: patch V8 to 7.9.317.25 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs: https://github.com/v8/v8/compare/7.9.317.23...7.9.317.25 PR-URL: https://github.com/nodejs/node/pull/30679 Reviewed-By: Michaël Zasso Reviewed-By: Jiawen Geng Reviewed-By: Colin Ihrig --- deps/v8/include/v8-version.h | 2 +- deps/v8/src/builtins/builtins-wasm-gen.cc | 27 ++++++++------ deps/v8/src/compiler/heap-refs.h | 1 + deps/v8/src/compiler/js-call-reducer.cc | 11 ++++-- deps/v8/src/compiler/map-inference.cc | 7 ++++ deps/v8/src/compiler/map-inference.h | 1 + .../mjsunit/regress/regress-crbug-1024758.js | 37 +++++++++++++++++++ 7 files changed, 69 insertions(+), 17 deletions(-) create mode 100644 deps/v8/test/mjsunit/regress/regress-crbug-1024758.js diff --git a/deps/v8/include/v8-version.h b/deps/v8/include/v8-version.h index 8970c573efda6f..e11961895d7f34 100644 --- a/deps/v8/include/v8-version.h +++ b/deps/v8/include/v8-version.h @@ -11,7 +11,7 @@ #define V8_MAJOR_VERSION 7 #define V8_MINOR_VERSION 9 #define V8_BUILD_NUMBER 317 -#define V8_PATCH_LEVEL 23 +#define V8_PATCH_LEVEL 25 // Use 1 for candidates and 0 otherwise. // (Boolean macro values are not supported by all preprocessors.) diff --git a/deps/v8/src/builtins/builtins-wasm-gen.cc b/deps/v8/src/builtins/builtins-wasm-gen.cc index 12270495c133e9..d6346fb9aa439e 100644 --- a/deps/v8/src/builtins/builtins-wasm-gen.cc +++ b/deps/v8/src/builtins/builtins-wasm-gen.cc @@ -121,18 +121,19 @@ TF_BUILTIN(WasmAtomicNotify, WasmBuiltinsAssembler) { TNode centry = LoadCEntryFromInstance(instance); TNode target = LoadBuiltinFromFrame(Builtins::kAllocateHeapNumber); + TNode context = LoadContextFromInstance(instance); // TODO(aseemgarg): Use SMIs if possible for address and count TNode address_heap = UncheckedCast( - CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); + CallStub(AllocateHeapNumberDescriptor(), target, context)); StoreHeapNumberValue(address_heap, ChangeUint32ToFloat64(address)); TNode count_heap = UncheckedCast( - CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); + CallStub(AllocateHeapNumberDescriptor(), target, context)); StoreHeapNumberValue(count_heap, ChangeUint32ToFloat64(count)); TNode result_smi = UncheckedCast(CallRuntimeWithCEntry( - Runtime::kWasmAtomicNotify, centry, NoContextConstant(), instance, + Runtime::kWasmAtomicNotify, centry, context, instance, address_heap, count_heap)); ReturnRaw(SmiToInt32(result_smi)); } @@ -149,23 +150,24 @@ TF_BUILTIN(WasmI32AtomicWait, WasmBuiltinsAssembler) { TNode centry = LoadCEntryFromInstance(instance); TNode target = LoadBuiltinFromFrame(Builtins::kAllocateHeapNumber); + TNode context = LoadContextFromInstance(instance); // TODO(aseemgarg): Use SMIs if possible for address and expected_value TNode address_heap = UncheckedCast( - CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); + CallStub(AllocateHeapNumberDescriptor(), target, context)); StoreHeapNumberValue(address_heap, ChangeUint32ToFloat64(address)); TNode expected_value_heap = UncheckedCast( - CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); + CallStub(AllocateHeapNumberDescriptor(), target, context)); StoreHeapNumberValue(expected_value_heap, ChangeInt32ToFloat64(expected_value)); TNode timeout_heap = UncheckedCast( - CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); + CallStub(AllocateHeapNumberDescriptor(), target, context)); StoreHeapNumberValue(timeout_heap, timeout); TNode result_smi = UncheckedCast(CallRuntimeWithCEntry( - Runtime::kWasmI32AtomicWait, centry, NoContextConstant(), instance, + Runtime::kWasmI32AtomicWait, centry, context, instance, address_heap, expected_value_heap, timeout_heap)); ReturnRaw(SmiToInt32(result_smi)); } @@ -184,28 +186,29 @@ TF_BUILTIN(WasmI64AtomicWait, WasmBuiltinsAssembler) { TNode centry = LoadCEntryFromInstance(instance); TNode target = LoadBuiltinFromFrame(Builtins::kAllocateHeapNumber); + TNode context = LoadContextFromInstance(instance); // TODO(aseemgarg): Use SMIs if possible for address and expected_value TNode address_heap = UncheckedCast( - CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); + CallStub(AllocateHeapNumberDescriptor(), target, context)); StoreHeapNumberValue(address_heap, ChangeUint32ToFloat64(address)); TNode expected_value_high_heap = UncheckedCast( - CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); + CallStub(AllocateHeapNumberDescriptor(), target, context)); StoreHeapNumberValue(expected_value_high_heap, ChangeUint32ToFloat64(expected_value_high)); TNode expected_value_low_heap = UncheckedCast( - CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); + CallStub(AllocateHeapNumberDescriptor(), target, context)); StoreHeapNumberValue(expected_value_low_heap, ChangeUint32ToFloat64(expected_value_low)); TNode timeout_heap = UncheckedCast( - CallStub(AllocateHeapNumberDescriptor(), target, NoContextConstant())); + CallStub(AllocateHeapNumberDescriptor(), target, context)); StoreHeapNumberValue(timeout_heap, timeout); TNode result_smi = UncheckedCast(CallRuntimeWithCEntry( - Runtime::kWasmI64AtomicWait, centry, NoContextConstant(), instance, + Runtime::kWasmI64AtomicWait, centry, context, instance, address_heap, expected_value_high_heap, expected_value_low_heap, timeout_heap)); ReturnRaw(SmiToInt32(result_smi)); diff --git a/deps/v8/src/compiler/heap-refs.h b/deps/v8/src/compiler/heap-refs.h index c6322ebe691936..f08e49832e202e 100644 --- a/deps/v8/src/compiler/heap-refs.h +++ b/deps/v8/src/compiler/heap-refs.h @@ -389,6 +389,7 @@ class ContextRef : public HeapObjectRef { V(JSFunction, object_function) \ V(JSFunction, promise_function) \ V(JSFunction, promise_then) \ + V(JSFunction, regexp_function) \ V(JSFunction, string_function) \ V(JSFunction, symbol_function) \ V(JSGlobalObject, global_object) \ diff --git a/deps/v8/src/compiler/js-call-reducer.cc b/deps/v8/src/compiler/js-call-reducer.cc index d400fa2673ee3d..b86b1e6baffc3e 100644 --- a/deps/v8/src/compiler/js-call-reducer.cc +++ b/deps/v8/src/compiler/js-call-reducer.cc @@ -7098,11 +7098,14 @@ Reduction JSCallReducer::ReduceRegExpPrototypeTest(Node* node) { Node* control = NodeProperties::GetControlInput(node); Node* regexp = NodeProperties::GetValueInput(node, 1); + // Only the initial JSRegExp map is valid here, since the following lastIndex + // check as well as the lowered builtin call rely on a known location of the + // lastIndex field. + Handle regexp_initial_map = + native_context().regexp_function().initial_map().object(); + MapInference inference(broker(), regexp, effect); - if (!inference.HaveMaps() || - !inference.AllOfInstanceTypes(InstanceTypeChecker::IsJSRegExp)) { - return inference.NoChange(); - } + if (!inference.Is(regexp_initial_map)) return inference.NoChange(); MapHandles const& regexp_maps = inference.GetMaps(); ZoneVector access_infos(graph()->zone()); diff --git a/deps/v8/src/compiler/map-inference.cc b/deps/v8/src/compiler/map-inference.cc index 1e2434f4ae67ce..6ce036aa0bd553 100644 --- a/deps/v8/src/compiler/map-inference.cc +++ b/deps/v8/src/compiler/map-inference.cc @@ -91,6 +91,13 @@ MapHandles const& MapInference::GetMaps() { return maps_; } +bool MapInference::Is(Handle expected_map) { + if (!HaveMaps()) return false; + const MapHandles& maps = GetMaps(); + if (maps.size() != 1) return false; + return maps[0].equals(expected_map); +} + void MapInference::InsertMapChecks(JSGraph* jsgraph, Node** effect, Node* control, const FeedbackSource& feedback) { diff --git a/deps/v8/src/compiler/map-inference.h b/deps/v8/src/compiler/map-inference.h index acba2eb0f2ff08..498b6bc15e7f37 100644 --- a/deps/v8/src/compiler/map-inference.h +++ b/deps/v8/src/compiler/map-inference.h @@ -55,6 +55,7 @@ class MapInference { V8_WARN_UNUSED_RESULT MapHandles const& GetMaps(); V8_WARN_UNUSED_RESULT bool AllOfInstanceTypes( std::function f); + V8_WARN_UNUSED_RESULT bool Is(Handle expected_map); // These methods provide a guard. // diff --git a/deps/v8/test/mjsunit/regress/regress-crbug-1024758.js b/deps/v8/test/mjsunit/regress/regress-crbug-1024758.js new file mode 100644 index 00000000000000..d6f77ee0f0b145 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-crbug-1024758.js @@ -0,0 +1,37 @@ +// Copyright 2019 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Flags: --allow-natives-syntax + +function f() { + return r.test("abc"); +} + +function to_dict(o) { + r.a = 42; + r.b = 42; + delete r.a; +} + +function to_fast(o) { + const obj = {}; + const obj2 = {}; + delete o.a; + obj.__proto__ = o; + obj[0] = 1; + obj.__proto__ = obj2; + delete obj[0]; + return o; +} + +// Shrink the instance size by first transitioning to dictionary properties, +// then back to fast properties. +const r = /./; +to_dict(r); +to_fast(r); + +%PrepareFunctionForOptimization(f); +assertTrue(f()); +%OptimizeFunctionOnNextCall(f); +assertTrue(f()); From 87136c9bde8ebff235b11f8f315ad30d4030d457 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Wed, 27 Nov 2019 13:42:44 -0800 Subject: [PATCH 121/180] doc: revise socket.connect() note Edit note in about `onread` option to `socket.connect()` for clarity. PR-URL: https://github.com/nodejs/node/pull/30691 Reviewed-By: Trivikram Kamat Reviewed-By: Anna Henningsen --- doc/api/net.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/api/net.md b/doc/api/net.md index 419bc8677fabd8..9bc9812d7ca6e9 100644 --- a/doc/api/net.md +++ b/doc/api/net.md @@ -650,9 +650,9 @@ For both types, available `options` include: * `onread` {Object} If specified, incoming data is stored in a single `buffer` and passed to the supplied `callback` when data arrives on the socket. - Note: this will cause the streaming functionality to not provide any data, - however events like `'error'`, `'end'`, and `'close'` will still be emitted - as normal and methods like `pause()` and `resume()` will also behave as + This will cause the streaming functionality to not provide any data. + The socket will emit events like `'error'`, `'end'`, and `'close'` + as usual. Methods like `pause()` and `resume()` will also behave as expected. * `buffer` {Buffer|Uint8Array|Function} Either a reusable chunk of memory to use for storing incoming data or a function that returns such. From 1e7c567734b5518adc21c61c60a00b6611d7a8fd Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Thu, 28 Nov 2019 01:35:05 +0100 Subject: [PATCH 122/180] doc: address nits for src/README.md Refs: https://github.com/nodejs/node/pull/30552#pullrequestreview-321954717 PR-URL: https://github.com/nodejs/node/pull/30693 Reviewed-By: Colin Ihrig Reviewed-By: Jiawen Geng Reviewed-By: Trivikram Kamat Reviewed-By: Benjamin Gruenbaum --- src/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/README.md b/src/README.md index e070bba3c44e3a..cfd6cd6266c695 100644 --- a/src/README.md +++ b/src/README.md @@ -795,7 +795,7 @@ heap if it is larger. This can be useful for performantly allocating temporary data if it is typically expected to be small (e.g. file paths). The `Utf8Value`, `TwoByteValue` (i.e. UTF-16 value) and `BufferValue` -(`Utf8Value` but copy data from a `Buffer` is that is passed) helpers +(`Utf8Value` but copy data from a `Buffer` if one is passed) helpers inherit from this class and allow accessing the characters in a JavaScript string this way. From 20dbce17d547e3938d23ddbd3d77a88aac55e920 Mon Sep 17 00:00:00 2001 From: Alex Zherdev Date: Wed, 27 Nov 2019 10:43:06 -0800 Subject: [PATCH 123/180] doc: avoid proposal syntax in code example PR-URL: https://github.com/nodejs/node/pull/30685 Reviewed-By: Anna Henningsen Reviewed-By: Myles Borins --- doc/api/esm.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/api/esm.md b/doc/api/esm.md index caaecaa0b41498..93b89adec12a98 100644 --- a/doc/api/esm.md +++ b/doc/api/esm.md @@ -654,7 +654,10 @@ CommonJS and ES module instances of the package: ```js // ./node_modules/pkg/index.mjs - export state from './state.cjs'; + import state from './state.cjs'; + export { + state + }; ``` Even if `pkg` is used via both `require` and `import` in an application (for From 43e78578a6c55ae702a8da1373a0b67810650072 Mon Sep 17 00:00:00 2001 From: Daniel Nalborczyk Date: Tue, 26 Nov 2019 10:40:06 -0500 Subject: [PATCH 124/180] doc: fix worker.resourceLimits type PR-URL: https://github.com/nodejs/node/pull/30664 Reviewed-By: Anna Henningsen Reviewed-By: Colin Ihrig Reviewed-By: Luigi Pinca Reviewed-By: Trivikram Kamat --- doc/api/worker_threads.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/worker_threads.md b/doc/api/worker_threads.md index af01ed7f7fba0b..b1402d66e1bf33 100644 --- a/doc/api/worker_threads.md +++ b/doc/api/worker_threads.md @@ -162,7 +162,7 @@ When this function is used, no `'message'` event will be emitted and the added: v13.2.0 --> -* {Object|undefined} +* {Object} * `maxYoungGenerationSizeMb` {number} * `maxOldGenerationSizeMb` {number} * `codeRangeSizeMb` {number} From aca0119089a0890500bbc6c48317cb67f4ea3af8 Mon Sep 17 00:00:00 2001 From: Daniel Nalborczyk Date: Tue, 26 Nov 2019 10:33:38 -0500 Subject: [PATCH 125/180] doc: fix worker.resourceLimits indentation PR-URL: https://github.com/nodejs/node/pull/30663 Reviewed-By: Colin Ihrig Reviewed-By: Anna Henningsen Reviewed-By: David Carlier Reviewed-By: Gireesh Punathil Reviewed-By: Luigi Pinca Reviewed-By: Trivikram Kamat --- doc/api/worker_threads.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/worker_threads.md b/doc/api/worker_threads.md index b1402d66e1bf33..b334a1091b47fd 100644 --- a/doc/api/worker_threads.md +++ b/doc/api/worker_threads.md @@ -157,7 +157,7 @@ console.log(receiveMessageOnPort(port2)); When this function is used, no `'message'` event will be emitted and the `onmessage` listener will not be invoked. -### worker.resourceLimits +## worker.resourceLimits From 40b762177fc4cd67e86aa8d58b775d6b145f6bb8 Mon Sep 17 00:00:00 2001 From: Thomas Watson Date: Wed, 20 Nov 2019 19:35:31 +0100 Subject: [PATCH 126/180] doc: add missing 'added' versions to module.builtinModules PR-URL: https://github.com/nodejs/node/pull/30562 Reviewed-By: Anna Henningsen Reviewed-By: Colin Ihrig Reviewed-By: Luigi Pinca Reviewed-By: Beth Griggs --- doc/api/modules.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/api/modules.md b/doc/api/modules.md index ff7191e462e8bb..42f42c07fd7b6d 100644 --- a/doc/api/modules.md +++ b/doc/api/modules.md @@ -945,7 +945,10 @@ via `require('module')`. ### module.builtinModules * {string[]} From e8af569200551b187d25f8a1b118530d7dd1fcca Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Tue, 19 Nov 2019 21:34:44 +0100 Subject: [PATCH 127/180] buffer: release buffers with free callbacks on env exit Invoke the free callback for a given `Buffer` if it was created with one, and mark the underlying `ArrayBuffer` as detached. This makes sure that the memory is released e.g. when addons inside Workers create such `Buffer`s. PR-URL: https://github.com/nodejs/node/pull/30551 Reviewed-By: Gabriel Schulhof Reviewed-By: Colin Ihrig Reviewed-By: Denys Otrishko Reviewed-By: Michael Dawson --- src/node_buffer.cc | 45 +++++++++++++++---- test/addons/worker-buffer-callback/binding.cc | 11 ++++- .../test-free-called.js | 17 +++++++ test/cctest/test_environment.cc | 32 +++++++++++++ 4 files changed, 95 insertions(+), 10 deletions(-) create mode 100644 test/addons/worker-buffer-callback/test-free-called.js diff --git a/src/node_buffer.cc b/src/node_buffer.cc index 8641270eaecfcd..e5c4655b4ccdb9 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -53,6 +53,7 @@ using v8::Context; using v8::EscapableHandleScope; using v8::FunctionCallbackInfo; using v8::Global; +using v8::HandleScope; using v8::Int32; using v8::Integer; using v8::Isolate; @@ -73,8 +74,10 @@ namespace { class CallbackInfo { public: + ~CallbackInfo(); + static inline void Free(char* data, void* hint); - static inline CallbackInfo* New(Isolate* isolate, + static inline CallbackInfo* New(Environment* env, Local object, FreeCallback callback, char* data, @@ -84,9 +87,10 @@ class CallbackInfo { CallbackInfo& operator=(const CallbackInfo&) = delete; private: + static void CleanupHook(void* data); static void WeakCallback(const WeakCallbackInfo&); inline void WeakCallback(Isolate* isolate); - inline CallbackInfo(Isolate* isolate, + inline CallbackInfo(Environment* env, Local object, FreeCallback callback, char* data, @@ -95,6 +99,7 @@ class CallbackInfo { FreeCallback const callback_; char* const data_; void* const hint_; + Environment* const env_; }; @@ -103,31 +108,53 @@ void CallbackInfo::Free(char* data, void*) { } -CallbackInfo* CallbackInfo::New(Isolate* isolate, +CallbackInfo* CallbackInfo::New(Environment* env, Local object, FreeCallback callback, char* data, void* hint) { - return new CallbackInfo(isolate, object, callback, data, hint); + return new CallbackInfo(env, object, callback, data, hint); } -CallbackInfo::CallbackInfo(Isolate* isolate, +CallbackInfo::CallbackInfo(Environment* env, Local object, FreeCallback callback, char* data, void* hint) - : persistent_(isolate, object), + : persistent_(env->isolate(), object), callback_(callback), data_(data), - hint_(hint) { + hint_(hint), + env_(env) { ArrayBuffer::Contents obj_c = object->GetContents(); CHECK_EQ(data_, static_cast(obj_c.Data())); if (object->ByteLength() != 0) CHECK_NOT_NULL(data_); persistent_.SetWeak(this, WeakCallback, v8::WeakCallbackType::kParameter); - isolate->AdjustAmountOfExternalAllocatedMemory(sizeof(*this)); + env->AddCleanupHook(CleanupHook, this); + env->isolate()->AdjustAmountOfExternalAllocatedMemory(sizeof(*this)); +} + + +CallbackInfo::~CallbackInfo() { + persistent_.Reset(); + env_->RemoveCleanupHook(CleanupHook, this); +} + + +void CallbackInfo::CleanupHook(void* data) { + CallbackInfo* self = static_cast(data); + + { + HandleScope handle_scope(self->env_->isolate()); + Local ab = self->persistent_.Get(self->env_->isolate()); + CHECK(!ab.IsEmpty()); + ab->Detach(); + } + + self->WeakCallback(self->env_->isolate()); } @@ -388,7 +415,7 @@ MaybeLocal New(Environment* env, } MaybeLocal ui = Buffer::New(env, ab, 0, length); - CallbackInfo::New(env->isolate(), ab, callback, data, hint); + CallbackInfo::New(env, ab, callback, data, hint); if (ui.IsEmpty()) return MaybeLocal(); diff --git a/test/addons/worker-buffer-callback/binding.cc b/test/addons/worker-buffer-callback/binding.cc index a40876ebb523a6..1141c8a051e077 100644 --- a/test/addons/worker-buffer-callback/binding.cc +++ b/test/addons/worker-buffer-callback/binding.cc @@ -3,17 +3,24 @@ #include using v8::Context; +using v8::FunctionCallbackInfo; using v8::Isolate; using v8::Local; using v8::Object; using v8::Value; +uint32_t free_call_count = 0; char data[] = "hello"; +void GetFreeCallCount(const FunctionCallbackInfo& args) { + args.GetReturnValue().Set(free_call_count); +} + void Initialize(Local exports, Local module, Local context) { Isolate* isolate = context->GetIsolate(); + NODE_SET_METHOD(exports, "getFreeCallCount", GetFreeCallCount); exports->Set(context, v8::String::NewFromUtf8( isolate, "buffer", v8::NewStringType::kNormal) @@ -22,7 +29,9 @@ void Initialize(Local exports, isolate, data, sizeof(data), - [](char* data, void* hint) {}, + [](char* data, void* hint) { + free_call_count++; + }, nullptr).ToLocalChecked()).Check(); } diff --git a/test/addons/worker-buffer-callback/test-free-called.js b/test/addons/worker-buffer-callback/test-free-called.js new file mode 100644 index 00000000000000..2a3cc9e47c22ff --- /dev/null +++ b/test/addons/worker-buffer-callback/test-free-called.js @@ -0,0 +1,17 @@ +'use strict'; +const common = require('../../common'); +const path = require('path'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); +const binding = path.resolve(__dirname, `./build/${common.buildType}/binding`); +const { getFreeCallCount } = require(binding); + +// Test that buffers allocated with a free callback through our APIs are +// released when a Worker owning it exits. + +const w = new Worker(`require(${JSON.stringify(binding)})`, { eval: true }); + +assert.strictEqual(getFreeCallCount(), 0); +w.on('exit', common.mustCall(() => { + assert.strictEqual(getFreeCallCount(), 1); +})); diff --git a/test/cctest/test_environment.cc b/test/cctest/test_environment.cc index 0db2963acc9ba3..132f7b44f7db62 100644 --- a/test/cctest/test_environment.cc +++ b/test/cctest/test_environment.cc @@ -1,3 +1,4 @@ +#include "node_buffer.h" #include "node_internals.h" #include "libplatform/libplatform.h" @@ -208,3 +209,34 @@ TEST_F(EnvironmentTest, SetImmediateCleanup) { EXPECT_EQ(called, 1); EXPECT_EQ(called_unref, 0); } + +static char hello[] = "hello"; + +TEST_F(EnvironmentTest, BufferWithFreeCallbackIsDetached) { + // Test that a Buffer allocated with a free callback is detached after + // its callback has been called. + const v8::HandleScope handle_scope(isolate_); + const Argv argv; + + int callback_calls = 0; + + v8::Local ab; + { + Env env {handle_scope, argv}; + v8::Local buf_obj = node::Buffer::New( + isolate_, + hello, + sizeof(hello), + [](char* data, void* hint) { + CHECK_EQ(data, hello); + ++*static_cast(hint); + }, + &callback_calls).ToLocalChecked(); + CHECK(buf_obj->IsUint8Array()); + ab = buf_obj.As()->Buffer(); + CHECK_EQ(ab->ByteLength(), sizeof(hello)); + } + + CHECK_EQ(callback_calls, 1); + CHECK_EQ(ab->ByteLength(), 0); +} From be9788bf2016233576fd33b73686de0ef902eef2 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Tue, 19 Nov 2019 21:36:41 +0100 Subject: [PATCH 128/180] n-api: detach external ArrayBuffers on env exit Make sure that `ArrayBuffer`s created using `napi_create_external_arraybuffer` are rendered unusable after its memory has been released. PR-URL: https://github.com/nodejs/node/pull/30551 Reviewed-By: Gabriel Schulhof Reviewed-By: Colin Ihrig Reviewed-By: Denys Otrishko Reviewed-By: Michael Dawson --- src/js_native_api_v8.cc | 43 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/src/js_native_api_v8.cc b/src/js_native_api_v8.cc index 05712afde0020f..5506b2b4c6204b 100644 --- a/src/js_native_api_v8.cc +++ b/src/js_native_api_v8.cc @@ -186,8 +186,8 @@ inline static napi_status ConcludeDeferred(napi_env env, } // Wrapper around v8impl::Persistent that implements reference counting. -class Reference : private Finalizer, RefTracker { - private: +class Reference : protected Finalizer, RefTracker { + protected: Reference(napi_env env, v8::Local value, uint32_t initial_refcount, @@ -289,7 +289,7 @@ class Reference : private Finalizer, RefTracker { } } - private: + protected: void Finalize(bool is_env_teardown = false) override { if (_finalize_callback != nullptr) { _env->CallIntoModuleThrow([&](napi_env env) { @@ -310,6 +310,7 @@ class Reference : private Finalizer, RefTracker { } } + private: // The N-API finalizer callback may make calls into the engine. V8's heap is // not in a consistent state during the weak callback, and therefore it does // not support calls back into it. However, it provides a mechanism for adding @@ -335,6 +336,37 @@ class Reference : private Finalizer, RefTracker { bool _delete_self; }; +class ArrayBufferReference final : public Reference { + public: + // Same signatures for ctor and New() as Reference, except this only works + // with ArrayBuffers: + template + explicit ArrayBufferReference(napi_env env, + v8::Local value, + Args&&... args) + : Reference(env, value, std::forward(args)...) {} + + template + static ArrayBufferReference* New(napi_env env, + v8::Local value, + Args&&... args) { + return new ArrayBufferReference(env, value, std::forward(args)...); + } + + private: + void Finalize(bool is_env_teardown) override { + if (is_env_teardown) { + v8::HandleScope handle_scope(_env->isolate); + v8::Local ab = Get(); + CHECK(!ab.IsEmpty()); + CHECK(ab->IsArrayBuffer()); + ab.As()->Detach(); + } + + Reference::Finalize(is_env_teardown); + } +}; + enum UnwrapAction { KeepWrap, RemoveWrap @@ -2587,8 +2619,9 @@ napi_status napi_create_external_arraybuffer(napi_env env, if (finalize_cb != nullptr) { // Create a self-deleting weak reference that invokes the finalizer - // callback. - v8impl::Reference::New(env, + // callback and detaches the ArrayBuffer if it still exists on Environment + // teardown. + v8impl::ArrayBufferReference::New(env, buffer, 0, true, From 099d3fdf87e612933a60d282446b5650565112d2 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Tue, 19 Nov 2019 21:39:07 +0100 Subject: [PATCH 129/180] test: port worker + buffer test to N-API This ports `test/addons/worker-buffer-callback` to N-API, with the small exception of using external `ArrayBuffer`s rather than external Node.js `Buffer`s. PR-URL: https://github.com/nodejs/node/pull/30551 Reviewed-By: Gabriel Schulhof Reviewed-By: Colin Ihrig Reviewed-By: Denys Otrishko Reviewed-By: Michael Dawson --- .../test_worker_buffer_callback/binding.gyp | 8 ++++ .../test-free-called.js | 17 ++++++++ .../test_worker_buffer_callback/test.js | 15 +++++++ .../test_worker_buffer_callback.c | 43 +++++++++++++++++++ 4 files changed, 83 insertions(+) create mode 100644 test/node-api/test_worker_buffer_callback/binding.gyp create mode 100644 test/node-api/test_worker_buffer_callback/test-free-called.js create mode 100644 test/node-api/test_worker_buffer_callback/test.js create mode 100644 test/node-api/test_worker_buffer_callback/test_worker_buffer_callback.c diff --git a/test/node-api/test_worker_buffer_callback/binding.gyp b/test/node-api/test_worker_buffer_callback/binding.gyp new file mode 100644 index 00000000000000..7ab6381930da9c --- /dev/null +++ b/test/node-api/test_worker_buffer_callback/binding.gyp @@ -0,0 +1,8 @@ +{ + 'targets': [ + { + 'target_name': 'binding', + 'sources': [ 'test_worker_buffer_callback.c' ] + } + ] +} diff --git a/test/node-api/test_worker_buffer_callback/test-free-called.js b/test/node-api/test_worker_buffer_callback/test-free-called.js new file mode 100644 index 00000000000000..2a3cc9e47c22ff --- /dev/null +++ b/test/node-api/test_worker_buffer_callback/test-free-called.js @@ -0,0 +1,17 @@ +'use strict'; +const common = require('../../common'); +const path = require('path'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); +const binding = path.resolve(__dirname, `./build/${common.buildType}/binding`); +const { getFreeCallCount } = require(binding); + +// Test that buffers allocated with a free callback through our APIs are +// released when a Worker owning it exits. + +const w = new Worker(`require(${JSON.stringify(binding)})`, { eval: true }); + +assert.strictEqual(getFreeCallCount(), 0); +w.on('exit', common.mustCall(() => { + assert.strictEqual(getFreeCallCount(), 1); +})); diff --git a/test/node-api/test_worker_buffer_callback/test.js b/test/node-api/test_worker_buffer_callback/test.js new file mode 100644 index 00000000000000..4884a27d39e6a2 --- /dev/null +++ b/test/node-api/test_worker_buffer_callback/test.js @@ -0,0 +1,15 @@ +'use strict'; +const common = require('../../common'); +const assert = require('assert'); +const { MessageChannel } = require('worker_threads'); +const { buffer } = require(`./build/${common.buildType}/binding`); + +// Test that buffers allocated with a free callback through our APIs are not +// transferred. + +const { port1 } = new MessageChannel(); +const origByteLength = buffer.byteLength; +port1.postMessage(buffer, [buffer]); + +assert.strictEqual(buffer.byteLength, origByteLength); +assert.notStrictEqual(buffer.byteLength, 0); diff --git a/test/node-api/test_worker_buffer_callback/test_worker_buffer_callback.c b/test/node-api/test_worker_buffer_callback/test_worker_buffer_callback.c new file mode 100644 index 00000000000000..b911fd86380644 --- /dev/null +++ b/test/node-api/test_worker_buffer_callback/test_worker_buffer_callback.c @@ -0,0 +1,43 @@ +#include +#include +#include +#include "../../js-native-api/common.h" + +uint32_t free_call_count = 0; +char data[] = "hello"; + +napi_value GetFreeCallCount(napi_env env, napi_callback_info info) { + napi_value value; + NAPI_CALL(env, napi_create_uint32(env, free_call_count, &value)); + return value; +} + +static void finalize_cb(napi_env env, void* finalize_data, void* hint) { + assert(finalize_data == data); + free_call_count++; +} + +NAPI_MODULE_INIT() { + napi_property_descriptor properties[] = { + DECLARE_NAPI_PROPERTY("getFreeCallCount", GetFreeCallCount) + }; + + NAPI_CALL(env, napi_define_properties( + env, exports, sizeof(properties) / sizeof(*properties), properties)); + + // This is a slight variation on the non-N-API test: We create an ArrayBuffer + // rather than a Node.js Buffer, since testing the latter would only test + // the same code paths and not the ones specific to N-API. + napi_value buffer; + NAPI_CALL(env, napi_create_external_arraybuffer( + env, + data, + sizeof(data), + finalize_cb, + NULL, + &buffer)); + + NAPI_CALL(env, napi_set_named_property(env, exports, "buffer", buffer)); + + return exports; +} From f37450f58075bcc2ec2c04fae40b874683eb418c Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Wed, 27 Nov 2019 21:51:54 +0100 Subject: [PATCH 130/180] dns: use length for building TXT string Rely on the length reported by C-Ares rather than `\0`-termination for creating the JS string for a dns TXT response. Fixes: https://github.com/nodejs/node/issues/30688 PR-URL: https://github.com/nodejs/node/pull/30690 Reviewed-By: Colin Ihrig Reviewed-By: David Carlier Reviewed-By: Ben Noordhuis Reviewed-By: James M Snell --- src/cares_wrap.cc | 3 ++- test/parallel/test-dns-resolveany.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cares_wrap.cc b/src/cares_wrap.cc index 1fb0f47dd80f08..f7a02e469aa79a 100644 --- a/src/cares_wrap.cc +++ b/src/cares_wrap.cc @@ -904,7 +904,8 @@ int ParseTxtReply(Environment* env, uint32_t i = 0, j; uint32_t offset = ret->Length(); for (j = 0; current != nullptr; current = current->next) { - Local txt = OneByteString(env->isolate(), current->txt); + Local txt = + OneByteString(env->isolate(), current->txt, current->length); // New record found - write out the current chunk if (current->record_start) { diff --git a/test/parallel/test-dns-resolveany.js b/test/parallel/test-dns-resolveany.js index bb15b1a38b4ad3..33beee847fd52c 100644 --- a/test/parallel/test-dns-resolveany.js +++ b/test/parallel/test-dns-resolveany.js @@ -11,7 +11,7 @@ const answers = [ { type: 'AAAA', address: '::42', ttl: 123 }, { type: 'MX', priority: 42, exchange: 'foobar.com', ttl: 124 }, { type: 'NS', value: 'foobar.org', ttl: 457 }, - { type: 'TXT', entries: [ 'v=spf1 ~all', 'xyz' ] }, + { type: 'TXT', entries: [ 'v=spf1 ~all', 'xyz\0foo' ] }, { type: 'PTR', value: 'baz.org', ttl: 987 }, { type: 'SOA', From 63eb4fee467c60cdbd60e07de3c6b707577f9c40 Mon Sep 17 00:00:00 2001 From: Brian White Date: Tue, 12 Nov 2019 22:41:12 -0500 Subject: [PATCH 131/180] buffer: fix 6-byte writeUIntBE() range check Fixes: https://github.com/nodejs/node/issues/30420 PR-URL: https://github.com/nodejs/node/pull/30459 Reviewed-By: Anna Henningsen Reviewed-By: David Carlier Reviewed-By: Colin Ihrig Reviewed-By: Ruben Bridgewater Reviewed-By: James M Snell --- lib/internal/buffer.js | 2 +- test/parallel/test-buffer-writeint.js | 2 +- test/parallel/test-buffer-writeuint.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/internal/buffer.js b/lib/internal/buffer.js index f3bc0e48d1c911..acd7d319d5e1b2 100644 --- a/lib/internal/buffer.js +++ b/lib/internal/buffer.js @@ -736,7 +736,7 @@ function writeUInt8(value, offset = 0) { function writeUIntBE(value, offset, byteLength) { if (byteLength === 6) - return writeU_Int48BE(this, value, offset, 0, 0xffffffffffffff); + return writeU_Int48BE(this, value, offset, 0, 0xffffffffffff); if (byteLength === 5) return writeU_Int40BE(this, value, offset, 0, 0xffffffffff); if (byteLength === 3) diff --git a/test/parallel/test-buffer-writeint.js b/test/parallel/test-buffer-writeint.js index 05b0cd1ebbfada..0e812cc8886941 100644 --- a/test/parallel/test-buffer-writeint.js +++ b/test/parallel/test-buffer-writeint.js @@ -213,7 +213,7 @@ const errorOutOfBounds = common.expectsError({ }); // Test 1 to 6 bytes. - for (let i = 1; i < 6; i++) { + for (let i = 1; i <= 6; i++) { ['writeIntBE', 'writeIntLE'].forEach((fn) => { const min = -(2 ** (i * 8 - 1)); const max = 2 ** (i * 8 - 1) - 1; diff --git a/test/parallel/test-buffer-writeuint.js b/test/parallel/test-buffer-writeuint.js index 3823b74d565519..c7708d663eca80 100644 --- a/test/parallel/test-buffer-writeuint.js +++ b/test/parallel/test-buffer-writeuint.js @@ -170,7 +170,7 @@ const assert = require('assert'); }); // Test 1 to 6 bytes. - for (let i = 1; i < 6; i++) { + for (let i = 1; i <= 6; i++) { const range = i < 5 ? `= ${val - 1}` : ` 2 ** ${i * 8}`; const received = i > 4 ? String(val).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1_') : From ba0115fe6f8dedcd931754e86d1100fb64f29230 Mon Sep 17 00:00:00 2001 From: Jeny Date: Wed, 20 Nov 2019 22:42:47 +0100 Subject: [PATCH 132/180] test:refactor createHook test PR-URL: https://github.com/nodejs/node/pull/30568 Reviewed-By: Anna Henningsen Reviewed-By: Colin Ihrig Reviewed-By: David Carlier Reviewed-By: Gireesh Punathil --- test/parallel/test-async-hooks-promise-triggerid.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/parallel/test-async-hooks-promise-triggerid.js b/test/parallel/test-async-hooks-promise-triggerid.js index 467fddd359d835..b860d60999e1ef 100644 --- a/test/parallel/test-async-hooks-promise-triggerid.js +++ b/test/parallel/test-async-hooks-promise-triggerid.js @@ -13,8 +13,8 @@ async_hooks.createHook({ if (type === 'PROMISE') { // Check that the last known Promise is triggering the creation of // this one. - assert.strictEqual(promiseAsyncIds[promiseAsyncIds.length - 1] || 1, - triggerId); + assert.strictEqual(triggerId, + promiseAsyncIds[promiseAsyncIds.length - 1] || 1); promiseAsyncIds.push(id); } }, 3), From bccfd124b069a3d4396bd8dba1bf2350383dea53 Mon Sep 17 00:00:00 2001 From: gengjiawen Date: Tue, 5 Nov 2019 23:40:42 +0800 Subject: [PATCH 133/180] src: remove unused variable in node_dir.cc PR-URL: https://github.com/nodejs/node/pull/30267 Reviewed-By: Anna Henningsen Reviewed-By: Colin Ihrig Reviewed-By: Richard Lau Reviewed-By: David Carlier Reviewed-By: Trivikram Kamat Reviewed-By: James M Snell Reviewed-By: Gireesh Punathil Reviewed-By: Luigi Pinca --- src/node_dir.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/node_dir.cc b/src/node_dir.cc index ffef27f4521403..9d145131beb94c 100644 --- a/src/node_dir.cc +++ b/src/node_dir.cc @@ -296,7 +296,6 @@ void AfterOpenDir(uv_fs_t* req) { } Environment* env = req_wrap->env(); - Local error; uv_dir_t* dir = static_cast(req->ptr); DirHandle* handle = DirHandle::New(env, dir); From be84ceefb8f08c5cc4a8dc85275ca57d0b355a0d Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Mon, 18 Nov 2019 14:49:56 +0100 Subject: [PATCH 134/180] src: clean up node_file.h - Move inline functions into an `-inl.h` file - Move override function definitions into `.cc` files - Remove `using` statements from header files - Make data fields of classes private - Mark classes at the end of hierarchies as `final` This is also partially being done in an attempt to avoid a particular internal compiler error, see https://github.com/nodejs/node/pull/30475#issuecomment-554740850 for details. PR-URL: https://github.com/nodejs/node/pull/30530 Reviewed-By: Jiawen Geng Reviewed-By: David Carlier Reviewed-By: James M Snell --- node.gyp | 1 + src/env.cc | 2 + src/node_dir.cc | 7 +- src/node_dir.h | 13 +- src/node_file-inl.h | 283 +++++++++++++++++++++++++++++ src/node_file.cc | 92 +++++++--- src/node_file.h | 382 ++++++++++----------------------------- src/node_stat_watcher.cc | 2 +- src/req_wrap-inl.h | 2 +- src/req_wrap.h | 5 +- 10 files changed, 462 insertions(+), 327 deletions(-) create mode 100644 src/node_file-inl.h diff --git a/node.gyp b/node.gyp index 810cea8c241359..d8b55960833194 100644 --- a/node.gyp +++ b/node.gyp @@ -613,6 +613,7 @@ 'src/node_dir.h', 'src/node_errors.h', 'src/node_file.h', + 'src/node_file-inl.h', 'src/node_http2.h', 'src/node_http2_state.h', 'src/node_i18n.h', diff --git a/src/env.cc b/src/env.cc index dc99e4bd5325d6..bf3420f826f02a 100644 --- a/src/env.cc +++ b/src/env.cc @@ -11,6 +11,7 @@ #include "node_process.h" #include "node_v8_platform-inl.h" #include "node_worker.h" +#include "req_wrap-inl.h" #include "tracing/agent.h" #include "tracing/traced_value.h" #include "util-inl.h" @@ -35,6 +36,7 @@ using v8::HandleScope; using v8::Integer; using v8::Isolate; using v8::Local; +using v8::MaybeLocal; using v8::NewStringType; using v8::Number; using v8::Object; diff --git a/src/node_dir.cc b/src/node_dir.cc index 9d145131beb94c..37285928fbd731 100644 --- a/src/node_dir.cc +++ b/src/node_dir.cc @@ -1,10 +1,11 @@ #include "node_dir.h" +#include "node_file-inl.h" #include "node_process.h" +#include "memory_tracker-inl.h" #include "util.h" #include "tracing/trace_event.h" -#include "req_wrap-inl.h" #include "string_bytes.h" #include @@ -85,6 +86,10 @@ DirHandle::~DirHandle() { CHECK(closed_); // We have to be closed at the point } +void DirHandle::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackFieldWithSize("dir", sizeof(*dir_)); +} + // Close the directory handle if it hasn't already been closed. A process // warning will be emitted using a SetImmediate to avoid calling back to // JS during GC. If closing the fd fails at this point, a fatal exception diff --git a/src/node_dir.h b/src/node_dir.h index ae6d0eb170d679..caef7a5d309180 100644 --- a/src/node_dir.h +++ b/src/node_dir.h @@ -4,8 +4,6 @@ #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS #include "node_file.h" -#include "node.h" -#include "req_wrap-inl.h" namespace node { @@ -20,16 +18,13 @@ class DirHandle : public AsyncWrap { ~DirHandle() override; static void New(const v8::FunctionCallbackInfo& args); - static void Open(const v8::FunctionCallbackInfo& args); - static void Read(const v8::FunctionCallbackInfo& args); - static void Close(const v8::FunctionCallbackInfo& args); + static void Open(const v8::FunctionCallbackInfo& args); + static void Read(const v8::FunctionCallbackInfo& args); + static void Close(const v8::FunctionCallbackInfo& args); inline uv_dir_t* dir() { return dir_; } - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackFieldWithSize("dir", sizeof(*dir_)); - } - + void MemoryInfo(MemoryTracker* tracker) const override; SET_MEMORY_INFO_NAME(DirHandle) SET_SELF_SIZE(DirHandle) diff --git a/src/node_file-inl.h b/src/node_file-inl.h new file mode 100644 index 00000000000000..390f6c7415770f --- /dev/null +++ b/src/node_file-inl.h @@ -0,0 +1,283 @@ +#ifndef SRC_NODE_FILE_INL_H_ +#define SRC_NODE_FILE_INL_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "node_file.h" +#include "req_wrap-inl.h" + +namespace node { +namespace fs { + +FSContinuationData::FSContinuationData(uv_fs_t* req, int mode, uv_fs_cb done_cb) + : done_cb_(done_cb), req_(req), mode_(mode) { +} + +void FSContinuationData::PushPath(std::string&& path) { + paths_.emplace_back(std::move(path)); +} + +void FSContinuationData::PushPath(const std::string& path) { + paths_.push_back(path); +} + +std::string FSContinuationData::PopPath() { + CHECK_GT(paths_.size(), 0); + std::string path = std::move(paths_.back()); + paths_.pop_back(); + return path; +} + +void FSContinuationData::Done(int result) { + req_->result = result; + done_cb_(req_); +} + +FSReqBase::FSReqBase(Environment* env, + v8::Local req, + AsyncWrap::ProviderType type, + bool use_bigint) + : ReqWrap(env, req, type), use_bigint_(use_bigint) { +} + +void FSReqBase::Init(const char* syscall, + const char* data, + size_t len, + enum encoding encoding) { + syscall_ = syscall; + encoding_ = encoding; + + if (data != nullptr) { + CHECK(!has_data_); + buffer_.AllocateSufficientStorage(len + 1); + buffer_.SetLengthAndZeroTerminate(len); + memcpy(*buffer_, data, len); + has_data_ = true; + } +} + +FSReqBase::FSReqBuffer& +FSReqBase::Init(const char* syscall, size_t len, enum encoding encoding) { + syscall_ = syscall; + encoding_ = encoding; + + buffer_.AllocateSufficientStorage(len + 1); + has_data_ = false; // so that the data does not show up in error messages + return buffer_; +} + +FSReqCallback::FSReqCallback(Environment* env, + v8::Local req, bool use_bigint) + : FSReqBase(env, req, AsyncWrap::PROVIDER_FSREQCALLBACK, use_bigint) {} + +template +void FillStatsArray(AliasedBufferBase* fields, + const uv_stat_t* s, + const size_t offset) { +#define SET_FIELD_WITH_STAT(stat_offset, stat) \ + fields->SetValue(offset + static_cast(FsStatsOffset::stat_offset), \ + static_cast(stat)) + +#define SET_FIELD_WITH_TIME_STAT(stat_offset, stat) \ + /* NOLINTNEXTLINE(runtime/int) */ \ + SET_FIELD_WITH_STAT(stat_offset, static_cast(stat)) + + SET_FIELD_WITH_STAT(kDev, s->st_dev); + SET_FIELD_WITH_STAT(kMode, s->st_mode); + SET_FIELD_WITH_STAT(kNlink, s->st_nlink); + SET_FIELD_WITH_STAT(kUid, s->st_uid); + SET_FIELD_WITH_STAT(kGid, s->st_gid); + SET_FIELD_WITH_STAT(kRdev, s->st_rdev); + SET_FIELD_WITH_STAT(kBlkSize, s->st_blksize); + SET_FIELD_WITH_STAT(kIno, s->st_ino); + SET_FIELD_WITH_STAT(kSize, s->st_size); + SET_FIELD_WITH_STAT(kBlocks, s->st_blocks); + + SET_FIELD_WITH_TIME_STAT(kATimeSec, s->st_atim.tv_sec); + SET_FIELD_WITH_TIME_STAT(kATimeNsec, s->st_atim.tv_nsec); + SET_FIELD_WITH_TIME_STAT(kMTimeSec, s->st_mtim.tv_sec); + SET_FIELD_WITH_TIME_STAT(kMTimeNsec, s->st_mtim.tv_nsec); + SET_FIELD_WITH_TIME_STAT(kCTimeSec, s->st_ctim.tv_sec); + SET_FIELD_WITH_TIME_STAT(kCTimeNsec, s->st_ctim.tv_nsec); + SET_FIELD_WITH_TIME_STAT(kBirthTimeSec, s->st_birthtim.tv_sec); + SET_FIELD_WITH_TIME_STAT(kBirthTimeNsec, s->st_birthtim.tv_nsec); + +#undef SET_FIELD_WITH_TIME_STAT +#undef SET_FIELD_WITH_STAT +} + +v8::Local FillGlobalStatsArray(Environment* env, + const bool use_bigint, + const uv_stat_t* s, + const bool second) { + const ptrdiff_t offset = + second ? static_cast(FsStatsOffset::kFsStatsFieldsNumber) : 0; + if (use_bigint) { + auto* const arr = env->fs_stats_field_bigint_array(); + FillStatsArray(arr, s, offset); + return arr->GetJSArray(); + } else { + auto* const arr = env->fs_stats_field_array(); + FillStatsArray(arr, s, offset); + return arr->GetJSArray(); + } +} + +template +FSReqPromise* +FSReqPromise::New(Environment* env, bool use_bigint) { + v8::Local obj; + if (!env->fsreqpromise_constructor_template() + ->NewInstance(env->context()) + .ToLocal(&obj)) { + return nullptr; + } + v8::Local resolver; + if (!v8::Promise::Resolver::New(env->context()).ToLocal(&resolver) || + obj->Set(env->context(), env->promise_string(), resolver).IsNothing()) { + return nullptr; + } + return new FSReqPromise(env, obj, use_bigint); +} + +template +FSReqPromise::~FSReqPromise() { + // Validate that the promise was explicitly resolved or rejected. + CHECK(finished_); +} + +template +FSReqPromise::FSReqPromise( + Environment* env, + v8::Local obj, + bool use_bigint) + : FSReqBase(env, obj, AsyncWrap::PROVIDER_FSREQPROMISE, use_bigint), + stats_field_array_( + env->isolate(), + static_cast(FsStatsOffset::kFsStatsFieldsNumber)) {} + +template +void FSReqPromise::Reject(v8::Local reject) { + finished_ = true; + v8::HandleScope scope(env()->isolate()); + InternalCallbackScope callback_scope(this); + v8::Local value = + object()->Get(env()->context(), + env()->promise_string()).ToLocalChecked(); + v8::Local resolver = value.As(); + USE(resolver->Reject(env()->context(), reject).FromJust()); +} + +template +void FSReqPromise::Resolve(v8::Local value) { + finished_ = true; + v8::HandleScope scope(env()->isolate()); + InternalCallbackScope callback_scope(this); + v8::Local val = + object()->Get(env()->context(), + env()->promise_string()).ToLocalChecked(); + v8::Local resolver = val.As(); + USE(resolver->Resolve(env()->context(), value).FromJust()); +} + +template +void FSReqPromise::ResolveStat(const uv_stat_t* stat) { + FillStatsArray(&stats_field_array_, stat); + Resolve(stats_field_array_.GetJSArray()); +} + +template +void FSReqPromise::SetReturnValue( + const v8::FunctionCallbackInfo& args) { + v8::Local val = + object()->Get(env()->context(), + env()->promise_string()).ToLocalChecked(); + v8::Local resolver = val.As(); + args.GetReturnValue().Set(resolver->GetPromise()); +} + +template +void FSReqPromise::MemoryInfo(MemoryTracker* tracker) const { + FSReqBase::MemoryInfo(tracker); + tracker->TrackField("stats_field_array", stats_field_array_); +} + +FSReqBase* GetReqWrap(Environment* env, v8::Local value, + bool use_bigint) { + if (value->IsObject()) { + return Unwrap(value.As()); + } else if (value->StrictEquals(env->fs_use_promises_symbol())) { + if (use_bigint) { + return FSReqPromise::New(env, use_bigint); + } else { + return FSReqPromise::New(env, use_bigint); + } + } + return nullptr; +} + +// Returns nullptr if the operation fails from the start. +template +FSReqBase* AsyncDestCall(Environment* env, FSReqBase* req_wrap, + const v8::FunctionCallbackInfo& args, + const char* syscall, const char* dest, + size_t len, enum encoding enc, uv_fs_cb after, + Func fn, Args... fn_args) { + CHECK_NOT_NULL(req_wrap); + req_wrap->Init(syscall, dest, len, enc); + int err = req_wrap->Dispatch(fn, fn_args..., after); + if (err < 0) { + uv_fs_t* uv_req = req_wrap->req(); + uv_req->result = err; + uv_req->path = nullptr; + after(uv_req); // after may delete req_wrap if there is an error + req_wrap = nullptr; + } else { + req_wrap->SetReturnValue(args); + } + + return req_wrap; +} + +// Returns nullptr if the operation fails from the start. +template +FSReqBase* AsyncCall(Environment* env, + FSReqBase* req_wrap, + const v8::FunctionCallbackInfo& args, + const char* syscall, enum encoding enc, + uv_fs_cb after, Func fn, Args... fn_args) { + return AsyncDestCall(env, req_wrap, args, + syscall, nullptr, 0, enc, + after, fn, fn_args...); +} + +// Template counterpart of SYNC_CALL, except that it only puts +// the error number and the syscall in the context instead of +// creating an error in the C++ land. +// ctx must be checked using value->IsObject() before being passed. +template +int SyncCall(Environment* env, v8::Local ctx, + FSReqWrapSync* req_wrap, const char* syscall, + Func fn, Args... args) { + env->PrintSyncTrace(); + int err = fn(env->event_loop(), &(req_wrap->req), args..., nullptr); + if (err < 0) { + v8::Local context = env->context(); + v8::Local ctx_obj = ctx.As(); + v8::Isolate* isolate = env->isolate(); + ctx_obj->Set(context, + env->errno_string(), + v8::Integer::New(isolate, err)).Check(); + ctx_obj->Set(context, + env->syscall_string(), + OneByteString(isolate, syscall)).Check(); + } + return err; +} + +} // namespace fs +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_NODE_FILE_INL_H_ diff --git a/src/node_file.cc b/src/node_file.cc index 48b382986c0bb5..577b9c7c4173af 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -18,7 +18,8 @@ // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -#include "node_file.h" +#include "node_file.h" // NOLINT(build/include_inline) +#include "node_file-inl.h" #include "aliased_buffer.h" #include "memory_tracker-inl.h" #include "node_buffer.h" @@ -107,6 +108,19 @@ inline int64_t GetOffset(Local value) { // functions, and thus does not wrap them properly. typedef void(*uv_fs_callback_t)(uv_fs_t*); + +void FSContinuationData::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("paths", paths_); +} + +FileHandleReadWrap::~FileHandleReadWrap() {} + +FSReqBase::~FSReqBase() {} + +void FSReqBase::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("continuation_data", continuation_data_); +} + // The FileHandle object wraps a file descriptor and will close it on garbage // collection if necessary. If that happens, a process warning will be // emitted (or a fatal exception will occur if the fd cannot be closed.) @@ -156,6 +170,16 @@ FileHandle::~FileHandle() { CHECK(closed_); // We have to be closed at the point } +int FileHandle::DoWrite(WriteWrap* w, + uv_buf_t* bufs, + size_t count, + uv_stream_t* send_handle) { + return UV_ENOSYS; // Not implemented (yet). +} + +void FileHandle::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("current_read", current_read_); +} // Close the file descriptor if it hasn't already been closed. A process // warning will be emitted using a SetImmediate to avoid calling back to @@ -225,12 +249,34 @@ FileHandle* FileHandle::CloseReq::file_handle() { return Unwrap(obj); } +FileHandle::CloseReq::CloseReq(Environment* env, + Local obj, + Local promise, + Local ref) + : ReqWrap(env, obj, AsyncWrap::PROVIDER_FILEHANDLECLOSEREQ) { + promise_.Reset(env->isolate(), promise); + ref_.Reset(env->isolate(), ref); +} + +FileHandle::CloseReq::~CloseReq() { + uv_fs_req_cleanup(req()); + promise_.Reset(); + ref_.Reset(); +} + +void FileHandle::CloseReq::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("promise", promise_); + tracker->TrackField("ref", ref_); +} + + + // Closes this FileHandle asynchronously and returns a Promise that will be // resolved when the callback is invoked, or rejects with a UVException if // there was a problem closing the fd. This is the preferred mechanism for // closing the FD object even tho the object will attempt to close // automatically on gc. -inline MaybeLocal FileHandle::ClosePromise() { +MaybeLocal FileHandle::ClosePromise() { Isolate* isolate = env()->isolate(); EscapableHandleScope scope(isolate); Local context = env()->context(); @@ -1157,13 +1203,13 @@ int MKDirpSync(uv_loop_t* loop, FSContinuationData continuation_data(req, mode, cb); continuation_data.PushPath(std::move(path)); - while (continuation_data.paths.size() > 0) { + while (continuation_data.paths().size() > 0) { std::string next_path = continuation_data.PopPath(); int err = uv_fs_mkdir(loop, req, next_path.c_str(), mode, nullptr); while (true) { switch (err) { case 0: - if (continuation_data.paths.size() == 0) { + if (continuation_data.paths().size() == 0) { return 0; } break; @@ -1173,7 +1219,7 @@ int MKDirpSync(uv_loop_t* loop, if (dirname != next_path) { continuation_data.PushPath(std::move(next_path)); continuation_data.PushPath(std::move(dirname)); - } else if (continuation_data.paths.size() == 0) { + } else if (continuation_data.paths().size() == 0) { err = UV_EEXIST; continue; } @@ -1188,7 +1234,7 @@ int MKDirpSync(uv_loop_t* loop, err = uv_fs_stat(loop, req, next_path.c_str(), nullptr); if (err == 0 && !S_ISDIR(req->statbuf.st_mode)) { uv_fs_req_cleanup(req); - if (orig_err == UV_EEXIST && continuation_data.paths.size() > 0) { + if (orig_err == UV_EEXIST && continuation_data.paths().size() > 0) { return UV_ENOTDIR; } return UV_EEXIST; @@ -1211,14 +1257,14 @@ int MKDirpAsync(uv_loop_t* loop, uv_fs_cb cb) { FSReqBase* req_wrap = FSReqBase::from_req(req); // on the first iteration of algorithm, stash state information. - if (req_wrap->continuation_data == nullptr) { - req_wrap->continuation_data = - std::make_unique(req, mode, cb); - req_wrap->continuation_data->PushPath(std::move(path)); + if (req_wrap->continuation_data() == nullptr) { + req_wrap->set_continuation_data( + std::make_unique(req, mode, cb)); + req_wrap->continuation_data()->PushPath(std::move(path)); } // on each iteration of algorithm, mkdir directory on top of stack. - std::string next_path = req_wrap->continuation_data->PopPath(); + std::string next_path = req_wrap->continuation_data()->PopPath(); int err = uv_fs_mkdir(loop, req, next_path.c_str(), mode, uv_fs_callback_t{[](uv_fs_t* req) { FSReqBase* req_wrap = FSReqBase::from_req(req); @@ -1230,12 +1276,12 @@ int MKDirpAsync(uv_loop_t* loop, while (true) { switch (err) { case 0: { - if (req_wrap->continuation_data->paths.size() == 0) { - req_wrap->continuation_data->Done(0); + if (req_wrap->continuation_data()->paths().size() == 0) { + req_wrap->continuation_data()->Done(0); } else { uv_fs_req_cleanup(req); MKDirpAsync(loop, req, path.c_str(), - req_wrap->continuation_data->mode, nullptr); + req_wrap->continuation_data()->mode(), nullptr); } break; } @@ -1243,19 +1289,19 @@ int MKDirpAsync(uv_loop_t* loop, std::string dirname = path.substr(0, path.find_last_of(kPathSeparator)); if (dirname != path) { - req_wrap->continuation_data->PushPath(std::move(path)); - req_wrap->continuation_data->PushPath(std::move(dirname)); - } else if (req_wrap->continuation_data->paths.size() == 0) { + req_wrap->continuation_data()->PushPath(std::move(path)); + req_wrap->continuation_data()->PushPath(std::move(dirname)); + } else if (req_wrap->continuation_data()->paths().size() == 0) { err = UV_EEXIST; continue; } uv_fs_req_cleanup(req); MKDirpAsync(loop, req, path.c_str(), - req_wrap->continuation_data->mode, nullptr); + req_wrap->continuation_data()->mode(), nullptr); break; } case UV_EPERM: { - req_wrap->continuation_data->Done(err); + req_wrap->continuation_data()->Done(err); break; } default: @@ -1267,14 +1313,14 @@ int MKDirpAsync(uv_loop_t* loop, FSReqBase* req_wrap = FSReqBase::from_req(req); int err = req->result; if (reinterpret_cast(req->data) == UV_EEXIST && - req_wrap->continuation_data->paths.size() > 0) { + req_wrap->continuation_data()->paths().size() > 0) { if (err == 0 && S_ISDIR(req->statbuf.st_mode)) { Environment* env = req_wrap->env(); uv_loop_t* loop = env->event_loop(); std::string path = req->path; uv_fs_req_cleanup(req); MKDirpAsync(loop, req, path.c_str(), - req_wrap->continuation_data->mode, nullptr); + req_wrap->continuation_data()->mode(), nullptr); return; } err = UV_ENOTDIR; @@ -1282,9 +1328,9 @@ int MKDirpAsync(uv_loop_t* loop, // verify that the path pointed to is actually a directory. if (err == 0 && !S_ISDIR(req->statbuf.st_mode)) err = UV_EEXIST; uv_fs_req_cleanup(req); - req_wrap->continuation_data->Done(err); + req_wrap->continuation_data()->Done(err); }}); - if (err < 0) req_wrap->continuation_data->Done(err); + if (err < 0) req_wrap->continuation_data()->Done(err); break; } break; diff --git a/src/node_file.h b/src/node_file.h index 84f4032cc2f6a3..1042baaf8f736b 100644 --- a/src/node_file.h +++ b/src/node_file.h @@ -6,113 +6,70 @@ #include "node.h" #include "aliased_buffer.h" #include "stream_base.h" -#include "memory_tracker-inl.h" -#include "req_wrap-inl.h" #include namespace node { - -using v8::Context; -using v8::FunctionCallbackInfo; -using v8::HandleScope; -using v8::Local; -using v8::MaybeLocal; -using v8::Object; -using v8::Promise; -using v8::Undefined; -using v8::Value; - namespace fs { // structure used to store state during a complex operation, e.g., mkdirp. class FSContinuationData : public MemoryRetainer { public: - FSContinuationData(uv_fs_t* req, int mode, uv_fs_cb done_cb) - : req(req), mode(mode), done_cb(done_cb) { - } - - uv_fs_t* req; - int mode; - std::vector paths{}; + inline FSContinuationData(uv_fs_t* req, int mode, uv_fs_cb done_cb); - void PushPath(std::string&& path) { - paths.emplace_back(std::move(path)); - } - - void PushPath(const std::string& path) { - paths.push_back(path); - } + inline void PushPath(std::string&& path); + inline void PushPath(const std::string& path); + inline std::string PopPath(); + inline void Done(int result); - std::string PopPath() { - CHECK_GT(paths.size(), 0); - std::string path = std::move(paths.back()); - paths.pop_back(); - return path; - } - - void Done(int result) { - req->result = result; - done_cb(req); - } - - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackField("paths", paths); - } + int mode() const { return mode_; } + const std::vector& paths() const { return paths_; } + void MemoryInfo(MemoryTracker* tracker) const override; SET_MEMORY_INFO_NAME(FSContinuationData) SET_SELF_SIZE(FSContinuationData) private: - uv_fs_cb done_cb; + uv_fs_cb done_cb_; + uv_fs_t* req_; + int mode_; + std::vector paths_; }; class FSReqBase : public ReqWrap { public: typedef MaybeStackBuffer FSReqBuffer; - std::unique_ptr continuation_data = nullptr; - - FSReqBase(Environment* env, Local req, AsyncWrap::ProviderType type, - bool use_bigint) - : ReqWrap(env, req, type), use_bigint_(use_bigint) { - } - void Init(const char* syscall, - const char* data, - size_t len, - enum encoding encoding) { - syscall_ = syscall; - encoding_ = encoding; - - if (data != nullptr) { - CHECK(!has_data_); - buffer_.AllocateSufficientStorage(len + 1); - buffer_.SetLengthAndZeroTerminate(len); - memcpy(*buffer_, data, len); - has_data_ = true; - } - } - - FSReqBuffer& Init(const char* syscall, size_t len, - enum encoding encoding) { - syscall_ = syscall; - encoding_ = encoding; - - buffer_.AllocateSufficientStorage(len + 1); - has_data_ = false; // so that the data does not show up in error messages - return buffer_; - } - - virtual void Reject(Local reject) = 0; - virtual void Resolve(Local value) = 0; + inline FSReqBase(Environment* env, + v8::Local req, + AsyncWrap::ProviderType type, + bool use_bigint); + ~FSReqBase() override; + + inline void Init(const char* syscall, + const char* data, + size_t len, + enum encoding encoding); + inline FSReqBuffer& Init(const char* syscall, size_t len, + enum encoding encoding); + + virtual void Reject(v8::Local reject) = 0; + virtual void Resolve(v8::Local value) = 0; virtual void ResolveStat(const uv_stat_t* stat) = 0; - virtual void SetReturnValue(const FunctionCallbackInfo& args) = 0; + virtual void SetReturnValue( + const v8::FunctionCallbackInfo& args) = 0; const char* syscall() const { return syscall_; } const char* data() const { return has_data_ ? *buffer_ : nullptr; } enum encoding encoding() const { return encoding_; } - bool use_bigint() const { return use_bigint_; } + FSContinuationData* continuation_data() const { + return continuation_data_.get(); + } + void set_continuation_data(std::unique_ptr data) { + continuation_data_ = std::move(data); + } + static FSReqBase* from_req(uv_fs_t* req) { return static_cast(ReqWrap::from_req(req)); } @@ -120,7 +77,10 @@ class FSReqBase : public ReqWrap { FSReqBase(const FSReqBase&) = delete; FSReqBase& operator=(const FSReqBase&) = delete; + void MemoryInfo(MemoryTracker* tracker) const override; + private: + std::unique_ptr continuation_data_; enum encoding encoding_ = UTF8; bool has_data_ = false; const char* syscall_ = nullptr; @@ -131,19 +91,16 @@ class FSReqBase : public ReqWrap { FSReqBuffer buffer_; }; -class FSReqCallback : public FSReqBase { +class FSReqCallback final : public FSReqBase { public: - FSReqCallback(Environment* env, Local req, bool use_bigint) - : FSReqBase(env, req, AsyncWrap::PROVIDER_FSREQCALLBACK, use_bigint) { } + inline FSReqCallback(Environment* env, + v8::Local req, + bool use_bigint); - void Reject(Local reject) override; - void Resolve(Local value) override; + void Reject(v8::Local reject) override; + void Resolve(v8::Local value) override; void ResolveStat(const uv_stat_t* stat) override; - void SetReturnValue(const FunctionCallbackInfo& args) override; - - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackField("continuation_data", continuation_data); - } + void SetReturnValue(const v8::FunctionCallbackInfo& args) override; SET_MEMORY_INFO_NAME(FSReqCallback) SET_SELF_SIZE(FSReqCallback) @@ -153,120 +110,27 @@ class FSReqCallback : public FSReqBase { }; template -constexpr void FillStatsArray(AliasedBufferBase* fields, - const uv_stat_t* s, - const size_t offset = 0) { -#define SET_FIELD_WITH_STAT(stat_offset, stat) \ - fields->SetValue(offset + static_cast(FsStatsOffset::stat_offset), \ - static_cast(stat)) - -#define SET_FIELD_WITH_TIME_STAT(stat_offset, stat) \ - /* NOLINTNEXTLINE(runtime/int) */ \ - SET_FIELD_WITH_STAT(stat_offset, static_cast(stat)) - - SET_FIELD_WITH_STAT(kDev, s->st_dev); - SET_FIELD_WITH_STAT(kMode, s->st_mode); - SET_FIELD_WITH_STAT(kNlink, s->st_nlink); - SET_FIELD_WITH_STAT(kUid, s->st_uid); - SET_FIELD_WITH_STAT(kGid, s->st_gid); - SET_FIELD_WITH_STAT(kRdev, s->st_rdev); - SET_FIELD_WITH_STAT(kBlkSize, s->st_blksize); - SET_FIELD_WITH_STAT(kIno, s->st_ino); - SET_FIELD_WITH_STAT(kSize, s->st_size); - SET_FIELD_WITH_STAT(kBlocks, s->st_blocks); - - SET_FIELD_WITH_TIME_STAT(kATimeSec, s->st_atim.tv_sec); - SET_FIELD_WITH_TIME_STAT(kATimeNsec, s->st_atim.tv_nsec); - SET_FIELD_WITH_TIME_STAT(kMTimeSec, s->st_mtim.tv_sec); - SET_FIELD_WITH_TIME_STAT(kMTimeNsec, s->st_mtim.tv_nsec); - SET_FIELD_WITH_TIME_STAT(kCTimeSec, s->st_ctim.tv_sec); - SET_FIELD_WITH_TIME_STAT(kCTimeNsec, s->st_ctim.tv_nsec); - SET_FIELD_WITH_TIME_STAT(kBirthTimeSec, s->st_birthtim.tv_sec); - SET_FIELD_WITH_TIME_STAT(kBirthTimeNsec, s->st_birthtim.tv_nsec); - -#undef SET_FIELD_WITH_TIME_STAT -#undef SET_FIELD_WITH_STAT -} - -inline Local FillGlobalStatsArray(Environment* env, - const bool use_bigint, - const uv_stat_t* s, - const bool second = false) { - const ptrdiff_t offset = - second ? static_cast(FsStatsOffset::kFsStatsFieldsNumber) : 0; - if (use_bigint) { - auto* const arr = env->fs_stats_field_bigint_array(); - FillStatsArray(arr, s, offset); - return arr->GetJSArray(); - } else { - auto* const arr = env->fs_stats_field_array(); - FillStatsArray(arr, s, offset); - return arr->GetJSArray(); - } -} +void FillStatsArray(AliasedBufferBase* fields, + const uv_stat_t* s, + const size_t offset = 0); + +inline v8::Local FillGlobalStatsArray(Environment* env, + const bool use_bigint, + const uv_stat_t* s, + const bool second = false); template -class FSReqPromise : public FSReqBase { +class FSReqPromise final : public FSReqBase { public: - static FSReqPromise* New(Environment* env, bool use_bigint) { - v8::Local obj; - if (!env->fsreqpromise_constructor_template() - ->NewInstance(env->context()) - .ToLocal(&obj)) { - return nullptr; - } - v8::Local resolver; - if (!v8::Promise::Resolver::New(env->context()).ToLocal(&resolver) || - obj->Set(env->context(), env->promise_string(), resolver).IsNothing()) { - return nullptr; - } - return new FSReqPromise(env, obj, use_bigint); - } - - ~FSReqPromise() override { - // Validate that the promise was explicitly resolved or rejected. - CHECK(finished_); - } - - void Reject(Local reject) override { - finished_ = true; - HandleScope scope(env()->isolate()); - InternalCallbackScope callback_scope(this); - Local value = - object()->Get(env()->context(), - env()->promise_string()).ToLocalChecked(); - Local resolver = value.As(); - USE(resolver->Reject(env()->context(), reject).FromJust()); - } - - void Resolve(Local value) override { - finished_ = true; - HandleScope scope(env()->isolate()); - InternalCallbackScope callback_scope(this); - Local val = - object()->Get(env()->context(), - env()->promise_string()).ToLocalChecked(); - Local resolver = val.As(); - USE(resolver->Resolve(env()->context(), value).FromJust()); - } - - void ResolveStat(const uv_stat_t* stat) override { - FillStatsArray(&stats_field_array_, stat); - Resolve(stats_field_array_.GetJSArray()); - } - - void SetReturnValue(const FunctionCallbackInfo& args) override { - Local val = - object()->Get(env()->context(), - env()->promise_string()).ToLocalChecked(); - Local resolver = val.As(); - args.GetReturnValue().Set(resolver->GetPromise()); - } + static inline FSReqPromise* New(Environment* env, bool use_bigint); + inline ~FSReqPromise() override; - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackField("stats_field_array", stats_field_array_); - tracker->TrackField("continuation_data", continuation_data); - } + inline void Reject(v8::Local reject) override; + inline void Resolve(v8::Local value) override; + inline void ResolveStat(const uv_stat_t* stat) override; + inline void SetReturnValue( + const v8::FunctionCallbackInfo& args) override; + inline void MemoryInfo(MemoryTracker* tracker) const override; SET_MEMORY_INFO_NAME(FSReqPromise) SET_SELF_SIZE(FSReqPromise) @@ -277,17 +141,15 @@ class FSReqPromise : public FSReqBase { FSReqPromise& operator=(const FSReqPromise&&) = delete; private: - FSReqPromise(Environment* env, v8::Local obj, bool use_bigint) - : FSReqBase(env, obj, AsyncWrap::PROVIDER_FSREQPROMISE, use_bigint), - stats_field_array_( - env->isolate(), - static_cast(FsStatsOffset::kFsStatsFieldsNumber)) {} + inline FSReqPromise(Environment* env, + v8::Local obj, + bool use_bigint); bool finished_ = false; AliasedBufferT stats_field_array_; }; -class FSReqAfterScope { +class FSReqAfterScope final { public: FSReqAfterScope(FSReqBase* wrap, uv_fs_t* req); ~FSReqAfterScope(); @@ -304,17 +166,18 @@ class FSReqAfterScope { private: FSReqBase* wrap_ = nullptr; uv_fs_t* req_ = nullptr; - HandleScope handle_scope_; - Context::Scope context_scope_; + v8::HandleScope handle_scope_; + v8::Context::Scope context_scope_; }; class FileHandle; // A request wrap specifically for uv_fs_read()s scheduled for reading // from a FileHandle. -class FileHandleReadWrap : public ReqWrap { +class FileHandleReadWrap final : public ReqWrap { public: FileHandleReadWrap(FileHandle* handle, v8::Local obj); + ~FileHandleReadWrap() override; static inline FileHandleReadWrap* from_req(uv_fs_t* req) { return static_cast(ReqWrap::from_req(req)); @@ -333,7 +196,7 @@ class FileHandleReadWrap : public ReqWrap { // A wrapper for a file descriptor that will automatically close the fd when // the object is garbage collected -class FileHandle : public AsyncWrap, public StreamBase { +class FileHandle final : public AsyncWrap, public StreamBase { public: static FileHandle* New(Environment* env, int fd, @@ -346,10 +209,10 @@ class FileHandle : public AsyncWrap, public StreamBase { // Will asynchronously close the FD and return a Promise that will // be resolved once closing is complete. - static void Close(const FunctionCallbackInfo& args); + static void Close(const v8::FunctionCallbackInfo& args); // Releases ownership of the FD. - static void ReleaseFD(const FunctionCallbackInfo& args); + static void ReleaseFD(const v8::FunctionCallbackInfo& args); // StreamBase interface: int ReadStart() override; @@ -366,13 +229,9 @@ class FileHandle : public AsyncWrap, public StreamBase { int DoWrite(WriteWrap* w, uv_buf_t* bufs, size_t count, - uv_stream_t* send_handle) override { - return UV_ENOSYS; // Not implemented (yet). - } + uv_stream_t* send_handle) override; - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackField("current_read", current_read_); - } + void MemoryInfo(MemoryTracker* tracker) const override; SET_MEMORY_INFO_NAME(FileHandle) SET_SELF_SIZE(FileHandle) @@ -389,36 +248,24 @@ class FileHandle : public AsyncWrap, public StreamBase { void Close(); void AfterClose(); - class CloseReq : public ReqWrap { + class CloseReq final : public ReqWrap { public: CloseReq(Environment* env, - Local obj, - Local promise, - Local ref) - : ReqWrap(env, obj, AsyncWrap::PROVIDER_FILEHANDLECLOSEREQ) { - promise_.Reset(env->isolate(), promise); - ref_.Reset(env->isolate(), ref); - } - - ~CloseReq() override { - uv_fs_req_cleanup(req()); - promise_.Reset(); - ref_.Reset(); - } + v8::Local obj, + v8::Local promise, + v8::Local ref); + ~CloseReq() override; FileHandle* file_handle(); - void MemoryInfo(MemoryTracker* tracker) const override { - tracker->TrackField("promise", promise_); - tracker->TrackField("ref", ref_); - } + void MemoryInfo(MemoryTracker* tracker) const override; SET_MEMORY_INFO_NAME(CloseReq) SET_SELF_SIZE(CloseReq) void Resolve(); - void Reject(Local reason); + void Reject(v8::Local reason); static CloseReq* from_req(uv_fs_t* req) { return static_cast(ReqWrap::from_req(req)); @@ -430,12 +277,12 @@ class FileHandle : public AsyncWrap, public StreamBase { CloseReq& operator=(const CloseReq&&) = delete; private: - v8::Global promise_{}; - v8::Global ref_{}; + v8::Global promise_{}; + v8::Global ref_{}; }; // Asynchronous close - inline MaybeLocal ClosePromise(); + v8::MaybeLocal ClosePromise(); int fd_; bool closing_ = false; @@ -467,53 +314,23 @@ class FSReqWrapSync { // that nullptr indicates a synchronous call, rather than a failure. // Failure conditions should be disambiguated and handled appropriately. inline FSReqBase* GetReqWrap(Environment* env, v8::Local value, - bool use_bigint = false) { - if (value->IsObject()) { - return Unwrap(value.As()); - } else if (value->StrictEquals(env->fs_use_promises_symbol())) { - if (use_bigint) { - return FSReqPromise::New(env, use_bigint); - } else { - return FSReqPromise::New(env, use_bigint); - } - } - return nullptr; -} + bool use_bigint = false); // Returns nullptr if the operation fails from the start. template inline FSReqBase* AsyncDestCall(Environment* env, FSReqBase* req_wrap, - const v8::FunctionCallbackInfo& args, + const v8::FunctionCallbackInfo& args, const char* syscall, const char* dest, size_t len, enum encoding enc, uv_fs_cb after, - Func fn, Args... fn_args) { - CHECK_NOT_NULL(req_wrap); - req_wrap->Init(syscall, dest, len, enc); - int err = req_wrap->Dispatch(fn, fn_args..., after); - if (err < 0) { - uv_fs_t* uv_req = req_wrap->req(); - uv_req->result = err; - uv_req->path = nullptr; - after(uv_req); // after may delete req_wrap if there is an error - req_wrap = nullptr; - } else { - req_wrap->SetReturnValue(args); - } - - return req_wrap; -} + Func fn, Args... fn_args); // Returns nullptr if the operation fails from the start. template inline FSReqBase* AsyncCall(Environment* env, FSReqBase* req_wrap, - const v8::FunctionCallbackInfo& args, + const v8::FunctionCallbackInfo& args, const char* syscall, enum encoding enc, - uv_fs_cb after, Func fn, Args... fn_args) { - return AsyncDestCall(env, req_wrap, args, - syscall, nullptr, 0, enc, - after, fn, fn_args...); -} + uv_fs_cb after, Func fn, Args... fn_args); // Template counterpart of SYNC_CALL, except that it only puts // the error number and the syscall in the context instead of @@ -522,22 +339,7 @@ inline FSReqBase* AsyncCall(Environment* env, template inline int SyncCall(Environment* env, v8::Local ctx, FSReqWrapSync* req_wrap, const char* syscall, - Func fn, Args... args) { - env->PrintSyncTrace(); - int err = fn(env->event_loop(), &(req_wrap->req), args..., nullptr); - if (err < 0) { - v8::Local context = env->context(); - v8::Local ctx_obj = ctx.As(); - v8::Isolate* isolate = env->isolate(); - ctx_obj->Set(context, - env->errno_string(), - v8::Integer::New(isolate, err)).Check(); - ctx_obj->Set(context, - env->syscall_string(), - OneByteString(isolate, syscall)).Check(); - } - return err; -} + Func fn, Args... args); } // namespace fs diff --git a/src/node_stat_watcher.cc b/src/node_stat_watcher.cc index ae30825cbbdbd2..a3f4ef8f505d39 100644 --- a/src/node_stat_watcher.cc +++ b/src/node_stat_watcher.cc @@ -23,7 +23,7 @@ #include "node_stat_watcher.h" #include "async_wrap-inl.h" #include "env.h" -#include "node_file.h" +#include "node_file-inl.h" #include "util-inl.h" #include diff --git a/src/req_wrap-inl.h b/src/req_wrap-inl.h index 4fa4d0cf217069..c905b605cbde97 100644 --- a/src/req_wrap-inl.h +++ b/src/req_wrap-inl.h @@ -120,7 +120,7 @@ struct MakeLibuvRequestCallback { using F = void(*)(ReqT* req, Args... args); static void Wrapper(ReqT* req, Args... args) { - ReqWrap* req_wrap = ContainerOf(&ReqWrap::req_, req); + ReqWrap* req_wrap = ReqWrap::from_req(req); req_wrap->env()->DecreaseWaitingRequestCounter(); F original_callback = reinterpret_cast(req_wrap->original_callback_); original_callback(req, args...); diff --git a/src/req_wrap.h b/src/req_wrap.h index 36eeb1cbc24005..5d7fcb42d01148 100644 --- a/src/req_wrap.h +++ b/src/req_wrap.h @@ -50,9 +50,10 @@ class ReqWrap : public AsyncWrap, public ReqWrapBase { private: friend int GenDebugSymbols(); - template - friend struct MakeLibuvRequestCallback; + // Adding `friend struct MakeLibuvRequestCallback` is not enough anymore + // for some reason. Consider this private. + public: typedef void (*callback_t)(); callback_t original_callback_ = nullptr; From 6f313f9ab0944beba57e97a208ca10726845343d Mon Sep 17 00:00:00 2001 From: Michael Lehenbauer Date: Wed, 27 Nov 2019 08:11:56 -0800 Subject: [PATCH 135/180] http2: fix session memory accounting after pausing The ability to pause input processing was added in 8a4a193 but introduced a session memory accounting mismatch leading to potential NGHTTP2_ENHANCE_YOUR_CALM errors. After pausing (https://github.com/nodejs/node/blob/f36331c1bfa4c4c202346b05dc3bd672f653e4df/src/node_http2.cc#L871), the early return on line 873 skips the DecrementCurrentSessionMemory(stream_buf_.len) call below (line 878). When we later finished processing the input chunk (https://github.com/nodejs/node/blob/f36331c1bfa4c4c202346b05dc3bd672f653e4df/src/node_http2.cc#L1858), we were calling DecrementCurrentSessionMemory(stream_buf_offset_) [line 1875] which was a no-op since we just set stream_buf_offset_ to 0 [line 1873]. The correct amount to decrement by is still stream_buf_.len, since that's the amount we skipped previously (line 878). Fixes: https://github.com/nodejs/node/issues/29223 Refs: https://github.com/nodejs/node/commit/164ac5b241b96089e6bad5bb83ea416966b3245f PR-URL: https://github.com/nodejs/node/pull/30684 Reviewed-By: Anna Henningsen Reviewed-By: Denys Otrishko Reviewed-By: James M Snell Reviewed-By: David Carlier --- src/node_http2.cc | 5 +- ...-http2-large-writes-session-memory-leak.js | 55 +++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 test/parallel/test-http2-large-writes-session-memory-leak.js diff --git a/src/node_http2.cc b/src/node_http2.cc index 17a8a859d9194f..9421c36f3be561 100644 --- a/src/node_http2.cc +++ b/src/node_http2.cc @@ -1879,7 +1879,10 @@ void Http2Session::OnStreamRead(ssize_t nread, const uv_buf_t& buf_) { nread = buf.size(); stream_buf_offset_ = 0; stream_buf_ab_.Reset(); - DecrementCurrentSessionMemory(stream_buf_offset_); + + // We have now fully processed the stream_buf_ input chunk (by moving the + // remaining part into buf, which will be accounted for below). + DecrementCurrentSessionMemory(stream_buf_.len); } // Shrink to the actual amount of used data. diff --git a/test/parallel/test-http2-large-writes-session-memory-leak.js b/test/parallel/test-http2-large-writes-session-memory-leak.js new file mode 100644 index 00000000000000..641923c06c9133 --- /dev/null +++ b/test/parallel/test-http2-large-writes-session-memory-leak.js @@ -0,0 +1,55 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const fixtures = require('../common/fixtures'); +const http2 = require('http2'); + +// Regression test for https://github.com/nodejs/node/issues/29223. +// There was a "leak" in the accounting of session memory leading +// to streams eventually failing with NGHTTP2_ENHANCE_YOUR_CALM. + +const server = http2.createSecureServer({ + key: fixtures.readKey('agent2-key.pem'), + cert: fixtures.readKey('agent2-cert.pem'), +}); + +// Simple server that sends 200k and closes the stream. +const data200k = 'a'.repeat(200 * 1024); +server.on('stream', (stream) => { + stream.write(data200k); + stream.end(); +}); + +server.listen(0, common.mustCall(() => { + const client = http2.connect(`https://localhost:${server.address().port}`, { + ca: fixtures.readKey('agent2-cert.pem'), + servername: 'agent2', + + // Set maxSessionMemory to 1MB so the leak causes errors faster. + maxSessionMemory: 1 + }); + + // Repeatedly create a new stream and read the incoming data. Even though we + // only have one stream active at a time, prior to the fix for #29223, + // session memory would steadily increase and we'd eventually hit the 1MB + // maxSessionMemory limit and get NGHTTP2_ENHANCE_YOUR_CALM errors trying to + // create new streams. + let streamsLeft = 50; + function newStream() { + const stream = client.request({ ':path': '/' }); + + stream.on('data', () => { }); + + stream.on('close', () => { + if (streamsLeft-- > 0) { + newStream(); + } else { + client.destroy(); + server.close(); + } + }); + } + + newStream(); +})); From bfcc9142f32ab91950dfce7ae44bce565e102087 Mon Sep 17 00:00:00 2001 From: legendecas Date: Fri, 29 Nov 2019 00:20:00 +0800 Subject: [PATCH 136/180] doc,n-api: mark napi_detach_arraybuffer as experimental MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As its actual release stage. PR-URL: https://github.com/nodejs/node/pull/30703 Reviewed-By: Denys Otrishko Reviewed-By: Luigi Pinca Reviewed-By: Michael Dawson Reviewed-By: Tobias Nießen --- doc/api/n-api.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/api/n-api.md b/doc/api/n-api.md index d1305c14d8b951..da1db512894a5c 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -3239,6 +3239,8 @@ defined in [Section 7.2.14][] of the ECMAScript Language Specification. added: v13.0.0 --> +> Stability: 1 - Experimental + ```C napi_status napi_detach_arraybuffer(napi_env env, napi_value arraybuffer) From ad9f737e442f39758175dc5e4d7ccc9bca3432b8 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 8 Nov 2019 07:52:38 +0000 Subject: [PATCH 137/180] doc: documenting a bit more FreeBSD case FreeBSD provides more up to date compilers than the one provided by the system. PR-URL: https://github.com/nodejs/node/pull/30325 Reviewed-By: Richard Lau Reviewed-By: Jiawen Geng Reviewed-By: Rod Vagg Reviewed-By: James M Snell --- BUILDING.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/BUILDING.md b/BUILDING.md index a65198b7a86cfc..14206976e48a12 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -116,7 +116,7 @@ platforms. This is true regardless of entries in the table below. | macOS | x64 | >= 10.11 | Tier 1 | | | SmartOS | x64 | >= 18 | Tier 2 | | | AIX | ppc64be >=power7 | >= 7.2 TL02 | Tier 2 | | -| FreeBSD | x64 | >= 11 | Experimental | Downgraded as of Node.js 12 | +| FreeBSD | x64 | >= 11 | Experimental | Downgraded as of Node.js 12 [7](#fn7) | 1: GCC 6 is not provided on the base platform, users will need the @@ -150,6 +150,10 @@ are provided. However, tests in our infrastructure only run on WoW64. Furthermore, compiling on x86 Windows is currently considered Experimental and may not be possible. +7: The default FreeBSD 12.0 compiler is Clang 6.0.1, but +FreeBSD 12.1 upgrades to 8.0.1. Other Clang/LLVM versions are provided +via the system's package manager, including Clang 9.0. + ### Supported toolchains Depending on the host platform, the selection of toolchains may vary. From 8fcb450934dac5f8a4cf2cfccf7c395cc831533d Mon Sep 17 00:00:00 2001 From: Harendra Singh Date: Wed, 6 Nov 2019 14:36:25 +0530 Subject: [PATCH 138/180] doc: fixed a typo in process.md If the process was not spawned with an IPC channel, `process.send` will be undefined and calling it as `process.send()` would throw error PR-URL: https://github.com/nodejs/node/pull/30277 Reviewed-By: Anna Henningsen --- doc/api/process.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/process.md b/doc/api/process.md index a9b5c4af09215b..217fbb66bc0f9b 100644 --- a/doc/api/process.md +++ b/doc/api/process.md @@ -1957,7 +1957,7 @@ If Node.js is spawned with an IPC channel, the `process.send()` method can be used to send messages to the parent process. Messages will be received as a [`'message'`][] event on the parent's [`ChildProcess`][] object. -If Node.js was not spawned with an IPC channel, `process.send()` will be +If Node.js was not spawned with an IPC channel, `process.send` will be `undefined`. The message goes through serialization and parsing. The resulting message might From 4800b623ed644fbd70ff1c0b7280814772fd6854 Mon Sep 17 00:00:00 2001 From: garygsc Date: Sat, 26 Oct 2019 02:54:18 -0600 Subject: [PATCH 139/180] test: use arrow functions in addons tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert all anonymous callback functions in `test/addons/**/*.js` to use arrow functions, except for those in `test/addons/make-callback/test.js` (which reference `this`) `writing-tests.md` states to use arrow functions when appropriate. PR-URL: https://github.com/nodejs/node/pull/30131 Reviewed-By: James M Snell Reviewed-By: Colin Ihrig Reviewed-By: Tobias Nießen Reviewed-By: Yongsheng Zhang --- test/addons/async-hello-world/test-makecallback.js | 2 +- test/addons/async-hello-world/test.js | 2 +- test/addons/load-long-path/test.js | 2 +- test/addons/make-callback-domain-warning/test.js | 10 +++++----- test/addons/make-callback-recurse/test.js | 2 +- test/addons/openssl-client-cert-engine/test.js | 6 +++--- test/addons/openssl-key-engine/test.js | 6 +++--- test/addons/repl-domain-abort/test.js | 2 +- .../test-stringbytes-external-exceed-max-by-1-ascii.js | 2 +- ...test-stringbytes-external-exceed-max-by-1-base64.js | 2 +- ...test-stringbytes-external-exceed-max-by-1-binary.js | 2 +- .../test-stringbytes-external-exceed-max-by-1-hex.js | 2 +- .../test-stringbytes-external-exceed-max-by-1-utf8.js | 6 +++--- .../test-stringbytes-external-exceed-max.js | 2 +- 14 files changed, 24 insertions(+), 24 deletions(-) diff --git a/test/addons/async-hello-world/test-makecallback.js b/test/addons/async-hello-world/test-makecallback.js index 0edf052e8c34fc..abbc4be9c680c3 100644 --- a/test/addons/async-hello-world/test-makecallback.js +++ b/test/addons/async-hello-world/test-makecallback.js @@ -3,7 +3,7 @@ const common = require('../../common'); const assert = require('assert'); const { runMakeCallback } = require(`./build/${common.buildType}/binding`); -runMakeCallback(5, common.mustCall(function(err, val) { +runMakeCallback(5, common.mustCall((err, val) => { assert.strictEqual(err, null); assert.strictEqual(val, 10); process.nextTick(common.mustCall()); diff --git a/test/addons/async-hello-world/test.js b/test/addons/async-hello-world/test.js index f24071087c0629..72b31f7ab6ec20 100644 --- a/test/addons/async-hello-world/test.js +++ b/test/addons/async-hello-world/test.js @@ -3,7 +3,7 @@ const common = require('../../common'); const assert = require('assert'); const { runCall } = require(`./build/${common.buildType}/binding`); -runCall(5, common.mustCall(function(err, val) { +runCall(5, common.mustCall((err, val) => { assert.strictEqual(err, null); assert.strictEqual(val, 10); process.nextTick(common.mustCall()); diff --git a/test/addons/load-long-path/test.js b/test/addons/load-long-path/test.js index 168dad492b0255..9a9d0763994b08 100644 --- a/test/addons/load-long-path/test.js +++ b/test/addons/load-long-path/test.js @@ -48,6 +48,6 @@ fs.writeFileSync(addonDestinationPath, contents); // Run test const child = fork(__filename, ['child'], { stdio: 'inherit' }); -child.on('exit', common.mustCall(function(code) { +child.on('exit', common.mustCall((code) => { assert.strictEqual(code, 0); })); diff --git a/test/addons/make-callback-domain-warning/test.js b/test/addons/make-callback-domain-warning/test.js index 2ea3c3f3d14b2b..0377415e1269aa 100644 --- a/test/addons/make-callback-domain-warning/test.js +++ b/test/addons/make-callback-domain-warning/test.js @@ -10,23 +10,23 @@ function makeCallback(object, cb) { } let latestWarning = null; -process.on('warning', function(warning) { +process.on('warning', (warning) => { latestWarning = warning; }); const d = domain.create(); // When domain is disabled, no warning will be emitted -makeCallback({ domain: d }, common.mustCall(function() { +makeCallback({ domain: d }, common.mustCall(() => { assert.strictEqual(latestWarning, null); - d.run(common.mustCall(function() { + d.run(common.mustCall(() => { // No warning will be emitted when no domain property is applied - makeCallback({}, common.mustCall(function() { + makeCallback({}, common.mustCall(() => { assert.strictEqual(latestWarning, null); // Warning is emitted when domain property is used and domain is enabled - makeCallback({ domain: d }, common.mustCall(function() { + makeCallback({ domain: d }, common.mustCall(() => { assert.strictEqual(latestWarning.name, 'DeprecationWarning'); assert.strictEqual(latestWarning.code, 'DEP0097'); })); diff --git a/test/addons/make-callback-recurse/test.js b/test/addons/make-callback-recurse/test.js index a93a0a4e01bfd7..4a540003acd8d1 100644 --- a/test/addons/make-callback-recurse/test.js +++ b/test/addons/make-callback-recurse/test.js @@ -71,7 +71,7 @@ assert.throws(() => { if (arg === 1) { // The tests are first run on bootstrap during LoadEnvironment() in // src/node.cc. Now run the tests through node::MakeCallback(). - setImmediate(function() { + setImmediate(() => { makeCallback({}, common.mustCall(() => { verifyExecutionOrder(2); })); diff --git a/test/addons/openssl-client-cert-engine/test.js b/test/addons/openssl-client-cert-engine/test.js index 9e7d507b07b7ac..e843e4bf433bff 100644 --- a/test/addons/openssl-client-cert-engine/test.js +++ b/test/addons/openssl-client-cert-engine/test.js @@ -43,14 +43,14 @@ const server = https.createServer(serverOptions, common.mustCall((req, res) => { headers: {} }; - const req = https.request(clientOptions, common.mustCall(function(response) { + const req = https.request(clientOptions, common.mustCall((response) => { let body = ''; response.setEncoding('utf8'); - response.on('data', function(chunk) { + response.on('data', (chunk) => { body += chunk; }); - response.on('end', common.mustCall(function() { + response.on('end', common.mustCall(() => { assert.strictEqual(body, 'hello world'); server.close(); })); diff --git a/test/addons/openssl-key-engine/test.js b/test/addons/openssl-key-engine/test.js index 5c93e6263626bf..2cd7ddabc17095 100644 --- a/test/addons/openssl-key-engine/test.js +++ b/test/addons/openssl-key-engine/test.js @@ -45,14 +45,14 @@ const server = https.createServer(serverOptions, common.mustCall((req, res) => { headers: {} }; - const req = https.request(clientOptions, common.mustCall(function(response) { + const req = https.request(clientOptions, common.mustCall((response) => { let body = ''; response.setEncoding('utf8'); - response.on('data', function(chunk) { + response.on('data', (chunk) => { body += chunk; }); - response.on('end', common.mustCall(function() { + response.on('end', common.mustCall(() => { assert.strictEqual(body, 'hello world'); server.close(); })); diff --git a/test/addons/repl-domain-abort/test.js b/test/addons/repl-domain-abort/test.js index 2049fe6e6a23f5..f8e70ffce96e45 100644 --- a/test/addons/repl-domain-abort/test.js +++ b/test/addons/repl-domain-abort/test.js @@ -31,7 +31,7 @@ if (common.isWindows) buildPath = buildPath.replace(/\\/g, '/'); let cb_ran = false; -process.on('exit', function() { +process.on('exit', () => { assert(cb_ran); console.log('ok'); }); diff --git a/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-ascii.js b/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-ascii.js index 5e3033db3ea193..55f50f524927e8 100644 --- a/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-ascii.js +++ b/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-ascii.js @@ -25,7 +25,7 @@ if (!binding.ensureAllocation(2 * kStringMaxLength)) common.skip(skipMessage); const stringLengthHex = kStringMaxLength.toString(16); -common.expectsError(function() { +common.expectsError(() => { buf.toString('ascii'); }, { message: `Cannot create a string longer than 0x${stringLengthHex} ` + diff --git a/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-base64.js b/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-base64.js index 400477034d0010..20d968787bf731 100644 --- a/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-base64.js +++ b/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-base64.js @@ -25,7 +25,7 @@ if (!binding.ensureAllocation(2 * kStringMaxLength)) common.skip(skipMessage); const stringLengthHex = kStringMaxLength.toString(16); -common.expectsError(function() { +common.expectsError(() => { buf.toString('base64'); }, { message: `Cannot create a string longer than 0x${stringLengthHex} ` + diff --git a/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-binary.js b/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-binary.js index ef5d6a21ed0042..f6ae5217ddf0ec 100644 --- a/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-binary.js +++ b/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-binary.js @@ -27,7 +27,7 @@ if (!binding.ensureAllocation(2 * kStringMaxLength)) common.skip(skipMessage); const stringLengthHex = kStringMaxLength.toString(16); -common.expectsError(function() { +common.expectsError(() => { buf.toString('latin1'); }, { message: `Cannot create a string longer than 0x${stringLengthHex} ` + diff --git a/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-hex.js b/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-hex.js index 844c8bcb335a87..fa7a47cf184cd1 100644 --- a/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-hex.js +++ b/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-hex.js @@ -25,7 +25,7 @@ if (!binding.ensureAllocation(2 * kStringMaxLength)) common.skip(skipMessage); const stringLengthHex = kStringMaxLength.toString(16); -common.expectsError(function() { +common.expectsError(() => { buf.toString('hex'); }, { message: `Cannot create a string longer than 0x${stringLengthHex} ` + diff --git a/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-utf8.js b/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-utf8.js index 15ec78b6c9ab1c..43c88bd49e7e15 100644 --- a/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-utf8.js +++ b/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max-by-1-utf8.js @@ -27,9 +27,9 @@ if (!binding.ensureAllocation(2 * kStringMaxLength)) const stringLengthHex = kStringMaxLength.toString(16); -assert.throws(function() { +assert.throws(() => { buf.toString(); -}, function(e) { +}, (e) => { if (e.message !== 'Array buffer allocation failed') { common.expectsError({ message: `Cannot create a string longer than 0x${stringLengthHex} ` + @@ -43,7 +43,7 @@ assert.throws(function() { } }); -common.expectsError(function() { +common.expectsError(() => { buf.toString('utf8'); }, { message: `Cannot create a string longer than 0x${stringLengthHex} ` + diff --git a/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max.js b/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max.js index d65144e04d457c..8b419eb4362de0 100644 --- a/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max.js +++ b/test/addons/stringbytes-external-exceed-max/test-stringbytes-external-exceed-max.js @@ -26,7 +26,7 @@ if (!binding.ensureAllocation(2 * kStringMaxLength)) const stringLengthHex = kStringMaxLength.toString(16); -common.expectsError(function() { +common.expectsError(() => { buf.toString('utf16le'); }, { message: `Cannot create a string longer than 0x${stringLengthHex} ` + From a25626c1ed7fda83b412624cbe5218cfe9bc1246 Mon Sep 17 00:00:00 2001 From: Sam Foxman Date: Mon, 4 Nov 2019 17:45:27 -0500 Subject: [PATCH 140/180] doc: clarify IncomingMessage.destroy() description State that the 'error' event is emitted on the underlying socket, not the IncomingMessage object. PR-URL: https://github.com/nodejs/node/pull/30255 Reviewed-By: James M Snell Reviewed-By: Denys Otrishko --- doc/api/http.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/api/http.md b/doc/api/http.md index 44bea87e3bc6ca..9762abd27e2e00 100644 --- a/doc/api/http.md +++ b/doc/api/http.md @@ -1798,8 +1798,8 @@ added: v0.3.0 * `error` {Error} Calls `destroy()` on the socket that received the `IncomingMessage`. If `error` -is provided, an `'error'` event is emitted and `error` is passed as an argument -to any listeners on the event. +is provided, an `'error'` event is emitted on the socket and `error` is passed +as an argument to any listeners on the event. ### message.headers + +Enable experimental WebAssembly System Interface (WASI) support. + ### `--experimental-wasm-modules` + +> Stability: 1 - Experimental + +The WASI API provides an implementation of the [WebAssembly System Interface][] +specification. WASI gives sandboxed WebAssembly applications access to the +underlying operating system via a collection of POSIX-like functions. + +```js +'use strict'; +const fs = require('fs'); +const { WASI } = require('wasi'); +const wasi = new WASI({ + args: process.argv, + env: process.env, + preopens: { + '/sandbox': '/some/real/path/that/wasm/can/access' + } +}); +const importObject = { wasi_unstable: wasi.wasiImport }; + +(async () => { + const wasm = await WebAssembly.compile(fs.readFileSync('./binary.wasm')); + const instance = await WebAssembly.instantiate(wasm, importObject); + + wasi.start(instance); +})(); +``` + +The `--experimental-wasi-unstable-preview0` and `--experimental-wasm-bigint` +CLI arguments are needed for the previous example to run. + +## Class: WASI + + +The `WASI` class provides the WASI system call API and additional convenience +methods for working with WASI-based applications. Each `WASI` instance +represents a distinct sandbox environment. For security purposes, each `WASI` +instance must have its command line arguments, environment variables, and +sandbox directory structure configured explicitly. + +### new WASI(\[options\]) + + +* `options` {Object} + * `args` {Array} An array of strings that the WebAssembly application will + see as command line arguments. The first argument is the virtual path to the + WASI command itself. **Default:** `[]`. + * `env` {Object} An object similar to `process.env` that the WebAssembly + application will see as its environment. **Default:** `{}`. + * `preopens` {Object} This object represents the WebAssembly application's + sandbox directory structure. The string keys of `preopens` are treated as + directories within the sandbox. The corresponding values in `preopens` are + the real paths to those directories on the host machine. + +### wasi.start(instance) + + +* `instance` {WebAssembly.Instance} + +Attempt to begin execution of `instance` by invoking its `_start()` export. +If `instance` does not contain a `_start()` export, then `start()` attempts to +invoke the `__wasi_unstable_reactor_start()` export. If neither of those exports +is present on `instance`, then `start()` does nothing. + +`start()` requires that `instance` exports a [`WebAssembly.Memory`][] named +`memory`. If `instance` does not have a `memory` export an exception is thrown. + +### wasi.wasiImport + + +* {Object} + +`wasiImport` is an object that implements the WASI system call API. This object +should be passed as the `wasi_unstable` import during the instantiation of a +[`WebAssembly.Instance`][]. + +[`WebAssembly.Instance`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Instance +[`WebAssembly.Memory`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Memory +[WebAssembly System Interface]: https://wasi.dev/ diff --git a/doc/node.1 b/doc/node.1 index e3628034e832e5..714772336e07c1 100644 --- a/doc/node.1 +++ b/doc/node.1 @@ -141,6 +141,9 @@ Enable experimental support for a package to load itself. .It Fl -experimental-vm-modules Enable experimental ES module support in VM module. . +.It Fl -experimental-wasi-unstable-preview0 +Enable experimental WebAssembly System Interface support. +. .It Fl -experimental-wasm-modules Enable experimental WebAssembly module support. . diff --git a/lib/internal/bootstrap/loaders.js b/lib/internal/bootstrap/loaders.js index cfefc56bd81bc8..044bea3114b840 100644 --- a/lib/internal/bootstrap/loaders.js +++ b/lib/internal/bootstrap/loaders.js @@ -157,6 +157,9 @@ function NativeModule(id) { this.loaded = false; this.loading = false; this.canBeRequiredByUsers = !id.startsWith('internal/'); + + if (id === 'wasi') + this.canBeRequiredByUsers = !!internalBinding('config').experimentalWasi; } // To be called during pre-execution when --expose-internals is on. diff --git a/lib/internal/errors.js b/lib/internal/errors.js index ee467f31f3e1f3..88a38f5e1de62d 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -1229,6 +1229,7 @@ E('ERR_VM_MODULE_LINKING_ERRORED', E('ERR_VM_MODULE_NOT_MODULE', 'Provided module is not an instance of Module', Error); E('ERR_VM_MODULE_STATUS', 'Module status %s', Error); +E('ERR_WASI_ALREADY_STARTED', 'WASI instance has already started', Error); E('ERR_WORKER_INVALID_EXEC_ARGV', (errors) => `Initiated Worker with invalid execArgv flags: ${errors.join(', ')}`, Error); diff --git a/lib/internal/modules/cjs/helpers.js b/lib/internal/modules/cjs/helpers.js index f7155c4aa1803f..2d73219a77772c 100644 --- a/lib/internal/modules/cjs/helpers.js +++ b/lib/internal/modules/cjs/helpers.js @@ -117,6 +117,11 @@ const builtinLibs = [ 'v8', 'vm', 'worker_threads', 'zlib' ]; +if (internalBinding('config').experimentalWasi) { + builtinLibs.push('wasi'); + builtinLibs.sort(); +} + if (typeof internalBinding('inspector').open === 'function') { builtinLibs.push('inspector'); builtinLibs.sort(); diff --git a/lib/wasi.js b/lib/wasi.js new file mode 100644 index 00000000000000..65b8473624198c --- /dev/null +++ b/lib/wasi.js @@ -0,0 +1,105 @@ +'use strict'; +/* global WebAssembly */ +const { + ArrayIsArray, + ArrayPrototypeForEach, + ArrayPrototypeMap, + FunctionPrototypeBind, + ObjectKeys +} = primordials; +const { + ERR_INVALID_ARG_TYPE, + ERR_WASI_ALREADY_STARTED +} = require('internal/errors').codes; +const { emitExperimentalWarning } = require('internal/util'); +const { WASI: _WASI } = internalBinding('wasi'); +const kSetMemory = Symbol('setMemory'); +const kStarted = Symbol('started'); + +emitExperimentalWarning('WASI'); + + +class WASI { + constructor(options = {}) { + if (options === null || typeof options !== 'object') + throw new ERR_INVALID_ARG_TYPE('options', 'object', options); + + // eslint-disable-next-line prefer-const + let { args, env, preopens } = options; + + if (ArrayIsArray(args)) + args = ArrayPrototypeMap(args, (arg) => { return String(arg); }); + else if (args === undefined) + args = []; + else + throw new ERR_INVALID_ARG_TYPE('options.args', 'Array', args); + + const envPairs = []; + + if (env !== null && typeof env === 'object') { + for (const key in env) { + const value = env[key]; + if (value !== undefined) + envPairs.push(`${key}=${value}`); + } + } else if (env !== undefined) { + throw new ERR_INVALID_ARG_TYPE('options.env', 'Object', env); + } + + const preopenArray = []; + + if (typeof preopens === 'object' && preopens !== null) { + ArrayPrototypeForEach(ObjectKeys(preopens), (key) => { + preopenArray.push(String(key)); + preopenArray.push(String(preopens[key])); + }); + } else if (preopens !== undefined) { + throw new ERR_INVALID_ARG_TYPE('options.preopens', 'Object', preopens); + } + + const wrap = new _WASI(args, envPairs, preopenArray); + + for (const prop in wrap) { + wrap[prop] = FunctionPrototypeBind(wrap[prop], wrap); + } + + this[kSetMemory] = wrap._setMemory; + delete wrap._setMemory; + this.wasiImport = wrap; + this[kStarted] = false; + } + + start(instance) { + if (!(instance instanceof WebAssembly.Instance)) { + throw new ERR_INVALID_ARG_TYPE( + 'instance', 'WebAssembly.Instance', instance); + } + + const exports = instance.exports; + + if (exports === null || typeof exports !== 'object') + throw new ERR_INVALID_ARG_TYPE('instance.exports', 'Object', exports); + + const { memory } = exports; + + if (!(memory instanceof WebAssembly.Memory)) { + throw new ERR_INVALID_ARG_TYPE( + 'instance.exports.memory', 'WebAssembly.Memory', memory); + } + + if (this[kStarted]) { + throw new ERR_WASI_ALREADY_STARTED(); + } + + this[kStarted] = true; + this[kSetMemory](memory); + + if (exports._start) + exports._start(); + else if (exports.__wasi_unstable_reactor_start) + exports.__wasi_unstable_reactor_start(); + } +} + + +module.exports = { WASI }; diff --git a/node.gyp b/node.gyp index d8b55960833194..cffd150845e51a 100644 --- a/node.gyp +++ b/node.gyp @@ -82,6 +82,7 @@ 'lib/util.js', 'lib/v8.js', 'lib/vm.js', + 'lib/wasi.js', 'lib/worker_threads.js', 'lib/zlib.js', 'lib/internal/assert.js', @@ -321,7 +322,10 @@ 'src/node_main.cc' ], - 'dependencies': [ 'deps/histogram/histogram.gyp:histogram' ], + 'dependencies': [ + 'deps/histogram/histogram.gyp:histogram', + 'deps/uvwasi/uvwasi.gyp:uvwasi', + ], 'msvs_settings': { 'VCLinkerTool': { @@ -495,7 +499,10 @@ 'src', '<(SHARED_INTERMEDIATE_DIR)' # for node_natives.h ], - 'dependencies': [ 'deps/histogram/histogram.gyp:histogram' ], + 'dependencies': [ + 'deps/histogram/histogram.gyp:histogram', + 'deps/uvwasi/uvwasi.gyp:uvwasi', + ], 'sources': [ 'src/api/async_resource.cc', @@ -560,6 +567,7 @@ 'src/node_url.cc', 'src/node_util.cc', 'src/node_v8.cc', + 'src/node_wasi.cc', 'src/node_watchdog.cc', 'src/node_worker.cc', 'src/node_zlib.cc', @@ -638,6 +646,7 @@ 'src/node_url.h', 'src/node_version.h', 'src/node_v8_platform-inl.h', + 'src/node_wasi.h', 'src/node_watchdog.h', 'src/node_worker.h', 'src/pipe_wrap.h', @@ -1072,6 +1081,7 @@ 'dependencies': [ '<(node_lib_target_name)', 'deps/histogram/histogram.gyp:histogram', + 'deps/uvwasi/uvwasi.gyp:uvwasi', 'node_dtrace_header', 'node_dtrace_ustack', 'node_dtrace_provider', @@ -1087,6 +1097,7 @@ 'deps/v8/include', 'deps/cares/include', 'deps/uv/include', + 'deps/uvwasi/include', 'test/cctest', ], @@ -1181,6 +1192,7 @@ 'dependencies': [ '<(node_lib_target_name)', 'deps/histogram/histogram.gyp:histogram', + 'deps/uvwasi/uvwasi.gyp:uvwasi', ], 'includes': [ @@ -1193,6 +1205,7 @@ 'deps/v8/include', 'deps/cares/include', 'deps/uv/include', + 'deps/uvwasi/include', ], 'defines': [ @@ -1224,6 +1237,7 @@ 'dependencies': [ '<(node_lib_target_name)', 'deps/histogram/histogram.gyp:histogram', + 'deps/uvwasi/uvwasi.gyp:uvwasi', ], 'includes': [ @@ -1236,6 +1250,7 @@ 'deps/v8/include', 'deps/cares/include', 'deps/uv/include', + 'deps/uvwasi/include', ], 'defines': [ 'NODE_WANT_INTERNALS=1' ], diff --git a/src/env-inl.h b/src/env-inl.h index d34bd03c7c6896..d75b4ea743c4f4 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -1102,6 +1102,21 @@ inline void Environment::SetProtoMethodNoSideEffect( t->SetClassName(name_string); // NODE_SET_PROTOTYPE_METHOD() compatibility. } +inline void Environment::SetInstanceMethod(v8::Local that, + const char* name, + v8::FunctionCallback callback) { + v8::Local signature = v8::Signature::New(isolate(), that); + v8::Local t = + NewFunctionTemplate(callback, signature, v8::ConstructorBehavior::kThrow, + v8::SideEffectType::kHasSideEffect); + // kInternalized strings are created in the old space. + const v8::NewStringType type = v8::NewStringType::kInternalized; + v8::Local name_string = + v8::String::NewFromUtf8(isolate(), name, type).ToLocalChecked(); + that->InstanceTemplate()->Set(name_string, t); + t->SetClassName(name_string); +} + void Environment::AddCleanupHook(void (*fn)(void*), void* arg) { auto insertion_info = cleanup_hooks_.emplace(CleanupHookCallback { fn, arg, cleanup_hook_counter_++ diff --git a/src/env.h b/src/env.h index c0c8ac0db4882d..c25a03ea1e520a 100644 --- a/src/env.h +++ b/src/env.h @@ -541,7 +541,8 @@ struct ContextInfo { #define DEBUG_CATEGORY_NAMES(V) \ NODE_ASYNC_PROVIDER_TYPES(V) \ V(INSPECTOR_SERVER) \ - V(INSPECTOR_PROFILER) + V(INSPECTOR_PROFILER) \ + V(WASI) enum class DebugCategory { #define V(name) name, @@ -1114,6 +1115,11 @@ class Environment : public MemoryRetainer { const char* name, v8::FunctionCallback callback); + inline void SetInstanceMethod(v8::Local that, + const char* name, + v8::FunctionCallback callback); + + // Safe variants denote the function has no side effects. inline void SetMethodNoSideEffect(v8::Local that, const char* name, diff --git a/src/node_binding.cc b/src/node_binding.cc index f57ddb54b629ca..82836585c589a5 100644 --- a/src/node_binding.cc +++ b/src/node_binding.cc @@ -85,6 +85,7 @@ V(util) \ V(uv) \ V(v8) \ + V(wasi) \ V(worker) \ V(zlib) diff --git a/src/node_config.cc b/src/node_config.cc index 92985dff2f8b0c..16050bdd5b967d 100644 --- a/src/node_config.cc +++ b/src/node_config.cc @@ -84,6 +84,9 @@ static void Initialize(Local target, READONLY_PROPERTY(target, "hasCachedBuiltins", v8::Boolean::New(isolate, native_module::has_code_cache)); + + if (env->options()->experimental_wasi) + READONLY_TRUE_PROPERTY(target, "experimentalWasi"); } // InitConfig } // namespace node diff --git a/src/node_options.cc b/src/node_options.cc index 505441d0620b29..498bedd1e5e892 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -347,6 +347,10 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() { &EnvironmentOptions::experimental_report, kAllowedInEnvironment); #endif // NODE_REPORT + AddOption("--experimental-wasi-unstable-preview0", + "experimental WASI support", + &EnvironmentOptions::experimental_wasi, + kAllowedInEnvironment); AddOption("--expose-internals", "", &EnvironmentOptions::expose_internals); AddOption("--frozen-intrinsics", "experimental frozen intrinsics support", diff --git a/src/node_options.h b/src/node_options.h index 763e6ab9c87b57..fea912da446568 100644 --- a/src/node_options.h +++ b/src/node_options.h @@ -152,6 +152,7 @@ class EnvironmentOptions : public Options { #ifdef NODE_REPORT bool experimental_report = false; #endif // NODE_REPORT + bool experimental_wasi = false; std::string eval_string; bool print_eval = false; bool force_repl = false; diff --git a/src/node_wasi.cc b/src/node_wasi.cc new file mode 100644 index 00000000000000..8de7f1fc1cae68 --- /dev/null +++ b/src/node_wasi.cc @@ -0,0 +1,1802 @@ +#include "env-inl.h" +#include "base_object-inl.h" +#include "debug_utils.h" +#include "util-inl.h" +#include "node.h" +#include "uv.h" +#include "uvwasi.h" +#include "node_wasi.h" + +namespace node { +namespace wasi { + +static inline bool is_access_oob(size_t mem_size, + uint32_t offset, + uint32_t buf_size) { + return offset + buf_size > mem_size; +} + +template +inline void Debug(WASI* wasi, Args&&... args) { + Debug(wasi->env(), DebugCategory::WASI, std::forward(args)...); +} + +#define RETURN_IF_BAD_ARG_COUNT(args, expected) \ + do { \ + if ((args).Length() != (expected)) { \ + (args).GetReturnValue().Set(UVWASI_EINVAL); \ + return; \ + } \ + } while (0) + +#define CHECK_TO_TYPE_OR_RETURN(args, input, type, result) \ + do { \ + if (!(input)->Is##type()) { \ + (args).GetReturnValue().Set(UVWASI_EINVAL); \ + return; \ + } \ + (result) = (input).As()->Value(); \ + } while (0) + +#define UNWRAP_BIGINT_OR_RETURN(args, input, type, result) \ + do { \ + if (!(input)->IsBigInt()) { \ + (args).GetReturnValue().Set(UVWASI_EINVAL); \ + return; \ + } \ + Local js_value = (input).As(); \ + bool lossless; \ + (result) = js_value->type ## Value(&lossless); \ + } while (0) + +#define GET_BACKING_STORE_OR_RETURN(wasi, args, mem_ptr, mem_size) \ + do { \ + uvwasi_errno_t err = (wasi)->backingStore((mem_ptr), (mem_size)); \ + if (err != UVWASI_ESUCCESS) { \ + (args).GetReturnValue().Set(err); \ + return; \ + } \ + } while (0) + +#define CHECK_BOUNDS_OR_RETURN(args, mem_size, offset, buf_size) \ + do { \ + if (is_access_oob((mem_size), (offset), (buf_size))) { \ + (args).GetReturnValue().Set(UVWASI_EOVERFLOW); \ + return; \ + } \ + } while (0) + + +using v8::Array; +using v8::ArrayBuffer; +using v8::BackingStore; +using v8::BigInt; +using v8::Context; +using v8::FunctionCallbackInfo; +using v8::FunctionTemplate; +using v8::Local; +using v8::Object; +using v8::String; +using v8::Uint32; +using v8::Value; + + +WASI::WASI(Environment* env, + Local object, + uvwasi_options_t* options) : BaseObject(env, object) { + MakeWeak(); + CHECK_EQ(uvwasi_init(&uvw_, options), UVWASI_ESUCCESS); +} + + +WASI::~WASI() { + uvwasi_destroy(&uvw_); +} + + +void WASI::New(const FunctionCallbackInfo& args) { + CHECK(args.IsConstructCall()); + CHECK_EQ(args.Length(), 3); + CHECK(args[0]->IsArray()); + CHECK(args[1]->IsArray()); + CHECK(args[2]->IsArray()); + + Environment* env = Environment::GetCurrent(args); + Local context = env->context(); + Local argv = args[0].As(); + const uint32_t argc = argv->Length(); + uvwasi_options_t options; + + options.fd_table_size = 3; + options.argc = argc; + options.argv = argc == 0 ? nullptr : new char*[argc]; + + for (uint32_t i = 0; i < argc; i++) { + auto arg = argv->Get(context, i).ToLocalChecked(); + CHECK(arg->IsString()); + node::Utf8Value str(env->isolate(), arg); + options.argv[i] = strdup(*str); + CHECK_NOT_NULL(options.argv[i]); + } + + Local env_pairs = args[1].As(); + const uint32_t envc = env_pairs->Length(); + options.envp = new char*[envc + 1]; + for (uint32_t i = 0; i < envc; i++) { + auto pair = env_pairs->Get(context, i).ToLocalChecked(); + CHECK(pair->IsString()); + node::Utf8Value str(env->isolate(), pair); + options.envp[i] = strdup(*str); + CHECK_NOT_NULL(options.envp[i]); + } + options.envp[envc] = nullptr; + + Local preopens = args[2].As(); + CHECK_EQ(preopens->Length() % 2, 0); + options.preopenc = preopens->Length() / 2; + options.preopens = UncheckedCalloc(options.preopenc); + int index = 0; + for (uint32_t i = 0; i < preopens->Length(); i += 2) { + auto mapped = preopens->Get(context, i).ToLocalChecked(); + auto real = preopens->Get(context, i + 1).ToLocalChecked(); + CHECK(mapped->IsString()); + CHECK(real->IsString()); + node::Utf8Value mapped_path(env->isolate(), mapped); + node::Utf8Value real_path(env->isolate(), real); + options.preopens[index].mapped_path = strdup(*mapped_path); + options.preopens[index].real_path = strdup(*real_path); + index++; + } + + new WASI(env, args.This(), &options); + + if (options.argv != nullptr) { + for (uint32_t i = 0; i < argc; i++) + free(options.argv[i]); + delete[] options.argv; + } + + if (options.envp != nullptr) { + for (uint32_t i = 0; options.envp[i]; i++) + free(options.envp[i]); + delete[] options.envp; + } +} + + +void WASI::ArgsGet(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t argv_offset; + uint32_t argv_buf_offset; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, argv_offset); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, argv_buf_offset); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "args_get(%d, %d)\n", argv_offset, argv_buf_offset); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, + mem_size, + argv_buf_offset, + wasi->uvw_.argv_buf_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, argv_offset, wasi->uvw_.argc * 4); + std::vector argv(wasi->uvw_.argc); + char* argv_buf = &memory[argv_buf_offset]; + uvwasi_errno_t err = uvwasi_args_get(&wasi->uvw_, argv.data(), argv_buf); + + if (err == UVWASI_ESUCCESS) { + for (size_t i = 0; i < wasi->uvw_.argc; i++) { + uint32_t offset = argv_buf_offset + (argv[i] - argv[0]); + wasi->writeUInt32(memory, offset, argv_offset + (i * 4)); + } + } + + args.GetReturnValue().Set(err); +} + + +void WASI::ArgsSizesGet(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t argc_offset; + uint32_t argv_buf_offset; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, argc_offset); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, argv_buf_offset); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "args_sizes_get(%d, %d)\n", argc_offset, argv_buf_offset); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, argc_offset, 4); + CHECK_BOUNDS_OR_RETURN(args, mem_size, argv_buf_offset, 4); + size_t argc; + size_t argv_buf_size; + uvwasi_errno_t err = uvwasi_args_sizes_get(&wasi->uvw_, + &argc, + &argv_buf_size); + if (err == UVWASI_ESUCCESS) { + wasi->writeUInt32(memory, argc, argc_offset); + wasi->writeUInt32(memory, argv_buf_size, argv_buf_offset); + } + + args.GetReturnValue().Set(err); +} + + +void WASI::ClockResGet(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t clock_id; + uint32_t resolution_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, clock_id); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, resolution_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "clock_res_get(%d, %d)\n", clock_id, resolution_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, resolution_ptr, 8); + uvwasi_timestamp_t resolution; + uvwasi_errno_t err = uvwasi_clock_res_get(&wasi->uvw_, + clock_id, + &resolution); + if (err == UVWASI_ESUCCESS) + wasi->writeUInt64(memory, resolution, resolution_ptr); + + args.GetReturnValue().Set(err); +} + + +void WASI::ClockTimeGet(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t clock_id; + uint64_t precision; + uint32_t time_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 3); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, clock_id); + UNWRAP_BIGINT_OR_RETURN(args, args[1], Uint64, precision); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, time_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "clock_time_get(%d, %d, %d)\n", clock_id, precision, time_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, time_ptr, 8); + uvwasi_timestamp_t time; + uvwasi_errno_t err = uvwasi_clock_time_get(&wasi->uvw_, + clock_id, + precision, + &time); + if (err == UVWASI_ESUCCESS) + wasi->writeUInt64(memory, time, time_ptr); + + args.GetReturnValue().Set(err); +} + + +void WASI::EnvironGet(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t environ_offset; + uint32_t environ_buf_offset; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, environ_offset); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, environ_buf_offset); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "environ_get(%d, %d)\n", environ_offset, environ_buf_offset); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, + mem_size, + environ_buf_offset, + wasi->uvw_.env_buf_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, environ_offset, wasi->uvw_.envc * 4); + std::vector environment(wasi->uvw_.envc); + char* environ_buf = &memory[environ_buf_offset]; + uvwasi_errno_t err = uvwasi_environ_get(&wasi->uvw_, + environment.data(), + environ_buf); + + if (err == UVWASI_ESUCCESS) { + for (size_t i = 0; i < wasi->uvw_.envc; i++) { + uint32_t offset = environ_buf_offset + (environment[i] - environment[0]); + wasi->writeUInt32(memory, offset, environ_offset + (i * 4)); + } + } + + args.GetReturnValue().Set(err); +} + + +void WASI::EnvironSizesGet(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t envc_offset; + uint32_t env_buf_offset; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, envc_offset); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, env_buf_offset); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "environ_sizes_get(%d, %d)\n", envc_offset, env_buf_offset); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, envc_offset, 4); + CHECK_BOUNDS_OR_RETURN(args, mem_size, env_buf_offset, 4); + size_t envc; + size_t env_buf_size; + uvwasi_errno_t err = uvwasi_environ_sizes_get(&wasi->uvw_, + &envc, + &env_buf_size); + if (err == UVWASI_ESUCCESS) { + wasi->writeUInt32(memory, envc, envc_offset); + wasi->writeUInt32(memory, env_buf_size, env_buf_offset); + } + + args.GetReturnValue().Set(err); +} + + +void WASI::FdAdvise(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint64_t offset; + uint64_t len; + uint8_t advice; + RETURN_IF_BAD_ARG_COUNT(args, 4); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + UNWRAP_BIGINT_OR_RETURN(args, args[1], Uint64, offset); + UNWRAP_BIGINT_OR_RETURN(args, args[2], Uint64, len); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, advice); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_advise(%d, %d, %d, %d)\n", fd, offset, len, advice); + uvwasi_errno_t err = uvwasi_fd_advise(&wasi->uvw_, fd, offset, len, advice); + args.GetReturnValue().Set(err); +} + + +void WASI::FdAllocate(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint64_t offset; + uint64_t len; + RETURN_IF_BAD_ARG_COUNT(args, 3); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + UNWRAP_BIGINT_OR_RETURN(args, args[1], Uint64, offset); + UNWRAP_BIGINT_OR_RETURN(args, args[2], Uint64, len); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_allocate(%d, %d, %d)\n", fd, offset, len); + uvwasi_errno_t err = uvwasi_fd_allocate(&wasi->uvw_, fd, offset, len); + args.GetReturnValue().Set(err); +} + + +void WASI::FdClose(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + RETURN_IF_BAD_ARG_COUNT(args, 1); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_close(%d)\n", fd); + uvwasi_errno_t err = uvwasi_fd_close(&wasi->uvw_, fd); + args.GetReturnValue().Set(err); +} + + +void WASI::FdDatasync(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + RETURN_IF_BAD_ARG_COUNT(args, 1); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_datasync(%d)\n", fd); + uvwasi_errno_t err = uvwasi_fd_datasync(&wasi->uvw_, fd); + args.GetReturnValue().Set(err); +} + + +void WASI::FdFdstatGet(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t buf; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, buf); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_fdstat_get(%d, %d)\n", fd, buf); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, buf, 24); + uvwasi_fdstat_t stats; + uvwasi_errno_t err = uvwasi_fd_fdstat_get(&wasi->uvw_, fd, &stats); + + if (err == UVWASI_ESUCCESS) { + wasi->writeUInt8(memory, stats.fs_filetype, buf); + wasi->writeUInt16(memory, stats.fs_flags, buf + 2); + wasi->writeUInt64(memory, stats.fs_rights_base, buf + 8); + wasi->writeUInt64(memory, stats.fs_rights_inheriting, buf + 16); + } + + args.GetReturnValue().Set(err); +} + + +void WASI::FdFdstatSetFlags(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint16_t flags; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, flags); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_fdstat_set_flags(%d, %d)\n", fd, flags); + uvwasi_errno_t err = uvwasi_fd_fdstat_set_flags(&wasi->uvw_, fd, flags); + args.GetReturnValue().Set(err); +} + + +void WASI::FdFdstatSetRights(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint64_t fs_rights_base; + uint64_t fs_rights_inheriting; + RETURN_IF_BAD_ARG_COUNT(args, 3); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + UNWRAP_BIGINT_OR_RETURN(args, args[1], Uint64, fs_rights_base); + UNWRAP_BIGINT_OR_RETURN(args, args[2], Uint64, fs_rights_inheriting); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "fd_fdstat_set_rights(%d, %d, %d)\n", + fd, + fs_rights_base, + fs_rights_inheriting); + uvwasi_errno_t err = uvwasi_fd_fdstat_set_rights(&wasi->uvw_, + fd, + fs_rights_base, + fs_rights_inheriting); + args.GetReturnValue().Set(err); +} + + +void WASI::FdFilestatGet(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t buf; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, buf); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_filestat_get(%d, %d)\n", fd, buf); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, buf, 56); + uvwasi_filestat_t stats; + uvwasi_errno_t err = uvwasi_fd_filestat_get(&wasi->uvw_, fd, &stats); + + if (err == UVWASI_ESUCCESS) { + wasi->writeUInt64(memory, stats.st_dev, buf); + wasi->writeUInt64(memory, stats.st_ino, buf + 8); + wasi->writeUInt8(memory, stats.st_filetype, buf + 16); + wasi->writeUInt32(memory, stats.st_nlink, buf + 20); + wasi->writeUInt64(memory, stats.st_size, buf + 24); + wasi->writeUInt64(memory, stats.st_atim, buf + 32); + wasi->writeUInt64(memory, stats.st_mtim, buf + 40); + wasi->writeUInt64(memory, stats.st_ctim, buf + 48); + } + + args.GetReturnValue().Set(err); +} + + +void WASI::FdFilestatSetSize(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint64_t st_size; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + UNWRAP_BIGINT_OR_RETURN(args, args[1], Uint64, st_size); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_filestat_set_size(%d, %d)\n", fd, st_size); + uvwasi_errno_t err = uvwasi_fd_filestat_set_size(&wasi->uvw_, fd, st_size); + args.GetReturnValue().Set(err); +} + + +void WASI::FdFilestatSetTimes(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint64_t st_atim; + uint64_t st_mtim; + uint16_t fst_flags; + RETURN_IF_BAD_ARG_COUNT(args, 4); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + UNWRAP_BIGINT_OR_RETURN(args, args[1], Uint64, st_atim); + UNWRAP_BIGINT_OR_RETURN(args, args[2], Uint64, st_mtim); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, fst_flags); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "fd_filestat_set_times(%d, %d, %d, %d)\n", + fd, + st_atim, + st_mtim, + fst_flags); + uvwasi_errno_t err = uvwasi_fd_filestat_set_times(&wasi->uvw_, + fd, + st_atim, + st_mtim, + fst_flags); + args.GetReturnValue().Set(err); +} + + +void WASI::FdPread(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t iovs_ptr; + uint32_t iovs_len; + uint64_t offset; + uint32_t nread_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 5); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, iovs_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, iovs_len); + UNWRAP_BIGINT_OR_RETURN(args, args[3], Uint64, offset); + CHECK_TO_TYPE_OR_RETURN(args, args[4], Uint32, nread_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "uvwasi_fd_pread(%d, %d, %d, %d, %d)\n", + fd, + iovs_ptr, + iovs_len, + offset, + nread_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, iovs_ptr, iovs_len * 8); + CHECK_BOUNDS_OR_RETURN(args, mem_size, nread_ptr, 4); + uvwasi_iovec_t* iovs = UncheckedCalloc(iovs_len); + + if (iovs == nullptr) { + args.GetReturnValue().Set(UVWASI_ENOMEM); + return; + } + + for (uint32_t i = 0; i < iovs_len; ++i) { + uint32_t buf_ptr; + uint32_t buf_len; + + wasi->readUInt32(memory, &buf_ptr, iovs_ptr); + wasi->readUInt32(memory, &buf_len, iovs_ptr + 4); + + if (is_access_oob(mem_size, buf_ptr, buf_len)) { + free(iovs); + args.GetReturnValue().Set(UVWASI_EOVERFLOW); + return; + } + + iovs_ptr += 8; + iovs[i].buf = static_cast(&memory[buf_ptr]); + iovs[i].buf_len = buf_len; + } + + size_t nread; + uvwasi_errno_t err = uvwasi_fd_pread(&wasi->uvw_, + fd, + iovs, + iovs_len, + offset, + &nread); + if (err == UVWASI_ESUCCESS) + wasi->writeUInt32(memory, nread, nread_ptr); + + free(iovs); + args.GetReturnValue().Set(err); +} + + +void WASI::FdPrestatGet(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t buf; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, buf); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_prestat_get(%d, %d)\n", fd, buf); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, buf, 8); + uvwasi_prestat_t prestat; + uvwasi_errno_t err = uvwasi_fd_prestat_get(&wasi->uvw_, fd, &prestat); + + if (err == UVWASI_ESUCCESS) { + wasi->writeUInt32(memory, prestat.pr_type, buf); + wasi->writeUInt32(memory, prestat.u.dir.pr_name_len, buf + 4); + } + + args.GetReturnValue().Set(err); +} + + +void WASI::FdPrestatDirName(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t path_ptr; + uint32_t path_len; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 3); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, path_len); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_prestat_dir_name(%d, %d, %d)\n", fd, path_ptr, path_len); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, path_ptr, path_len); + uvwasi_errno_t err = uvwasi_fd_prestat_dir_name(&wasi->uvw_, + fd, + &memory[path_ptr], + path_len); + args.GetReturnValue().Set(err); +} + + +void WASI::FdPwrite(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t iovs_ptr; + uint32_t iovs_len; + uint64_t offset; + uint32_t nwritten_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 5); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, iovs_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, iovs_len); + UNWRAP_BIGINT_OR_RETURN(args, args[3], Uint64, offset); + CHECK_TO_TYPE_OR_RETURN(args, args[4], Uint32, nwritten_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "uvwasi_fd_pwrite(%d, %d, %d, %d, %d)\n", + fd, + iovs_ptr, + iovs_len, + offset, + nwritten_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, iovs_ptr, iovs_len * 8); + CHECK_BOUNDS_OR_RETURN(args, mem_size, nwritten_ptr, 4); + uvwasi_ciovec_t* iovs = UncheckedCalloc(iovs_len); + + if (iovs == nullptr) { + args.GetReturnValue().Set(UVWASI_ENOMEM); + return; + } + + for (uint32_t i = 0; i < iovs_len; ++i) { + uint32_t buf_ptr; + uint32_t buf_len; + + wasi->readUInt32(memory, &buf_ptr, iovs_ptr); + wasi->readUInt32(memory, &buf_len, iovs_ptr + 4); + + if (is_access_oob(mem_size, buf_ptr, buf_len)) { + free(iovs); + args.GetReturnValue().Set(UVWASI_EOVERFLOW); + return; + } + + iovs_ptr += 8; + iovs[i].buf = static_cast(&memory[buf_ptr]); + iovs[i].buf_len = buf_len; + } + + size_t nwritten; + uvwasi_errno_t err = uvwasi_fd_pwrite(&wasi->uvw_, + fd, + iovs, + iovs_len, + offset, + &nwritten); + if (err == UVWASI_ESUCCESS) + wasi->writeUInt32(memory, nwritten, nwritten_ptr); + + free(iovs); + args.GetReturnValue().Set(err); +} + + +void WASI::FdRead(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t iovs_ptr; + uint32_t iovs_len; + uint32_t nread_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 4); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, iovs_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, iovs_len); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, nread_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_read(%d, %d, %d, %d)\n", fd, iovs_ptr, iovs_len, nread_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, iovs_ptr, iovs_len * 8); + CHECK_BOUNDS_OR_RETURN(args, mem_size, nread_ptr, 4); + uvwasi_iovec_t* iovs = UncheckedCalloc(iovs_len); + + if (iovs == nullptr) { + args.GetReturnValue().Set(UVWASI_ENOMEM); + return; + } + + for (uint32_t i = 0; i < iovs_len; ++i) { + uint32_t buf_ptr; + uint32_t buf_len; + + wasi->readUInt32(memory, &buf_ptr, iovs_ptr); + wasi->readUInt32(memory, &buf_len, iovs_ptr + 4); + + if (is_access_oob(mem_size, buf_ptr, buf_len)) { + free(iovs); + args.GetReturnValue().Set(UVWASI_EOVERFLOW); + return; + } + + iovs_ptr += 8; + iovs[i].buf = static_cast(&memory[buf_ptr]); + iovs[i].buf_len = buf_len; + } + + size_t nread; + uvwasi_errno_t err = uvwasi_fd_read(&wasi->uvw_, + fd, + iovs, + iovs_len, + &nread); + if (err == UVWASI_ESUCCESS) + wasi->writeUInt32(memory, nread, nread_ptr); + + free(iovs); + args.GetReturnValue().Set(err); +} + + +void WASI::FdReaddir(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t buf_ptr; + uint32_t buf_len; + uint64_t cookie; + uint32_t bufused_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 5); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, buf_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, buf_len); + UNWRAP_BIGINT_OR_RETURN(args, args[3], Uint64, cookie); + CHECK_TO_TYPE_OR_RETURN(args, args[4], Uint32, bufused_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "uvwasi_fd_readdir(%d, %d, %d, %d, %d)\n", + fd, + buf_ptr, + buf_len, + cookie, + bufused_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, buf_ptr, buf_len); + CHECK_BOUNDS_OR_RETURN(args, mem_size, bufused_ptr, 4); + size_t bufused; + uvwasi_errno_t err = uvwasi_fd_readdir(&wasi->uvw_, + fd, + &memory[buf_ptr], + buf_len, + cookie, + &bufused); + if (err == UVWASI_ESUCCESS) + wasi->writeUInt32(memory, bufused, bufused_ptr); + + args.GetReturnValue().Set(err); +} + + +void WASI::FdRenumber(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t from; + uint32_t to; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, from); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, to); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_renumber(%d, %d)\n", from, to); + uvwasi_errno_t err = uvwasi_fd_renumber(&wasi->uvw_, from, to); + args.GetReturnValue().Set(err); +} + + +void WASI::FdSeek(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + int64_t offset; + uint8_t whence; + uint32_t newoffset_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 4); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + UNWRAP_BIGINT_OR_RETURN(args, args[1], Int64, offset); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, whence); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, newoffset_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_seek(%d, %d, %d, %d)\n", fd, offset, whence, newoffset_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, newoffset_ptr, 8); + uvwasi_filesize_t newoffset; + uvwasi_errno_t err = uvwasi_fd_seek(&wasi->uvw_, + fd, + offset, + whence, + &newoffset); + if (err == UVWASI_ESUCCESS) + wasi->writeUInt64(memory, newoffset, newoffset_ptr); + + args.GetReturnValue().Set(err); +} + + +void WASI::FdSync(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + RETURN_IF_BAD_ARG_COUNT(args, 1); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_sync(%d)\n", fd); + uvwasi_errno_t err = uvwasi_fd_sync(&wasi->uvw_, fd); + args.GetReturnValue().Set(err); +} + + +void WASI::FdTell(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t offset_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, offset_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "fd_tell(%d, %d)\n", fd, offset_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, offset_ptr, 8); + uvwasi_filesize_t offset; + uvwasi_errno_t err = uvwasi_fd_tell(&wasi->uvw_, fd, &offset); + + if (err == UVWASI_ESUCCESS) + wasi->writeUInt64(memory, offset, offset_ptr); + + args.GetReturnValue().Set(err); +} + + +void WASI::FdWrite(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t iovs_ptr; + uint32_t iovs_len; + uint32_t nwritten_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 4); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, iovs_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, iovs_len); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, nwritten_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "fd_write(%d, %d, %d, %d)\n", + fd, + iovs_ptr, + iovs_len, + nwritten_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, iovs_ptr, iovs_len * 8); + CHECK_BOUNDS_OR_RETURN(args, mem_size, nwritten_ptr, 4); + uvwasi_ciovec_t* iovs = UncheckedCalloc(iovs_len); + + if (iovs == nullptr) { + args.GetReturnValue().Set(UVWASI_ENOMEM); + return; + } + + for (uint32_t i = 0; i < iovs_len; ++i) { + uint32_t buf_ptr; + uint32_t buf_len; + + wasi->readUInt32(memory, &buf_ptr, iovs_ptr); + wasi->readUInt32(memory, &buf_len, iovs_ptr + 4); + + if (is_access_oob(mem_size, buf_ptr, buf_len)) { + free(iovs); + args.GetReturnValue().Set(UVWASI_EOVERFLOW); + return; + } + + iovs_ptr += 8; + iovs[i].buf = static_cast(&memory[buf_ptr]); + iovs[i].buf_len = buf_len; + } + + size_t nwritten; + uvwasi_errno_t err = uvwasi_fd_write(&wasi->uvw_, + fd, + iovs, + iovs_len, + &nwritten); + if (err == UVWASI_ESUCCESS) + wasi->writeUInt32(memory, nwritten, nwritten_ptr); + + free(iovs); + args.GetReturnValue().Set(err); +} + + +void WASI::PathCreateDirectory(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t path_ptr; + uint32_t path_len; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 3); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, path_len); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "path_create_directory(%d, %d, %d)\n", fd, path_ptr, path_len); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, path_ptr, path_len); + uvwasi_errno_t err = uvwasi_path_create_directory(&wasi->uvw_, + fd, + &memory[path_ptr], + path_len); + args.GetReturnValue().Set(err); +} + + +void WASI::PathFilestatGet(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t flags; + uint32_t path_ptr; + uint32_t path_len; + uint32_t buf_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 5); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, flags); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, path_len); + CHECK_TO_TYPE_OR_RETURN(args, args[4], Uint32, buf_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "path_filestat_get(%d, %d, %d, %d, %d)\n", + fd, + path_ptr, + path_len); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, path_ptr, path_len); + CHECK_BOUNDS_OR_RETURN(args, mem_size, buf_ptr, 56); + uvwasi_filestat_t stats; + uvwasi_errno_t err = uvwasi_path_filestat_get(&wasi->uvw_, + fd, + flags, + &memory[path_ptr], + path_len, + &stats); + if (err == UVWASI_ESUCCESS) { + wasi->writeUInt64(memory, stats.st_dev, buf_ptr); + wasi->writeUInt64(memory, stats.st_ino, buf_ptr + 8); + wasi->writeUInt8(memory, stats.st_filetype, buf_ptr + 16); + wasi->writeUInt32(memory, stats.st_nlink, buf_ptr + 20); + wasi->writeUInt64(memory, stats.st_size, buf_ptr + 24); + wasi->writeUInt64(memory, stats.st_atim, buf_ptr + 32); + wasi->writeUInt64(memory, stats.st_mtim, buf_ptr + 40); + wasi->writeUInt64(memory, stats.st_ctim, buf_ptr + 48); + } + + args.GetReturnValue().Set(err); +} + + +void WASI::PathFilestatSetTimes(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t flags; + uint32_t path_ptr; + uint32_t path_len; + uint64_t st_atim; + uint64_t st_mtim; + uint16_t fst_flags; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 7); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, flags); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, path_len); + UNWRAP_BIGINT_OR_RETURN(args, args[4], Uint64, st_atim); + UNWRAP_BIGINT_OR_RETURN(args, args[5], Uint64, st_mtim); + CHECK_TO_TYPE_OR_RETURN(args, args[6], Uint32, fst_flags); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "path_filestat_set_times(%d, %d, %d, %d, %d, %d, %d)\n", + fd, + flags, + path_ptr, + path_len, + st_atim, + st_mtim, + fst_flags); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, path_ptr, path_len); + uvwasi_errno_t err = uvwasi_path_filestat_set_times(&wasi->uvw_, + fd, + flags, + &memory[path_ptr], + path_len, + st_atim, + st_mtim, + fst_flags); + args.GetReturnValue().Set(err); +} + + +void WASI::PathLink(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t old_fd; + uint32_t old_flags; + uint32_t old_path_ptr; + uint32_t old_path_len; + uint32_t new_fd; + uint32_t new_path_ptr; + uint32_t new_path_len; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 7); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, old_fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, old_flags); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, old_path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, old_path_len); + CHECK_TO_TYPE_OR_RETURN(args, args[4], Uint32, new_fd); + CHECK_TO_TYPE_OR_RETURN(args, args[5], Uint32, new_path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[6], Uint32, new_path_len); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "path_link(%d, %d, %d, %d, %d, %d, %d)\n", + old_fd, + old_flags, + old_path_ptr, + old_path_len, + new_fd, + new_path_ptr, + new_path_len); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, old_path_ptr, old_path_len); + CHECK_BOUNDS_OR_RETURN(args, mem_size, new_path_ptr, new_path_len); + uvwasi_errno_t err = uvwasi_path_link(&wasi->uvw_, + old_fd, + old_flags, + &memory[old_path_ptr], + old_path_len, + new_fd, + &memory[new_path_ptr], + new_path_len); + args.GetReturnValue().Set(err); +} + + +void WASI::PathOpen(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t dirfd; + uint32_t dirflags; + uint32_t path_ptr; + uint32_t path_len; + uint32_t o_flags; + uint64_t fs_rights_base; + uint64_t fs_rights_inheriting; + uint32_t fs_flags; + uint32_t fd_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 9); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, dirfd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, dirflags); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, path_len); + CHECK_TO_TYPE_OR_RETURN(args, args[4], Uint32, o_flags); + UNWRAP_BIGINT_OR_RETURN(args, args[5], Uint64, fs_rights_base); + UNWRAP_BIGINT_OR_RETURN(args, args[6], Uint64, fs_rights_inheriting); + CHECK_TO_TYPE_OR_RETURN(args, args[7], Uint32, fs_flags); + CHECK_TO_TYPE_OR_RETURN(args, args[8], Uint32, fd_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "path_open(%d, %d, %d, %d, %d, %d, %d, %d, %d)\n", + dirfd, + dirflags, + path_ptr, + path_len, + o_flags, + fs_rights_base, + fs_rights_inheriting, + fs_flags, + fd_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, path_ptr, path_len); + CHECK_BOUNDS_OR_RETURN(args, mem_size, fd_ptr, 4); + uvwasi_fd_t fd; + uvwasi_errno_t err = uvwasi_path_open(&wasi->uvw_, + dirfd, + dirflags, + &memory[path_ptr], + path_len, + static_cast(o_flags), + fs_rights_base, + fs_rights_inheriting, + static_cast(fs_flags), + &fd); + if (err == UVWASI_ESUCCESS) + wasi->writeUInt32(memory, fd, fd_ptr); + + args.GetReturnValue().Set(err); +} + + +void WASI::PathReadlink(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t path_ptr; + uint32_t path_len; + uint32_t buf_ptr; + uint32_t buf_len; + uint32_t bufused_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 6); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, path_len); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, buf_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[4], Uint32, buf_len); + CHECK_TO_TYPE_OR_RETURN(args, args[5], Uint32, bufused_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "path_readlink(%d, %d, %d, %d, %d, %d)\n", + fd, + path_ptr, + path_len, + buf_ptr, + buf_len, + bufused_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, path_ptr, path_len); + CHECK_BOUNDS_OR_RETURN(args, mem_size, buf_ptr, buf_len); + CHECK_BOUNDS_OR_RETURN(args, mem_size, bufused_ptr, 4); + size_t bufused; + uvwasi_errno_t err = uvwasi_path_readlink(&wasi->uvw_, + fd, + &memory[path_ptr], + path_len, + &memory[buf_ptr], + buf_len, + &bufused); + if (err == UVWASI_ESUCCESS) + wasi->writeUInt32(memory, bufused, bufused_ptr); + + args.GetReturnValue().Set(err); +} + + +void WASI::PathRemoveDirectory(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t path_ptr; + uint32_t path_len; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 3); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, path_len); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "path_remove_directory(%d, %d, %d)\n", fd, path_ptr, path_len); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, path_ptr, path_len); + uvwasi_errno_t err = uvwasi_path_remove_directory(&wasi->uvw_, + fd, + &memory[path_ptr], + path_len); + args.GetReturnValue().Set(err); +} + + +void WASI::PathRename(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t old_fd; + uint32_t old_path_ptr; + uint32_t old_path_len; + uint32_t new_fd; + uint32_t new_path_ptr; + uint32_t new_path_len; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 6); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, old_fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, old_path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, old_path_len); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, new_fd); + CHECK_TO_TYPE_OR_RETURN(args, args[4], Uint32, new_path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[5], Uint32, new_path_len); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "path_rename(%d, %d, %d, %d, %d, %d)\n", + old_fd, + old_path_ptr, + old_path_len, + new_fd, + new_path_ptr, + new_path_len); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, old_path_ptr, old_path_len); + CHECK_BOUNDS_OR_RETURN(args, mem_size, new_path_ptr, new_path_len); + uvwasi_errno_t err = uvwasi_path_rename(&wasi->uvw_, + old_fd, + &memory[old_path_ptr], + old_path_len, + new_fd, + &memory[new_path_ptr], + new_path_len); + args.GetReturnValue().Set(err); +} + + +void WASI::PathSymlink(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t old_path_ptr; + uint32_t old_path_len; + uint32_t fd; + uint32_t new_path_ptr; + uint32_t new_path_len; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 5); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, old_path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, old_path_len); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, new_path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[4], Uint32, new_path_len); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "path_symlink(%d, %d, %d, %d, %d)\n", + old_path_ptr, + old_path_len, + fd, + new_path_ptr, + new_path_len); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, old_path_ptr, old_path_len); + CHECK_BOUNDS_OR_RETURN(args, mem_size, new_path_ptr, new_path_len); + uvwasi_errno_t err = uvwasi_path_symlink(&wasi->uvw_, + &memory[old_path_ptr], + old_path_len, + fd, + &memory[new_path_ptr], + new_path_len); + args.GetReturnValue().Set(err); +} + + +void WASI::PathUnlinkFile(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t fd; + uint32_t path_ptr; + uint32_t path_len; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 3); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, path_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, path_len); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "path_unlink_file(%d, %d, %d)\n", fd, path_ptr, path_len); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, path_ptr, path_len); + uvwasi_errno_t err = uvwasi_path_unlink_file(&wasi->uvw_, + fd, + &memory[path_ptr], + path_len); + args.GetReturnValue().Set(err); +} + + +void WASI::PollOneoff(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t in_ptr; + uint32_t out_ptr; + uint32_t nsubscriptions; + uint32_t nevents_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 4); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, in_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, out_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, nsubscriptions); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, nevents_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "poll_oneoff(%d, %d, %d, %d)\n", + in_ptr, + out_ptr, + nsubscriptions, + nevents_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, in_ptr, nsubscriptions * 56); + CHECK_BOUNDS_OR_RETURN(args, mem_size, out_ptr, nsubscriptions * 32); + CHECK_BOUNDS_OR_RETURN(args, mem_size, nevents_ptr, 4); + uvwasi_subscription_t* in = + UncheckedCalloc(nsubscriptions); + + if (in == nullptr) { + args.GetReturnValue().Set(UVWASI_ENOMEM); + return; + } + + uvwasi_event_t* out = UncheckedCalloc(nsubscriptions); + + if (out == nullptr) { + free(in); + args.GetReturnValue().Set(UVWASI_ENOMEM); + return; + } + + for (uint32_t i = 0; i < nsubscriptions; ++i) { + uvwasi_subscription_t sub = in[i]; + wasi->readUInt64(memory, &sub.userdata, in_ptr); + wasi->readUInt8(memory, &sub.type, in_ptr + 8); + + if (sub.type == UVWASI_EVENTTYPE_CLOCK) { + wasi->readUInt64(memory, &sub.u.clock.identifier, in_ptr + 16); + wasi->readUInt32(memory, &sub.u.clock.clock_id, in_ptr + 24); + wasi->readUInt64(memory, &sub.u.clock.timeout, in_ptr + 32); + wasi->readUInt64(memory, &sub.u.clock.precision, in_ptr + 40); + wasi->readUInt16(memory, &sub.u.clock.flags, in_ptr + 48); + } else if (sub.type == UVWASI_EVENTTYPE_FD_READ || + sub.type == UVWASI_EVENTTYPE_FD_WRITE) { + wasi->readUInt32(memory, &sub.u.fd_readwrite.fd, in_ptr + 16); + } + + in_ptr += 56; + } + + size_t nevents; + uvwasi_errno_t err = uvwasi_poll_oneoff(&wasi->uvw_, + in, + out, + nsubscriptions, + &nevents); + if (err == UVWASI_ESUCCESS) { + wasi->writeUInt32(memory, nevents, nevents_ptr); + + for (uint32_t i = 0; i < nsubscriptions; ++i) { + uvwasi_event_t event = out[i]; + + wasi->writeUInt64(memory, event.userdata, out_ptr); + wasi->writeUInt16(memory, event.error, out_ptr + 8); + wasi->writeUInt8(memory, event.type, out_ptr + 10); + + if (event.type == UVWASI_EVENTTYPE_FD_READ || + event.type == UVWASI_EVENTTYPE_FD_WRITE) { + wasi->writeUInt64(memory, event.u.fd_readwrite.nbytes, out_ptr + 16); + wasi->writeUInt16(memory, event.u.fd_readwrite.flags, out_ptr + 24); + } + + out_ptr += 32; + } + } + + free(in); + free(out); + args.GetReturnValue().Set(err); +} + + +void WASI::ProcExit(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t code; + RETURN_IF_BAD_ARG_COUNT(args, 1); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, code); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "proc_exit(%d)\n", code); + args.GetReturnValue().Set(uvwasi_proc_exit(&wasi->uvw_, code)); +} + + +void WASI::ProcRaise(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t sig; + RETURN_IF_BAD_ARG_COUNT(args, 1); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, sig); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "proc_raise(%d)\n", sig); + uvwasi_errno_t err = uvwasi_proc_raise(&wasi->uvw_, sig); + args.GetReturnValue().Set(err); +} + + +void WASI::RandomGet(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t buf_ptr; + uint32_t buf_len; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, buf_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, buf_len); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "random_get(%d, %d)\n", buf_ptr, buf_len); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, buf_ptr, buf_len); + uvwasi_errno_t err = uvwasi_random_get(&wasi->uvw_, + &memory[buf_ptr], + buf_len); + args.GetReturnValue().Set(err); +} + + +void WASI::SchedYield(const FunctionCallbackInfo& args) { + WASI* wasi; + RETURN_IF_BAD_ARG_COUNT(args, 0); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "sched_yield()\n"); + uvwasi_errno_t err = uvwasi_sched_yield(&wasi->uvw_); + args.GetReturnValue().Set(err); +} + + +void WASI::SockRecv(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t sock; + uint32_t ri_data_ptr; + uint32_t ri_data_len; + uint16_t ri_flags; + uint32_t ro_datalen_ptr; + uint16_t ro_flags_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 6); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, sock); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, ri_data_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, ri_data_len); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, ri_flags); + CHECK_TO_TYPE_OR_RETURN(args, args[4], Uint32, ro_datalen_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[5], Uint32, ro_flags_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "sock_recv(%d, %d, %d, %d, %d, %d)\n", + sock, + ri_data_ptr, + ri_data_len, + ri_flags, + ro_datalen_ptr, + ro_flags_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, ri_data_ptr, ri_data_len * 8); + CHECK_BOUNDS_OR_RETURN(args, mem_size, ro_datalen_ptr, 4); + CHECK_BOUNDS_OR_RETURN(args, mem_size, ro_flags_ptr, 4); + uvwasi_iovec_t* ri_data = UncheckedCalloc(ri_data_len); + + if (ri_data == nullptr) { + args.GetReturnValue().Set(UVWASI_ENOMEM); + return; + } + + for (uint32_t i = 0; i < ri_data_len; ++i) { + uint32_t buf_ptr; + uint32_t buf_len; + + wasi->readUInt32(memory, &buf_ptr, ri_data_ptr); + wasi->readUInt32(memory, &buf_len, ri_data_ptr + 4); + + if (is_access_oob(mem_size, buf_ptr, buf_len)) { + free(ri_data); + args.GetReturnValue().Set(UVWASI_EOVERFLOW); + return; + } + + ri_data_ptr += 8; + ri_data[i].buf = static_cast(&memory[buf_ptr]); + ri_data[i].buf_len = buf_len; + } + + size_t ro_datalen; + uvwasi_roflags_t ro_flags; + uvwasi_errno_t err = uvwasi_sock_recv(&wasi->uvw_, + sock, + ri_data, + ri_data_len, + ri_flags, + &ro_datalen, + &ro_flags); + if (err == UVWASI_ESUCCESS) { + wasi->writeUInt32(memory, ro_datalen, ro_datalen_ptr); + wasi->writeUInt32(memory, ro_flags, ro_flags_ptr); + } + + free(ri_data); + args.GetReturnValue().Set(err); +} + + +void WASI::SockSend(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t sock; + uint32_t si_data_ptr; + uint32_t si_data_len; + uint16_t si_flags; + uint32_t so_datalen_ptr; + char* memory; + size_t mem_size; + RETURN_IF_BAD_ARG_COUNT(args, 5); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, sock); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, si_data_ptr); + CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, si_data_len); + CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, si_flags); + CHECK_TO_TYPE_OR_RETURN(args, args[4], Uint32, so_datalen_ptr); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, + "sock_send(%d, %d, %d, %d, %d)\n", + sock, + si_data_ptr, + si_data_len, + si_flags, + so_datalen_ptr); + GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size); + CHECK_BOUNDS_OR_RETURN(args, mem_size, si_data_ptr, si_data_len * 8); + CHECK_BOUNDS_OR_RETURN(args, mem_size, so_datalen_ptr, 4); + uvwasi_ciovec_t* si_data = UncheckedCalloc(si_data_len); + + if (si_data == nullptr) { + args.GetReturnValue().Set(UVWASI_ENOMEM); + return; + } + + for (uint32_t i = 0; i < si_data_len; ++i) { + uint32_t buf_ptr; + uint32_t buf_len; + + wasi->readUInt32(memory, &buf_ptr, si_data_ptr); + wasi->readUInt32(memory, &buf_len, si_data_ptr + 4); + + if (is_access_oob(mem_size, buf_ptr, buf_len)) { + free(si_data); + args.GetReturnValue().Set(UVWASI_EOVERFLOW); + return; + } + + si_data_ptr += 8; + si_data[i].buf = static_cast(&memory[buf_ptr]); + si_data[i].buf_len = buf_len; + } + + size_t so_datalen; + uvwasi_errno_t err = uvwasi_sock_send(&wasi->uvw_, + sock, + si_data, + si_data_len, + si_flags, + &so_datalen); + if (err == UVWASI_ESUCCESS) + wasi->writeUInt32(memory, so_datalen, so_datalen_ptr); + + free(si_data); + args.GetReturnValue().Set(err); +} + + +void WASI::SockShutdown(const FunctionCallbackInfo& args) { + WASI* wasi; + uint32_t sock; + uint8_t how; + RETURN_IF_BAD_ARG_COUNT(args, 2); + CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, sock); + CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, how); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + Debug(wasi, "sock_shutdown(%d, %d)\n", sock, how); + uvwasi_errno_t err = uvwasi_sock_shutdown(&wasi->uvw_, sock, how); + args.GetReturnValue().Set(err); +} + + +void WASI::_SetMemory(const FunctionCallbackInfo& args) { + WASI* wasi; + CHECK_EQ(args.Length(), 1); + CHECK(args[0]->IsObject()); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + wasi->memory_.Reset(wasi->env()->isolate(), args[0].As()); +} + + +void WASI::readUInt8(char* memory, uint8_t* value, uint32_t offset) { + CHECK_NOT_NULL(memory); + CHECK_NOT_NULL(value); + *value = memory[offset] & 0xFF; +} + + +void WASI::readUInt16(char* memory, uint16_t* value, uint32_t offset) { + CHECK_NOT_NULL(memory); + CHECK_NOT_NULL(value); + *value = (memory[offset] & 0xFF) | + ((memory[offset + 1] & 0xFF) << 8); +} + + +void WASI::readUInt32(char* memory, uint32_t* value, uint32_t offset) { + CHECK_NOT_NULL(memory); + CHECK_NOT_NULL(value); + *value = (memory[offset] & 0xFF) | + ((memory[offset + 1] & 0xFF) << 8) | + ((memory[offset + 2] & 0xFF) << 16) | + ((memory[offset + 3] & 0xFF) << 24); +} + + +void WASI::readUInt64(char* memory, uint64_t* value, uint32_t offset) { + CHECK_NOT_NULL(memory); + CHECK_NOT_NULL(value); + uint64_t low = (memory[offset] & 0xFF) | + ((memory[offset + 1] & 0xFF) << 8) | + ((memory[offset + 2] & 0xFF) << 16) | + ((memory[offset + 3] & 0xFF) << 24); + uint64_t high = (memory[offset + 4] & 0xFF) | + ((memory[offset + 5] & 0xFF) << 8) | + ((memory[offset + 6] & 0xFF) << 16) | + ((memory[offset + 7] & 0xFF) << 24); + *value = (high << 32) + low; +} + + +void WASI::writeUInt8(char* memory, uint8_t value, uint32_t offset) { + CHECK_NOT_NULL(memory); + memory[offset] = value & 0xFF; +} + + +void WASI::writeUInt16(char* memory, uint16_t value, uint32_t offset) { + CHECK_NOT_NULL(memory); + memory[offset++] = value & 0xFF; + memory[offset] = (value >> 8) & 0xFF; +} + + +void WASI::writeUInt32(char* memory, uint32_t value, uint32_t offset) { + CHECK_NOT_NULL(memory); + memory[offset++] = value & 0xFF; + memory[offset++] = (value >> 8) & 0xFF; + memory[offset++] = (value >> 16) & 0xFF; + memory[offset] = (value >> 24) & 0xFF; +} + + +void WASI::writeUInt64(char* memory, uint64_t value, uint32_t offset) { + CHECK_NOT_NULL(memory); + memory[offset++] = value & 0xFF; + memory[offset++] = (value >> 8) & 0xFF; + memory[offset++] = (value >> 16) & 0xFF; + memory[offset++] = (value >> 24) & 0xFF; + memory[offset++] = (value >> 32) & 0xFF; + memory[offset++] = (value >> 40) & 0xFF; + memory[offset++] = (value >> 48) & 0xFF; + memory[offset] = (value >> 56) & 0xFF; +} + + +uvwasi_errno_t WASI::backingStore(char** store, size_t* byte_length) { + Environment* env = this->env(); + Local memory = PersistentToLocal::Strong(this->memory_); + Local prop; + + if (!memory->Get(env->context(), env->buffer_string()).ToLocal(&prop)) + return UVWASI_EINVAL; + + if (!prop->IsArrayBuffer()) + return UVWASI_EINVAL; + + Local ab = prop.As(); + std::shared_ptr backing_store = ab->GetBackingStore(); + *byte_length = backing_store->ByteLength(); + *store = static_cast(backing_store->Data()); + return UVWASI_ESUCCESS; +} + + +static void Initialize(Local target, + Local unused, + Local context, + void* priv) { + Environment* env = Environment::GetCurrent(context); + + Local tmpl = env->NewFunctionTemplate(WASI::New); + auto wasi_wrap_string = FIXED_ONE_BYTE_STRING(env->isolate(), "WASI"); + tmpl->InstanceTemplate()->SetInternalFieldCount(1); + tmpl->SetClassName(wasi_wrap_string); + + env->SetProtoMethod(tmpl, "args_get", WASI::ArgsGet); + env->SetProtoMethod(tmpl, "args_sizes_get", WASI::ArgsSizesGet); + env->SetProtoMethod(tmpl, "clock_res_get", WASI::ClockResGet); + env->SetProtoMethod(tmpl, "clock_time_get", WASI::ClockTimeGet); + env->SetProtoMethod(tmpl, "environ_get", WASI::EnvironGet); + env->SetProtoMethod(tmpl, "environ_sizes_get", WASI::EnvironSizesGet); + env->SetProtoMethod(tmpl, "fd_advise", WASI::FdAdvise); + env->SetProtoMethod(tmpl, "fd_allocate", WASI::FdAllocate); + env->SetProtoMethod(tmpl, "fd_close", WASI::FdClose); + env->SetProtoMethod(tmpl, "fd_datasync", WASI::FdDatasync); + env->SetProtoMethod(tmpl, "fd_fdstat_get", WASI::FdFdstatGet); + env->SetProtoMethod(tmpl, "fd_fdstat_set_flags", WASI::FdFdstatSetFlags); + env->SetProtoMethod(tmpl, "fd_fdstat_set_rights", WASI::FdFdstatSetRights); + env->SetProtoMethod(tmpl, "fd_filestat_get", WASI::FdFilestatGet); + env->SetProtoMethod(tmpl, "fd_filestat_set_size", WASI::FdFilestatSetSize); + env->SetProtoMethod(tmpl, "fd_filestat_set_times", WASI::FdFilestatSetTimes); + env->SetProtoMethod(tmpl, "fd_pread", WASI::FdPread); + env->SetProtoMethod(tmpl, "fd_prestat_get", WASI::FdPrestatGet); + env->SetProtoMethod(tmpl, "fd_prestat_dir_name", WASI::FdPrestatDirName); + env->SetProtoMethod(tmpl, "fd_pwrite", WASI::FdPwrite); + env->SetProtoMethod(tmpl, "fd_read", WASI::FdRead); + env->SetProtoMethod(tmpl, "fd_readdir", WASI::FdReaddir); + env->SetProtoMethod(tmpl, "fd_renumber", WASI::FdRenumber); + env->SetProtoMethod(tmpl, "fd_seek", WASI::FdSeek); + env->SetProtoMethod(tmpl, "fd_sync", WASI::FdSync); + env->SetProtoMethod(tmpl, "fd_tell", WASI::FdTell); + env->SetProtoMethod(tmpl, "fd_write", WASI::FdWrite); + env->SetProtoMethod(tmpl, "path_create_directory", WASI::PathCreateDirectory); + env->SetProtoMethod(tmpl, "path_filestat_get", WASI::PathFilestatGet); + env->SetProtoMethod(tmpl, + "path_filestat_set_times", + WASI::PathFilestatSetTimes); + env->SetProtoMethod(tmpl, "path_link", WASI::PathLink); + env->SetProtoMethod(tmpl, "path_open", WASI::PathOpen); + env->SetProtoMethod(tmpl, "path_readlink", WASI::PathReadlink); + env->SetProtoMethod(tmpl, "path_remove_directory", WASI::PathRemoveDirectory); + env->SetProtoMethod(tmpl, "path_rename", WASI::PathRename); + env->SetProtoMethod(tmpl, "path_symlink", WASI::PathSymlink); + env->SetProtoMethod(tmpl, "path_unlink_file", WASI::PathUnlinkFile); + env->SetProtoMethod(tmpl, "poll_oneoff", WASI::PollOneoff); + env->SetProtoMethod(tmpl, "proc_exit", WASI::ProcExit); + env->SetProtoMethod(tmpl, "proc_raise", WASI::ProcRaise); + env->SetProtoMethod(tmpl, "random_get", WASI::RandomGet); + env->SetProtoMethod(tmpl, "sched_yield", WASI::SchedYield); + env->SetProtoMethod(tmpl, "sock_recv", WASI::SockRecv); + env->SetProtoMethod(tmpl, "sock_send", WASI::SockSend); + env->SetProtoMethod(tmpl, "sock_shutdown", WASI::SockShutdown); + + env->SetInstanceMethod(tmpl, "_setMemory", WASI::_SetMemory); + + target->Set(env->context(), + wasi_wrap_string, + tmpl->GetFunction(context).ToLocalChecked()).ToChecked(); +} + + +} // namespace wasi +} // namespace node + +NODE_MODULE_CONTEXT_AWARE_INTERNAL(wasi, node::wasi::Initialize) diff --git a/src/node_wasi.h b/src/node_wasi.h new file mode 100644 index 00000000000000..ca726e48a42a47 --- /dev/null +++ b/src/node_wasi.h @@ -0,0 +1,103 @@ +#ifndef SRC_NODE_WASI_H_ +#define SRC_NODE_WASI_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "base_object.h" +#include "memory_tracker-inl.h" +#include "uvwasi.h" + +namespace node { +namespace wasi { + + +class WASI : public BaseObject { + public: + WASI(Environment* env, + v8::Local object, + uvwasi_options_t* options); + static void New(const v8::FunctionCallbackInfo& args); + void MemoryInfo(MemoryTracker* tracker) const override { + /* TODO(cjihrig): Get memory consumption from uvwasi. */ + tracker->TrackField("memory", memory_); + } + + SET_MEMORY_INFO_NAME(WASI) + SET_SELF_SIZE(WASI) + + static void ArgsGet(const v8::FunctionCallbackInfo& args); + static void ArgsSizesGet(const v8::FunctionCallbackInfo& args); + static void ClockResGet(const v8::FunctionCallbackInfo& args); + static void ClockTimeGet(const v8::FunctionCallbackInfo& args); + static void EnvironGet(const v8::FunctionCallbackInfo& args); + static void EnvironSizesGet(const v8::FunctionCallbackInfo& args); + static void FdAdvise(const v8::FunctionCallbackInfo& args); + static void FdAllocate(const v8::FunctionCallbackInfo& args); + static void FdClose(const v8::FunctionCallbackInfo& args); + static void FdDatasync(const v8::FunctionCallbackInfo& args); + static void FdFdstatGet(const v8::FunctionCallbackInfo& args); + static void FdFdstatSetFlags(const v8::FunctionCallbackInfo& args); + static void FdFdstatSetRights( + const v8::FunctionCallbackInfo& args); + static void FdFilestatGet(const v8::FunctionCallbackInfo& args); + static void FdFilestatSetSize( + const v8::FunctionCallbackInfo& args); + static void FdFilestatSetTimes( + const v8::FunctionCallbackInfo& args); + static void FdPread(const v8::FunctionCallbackInfo& args); + static void FdPrestatGet(const v8::FunctionCallbackInfo& args); + static void FdPrestatDirName(const v8::FunctionCallbackInfo& args); + static void FdPwrite(const v8::FunctionCallbackInfo& args); + static void FdRead(const v8::FunctionCallbackInfo& args); + static void FdReaddir(const v8::FunctionCallbackInfo& args); + static void FdRenumber(const v8::FunctionCallbackInfo& args); + static void FdSeek(const v8::FunctionCallbackInfo& args); + static void FdSync(const v8::FunctionCallbackInfo& args); + static void FdTell(const v8::FunctionCallbackInfo& args); + static void FdWrite(const v8::FunctionCallbackInfo& args); + static void PathCreateDirectory( + const v8::FunctionCallbackInfo& args); + static void PathFilestatGet(const v8::FunctionCallbackInfo& args); + static void PathFilestatSetTimes( + const v8::FunctionCallbackInfo& args); + static void PathLink(const v8::FunctionCallbackInfo& args); + static void PathOpen(const v8::FunctionCallbackInfo& args); + static void PathReadlink(const v8::FunctionCallbackInfo& args); + static void PathRemoveDirectory( + const v8::FunctionCallbackInfo& args); + static void PathRename(const v8::FunctionCallbackInfo& args); + static void PathSymlink(const v8::FunctionCallbackInfo& args); + static void PathUnlinkFile(const v8::FunctionCallbackInfo& args); + static void PollOneoff(const v8::FunctionCallbackInfo& args); + static void ProcExit(const v8::FunctionCallbackInfo& args); + static void ProcRaise(const v8::FunctionCallbackInfo& args); + static void RandomGet(const v8::FunctionCallbackInfo& args); + static void SchedYield(const v8::FunctionCallbackInfo& args); + static void SockRecv(const v8::FunctionCallbackInfo& args); + static void SockSend(const v8::FunctionCallbackInfo& args); + static void SockShutdown(const v8::FunctionCallbackInfo& args); + + static void _SetMemory(const v8::FunctionCallbackInfo& args); + + private: + ~WASI() override; + inline void readUInt8(char* memory, uint8_t* value, uint32_t offset); + inline void readUInt16(char* memory, uint16_t* value, uint32_t offset); + inline void readUInt32(char* memory, uint32_t* value, uint32_t offset); + inline void readUInt64(char* memory, uint64_t* value, uint32_t offset); + inline void writeUInt8(char* memory, uint8_t value, uint32_t offset); + inline void writeUInt16(char* memory, uint16_t value, uint32_t offset); + inline void writeUInt32(char* memory, uint32_t value, uint32_t offset); + inline void writeUInt64(char* memory, uint64_t value, uint32_t offset); + uvwasi_errno_t backingStore(char** store, size_t* byte_length); + uvwasi_t uvw_; + v8::Global memory_; +}; + + +} // namespace wasi +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_NODE_WASI_H_ diff --git a/test/fixtures/outside.txt b/test/fixtures/outside.txt new file mode 100644 index 00000000000000..044c4b9614586d --- /dev/null +++ b/test/fixtures/outside.txt @@ -0,0 +1,2 @@ +this file is part of the WASI tests. it exists outside of the sandbox, and +should be inaccessible from the WASI tests. diff --git a/test/fixtures/wasi/input.txt b/test/fixtures/wasi/input.txt new file mode 100644 index 00000000000000..4c380537647f19 --- /dev/null +++ b/test/fixtures/wasi/input.txt @@ -0,0 +1 @@ +hello from input.txt diff --git a/test/fixtures/wasi/notadir b/test/fixtures/wasi/notadir new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/test/fixtures/wasi/simple-wasi.wasm b/test/fixtures/wasi/simple-wasi.wasm new file mode 100755 index 0000000000000000000000000000000000000000..334d3a3dcac6c09aade3babbc7a6f9a8a3ab6f16 GIT binary patch literal 15873 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 +#include +#include + +int main() { + FILE* file = fopen("/sandbox/../outside.txt", "r"); + assert(file == NULL); + assert(errno == ENOTCAPABLE); + + return 0; +} diff --git a/test/wasi/c/clock_getres.c b/test/wasi/c/clock_getres.c new file mode 100644 index 00000000000000..eaac02c665b9f2 --- /dev/null +++ b/test/wasi/c/clock_getres.c @@ -0,0 +1,17 @@ +#include +#include + +int main() { + struct timespec ts; + int r; + + // supported clocks + r = clock_getres(CLOCK_REALTIME, &ts); + assert(r == 0); + r = clock_getres(CLOCK_MONOTONIC, &ts); + assert(r == 0); + r = clock_getres(CLOCK_PROCESS_CPUTIME_ID, &ts); + assert(r == 0); + r = clock_getres(CLOCK_THREAD_CPUTIME_ID, &ts); + assert(r == 0); +} diff --git a/test/wasi/c/exitcode.c b/test/wasi/c/exitcode.c new file mode 100644 index 00000000000000..9c44b0de741a69 --- /dev/null +++ b/test/wasi/c/exitcode.c @@ -0,0 +1,3 @@ +int main() { + return 120; +} diff --git a/test/wasi/c/fd_prestat_get_refresh.c b/test/wasi/c/fd_prestat_get_refresh.c new file mode 100644 index 00000000000000..fb644ab49b208a --- /dev/null +++ b/test/wasi/c/fd_prestat_get_refresh.c @@ -0,0 +1,8 @@ +#include + +int main(void) { + isatty(1); + __builtin_wasm_memory_grow(0, 1); + isatty(1); + return 0; +} diff --git a/test/wasi/c/follow_symlink.c b/test/wasi/c/follow_symlink.c new file mode 100644 index 00000000000000..badb0ee2676bbe --- /dev/null +++ b/test/wasi/c/follow_symlink.c @@ -0,0 +1,14 @@ +#include +#include + +int main() { + FILE* file = fopen("/sandbox/subdir/input_link.txt", "r"); + assert(file != NULL); + + char c = fgetc(file); + while (c != EOF) { + int wrote = fputc(c, stdout); + assert(wrote != EOF); + c = fgetc(file); + } +} diff --git a/test/wasi/c/getentropy.c b/test/wasi/c/getentropy.c new file mode 100644 index 00000000000000..75547e1c474bba --- /dev/null +++ b/test/wasi/c/getentropy.c @@ -0,0 +1,18 @@ +#include +#include + +int main() { + char buf[256] = {0}; + int r = getentropy(buf, 256); + assert(r == 0); + + for (int i = 0; i < 256; i++) { + if (buf[i] != 0) { + return 0; + } + } + + // if this ever is reached, we either have a bug or should buy a lottery + // ticket + return 1; +} diff --git a/test/wasi/c/getrusage.c b/test/wasi/c/getrusage.c new file mode 100644 index 00000000000000..ad1e430b853395 --- /dev/null +++ b/test/wasi/c/getrusage.c @@ -0,0 +1,34 @@ +#include +#include + +int main() { + struct rusage ru1; + struct rusage ru2; + long long s1; + long long us1; + long long s2; + long long us2; + int r; + int success = 0; + + r = getrusage(RUSAGE_SELF, &ru1); + assert(r == 0); + s1 = ru1.ru_utime.tv_sec; + us1 = ru1.ru_utime.tv_usec; + + for (int i = 0; i < 10000; i++) { + r = getrusage(RUSAGE_SELF, &ru2); + assert(r == 0); + s2 = ru2.ru_utime.tv_sec; + us2 = ru2.ru_utime.tv_usec; + assert(s1 <= s2); + + // Verify that some time has passed. + if (s2 > s1 || (s2 == s1 && us2 > us1)) { + success = 1; + break; + } + } + + assert(success == 1); +} diff --git a/test/wasi/c/gettimeofday.c b/test/wasi/c/gettimeofday.c new file mode 100644 index 00000000000000..209a54e4983bfa --- /dev/null +++ b/test/wasi/c/gettimeofday.c @@ -0,0 +1,35 @@ +#include +#include +#include + +int main() { + struct timeval tv1; + struct timeval tv2; + long long s1; + long long us1; + long long s2; + long long us2; + int r; + int success = 0; + + r = gettimeofday(&tv1, NULL); + assert(r == 0); + s1 = tv1.tv_sec; + us1 = tv1.tv_usec; + + for (int i = 0; i < 10000; i++) { + r = gettimeofday(&tv2, NULL); + assert(r == 0); + s2 = tv2.tv_sec; + us2 = tv2.tv_usec; + assert(s1 <= s2); + + // Verify that some time has passed. + if (s2 > s1 || (s2 == s1 && us2 > us1)) { + success = 1; + break; + } + } + + assert(success == 1); +} diff --git a/test/wasi/c/notdir.c b/test/wasi/c/notdir.c new file mode 100644 index 00000000000000..03f369ffd3a862 --- /dev/null +++ b/test/wasi/c/notdir.c @@ -0,0 +1,11 @@ +#include +#include +#include + +int main() { + DIR* dir = opendir("/sandbox/notadir"); + assert(dir == NULL); + assert(errno == ENOTDIR); + + return 0; +} diff --git a/test/wasi/c/poll.c b/test/wasi/c/poll.c new file mode 100644 index 00000000000000..6b6ef71fd68c3b --- /dev/null +++ b/test/wasi/c/poll.c @@ -0,0 +1,31 @@ +#include +#include +#include +#include + +int main(void) { + struct pollfd fds[2]; + time_t before, now; + int ret; + + fds[0] = (struct pollfd){.fd = 1, .events = POLLOUT, .revents = 0}; + fds[1] = (struct pollfd){.fd = 2, .events = POLLOUT, .revents = 0}; + + ret = poll(fds, 2, -1); + assert(ret == 2); + assert(fds[0].revents == POLLOUT); + assert(fds[1].revents == POLLOUT); + + fds[0] = (struct pollfd){.fd = 0, .events = POLLIN, .revents = 0}; + time(&before); + ret = poll(fds, 1, 2000); + time(&now); + assert(ret == 0); + assert(now - before >= 2); + + sleep(1); + time(&now); + assert(now - before >= 3); + + return 0; +} diff --git a/test/wasi/c/preopen_populates.c b/test/wasi/c/preopen_populates.c new file mode 100644 index 00000000000000..03b2213bb9a36c --- /dev/null +++ b/test/wasi/c/preopen_populates.c @@ -0,0 +1,3 @@ +int main(void) { + return 0; +} diff --git a/test/wasi/c/read_file.c b/test/wasi/c/read_file.c new file mode 100644 index 00000000000000..40023e29e2727f --- /dev/null +++ b/test/wasi/c/read_file.c @@ -0,0 +1,14 @@ +#include +#include + +int main() { + FILE* file = fopen("/sandbox/input.txt", "r"); + assert(file != NULL); + + char c = fgetc(file); + while (c != EOF) { + int wrote = fputc(c, stdout); + assert(wrote != EOF); + c = fgetc(file); + } +} diff --git a/test/wasi/c/read_file_twice.c b/test/wasi/c/read_file_twice.c new file mode 100644 index 00000000000000..e295e38a3b3e13 --- /dev/null +++ b/test/wasi/c/read_file_twice.c @@ -0,0 +1,16 @@ +#include +#include + +int main() { + for (int i = 0; i < 2; i++) { + FILE* file = fopen("/sandbox/input.txt", "r"); + assert(file != NULL); + + char c = fgetc(file); + while (c != EOF) { + int wrote = fputc(c, stdout); + assert(wrote != EOF); + c = fgetc(file); + } + } +} diff --git a/test/wasi/c/stat.c b/test/wasi/c/stat.c new file mode 100644 index 00000000000000..fd3854937b9e6e --- /dev/null +++ b/test/wasi/c/stat.c @@ -0,0 +1,53 @@ +#include + +#include +#include +#include +#include + +#define BASE_DIR "/tmp" +#define OUTPUT_DIR BASE_DIR "/testdir" +#define PATH OUTPUT_DIR "/output.txt" +#define SIZE 500 + +int main(void) { + struct stat st; + int fd; + int ret; + off_t pos; + + (void)st; + ret = mkdir(OUTPUT_DIR, 0755); + assert(ret == 0); + + fd = open(PATH, O_CREAT | O_WRONLY, 0666); + assert(fd != -1); + + pos = lseek(fd, SIZE - 1, SEEK_SET); + assert(pos == SIZE - 1); + + ret = (int)write(fd, "", 1); + assert(ret == 1); + + ret = fstat(fd, &st); + assert(ret == 0); + assert(st.st_size == SIZE); + + ret = close(fd); + assert(ret == 0); + + ret = access(PATH, R_OK); + assert(ret == 0); + + ret = stat(PATH, &st); + assert(ret == 0); + assert(st.st_size == SIZE); + + ret = unlink(PATH); + assert(ret == 0); + + ret = stat(PATH, &st); + assert(ret == -1); + + return 0; +} diff --git a/test/wasi/c/stdin.c b/test/wasi/c/stdin.c new file mode 100644 index 00000000000000..5a81ea1265fb11 --- /dev/null +++ b/test/wasi/c/stdin.c @@ -0,0 +1,13 @@ +#include + +int main(void) { + char x[32]; + + if (fgets(x, sizeof x, stdin) == NULL) { + return ferror(stdin); + } + if (fputs(x, stdout) == EOF) { + return ferror(stdout); + } + return 0; +} diff --git a/test/wasi/c/symlink_escape.c b/test/wasi/c/symlink_escape.c new file mode 100644 index 00000000000000..32dcc64eebdc2b --- /dev/null +++ b/test/wasi/c/symlink_escape.c @@ -0,0 +1,9 @@ +#include +#include +#include + +int main() { + FILE* file = fopen("/sandbox/subdir/outside.txt", "r"); + assert(file == NULL); + assert(errno == ENOTCAPABLE); +} diff --git a/test/wasi/c/symlink_loop.c b/test/wasi/c/symlink_loop.c new file mode 100644 index 00000000000000..23bd70ba601176 --- /dev/null +++ b/test/wasi/c/symlink_loop.c @@ -0,0 +1,9 @@ +#include +#include +#include + +int main() { + FILE* file = fopen("/sandbox/subdir/loop1", "r"); + assert(file == NULL); + assert(errno == ELOOP); +} diff --git a/test/wasi/c/write_file.c b/test/wasi/c/write_file.c new file mode 100644 index 00000000000000..c4cf30cf2954cb --- /dev/null +++ b/test/wasi/c/write_file.c @@ -0,0 +1,15 @@ +#include +#include +#include + +static char* message = "hello, file!"; + +int main() { + FILE* file = fopen("/tmp/output.txt", "w"); + assert(file != NULL); + + int nwritten = fprintf(file, "%s", message); + assert(nwritten == strlen(message)); + int r = fclose(file); + assert(r == 0); +} diff --git a/test/wasi/test-wasi-binding.js b/test/wasi/test-wasi-binding.js new file mode 100644 index 00000000000000..876c8a15a72c13 --- /dev/null +++ b/test/wasi/test-wasi-binding.js @@ -0,0 +1,19 @@ +// Flags: --experimental-wasi-unstable-preview0 +'use strict'; + +const common = require('../common'); + +const assert = require('assert'); +const fixtures = require('../common/fixtures'); +const buffer = fixtures.readSync(['wasi', 'simple-wasi.wasm']); +const { WASI } = require('wasi'); +const wasi = new WASI({ args: [], env: process.env }); +const importObject = { + wasi_unstable: wasi.wasiImport +}; + +WebAssembly.instantiate(buffer, importObject) +.then(common.mustCall((results) => { + assert(results.instance.exports._start); + wasi.start(results.instance); +})); diff --git a/test/wasi/test-wasi-symlinks.js b/test/wasi/test-wasi-symlinks.js new file mode 100644 index 00000000000000..3829464198273b --- /dev/null +++ b/test/wasi/test-wasi-symlinks.js @@ -0,0 +1,78 @@ +'use strict'; +const common = require('../common'); +const fs = require('fs'); +const path = require('path'); + +if (process.argv[2] === 'wasi-child') { + common.expectWarning('ExperimentalWarning', + 'WASI is an experimental feature. This feature could ' + + 'change at any time'); + + const { WASI } = require('wasi'); + const wasmDir = path.join(__dirname, 'wasm'); + const wasi = new WASI({ + args: [], + env: process.env, + preopens: { + '/sandbox': process.argv[4] + } + }); + const importObject = { wasi_unstable: wasi.wasiImport }; + const modulePath = path.join(wasmDir, `${process.argv[3]}.wasm`); + const buffer = fs.readFileSync(modulePath); + + (async () => { + const { instance } = await WebAssembly.instantiate(buffer, importObject); + + wasi.start(instance); + })(); +} else { + if (!common.canCreateSymLink()) { + common.skip('insufficient privileges'); + } + + const assert = require('assert'); + const cp = require('child_process'); + const tmpdir = require('../../test/common/tmpdir'); + + // Setup the sandbox environment. + tmpdir.refresh(); + const sandbox = path.join(tmpdir.path, 'sandbox'); + const sandboxedFile = path.join(sandbox, 'input.txt'); + const externalFile = path.join(tmpdir.path, 'outside.txt'); + const sandboxedDir = path.join(sandbox, 'subdir'); + const sandboxedSymlink = path.join(sandboxedDir, 'input_link.txt'); + const escapingSymlink = path.join(sandboxedDir, 'outside.txt'); + const loopSymlink1 = path.join(sandboxedDir, 'loop1'); + const loopSymlink2 = path.join(sandboxedDir, 'loop2'); + + fs.mkdirSync(sandbox); + fs.mkdirSync(sandboxedDir); + fs.writeFileSync(sandboxedFile, 'hello from input.txt', 'utf8'); + fs.writeFileSync(externalFile, 'this should be inaccessible', 'utf8'); + fs.symlinkSync(sandboxedFile, sandboxedSymlink, 'file'); + fs.symlinkSync(externalFile, escapingSymlink, 'file'); + fs.symlinkSync(loopSymlink2, loopSymlink1, 'file'); + fs.symlinkSync(loopSymlink1, loopSymlink2, 'file'); + + function runWASI(options) { + console.log('executing', options.test); + const opts = { env: { ...process.env, NODE_DEBUG_NATIVE: 'wasi' } }; + const child = cp.spawnSync(process.execPath, [ + '--experimental-wasi-unstable-preview0', + '--experimental-wasm-bigint', + __filename, + 'wasi-child', + options.test, + sandbox + ], opts); + console.log(child.stderr.toString()); + assert.strictEqual(child.status, 0); + assert.strictEqual(child.signal, null); + assert.strictEqual(child.stdout.toString(), options.stdout || ''); + } + + runWASI({ test: 'follow_symlink', stdout: 'hello from input.txt' }); + runWASI({ test: 'symlink_escape' }); + runWASI({ test: 'symlink_loop' }); +} diff --git a/test/wasi/test-wasi.js b/test/wasi/test-wasi.js new file mode 100644 index 00000000000000..fa2e0894c906ce --- /dev/null +++ b/test/wasi/test-wasi.js @@ -0,0 +1,81 @@ +'use strict'; +const common = require('../common'); + +if (process.argv[2] === 'wasi-child') { + const fixtures = require('../common/fixtures'); + const tmpdir = require('../../test/common/tmpdir'); + const fs = require('fs'); + const path = require('path'); + + common.expectWarning('ExperimentalWarning', + 'WASI is an experimental feature. This feature could ' + + 'change at any time'); + + const { WASI } = require('wasi'); + tmpdir.refresh(); + const wasmDir = path.join(__dirname, 'wasm'); + const wasi = new WASI({ + args: [], + env: process.env, + preopens: { + '/sandbox': fixtures.path('wasi'), + '/tmp': tmpdir.path + } + }); + const importObject = { wasi_unstable: wasi.wasiImport }; + const modulePath = path.join(wasmDir, `${process.argv[3]}.wasm`); + const buffer = fs.readFileSync(modulePath); + + (async () => { + const { instance } = await WebAssembly.instantiate(buffer, importObject); + + wasi.start(instance); + })(); +} else { + const assert = require('assert'); + const cp = require('child_process'); + const { EOL } = require('os'); + + function runWASI(options) { + console.log('executing', options.test); + const opts = { env: { ...process.env, NODE_DEBUG_NATIVE: 'wasi' } }; + + if (options.stdin !== undefined) + opts.input = options.stdin; + + const child = cp.spawnSync(process.execPath, [ + '--experimental-wasi-unstable-preview0', + '--experimental-wasm-bigint', + __filename, + 'wasi-child', + options.test + ], opts); + console.log(child.stderr.toString()); + assert.strictEqual(child.status, options.exitCode || 0); + assert.strictEqual(child.signal, null); + assert.strictEqual(child.stdout.toString(), options.stdout || ''); + } + + runWASI({ test: 'cant_dotdot' }); + runWASI({ test: 'clock_getres' }); + runWASI({ test: 'exitcode', exitCode: 120 }); + runWASI({ test: 'fd_prestat_get_refresh' }); + runWASI({ test: 'getentropy' }); + runWASI({ test: 'getrusage' }); + runWASI({ test: 'gettimeofday' }); + runWASI({ test: 'notdir' }); + // runWASI({ test: 'poll' }); + runWASI({ test: 'preopen_populates' }); + runWASI({ test: 'read_file', stdout: `hello from input.txt${EOL}` }); + runWASI({ + test: 'read_file_twice', + stdout: `hello from input.txt${EOL}hello from input.txt${EOL}` + }); + runWASI({ test: 'stat' }); + runWASI({ test: 'write_file' }); + + // Tests that are currently unsupported on Windows. + if (!common.isWindows) { + runWASI({ test: 'stdin', stdin: 'hello world', stdout: 'hello world' }); + } +} diff --git a/test/wasi/testcfg.py b/test/wasi/testcfg.py new file mode 100644 index 00000000000000..ec6cbc5fe3dc3a --- /dev/null +++ b/test/wasi/testcfg.py @@ -0,0 +1,6 @@ +import sys, os +sys.path.append(os.path.join(os.path.dirname(__file__), '..')) +import testpy + +def GetConfiguration(context, root): + return testpy.ParallelTestConfiguration(context, root, 'wasi') diff --git a/test/wasi/wasi.status b/test/wasi/wasi.status new file mode 100644 index 00000000000000..12212e8f72822b --- /dev/null +++ b/test/wasi/wasi.status @@ -0,0 +1,7 @@ +prefix wasi + +# To mark a test as flaky, list the test name in the appropriate section +# below, without ".js", followed by ": PASS,FLAKY". Example: +# sample-test : PASS,FLAKY + +[true] # This section applies to all platforms diff --git a/test/wasi/wasm/cant_dotdot.wasm b/test/wasi/wasm/cant_dotdot.wasm new file mode 100755 index 0000000000000000000000000000000000000000..61e202d6913a3e000ecbd8978ce183df41f8e64f GIT binary patch literal 33007 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 J->)&w{{wP$&w>B| literal 0 HcmV?d00001 diff --git a/test/wasi/wasm/clock_getres.wasm b/test/wasi/wasm/clock_getres.wasm new file mode 100755 index 0000000000000000000000000000000000000000..fac14edb04d794431f1b5a605af66af8c95c2949 GIT binary patch literal 30146 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 ogiscTh+}Z+;a$u? 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 GCGcOAZ1mFr literal 0 HcmV?d00001 diff --git a/test/wasi/wasm/follow_symlink.wasm b/test/wasi/wasm/follow_symlink.wasm new file mode 100755 index 0000000000000000000000000000000000000000..48cf8da1eb28605e93a9611e394a0632b899c4ef GIT binary patch literal 34948 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 RdanPp$LdSB_W3Es`G5ZeZDRlc literal 0 HcmV?d00001 diff --git a/test/wasi/wasm/getrusage.wasm b/test/wasi/wasm/getrusage.wasm new file mode 100755 index 0000000000000000000000000000000000000000..524e809175b5aafe5f8124ecf0d181eae03c8d99 GIT binary patch literal 30602 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 F{{sOI-^l;~ literal 0 HcmV?d00001 diff --git a/test/wasi/wasm/gettimeofday.wasm b/test/wasi/wasm/gettimeofday.wasm new file mode 100755 index 0000000000000000000000000000000000000000..94627f00866903691760e8cfb59830d432c1978f GIT binary patch literal 30428 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 literal 0 HcmV?d00001 diff --git a/test/wasi/wasm/read_file_twice.wasm b/test/wasi/wasm/read_file_twice.wasm new file mode 100755 index 0000000000000000000000000000000000000000..cd075a4de75a51b6921a665137bb4c04a1ec3a7b GIT binary patch literal 35018 zcmeI5eUM$}UEj~ky}Nt&-m80eS6c1rO3`zUBzqM}mYg_}qr7;JVo8?m*e*#a)BYi^ zrM10xSG(FbS#A^U#x@~PXaj*l0xc#$6GBqLkV0AlF(rUYlQwkdG&AiVVTRI7o6fkM zPVr0$?&te^&bcqr%8|j$(3!;1J#Wv;@BQt0&e>pQ^->rFK{(rZG~U?Q2sb!f+X$~c z8rem-5jH|&YxxbDlxU)+B|YF61dj&(^4hh)-whru>!BZTgHfXQZG`WOek9&j ze`;oR?)>`V>e|d>3$xoEzj*%g$}AUa=bxBe3!?tVV?B>A&aIqZoVhd`1pRGBW*4u_ ztt>5`U!D8Av#VQ;QY~6+F~hc*l_yqj-gU?xZ_%%Ed1dLs`Pr*;YeC!(PaT*azj!l@ zDwk*0o;<&Fd3G@<_4ga1--U&x)!Crj|3E~@2V9+9JOB8?%oD5E6Kaq)tFyE7!9ags z4f@o|+}dm~*#AHsR%T}|27~dL(?dg*s9cWYC>|Ot2gC7DR2~|tmWRsxFDQqSkRT{W zk^WVN%0Vz#j)Gc!h`SLTBc4RtO6Bl!I51F-!yp>i5pRUq*MFf@-3W@qA0Dg@-ccI3 zG<#`j<>@G>oX}BT>m@owcDwlj-K)p>WjhN_27#V7e_9U`9yEVcXD+Un zsxD-hG^_{Jsz!G~R%Bc)JWY7(`!TXY-EKDkH$W@k;VK)r& z^si={9}=v!>?3xpyPB&v|GB}xjX}2^kG5;D?McHmlBTOBBd+O2&L*P_Hs(g%*mANx zns(b^&5zj#JF<`4aXcGLc4oh7=M!#+8+SY1MDu4ga!~a{PP)nFCvB=-OttG|FlO*? zBrO*m3C5D$*+jC(?RI(+?lyP(S~BIP-0kbhLC}4pd*f8}vcYu79dw79|H7s}%=CwkM>T;}OOCiB$sO)M za@5`7j-E}9G0C0on7ea1c@vnv2~2<8MtF0M>0MbZc}tGzarb6-mwSsl-jC@CcOu91 zZl<~$Olt^@=h9ml|D=1XJK0X&#>2P4#^T}byfS|FCz%t=(d6b>JG;Ehv3jnR z##wwNEnP{2EMAtLVWO5@X(bC+({dKCr32a5)w7MdrKN1Bodm8l6%D1a8_2$9Ptt)i zb)?!2h%o~+k%R}exC)b{P+#15puWv!Ws=LwNi5s&IRj_#bbTA#9)ev#HUto|9D9=? z7iX`$^2#8fb4FjDZvOc_?W}nv8(L0FYYepc^}TjoT29NrOPjLY-fYp{Kv9U_9A5wu zz5}lv-+oC5>Vq|jNg=Bk;vqN8Dyh zYHbGGlcbzAWT_D&krqc{8V(=KB6T$!7^;P+i4A!}?je#)1RuKM$J37ZVIm9hqfs=C z@L@^_TTT4rj*{4e$GwjDl@cg`1M&iAd_>@Ng~;aOSG`gEL9IkJ#?J&X@s?ixU1u+KI^%u->K;(Bz3#OtM?V9JZy4*(pz0P~Z zv*w&a9&0HQ zUFRRxIf4)4=#T@Tu3*Mv5*&$^{6mLtIZ;6(I; z{tmfBtxY77eeF~K;Zx<+;~oS*>QDJ58ugnbVL`UJS%R||B$GUY{`Y`quuUulz2;K_ z5|{}8qG_S<4fXPY3|C*bE6g3v)Ro>%oK$wf1IkHA+x!Hj<@&Ji>UmUB4D^!vC4tPJ z5D(N_I!15E(ALF(nL?yPnOYV!pI1;xxVCWyCs|1%Kc=CKP7)o!cheUna~?bfNX6tx z+eswB1p}g&?9YANRe3dJq8yoFkIXRL^VwneS&LpE=CR|^^CD(#+^E-kN8)StrE5sN zWmNFxO{F)a$Z=6fVd{;ho27qe2AaU`Yp)atAo{2TFPZo?)VaMBW5{ z5oa$VoDrJRC4dY%lnmXVi{?9`nFcx0plD7hM)k<6Io8HS5VSTRoCna=My)A>vLUiS zXFJPnRW$EA?xL+`$i)E(w=;RId=BhDhrI=(gNFewhY|J)RH(0bG|ujBgG)ZDNgzO_ zkjc&=y-oMW5;4S9QgA%7tBzF>I9o}hY><~m4yClxR^-pANg-Nm1E}*0-rUIK(FIU} zM#jMIi#D2;Sml2U2>-}L9!hK+kG`tjo+xG=jz@o3+&LconYzS6!Y3x2d`(XgaC27K zAKR5o8JJ-b;MHbyR}MC9 zZZMR1y>@oD0$A9KQrE~(3~RJq`f6%RvVf`Z6U$23#00=(6}hPvhlX6aW0rv&HAf@c zpy5ez9WZ#z0vHJEt#-*VwgD4L#cX&Zxp{>y17HebS}x2?;Yf<3s4G<|n^YC-jYvwm z2pT&fu<4V5iAmXlzSv15(uV|Po|khnlyfpD{EX&tCkX<9ds1MlILy43g@(GqW=Y^m zB&k_=w%)+jUtOkNQp#Q-!5!Aqno?51isO{uC#1ozTD)1?R_&sCGn?v7teD6kI6US3 zTOlNY8S(-{_|y>?NLul<2mW$)q6g7)#_x8Pweb{$1eh=q?@G2W_hqjpXF~;RWfrsG zVDz%Y{{+mb=*pDlL_{>XwKmae2nn)IlA>IFxV;~zEI~m4|^WREA#01$mQ^lrb3IuhNm^ zzbwbMS7Ojf{v(oqietUWf1n()C;7*`tk7oQy5zrX$$xpv1>qDCVXgQiGU(uQ)aL9rs5U=AiFnBh38sq}7Xzq-3-pw5 zG3=1yDtioHB(HQdG@(Ndg5oavse`%`R0~0cpmx_fGbqiy+qH4knpu3ZYyi_<5s~Pa zyAcJRK(4Sc2OkjVRG6>=YlbSUMIO#6xJekQ8&C`jZ#v|Oqh_%PFej5g?~q9Z^GwDo z%R(r|HhW?D9!^vUT1KjT&!{$Bk4!i_;;XC&;qKHkF!nN*aJ2|dVi|pO)Q2>2o;WBM zjC*oHwd&0Ux$fkGgfNk1F{BNu`q~VdLz!RPl~0?&r>eu2`wefI`x!>erjfvFxF`2h zi0M&91+L5ec$VjWP(o_1)X>1{Xa&TGyD1_y>b zz%9rv2eG~?R$E$*7#*bGRYC_*Mj2=MT7;rx17JCzwk(HIhN5g3U^!IC>n|H}-7OnZ za$BJdmZLp9U#GmC?u2#{J)5G}TFo2sBCaXgK6v>GtQbfQfVk^JjmZWEje`NyaP zshP_G!3KZ=N|p)CbpvT0bYh`*Y5uW*HH7S7(yVe(YWT@-c{IH=TNhyqdKB>$Iw?gA zvFiItt~Ze>zkc&$x~P71TR6C3=u*DmS$36WQx5O*g?|E6Ld7Ju77`w;lMuR4 z=tT3Wg}D}@pTksOESS<0QNOY%J_Zg|>hp1yb*|yD7-J}fYFc6NTIszs)v=OTgu=@k)^jUHhDlp7?#FXM#YCK{z+u1t&5r<5={HaMXOX{%U z+XQYsBNjj*j36RmG_)|+5=O(#s4Wk4YrZE>G`#eXGI2z*;eA)xn3VSH;+$Y5Jb zp)k3r6y6ZG^_1?n;yE&1SR4u_Jtm@&t5!!S`vN!geXEoY|LLN&R8@MIiX>7PGanM6 zR<_=2wa&e$c7Nb@ox5XQPsr-wKvqzfRv9hh~03^O8J-qdM9OURk`E4EOv|Jy8uqU%WQJtyI>v_T!s3v z#9wBQd`t1Q58ue8d%`zhWk-wm!W@RgC|v;-SLgzkpRRBRMd|8r+!YcY}m4qY&dF&!}Zuv;y12=jWrMv1XyB@r# zXbA;;U9g~vv`$u3Qm4)Uoi^m~7G;R72JQDnP|yx@F`=q$TP0LZ$4<(%9tnukEUXX# zqUMWo#k+V`tp-$LKd8%~S_=62OdLfU@wI$&m77mX-iv#{a_}TUn=ggSI$UmNld4}- zh~SDw2!pB?*)u9!2xi*3U489N}{7IiE@mKI;a)t6rAA5CRUho8L!A zWC>Wr|GdP`MvF4*ldg0m7?P)lr3d4Q6z6Q+A+#-zM%l!bY-3fsAVM}jeOW^>8#@MG z*}F{G!(=LK>7s;lXtH^Rx!BPVi_}FqUyC8x?%tVI#>tNN-jSAPUq+y81;D^loJKUF z3PmY%ZO;l6voCtPR^>aFE`;_`iGbNgkDQahL*8D7x5Rveo6b?d!o!Y_C zGg=hP?l_xhM@@}K^_Z1XmXIeF+UYi_VZ$>sX%>@rkE}t6uvPHW#y$;cDSnA@Q7Rra zvqD{0}u~-bmX|;ktV1!tp|j8u_BNYWDF0h zFPABEzi4~ZY|S6;V#y?alrpqhA5_* z1@Q9QE`w7g3eY^3Q$m4rF_-R|_!qZuT!G(aD&tT*Me)B9g#$fW9Ti%gt4}^4d&xRz z6^d2LRfys4^Cnw1M%jvzJVh{+DDcKP)Jhj99>@&Agc=1&kz-<^`FlcYmp#Zg!mKs4{z@$1Z- zh7DbW%$-jKK0I9aAJ*tXU`@#L760-TqGV%CsD|nL4() zuF0f!imKU0m-~p_wxIblLZtcg`mKI9{=B7=D*a02#78J$J5^)A(`neQbBM8ZMQ~`N$@lA08cCX9V;U#ai;TQG| zI1PB0-zw1jbwTMu5Z|ai>pAe+A%AW3?@aFL_-S*CqV%^X5!|~)Huvz*81VX!Z9IS7 z$F6>#!Ww6y{&osN8*K#Yc=YyLg8w?n_I(0IVOt3!ig;7{IgFW?mLg7Oj;@=+x(}J8yhstsFsHsc6Y9tJ{BNgX;`X7yyZK)sCuX6 z0=-=H%1tRs0f|{nyra1Xj0V=`8PP)_^7ElI69qZc{MUYR-F5oeWP17X{}m8%U^av7 z<-j*Gh4?S|#V+Qc?TW)Nw6GNl650JKZ|wU*sm)E&Nrub*l(9JcQE+CZ`Vn3O*@$|Q zhu9+|jV=CTG;OQNV%#ubmbR4!O1v#1n#Nrlk0&O(lyUUiVJtQ-;%8a>B3?D-$kPyj zH-A9*+M5qOeWlZRZWLNiJVjbQ^+NEhN+U!hqIA+plGMX; z*iNEkFc6y*=}4{~rRwiQRFS*BHJ?&KA>V5C0+tX3=Y+Cxr6-(k=lv30vLUns8z9*l zt1ybQsQJ$fGTYJ`Cn03HyPXWi)099h0u;MI!wS5mbm)P!VhVx)a!(ryDlIm*dHqzf zPpy1jrh~$$_RT9MqC4w@B-Z&{6~wUYD)3udMO5Pe)F}_PT({LNjDg%g%+M55lL3*& zduSv>E9M<9t?q%hPJ~%3tIHV*lzC;x%Ib154*Ufe_d|b4#$Db@Li_n9p#fbPgN#&C zIklfP7guR~pR^)3N;H*;64Upg7*xME3d>tcN>?lnYY&v9#^`Qq0tgbc3(n4`7SZzH z?qtvaBOJCR6GY{+qJ>YP2H{|-`OE%>bag0(&!T89O;KA;ZU8IHi)e(e$N*^~qZzw-MWxh|M-nAUH*gvlghcaW z0u$uljKCI1 z&C>vs0-rD|{ELu_l=&#SRH>)gZhlIDQIfJ=e-xapM@9^$@sDsheldt~^KeEHxr9y2uk418nw4+z};j)>#zjixU2v-UJfAolmTrU(}LzN z3O)fIXD_gyaac%k{sG-S9_{D!8Gq_Heb%4u=JYv#ig-1@pi^zHj=Jt4uKsXHINvp8 zJ-o>N=Z#z-S0cpLFpTo6q!0nZro5NyCL8W>f84px#4K!n&Tf#S6Nnh+cd0cJqC*dD z1X9N=l&U9QO-ap@H2;+WA#2|07ghfb{Qw0)yD=ieE&DxwGFli!Yi3q%f^%en6vhE( zZsL?;=td{X`plMOl4Uy?G)&d#+SFPn<^WDMe@zeyb08GIV`MOz)&$#A)X~Bz&tbe` zs`{S-1$sh@B2X)GCs#YU0H=(xaCr8?qAZ}Vt|U%V9~u<{qvBzgAIayczVnK zL>+odk_viDDvjRrF0}W({7~KG)bhwOQ%KpY9)V<*9V2p#W01>Nt)LOS!R_LW<%_8y zG3y{FLSJ@-@xa4EimlfQic^{H~nWU6yf(}>^;Y?7Uz ziWuhw)y?s5_*oJU6_4EIZEGjU77E(2o3%nIFzZjd33kmieOaJE(bq>|s< zUVW8#D)!DptY*)zW_cbeyS8#HSjw1D!F#<2@9oI-ZVgpLdd*efAbg8uy zE#eR%TYVit*L$gEjU$s`>dy^r$$4eaZJWOz4Q#k=?POcQvDb363U*3SJaD6;tp3yB zm{;A5b#Xwe`bd9BHC@u5FrjbZOV`PWf}@afIxP%B)#1^W?11e1h`NlZz8>-Nw+i>E zMjF$&UfUY76eDhQKJB8Fj@(~w0gDgWG#D#g012*vw+%!!jkoS~PVe4_*_S{1D}Tc4KPWyI!wV@SXu}mRSK<8qtzrDQa}v=acPjTq}~0PFAIn?DPhr zPV?~Gvm7Nhh~ai^!^WZO;lQn9I$!h{65*HVMgm1FR- zdmD{<>jS*7o$Kvbc5=ZHhJBc&6LW$*-@r&2D^d7bA(n8mB?|A5TL|^u&<-E}mN-Ea zUBkQYm`h~uV2stj+ZZOucgrA{5Xg+7fl>2I<_O{TE3f>$<#ZQcb}+_cV0RXC{^gFK zxFL>$(fMVjZ0HRaHnea$)`G~M7$*>T&Ep!|?hc|j3W9**TJgVEr9xN7Fs`0u29qY2z`s$L*Ps4FVC( z`^~7`{P!Yo+H$Su(^lRx2xb6)EwsGPg{MOezsY=)1pnz9G9`nd+nrU~d@O@|%jq}( zAxYH->XTW`HRn&)C%Ih1y5{2@n@;v1Uj-n?ra#V?Hl*@tL#AN2^zz8r`UK#$KIZoP z%=2KTH&=JbVl_pH7YL4bIIPlx9LkpRIbF`DNJAu%h|Z;rR-XSVN(j(zoId`eKITSF zV}iz$k$74hgorZ3ei(Wee!;pWV^wqwtVY#mB-^~!PDk)s@W1LtnH@MJ!rA7~h@l6_*=1m$zpxvmG_~Fk(K(!So04|9&k_hrQ`MO4uj(w^AB(~A`_?&vfzy290cJNUV&jT zip~Fa7}*0Q(%E!_Oj?6VyRgwGKw`?5kee@P9zfHZ|2)QlAloAu-^);rGqPqKiebQkRUI!sVIUS%-W#d9L6ILtM8j4GNQb$dUN4K8QLR+PH!Jr#clI%^HwR@gh# zgTh0Iz%g*C`RIYb7Bq2PO*He)+MC4NA^m$mw`jhC=X^OxCi}S12ZmGuJg$cZWAYNY zzRw&QIb5X(0MN18orX52M{6XMA_wMbQ^wdeTFIzE zMT|3=`;VrHW~=!X&r3A15F8$0m`mb@8^aMC>q97~s+YR=)~9L*ZL)6yMl~A{iCTCh zUb&0fZrCh)D|KG2phckvFJ%`x=Rxo|;g>ip8EpMxg8ZYCass(LVQ4@9Gxy3s&3@%8 z*~fc~&|~n8VTrg{7rnHtaK(a5tZ0M5L7~J0Xr+e`vNL-i89qqZTb^v4IF}D30 zr|XAmM;J%>hnWG~N?`>wrP+@L3}xQkZdd1-w-DM#=={^{CuMB2pVlv|bIW|M1Ur;{ z#BTq%-ELr8UF)${7I((c0J3T&wrsti+=XIE`v@;g-sbl*+ylQJH5z%F!kc;n2f<#cFYPv zyRv8nH5y|B5b?t#)g&1QHOY=-hX5tGucbRO3JhTBl{bxfdeHi)iDwXeJ-dJ`qf?t# zeeTlwl1z>Umak{l*94{zQU2DLrK1v*~aU0f*e!HD`O$T}h5L4CKY6USnJ z*vS$G5+~b3=D$4{6Q}h*sOf>$3|Qmr>(SH$t-HAE9&FbGimWV{_4i;| zmmQHjICkBG-Fl#}Q;^Q}&EfQbcb#Nh`k(CA6Ma2RAGGN7sp*B;WSc(W65)9GuvW~0 zPgImR5JQ$Zu&Z}~12uv{4y3z79LS0)iaOW~ssz-RWC?mzoAOD$*<`#5FZ*IbLnawv zuI@?|RdHxch8+E+{v&maf)G_EH}P_MZ;WUHmu@QuVO$zykM)jh zkN(|9h{uBwwvMMn$|T)x^fO6vDoFO?HSv^hA)HD+NWiNiOyk-2rF^>Z)aH-=_x~Jk z5&+xDwfDc1MvZ(3s{d7DWnaJ>(@lbU#0&@+aU|X6#{FY4 zxMTAB`}snMsxEGaDsdHNsQ~i0VKe6TF0DhVq_c$mIY0|=t zs_a9IabVf)SfE7%E`jzwt2K<_xRs{Rx`neIRkdTE8&!{e;^scN$^J%NxW|i>%+cfx z*`Kh(f`Rv2U1A@@H0HBt-R&o`*(bE_tJ`{rNbj+5^iz|pBDe$t1v?dCvf-ow7JQ~s zdwf;SqAu1Dkrn9?VGx_=^EKyneU{7r8k|XQTTX9>X3XvKfwQOD55Zr@7@H_SI+ax} zr3VCJdN6zL(RMPf$ryFY9RQB>08s>m7}XRYSv&jC=UJ7Wm}kn{=jRi^GspxGW`f|q zoi>e^C1xrK zj3X>Z@U3$9VfEfH(Err7T7*ycBzpi{7bL1#<#i*e0_u$dFDPNwU_{8 z`{0cc_;$gw4=AL9n27`Kw&D$Yog(cPx85eXCkI|dy(Th@izIvWes2A}`z%;f~pGn zd&F0Qq+DnP1w)GHc$=FLFJv47Xdc`z;3ixHBjMl5&s&TV_HWt90$cF1(AIxYi2k>& zq*JZ?>w7qzLZxgi@t_HDF?;$(kSdbmgDiRMq;E>GP7RJ2ap6B0VDjvIh&c7-`aMex@L;gG%J_7pN-C=Ec=|=H~=dmP2 zfcm+iXx3B}1B=HP4be_Tu!AbF=*Fu@@>Wh(IZ%B@t#Ag^zHl~@aV>_HP$$*oVk z5B9}*y}|jez9&bz!Fdlp3eNx9;^W>(5QSmjKleZ@y(2kF=+bfXN2O?5D?>q`FN^6C ze~3)hE9EM(UKO^?3zGGscF=Y_Uo)nkUOO2@-i%>!MM65HTn%p%k6D}U8 zaHq9ulIPyXGO=CqHyF4v-{pWiDr0rjxp&uDFLYC~7M<4F&sw=7>}ie-(yo`I?sk^4 z2-VV>K5|e1D%5e^Y|qP81?7-HkKTxUj`gUpmO(CXvjqz&)eXp*&+WiQmkB}={*mT( zQVOo0YNrNdP@s1Ex%##$4*;C~W0v$D;}T8L&kdT=DKE3HK@valdgQx<%%zl1pTyvl zQe7G2Y9Y^oR&vaZC3h0=N`@evci4AcCr3UD93>{Wb#an1A~J{)WqqvGsPxktKwm^L z`MT4MNxoE6?#Ne9qvAm>Uwz`ldsaQf=ZaZqfcpVUNecOlVh82rTS!v{^J-qI(@6dV zyN?sOzmKi5wtC?mGPP4aq%D^Y9q+I6u9MBg-p?CZJF>;f0mXleR^xSJZHf6itR2xe z>b(JE>qB0haM-vP*j%E|v|@cbCbHY2hf3}cuH{g#hFWg|G1gYMF!@ahvVLwN$Q_e! z!pu8pw_@^NeXT_E31cmdL(C37?D8n;F#=WpteD7xXMk*z%!M3C?PM)2(X)f(zM>`# z9mvS^Wn&#^q5~atuWW-Y2V45ITu@gESsGe@9@aOix4TBagCqu-6t(noF1=1R z%t88M<_NMicA6AuQY*U%9Xl3Yt<=@yAV<(?tt@HP!{SIeNLGKL)}&=HU265$b8?5F zYU!vHe@qEb8Da0P+)&d?sgkl2pic#TqaMQ)Il3Buz-tQ;nufTBuA#OkNPf1~SMB4dn1hwiP|I zFm+!m-JLJ+?6B=SJKS!z0f7E?H_1aR7IlT3kJNB?F%wUfPI{Z?7>;t_0SUK$ZV>MA zEjW4L);NhrQg7<7H*#_(oIGH{ec(FbKG2oNK4EqI4Tp}A;0cS|zBJp1&oPhB4&kGp zTf%3@>x0h@!)M2J`1C^!-@3#<#!AveUmf{6HQLIu?7TH#znx|2sU!EO(aq|}qqx-$ z@kM%X6#7V?i&BqQ8SD3#%(bnc9xs%iT+17P@{# z7M=>;JM#BulrIt5v2Hog-mm2w-ifaM2Cq3`w7=bd6bXqG!)ydIjUa~GMgz8VlnE6Z z1I^Ol2s_wE2~7JpExEw9EKzyjedPQZ-}2Dcl=Q}m>J+rF%`e*5dt>LjBKE~>{fJuM z%^tl%$xS^jc+7F&=IngP+KY6Z30SB)63BFO6l+H+%j zO$n1Fblg_pUADIZ<7He80 z+r_IXGp#cGE8qIRPqkT#romN)cY3urk=LO1#8==Hp`boeV-IyyK+Wq+wk6rO#$A@x zG_%d>)nBfLL$)_v=zHWz1r7N?bYYNKdo2Avd*Q651#srB4xvY!_p?A;Jq?VQ6Ya0J z?Zmt|=J=wT-GVGO5uc`2m9Lat=6hRy4zQKZ#fpblW8;}?X?nPuQ|}fZ~UZ5K61YDAb^S{{_g5M@NZ!rDVS$PFN+%z?M3H5=Ff_ts>SGV$A}2w4lo;9`PN^;!z)B{k{4S>+RK#m>eY^x9j1P#1^O*Jz`!|n`Q<; z>Sz$WtxpUM(fDM*`lC=ToeZ!FIzwEsbvhe38NgVb4W0~GL)6*O$w04|a#lGRunelR z;gi9zKdYVuCc9TV8SwI#p4LwW+6m3swv&NAw8~k7x0eu^9<}%2YP(pjD`u^%u~$xY zGySo9uMKds5V^hZ6Lk#5jaFc*iOha50A@ti4=UfX8R31YexxWaRR1iB_%ngm7|DN# z#oncISQ2y}ee#!4#-|wr?m_^pzYa1|@6R8{$Y;@Tu+vb>$?xmjuh{*JLFQ$ zG``&Fw$M3yvU7H^7z*gwtTnNc@^$;j46O@4lMbA6A9MqJ=8Exn5hKLg@G`Pqw6QOl zE8J+A6vuHY+ z##ft5Woot7XN1kv`vAZ@XU6i*Q_W={;pHkA^}BtM&PNClgYo{*>elT-jltigDWZg} z218r$kgRAB7zi?qa9V(etazMQ98x0!;DCI?UT|tx@5tZO^VjMZ=I^WUWlLgLxnc5wpBKLTTP)s~Ac6 zK`sQANkEWM!y89Mx;=6>GFR1IVFKNrzD z3+wAe|1uc!qTXDSv7)JJKx&%ZLu?naFU?;KT*+S0Rgl@s zjoIb~kH2VZxZQE-wfR}}9ExR|)7qyWTX6%UB@Skj#U~WlV%`gDr^DB`9FKUgue)+QYUXiuGKi-24uKIV3z}aQElpPb zVHkzHb|reb;5X9f)LkF?1VrL(Q+|n;xXtT$u2_{0*aZ&|_XhUAm)`Yp7-O5|6Tq}U zBb)ZAoa6=>dgiKKMz#xs%F_33ZaiySX@TISd!GBEUWS*n^P-3h6~h0Yl9;U#L)D)j zJGwfvc=55Nt4HS+FR!oNv37MWSP34VTbR{fWPIpvKKNj8;i&#N?0MZgzxLGJh1okU z1ea#!7K5i&me#D<>4(n*cLdq$>g>we+|r_Ze1^6cPr3uEZtB3Qj{LiLa5z|bYR3M7 z*rVGGyC9exz4vX=J%`V3f5)4T4Yvl)JaGC%<$RNFD0 z-qF1Koz=VUoH&|Q-a4}HU2i&dvRt}t>h}5@-|&{fUH6UcfAEzcc+dKU3$v@M!MTNn z*(YWe++$AzEbETwYq4S(#gS+O03nT!B~m>&tY!GP5v8C;jQ*cdaa~t{%BKtLfar+~Pd_XD%LD zTBMU<=!XvOEwD5yGBPxK*V5u5sEcNz=^8A6PO~dLq+6T21b9p9YyFKMUtfii_bsii zxw%z&n~*la}ep=(T8(>7A(lYhLkhDTZaYy376t; zmqX`P`!3%*dv(oYX%szub@qZ7b7^KRpQs=eEL*>X&;(h20;z!A*txX$MDU)cFFm%j zFn7Ty=oXfiE*tapm#e#t-V5_D%`86c^oO8rT0=7@FgO{#FV3!BSed(wKmluqXQyT^ zUjJCQUU+hD;i4<#O^pgdnCx}LE$GZG7O$%7!qSEL-WfW=brJsSp?hj+oxztbTv%TT z-oxMOp3N2L!qOEqLz48!3UafwzUr2qTAY<8^r_8-859xo5S&?G^im>4n!9v)VOF&C zs&olv&!7)TIhZ447rjQDLkrjD9-m{_75;ei6awZt_Uig&^v~nDhRrCh3K4&N=8{xXBXk~`3gs9DMZ^9%9-3WyYKcF-fZ|+)?m=_3p60sXl^tT`_XJhPFe$t>{qo=XyRe%=377FbG?f>O>G!26$Kv2Gl3-Sv1yhTXDh2bv>^KK@?PUyP}7k z=S92DvDP^Yf-#-jPsQKnxA%EZQ%;*bmkrKK`UxujHov{kdzx}O+H*OgbNi|I+x+tX zUEJNZOTSzHukX1LZ|ioQyG10R8#_Bc6@Q!G-shc8b~4^`xkKmnQ}MU?<^S*T3T9gB z1ps}20z1dg#ie!ZmBa6?OKgzl^_3Nb2KBo*w<=q9@s8p5B0!5ecXMlpWk}tTg$qY6 zugop3tzyfLXrv?gNNY=O?Md%2QG!dDbq45xk<(*$zUj?(z2*3cyWg6fde^FJ^k-G@9A&8%CE`_{|}ClW61yj literal 0 HcmV?d00001 diff --git a/test/wasi/wasm/stat.wasm b/test/wasi/wasm/stat.wasm new file mode 100755 index 0000000000000000000000000000000000000000..9007334d37d1a166a9399975059aaf324bad1021 GIT binary patch literal 34507 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 literal 0 HcmV?d00001 diff --git a/test/wasi/wasm/stdin.wasm b/test/wasi/wasm/stdin.wasm new file mode 100755 index 0000000000000000000000000000000000000000..7264608753229f610a0327453e49d0b0beb01ac3 GIT binary patch literal 18390 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 A_W%F@ literal 0 HcmV?d00001 diff --git a/test/wasi/wasm/symlink_escape.wasm b/test/wasi/wasm/symlink_escape.wasm new file mode 100755 index 0000000000000000000000000000000000000000..0cdb8327a1a23b88c42ae305be21ba6832cc3c4f GIT binary patch literal 33026 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 OZ|(1H?f2`9^Zx)Dvf2ay literal 0 HcmV?d00001 diff --git a/test/wasi/wasm/symlink_loop.wasm b/test/wasi/wasm/symlink_loop.wasm new file mode 100755 index 0000000000000000000000000000000000000000..3883d5278c7008ad7053529111570c9ff7c8574e GIT binary patch literal 33009 zcmeI5dvG1sec#XS-V5O3-phLd@Bt8{d3GsEpec%!ACyAJ5tkMzQIaJ!cGFDz2Mv*s zgbNTLUKG{X1S~0zlP0Ybr-|*Pjp8J=$F-+U<0fts$4cWQZYs~He{|~Urk#mr zs%bloXIfkRe1B*6K1hhB6KAHKiDlm1JioJ(2H|M}^OF}} z52M=U+4ZN-FJGQpa+Se;BlNqlxV$#!s)H3GLNVal-1_+^7iXVZyPi*TW~{z zeKqJatMlt~Zg{Xl9aiUNFS_C6)O{l(wYXYMk~kR|uDa1=B(9E()T>ojjU*k;RpZ#z zMyexKH(ZTfqd7uZOqZDQct@ogU5=^@5V?40SF#!9ue?~PZ@TjGvab){RT;W8cWHU` z+1S<3pMQF8_VW2BX4mGDX!QK~i?i#q=jWC#Ch^dD=(f7weVNSJFm& z$~U0!M{J;G{wsFfkq@V>{MYTi?VEmwZ~1onmo&1g=RO?+h;ctY6MsbPN+!P zyZlbS>ukE4;rIC6e$Pre$?%g5|C=_{R5ASCd?cMNhTrF>{9Zrp_m#s7;zqh3%-;YG zZ|I~4{DFYE-`}dYqG+@IAwv<0wCbafzx`o7*MIHJQKL`#qtGdc!guEvhJHPIILPBC ze~vj(o;HOOo&53&*ZR5XEXk8AS>;OR@?=G#hLW!M%4#01Wz{@c&xZ1^s%Hm@%PRRu zCw0C8i?YNI<*!&xHgvWLKgGTVg(DsD)-{rPzKM3Z{EM3pH+R^qOmcYzX+V5GZQu-_ zY3_hEBT(JtBLE@gIUv^Ouf6u#FrafsUuw6$bzdiMU&%*SvdTIGZGG*4Jy%w;D)7># zYQGgC7Fwxw!mYf1uC{W3-t}sFt`|4xTKZdFxujt`OUDM_}jbtz#DId!BU&$*g zKo;nDcA6&fbn5&NE!_i*?<*J`N^7fFuSUHtAZFsv1~R>~+4Rt+qJh_QMii=0Wnp!t zxLL_JK@#j5SMoN6@HDR)wW{8&O3}pPSWrM1jicx2N^XSedycyGooxg_oCoMW&JWSI z$9dVe#;}MR=huAonUxc<30_?gNSydtJrP&**vYt<;Z{i_rB@&^)OV{*Mv47M7g0Y9 zR%euqKfHPa%XXm;DxdxNL`W}2Rgng!L7n7=#+G_Ay^ZM2k|M7Mmj$% zn^2>1!RIs)8GcK$n9_)5aUS{FdRD_^phq&r$HwIrfK1#Q)55QU1CCE6{0sm2%8od4QjsockRPbvtLL)HH1sfKnnsgcL9ZtM z5OO5|QD~#XzzqrIx*vKc?1apd=2Ez6WHpZ^W0WA|K-d6!BzgpoQ-REe`4B^8h^@;M zc+l1u!?5T!Kqsd#10w=qpk2*sec+=MeI&T*#+ha{@UzAwiEhPVNU4iOAz@a{v=5!` z8mkOCIDy;9+xbr401&{R?s(wFiZmnP&5b=_*u{y)iC7M<1e#i*heuGeNoZ1%Nsd4W zs2Xjv>cHPo)Vdxy3`g#tLBAaKS<1YS>wY$1O28^up#keh2v zK|c`=miEaU_WoQt1gMm?GmJI{$xJ=W$JV2*K^76FY5V~SYL*@HOI4~}qmBy7Z`4FG zf}D5iUeY+KO9P#(Daz&E46dMtppwPW=FY}0BYaQ{$N?z9QGghO>iKi#I3V&nr3Ewb zOZH5?tS-gqf6)1$c-EX-$YU)G^*Su#O>$VO!dxGmM2MlLW} z&BMqIf>VavQbm_$j{6Z)poNjc8MDnkG>fe8=x^^Of5Jvs!&_tvD3F{D-Z*5iJEQZ7+&!GPU z;2G`^OF^%NjDQ3t!oR?Xuc=oEWPtdq_EhmToT;z8lQ^mB-NOpYS-FG>Dl5&=&^7R= zq8RAEs$c44{)D(wZ|NAlAwx$GLuLw*4lP!4*M3n!CE?oUS)61ojl-CRGCE0g0N+ht zkjw?}7-BUdN7_ka2`(5Az2x5taaZkT$ixLQqXC&wx)-y<@CPh9&S&KOPU5kLkVN=AOz#|vH2%v?b7?xn3}D8vE#Wq0yg#T?jy5{E9M-D3b(zzBNXHbFurT4~ z71bi(=B)BRvL~4`FvBFktF8C}$;wcE6%ysG70pNuchj&G&4}{{CDe{2L}?a<#)o7L zPQ+gdvx`nQ7)rceC%;z#EbK+88>~B&}?Druda|LsN}D)z#Ua>LrYS@isO{uC#1ozTD;lVQSYJpdNwtf zSTT`7aClhNZ4r{dkfxBp5I%JU29nkS?Sa3V-_wWaIpcRH&((vF029W-yOtkBYPMx< zpPY@9td&{J-QoCEiT^z?r=}-UntNiR$?dg?RwGD|b<$LY`cQluPF;M?hl8F}GgXku`b&)FDA3sF_jg970PdI7LgP1%Om(0Z0aU(jh20lyPfWfmqaY zEcvw-A>E)>h*DVpYi)v4E9h*L%qK%=WD5aB!lukYGEBuS04mg-p$ZcvsYVNZwHxuW zDkPXLV_XcN7F?ibi8h|JB(Q4uB6+2wp$Q!d5VTIGpE{^JLA4N62x_I)nL%kD+%BRX zS~E*nEE~YIS41Q_7H&j=Cy*;_EWif@I+Z4@#F~jqYf*%A3T`Awy?|m=c+;U+IBFJ) z01GmO`!1P8u)t)zvMhvRY_k`(-ouFsK|(Ykf_#f=qs`cavn#$@^&s5cdw9YeBW@UkmcwT?-Pz#I}lI*}%Olc$h;gzoaLhHbYpdj@sI9 zblbI`VZ>}23A{%8)_w{x1FERNb!$KBinSkA(l*wlf>ZzDx>>*hv#p z+Ex0D08kcsX=*}P5sX04UKxSJ*4t=NzYz!n3PaOryO;$~ekZe`l*gfx5vBf<0ft5x zpR^}t97!m21Loi_UN39WC%H9cEeK(Nrp#J|CzD))=peb}3PC_Kxv~~CBWux3?npkQ zkgCSGSanDX42P^mhOfIZZ&4E(iV{PRf>STt9!!9$%Eqt_Q7o6iskBd&m3fO&qG_dg z1X|rho*8Kyd8P}JjP}Lm0~yE?;LsaM#v1oaG9HQNo+PvEP|A~>2%&CIl5ds(xBR?> z=mxW&(Gg-r+2$0%Q5G}TGo64Z2NpcG7 zXgOe#=|r0#BKgn&s|?BD!DJ=8zCBO2Z`bV*-OmtSS)kjr~R z;-3JOP%%xcMaSO% zL%K9@-2h+{k7&n3LczjXA~Lj%t5h7=b(P-0QEJmD5y@k+WyWq27>TyUle8Uqh!xcn z;`O(x(uxg5A`#2D)k#nTI||3snkC0<`EHDn$0qb5AVL~~WJ5>{{j7u&>(v}|Y z%R3krBRdj>u*gdW7DqfGH`Pldf=UQp~P0Z1^^TTi=K!PzWQ4NEi(*%(aBka5HM(x-gb;TQVSD z$zO>S0^c@L2oQpL*KVs z^5H*yyq+mb4^xpu5@Y5=V${m^do9;_0M#B0+^%zXj4|Nu&_mnNWa#VNowi18zc(%e zH!LrK8_W7AxT{D@zM!bRkeZVTz-KzDyttqah8h~MS&hp^o5%xr~TWdIBt-&;=|% zUEvOj(DirSA;C2CwoAXuvoWnRV|JHt1S4T|Cead3ln4npa>jJAdmS=~>gca!64D6d zv5VxnWI@UtPla>_P>@Z-b-D*?#RS`K##6oar}HHiQ~=WT5-O}FJzVA z#RFgkc#@#4SEChOE_d=NMT(t+7FG5qedVYdk*9~Hhm*++=WN{}v~7>Z z`Q(*+b4{lpBF_K(zJ_8pP7J(uV1=-U$&}dALj~v1<_rvTaiSp+smpY}Hbe4#{WB|# zlb`4>k(TE#BT#nU#=ujYMl_-fMJ4y0zzSb&)!QG@G_IaOb2#{;HmRhBNqZy6b<{r+ zMR9pz27V@CkYXhm#*;hjUedj+CQ%e0LfS^CxTs7{+y08 z@y=BJrI`{<$dt4{s`jjEETP`TNrQH=aP|LCiCUWhkgnvQM?bIpv2lxoPMk4e;$CSe zkY*9&+n%2V5+V}*lf*%Uu#-DD#sMgT(YqcpHT`dYN2nc4OR zIp%zfu!fP84@R6j9N(gn9dus3@D*`z|#_+KGa+NgqAKFnf+w+IJSTczpsSNGbyZQzaAEOs=Id_i$ z5qAwOGZfgx4~K$^gxRC0RD-XWszZTLC58y<8}bkpMGE=mBaqf%|5JoqIN)uR_r<)I zy0~bw=I!E4qqI>y@=E(>Og(W6;1y0Dy3-XB&{WGQp}>WhOLtBD%OV_?3nwjQ9Lic0 z|0{7c)Th;Psnv!06a#XWtczBuSd~JB81BJfvSnkmTG1j8>C8z{;EfBYwOk;1ATtCL z8YCn|j>*OL?+B^AR#BV6|}&{0_$f|<`}MmB~N~g zURw?h6-O=Lqhy?_N>>%EhlgehpANll8+(7~h1?PPK}N}ZSU75PP2nIDhzdGsEW`ZE z0@@U(xDW8A(kKgyUhLI>S@)sdN2}JP{cD;K0kQ4>*6foCbVLI4aQooS^g(h;P=PlSfrF@?s<+0Lzi~@2Vc%;o|kP z6=~bg<^SDb+>ub1f5U}FHurxn9`@p2XuIm+!8B}V_@&tnrjVVQ6GMQOc}(`X%?gR-wnDQISjkkqLfoD|-HM z1a|0gthObgX31zics%-^XC^K%aW$8CPx z9I1wGJgOesFd8>dMt*^IzEc0an1o%=j1^N#C}hTLX~u4i;GERLN0A`+J2}m+N6y}L zVFM)F&JspR9=AVbklE?MB#j`;y`6M8IYqX~oUPae8kS3|WFrq}HB%5Q1!da1txa=| zC^!(Ut4K>O$d;0W(h+63AG)&@!y2ZTtA-d>eND$2q>(Hq0d*#TEh}kDL}MW3cQQ12 zt8_^03C0k~(B@>4`GFQ?S)7I86YBOG-Ee!15~EPb5Wvl z1E+yONVGpD5bHmd^fZ>$R}rTLkoBAYH}m$kSRy5Rdu`5{wD&A)xY)nWA{KaHexVMsKVuhygTD4 ztt~1NwRHPgo)k|Pt(xXoHYOF2<@&ZL5zP=37t9)Ij#GfU#ooc45`Yu30u+41LG&2| z+RgyE_9q0N08jFlI65{eq_}^-iciG1ar<1j_1r!mZufEfLbye|+W$hgI>{RMY<5!r z$%x3QXUh6`k^heyxjbO~g^e8o!-Y7JOvy=GfR2l?w@r z`KvUPw%Xc}(W;&ixoT4qOJzF@TB)-K@42v|QwTc3ti#;MOpVI>V)F%gvY?eT$=}{A zdapv~qglz}lfr7dI4O<>kdcthRp)a38EaD_m?XN$T9NzcK#8IHozNC|%_%QtIEMD; zgWCFvLcTa>nCU>XrTRaMc&!plq997In3%?p?DM)yX&mlO#8w5x!hb?M-y#9$gq|nC z66IfkATpjTWVvT9Od;y0LJKfoQS&rXSNf!Yp|+J2UILe{LRAb?$2>H*0}5hM``-wQ zdIokm5Tp8*lq4hJa(D`9{-qi`FkSzoG`ui)uMm(WML1%?+sm5eMX2mMTItE$em?5n zeztq7%1_c(*B80UroW8veHn38dKw5c(wE6-CHX7th}o#^F9?Pr8kLMyN`mQX^4?9& z+zC;FgRSJ}?aZ7+TA3vTmR-Z%QuNLaGl<7Dn33>l6+IKIEKf+e;&;G;D20%lO1vI? z3hPO%u88Z}!en6hY{Qpguk|z2hFO(7p6Nn`_7vrCMbPMJCvvsvtCpO%fkT9crO`>1a3{pJTquIBcL-ZO875cQ((#GchW(C(htR;5 z-_c2T^mFZ2?=9Pyk-&6*T$I&+8l1P}RG8@Dkal2^{)o({q(5cC&?2N6(=kO?5vfmF z#HXx}C1UeKN(YXq%b0SZF)yYn$(m`T35^@HttCq_=EoPZ9$MMhL(OSm2{9k42QvgD zxVm5)2#Z=xEy@|*h~tUOqO6@i-pO{_9tU-aa<{-9=eM2BCX~C~32(XCl}Efs%S}_Q zc$k>(5ZI+{N$6&}OToZy=5Ockk!d)N?{rklfeJHEs2wOre)np++fQUI zMga)x<#mR=Xx7(RWb0=Tle|$@j;@iQj|9DCj{(~B=@DcQP~sPH4awNTcKw$n6BLD1Z5*zxJoRsv%jA^3lZ%5_I57 zkgI6nq3Lud6b2;Y8b)uAsF%_#3^}TtD&%yJa`fNVeY2?)IwaX6rs=E)7Epdsdll%L zD6Im6PiZ{AbAh*%cWd_u(#fi{(mlaIG-RH%2`8sT;azeIk>2sx6$0oAH;AHdNfgS28jmGTm@1Wk+ZBYCn=rh?og{?l4+?!5gvhgWj z$IaRV(_c8f`L`sa`Bt0#)cgbXj>Y@+OBwcc?{vCPLO=a6-o1~~z4?~vD6=Hi-u(IS zXqLiHe>B^FKfv(6S2v&V`~Ch|*&qr&@{e`Rb^{QSRDZZRl{b8Q;Y@Ri$91f0G2Zc0>3-y^1myUs zkEi=h<+GMd!9MBb(FdB7fH(ayzyB9s1T(#5xK|ddElRxPR+{=KLHiLdRa-N9JuYNO zLoAVq&t_}p?g4w~ywGnL^^^?tx9cCuX}T(|nO`Nu(vShRXLxi>o? zF1HFU@65l!(K(m@FZ~8C@0w1%Kfq4;)>iv(H%~O)Dka$!TBN>lQl6faPNfJ>U>2i( zAITQ(s9@4@#u7?T4AP0hRV*qwfKU;#(8#f2DZ< zO>b#ajRQfp$8x@Hpe(}JjuE$Ka%%5A)BB*iVD~$)9E}ro24fGiAfmF%JcGx$qUmve z;1l`qN`8Emqz=?eJ_wz)UunCcUFt#M5k%lPxHLlaKwt}+xUM#ud3WPY;_Zn3J**;{ zui`ns;nJxAE)0MnRRE9cp~0BEO0poV99jijl`dQbD8~$+$D7B{sLDp*awqjJT%+;2Rv2RVM;|3Km&UoQJ+9sOQ?LP>-L=#KFp#sBP5jXqQFCeH}3B`H)C7jYq2Huf`p|IwH$Ho$(P7sTH&+_2AX~LigUeC+&=0 z+L8=T|I-xtMYeD__Y!-e-iKfNu;-#Kk72_ZBi`jf?@(gT-S@ zj~qGo2i}(*z>^?xJuY{(=k~@Sj7h-Q_SVldk2H=lj@BP$25>8-70{GsKUy%9dH4Cf z-MU~Qbmq&4+WhBaZ1Z2xFRb$`d|LuLlz-TYf7Xgy*j7LN#B`o?$I$??Y88&xyrjir zFm0txx<%7wM8|@D7tNbYX)te4W_$7oRQ*F)T8%d-q}Y}Jx$IWjwO8+Mq6To=?|%_% zo=)?6dpcpznn%ed{bYV%BWvN|*KE0f1&}SWWsD!|4c2IM1I^-yL(Cl3UHIuN`vPKqXl*)KqbXdr~9pZz^_+3F>gbv8V`aWfRJ$#I< zu(BW9Lw-uIn*|MwJT6Mzl!yNhprSax9}QUZ|FxamWq!1XVGfiJpC%=T2`$2#w?(e8 zzkEQ)8~e%!1wI(j5CvH$1l%?E`6h8J2AJbumP6NZGHvwvs9ZSNd7tp9w1hZqu%e*~ zZ91??I?$u73T@o@>ne7tf+QY0|iBV1S&)f9Db za8C=+kdh_nRcdhwORd_KI6B;qe7<2Wuy2y${V^+xVU+O=Wj1mf9z-!_u z-%2=@et>{iNto92@6Gts+Uc#I{{4_TnaL_uWO$$G+v=1@Hp%uStkrpku1lk8J*T5I*c(jbh zG|qZl*^YyLTs;non+N442OIU^-d!%qJWbw^|0zc_82C2JOB`gF)2YCE1 z?re7ZN_Gb{V{TszoIlff6#ja~*g^ranY?x>J0uXZ!}$yE>F`lLCS%kYe+W3TLqriI zVw6*WWS#tjpJ#((mllci!kWaJ4jlPvXq1XrNm4rf!zqpQ5Jx5_fh@+$mxG-TQ9?>`_laY zF7iZIvKi=BRJ?j$ar2G)sGT`m#yva&35Topi;R11u+wc{O#r2(7Hw1 zEpEMCa?cug4fUGLF)otq@rNk-p@&woHjAtdslQG35xAe~kWf^&>~>@q>j-@giMpCh zatvP!}!63H!HgWPvSsS!f$R{zCuTSF@SvhnoAjok68+FY&MmaXEYTMvy8O z#fRDQIKY!i03l>W>ISV=&I1b)I1qHDT!%o2?mEO4AV>Ue=y0S^aqahgtgS2t7LPGnqMedphm~Ma z!ObIiOD8KGs6OMCI0NdCINOc4J#4}3VG~rzZCH389*Fb$gY&%uHAj2Fc^^Is&i|jq z$G?#v3d6vE{^9BDuJjn8OV`aGlcH&_36Au_98DOb7bRbtD$Agf-~4%+T6 z_KfMLR|Lk9H)Gf_f0vG@wV*6Jh*)PvHMwiqO1P|0;!bJ3{2gp%5vpYky^vl4D%5e^obM}C1?7-H zkKc%Vf%UksmO(CYvkeO=)eXp*&+WoSmq|hr{;|yME-83^rjr?vu0-vQbIl!fDgc}V zXtwA*&Lf(lpBpr#n_lMhgd~3QyRqIKW-cxH^!WmAN!3*`t`_nfnof`ViS%v)Uda%o z3l974>*Oe`0>_C7Ze5(DjED@PL|PwfH7@-O1~4R1Oup{+6Ou0_mAmrQ*Ql&0~@@=H4hIutF)omnyg5Af7JT$;oSzEoF44FFF-Tt^- zI&^%f$$Kd_6Ng7{WbN2CI|n5HFE~Q_ootwc49UzfWNYFKOQ0$3>>_lW zSh!iLtIt7>q0`z~(yoWak#dmi{z9!OTfy|GHJHyS9EP%`e6^cRE-iqn2zzhmhMXQ5 zM)qf-0Tm35`ba-cFB}U^wX%}=SJY5sqi-Zh-$vVYBpFxCa2-i@-x^79VCtQhQvMnGXdh;6bDi^$3kc~xD&&IcyudEPmf-E-zU2JLs~BXLi7Vz z@072T6YwAoX;>ADz;Z7-IC4WK+9E(!K*1T)X;Mob#GN%GicQ*%&1%1gD4b0PZOo+y zATJ9qf8cSsJwW3hB4}Ij*fMQHQ712mVFMXtg$8nXEVdOrvoXbH%f4cRXO|t{+2!|f z3;^_Z`Y9^0SmYH7K9a-T%S_a2IT>u8XE@SuI~;$y9}RQ*Woh=HGJz4 z|2R8IlLL9=>(ppF%d+Rzfc*+MtF#U~w~?ux*uBENlxCslS7hPI;Jv+g0Y~d4LOb>?hdQ@u z`-b;d>tEnKDU9~F`;R0ckz$mOVWtt$XvetYNJo`Wu{G4L438y^c$~m=@X(TXc4Udf z0`HC%b$oY0U%t^RFUnKU!VbUaK<|y6-xBM%=C)Pj`d+WrBl>koMRJ4FBW5ov!>^5g3JT>drQfG=L?Z2zW8_m=N(z7R(VCqYR{ zgwGrWAuC=)&Jzm~A~PU5RIQ*^=c*9{b_99LXkj@S- zo9oXjBtf&dE!S`eEun<<$?cO8_ME@}xfezj?Hso@xP7vgG1D5uzxK`l{Y;0wXc}B) zc(2z$Q+W+?PkgaU5en)fH4ae6CDgnRWk-@jYusg3O>;Y?UjNm4G-7Adg?>PurJy12 zL>C50bjH&D@QmTCtp#wVRF^0q&I?{3uAT-)!j0Z}u;awMdFJ{0l@&o2n~2YdD$7^N zFY|4rFbCL5=Mu$9O8R%7wO4{dcAt4;S>M3!KJ$4c0mB6-7?3wD?cj^n&p;)5$99&4 z!BJ>blW8;}?X?pVj@|Rw1;d&q#mL3Zg8(X>Dr~sH`eKWu@^+r#H0T$9D>ujYC)Zfb3#Nf0Tr` z@QjLl?pbH>8I6y%1`=kY`N%p#GI_<`ATsLe+hE{`)KE3rtAfzAK-e34LGv0EzOh#< zy7OYX=+5Vl#B*HOsWFhZ;%B@#MF(ygL1DRf4*MTaWTEvp95 z8SFKHPJgdKbjEK$$0WXzFBj;*>3(#$=Onenflo*V#Gu5jBGwjS?Gk#lpvNbw_~u|) zHGtS)uK~pRdkrEcN6ClC`uHTV1?r`MSP<2AsSwAUd)t5*8luTb$Nr;GuAFpO1>GU8 zIycJQ&`AelbvJy{v4^O;k&{j@8gf@V>DUI<-RMa-8t&>Rfyv4mCmnB(skV92=_E9F zJ5D-%6qCEwN!LPT`qVyvtK(vYu9&s5#z8sN&Gg60zB9nhLKOBQOw=_LH(G)1CNlfM z0GJV3Kd5}mW`wV83?fBxq5gm3m_KTEjgi7fGdwstk0n9((Wmej=X^#_rBq`5b&-*J zUs0VPpJl`0ZbNM+zqeanv+|rl7G%50)j&P@tl*={w~R4L3;BDkKDOSn;+5Qb(;>WD z%0({oDub&!=HzL(tK?If#u`qB%g%6Vhf9OTm%H5-yLV4_?=F@@0X>JcCRa1QL>`-= z_0eatq0{~Yeuz&JF&-~sM0gwCX4Z=~_SLE4j(zvy4(%%LymTVgX@9<+75uBK?>C~h zzob|qbD|DX5a@Dty8b5-F9F8PAUfGwfAJ|Ei_bnWwOZ?wuV(5)01%urV+H4_<}#4* zat(|I-M&Z{B7~U1czW&3j9^R0I*;u8F53PBJ$SEIFVqOd%p*De-hVlau~*q- ztZ1qnkecTA5!*$aOAAj!SMrzi6{`Vg*|M& zw?h`G+lWRU6x$n5mpPH0G09WdTM@9L%T6Z!mDgydTzXhp%lr9`WEn zcdhZrnJ4ur7oXBQ1V*IXwf|7GG+FrvQ5^BwmFVT&7qa;DTR!*+h_sTS2`%C!ig_K+ zm%GxAQ}6(Bzq9|n`i_so7&|PV0;UBT`Khp!6BUu6=dRjgY^N~DEd9vV=JR%x76@Lw z?}abwZF)&NFN(-eA^iVwGT~^%Nc~qPj;+luU3_Bs>an$rC;0Q9#}=2DFW>D}-IMc+ zbN=mb_mBR~M;>t(j;%d=X>opO;k>Hvy5KI&&M&#S)zzhCwK?<1V~;)V?sECs+T7~; z{PL22a+dBFPx?b^e&*1cuKc@rc+{;vGi(0>Zu*@XzyEFVeRn>v^X+dwJ~};g_Te-4 zw9f6=lbqWBy+=+QcaoZ!WIrpxO3m4|r*4(+p#kr?u7yT2@uFv_kxs{E%r3-WJ{$)Y#fqZ$@ zegE9r#^SnPT3+|7bNUOZb4&IIQ{AJK%=-29XWip-tC!~2)`ZfIfUqPMoPpT(>s?)DOdwI@!UY@-$=NIRmT=xdUm3gpuY=fsKefVRaLSuPjl~zw| ztUc?_Ev3C&!ah^{4 z6Q}Q3U0z!|dT~zEc`=^;vlovpFVV>`3`4v7ODv6wj10}*vAnbd>Y|xwx(*AV)7)ww z>DK2j0p9Y)`e5THH`bu!gUf5{etr$$_~WS;o;Kj}ix(Md&BHs|xYMi43v)~U@;pR3 zckHo(pCtkH;@Y0jh)L&Pq}wJd+CYg#rX?HK`+%Y=Ibx)_8Pq#=3km!de-X?*4ng&W9mqEffRuwdLUu7|#5uHZeg4ULhF#@P-Cm*fBY^Io zo?Y`#%t61kIiR5Q@zv$^sAY;9-9V=*ETMre*w>RY)1K1iujYWm!z5+p<8JxRAA)F zhJ$lFI=BAJG9SuEaV|o4S3dV^(RP}-6#Dfm;_MS8e3+CcpM=(HW`%kPy~~KFxh@1{ zhge0Kpz0O&tmW*Y{uJ`F(%{Rh%TGz0`wT*VDME#TG^u|Y--JN@jg4hY;@s5>b8{EP z7OeLC)619rES49OL(w~CFV8+Pzc{}>KPT;31MK+=^HNP@+N?dLL5HzR&K*7Cy+8hg z$FI7XrH#eK!+q|^jk>9+DHT*hX-$`UxW`?&u~F)i(P0-&*Xonb)rP37yCL-{dX|lK z-BCVFxzVQTo6hlg2^Ww*X;*S$8ncg{`d-hOJKnBV^TzNXx^`yN}|SM=j*p_t$P z`o5;zj`ux|>E3>7p_pI(zn9Xzd-c2h{|4%fc-yz@mhSA{Ik!jm_EQVR{Px%PHRX19 z-{UUb+fOYN^UMF=6%@?0Gzb9t{uFkOpNq>I*ej3U+mP5G%^Rz$2o36Yaehs5z+O@W(GAph!t`g&o)tl4(r6H^oM?{j7#dZVda|I+q7d~@@eXRM&ISzbeH=N@tNt*C+1Ws@FNUCJTdBk|Ll(bMryBs-c< z&aW?=yS%)=zhO4IrsxK_8om-y2*m2Qtn~Bcym4&sH z<#X!`|DdzJ-6-|4#WpkSm|uH*{nfjUS@kykYM0hl&Y$aCUD$AyVR)Lr{Mdz8!>D#? ze&dOAE0;RUt~%UrjDF{rR@OVt4OfT=#enObjdPDJ%|E_=J)uTvv)<_}x{=|&8uZDv zg^i9I9j;J^wa)wnH=3Nz$Hr=jbCpU`85?!ccx5bcV`KG-1jEH~r2kx!M71$D=Au!T zM2+ScWeJ@U%99fh53#7bDiZ2Nj!26%B^jP{&YN#(z+k__0_ZypYjbT{1Y}% zGyesS4$U;F$E2bMcG+SfETX-;|i~8SAvuPxzhS z`D+HuE-H7Oh$)zhcc#1PxySD*drtf5_KVhR#_vw|`k8dr@Ab21(tSX=-|zGLSJT%4 z9+G7JCmZqB zV#M3>9qC*#;$eTQzs=A2!-FI4OJ5I5jxg4dZhE`FJ+Q>TzEy8U(PsODHWjRD)yI*G z_D|}${+TyMjRCccLZ=|3-=5z{+Dp+xK~cW&bIgfWw<( zC#%u{tjDUatmV;qR?U-*Y$X2+_3WYwStTFqrp{O9;;}68Bl$~KlZ~8dB50DU28Cl? z37u;s^?VbxclqZxA8PKfS()V0Dq@OWy=dT!-q+j#+s7cg%f|phv_Bx3%wKusl~F+F zj=t1xedC^P-oBELt!9-C2HN_{0eh~jW>w&&P1TCGT2>s>Y;BR!%0}M0(xt;__li&h zzm2tYtd5yU7SpO9lg|GIwy;3)J`@xP^BHQQD~tR-aAtNR9RW;CN?J=!0W1`#Wqw%kVZvMcg=l##f(QJrSGW)dhjX ziC=3c;)))-85i>?Druzj3M7X5Zmr2Eu^;Op>PNxqzGe*+h4`!E3qZnm`DmWpy0R`f(;7e~roi>ck$Ne55B}$kj+z!#D~11lw0>R79yDZk5zEssrTC;y%F( zWkryBBsUIpeN}>6g;mfg>1HFaCK3MBO-eG!5eNZQqit3l_&bhTSESZvzynFDc}tcW zF%oHUB&Ol;!7Nf=!-1h*ikjF^G^7lXWFkQ8iyyT;@xw%x;zy(S6vBroA*3|%lRHY2 z03KyM@vEdz00$HV&iIJH>j{x9#IJs%_=ya1bB!73C&Izf0hzd!}Aimtyok=zLH-YtAj?v6doH z!5$p5s3D~+nIJ!8Bec%~g8jRbmt>=liUT}BC*Fp(#expkylK=51 z{_`iQ>n8#TVbo8DCK`3iZnw57a27*il4sEWA@GcLiKU>|Vn#p$6X9QA#9y-B!8Rz6 z0ph=~C(Iqr)K}g~oK*GhA*~l!QiTaBtIhGyHSnmSuFq@k)XDq_ai`wWF?vIWt{z6r z6e1m3Mdhwd4<;VTH_zZCYiS(DG?dXvq67GD`hsLGg2xEUBstP<8cT4&faoRvMu@v= zuZ2upATu728K-+OI}E?yq8EsH{6zd~!lJR;s5e+5@iqI>H>ANbD)eiK_zL7l7WC{m}n%GH^E;d`HKi=jHdJnAcGDi zBR}fn#hz$pt{@r|EhvRnhVj)LYvW@G+87beBj{?Y(Uw8k6j`9N-4(VfUJM=g(pEDR z;(-0KJ9({Q4(vdQLyyt!-2hj>2zv!8)K@&3ikE++{on71yF%T#=y!&8%;~B@&QRm zs4|g<5}PODKU42O6tfN|;y*7-PQ)KnmqbW}g$Xw=sTKh@XO(}=o@C0v43hw_w&MFG zDm=^_M+4c zRw#xw+Ae)HwPmq@sR$FxO4-B&z^p0?Q!NgS`D)KB137ArMs`6Xkm5RE@R%hq5H?ut zif3#CCX$NT@J4d;3VjB^6vVVznwip(lvhy~Hv6hcRmt9%MM)n)V;vW<%r=ejRR_yCatHH7#S)&&~9TF6R zni;juA+&^oGqhA%07#VD_?!3BC+a53zdmIPJ} zUnH+|G&G?@0fN@)^iv0QC#V*J3PG*ZIx{HEgWI)n)tXttV%Y$uy&@vfv2Y^_Jb_$c zV*x%O(5WOr`B`3#J`j3rzxgOfx?9~})LO;Rizv=&ST)`Ds^ zxEAEOw-zLXiES0bvVnV9@Gyr~eo0?GZHBN^9k;dL__k|5!-&~55_pXdto;;X22@dj z>(+kM6>C2zAvM?1(7@_x1;j}DOF$wdhb=*#M!TsfEP__dVjvwQ?R8h$i_*!zS&Q&wl1mUBB)4242xz8L)`DhaEqciv$%hnD)fgA64rzhWkhRG0bvNcMYC=O% zVhB=j>V?~b2~btp81_es4GGqeX;pK2C@V= z^hT1g#)FcKN8-6J$t*jR@+2ohsN0w1*Ghm}eqKU!gWwQxG$0Hy{Oe+F6P!x<$Edi> z%;kt+13&>K%LL{sK$=IrSQuPds1~q>kX=lgl`hH*Kl!bIW{_sIpZSf>+M;>BD^@MnXt*W$Qr;|v;GH!Je)WD8{_dF)? zmHG$D3FP5ispb;~bTN;KFtvHc?tlss8tjfC#*;=uGh|)jQyQ!tHke5z*iNB3VRwXy zagp{2Id;baV;f~UVBL=*OelwQr)r{=aml4f+S2EJc?ZK{WJjV97J13g;)o~YMw?OQ zg@cIT`NTCiUWf<=WKcv*HU@+NP;QWbu#FRalW3HVL0}+9DXjN5;g2|E(iLt^irMal z4c{bi8yK+!3Sk5h38SHfxt1^*Zr`FXmT_A$Bwxwjh!g_fHc|-aLsFRBoD?$H_EIQJ zUR4Tjh}#BI_uKJ&h>5VUXChjKYW0M2C~!mHw_EbzKYhHBDN7Gikwg+>=0jrC%JzFL z*LeWd9uC~Db9am})oBUMs2@0E(13#FM%7&`Y5=oNK3w;sQr+dBNM=9 zI;y<5pbmx_8nEPJ3}QDNvzB~J0lm{IwyN6mT^75=@?8KY-(@zr^j$EI46Z``MB*>A z$9hZgbqL>BOAmx^z{+Wp;Dx!2iBWn2ES}H>EI(c04vNt8cfBFOH1xL1pv$u{tutfx zmT&|kVRRAu|2{>}bbg_G#eu?VnuVoU_2;{MgzsNBqM|Jn z@bzv<8EM_D!Ltf^2I#aYhqo+4Y&qy~E`o%1REP=5PCF{0bUJoYuJx!RPP4E=1c=*T zkSpFxUA^we#D2dXot?p|e=3RN&E%Ql;FDj>D#42fzzXmrL0d0JtGZn3<}=D))QI59 zMhJtl7WuPETnJ{``PC-El#ZPK?XQ31ed#E`V*V{fFkH&cLv&iknazh)HYQL1&eva| zy>ix!vSs$(kDmVBXSd?#@p>btUvQ_tpgE$|+2_MjYVWq>7jyiXmbXJxj4~~h}2~|Uz;KMzQLK5#>r0%mPpImQ!XU*;Fv?+!c%(#4g{0R$ zo#|nfh}L1>JsUyL*R{#zZXv3vu~<$7yQ=&-9d!&z{iT@_O~{nAKcx1oYAm7N!%2ho zuyFPNQi)od0g#^Lphus*CJKsMoWS~do48*Z3Zz*C`L^e0frN;J|0Hn`A?)N1j-An_ zV1CE_sZP{1sA?wcl(L09z0}QiNDW)XQK@!0dH>2Dga}&)1Rc85kha9HFfK|(RXdNR zjxDQVnr0=xgS98mQGQHeV>^GQo3?!`CuiJJYC>TRr6gkM#-JPnD|@5LO3ngo+maYk zj+TyXj58UaPxv-l-t-o(>LmUsC;CdXs+rmL207+@H(?DUDIbhDcR5r>B|GeXl1{$L zFIa}q+n}PnRs!vGg6Y`oN+;t}8b`EOa7GC|S+LPhh%32Ec_4=suuIDTI}Rsfr#HXP z$IqR1-vxF&xAmNq&*DzQPo$)nIUl4<@Cd|%9z8iOd87%NOd9}UUaSb@1R2A_^2=4y z++Ve$X13=Kcd=v=KT;Xmt#|dyNPLW5z~$V%1Bke5XqlnFE`BH!R3ywEMWq^i#Z;YE zgeoyaP~VV;s3=m%H{Sti9riy($b|#mMtNV%d#Q^{Hf!E44qHka)g!O8e_BE;w*X$@ zNTfSmApuRboDvFLh`Dsv#J?=Uak+2=RK}sKMe)BBM)+G`cXAyu{@!~~h# zj8l?o7i|bXb-~z{){K0YM$#-;98|3RhZ;-PoHFSrtkOtqBvKCsqIu7bUuWhtY~*8P z?yf{`pS$V15^{6}p2ZcuCb@};5m^4DXNqKtH4Ev3L@Q2XcPMFJIVFc}rj8@18#1ZA zq-ws|=RRV$!?iymMB1O#@A`M+&kH}w0ph}sqNO+mEuwXrf{Ubr3;4SH%R2s<|G8uv z@a??kUflUF8>LGzW1^pmIGc=MSOvt9cu&x1%egh3OxBJQv^P}Rzohc^uj{u(6Er?< z$vaXP!dv=2k> zRNHC!3B(QZ^W-PqZ;bU%CE;O>a|7-EE7sp52H4hJFVT%F}pb~1gSugF)#$C35KT4dGJFSzHlJU$kUi(L9g ziu&2D=(&dx*pY{EJ3Ch){epCGDAI}Q-JuXH8l(uYM}3^d zskfvQDJa$cyYAZgEm{pqv}0*QNf1gRTcq_ZSzNy`WOW9JuZb{vgbabmKWu~w3XuN+ z!@|KY!pLIu)1sL0z_|x}dI`O#K0)KT_-+l@{)EPcafI zx$q-4KmL~-NH0)U4_n9df@OmN`&Ndefp)%9|NWRuV&A?NQ%WdgUv16EHj?0+mg`lB z%Kc8x|C=Yy;~s2)WczHwD9PjYCk-+?)|jLbWVySWjwYu_gIN?Hc7cW^`6}7iLs`uf z1WQ4gHkE6$9B>MrO$$EqrHeB1B*%1iT7Hr4>=EGs4;jjh(zD= zij{0sC#D4boMoWG*b1=x=pu|Aql3$DSDj|D11)Tf719)UF`Rw`E6j^RgfPeeO9h%4 zyLnBaGAo{o5|tY`t>HkT{b7Mv|DmL>u`COV05X8A-}w)-s6qVz99E>`p_N3P|5yW9S_03$77z5bNABc=

4-Br(tI&644Analx#Se>nxXTWo6F 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 literal 0 HcmV?d00001 diff --git a/tools/doc/type-parser.js b/tools/doc/type-parser.js index 0b9458351e6618..ef4499e50ff35a 100644 --- a/tools/doc/type-parser.js +++ b/tools/doc/type-parser.js @@ -18,7 +18,7 @@ const jsGlobalTypes = [ 'Array', 'ArrayBuffer', 'ArrayBufferView', 'DataView', 'Date', 'Error', 'EvalError', 'Function', 'Map', 'Object', 'Promise', 'RangeError', 'ReferenceError', 'RegExp', 'Set', 'SharedArrayBuffer', 'SyntaxError', - 'TypeError', 'TypedArray', 'URIError', 'Uint8Array', + 'TypeError', 'TypedArray', 'URIError', 'Uint8Array', 'WebAssembly.Instance', ]; const customTypesMap = { diff --git a/tools/license-builder.sh b/tools/license-builder.sh index 60f38e5cebf694..51c5b4d0c6a66d 100755 --- a/tools/license-builder.sh +++ b/tools/license-builder.sh @@ -108,4 +108,6 @@ addlicense "node-heapdump" "src/heap_utils.cc" \ addlicense "rimraf" "lib/internal/fs/rimraf.js" \ "$(curl -sL https://raw.githubusercontent.com/isaacs/rimraf/0e365ac4e4d64a25aa2a3cc026348f13410210e1/LICENSE)" +addlicense "uvwasi" "deps/uvwasi" "$(cat ${rootdir}/deps/uvwasi/LICENSE)" + mv $tmplicense $licensefile From 01ab031ccac53aee9213e1008f94ef8b8b5019a6 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Thu, 28 Nov 2019 23:13:53 -0800 Subject: [PATCH 160/180] test: remove unused callback argument PR-URL: https://github.com/nodejs/node/pull/30712 Reviewed-By: David Carlier Reviewed-By: Luigi Pinca --- test/sequential/test-http-max-http-headers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/sequential/test-http-max-http-headers.js b/test/sequential/test-http-max-http-headers.js index 9ee4d8c352928b..f54d23854801cc 100644 --- a/test/sequential/test-http-max-http-headers.js +++ b/test/sequential/test-http-max-http-headers.js @@ -89,7 +89,7 @@ function test1() { headers = fillHeaders(headers, currentSize); const server = net.createServer((sock) => { - sock.once('data', (chunk) => { + sock.once('data', () => { writeHeaders(sock, headers); sock.resume(); }); From d69b9b753a908d740c66ff50e890dccaa0e78c46 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Thu, 28 Nov 2019 23:15:25 -0800 Subject: [PATCH 161/180] test: simplify forEach() usage Use an array literal instead of using split() on a very short string. PR-URL: https://github.com/nodejs/node/pull/30712 Reviewed-By: David Carlier Reviewed-By: Luigi Pinca --- test/sequential/test-http-max-http-headers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/sequential/test-http-max-http-headers.js b/test/sequential/test-http-max-http-headers.js index f54d23854801cc..39f5033f0605f6 100644 --- a/test/sequential/test-http-max-http-headers.js +++ b/test/sequential/test-http-max-http-headers.js @@ -24,7 +24,7 @@ function once(cb) { } function finished(client, callback) { - 'abort error end'.split(' ').forEach((e) => { + ['abort', 'error', 'end'].forEach((e) => { client.on(e, once(() => setImmediate(callback))); }); } From 1222be81e3face59fdb7919af9ca7dddbaa15cb8 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Thu, 28 Nov 2019 23:18:41 -0800 Subject: [PATCH 162/180] test: remove unused callback argument PR-URL: https://github.com/nodejs/node/pull/30712 Reviewed-By: David Carlier Reviewed-By: Luigi Pinca --- test/sequential/test-http-max-http-headers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/sequential/test-http-max-http-headers.js b/test/sequential/test-http-max-http-headers.js index 39f5033f0605f6..d421ad5f2bfbaf 100644 --- a/test/sequential/test-http-max-http-headers.js +++ b/test/sequential/test-http-max-http-headers.js @@ -134,7 +134,7 @@ const test2 = common.mustCall(() => { client.resume(); }); - finished(client, common.mustCall((err) => { + finished(client, common.mustCall(() => { server.close(test3); })); })); From 1918b4e84fcb755cf1d22b79c88b11484a88ce90 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Thu, 28 Nov 2019 23:28:53 -0800 Subject: [PATCH 163/180] test: correct header length subtraction In test-http-max-http-headers, a comment asks why we are subtracting 32 from the length of the invalid-length-by-1 headers instead of just 1. Subtracting 1 seems to be correct and works. PR-URL: https://github.com/nodejs/node/pull/30712 Reviewed-By: David Carlier Reviewed-By: Luigi Pinca --- test/sequential/test-http-max-http-headers.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/sequential/test-http-max-http-headers.js b/test/sequential/test-http-max-http-headers.js index d421ad5f2bfbaf..04fdebd48cfd61 100644 --- a/test/sequential/test-http-max-http-headers.js +++ b/test/sequential/test-http-max-http-headers.js @@ -37,8 +37,7 @@ function fillHeaders(headers, currentSize, valid = false) { // Generate valid headers if (valid) { - // TODO(mcollina): understand why -32 is needed instead of -1 - headers = headers.slice(0, -32); + headers = headers.slice(0, -1); } return headers + '\r\n\r\n'; } From 1e199ceb71397149b2ed07354dfde66f26f40433 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Thu, 28 Nov 2019 23:42:12 -0800 Subject: [PATCH 164/180] test: move test-http-max-http-headers to parallel test-http-max-http-headers seems to run fine in parallel, even with `tools/test.py -j 96 --repeat 192 test/parallel/test-http-max-http-headers.js`. The same applies to `test-set-http-max-http-headers.js` which (as written) depends on `test-http-max-http-headers.js` being in the same directory. So that is being moved too. PR-URL: https://github.com/nodejs/node/pull/30712 Reviewed-By: David Carlier Reviewed-By: Luigi Pinca --- test/{sequential => parallel}/test-http-max-http-headers.js | 0 .../test-set-http-max-http-headers.js | 6 ++---- 2 files changed, 2 insertions(+), 4 deletions(-) rename test/{sequential => parallel}/test-http-max-http-headers.js (100%) rename test/{sequential => parallel}/test-set-http-max-http-headers.js (91%) diff --git a/test/sequential/test-http-max-http-headers.js b/test/parallel/test-http-max-http-headers.js similarity index 100% rename from test/sequential/test-http-max-http-headers.js rename to test/parallel/test-http-max-http-headers.js diff --git a/test/sequential/test-set-http-max-http-headers.js b/test/parallel/test-set-http-max-http-headers.js similarity index 91% rename from test/sequential/test-set-http-max-http-headers.js rename to test/parallel/test-set-http-max-http-headers.js index cfe1ed69537743..c4df779d2bd4fa 100644 --- a/test/sequential/test-set-http-max-http-headers.js +++ b/test/parallel/test-set-http-max-http-headers.js @@ -37,8 +37,7 @@ test(function(cb) { NODE_DEBUG: 'http' }); - // Validate that the test fails if the max header size is too small. - // Validate that the test now passes if the same limit becomes large enough. + // Validate that the test now passes if the same limit is large enough. const args = ['--expose-internals', '--max-http-header-size=1024', testName, @@ -76,8 +75,7 @@ if (!process.config.variables.node_without_node_options) { }); test(function(cb) { - // Validate that the test now passes if the same limit - // becomes large enough. + // Validate that the test now passes if the same limit is large enough. const args = ['--expose-internals', testName, '1024']; const cp = spawn(process.execPath, args, { env, stdio: 'inherit' }); From e65ad865c6dd19c5ef2263b11109430f8f853f7d Mon Sep 17 00:00:00 2001 From: Reza Fatahi Date: Sat, 30 Nov 2019 17:22:44 -0800 Subject: [PATCH 165/180] src: change header file in node_stat_watcher.cc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit change src/node_stat_watcher.cc to import `env-inl.h` instead of `env.h`. PR-URL: https://github.com/nodejs/node/pull/29976 Reviewed-By: Ben Noordhuis Reviewed-By: Michaël Zasso Reviewed-By: Colin Ihrig Reviewed-By: Gireesh Punathil --- src/node_stat_watcher.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node_stat_watcher.cc b/src/node_stat_watcher.cc index a3f4ef8f505d39..0d67eceed54931 100644 --- a/src/node_stat_watcher.cc +++ b/src/node_stat_watcher.cc @@ -22,7 +22,7 @@ #include "memory_tracker-inl.h" #include "node_stat_watcher.h" #include "async_wrap-inl.h" -#include "env.h" +#include "env-inl.h" #include "node_file-inl.h" #include "util-inl.h" From 7aa1df707679278474c7016bb1620253ee40a6f5 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Fri, 29 Nov 2019 12:02:04 -0800 Subject: [PATCH 166/180] tls: introduce ERR_TLS_INVALID_CONTEXT It is trivially possible to cause an internal assertion error with tls.createSecurePair(). Throw a friendly error instead. Reserve internal assertions for things that we believe to be impossible. PR-URL: https://github.com/nodejs/node/pull/30718 Reviewed-By: Sam Roberts Reviewed-By: Luigi Pinca Reviewed-By: Colin Ihrig Reviewed-By: Anna Henningsen Reviewed-By: James M Snell --- doc/api/errors.md | 8 ++++++++ lib/_tls_wrap.js | 6 ++++-- lib/internal/errors.js | 1 + test/parallel/test-tls-basic-validations.js | 8 ++++++-- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/doc/api/errors.md b/doc/api/errors.md index bdfd0df4a7bba7..9cba4de5f2e628 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -1809,6 +1809,14 @@ recommended to use 2048 bits or larger for stronger security. A TLS/SSL handshake timed out. In this case, the server must also abort the connection. + +### ERR_TLS_INVALID_CONTEXT + + +The context must be a `SecureContext`. + ### ERR_TLS_INVALID_PROTOCOL_METHOD diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js index 6f08f91c43dd5d..530a41a1e855fd 100644 --- a/lib/_tls_wrap.js +++ b/lib/_tls_wrap.js @@ -56,6 +56,7 @@ const { ERR_SOCKET_CLOSED, ERR_TLS_DH_PARAM_SIZE, ERR_TLS_HANDSHAKE_TIMEOUT, + ERR_TLS_INVALID_CONTEXT, ERR_TLS_RENEGOTIATION_DISABLED, ERR_TLS_REQUIRED_SERVER_NAME, ERR_TLS_SESSION_ATTACK, @@ -517,8 +518,9 @@ TLSSocket.prototype._wrapHandle = function(wrap) { options.credentials || tls.createSecureContext(options); assert(handle.isStreamBase, 'handle must be a StreamBase'); - assert(context.context instanceof NativeSecureContext, - 'context.context must be a NativeSecureContext'); + if (!(context.context instanceof NativeSecureContext)) { + throw new ERR_TLS_INVALID_CONTEXT('context'); + } const res = tls_wrap.wrap(handle, context.context, !!options.isServer); res._parent = handle; // C++ "wrap" object: TCPWrap, JSStream, ... res._parentWrap = wrap; // JS object: net.Socket, JSStreamSocket, ... diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 88a38f5e1de62d..f85253ec444256 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -1169,6 +1169,7 @@ E('ERR_TLS_CERT_ALTNAME_INVALID', function(reason, host, cert) { }, Error); E('ERR_TLS_DH_PARAM_SIZE', 'DH parameter size %s is less than 2048', Error); E('ERR_TLS_HANDSHAKE_TIMEOUT', 'TLS handshake timeout', Error); +E('ERR_TLS_INVALID_CONTEXT', '%s must be a SecureContext', TypeError), E('ERR_TLS_INVALID_PROTOCOL_VERSION', '%j is not a valid %s TLS protocol version', TypeError); E('ERR_TLS_PROTOCOL_VERSION_CONFLICT', diff --git a/test/parallel/test-tls-basic-validations.js b/test/parallel/test-tls-basic-validations.js index 925c6643a1a0cc..c4e2833464cb44 100644 --- a/test/parallel/test-tls-basic-validations.js +++ b/test/parallel/test-tls-basic-validations.js @@ -78,9 +78,13 @@ common.expectsError( assert.throws(() => tls.createServer({ ticketKeys: Buffer.alloc(0) }), /TypeError: Ticket keys length must be 48 bytes/); -common.expectsInternalAssertion( +assert.throws( () => tls.createSecurePair({}), - 'context.context must be a NativeSecureContext' + { + message: 'context must be a SecureContext', + code: 'ERR_TLS_INVALID_CONTEXT', + name: 'TypeError', + } ); { From f46df0b4968b061ba112ee482efc810b31653392 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Fri, 29 Nov 2019 16:52:11 -0800 Subject: [PATCH 167/180] doc: update socket.bufferSize text Edit text for clarity and readability. PR-URL: https://github.com/nodejs/node/pull/30723 Reviewed-By: Anna Henningsen Reviewed-By: Gireesh Punathil Reviewed-By: James M Snell --- doc/api/net.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/doc/api/net.md b/doc/api/net.md index 9bc9812d7ca6e9..7ec75e358f396a 100644 --- a/doc/api/net.md +++ b/doc/api/net.md @@ -548,19 +548,17 @@ added: v0.3.8 * {integer} +This property shows the number of characters buffered for writing. The buffer +may contain strings whose length after encoding is not yet known. So this number +is only an approximation of the number of bytes in the buffer. + `net.Socket` has the property that `socket.write()` always works. This is to help users get up and running quickly. The computer cannot always keep up with the amount of data that is written to a socket. The network connection simply might be too slow. Node.js will internally queue up the data written to a -socket and send it out over the wire when it is possible. (Internally it is -polling on the socket's file descriptor for being writable). - -The consequence of this internal buffering is that memory may grow. This -property shows the number of characters currently buffered to be written. -(Number of characters is approximately equal to the number of bytes to be -written, but the buffer may contain strings, and the strings are lazily -encoded, so the exact number of bytes is not known.) +socket and send it out over the wire when it is possible. +The consequence of this internal buffering is that memory may grow. Users who experience large or growing `bufferSize` should attempt to "throttle" the data flows in their program with [`socket.pause()`][] and [`socket.resume()`][]. From a8002d92ab2a352e3b77a06a846a0663b8266f7c Mon Sep 17 00:00:00 2001 From: Michael Dawson Date: Thu, 28 Nov 2019 15:55:25 -0500 Subject: [PATCH 168/180] doc: update README.md to fix active/maint times Update REAMDE.md to be consistent with the active/maintenance times (12/18) outlined in https://github.com/nodejs/Release#release-plan. This was missed when the times were swapped in the Node.js 12 timeframe. PR-URL: https://github.com/nodejs/node/pull/30707 Reviewed-By: Myles Borins Reviewed-By: Trivikram Kamat Reviewed-By: Colin Ihrig Reviewed-By: Gireesh Punathil --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e200d25eb133d6..7b3f7b69742dfc 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ Looking for help? Check out the each October. * **LTS**: Releases that receive Long-term Support, with a focus on stability and security. Every even-numbered major version will become an LTS release. - LTS releases receive 18 months of _Active LTS_ support and a further 12 months + LTS releases receive 12 months of _Active LTS_ support and a further 18 months of _Maintenance_. LTS release lines have alphabetically-ordered codenames, beginning with v4 Argon. There are no breaking changes or feature additions, except in some special circumstances. From 61af1fcaa1422a64a7a00a8f43405a4cdf9d0f52 Mon Sep 17 00:00:00 2001 From: Myles Borins Date: Tue, 26 Nov 2019 15:35:08 -0500 Subject: [PATCH 169/180] doc: update signature algorithm in release doc Updated doc to reflect what is now done in tools/release.sh PR-URL: https://github.com/nodejs/node/pull/30673 Reviewed-By: Rod Vagg --- doc/releases.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/releases.md b/doc/releases.md index c8ff4214c6b3d5..0fc7fb6220fa70 100644 --- a/doc/releases.md +++ b/doc/releases.md @@ -577,14 +577,14 @@ however. computer. **e.** Sign the `SHASUMS256.txt` file using a command similar to: `gpg ---default-key YOURKEY --clearsign /path/to/SHASUMS256.txt`. You will be prompted -by GPG for your password. The signed file will be named `SHASUMS256.txt.asc`. +--default-key YOURKEY --digest-algo SHA256 --clearsign /path/to/SHASUMS256.txt`. +You will be prompted by GPG for your password. The signed file will be named +SHASUMS256.txt.asc. **f.** Output an ASCII armored version of your public GPG key using a command -similar to: `gpg --default-key YOURKEY --armor --export --output -/path/to/SHASUMS256.txt.gpg`. This does not require your password and is mainly -a convenience for users, although not the recommended way to get a copy of your -key. +similar to: `gpg --default-key YOURKEY --digest-algo SHA256 --detach-sign /path/to/SHASUMS256.txt`. +You will be prompted by GPG for your password. The signed file will be named +SHASUMS256.txt.sig. **g.** Upload the `SHASUMS256.txt` files back to the server into the release directory. @@ -594,8 +594,8 @@ release, you should re-run `tools/release.sh` after the ARM builds have finished. That will move the ARM artifacts into the correct location. You will be prompted to re-sign `SHASUMS256.txt`. -It is possible to only sign a release by running `./tools/release.sh -s -vX.Y.Z`. +**It is possible to only sign a release by running `./tools/release.sh -s +vX.Y.Z`.** ### 14. Check the Release From 19b31c1bc59d54fe50c1681550d71d3323fa5bb6 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Fri, 29 Nov 2019 21:46:26 -0800 Subject: [PATCH 170/180] doc: revise REPL uncaught exception text MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simplify the text about uncaught exceptions in the REPL. PR-URL: https://github.com/nodejs/node/pull/30729 Reviewed-By: Gireesh Punathil Reviewed-By: Anto Aravinth Reviewed-By: Michaël Zasso Reviewed-By: Beth Griggs Reviewed-By: Trivikram Kamat --- doc/api/repl.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/doc/api/repl.md b/doc/api/repl.md index cd7edb59ddf0a1..967336710ce3a1 100644 --- a/doc/api/repl.md +++ b/doc/api/repl.md @@ -151,10 +151,9 @@ REPL session. This use of the [`domain`][] module in the REPL has these side effects: -* Uncaught exceptions only emit the [`'uncaughtException'`][] event if the - `repl` is used as standalone program. If the `repl` is included anywhere in - another application, adding a listener for this event will throw an - [`ERR_INVALID_REPL_INPUT`][] exception. +* Uncaught exceptions only emit the [`'uncaughtException'`][] event in the + standalone REPL. Adding a listener for this event in a REPL within + another Node.js program throws [`ERR_INVALID_REPL_INPUT`][]. * Trying to use [`process.setUncaughtExceptionCaptureCallback()`][] throws an [`ERR_DOMAIN_CANNOT_SET_UNCAUGHT_EXCEPTION_CAPTURE`][] error. From b5c7dad95a60d3be5a697d4dc5b4037090ea5211 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Sun, 1 Dec 2019 21:10:54 -0800 Subject: [PATCH 171/180] test: fix test-benchmark-streams test-benchmark-streams is currently failing because the `sync` option is not specified in the test, resulting in too many benchmarks running. PR-URL: https://github.com/nodejs/node/pull/30757 Reviewed-By: Beth Griggs Reviewed-By: Ruben Bridgewater --- test/benchmark/test-benchmark-streams.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/benchmark/test-benchmark-streams.js b/test/benchmark/test-benchmark-streams.js index f90838794cdf2a..51cdace5748fa4 100644 --- a/test/benchmark/test-benchmark-streams.js +++ b/test/benchmark/test-benchmark-streams.js @@ -7,7 +7,8 @@ const runBenchmark = require('../common/benchmark'); runBenchmark('streams', [ 'kind=duplex', + 'n=1', + 'sync=no', 'type=buffer', - 'n=1' ], { NODEJS_BENCHMARK_ZERO_ALLOWED: 1 }); From 37c70ee198388ffc5ba5a56c3c51a72f5053637a Mon Sep 17 00:00:00 2001 From: garygsc Date: Sat, 26 Oct 2019 13:17:23 -0600 Subject: [PATCH 172/180] test: use arrow functions in async-hooks tests Convert all anonymous callback functions in `test/async-hooks/*.js` to use arrow functions. `writing-tests.md` states to use arrow functions when appropriate. PR-URL: https://github.com/nodejs/node/pull/30137 Reviewed-By: Anna Henningsen Reviewed-By: Gireesh Punathil Reviewed-By: Trivikram Kamat --- test/async-hooks/test-disable-in-init.js | 2 +- test/async-hooks/test-graph.http.js | 8 ++++---- test/async-hooks/test-graph.pipeconnect.js | 4 ++-- test/async-hooks/test-nexttick-default-trigger.js | 4 ++-- test/async-hooks/test-pipeconnectwrap.js | 4 ++-- test/async-hooks/test-queue-microtask.js | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/test/async-hooks/test-disable-in-init.js b/test/async-hooks/test-disable-in-init.js index b7ab31e6d97dc2..17537c62f30885 100644 --- a/test/async-hooks/test-disable-in-init.js +++ b/test/async-hooks/test-disable-in-init.js @@ -7,7 +7,7 @@ const fs = require('fs'); let nestedCall = false; async_hooks.createHook({ - init: common.mustCall(function() { + init: common.mustCall(() => { nestedHook.disable(); if (!nestedCall) { nestedCall = true; diff --git a/test/async-hooks/test-graph.http.js b/test/async-hooks/test-graph.http.js index 12862467a6947e..db0c1265670a6f 100644 --- a/test/async-hooks/test-graph.http.js +++ b/test/async-hooks/test-graph.http.js @@ -11,11 +11,11 @@ const http = require('http'); const hooks = initHooks(); hooks.enable(); -const server = http.createServer(common.mustCall(function(req, res) { +const server = http.createServer(common.mustCall((req, res) => { res.end(); - this.close(common.mustCall()); + server.close(common.mustCall()); })); -server.listen(0, common.mustCall(function() { +server.listen(0, common.mustCall(() => { http.get({ host: '::1', family: 6, @@ -23,7 +23,7 @@ server.listen(0, common.mustCall(function() { }, common.mustCall()); })); -process.on('exit', function() { +process.on('exit', () => { hooks.disable(); verifyGraph( diff --git a/test/async-hooks/test-graph.pipeconnect.js b/test/async-hooks/test-graph.pipeconnect.js index 03d2902c835d48..440ea906a17c4d 100644 --- a/test/async-hooks/test-graph.pipeconnect.js +++ b/test/async-hooks/test-graph.pipeconnect.js @@ -12,9 +12,9 @@ tmpdir.refresh(); const hooks = initHooks(); hooks.enable(); -net.createServer(function(c) { +const server = net.createServer((c) => { c.end(); - this.close(); + server.close(); }).listen(common.PIPE, common.mustCall(onlisten)); function onlisten() { diff --git a/test/async-hooks/test-nexttick-default-trigger.js b/test/async-hooks/test-nexttick-default-trigger.js index 1bc93f29737389..7d6d98c9abc902 100644 --- a/test/async-hooks/test-nexttick-default-trigger.js +++ b/test/async-hooks/test-nexttick-default-trigger.js @@ -14,11 +14,11 @@ hooks.enable(); const rootAsyncId = async_hooks.executionAsyncId(); -process.nextTick(common.mustCall(function() { +process.nextTick(common.mustCall(() => { assert.strictEqual(async_hooks.triggerAsyncId(), rootAsyncId); })); -process.on('exit', function() { +process.on('exit', () => { hooks.sanityCheck(); const as = hooks.activitiesOfTypes('TickObject'); diff --git a/test/async-hooks/test-pipeconnectwrap.js b/test/async-hooks/test-pipeconnectwrap.js index f086807e393145..b57cc63cf59e46 100644 --- a/test/async-hooks/test-pipeconnectwrap.js +++ b/test/async-hooks/test-pipeconnectwrap.js @@ -17,9 +17,9 @@ let pipe1, pipe2; let pipeserver; let pipeconnect; -net.createServer(common.mustCall(function(c) { +const server = net.createServer(common.mustCall((c) => { c.end(); - this.close(); + server.close(); process.nextTick(maybeOnconnect.bind(null, 'server')); })).listen(common.PIPE, common.mustCall(onlisten)); diff --git a/test/async-hooks/test-queue-microtask.js b/test/async-hooks/test-queue-microtask.js index dfa537752e37fc..1e2029efe4ad0e 100644 --- a/test/async-hooks/test-queue-microtask.js +++ b/test/async-hooks/test-queue-microtask.js @@ -11,11 +11,11 @@ hooks.enable(); const rootAsyncId = async_hooks.executionAsyncId(); -queueMicrotask(common.mustCall(function() { +queueMicrotask(common.mustCall(() => { assert.strictEqual(async_hooks.triggerAsyncId(), rootAsyncId); })); -process.on('exit', function() { +process.on('exit', () => { hooks.sanityCheck(); const as = hooks.activitiesOfTypes('Microtask'); From 3d302ff2765d413a8fbe803a32c0b67d1220dc5a Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Sat, 30 Nov 2019 06:52:35 -0800 Subject: [PATCH 173/180] doc: fix typographical error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Insert missing word. PR-URL: https://github.com/nodejs/node/pull/30735 Reviewed-By: Anna Henningsen Reviewed-By: Gireesh Punathil Reviewed-By: Trivikram Kamat Reviewed-By: Colin Ihrig Reviewed-By: Luigi Pinca Reviewed-By: Tobias Nießen Reviewed-By: Ruben Bridgewater Reviewed-By: James M Snell --- doc/api/perf_hooks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/perf_hooks.md b/doc/api/perf_hooks.md index 3fcdd99509ca0f..1ccf3ee37578e3 100644 --- a/doc/api/perf_hooks.md +++ b/doc/api/perf_hooks.md @@ -547,7 +547,7 @@ The standard deviation of the recorded event loop delays. ### Measuring the duration of async operations The following example uses the [Async Hooks][] and Performance APIs to measure -the actual duration of a Timeout operation (including the amount of time it +the actual duration of a Timeout operation (including the amount of time it took to execute the callback). ```js From 6a53152b425f57d9b0a4582e76e881e9464f246d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Reis?= Date: Fri, 29 Nov 2019 01:45:52 +0000 Subject: [PATCH 174/180] build,win: add test-ci-native and test-ci-js PR-URL: https://github.com/nodejs/node/pull/30724 Refs: https://github.com/nodejs/build/issues/1996 Reviewed-By: Rod Vagg Reviewed-By: Sam Roberts Reviewed-By: Rich Trott --- Makefile | 1 + vcbuild.bat | 13 +++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 21ab5de0b9daf0..6b678889ca8986 100644 --- a/Makefile +++ b/Makefile @@ -489,6 +489,7 @@ test-all-valgrind: test-build test-all-suites: | clear-stalled test-build bench-addons-build doc-only ## Run all test suites. $(PYTHON) tools/test.py $(PARALLEL_ARGS) --mode=$(BUILDTYPE_LOWER) test/* +# CI_* variables should be kept synchronized with the ones in vcbuild.bat CI_NATIVE_SUITES ?= addons js-native-api node-api CI_JS_SUITES ?= default ifeq ($(node_use_openssl), false) diff --git a/vcbuild.bat b/vcbuild.bat index 10c9ef34bb6a40..677978cb3ed7f9 100644 --- a/vcbuild.bat +++ b/vcbuild.bat @@ -15,6 +15,13 @@ if /i "%1"=="/?" goto help cd %~dp0 +@rem CI_* variables should be kept synchronized with the ones in Makefile +set CI_NATIVE_SUITES=addons js-native-api node-api +set CI_JS_SUITES=default +set CI_DOC=doctool +@rem Same as the test-ci target in Makefile +set "common_test_suites=%CI_JS_SUITES% %CI_NATIVE_SUITES% %CI_DOC%&set build_addons=1&set build_js_native_api_tests=1&set build_node_api_tests=1" + @rem Process arguments. set config=Release set target=Build @@ -51,10 +58,8 @@ set build_js_native_api_tests= set build_node_api_tests= set test_node_inspect= set test_check_deopts= -set js_test_suites=default set v8_test_options= set v8_build_options= -set "common_test_suites=%js_test_suites% doctool addons js-native-api node-api&set build_addons=1&set build_js_native_api_tests=1&set build_node_api_tests=1" set http2_debug= set nghttp2_debug= set link_module= @@ -86,8 +91,8 @@ if /i "%1"=="noetw" set noetw=1&goto arg-ok if /i "%1"=="ltcg" set ltcg=1&goto arg-ok if /i "%1"=="licensertf" set licensertf=1&goto arg-ok if /i "%1"=="test" set test_args=%test_args% -J %common_test_suites%&set lint_cpp=1&set lint_js=1&set lint_md=1&goto arg-ok -:: test-ci is deprecated -if /i "%1"=="test-ci" goto arg-ok +if /i "%1"=="test-ci-native" set test_args=%test_args% %test_ci_args% -J -p tap --logfile test.tap %CI_NATIVE_SUITES% %CI_DOC%&set build_addons=1&set build_js_native_api_tests=1&set build_node_api_tests=1&set cctest_args=%cctest_args% --gtest_output=xml:cctest.junit.xml&goto arg-ok +if /i "%1"=="test-ci-js" set test_args=%test_args% %test_ci_args% -J -p tap --logfile test.tap %CI_JS_SUITES%&set no_cctest=1&goto arg-ok if /i "%1"=="build-addons" set build_addons=1&goto arg-ok if /i "%1"=="build-js-native-api-tests" set build_js_native_api_tests=1&goto arg-ok if /i "%1"=="build-node-api-tests" set build_node_api_tests=1&goto arg-ok From 0aae502c67071a053e1ae0ede367c605959ee00b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Reis?= Date: Sat, 30 Nov 2019 01:13:48 +0000 Subject: [PATCH 175/180] build,win: propagate error codes in vcbuild Don't exit vcbuild with error code 0 when cctest fails. PR-URL: https://github.com/nodejs/node/pull/30724 Refs: https://github.com/nodejs/build/issues/1996 Reviewed-By: Rod Vagg Reviewed-By: Sam Roberts Reviewed-By: Rich Trott --- vcbuild.bat | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/vcbuild.bat b/vcbuild.bat index 677978cb3ed7f9..1e142a658bc68c 100644 --- a/vcbuild.bat +++ b/vcbuild.bat @@ -68,6 +68,7 @@ set cctest= set openssl_no_asm= set doc= set extra_msbuild_args= +set exit_code=0 :next-arg if "%1"=="" goto args-done @@ -633,9 +634,11 @@ if defined no_cctest echo Skipping cctest because no-cctest was specified && got if not exist "%config%\cctest.exe" echo cctest.exe not found. Run "vcbuild test" or "vcbuild cctest" to build it. && goto run-test-py echo running 'cctest %cctest_args%' "%config%\cctest" %cctest_args% +if %errorlevel% neq 0 set exit_code=%errorlevel% :run-test-py echo running 'python tools\test.py %test_args%' python tools\test.py %test_args% +if %errorlevel% neq 0 set exit_code=%errorlevel% goto test-v8 :test-v8 @@ -715,7 +718,7 @@ echo vcbuild.bat no-cctest : skip building cctest.exe goto exit :exit -goto :EOF +exit /b %exit_code% rem *************** From 81d81a59040846efb045091d4716b0ebc6c76d83 Mon Sep 17 00:00:00 2001 From: Xu Meng Date: Fri, 29 Nov 2019 02:28:49 -0600 Subject: [PATCH 176/180] test: add an indicator `isIBMi` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have to skip some test cases on IBM i. On IBM i, process.platform and os.platform() both return aix, It is not enough to differentiate between IBM i and real AIX system. Also updated parallel/test-cluster-bind-privileged-port.js for test. PR-URL: https://github.com/nodejs/node/pull/30714 Reviewed-By: Sam Roberts Reviewed-By: Michaël Zasso Reviewed-By: James M Snell Reviewed-By: Richard Lau Reviewed-By: Colin Ihrig --- test/common/README.md | 5 +++++ test/common/index.js | 4 ++++ test/common/index.mjs | 2 ++ test/parallel/test-cluster-bind-privileged-port.js | 3 +++ 4 files changed, 14 insertions(+) diff --git a/test/common/README.md b/test/common/README.md index db32c48b4065e0..5f8b6cb3090d9d 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -237,6 +237,11 @@ Attempts to 'kill' `pid` Platform check for Free BSD. +### isIBMi +* [<boolean>][] + +Platform check for IBMi. + ### isLinux * [<boolean>][] diff --git a/test/common/index.js b/test/common/index.js index 888a1feba37462..09b7f5577844cb 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -102,6 +102,9 @@ if (process.argv.length === 2 && const isWindows = process.platform === 'win32'; const isAIX = process.platform === 'aix'; +// On IBMi, process.platform and os.platform() both return 'aix', +// It is not enough to differentiate between IBMi and real AIX system. +const isIBMi = os.type() === 'OS400'; const isLinuxPPCBE = (process.platform === 'linux') && (process.arch === 'ppc64') && (os.endianness() === 'BE'); @@ -762,6 +765,7 @@ module.exports = { isAIX, isAlive, isFreeBSD, + isIBMi, isLinux, isLinuxPPCBE, isMainThread, diff --git a/test/common/index.mjs b/test/common/index.mjs index 5ad6ec3c11eadc..54f6dc7f173cdf 100644 --- a/test/common/index.mjs +++ b/test/common/index.mjs @@ -9,6 +9,7 @@ const { isMainThread, isWindows, isAIX, + isIBMi, isLinuxPPCBE, isSunOS, isFreeBSD, @@ -55,6 +56,7 @@ export { isMainThread, isWindows, isAIX, + isIBMi, isLinuxPPCBE, isSunOS, isFreeBSD, diff --git a/test/parallel/test-cluster-bind-privileged-port.js b/test/parallel/test-cluster-bind-privileged-port.js index e95768a8fcc2b4..57aa4c735d480f 100644 --- a/test/parallel/test-cluster-bind-privileged-port.js +++ b/test/parallel/test-cluster-bind-privileged-port.js @@ -26,6 +26,9 @@ const common = require('../common'); if (common.isOSX) common.skip('macOS may allow ordinary processes to use any port'); +if (common.isIBMi) + common.skip('IBMi may allow ordinary processes to use any port'); + if (common.isWindows) common.skip('not reliable on Windows.'); From 49e047f7a1eec52ec8480858ffc98eb2b9e5039d Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Sat, 30 Nov 2019 11:28:38 -0800 Subject: [PATCH 177/180] test: add coverage for ERR_TLS_INVALID_PROTOCOL_VERSION There is currently no test that confirms that an invalid TLS protocol results in ERR_TLS_INVALID_PROTOCOL_VERSION. Add tests to check this for the `minVersion` and `maxVersion` options in `createSecureContext()`. Refs: https://codecov.io/gh/nodejs/node/src/c14c476614e3134867ddb997bdfe5a41ba668175/lib/_tls_common.js#L56 Refs: https://coverage.nodejs.org/coverage-c14c476614e31348/lib/_tls_common.js.html#L56 PR-URL: https://github.com/nodejs/node/pull/30741 Reviewed-By: Luigi Pinca Reviewed-By: Sam Roberts Reviewed-By: Trivikram Kamat Reviewed-By: Ruben Bridgewater Reviewed-By: James M Snell --- test/parallel/test-tls-basic-validations.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/parallel/test-tls-basic-validations.js b/test/parallel/test-tls-basic-validations.js index c4e2833464cb44..763b60fac536d8 100644 --- a/test/parallel/test-tls-basic-validations.js +++ b/test/parallel/test-tls-basic-validations.js @@ -118,3 +118,15 @@ assert.throws( } ); } + +assert.throws(() => { tls.createSecureContext({ minVersion: 'fhqwhgads' }); }, + { + code: 'ERR_TLS_INVALID_PROTOCOL_VERSION', + name: 'TypeError' + }); + +assert.throws(() => { tls.createSecureContext({ maxVersion: 'fhqwhgads' }); }, + { + code: 'ERR_TLS_INVALID_PROTOCOL_VERSION', + name: 'TypeError' + }); From 6545314a4fcb7af877c94ccf666c623c5b6171d1 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Tue, 26 Nov 2019 13:03:22 +0800 Subject: [PATCH 178/180] build: add --without-node-code-cache configure option So that it's possible to build without code cache (in particular, without building mkcodecache) for testing. PR-URL: https://github.com/nodejs/node/pull/30647 Refs: https://github.com/nodejs/node/issues/28845 Reviewed-By: Ben Noordhuis Reviewed-By: Anna Henningsen Reviewed-By: David Carlier Reviewed-By: James M Snell --- configure.py | 15 ++++++++++++--- node.gyp | 2 +- test/parallel/test-code-cache.js | 7 +++---- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/configure.py b/configure.py index 959c1db6518fdb..61708a4447d125 100755 --- a/configure.py +++ b/configure.py @@ -455,6 +455,11 @@ dest='without_node_snapshot', help='Turn off V8 snapshot integration. Currently experimental.') +parser.add_option('--without-node-code-cache', + action='store_true', + dest='without_node_code_cache', + help='Turn off V8 Code cache integration.') + intl_optgroup.add_option('--download', action='store', dest='download_list', @@ -980,6 +985,12 @@ def configure_node(o): else: o['variables']['node_use_node_snapshot'] = 'false' + if not options.without_node_code_cache: + # TODO(refack): fix this when implementing embedded code-cache when cross-compiling. + o['variables']['node_use_node_code_cache'] = b(not cross_compiling) + else: + o['variables']['node_use_node_code_cache'] = 'false' + if target_arch == 'arm': configure_arm(o) elif target_arch in ('mips', 'mipsel', 'mips64el'): @@ -1100,9 +1111,7 @@ def configure_node(o): o['variables']['debug_nghttp2'] = 'false' o['variables']['node_no_browser_globals'] = b(options.no_browser_globals) - # TODO(refack): fix this when implementing embedded code-cache when cross-compiling. - if o['variables']['want_separate_host_toolset'] == 0: - o['variables']['node_code_cache'] = 'yes' # For testing + o['variables']['node_shared'] = b(options.shared) node_module_version = getmoduleversion.get_version() diff --git a/node.gyp b/node.gyp index cffd150845e51a..8becce9062edef 100644 --- a/node.gyp +++ b/node.gyp @@ -436,7 +436,7 @@ }, }, }], - ['want_separate_host_toolset==0', { + ['node_use_node_code_cache=="true"', { 'dependencies': [ 'mkcodecache', ], diff --git a/test/parallel/test-code-cache.js b/test/parallel/test-code-cache.js index d01392f1ee6433..3c4488c557d524 100644 --- a/test/parallel/test-code-cache.js +++ b/test/parallel/test-code-cache.js @@ -33,6 +33,8 @@ const loadedModules = process.moduleLoadList // are all compiled without cache and we are doing the bookkeeping right. if (!process.features.cached_builtins) { console.log('The binary is not configured with code cache'); + assert(!process.config.variables.node_use_node_code_cache); + if (isMainThread) { assert.deepStrictEqual(compiledWithCache, new Set()); for (const key of loadedModules) { @@ -46,10 +48,7 @@ if (!process.features.cached_builtins) { assert.notDeepStrictEqual(compiledWithCache, new Set()); } } else { // Native compiled - assert.strictEqual( - process.config.variables.node_code_cache, - 'yes' - ); + assert(process.config.variables.node_use_node_code_cache); if (!isMainThread) { for (const key of [ 'internal/bootstrap/pre_execution' ]) { From 648766bccffa5e4712bddfd5b21297279ee62daf Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Tue, 26 Nov 2019 13:30:28 +0800 Subject: [PATCH 179/180] build: do not build mksnapshot and mkcodecache for --shared To build mkcodecache and mksnapshot (they are executables), we currently build libnode with unresolved symbols, then build the two exectuables with src/node_snapshot_stub.cc and src/node_code_cache_stub.cc. Each of them write a C++ file to disk when being run. We then use the generated C++ files & libnode (with unresolved symbols) to build the final Node executable. However, if libnode itself is the final product, then we should not build it with unresolved symbols. https://github.com/nodejs/node/pull/28897 added the two stubs for the libnode target when the --shared configure option is used, but it did not get rid of the actions to build and run mksnapshot and mkcodecache for --shared, so to get it working we also need a patch to make sure --shared imply --without-node-code-cache and --without-node-snapshot, until we actually fix the TODO so that mksnapshot and mkcodecache do not use the libnode that way. PR-URL: https://github.com/nodejs/node/pull/30647 Refs: https://github.com/nodejs/node/issues/28845 Reviewed-By: Ben Noordhuis Reviewed-By: Anna Henningsen Reviewed-By: David Carlier Reviewed-By: James M Snell --- configure.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/configure.py b/configure.py index 61708a4447d125..48624aba92bf19 100755 --- a/configure.py +++ b/configure.py @@ -981,13 +981,15 @@ def configure_node(o): o['variables']['want_separate_host_toolset'] = int(cross_compiling) if not options.without_node_snapshot: - o['variables']['node_use_node_snapshot'] = b(not cross_compiling) + o['variables']['node_use_node_snapshot'] = b( + not cross_compiling and not options.shared) else: o['variables']['node_use_node_snapshot'] = 'false' if not options.without_node_code_cache: # TODO(refack): fix this when implementing embedded code-cache when cross-compiling. - o['variables']['node_use_node_code_cache'] = b(not cross_compiling) + o['variables']['node_use_node_code_cache'] = b( + not cross_compiling and not options.shared) else: o['variables']['node_use_node_code_cache'] = 'false' From f5fe38400def6e0c73b27db436af5df914afe908 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Tue, 3 Dec 2019 11:41:12 +0100 Subject: [PATCH 180/180] 2019-12-03, Version 13.3.0 (Current) Notable changes: * fs: * Reworked experimental recursive `rmdir()` (cjihrig) https://github.com/nodejs/node/pull/30644 * The `maxBusyTries` option is renamed to `maxRetries`, and its default is set to 0. The `emfileWait` option has been removed, and `EMFILE` errors use the same retry logic as other errors. The `retryDelay` option is now supported. `ENFILE` errors are now retried. * http: * Make maximum header size configurable per-stream or per-server (Anna Henningsen) https://github.com/nodejs/node/pull/30570 * http2: * Make maximum tolerated rejected streams configurable (Denys Otrishko) https://github.com/nodejs/node/pull/30534 * Allow to configure maximum tolerated invalid frames (Denys Otrishko) https://github.com/nodejs/node/pull/30534 * wasi: * Introduce initial WASI support (cjihrig) https://github.com/nodejs/node/pull/30258 PR-URL: https://github.com/nodejs/node/pull/30774 --- CHANGELOG.md | 3 +- doc/api/cli.md | 2 +- doc/api/errors.md | 2 +- doc/api/fs.md | 6 +- doc/api/http.md | 4 +- doc/api/http2.md | 8 +- doc/api/n-api.md | 2 +- doc/api/wasi.md | 10 +- doc/changelogs/CHANGELOG_V13.md | 202 ++++++++++++++++++++++++++++++++ src/node_version.h | 6 +- 10 files changed, 224 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5c8f757eb7f80..39c3d092a4ba98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,7 +30,8 @@ release.

-13.2.0
+13.3.0
+13.2.0
13.1.0
13.0.1
13.0.0
diff --git a/doc/api/cli.md b/doc/api/cli.md index fa600336b704f7..ad8f86f86121b4 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -232,7 +232,7 @@ Enable experimental ES Module support in the `vm` module. ### `--experimental-wasi-unstable-preview0` Enable experimental WebAssembly System Interface (WASI) support. diff --git a/doc/api/errors.md b/doc/api/errors.md index 9cba4de5f2e628..cf21c142d6dd97 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -1812,7 +1812,7 @@ connection. ### ERR_TLS_INVALID_CONTEXT The context must be a `SecureContext`. diff --git a/doc/api/fs.md b/doc/api/fs.md index 267fa9ec2bfa14..9a91f9f22082e0 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -3220,7 +3220,7 @@ Synchronous rename(2). Returns `undefined`. > Stability: 1 - Experimental diff --git a/doc/api/wasi.md b/doc/api/wasi.md index 69afa836200170..f5425bb2bd85ce 100644 --- a/doc/api/wasi.md +++ b/doc/api/wasi.md @@ -1,6 +1,6 @@ # WebAssembly System Interface (WASI) - + > Stability: 1 - Experimental @@ -34,7 +34,7 @@ CLI arguments are needed for the previous example to run. ## Class: WASI The `WASI` class provides the WASI system call API and additional convenience @@ -45,7 +45,7 @@ sandbox directory structure configured explicitly. ### new WASI(\[options\]) * `options` {Object} @@ -61,7 +61,7 @@ added: REPLACEME ### wasi.start(instance) * `instance` {WebAssembly.Instance} @@ -76,7 +76,7 @@ is present on `instance`, then `start()` does nothing. ### wasi.wasiImport * {Object} diff --git a/doc/changelogs/CHANGELOG_V13.md b/doc/changelogs/CHANGELOG_V13.md index 9689d1589cebf4..4cc76438459d23 100644 --- a/doc/changelogs/CHANGELOG_V13.md +++ b/doc/changelogs/CHANGELOG_V13.md @@ -9,6 +9,7 @@
+13.3.0
13.2.0
13.1.0
13.0.1
@@ -32,6 +33,207 @@ * [io.js](CHANGELOG_IOJS.md) * [Archive](CHANGELOG_ARCHIVE.md) + +## 2019-12-03, Version 13.3.0 (Current), @BridgeAR + +### Notable Changes + +* **fs**: + * Reworked experimental recursive `rmdir()` (cjihrig) [#30644](https://github.com/nodejs/node/pull/30644) + * The `maxBusyTries` option is renamed to `maxRetries`, and its default is + set to 0. The `emfileWait` option has been removed, and `EMFILE` errors + use the same retry logic as other errors. The `retryDelay` option is now + supported. `ENFILE` errors are now retried. +* **http**: + * Make maximum header size configurable per-stream or per-server (Anna Henningsen) [#30570](https://github.com/nodejs/node/pull/30570) +* **http2**: + * Make maximum tolerated rejected streams configurable (Denys Otrishko) [#30534](https://github.com/nodejs/node/pull/30534) + * Allow to configure maximum tolerated invalid frames (Denys Otrishko) [#30534](https://github.com/nodejs/node/pull/30534) +* **wasi**: + * Introduce initial WASI support (cjihrig) [#30258](https://github.com/nodejs/node/pull/30258) + +### Commits + +* [[`4cd4e7c17a`](https://github.com/nodejs/node/commit/4cd4e7c17a)] - **benchmark,doc,lib,test**: prepare for padding lint rule (Rich Trott) [#30696](https://github.com/nodejs/node/pull/30696) +* [[`63eb4fee46`](https://github.com/nodejs/node/commit/63eb4fee46)] - **buffer**: fix 6-byte writeUIntBE() range check (Brian White) [#30459](https://github.com/nodejs/node/pull/30459) +* [[`e8af569200`](https://github.com/nodejs/node/commit/e8af569200)] - **buffer**: release buffers with free callbacks on env exit (Anna Henningsen) [#30551](https://github.com/nodejs/node/pull/30551) +* [[`648766bccf`](https://github.com/nodejs/node/commit/648766bccf)] - **build**: do not build mksnapshot and mkcodecache for --shared (Joyee Cheung) [#30647](https://github.com/nodejs/node/pull/30647) +* [[`6545314a4f`](https://github.com/nodejs/node/commit/6545314a4f)] - **build**: add --without-node-code-cache configure option (Joyee Cheung) [#30647](https://github.com/nodejs/node/pull/30647) +* [[`80ada94cd3`](https://github.com/nodejs/node/commit/80ada94cd3)] - **build**: use Node.js instead of Node in configure (Tobias Nießen) [#30642](https://github.com/nodejs/node/pull/30642) +* [[`0aae502c67`](https://github.com/nodejs/node/commit/0aae502c67)] - **build,win**: propagate error codes in vcbuild (João Reis) [#30724](https://github.com/nodejs/node/pull/30724) +* [[`6a53152b42`](https://github.com/nodejs/node/commit/6a53152b42)] - **build,win**: add test-ci-native and test-ci-js (João Reis) [#30724](https://github.com/nodejs/node/pull/30724) +* [[`30a4f68a15`](https://github.com/nodejs/node/commit/30a4f68a15)] - **child_process**: document kill() return value (cjihrig) [#30669](https://github.com/nodejs/node/pull/30669) +* [[`dae36a9692`](https://github.com/nodejs/node/commit/dae36a9692)] - **child_process**: replace var with let/const (dnlup) [#30389](https://github.com/nodejs/node/pull/30389) +* [[`4b13bca31a`](https://github.com/nodejs/node/commit/4b13bca31a)] - **child_process**: replace var with const/let in internal/child\_process.js (Luis Camargo) [#30414](https://github.com/nodejs/node/pull/30414) +* [[`378c54fe97`](https://github.com/nodejs/node/commit/378c54fe97)] - **cluster**: replace vars in child.js (EmaSuriano) [#30383](https://github.com/nodejs/node/pull/30383) +* [[`708e67a732`](https://github.com/nodejs/node/commit/708e67a732)] - **cluster**: replace var with let (Herrmann, Rene R. (656)) [#30425](https://github.com/nodejs/node/pull/30425) +* [[`55fbe45f69`](https://github.com/nodejs/node/commit/55fbe45f69)] - **cluster**: replace var by let in shared\_handle.js (poutch) [#30402](https://github.com/nodejs/node/pull/30402) +* [[`4affc30a12`](https://github.com/nodejs/node/commit/4affc30a12)] - **crypto**: automatically manage memory for ECDSA\_SIG (Tobias Nießen) [#30641](https://github.com/nodejs/node/pull/30641) +* [[`55c2ac70b7`](https://github.com/nodejs/node/commit/55c2ac70b7)] - **crypto**: remove redundant validateUint32 argument (Tobias Nießen) [#30579](https://github.com/nodejs/node/pull/30579) +* [[`0ba877a541`](https://github.com/nodejs/node/commit/0ba877a541)] - **deps**: V8: cherry-pick 0dfd9ea51241 (bcoe) [#30713](https://github.com/nodejs/node/pull/30713) +* [[`b470354057`](https://github.com/nodejs/node/commit/b470354057)] - **deps**: patch V8 to 7.9.317.25 (Myles Borins) [#30679](https://github.com/nodejs/node/pull/30679) +* [[`d257448bca`](https://github.com/nodejs/node/commit/d257448bca)] - **deps**: update llhttp to 2.0.1 (Fedor Indutny) [#30553](https://github.com/nodejs/node/pull/30553) +* [[`456d250d2d`](https://github.com/nodejs/node/commit/456d250d2d)] - **deps**: V8: backport 93f189f19a03 (Michaël Zasso) [#30681](https://github.com/nodejs/node/pull/30681) +* [[`aa01ebdbca`](https://github.com/nodejs/node/commit/aa01ebdbca)] - **deps**: V8: cherry-pick ca5b0ec (Anna Henningsen) [#30708](https://github.com/nodejs/node/pull/30708) +* [[`f37450f580`](https://github.com/nodejs/node/commit/f37450f580)] - **dns**: use length for building TXT string (Anna Henningsen) [#30690](https://github.com/nodejs/node/pull/30690) +* [[`3d302ff276`](https://github.com/nodejs/node/commit/3d302ff276)] - **doc**: fix typographical error (Rich Trott) [#30735](https://github.com/nodejs/node/pull/30735) +* [[`19b31c1bc5`](https://github.com/nodejs/node/commit/19b31c1bc5)] - **doc**: revise REPL uncaught exception text (Rich Trott) [#30729](https://github.com/nodejs/node/pull/30729) +* [[`61af1fcaa1`](https://github.com/nodejs/node/commit/61af1fcaa1)] - **doc**: update signature algorithm in release doc (Myles Borins) [#30673](https://github.com/nodejs/node/pull/30673) +* [[`a8002d92ab`](https://github.com/nodejs/node/commit/a8002d92ab)] - **doc**: update README.md to fix active/maint times (Michael Dawson) [#30707](https://github.com/nodejs/node/pull/30707) +* [[`f46df0b496`](https://github.com/nodejs/node/commit/f46df0b496)] - **doc**: update socket.bufferSize text (Rich Trott) [#30723](https://github.com/nodejs/node/pull/30723) +* [[`cbd50262c0`](https://github.com/nodejs/node/commit/cbd50262c0)] - **doc**: note that buf.buffer's contents might differ (AJ Jordan) [#29651](https://github.com/nodejs/node/pull/29651) +* [[`a25626c1ed`](https://github.com/nodejs/node/commit/a25626c1ed)] - **doc**: clarify IncomingMessage.destroy() description (Sam Foxman) [#30255](https://github.com/nodejs/node/pull/30255) +* [[`8fcb450934`](https://github.com/nodejs/node/commit/8fcb450934)] - **doc**: fixed a typo in process.md (Harendra Singh) [#30277](https://github.com/nodejs/node/pull/30277) +* [[`ad9f737e44`](https://github.com/nodejs/node/commit/ad9f737e44)] - **doc**: documenting a bit more FreeBSD case (David Carlier) [#30325](https://github.com/nodejs/node/pull/30325) +* [[`40b762177f`](https://github.com/nodejs/node/commit/40b762177f)] - **doc**: add missing 'added' versions to module.builtinModules (Thomas Watson) [#30562](https://github.com/nodejs/node/pull/30562) +* [[`aca0119089`](https://github.com/nodejs/node/commit/aca0119089)] - **doc**: fix worker.resourceLimits indentation (Daniel Nalborczyk) [#30663](https://github.com/nodejs/node/pull/30663) +* [[`43e78578a6`](https://github.com/nodejs/node/commit/43e78578a6)] - **doc**: fix worker.resourceLimits type (Daniel Nalborczyk) [#30664](https://github.com/nodejs/node/pull/30664) +* [[`20dbce17d5`](https://github.com/nodejs/node/commit/20dbce17d5)] - **doc**: avoid proposal syntax in code example (Alex Zherdev) [#30685](https://github.com/nodejs/node/pull/30685) +* [[`1e7c567734`](https://github.com/nodejs/node/commit/1e7c567734)] - **doc**: address nits for src/README.md (Anna Henningsen) [#30693](https://github.com/nodejs/node/pull/30693) +* [[`87136c9bde`](https://github.com/nodejs/node/commit/87136c9bde)] - **doc**: revise socket.connect() note (Rich Trott) [#30691](https://github.com/nodejs/node/pull/30691) +* [[`fcde49700c`](https://github.com/nodejs/node/commit/fcde49700c)] - **doc**: remove "this API is unstable" note for v8 serdes API (bruce-one) [#30631](https://github.com/nodejs/node/pull/30631) +* [[`809a2b056b`](https://github.com/nodejs/node/commit/809a2b056b)] - **doc**: fixup incorrect flag name reference (Guy Bedford) [#30651](https://github.com/nodejs/node/pull/30651) +* [[`3d978839c1`](https://github.com/nodejs/node/commit/3d978839c1)] - **doc**: minor updates to releases.md (Beth Griggs) [#30636](https://github.com/nodejs/node/pull/30636) +* [[`e9f031c741`](https://github.com/nodejs/node/commit/e9f031c741)] - **doc**: add 13 and 12 to previous versions (Andrew Hughes) [#30590](https://github.com/nodejs/node/pull/30590) +* [[`8ab18b6b6f`](https://github.com/nodejs/node/commit/8ab18b6b6f)] - **doc**: update AUTHORS list (Gus Caplan) [#30672](https://github.com/nodejs/node/pull/30672) +* [[`329a821d25`](https://github.com/nodejs/node/commit/329a821d25)] - **doc**: add explanation why keep var with for loop (Lucas Recknagel) [#30380](https://github.com/nodejs/node/pull/30380) +* [[`426ca263c8`](https://github.com/nodejs/node/commit/426ca263c8)] - **doc**: document "Resume Build" limitation (Richard Lau) [#30604](https://github.com/nodejs/node/pull/30604) +* [[`00f7cc65a1`](https://github.com/nodejs/node/commit/00f7cc65a1)] - **doc**: add note of caution about non-conforming streams (Robert Nagy) [#29895](https://github.com/nodejs/node/pull/29895) +* [[`7d98a59c39`](https://github.com/nodejs/node/commit/7d98a59c39)] - **doc**: add note about debugging worker\_threads (Denys Otrishko) [#30594](https://github.com/nodejs/node/pull/30594) +* [[`8ef629a78a`](https://github.com/nodejs/node/commit/8ef629a78a)] - **doc**: simplify "is recommended" language in assert documentation (Rich Trott) [#30558](https://github.com/nodejs/node/pull/30558) +* [[`19d192d1f0`](https://github.com/nodejs/node/commit/19d192d1f0)] - **doc**: fix a typo in a date for version 13.2.0 (Kirlat) [#30587](https://github.com/nodejs/node/pull/30587) +* [[`b67759a93c`](https://github.com/nodejs/node/commit/b67759a93c)] - **doc,deps**: document how to maintain ICU in Node.js (Steven R. Loomis) [#30607](https://github.com/nodejs/node/pull/30607) +* [[`bfcc9142f3`](https://github.com/nodejs/node/commit/bfcc9142f3)] - **doc,n-api**: mark napi\_detach\_arraybuffer as experimental (legendecas) [#30703](https://github.com/nodejs/node/pull/30703) +* [[`365f0ab09b`](https://github.com/nodejs/node/commit/365f0ab09b)] - **esm**: data URLs should ignore unknown parameters (Bradley Farias) [#30593](https://github.com/nodejs/node/pull/30593) +* [[`0285aa0967`](https://github.com/nodejs/node/commit/0285aa0967)] - **events**: improve performance caused by primordials (guzhizhou) [#30577](https://github.com/nodejs/node/pull/30577) +* [[`3475f9b82c`](https://github.com/nodejs/node/commit/3475f9b82c)] - **fs**: add ENFILE to rimraf retry logic (cjihrig) [#30644](https://github.com/nodejs/node/pull/30644) +* [[`f725953433`](https://github.com/nodejs/node/commit/f725953433)] - **fs**: add retryDelay option to rimraf (cjihrig) [#30644](https://github.com/nodejs/node/pull/30644) +* [[`51bc379243`](https://github.com/nodejs/node/commit/51bc379243)] - **fs**: remove rimraf's emfileWait option (cjihrig) [#30644](https://github.com/nodejs/node/pull/30644) +* [[`612a3a2e6c`](https://github.com/nodejs/node/commit/612a3a2e6c)] - **fs**: make rimraf default to 0 retries (cjihrig) [#30644](https://github.com/nodejs/node/pull/30644) +* [[`fa1f87b199`](https://github.com/nodejs/node/commit/fa1f87b199)] - **fs**: rename rimraf's maxBusyTries to maxRetries (cjihrig) [#30644](https://github.com/nodejs/node/pull/30644) +* [[`8ee27ffe77`](https://github.com/nodejs/node/commit/8ee27ffe77)] - **fs**: change var to let (Àlvar Pérez) [#30407](https://github.com/nodejs/node/pull/30407) +* [[`850c2a72ea`](https://github.com/nodejs/node/commit/850c2a72ea)] - **fs**: cover fs.opendir ERR\_INVALID\_CALLBACK (Vladislav Botvin) [#30307](https://github.com/nodejs/node/pull/30307) +* [[`62574087ea`](https://github.com/nodejs/node/commit/62574087ea)] - **(SEMVER-MINOR)** **http**: make maximum header size configurable per-stream or per-server (Anna Henningsen) [#30570](https://github.com/nodejs/node/pull/30570) +* [[`1d1d136806`](https://github.com/nodejs/node/commit/1d1d136806)] - **http**: set socket.server unconditionally (Anna Henningsen) [#30571](https://github.com/nodejs/node/pull/30571) +* [[`6848bfbf65`](https://github.com/nodejs/node/commit/6848bfbf65)] - **http**: replace var with let (Guilherme Goncalves) [#30421](https://github.com/nodejs/node/pull/30421) +* [[`8256d38349`](https://github.com/nodejs/node/commit/8256d38349)] - **http**: destructure primordials in lib/\_http\_server.js (Artem Maksimov) [#30315](https://github.com/nodejs/node/pull/30315) +* [[`3b169f1dbd`](https://github.com/nodejs/node/commit/3b169f1dbd)] - **http**: improve performance caused by primordials (Lucas Recknagel) [#30416](https://github.com/nodejs/node/pull/30416) +* [[`6f313f9ab0`](https://github.com/nodejs/node/commit/6f313f9ab0)] - **http2**: fix session memory accounting after pausing (Michael Lehenbauer) [#30684](https://github.com/nodejs/node/pull/30684) +* [[`7d37bcebea`](https://github.com/nodejs/node/commit/7d37bcebea)] - **(SEMVER-MINOR)** **http2**: make maximum tolerated rejected streams configurable (Denys Otrishko) [#30534](https://github.com/nodejs/node/pull/30534) +* [[`092a3c28aa`](https://github.com/nodejs/node/commit/092a3c28aa)] - **(SEMVER-MINOR)** **http2**: allow to configure maximum tolerated invalid frames (Denys Otrishko) [#30534](https://github.com/nodejs/node/pull/30534) +* [[`e92afd998f`](https://github.com/nodejs/node/commit/e92afd998f)] - **(SEMVER-MINOR)** **http2**: replace direct array usage with struct for js\_fields\_ (Denys Otrishko) [#30534](https://github.com/nodejs/node/pull/30534) +* [[`30ef8e4cbd`](https://github.com/nodejs/node/commit/30ef8e4cbd)] - **http2**: change var to let compact.js (Maria Emmanouil) [#30392](https://github.com/nodejs/node/pull/30392) +* [[`1a2ed4a5f4`](https://github.com/nodejs/node/commit/1a2ed4a5f4)] - **http2**: core.js replace var with let (Daniel Schuech) [#30403](https://github.com/nodejs/node/pull/30403) +* [[`f7ca7e6677`](https://github.com/nodejs/node/commit/f7ca7e6677)] - **http2**: replace var with let/const (Paolo Ceschi Berrini) [#30417](https://github.com/nodejs/node/pull/30417) +* [[`6322611077`](https://github.com/nodejs/node/commit/6322611077)] - **inspector**: properly shut down uv\_async\_t (Anna Henningsen) [#30612](https://github.com/nodejs/node/pull/30612) +* [[`de3a1c3019`](https://github.com/nodejs/node/commit/de3a1c3019)] - **lib**: enforce use of primordial Number (Sebastien Ahkrin) [#30700](https://github.com/nodejs/node/pull/30700) +* [[`5a9340d723`](https://github.com/nodejs/node/commit/5a9340d723)] - **lib**: use static Number properties from primordials (Michaël Zasso) [#30686](https://github.com/nodejs/node/pull/30686) +* [[`892bde635e`](https://github.com/nodejs/node/commit/892bde635e)] - **lib**: enforce use of Boolean from primordials (Michaël Zasso) [#30698](https://github.com/nodejs/node/pull/30698) +* [[`ae2c7d0b02`](https://github.com/nodejs/node/commit/ae2c7d0b02)] - **lib**: replace Date.now function by primordial DateNow (Tchoupinax) [#30689](https://github.com/nodejs/node/pull/30689) +* [[`c09e3deac5`](https://github.com/nodejs/node/commit/c09e3deac5)] - **lib**: replace ArrayBuffer.isView by primordial ArrayBuffer (Vincent Dhennin) [#30692](https://github.com/nodejs/node/pull/30692) +* [[`5ef4dceb95`](https://github.com/nodejs/node/commit/5ef4dceb95)] - **lib**: enforce use of Array from primordials (Michaël Zasso) [#30635](https://github.com/nodejs/node/pull/30635) +* [[`a4dfe3b7dc`](https://github.com/nodejs/node/commit/a4dfe3b7dc)] - **lib**: flatten access to primordials (Michaël Zasso) [#30610](https://github.com/nodejs/node/pull/30610) +* [[`b545b91de5`](https://github.com/nodejs/node/commit/b545b91de5)] - **lib**: use let instead of var (Shubham Chaturvedi) [#30375](https://github.com/nodejs/node/pull/30375) +* [[`5120926337`](https://github.com/nodejs/node/commit/5120926337)] - **lib**: replace var with let/const (jens-cappelle) [#30391](https://github.com/nodejs/node/pull/30391) +* [[`b18b056d64`](https://github.com/nodejs/node/commit/b18b056d64)] - **lib**: replace var w/ let (Chris Oyler) [#30386](https://github.com/nodejs/node/pull/30386) +* [[`3796885096`](https://github.com/nodejs/node/commit/3796885096)] - **lib**: replace var with let/const (Tijl Claessens) [#30390](https://github.com/nodejs/node/pull/30390) +* [[`ffe3040659`](https://github.com/nodejs/node/commit/ffe3040659)] - **lib**: adding perf notes js\_stream\_socket.js (ryan jarvinen) [#30415](https://github.com/nodejs/node/pull/30415) +* [[`797b938c49`](https://github.com/nodejs/node/commit/797b938c49)] - **lib**: replace var with let (Dennis Saenger) [#30396](https://github.com/nodejs/node/pull/30396) +* [[`0b64e45e41`](https://github.com/nodejs/node/commit/0b64e45e41)] - **lib**: main\_thread\_only change var to let (matijagaspar) [#30398](https://github.com/nodejs/node/pull/30398) +* [[`d024630f44`](https://github.com/nodejs/node/commit/d024630f44)] - **lib**: change var to let in stream\_base\_commons (Kyriakos Markakis) [#30426](https://github.com/nodejs/node/pull/30426) +* [[`3c041edbe7`](https://github.com/nodejs/node/commit/3c041edbe7)] - **lib**: use let instead of var (Semir Ajruli) [#30424](https://github.com/nodejs/node/pull/30424) +* [[`d277c375fd`](https://github.com/nodejs/node/commit/d277c375fd)] - **lib**: changed var to let (Oliver Belaifa) [#30427](https://github.com/nodejs/node/pull/30427) +* [[`0fd89cc0f1`](https://github.com/nodejs/node/commit/0fd89cc0f1)] - **lib**: replace var with let/const (Dries Stelten) [#30409](https://github.com/nodejs/node/pull/30409) +* [[`bdba03e3ed`](https://github.com/nodejs/node/commit/bdba03e3ed)] - **lib**: change var to let (Dimitris Ktistakis) [#30408](https://github.com/nodejs/node/pull/30408) +* [[`48fef42ca9`](https://github.com/nodejs/node/commit/48fef42ca9)] - **lib**: replace var with let/const (Tembrechts) [#30404](https://github.com/nodejs/node/pull/30404) +* [[`502173b54e`](https://github.com/nodejs/node/commit/502173b54e)] - **lib**: replace var to let in cli\_table.js (Jing Lin) [#30400](https://github.com/nodejs/node/pull/30400) +* [[`2cf8a7f117`](https://github.com/nodejs/node/commit/2cf8a7f117)] - **module**: fix specifier resolution algorithm (Rongjian Zhang) [#30574](https://github.com/nodejs/node/pull/30574) +* [[`be9788bf20`](https://github.com/nodejs/node/commit/be9788bf20)] - **n-api**: detach external ArrayBuffers on env exit (Anna Henningsen) [#30551](https://github.com/nodejs/node/pull/30551) +* [[`8171cef921`](https://github.com/nodejs/node/commit/8171cef921)] - **(SEMVER-MINOR)** **n-api**: implement napi\_is\_detached\_arraybuffer (Denys Otrishko) [#30613](https://github.com/nodejs/node/pull/30613) +* [[`cc5875b2e6`](https://github.com/nodejs/node/commit/cc5875b2e6)] - **n-api**: add missed nullptr check in napi\_has\_own\_property (Denys Otrishko) [#30626](https://github.com/nodejs/node/pull/30626) +* [[`017280e6e2`](https://github.com/nodejs/node/commit/017280e6e2)] - **net**: replaced vars to lets and consts (nathias) [#30401](https://github.com/nodejs/node/pull/30401) +* [[`56248a827a`](https://github.com/nodejs/node/commit/56248a827a)] - **process**: replace var with let/const (Jesper Ek) [#30382](https://github.com/nodejs/node/pull/30382) +* [[`5c40b2f9ac`](https://github.com/nodejs/node/commit/5c40b2f9ac)] - **process**: replace vars in per\_thread.js (EmaSuriano) [#30385](https://github.com/nodejs/node/pull/30385) +* [[`c50bbf58da`](https://github.com/nodejs/node/commit/c50bbf58da)] - **readline**: change var to let (dnlup) [#30435](https://github.com/nodejs/node/pull/30435) +* [[`b91d22cc8d`](https://github.com/nodejs/node/commit/b91d22cc8d)] - **repl**: fix referrer for dynamic import (Corey Farrell) [#30609](https://github.com/nodejs/node/pull/30609) +* [[`4e5818a456`](https://github.com/nodejs/node/commit/4e5818a456)] - **repl**: change var to let (Oliver Belaifa) [#30428](https://github.com/nodejs/node/pull/30428) +* [[`e65ad865c6`](https://github.com/nodejs/node/commit/e65ad865c6)] - **src**: change header file in node\_stat\_watcher.cc (Reza Fatahi) [#29976](https://github.com/nodejs/node/pull/29976) +* [[`be84ceefb8`](https://github.com/nodejs/node/commit/be84ceefb8)] - **src**: clean up node\_file.h (Anna Henningsen) [#30530](https://github.com/nodejs/node/pull/30530) +* [[`bccfd124b0`](https://github.com/nodejs/node/commit/bccfd124b0)] - **src**: remove unused variable in node\_dir.cc (gengjiawen) [#30267](https://github.com/nodejs/node/pull/30267) +* [[`fc11db18fe`](https://github.com/nodejs/node/commit/fc11db18fe)] - **src**: inline SetSNICallback (Anna Henningsen) [#30548](https://github.com/nodejs/node/pull/30548) +* [[`7bd587ef0c`](https://github.com/nodejs/node/commit/7bd587ef0c)] - **src**: use BaseObjectPtr to store SNI context (Anna Henningsen) [#30548](https://github.com/nodejs/node/pull/30548) +* [[`8ec0d75de7`](https://github.com/nodejs/node/commit/8ec0d75de7)] - **src**: cleanup unused headers (Alexandre Ferrando) [#30328](https://github.com/nodejs/node/pull/30328) +* [[`6c249c0982`](https://github.com/nodejs/node/commit/6c249c0982)] - **src**: run native immediates during Environment cleanup (Anna Henningsen) [#30666](https://github.com/nodejs/node/pull/30666) +* [[`bea25016d1`](https://github.com/nodejs/node/commit/bea25016d1)] - **src**: no SetImmediate from destructor in stream\_pipe code (Anna Henningsen) [#30666](https://github.com/nodejs/node/pull/30666) +* [[`94357db815`](https://github.com/nodejs/node/commit/94357db815)] - **src**: add more `can_call_into_js()` guards (Anna Henningsen) [#30666](https://github.com/nodejs/node/pull/30666) +* [[`d54432f974`](https://github.com/nodejs/node/commit/d54432f974)] - **src**: keep object alive in stream\_pipe code (Anna Henningsen) [#30666](https://github.com/nodejs/node/pull/30666) +* [[`d194c0ff37`](https://github.com/nodejs/node/commit/d194c0ff37)] - **src**: replaced var with let (Aldo Ambrosioni) [#30397](https://github.com/nodejs/node/pull/30397) +* [[`44f28ea155`](https://github.com/nodejs/node/commit/44f28ea155)] - **src**: fix -Wsign-compare warnings (cjihrig) [#30565](https://github.com/nodejs/node/pull/30565) +* [[`1916acb3cb`](https://github.com/nodejs/node/commit/1916acb3cb)] - **src**: fix signal handler crash on close (Shelley Vohr) [#30582](https://github.com/nodejs/node/pull/30582) +* [[`9e9e48bf7e`](https://github.com/nodejs/node/commit/9e9e48bf7e)] - **src**: use uv\_async\_t for WeakRefs (Anna Henningsen) [#30616](https://github.com/nodejs/node/pull/30616) +* [[`9d8d2e1f45`](https://github.com/nodejs/node/commit/9d8d2e1f45)] - **src,doc**: fix broken links (cjihrig) [#30662](https://github.com/nodejs/node/pull/30662) +* [[`f135c38796`](https://github.com/nodejs/node/commit/f135c38796)] - **src,doc**: add C++ internals documentation (Anna Henningsen) [#30552](https://github.com/nodejs/node/pull/30552) +* [[`e968e26dbd`](https://github.com/nodejs/node/commit/e968e26dbd)] - **stream**: improve performance for sync write finishes (Anna Henningsen) [#30710](https://github.com/nodejs/node/pull/30710) +* [[`49e047f7a1`](https://github.com/nodejs/node/commit/49e047f7a1)] - **test**: add coverage for ERR\_TLS\_INVALID\_PROTOCOL\_VERSION (Rich Trott) [#30741](https://github.com/nodejs/node/pull/30741) +* [[`81d81a5904`](https://github.com/nodejs/node/commit/81d81a5904)] - **test**: add an indicator `isIBMi` (Xu Meng) [#30714](https://github.com/nodejs/node/pull/30714) +* [[`37c70ee198`](https://github.com/nodejs/node/commit/37c70ee198)] - **test**: use arrow functions in async-hooks tests (garygsc) [#30137](https://github.com/nodejs/node/pull/30137) +* [[`b5c7dad95a`](https://github.com/nodejs/node/commit/b5c7dad95a)] - **test**: fix test-benchmark-streams (Rich Trott) [#30757](https://github.com/nodejs/node/pull/30757) +* [[`1e199ceb71`](https://github.com/nodejs/node/commit/1e199ceb71)] - **test**: move test-http-max-http-headers to parallel (Rich Trott) [#30712](https://github.com/nodejs/node/pull/30712) +* [[`1918b4e84f`](https://github.com/nodejs/node/commit/1918b4e84f)] - **test**: correct header length subtraction (Rich Trott) [#30712](https://github.com/nodejs/node/pull/30712) +* [[`1222be81e3`](https://github.com/nodejs/node/commit/1222be81e3)] - **test**: remove unused callback argument (Rich Trott) [#30712](https://github.com/nodejs/node/pull/30712) +* [[`d69b9b753a`](https://github.com/nodejs/node/commit/d69b9b753a)] - **test**: simplify forEach() usage (Rich Trott) [#30712](https://github.com/nodejs/node/pull/30712) +* [[`01ab031cca`](https://github.com/nodejs/node/commit/01ab031cca)] - **test**: remove unused callback argument (Rich Trott) [#30712](https://github.com/nodejs/node/pull/30712) +* [[`93707c4916`](https://github.com/nodejs/node/commit/93707c4916)] - **test**: increase coverage for trace\_events.js (Rich Trott) [#30705](https://github.com/nodejs/node/pull/30705) +* [[`4800b623ed`](https://github.com/nodejs/node/commit/4800b623ed)] - **test**: use arrow functions in addons tests (garygsc) [#30131](https://github.com/nodejs/node/pull/30131) +* [[`ba0115fe6f`](https://github.com/nodejs/node/commit/ba0115fe6f)] - **test**: refactor createHook test (Jeny) [#30568](https://github.com/nodejs/node/pull/30568) +* [[`099d3fdf87`](https://github.com/nodejs/node/commit/099d3fdf87)] - **test**: port worker + buffer test to N-API (Anna Henningsen) [#30551](https://github.com/nodejs/node/pull/30551) +* [[`83861fb333`](https://github.com/nodejs/node/commit/83861fb333)] - **test**: revert 6d022c13 (Anna Henningsen) [#30708](https://github.com/nodejs/node/pull/30708) +* [[`a3b758d634`](https://github.com/nodejs/node/commit/a3b758d634)] - **test**: move test-https-server-consumed-timeout to parallel (Rich Trott) [#30677](https://github.com/nodejs/node/pull/30677) +* [[`00f532f15e`](https://github.com/nodejs/node/commit/00f532f15e)] - **test**: remove unnecessary common.platformTimeout() call (Rich Trott) [#30677](https://github.com/nodejs/node/pull/30677) +* [[`ecb902f33c`](https://github.com/nodejs/node/commit/ecb902f33c)] - **test**: do not skip test-http-server-consumed-timeout (Rich Trott) [#30677](https://github.com/nodejs/node/pull/30677) +* [[`49458deb4f`](https://github.com/nodejs/node/commit/49458deb4f)] - **test**: remove unused function argument from http test (Rich Trott) [#30677](https://github.com/nodejs/node/pull/30677) +* [[`a2f440d326`](https://github.com/nodejs/node/commit/a2f440d326)] - **test**: add logging in case of infinite loop (Rich Trott) [#30649](https://github.com/nodejs/node/pull/30649) +* [[`3e3ad396bd`](https://github.com/nodejs/node/commit/3e3ad396bd)] - **test**: remove destructuring from test-inspector-contexts (Rich Trott) [#30649](https://github.com/nodejs/node/pull/30649) +* [[`3571e132a7`](https://github.com/nodejs/node/commit/3571e132a7)] - **test**: check for session.post() errors in test-insepctor-context (Rich Trott) [#30649](https://github.com/nodejs/node/pull/30649) +* [[`37696320a2`](https://github.com/nodejs/node/commit/37696320a2)] - **test**: add mustCall() to test-inspector-contexts (Rich Trott) [#30649](https://github.com/nodejs/node/pull/30649) +* [[`0972fa3c16`](https://github.com/nodejs/node/commit/0972fa3c16)] - **test**: add regression test for signal handler removal in exit (Anna Henningsen) [#30589](https://github.com/nodejs/node/pull/30589) +* [[`5ecfd947e2`](https://github.com/nodejs/node/commit/5ecfd947e2)] - **(SEMVER-MINOR)** **test**: update and harden http2-reset-flood (Denys Otrishko) [#30534](https://github.com/nodejs/node/pull/30534) +* [[`70d6fa122a`](https://github.com/nodejs/node/commit/70d6fa122a)] - **test**: skip test-domain-error-types in debug mode temporariliy (Rich Trott) [#30629](https://github.com/nodejs/node/pull/30629) +* [[`949f2ad528`](https://github.com/nodejs/node/commit/949f2ad528)] - **test**: move test-worker-prof to sequential (Rich Trott) [#30628](https://github.com/nodejs/node/pull/30628) +* [[`d4b61709f1`](https://github.com/nodejs/node/commit/d4b61709f1)] - **test**: dir class initialisation w/o handler (Dmitriy Kikinskiy) [#30313](https://github.com/nodejs/node/pull/30313) +* [[`60b17b4fe6`](https://github.com/nodejs/node/commit/60b17b4fe6)] - **test**: change object assign by spread operator (poutch) [#30438](https://github.com/nodejs/node/pull/30438) +* [[`97e627335f`](https://github.com/nodejs/node/commit/97e627335f)] - **test**: use useful message argument in test function (Rich Trott) [#30618](https://github.com/nodejs/node/pull/30618) +* [[`d651c7dd6b`](https://github.com/nodejs/node/commit/d651c7dd6b)] - **test**: test for minimum ICU version consistency (Richard Lau) [#30608](https://github.com/nodejs/node/pull/30608) +* [[`dade9069c3`](https://github.com/nodejs/node/commit/dade9069c3)] - **test**: code&learn var to let update (Nazar Malyy) [#30436](https://github.com/nodejs/node/pull/30436) +* [[`e401e8c8ed`](https://github.com/nodejs/node/commit/e401e8c8ed)] - **test**: change object assign to spread object (poutch) [#30422](https://github.com/nodejs/node/pull/30422) +* [[`2ecc735c48`](https://github.com/nodejs/node/commit/2ecc735c48)] - **test**: use spread instead of Object.assign (dnlup) [#30419](https://github.com/nodejs/node/pull/30419) +* [[`d8da9dacab`](https://github.com/nodejs/node/commit/d8da9dacab)] - **test**: changed var to let in module-errors (Jamar Torres) [#30413](https://github.com/nodejs/node/pull/30413) +* [[`9dab32f340`](https://github.com/nodejs/node/commit/9dab32f340)] - **test**: use spread instead of object.assign (Shubham Chaturvedi) [#30412](https://github.com/nodejs/node/pull/30412) +* [[`7e7a8165a8`](https://github.com/nodejs/node/commit/7e7a8165a8)] - **test**: replace var with let in pre\_execution.js (Vladimir Adamic) [#30411](https://github.com/nodejs/node/pull/30411) +* [[`8a9ee48797`](https://github.com/nodejs/node/commit/8a9ee48797)] - **test**: change var to let in test-trace-events (Jon Church) [#30406](https://github.com/nodejs/node/pull/30406) +* [[`d6a448825c`](https://github.com/nodejs/node/commit/d6a448825c)] - **test**: dns utils replace var (Osmond van Hemert) [#30405](https://github.com/nodejs/node/pull/30405) +* [[`01e0571e94`](https://github.com/nodejs/node/commit/01e0571e94)] - **test**: test cover cases when trace is empty (telenord) [#30311](https://github.com/nodejs/node/pull/30311) +* [[`f8dfa2d704`](https://github.com/nodejs/node/commit/f8dfa2d704)] - **test**: switch to object spread in common/benchmark.js (palmires) [#30309](https://github.com/nodejs/node/pull/30309) +* [[`36671f9bf8`](https://github.com/nodejs/node/commit/36671f9bf8)] - **test**: add common.mustCall() to stream test (Rich Trott) [#30561](https://github.com/nodejs/node/pull/30561) +* [[`106235fe91`](https://github.com/nodejs/node/commit/106235fe91)] - **test**: move explanatory comment to expected location in file (Rich Trott) [#30561](https://github.com/nodejs/node/pull/30561) +* [[`081b4e2496`](https://github.com/nodejs/node/commit/081b4e2496)] - **test**: move stream test to parallel (Rich Trott) [#30561](https://github.com/nodejs/node/pull/30561) +* [[`103d01e057`](https://github.com/nodejs/node/commit/103d01e057)] - **test**: remove string literal as message in strictEqual() in stream test (Rich Trott) [#30561](https://github.com/nodejs/node/pull/30561) +* [[`ebba3228e2`](https://github.com/nodejs/node/commit/ebba3228e2)] - **test**: use arrow function for callback in stream test (Rich Trott) [#30561](https://github.com/nodejs/node/pull/30561) +* [[`e122d397c0`](https://github.com/nodejs/node/commit/e122d397c0)] - **test**: replace setTimeout with setImmediate in stream test (Rich Trott) [#30561](https://github.com/nodejs/node/pull/30561) +* [[`20ee4997f3`](https://github.com/nodejs/node/commit/20ee4997f3)] - **test**: refactor test-dgram-multicast-set-interface-lo.js (Taylor Gagne) [#30536](https://github.com/nodejs/node/pull/30536) +* [[`7aa1df7076`](https://github.com/nodejs/node/commit/7aa1df7076)] - **tls**: introduce ERR\_TLS\_INVALID\_CONTEXT (Rich Trott) [#30718](https://github.com/nodejs/node/pull/30718) +* [[`0b0f0237c1`](https://github.com/nodejs/node/commit/0b0f0237c1)] - **tls**: add memory tracking support to SSLWrap (Anna Henningsen) [#30548](https://github.com/nodejs/node/pull/30548) +* [[`89e2c71b27`](https://github.com/nodejs/node/commit/89e2c71b27)] - **tls**: allow empty subject even with altNames defined (Jason Macgowan) [#22906](https://github.com/nodejs/node/pull/22906) +* [[`941a91daed`](https://github.com/nodejs/node/commit/941a91daed)] - **tools**: enforce blank line between functions (Rich Trott) [#30696](https://github.com/nodejs/node/pull/30696) +* [[`5a6f836a15`](https://github.com/nodejs/node/commit/5a6f836a15)] - **tools**: add unified plugin changing links for html docs (Marek Łabuz) [#29946](https://github.com/nodejs/node/pull/29946) +* [[`84f7b5c752`](https://github.com/nodejs/node/commit/84f7b5c752)] - **tools**: enable more eslint rules (cjihrig) [#30598](https://github.com/nodejs/node/pull/30598) +* [[`5522467cf5`](https://github.com/nodejs/node/commit/5522467cf5)] - **tools**: update ESLint to 6.7.1 (cjihrig) [#30598](https://github.com/nodejs/node/pull/30598) +* [[`1f10681496`](https://github.com/nodejs/node/commit/1f10681496)] - **tty**: truecolor check moved before 256 check (Duncan Healy) [#30474](https://github.com/nodejs/node/pull/30474) +* [[`6a0dd1cbbd`](https://github.com/nodejs/node/commit/6a0dd1cbbd)] - **util**: fix .format() not always calling toString when it should be (Ruben Bridgewater) [#30343](https://github.com/nodejs/node/pull/30343) +* [[`1040e7222f`](https://github.com/nodejs/node/commit/1040e7222f)] - **util**: fix inspection of errors with tampered name or stack property (Ruben Bridgewater) [#30576](https://github.com/nodejs/node/pull/30576) +* [[`18e9b56bf6`](https://github.com/nodejs/node/commit/18e9b56bf6)] - **util**: use let instead of var for util/inspect.js (Luciano) [#30399](https://github.com/nodejs/node/pull/30399) +* [[`9ec53cf5c1`](https://github.com/nodejs/node/commit/9ec53cf5c1)] - **(SEMVER-MINOR)** **wasi**: introduce initial WASI support (cjihrig) [#30258](https://github.com/nodejs/node/pull/30258) + ## 2019-11-21, Version 13.2.0 (Current), @MylesBorins diff --git a/src/node_version.h b/src/node_version.h index ed3787b8f78319..3193890e0c8443 100644 --- a/src/node_version.h +++ b/src/node_version.h @@ -23,13 +23,13 @@ #define SRC_NODE_VERSION_H_ #define NODE_MAJOR_VERSION 13 -#define NODE_MINOR_VERSION 2 -#define NODE_PATCH_VERSION 1 +#define NODE_MINOR_VERSION 3 +#define NODE_PATCH_VERSION 0 #define NODE_VERSION_IS_LTS 0 #define NODE_VERSION_LTS_CODENAME "" -#define NODE_VERSION_IS_RELEASE 0 +#define NODE_VERSION_IS_RELEASE 1 #ifndef NODE_STRINGIFY #define NODE_STRINGIFY(n) NODE_STRINGIFY_HELPER(n)