From bc71278563b60caba7e2d39c170d439a205ff529 Mon Sep 17 00:00:00 2001 From: James M Snell Date: Wed, 3 Jun 2020 18:13:01 -0700 Subject: [PATCH 01/43] events: lazy load perf_hooks for EventTarget MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/33717 Reviewed-By: Ruben Bridgewater Reviewed-By: Zeyu Yang Reviewed-By: Denys Otrishko Reviewed-By: Juan José Arboleda Reviewed-By: Benjamin Gruenbaum Reviewed-By: Trivikram Kamat --- lib/internal/event_target.js | 11 +++++++++-- test/parallel/test-bootstrap-modules.js | 4 ---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/internal/event_target.js b/lib/internal/event_target.js index 8641129b132914..3df88cedf58a3d 100644 --- a/lib/internal/event_target.js +++ b/lib/internal/event_target.js @@ -19,7 +19,6 @@ const { } } = require('internal/errors'); -const perf_hooks = require('perf_hooks'); const { customInspectSymbol } = require('internal/util'); const { inspect } = require('util'); @@ -30,11 +29,19 @@ const kTarget = Symbol('kTarget'); const kNewListener = Symbol('kNewListener'); const kRemoveListener = Symbol('kRemoveListener'); +// Lazy load perf_hooks to avoid the additional overhead on startup +let perf_hooks; +function lazyNow() { + if (perf_hooks === undefined) + perf_hooks = require('perf_hooks'); + return perf_hooks.performance.now(); +} + class Event { #type = undefined; #defaultPrevented = false; #cancelable = false; - #timestamp = perf_hooks.performance.now(); + #timestamp = lazyNow(); // None of these are currently used in the Node.js implementation // of EventTarget because there is no concept of bubbling or diff --git a/test/parallel/test-bootstrap-modules.js b/test/parallel/test-bootstrap-modules.js index d4aa7bdd3142c5..2668c9bf2102db 100644 --- a/test/parallel/test-bootstrap-modules.js +++ b/test/parallel/test-bootstrap-modules.js @@ -21,7 +21,6 @@ const expectedModules = new Set([ 'Internal Binding module_wrap', 'Internal Binding native_module', 'Internal Binding options', - 'Internal Binding performance', 'Internal Binding process_methods', 'Internal Binding report', 'Internal Binding string_decoder', @@ -32,7 +31,6 @@ const expectedModules = new Set([ 'Internal Binding types', 'Internal Binding url', 'Internal Binding util', - 'NativeModule async_hooks', 'NativeModule buffer', 'NativeModule events', 'NativeModule fs', @@ -50,7 +48,6 @@ const expectedModules = new Set([ 'NativeModule internal/fixed_queue', 'NativeModule internal/fs/dir', 'NativeModule internal/fs/utils', - 'NativeModule internal/histogram', 'NativeModule internal/idna', 'NativeModule internal/linkedlist', 'NativeModule internal/modules/run_main', @@ -88,7 +85,6 @@ const expectedModules = new Set([ 'NativeModule internal/vm/module', 'NativeModule internal/worker/js_transferable', 'NativeModule path', - 'NativeModule perf_hooks', 'NativeModule timers', 'NativeModule url', 'NativeModule util', From ff74e35c0bfc206fdbac21b5df61edf860a3267b Mon Sep 17 00:00:00 2001 From: Dan Fabulich Date: Tue, 19 May 2020 17:41:01 -0700 Subject: [PATCH 02/43] process: add unhandled-rejection throw and warn-with-error-code This PR defines two new modes for the --unhandled-rejections flag. The first mode is called "throw". The "throw" mode first emits unhandledRejection. If this hook is not set, the "throw" mode will raise the unhandled rejection as an uncaught exception. The second mode is called "warn-with-error-code". The "warn-with-error-code" mode first emits unhandledRejection. If this hook is not set, the "warn-with-error-code" mode will trigger a warning and set the process's exit code to 1. The PR doesn't change the default behavior for unhandled rejections. That will come in a separate PR. Refs: https://github.com/nodejs/node/pull/33021 PR-URL: https://github.com/nodejs/node/pull/33475 Reviewed-By: Benjamin Gruenbaum Reviewed-By: Anna Henningsen --- doc/api/cli.md | 6 +- lib/internal/process/promises.js | 52 +++++++++++++++--- src/node_options.cc | 2 + .../promise_unhandled_warn_with_error.js | 10 ++++ .../promise_unhandled_warn_with_error.out | 10 ++++ .../test-promise-unhandled-throw-handler.js | 38 +++++++++++++ test/parallel/test-promise-unhandled-throw.js | 55 +++++++++++++++++++ 7 files changed, 164 insertions(+), 9 deletions(-) create mode 100644 test/message/promise_unhandled_warn_with_error.js create mode 100644 test/message/promise_unhandled_warn_with_error.out create mode 100644 test/parallel/test-promise-unhandled-throw-handler.js create mode 100644 test/parallel/test-promise-unhandled-throw.js diff --git a/doc/api/cli.md b/doc/api/cli.md index bee20f34525d24..ba7cd510f31c9e 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -983,11 +983,15 @@ for the very first unhandled rejection in case no [`unhandledRejection`][] hook is used. Using this flag allows to change what should happen when an unhandled rejection -occurs. One of three modes can be chosen: +occurs. One of the following modes can be chosen: +* `throw`: Emit [`unhandledRejection`][]. If this hook is not set, raise the + unhandled rejection as an uncaught exception. * `strict`: Raise the unhandled rejection as an uncaught exception. * `warn`: Always trigger a warning, no matter if the [`unhandledRejection`][] hook is set or not but do not print the deprecation warning. +* `warn-with-error-code`: Emit [`unhandledRejection`][]. If this hook is not + set, trigger a warning, and set the process exit code to 1. * `none`: Silence all warnings. ### `--use-bundled-ca`, `--use-openssl-ca` diff --git a/lib/internal/process/promises.js b/lib/internal/process/promises.js index 1a0ed06f213df8..f2145d425c620f 100644 --- a/lib/internal/process/promises.js +++ b/lib/internal/process/promises.js @@ -30,22 +30,37 @@ const pendingUnhandledRejections = []; const asyncHandledRejections = []; let lastPromiseId = 0; -// --unhandled-rejection=none: +// --unhandled-rejections=none: // Emit 'unhandledRejection', but do not emit any warning. const kIgnoreUnhandledRejections = 0; -// --unhandled-rejection=warn: + +// --unhandled-rejections=warn: // Emit 'unhandledRejection', then emit 'UnhandledPromiseRejectionWarning'. const kAlwaysWarnUnhandledRejections = 1; -// --unhandled-rejection=strict: + +// --unhandled-rejections=strict: // Emit 'uncaughtException'. If it's not handled, print the error to stderr // and exit the process. // Otherwise, emit 'unhandledRejection'. If 'unhandledRejection' is not // handled, emit 'UnhandledPromiseRejectionWarning'. -const kThrowUnhandledRejections = 2; -// --unhandled-rejection is unset: -// Emit 'unhandledRejection', if it's handled, emit +const kStrictUnhandledRejections = 2; + +// --unhandled-rejections=throw: +// Emit 'unhandledRejection', if it's unhandled, emit +// 'uncaughtException'. If it's not handled, print the error to stderr +// and exit the process. +const kThrowUnhandledRejections = 3; + +// --unhandled-rejections=warn-with-error-code: +// Emit 'unhandledRejection', if it's unhandled, emit +// 'UnhandledPromiseRejectionWarning', then set process exit code to 1. + +const kWarnWithErrorCodeUnhandledRejections = 4; + +// --unhandled-rejections is unset: +// Emit 'unhandledRejection', if it's unhandled, emit // 'UnhandledPromiseRejectionWarning', then emit deprecation warning. -const kDefaultUnhandledRejections = 3; +const kDefaultUnhandledRejections = 5; let unhandledRejectionsMode; @@ -65,7 +80,11 @@ function getUnhandledRejectionsMode() { case 'warn': return kAlwaysWarnUnhandledRejections; case 'strict': + return kStrictUnhandledRejections; + case 'throw': return kThrowUnhandledRejections; + case 'warn-with-error-code': + return kWarnWithErrorCodeUnhandledRejections; default: return kDefaultUnhandledRejections; } @@ -188,7 +207,7 @@ function processPromiseRejections() { promiseInfo.warned = true; const { reason, uid } = promiseInfo; switch (unhandledRejectionsMode) { - case kThrowUnhandledRejections: { + case kStrictUnhandledRejections: { const err = reason instanceof Error ? reason : generateUnhandledRejectionError(reason); triggerUncaughtException(err, true /* fromPromise */); @@ -205,6 +224,23 @@ function processPromiseRejections() { emitUnhandledRejectionWarning(uid, reason); break; } + case kThrowUnhandledRejections: { + const handled = process.emit('unhandledRejection', reason, promise); + if (!handled) { + const err = reason instanceof Error ? + reason : generateUnhandledRejectionError(reason); + triggerUncaughtException(err, true /* fromPromise */); + } + break; + } + case kWarnWithErrorCodeUnhandledRejections: { + const handled = process.emit('unhandledRejection', reason, promise); + if (!handled) { + emitUnhandledRejectionWarning(uid, reason); + process.exitCode = 1; + } + break; + } case kDefaultUnhandledRejections: { const handled = process.emit('unhandledRejection', reason, promise); if (!handled) { diff --git a/src/node_options.cc b/src/node_options.cc index fb730975761b32..d7434b4b124872 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -118,6 +118,8 @@ void EnvironmentOptions::CheckOptions(std::vector* errors) { } if (!unhandled_rejections.empty() && + unhandled_rejections != "warn-with-error-code" && + unhandled_rejections != "throw" && unhandled_rejections != "strict" && unhandled_rejections != "warn" && unhandled_rejections != "none") { diff --git a/test/message/promise_unhandled_warn_with_error.js b/test/message/promise_unhandled_warn_with_error.js new file mode 100644 index 00000000000000..0f22e8deeba653 --- /dev/null +++ b/test/message/promise_unhandled_warn_with_error.js @@ -0,0 +1,10 @@ +// Flags: --unhandled-rejections=warn-with-error-code +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +common.disableCrashOnUnhandledRejection(); + +Promise.reject(new Error('alas')); +process.on('exit', assert.strictEqual.bind(null, 1)); diff --git a/test/message/promise_unhandled_warn_with_error.out b/test/message/promise_unhandled_warn_with_error.out new file mode 100644 index 00000000000000..b539adb2d1e769 --- /dev/null +++ b/test/message/promise_unhandled_warn_with_error.out @@ -0,0 +1,10 @@ +*UnhandledPromiseRejectionWarning: Error: alas + at *promise_unhandled_warn_with_error.js:*:* + at * + at * + at * + at * + at * + at * +(Use `node --trace-warnings ...` to show where the warning was created) +*UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1) \ No newline at end of file diff --git a/test/parallel/test-promise-unhandled-throw-handler.js b/test/parallel/test-promise-unhandled-throw-handler.js new file mode 100644 index 00000000000000..be441c2d34c6bd --- /dev/null +++ b/test/parallel/test-promise-unhandled-throw-handler.js @@ -0,0 +1,38 @@ +// Flags: --unhandled-rejections=throw +'use strict'; + +const common = require('../common'); +const Countdown = require('../common/countdown'); +const assert = require('assert'); + +common.disableCrashOnUnhandledRejection(); + +// Verify that the unhandledRejection handler prevents triggering +// uncaught exceptions + +const err1 = new Error('One'); + +const errors = [err1, null]; + +const ref = new Promise(() => { + throw err1; +}); +// Explicitly reject `null`. +Promise.reject(null); + +process.on('warning', common.mustNotCall('warning')); +process.on('rejectionHandled', common.mustNotCall('rejectionHandled')); +process.on('exit', assert.strictEqual.bind(null, 0)); +process.on('uncaughtException', common.mustNotCall('uncaughtException')); + +const timer = setTimeout(() => console.log(ref), 1000); + +const counter = new Countdown(2, () => { + clearTimeout(timer); +}); + +process.on('unhandledRejection', common.mustCall((err) => { + counter.dec(); + const knownError = errors.shift(); + assert.deepStrictEqual(err, knownError); +}, 2)); diff --git a/test/parallel/test-promise-unhandled-throw.js b/test/parallel/test-promise-unhandled-throw.js new file mode 100644 index 00000000000000..30c6b87135b49b --- /dev/null +++ b/test/parallel/test-promise-unhandled-throw.js @@ -0,0 +1,55 @@ +// Flags: --unhandled-rejections=throw +'use strict'; + +const common = require('../common'); +const Countdown = require('../common/countdown'); +const assert = require('assert'); + +common.disableCrashOnUnhandledRejection(); + +// Verify that unhandled rejections always trigger uncaught exceptions instead +// of triggering unhandled rejections. + +const err1 = new Error('One'); +const err2 = new Error( + 'This error originated either by throwing ' + + 'inside of an async function without a catch block, or by rejecting a ' + + 'promise which was not handled with .catch(). The promise rejected with the' + + ' reason "null".' +); +err2.code = 'ERR_UNHANDLED_REJECTION'; +Object.defineProperty(err2, 'name', { + value: 'UnhandledPromiseRejection', + writable: true, + configurable: true +}); + +const errors = [err1, err2]; +const identical = [true, false]; + +const ref = new Promise(() => { + throw err1; +}); +// Explicitly reject `null`. +Promise.reject(null); + +process.on('warning', common.mustNotCall('warning')); +// If we add an unhandledRejection handler, the exception won't be thrown +// process.on('unhandledRejection', common.mustCall(2)); +process.on('rejectionHandled', common.mustNotCall('rejectionHandled')); +process.on('exit', assert.strictEqual.bind(null, 0)); + +const timer = setTimeout(() => console.log(ref), 1000); + +const counter = new Countdown(2, () => { + clearTimeout(timer); +}); + +process.on('uncaughtException', common.mustCall((err, origin) => { + counter.dec(); + assert.strictEqual(origin, 'unhandledRejection', err); + const knownError = errors.shift(); + assert.deepStrictEqual(err, knownError); + // Check if the errors are reference equal. + assert(identical.shift() ? err === knownError : err !== knownError); +}, 2)); From 14d012ef96f25b097ca8d87cf32f173e3aa3bf7e Mon Sep 17 00:00:00 2001 From: James M Snell Date: Tue, 16 Jun 2020 12:31:05 -0700 Subject: [PATCH 03/43] quic: fix minor linting issue PR-URL: https://github.com/nodejs/node/pull/33913 Reviewed-By: Anna Henningsen Reviewed-By: Richard Lau --- src/quic/node_quic_socket.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/quic/node_quic_socket.cc b/src/quic/node_quic_socket.cc index 5e12df5992b7c6..839c33f5bf42db 100644 --- a/src/quic/node_quic_socket.cc +++ b/src/quic/node_quic_socket.cc @@ -32,13 +32,11 @@ using v8::Context; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::HandleScope; -using v8::Integer; using v8::Isolate; using v8::Local; using v8::Number; using v8::Object; using v8::ObjectTemplate; -using v8::PropertyAttribute; using v8::String; using v8::Value; From ee7f0e3f7550508f20c3d7f7c6b2be78a0a9a938 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Sun, 14 Jun 2020 13:56:18 -0700 Subject: [PATCH 04/43] doc,stream: split finish and end events into separate entries The stream doc has the only instance in our docs where two events are combined into a single entry. Split them into separate adjacent entries. PR-URL: https://github.com/nodejs/node/pull/33881 Reviewed-By: Anna Henningsen Reviewed-By: Robert Nagy Reviewed-By: Luigi Pinca Reviewed-By: James M Snell --- doc/api/stream.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/doc/api/stream.md b/doc/api/stream.md index 369b5ee1e307f5..b0b2470d435a94 100644 --- a/doc/api/stream.md +++ b/doc/api/stream.md @@ -2734,15 +2734,19 @@ const myTransform = new Transform({ }); ``` -#### Events: `'finish'` and `'end'` +#### Event: `'end'` -The [`'finish'`][] and [`'end'`][] events are from the `stream.Writable` -and `stream.Readable` classes, respectively. The `'finish'` event is emitted -after [`stream.end()`][stream-end] is called and all chunks have been processed -by [`stream._transform()`][stream-_transform]. The `'end'` event is emitted -after all data has been output, which occurs after the callback in +The [`'end'`][] event is from the `stream.Readable` class. The `'end'` event is +emitted after all data has been output, which occurs after the callback in [`transform._flush()`][stream-_flush] has been called. In the case of an error, -neither `'finish'` nor `'end'` should be emitted. +`'end'` should not be emitted. + +#### Event: `'finish'` + +The [`'finish'`][] event is from the `stream.Writable` class. The `'finish'` +event is emitted after [`stream.end()`][stream-end] is called and all chunks +have been processed by [`stream._transform()`][stream-_transform]. In the case +of an error, `'finish'` should not be emitted. #### `transform._flush(callback)` From 1e4187fcf4b8cc27df027fee2c4c266f17f14161 Mon Sep 17 00:00:00 2001 From: Pranshu Srivastava Date: Sat, 25 Apr 2020 03:37:43 +0530 Subject: [PATCH 05/43] http2: add `invalidheaders` test Refs: https://github.com/nodejs/node/issues/29829 PR-URL: https://github.com/nodejs/node/pull/33161 Reviewed-By: Robert Nagy Reviewed-By: Matteo Collina Reviewed-By: James M Snell Reviewed-By: Trivikram Kamat Reviewed-By: Denys Otrishko --- lib/internal/http2/compat.js | 2 +- lib/internal/http2/util.js | 6 +- .../parallel/test-http2-invalidheaderfield.js | 65 +++++++++++++++++++ .../test-http2-invalidheaderfields-client.js | 20 ++++-- 4 files changed, 84 insertions(+), 9 deletions(-) create mode 100644 test/parallel/test-http2-invalidheaderfield.js diff --git a/lib/internal/http2/compat.js b/lib/internal/http2/compat.js index 841f9b69a033cd..6a2d5097116cc2 100644 --- a/lib/internal/http2/compat.js +++ b/lib/internal/http2/compat.js @@ -73,7 +73,7 @@ let statusConnectionHeaderWarned = false; // close as possible to the current require('http') API const assertValidHeader = hideStackFrames((name, value) => { - if (name === '' || typeof name !== 'string') { + if (name === '' || typeof name !== 'string' || name.indexOf(' ') >= 0) { throw new ERR_INVALID_HTTP_TOKEN('Header name', name); } if (isPseudoHeader(name)) { diff --git a/lib/internal/http2/util.js b/lib/internal/http2/util.js index 2ef0824cf760bc..8327a1d248f803 100644 --- a/lib/internal/http2/util.js +++ b/lib/internal/http2/util.js @@ -18,7 +18,8 @@ const { ERR_HTTP2_INVALID_CONNECTION_HEADERS, ERR_HTTP2_INVALID_PSEUDOHEADER, ERR_HTTP2_INVALID_SETTING_VALUE, - ERR_INVALID_ARG_TYPE + ERR_INVALID_ARG_TYPE, + ERR_INVALID_HTTP_TOKEN }, addCodeToName, hideStackFrames @@ -490,6 +491,9 @@ function mapToHeaders(map, count++; continue; } + if (key.indexOf(' ') >= 0) { + throw new ERR_INVALID_HTTP_TOKEN('Header name', key); + } if (isIllegalConnectionSpecificHeader(key, value)) { throw new ERR_HTTP2_INVALID_CONNECTION_HEADERS(key); } diff --git a/test/parallel/test-http2-invalidheaderfield.js b/test/parallel/test-http2-invalidheaderfield.js new file mode 100644 index 00000000000000..0ff8503b8cf79b --- /dev/null +++ b/test/parallel/test-http2-invalidheaderfield.js @@ -0,0 +1,65 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) { common.skip('missing crypto'); } + +// Check for: +// Spaced headers +// Psuedo headers +// Capitalized headers + +const http2 = require('http2'); +const { throws, strictEqual } = require('assert'); + +const server = http2.createServer(common.mustCall((req, res) => { + throws(() => { + res.setHeader(':path', '/'); + }, { + code: 'ERR_HTTP2_PSEUDOHEADER_NOT_ALLOWED' + }); + throws(() => { + res.setHeader('t est', 123); + }, { + code: 'ERR_INVALID_HTTP_TOKEN' + }); + res.setHeader('TEST', 123); + res.setHeader('test_', 123); + res.setHeader(' test', 123); + res.end(); +})); + +server.listen(0, common.mustCall(() => { + const session1 = http2.connect(`http://localhost:${server.address().port}`); + session1.request({ 'test_': 123, 'TEST': 123 }) + .on('end', common.mustCall(() => { + session1.close(); + server.close(); + })); + + const session2 = http2.connect(`http://localhost:${server.address().port}`); + session2.on('error', common.mustCall((e) => { + strictEqual(e.code, 'ERR_INVALID_HTTP_TOKEN'); + })); + throws(() => { + session2.request({ 't est': 123 }); + }, { + code: 'ERR_INVALID_HTTP_TOKEN' + }); + + const session3 = http2.connect(`http://localhost:${server.address().port}`); + session3.on('error', common.mustCall((e) => { + strictEqual(e.code, 'ERR_INVALID_HTTP_TOKEN'); + })); + throws(() => { + session3.request({ ' test': 123 }); + }, { + code: 'ERR_INVALID_HTTP_TOKEN' + }); + + const session4 = http2.connect(`http://localhost:${server.address().port}`); + throws(() => { + session4.request({ ':test': 123 }); + }, { + code: 'ERR_HTTP2_INVALID_PSEUDOHEADER' + }); + session4.close(); +})); diff --git a/test/parallel/test-http2-invalidheaderfields-client.js b/test/parallel/test-http2-invalidheaderfields-client.js index 90a3ea4622fccc..a5681970faab27 100644 --- a/test/parallel/test-http2-invalidheaderfields-client.js +++ b/test/parallel/test-http2-invalidheaderfields-client.js @@ -9,7 +9,11 @@ const server1 = http2.createServer(); server1.listen(0, common.mustCall(() => { const session = http2.connect(`http://localhost:${server1.address().port}`); // Check for req headers - session.request({ 'no underscore': 123 }); + assert.throws(() => { + session.request({ 'no underscore': 123 }); + }, { + code: 'ERR_INVALID_HTTP_TOKEN' + }); session.on('error', common.mustCall((e) => { assert.strictEqual(e.code, 'ERR_INVALID_HTTP_TOKEN'); server1.close(); @@ -18,15 +22,18 @@ server1.listen(0, common.mustCall(() => { const server2 = http2.createServer(common.mustCall((req, res) => { // check for setHeader - res.setHeader('x y z', 123); + assert.throws(() => { + res.setHeader('x y z', 123); + }, { + code: 'ERR_INVALID_HTTP_TOKEN' + }); res.end(); })); server2.listen(0, common.mustCall(() => { const session = http2.connect(`http://localhost:${server2.address().port}`); const req = session.request(); - req.on('error', common.mustCall((e) => { - assert.strictEqual(e.code, 'ERR_HTTP2_STREAM_ERROR'); + req.on('end', common.mustCall(() => { session.close(); server2.close(); })); @@ -39,7 +46,7 @@ const server3 = http2.createServer(common.mustCall((req, res) => { 'an invalid header': 123 }); }), { - code: 'ERR_HTTP2_INVALID_STREAM' + code: 'ERR_INVALID_HTTP_TOKEN' }); res.end(); })); @@ -47,8 +54,7 @@ const server3 = http2.createServer(common.mustCall((req, res) => { server3.listen(0, common.mustCall(() => { const session = http2.connect(`http://localhost:${server3.address().port}`); const req = session.request(); - req.on('error', common.mustCall((e) => { - assert.strictEqual(e.code, 'ERR_HTTP2_STREAM_ERROR'); + req.on('end', common.mustCall(() => { server3.close(); session.close(); })); From 50fb0199cd274b177e596310018aa02c8067bcb2 Mon Sep 17 00:00:00 2001 From: Mathias Buus Date: Tue, 9 Jun 2020 10:53:47 +0200 Subject: [PATCH 06/43] n-api: document nextTick timing in callbacks PR-URL: https://github.com/nodejs/node/pull/33804 Reviewed-By: Matteo Collina Reviewed-By: Chengzhong Wu Reviewed-By: James M Snell Reviewed-By: Anna Henningsen Reviewed-By: Gabriel Schulhof Reviewed-By: Michael Dawson --- doc/api/n-api.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/api/n-api.md b/doc/api/n-api.md index 58e79c6e9933de..ae0b0b09d06926 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -4755,6 +4755,9 @@ is sufficient and appropriate. Use of the `napi_make_callback` function may be required when implementing custom async behavior that does not use `napi_create_async_work`. +Any `process.nextTick`s or Promises scheduled on the microtask queue by +JavaScript during the callback are ran before returning back to C/C++. + ### napi_open_callback_scope -* `msg` {Buffer|Uint8Array|string|Array} Message to be sent. +* `msg` {Buffer|TypedArray|DataView|string|Array} Message to be sent. * `offset` {integer} Offset in the buffer where the message starts. * `length` {integer} Number of bytes in the message. * `port` {integer} Destination port. @@ -416,8 +419,8 @@ specified. Connected sockets, on the other hand, will use their associated remote endpoint, so the `port` and `address` arguments must not be set. The `msg` argument contains the message to be sent. -Depending on its type, different behavior can apply. If `msg` is a `Buffer` -or `Uint8Array`, +Depending on its type, different behavior can apply. If `msg` is a `Buffer`, +any `TypedArray` or a `DataView`, the `offset` and `length` specify the offset within the `Buffer` where the message begins and the number of bytes in the message, respectively. If `msg` is a `String`, then it is automatically converted to a `Buffer` @@ -446,7 +449,8 @@ passed as the first argument to the `callback`. If a `callback` is not given, the error is emitted as an `'error'` event on the `socket` object. Offset and length are optional but both *must* be set if either are used. -They are supported only when the first argument is a `Buffer` or `Uint8Array`. +They are supported only when the first argument is a `Buffer`, a `TypedArray`, +or a `DataView`. Example of sending a UDP packet to a port on `localhost`; diff --git a/lib/dgram.js b/lib/dgram.js index ddac50ade190b5..1c1b4781617695 100644 --- a/lib/dgram.js +++ b/lib/dgram.js @@ -54,7 +54,7 @@ const { } = require('internal/validators'); const { Buffer } = require('buffer'); const { deprecate } = require('internal/util'); -const { isUint8Array } = require('internal/util/types'); +const { isArrayBufferView } = require('internal/util/types'); const EventEmitter = require('events'); const { defaultTriggerAsyncIdScope, @@ -455,15 +455,19 @@ Socket.prototype.sendto = function(buffer, function sliceBuffer(buffer, offset, length) { if (typeof buffer === 'string') { buffer = Buffer.from(buffer); - } else if (!isUint8Array(buffer)) { + } else if (!isArrayBufferView(buffer)) { throw new ERR_INVALID_ARG_TYPE('buffer', - ['Buffer', 'Uint8Array', 'string'], buffer); + ['Buffer', + 'TypedArray', + 'DataView', + 'string'], + buffer); } offset = offset >>> 0; length = length >>> 0; - return buffer.slice(offset, offset + length); + return Buffer.from(buffer.buffer, buffer.byteOffset + offset, length); } @@ -474,10 +478,10 @@ function fixBufferList(list) { const buf = list[i]; if (typeof buf === 'string') newlist[i] = Buffer.from(buf); - else if (!isUint8Array(buf)) + else if (!isArrayBufferView(buf)) return null; else - newlist[i] = buf; + newlist[i] = Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength); } return newlist; @@ -581,16 +585,23 @@ Socket.prototype.send = function(buffer, if (!ArrayIsArray(buffer)) { if (typeof buffer === 'string') { list = [ Buffer.from(buffer) ]; - } else if (!isUint8Array(buffer)) { + } else if (!isArrayBufferView(buffer)) { throw new ERR_INVALID_ARG_TYPE('buffer', - ['Buffer', 'Uint8Array', 'string'], + ['Buffer', + 'TypedArray', + 'DataView', + 'string'], buffer); } else { list = [ buffer ]; } } else if (!(list = fixBufferList(buffer))) { throw new ERR_INVALID_ARG_TYPE('buffer list arguments', - ['Buffer', 'string'], buffer); + ['Buffer', + 'TypedArray', + 'DataView', + 'string'], + buffer); } if (!connected) diff --git a/test/parallel/test-dgram-send-bad-arguments.js b/test/parallel/test-dgram-send-bad-arguments.js index ea51a4d16504f3..3e42f31b1af4b6 100644 --- a/test/parallel/test-dgram-send-bad-arguments.js +++ b/test/parallel/test-dgram-send-bad-arguments.js @@ -36,7 +36,7 @@ function checkArgs(connected) { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError', message: 'The "buffer" argument must be of type string or an instance ' + - 'of Buffer or Uint8Array. Received undefined' + 'of Buffer, TypedArray, or DataView. Received undefined' } ); @@ -90,7 +90,7 @@ function checkArgs(connected) { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError', message: 'The "buffer" argument must be of type string or an instance ' + - 'of Buffer or Uint8Array. Received type number (23)' + 'of Buffer, TypedArray, or DataView. Received type number (23)' } ); @@ -101,7 +101,8 @@ function checkArgs(connected) { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError', message: 'The "buffer list arguments" argument must be of type string ' + - 'or an instance of Buffer. Received an instance of Array' + 'or an instance of Buffer, TypedArray, or DataView. ' + + 'Received an instance of Array' } ); } diff --git a/test/parallel/test-dgram-send-default-host.js b/test/parallel/test-dgram-send-default-host.js index 4e2541783d2c34..bf8911c64f606e 100644 --- a/test/parallel/test-dgram-send-default-host.js +++ b/test/parallel/test-dgram-send-default-host.js @@ -12,6 +12,11 @@ const toSend = [Buffer.alloc(256, 'x'), 'hello']; const received = []; +let totalBytesSent = 0; +let totalBytesReceived = 0; +const arrayBufferViewsCount = common.getArrayBufferViews( + Buffer.from('') +).length; client.on('listening', common.mustCall(() => { const port = client.address().port; @@ -21,24 +26,47 @@ client.on('listening', common.mustCall(() => { client.send([toSend[2]], port); client.send(toSend[3], 0, toSend[3].length, port); - client.send(new Uint8Array(toSend[0]), 0, toSend[0].length, port); - client.send(new Uint8Array(toSend[1]), port); - client.send([new Uint8Array(toSend[2])], port); - client.send(new Uint8Array(Buffer.from(toSend[3])), - 0, toSend[3].length, port); + totalBytesSent += toSend.map((buf) => buf.length) + .reduce((a, b) => a + b, 0); + + for (const msgBuf of common.getArrayBufferViews(toSend[0])) { + client.send(msgBuf, 0, msgBuf.byteLength, port); + totalBytesSent += msgBuf.byteLength; + } + for (const msgBuf of common.getArrayBufferViews(toSend[1])) { + client.send(msgBuf, port); + totalBytesSent += msgBuf.byteLength; + } + for (const msgBuf of common.getArrayBufferViews(toSend[2])) { + client.send([msgBuf], port); + totalBytesSent += msgBuf.byteLength; + } })); client.on('message', common.mustCall((buf, info) => { received.push(buf.toString()); + totalBytesReceived += info.size; - if (received.length === toSend.length * 2) { - // The replies may arrive out of order -> sort them before checking. - received.sort(); - - const expected = toSend.concat(toSend).map(String).sort(); - assert.deepStrictEqual(received, expected); + if (totalBytesReceived === totalBytesSent) { client.close(); } -}, toSend.length * 2)); + // For every buffer in `toSend`, we send the raw Buffer, + // as well as every TypedArray in getArrayBufferViews() +}, toSend.length + (toSend.length - 1) * arrayBufferViewsCount)); + +client.on('close', common.mustCall((buf, info) => { + // The replies may arrive out of order -> sort them before checking. + received.sort(); + + const repeated = [...toSend]; + for (let i = 0; i < arrayBufferViewsCount; i++) { + repeated.push(...toSend.slice(0, 3)); + } + + assert.strictEqual(totalBytesSent, totalBytesReceived); + + const expected = repeated.map(String).sort(); + assert.deepStrictEqual(received, expected); +})); client.bind(0); diff --git a/test/parallel/test-dgram-udp6-send-default-host.js b/test/parallel/test-dgram-udp6-send-default-host.js index d801ca7e8dcd08..b0780824b3815a 100644 --- a/test/parallel/test-dgram-udp6-send-default-host.js +++ b/test/parallel/test-dgram-udp6-send-default-host.js @@ -15,26 +15,62 @@ const toSend = [Buffer.alloc(256, 'x'), 'hello']; const received = []; +let totalBytesSent = 0; +let totalBytesReceived = 0; +const arrayBufferViewLength = common.getArrayBufferViews( + Buffer.from('') +).length; client.on('listening', common.mustCall(() => { const port = client.address().port; + client.send(toSend[0], 0, toSend[0].length, port); client.send(toSend[1], port); client.send([toSend[2]], port); client.send(toSend[3], 0, toSend[3].length, port); + + totalBytesSent += toSend.map((buf) => buf.length) + .reduce((a, b) => a + b, 0); + + for (const msgBuf of common.getArrayBufferViews(toSend[0])) { + client.send(msgBuf, 0, msgBuf.byteLength, port); + totalBytesSent += msgBuf.byteLength; + } + for (const msgBuf of common.getArrayBufferViews(toSend[1])) { + client.send(msgBuf, port); + totalBytesSent += msgBuf.byteLength; + } + for (const msgBuf of common.getArrayBufferViews(toSend[2])) { + client.send([msgBuf], port); + totalBytesSent += msgBuf.byteLength; + } })); client.on('message', common.mustCall((buf, info) => { received.push(buf.toString()); + totalBytesReceived += info.size; - if (received.length === toSend.length) { - // The replies may arrive out of order -> sort them before checking. - received.sort(); - - const expected = toSend.map(String).sort(); - assert.deepStrictEqual(received, expected); + if (totalBytesReceived === totalBytesSent) { client.close(); } -}, toSend.length)); + // For every buffer in `toSend`, we send the raw Buffer, + // as well as every TypedArray in getArrayBufferViews() +}, toSend.length + (toSend.length - 1) * arrayBufferViewLength)); + +client.on('close', common.mustCall((buf, info) => { + // The replies may arrive out of order -> sort them before checking. + received.sort(); + + const repeated = [...toSend]; + for (let i = 0; i < arrayBufferViewLength; i++) { + // We get arrayBufferViews only for toSend[0..2]. + repeated.push(...toSend.slice(0, 3)); + } + + assert.strictEqual(totalBytesSent, totalBytesReceived); + + const expected = repeated.map(String).sort(); + assert.deepStrictEqual(received, expected); +})); client.bind(0); From 9918bdf5cb07f58d230522244a372cbb1b510956 Mon Sep 17 00:00:00 2001 From: rickyes Date: Fri, 10 Apr 2020 17:18:40 +0800 Subject: [PATCH 24/43] lib: replace charCodeAt with fixed Unicode PR-URL: https://github.com/nodejs/node/pull/32758 Reviewed-By: James M Snell Reviewed-By: Luigi Pinca Reviewed-By: Trivikram Kamat Reviewed-By: Zeyu Yang --- lib/internal/console/constructor.js | 14 ++++++++++---- lib/internal/constants.js | 4 ++++ lib/internal/trace_events_async_hooks.js | 8 ++++++-- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/lib/internal/console/constructor.js b/lib/internal/console/constructor.js index 458a5cd2738eea..dfad0811b25201 100644 --- a/lib/internal/console/constructor.js +++ b/lib/internal/console/constructor.js @@ -42,13 +42,19 @@ const { const { isTypedArray, isSet, isMap, isSetIterator, isMapIterator, } = require('internal/util/types'); +const { + CHAR_LOWERCASE_B, + CHAR_LOWERCASE_E, + CHAR_LOWERCASE_N, + CHAR_UPPERCASE_C, +} = require('internal/constants'); const kCounts = Symbol('counts'); const kTraceConsoleCategory = 'node,node.console'; -const kTraceCount = 'C'.charCodeAt(0); -const kTraceBegin = 'b'.charCodeAt(0); -const kTraceEnd = 'e'.charCodeAt(0); -const kTraceInstant = 'n'.charCodeAt(0); +const kTraceCount = CHAR_UPPERCASE_C; +const kTraceBegin = CHAR_LOWERCASE_B; +const kTraceEnd = CHAR_LOWERCASE_E; +const kTraceInstant = CHAR_LOWERCASE_N; const kSecond = 1000; const kMinute = 60 * kSecond; diff --git a/lib/internal/constants.js b/lib/internal/constants.js index dfa30bea306e65..bf539a9f37d134 100644 --- a/lib/internal/constants.js +++ b/lib/internal/constants.js @@ -8,6 +8,10 @@ module.exports = { CHAR_LOWERCASE_A: 97, /* a */ CHAR_UPPERCASE_Z: 90, /* Z */ CHAR_LOWERCASE_Z: 122, /* z */ + CHAR_UPPERCASE_C: 67, /* C */ + CHAR_LOWERCASE_B: 98, /* b */ + CHAR_LOWERCASE_E: 101, /* e */ + CHAR_LOWERCASE_N: 110, /* n */ // Non-alphabetic chars. CHAR_DOT: 46, /* . */ diff --git a/lib/internal/trace_events_async_hooks.js b/lib/internal/trace_events_async_hooks.js index 84c7745e47368f..9796f6866d96c8 100644 --- a/lib/internal/trace_events_async_hooks.js +++ b/lib/internal/trace_events_async_hooks.js @@ -10,12 +10,16 @@ const { const { trace } = internalBinding('trace_events'); const async_wrap = internalBinding('async_wrap'); const async_hooks = require('async_hooks'); +const { + CHAR_LOWERCASE_B, + CHAR_LOWERCASE_E, +} = require('internal/constants'); // Use small letters such that chrome://tracing groups by the name. // The behavior is not only useful but the same as the events emitted using // the specific C++ macros. -const kBeforeEvent = 'b'.charCodeAt(0); -const kEndEvent = 'e'.charCodeAt(0); +const kBeforeEvent = CHAR_LOWERCASE_B; +const kEndEvent = CHAR_LOWERCASE_E; const kTraceEventCategory = 'node,node.async_hooks'; const kEnabled = Symbol('enabled'); From d1d412b413d2f8febb1795459ff04c36c9c87f2c Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Sun, 14 Jun 2020 14:49:34 -0700 Subject: [PATCH 25/43] doc: use sentence-case for headings in docs PR-URL: https://github.com/nodejs/node/pull/33889 Refs: https://developers.google.com/style/capitalization#capitalization-in-titles-and-headings Refs: https://docs.microsoft.com/en-us/style-guide/capitalization Reviewed-By: Luigi Pinca Reviewed-By: James M Snell --- doc/api/addons.md | 6 +-- doc/api/async_hooks.md | 8 ++-- doc/api/buffer.md | 4 +- doc/api/child_process.md | 44 ++++++++++---------- doc/api/cli.md | 16 +++---- doc/api/cluster.md | 6 +-- doc/api/crypto.md | 18 ++++---- doc/api/debugger.md | 8 ++-- doc/api/deprecations.md | 14 +++---- doc/api/dgram.md | 10 ++--- doc/api/dns.md | 2 +- doc/api/documentation.md | 6 +-- doc/api/domain.md | 12 +++--- doc/api/embedding.md | 2 +- doc/api/errors.md | 20 ++++----- doc/api/esm.md | 58 +++++++++++++------------- doc/api/events.md | 14 +++---- doc/api/fs.md | 40 +++++++++--------- doc/api/globals.md | 2 +- doc/api/http2.md | 14 +++---- doc/api/index.md | 34 +++++++-------- doc/api/inspector.md | 4 +- doc/api/intl.md | 2 +- doc/api/modules.md | 24 +++++------ doc/api/n-api.md | 62 +++++++++++++-------------- doc/api/net.md | 2 +- doc/api/os.md | 18 ++++---- doc/api/perf_hooks.md | 2 +- doc/api/policy.md | 8 ++-- doc/api/process.md | 10 ++--- doc/api/querystring.md | 2 +- doc/api/readline.md | 4 +- doc/api/repl.md | 24 +++++------ doc/api/report.md | 4 +- doc/api/stream.md | 88 +++++++++++++++++++-------------------- doc/api/string_decoder.md | 2 +- doc/api/synopsis.md | 2 +- doc/api/timers.md | 4 +- doc/api/tls.md | 42 ++++++++++--------- doc/api/tracing.md | 6 +-- doc/api/url.md | 6 +-- doc/api/util.md | 12 +++--- doc/api/v8.md | 4 +- doc/api/vm.md | 6 +-- doc/api/worker_threads.md | 2 +- doc/api/zlib.md | 10 ++--- 46 files changed, 346 insertions(+), 342 deletions(-) diff --git a/doc/api/addons.md b/doc/api/addons.md index 34f49d9a24ac77..9a2b6d1a536359 100644 --- a/doc/api/addons.md +++ b/doc/api/addons.md @@ -1,4 +1,4 @@ -# C++ Addons +# C++ addons @@ -395,7 +395,7 @@ only the symbols exported by Node.js will be available. source image. Using this option, the Addon will have access to the full set of dependencies. -### Loading Addons using `require()` +### Loading addons using `require()` The filename extension of the compiled Addon binary is `.node` (as opposed to `.dll` or `.so`). The [`require()`][require] function is written to look for @@ -410,7 +410,7 @@ there is a file `addon.js` in the same directory as the binary `addon.node`, then [`require('addon')`][require] will give precedence to the `addon.js` file and load it instead. -## Native Abstractions for Node.js +## Native abstractions for Node.js Each of the examples illustrated in this document make direct use of the Node.js and V8 APIs for implementing Addons. The V8 API can, and has, changed diff --git a/doc/api/async_hooks.md b/doc/api/async_hooks.md index 9978070eba51a7..bd721dc13af5d0 100644 --- a/doc/api/async_hooks.md +++ b/doc/api/async_hooks.md @@ -1,4 +1,4 @@ -# Async Hooks +# Async hooks @@ -127,7 +127,7 @@ class MyAddedCallbacks extends MyAsyncCallbacks { const asyncHook = async_hooks.createHook(new MyAddedCallbacks()); ``` -##### Error Handling +##### Error handling If any `AsyncHook` callbacks throw, the application will print the stack trace and exit. The exit path does follow that of an uncaught exception, but @@ -201,7 +201,7 @@ be called again until enabled. For API consistency `disable()` also returns the `AsyncHook` instance. -#### Hook Callbacks +#### Hook callbacks Key events in the lifetime of asynchronous events have been categorized into four areas: instantiation, before/after the callback is called, and when the @@ -628,7 +628,7 @@ only on chained promises. That means promises not created by `then()`/`catch()` will not have the `before` and `after` callbacks fired on them. For more details see the details of the V8 [PromiseHooks][] API. -## JavaScript Embedder API +## JavaScript embedder API Library developers that handle their own asynchronous resources performing tasks like I/O, connection pooling, or managing callback queues may use the diff --git a/doc/api/buffer.md b/doc/api/buffer.md index 0da931480fc149..3684e3169b7829 100644 --- a/doc/api/buffer.md +++ b/doc/api/buffer.md @@ -53,7 +53,7 @@ const buf6 = Buffer.from('tést'); const buf7 = Buffer.from('tést', 'latin1'); ``` -## Buffers and Character Encodings +## Buffers and character encodings diff --git a/doc/api/child_process.md b/doc/api/child_process.md index 2f18567737e66a..ba29c0dc5f0490 100644 --- a/doc/api/child_process.md +++ b/doc/api/child_process.md @@ -1,4 +1,4 @@ -# Child Process +# Child process @@ -70,7 +70,7 @@ For certain use cases, such as automating shell scripts, the the synchronous methods can have significant impact on performance due to stalling the event loop while spawned processes complete. -## Asynchronous Process Creation +## Asynchronous process creation The [`child_process.spawn()`][], [`child_process.fork()`][], [`child_process.exec()`][], and [`child_process.execFile()`][] methods all follow the idiomatic asynchronous @@ -153,7 +153,7 @@ changes: * `env` {Object} Environment key-value pairs. **Default:** `process.env`. * `encoding` {string} **Default:** `'utf8'` * `shell` {string} Shell to execute the command with. See - [Shell Requirements][] and [Default Windows Shell][]. **Default:** + [Shell requirements][] and [Default Windows shell][]. **Default:** `'/bin/sh'` on Unix, `process.env.ComSpec` on Windows. * `timeout` {number} **Default:** `0` * `maxBuffer` {number} Largest amount of data in bytes allowed on stdout or @@ -270,8 +270,8 @@ changes: done on Windows. Ignored on Unix. **Default:** `false`. * `shell` {boolean|string} If `true`, runs `command` inside of a shell. Uses `'/bin/sh'` on Unix, and `process.env.ComSpec` on Windows. A different - shell can be specified as a string. See [Shell Requirements][] and - [Default Windows Shell][]. **Default:** `false` (no shell). + shell can be specified as a string. See [Shell requirements][] and + [Default Windows shell][]. **Default:** `false` (no shell). * `callback` {Function} Called with the output when process terminates. * `error` {Error} * `stdout` {string|Buffer} @@ -355,7 +355,7 @@ changes: **Default:** `process.execArgv`. * `serialization` {string} Specify the kind of serialization used for sending messages between processes. Possible values are `'json'` and `'advanced'`. - See [Advanced Serialization][] for more details. **Default:** `'json'`. + See [Advanced serialization][] for more details. **Default:** `'json'`. * `silent` {boolean} If `true`, stdin, stdout, and stderr of the child will be piped to the parent, otherwise they will be inherited from the parent, see the `'pipe'` and `'inherit'` options for [`child_process.spawn()`][]'s @@ -434,11 +434,11 @@ changes: * `gid` {number} Sets the group identity of the process (see setgid(2)). * `serialization` {string} Specify the kind of serialization used for sending messages between processes. Possible values are `'json'` and `'advanced'`. - See [Advanced Serialization][] for more details. **Default:** `'json'`. + See [Advanced serialization][] for more details. **Default:** `'json'`. * `shell` {boolean|string} If `true`, runs `command` inside of a shell. Uses `'/bin/sh'` on Unix, and `process.env.ComSpec` on Windows. A different - shell can be specified as a string. See [Shell Requirements][] and - [Default Windows Shell][]. **Default:** `false` (no shell). + shell can be specified as a string. See [Shell requirements][] and + [Default Windows shell][]. **Default:** `false` (no shell). * `windowsVerbatimArguments` {boolean} No quoting or escaping of arguments is done on Windows. Ignored on Unix. This is set to `true` automatically when `shell` is specified and is CMD. **Default:** `false`. @@ -699,7 +699,7 @@ see [V8 issue 7381](https://bugs.chromium.org/p/v8/issues/detail?id=7381). See also: [`child_process.exec()`][] and [`child_process.fork()`][]. -## Synchronous Process Creation +## Synchronous process creation The [`child_process.spawnSync()`][], [`child_process.execSync()`][], and [`child_process.execFileSync()`][] methods are synchronous and will block the @@ -755,8 +755,8 @@ changes: normally be created on Windows systems. **Default:** `false`. * `shell` {boolean|string} If `true`, runs `command` inside of a shell. Uses `'/bin/sh'` on Unix, and `process.env.ComSpec` on Windows. A different - shell can be specified as a string. See [Shell Requirements][] and - [Default Windows Shell][]. **Default:** `false` (no shell). + shell can be specified as a string. See [Shell requirements][] and + [Default Windows shell][]. **Default:** `false` (no shell). * Returns: {Buffer|string} The stdout from the command. The `child_process.execFileSync()` method is generally identical to @@ -804,7 +804,7 @@ changes: **Default:** `'pipe'`. * `env` {Object} Environment key-value pairs. **Default:** `process.env`. * `shell` {string} Shell to execute the command with. See - [Shell Requirements][] and [Default Windows Shell][]. **Default:** + [Shell requirements][] and [Default Windows shell][]. **Default:** `'/bin/sh'` on Unix, `process.env.ComSpec` on Windows. * `uid` {number} Sets the user identity of the process. (See setuid(2)). * `gid` {number} Sets the group identity of the process. (See setgid(2)). @@ -884,8 +884,8 @@ changes: **Default:** `'buffer'`. * `shell` {boolean|string} If `true`, runs `command` inside of a shell. Uses `'/bin/sh'` on Unix, and `process.env.ComSpec` on Windows. A different - shell can be specified as a string. See [Shell Requirements][] and - [Default Windows Shell][]. **Default:** `false` (no shell). + shell can be specified as a string. See [Shell requirements][] and + [Default Windows shell][]. **Default:** `false` (no shell). * `windowsVerbatimArguments` {boolean} No quoting or escaping of arguments is done on Windows. Ignored on Unix. This is set to `true` automatically when `shell` is specified and is CMD. **Default:** `false`. @@ -1025,7 +1025,7 @@ message might not be the same as what is originally sent. If the `serialization` option was set to `'advanced'` used when spawning the child process, the `message` argument can contain data that JSON is not able to represent. -See [Advanced Serialization][] for more details. +See [Advanced serialization][] for more details. ### `subprocess.channel` @@ -162,7 +162,7 @@ added: v12.12.0 > Stability: 1 - Experimental -Enable experimental Source Map V3 support for stack traces. +Enable experimental Source Map v3 support for stack traces. Currently, overriding `Error.prepareStackTrace` is ignored when the `--enable-source-maps` flag is set. @@ -1132,7 +1132,7 @@ added: v0.1.3 Print node's version. -## Environment Variables +## Environment variables ### `NODE_DEBUG=module[,…]` @@ -753,7 +753,7 @@ changes: `undefined` (inherits from parent process). * `serialization` {string} Specify the kind of serialization used for sending messages between processes. Possible values are `'json'` and `'advanced'`. - See [Advanced Serialization for `child_process`][] for more details. + See [Advanced serialization for `child_process`][] for more details. **Default:** `false`. * `silent` {boolean} Whether or not to send output to parent's stdio. **Default:** `false`. @@ -883,5 +883,5 @@ socket.on('data', (id) => { [`process` event: `'message'`]: process.html#process_event_message [`server.close()`]: net.html#net_event_close [`worker.exitedAfterDisconnect`]: #cluster_worker_exitedafterdisconnect -[Advanced Serialization for `child_process`]: child_process.html#child_process_advanced_serialization +[Advanced serialization for `child_process`]: child_process.html#child_process_advanced_serialization [Child Process module]: child_process.html#child_process_child_process_fork_modulepath_args_options diff --git a/doc/api/crypto.md b/doc/api/crypto.md index 5d32ea41e6e208..2e1f2981ee7232 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -1579,7 +1579,7 @@ added: v6.3.0 * Returns: {Object} An object containing commonly used constants for crypto and security related operations. The specific constants currently defined are - described in [Crypto Constants][]. + described in [Crypto constants][]. ### `crypto.DEFAULT_ENCODING` @@ -6,7 +6,7 @@ -The `dgram` module provides an implementation of UDP Datagram sockets. +The `dgram` module provides an implementation of UDP datagram sockets. ```js const dgram = require('dgram'); @@ -558,7 +558,7 @@ also use explicit scope in addresses, so only packets sent to a multicast address without specifying an explicit scope are affected by the most recent successful use of this call. -#### Examples: IPv6 Outgoing Multicast Interface +#### Example: IPv6 outgoing multicast interface On most systems, where scope format uses the interface name: @@ -580,7 +580,7 @@ socket.bind(1234, () => { }); ``` -#### Example: IPv4 Outgoing Multicast Interface +#### Example: IPv4 outgoing multicast interface All systems use an IP of the host on the desired physical interface: ```js @@ -591,7 +591,7 @@ socket.bind(1234, () => { }); ``` -#### Call Results +#### Call results A call on a socket that is not ready to send or no longer open may throw a *Not running* [`Error`][]. diff --git a/doc/api/dns.md b/doc/api/dns.md index 96df42c980fbe9..909d237f6777b9 100644 --- a/doc/api/dns.md +++ b/doc/api/dns.md @@ -625,7 +625,7 @@ That is, if attempting to resolve with the first server provided results in a subsequent servers provided. Fallback DNS servers will only be used if the earlier ones time out or result in some other error. -## DNS Promises API +## DNS promises API The `dns.promises` API provides an alternative set of asynchronous DNS methods that return `Promise` objects rather than using callbacks. The API is accessible diff --git a/doc/api/documentation.md b/doc/api/documentation.md index 7059c4d33e11ca..f15936b3f43279 100644 --- a/doc/api/documentation.md +++ b/doc/api/documentation.md @@ -1,4 +1,4 @@ -# About this Documentation +# About this documentation @@ -12,7 +12,7 @@ Node.js is a JavaScript runtime built on the [V8 JavaScript engine][]. Report errors in this documentation in [the issue tracker][]. See [the contributing guide][] for directions on how to submit pull requests. -## Stability Index +## Stability index @@ -43,7 +43,7 @@ Bugs or behavior changes may surprise end users when Experimental API modifications occur. To avoid surprises, use of an Experimental feature may need a command-line flag. Experimental features may also emit a [warning][]. -## JSON Output +## JSON output diff --git a/doc/api/domain.md b/doc/api/domain.md index fa3aef66b98241..b9219fbb4d714e 100644 --- a/doc/api/domain.md +++ b/doc/api/domain.md @@ -30,7 +30,7 @@ will be notified, rather than losing the context of the error in the `process.on('uncaughtException')` handler, or causing the program to exit immediately with an error code. -## Warning: Don't Ignore Errors! +## Warning: Don't ignore errors! @@ -199,7 +199,7 @@ are added to it. * `error.domainThrown` A boolean indicating whether the error was thrown, emitted, or passed to a bound callback function. -## Implicit Binding +## Implicit binding @@ -225,7 +225,7 @@ Implicit binding routes thrown errors and `'error'` events to the `Domain`. Implicit binding only takes care of thrown errors and `'error'` events. -## Explicit Binding +## Explicit binding @@ -432,9 +432,9 @@ d.run(() => { In this example, the `d.on('error')` handler will be triggered, rather than crashing the program. -## Domains and Promises +## Domains and promises -As of Node.js 8.0.0, the handlers of Promises are run inside the domain in +As of Node.js 8.0.0, the handlers of promises are run inside the domain in which the call to `.then()` or `.catch()` itself was made: ```js @@ -472,7 +472,7 @@ d2.run(() => { ``` Domains will not interfere with the error handling mechanisms for -Promises. In other words, no `'error'` event will be emitted for unhandled +promises. In other words, no `'error'` event will be emitted for unhandled `Promise` rejections. [`Error`]: errors.html#errors_class_error diff --git a/doc/api/embedding.md b/doc/api/embedding.md index a8fe2de72c9310..1a416f52c600de 100644 --- a/doc/api/embedding.md +++ b/doc/api/embedding.md @@ -1,4 +1,4 @@ -# C++ Embedder API +# C++ embedder API diff --git a/doc/api/errors.md b/doc/api/errors.md index fdc3cc1e6ecb42..6bc57d656a5a97 100644 --- a/doc/api/errors.md +++ b/doc/api/errors.md @@ -16,11 +16,11 @@ errors: Node.js detects an exceptional logic violation that should never occur. These are raised typically by the `assert` module. -All JavaScript and System errors raised by Node.js inherit from, or are +All JavaScript and system errors raised by Node.js inherit from, or are instances of, the standard JavaScript {Error} class and are guaranteed to provide *at least* the properties available on that class. -## Error Propagation and Interception +## Error propagation and interception @@ -186,7 +186,7 @@ circumstance of why the error occurred. `Error` objects capture a "stack trace" detailing the point in the code at which the `Error` was instantiated, and may provide a text description of the error. -All errors generated by Node.js, including all System and JavaScript errors, +All errors generated by Node.js, including all system and JavaScript errors, will either be instances of, or inherit from, the `Error` class. ### `new Error(message)` @@ -258,7 +258,7 @@ not capture any frames. The `error.code` property is a string label that identifies the kind of error. `error.code` is the most stable way to identify an error. It will only change between major versions of Node.js. In contrast, `error.message` strings may -change between any versions of Node.js. See [Node.js Error Codes][] for details +change between any versions of Node.js. See [Node.js error codes][] for details about specific codes. ### `error.message` @@ -497,7 +497,7 @@ If present, `error.port` is the network connection port that is not available. The `error.syscall` property is a string describing the [syscall][] that failed. -### Common System Errors +### Common system errors This is a list of system errors commonly-encountered when writing a Node.js program. For a comprehensive list, see the [`errno`(3) man page][]. @@ -574,7 +574,7 @@ require('url').parse(() => { }); Node.js will generate and throw `TypeError` instances *immediately* as a form of argument validation. -## Exceptions vs. Errors +## Exceptions vs. errors @@ -588,7 +588,7 @@ Some exceptions are *unrecoverable* at the JavaScript layer. Such exceptions will *always* cause the Node.js process to crash. Examples include `assert()` checks or `abort()` calls in the C++ layer. -## OpenSSL Errors +## OpenSSL errors Errors originating in `crypto` or `tls` are of class `Error`, and in addition to the standard `.code` and `.message` properties, may have some additional @@ -612,7 +612,7 @@ The OpenSSL library the error originates in. A human-readable string describing the reason for the error. -## Node.js Error Codes +## Node.js error codes ### `ERR_AMBIGUOUS_ARGUMENT` @@ -2339,7 +2339,7 @@ changes: A module file could not be resolved while attempting a [`require()`][] or `import` operation. -## Legacy Node.js Error Codes +## Legacy Node.js error codes > Stability: 0 - Deprecated. These error codes are either inconsistent, or have > been removed. @@ -2744,7 +2744,7 @@ such as `process.stdout.on('data')`. [`zlib`]: zlib.html [ES Module]: esm.html [ICU]: intl.html#intl_internationalization_support -[Node.js Error Codes]: #nodejs-error-codes +[Node.js error codes]: #nodejs-error-codes [V8's stack trace API]: https://github.com/v8/v8/wiki/Stack-Trace-API [WHATWG Supported Encodings]: util.html#util_whatwg_supported_encodings [WHATWG URL API]: url.html#url_the_whatwg_url_api diff --git a/doc/api/esm.md b/doc/api/esm.md index b92ae3138e97a4..9252eaeed2306a 100644 --- a/doc/api/esm.md +++ b/doc/api/esm.md @@ -1,4 +1,4 @@ -# ECMAScript Modules +# ECMAScript modules @@ -121,7 +121,7 @@ files in the package should be interpreted. Regardless of the value of the `"type"` field, `.mjs` files are always treated as ES modules and `.cjs` files are always treated as CommonJS. -### Package Scope and File Extensions +### Package scope and file extensions A folder containing a `package.json` file, and all subfolders below that folder down until the next folder containing another `package.json`, is considered a @@ -196,7 +196,7 @@ unspecified. ## Packages -### Package Entry Points +### Package entry points In a package’s `package.json` file, two fields can define entry points for a package: `"main"` and `"exports"`. The `"main"` field is supported in all @@ -216,7 +216,7 @@ CommonJS; `"main"` will be overridden by `"exports"` if it exists. As such fallback for legacy versions of Node.js that do not support the `"exports"` field. -[Conditional Exports][] can be used within `"exports"` to define different +[Conditional exports][] can be used within `"exports"` to define different package entry points per environment, including whether the package is referenced via `require` or via `import`. For more information about supporting both CommonJS and ES Modules in a single package please consult @@ -274,7 +274,7 @@ will encapsulation be lost but module consumers will be unable to `import feature from 'my-mod/feature'` as they will need to provide the full path `import feature from 'my-mod/feature/index.js`. -#### Main Entry Point Export +#### Main entry point export To set the main entry point for a package, it is advisable to define both `"exports"` and `"main"` in the package’s `package.json` file: @@ -298,7 +298,7 @@ package. It is not a strong encapsulation since a direct require of any absolute subpath of the package such as `require('/path/to/node_modules/pkg/subpath.js')` will still load `subpath.js`. -#### Subpath Exports +#### Subpath exports When using the `"exports"` field, custom subpaths can be defined along with the main entry point by treating the main entry point as the @@ -355,7 +355,7 @@ module inside the subfolder. Any modules which are not public should be moved to another folder to retain the encapsulation benefits of exports. -#### Package Exports Fallbacks +#### Package exports fallbacks For possible new specifier support in future, array fallbacks are supported for all invalid specifiers: @@ -372,7 +372,7 @@ supported for all invalid specifiers: Since `"not:valid"` is not a valid specifier, `"./submodule.js"` is used instead as the fallback, as if it were the only target. -#### Exports Sugar +#### Exports sugar If the `"."` export is the only export, the `"exports"` field provides sugar for this case being the direct `"exports"` field value. @@ -398,7 +398,7 @@ can be written: } ``` -#### Conditional Exports +#### Conditional exports Conditional exports provide a way to map to different paths depending on certain conditions. They are supported for both CommonJS and ES module imports. @@ -536,7 +536,7 @@ and in a CommonJS one. For example, this code will also work: const { something } = require('a-package/foo'); // Loads from ./foo.js. ``` -### Dual CommonJS/ES Module Packages +### Dual CommonJS/ES module packages Prior to the introduction of support for ES modules in Node.js, it was a common pattern for package authors to include both CommonJS and ES module JavaScript @@ -549,12 +549,12 @@ ignores) the top-level `"module"` field. Node.js can now run ES module entry points, and a package can contain both CommonJS and ES module entry points (either via separate specifiers such as `'pkg'` and `'pkg/es-module'`, or both at the same specifier via [Conditional -Exports][]). Unlike in the scenario where `"module"` is only used by bundlers, +exports][]). Unlike in the scenario where `"module"` is only used by bundlers, or ES module files are transpiled into CommonJS on the fly before evaluation by Node.js, the files referenced by the ES module entry point are evaluated as ES modules. -#### Dual Package Hazard +#### Dual package hazard When an application is using a package that provides both CommonJS and ES module sources, there is a risk of certain bugs if both versions of the package get @@ -577,7 +577,7 @@ all-CommonJS or all-ES module environments, respectively, and therefore is surprising to users. It also differs from the behavior users are familiar with when using transpilation via tools like [Babel][] or [`esm`][]. -#### Writing Dual Packages While Avoiding or Minimizing Hazards +#### Writing dual packages while avoiding or minimizing hazards First, the hazard described in the previous section occurs when a package contains both CommonJS and ES module sources and both sources are provided for @@ -607,11 +607,11 @@ following conditions: browsers. 1. The hazards described in the previous section are avoided or minimized. -##### Approach #1: Use an ES Module Wrapper +##### Approach #1: Use an ES module wrapper Write the package in CommonJS or transpile ES module sources into CommonJS, and create an ES module wrapper file that defines the named exports. Using -[Conditional Exports][], the ES module wrapper is used for `import` and the +[Conditional exports][], the ES module wrapper is used for `import` and the CommonJS entry point for `require`. @@ -689,7 +689,7 @@ stateless): } ``` -##### Approach #2: Isolate State +##### Approach #2: Isolate state A `package.json` file can define the separate CommonJS and ES module entry points directly: @@ -859,7 +859,7 @@ property: * `url` {string} The absolute `file:` URL of the module. -## Differences Between ES Modules and CommonJS +## Differences between ES modules and CommonJS ### Mandatory file extensions @@ -955,7 +955,7 @@ To include an ES module into CommonJS, use [`import()`][]. ### `import` statements An `import` statement can reference an ES module or a CommonJS module. Other -file types such as JSON or Native modules are not supported. For those, use +file types such as JSON or native modules are not supported. For those, use [`module.createRequire()`][]. `import` statements are permitted only in ES modules. For similar functionality @@ -991,9 +991,9 @@ It is also possible to [Dynamic `import()`][] is supported in both CommonJS and ES modules. It can be used to include ES module files from CommonJS code. -## CommonJS, JSON, and Native Modules +## CommonJS, JSON, and native modules -CommonJS, JSON, and Native modules can be used with +CommonJS, JSON, and native modules can be used with [`module.createRequire()`][]. ```js @@ -1043,7 +1043,7 @@ syncBuiltinESMExports(); fs.readFileSync === readFileSync; ``` -## Experimental JSON Modules +## Experimental JSON modules Currently importing JSON modules are only supported in the `commonjs` mode and are loaded using the CJS loader. [WHATWG JSON modules specification][] are @@ -1073,7 +1073,7 @@ node index.mjs # fails node --experimental-json-modules index.mjs # works ``` -## Experimental Wasm Modules +## Experimental Wasm modules Importing Web Assembly modules is supported under the `--experimental-wasm-modules` flag, allowing any `.wasm` files to be @@ -1097,7 +1097,7 @@ node --experimental-wasm-modules index.mjs would provide the exports interface for the instantiation of `module.wasm`. -## Experimental Top-Level `await` +## Experimental top-level `await` When the `--experimental-top-level-await` flag is provided, `await` may be used in the top level (outside of async functions) within modules. This implements @@ -1123,7 +1123,7 @@ node b.mjs # fails node --experimental-top-level-await b.mjs # works ``` -## Experimental Loaders +## Experimental loaders **Note: This API is currently being redesigned and will still change.** @@ -1148,11 +1148,11 @@ and parent URL. The module specifier is the string in an `import` statement or this one, or `undefined` if this is the main entry point for the application. The `conditions` property on the `context` is an array of conditions for -[Conditional Exports][] that apply to this resolution request. They can be used +[Conditional exports][] that apply to this resolution request. They can be used for looking up conditional mappings elsewhere or to modify the list when calling the default resolution logic. -The [current set of Node.js default conditions][Conditional Exports] will always +The [current set of Node.js default conditions][Conditional exports] will always be in the `context.conditions` list passed to the hook. If the hook wants to ensure Node.js-compatible resolution logic, all items from this default condition list **must** be passed through to the `defaultResolve` function. @@ -1512,7 +1512,7 @@ loaded from disk but before Node.js executes it; and so on for any `.coffee`, `.litcoffee` or `.coffee.md` files referenced via `import` statements of any loaded file. -## Resolution Algorithm +## Resolution algorithm ### Features @@ -1525,7 +1525,7 @@ The resolver has the following properties: * No folder mains * Bare specifier package resolution lookup through node_modules -### Resolver Algorithm +### Resolver algorithm The algorithm to load an ES module specifier is given through the **ESM_RESOLVE** method below. It returns the resolved URL for a @@ -1797,7 +1797,7 @@ success! [Babel]: https://babeljs.io/ [CommonJS]: modules.html -[Conditional Exports]: #esm_conditional_exports +[Conditional exports]: #esm_conditional_exports [Dynamic `import()`]: https://wiki.developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#Dynamic_Imports [ECMAScript-modules implementation]: https://github.com/nodejs/modules/blob/master/doc/plan-for-new-modules-implementation.md [ECMAScript Top-Level `await` proposal]: https://github.com/tc39/proposal-top-level-await/ diff --git a/doc/api/events.md b/doc/api/events.md index 9ceee6a30bf8bf..738e7e829753cc 100644 --- a/doc/api/events.md +++ b/doc/api/events.md @@ -74,7 +74,7 @@ myEmitter.on('event', (a, b) => { myEmitter.emit('event', 'a', 'b'); ``` -## Asynchronous vs. Synchronous +## Asynchronous vs. synchronous The `EventEmitter` calls all listeners synchronously in the order in which they were registered. This ensures the proper sequencing of @@ -167,7 +167,7 @@ myEmitter.emit('error', new Error('whoops!')); // Still throws and crashes Node.js ``` -## Capture Rejections of Promises +## Capture rejections of promises > Stability: 1 - captureRejections is experimental. @@ -968,7 +968,7 @@ There are two key differences between the Node.js `EventTarget` and the 2. In the Node.js `EventTarget`, if an event listener is an async function or returns a `Promise`, and the returned `Promise` rejects, the rejection will be automatically captured and handled the same way as a listener that - throws synchronously (see [`EventTarget` Error Handling][] for details). + throws synchronously (see [`EventTarget` error handling][] for details). ### `NodeEventTarget` vs. `EventEmitter` @@ -990,7 +990,7 @@ and cannot be used in place of an `EventEmitter` in most cases. 3. The `NodeEventTarget` supports `EventListener` objects as well as functions as handlers for all event types. -### Event Listener +### Event listener Event listeners registered for an event `type` may either be JavaScript functions or objects with a `handleEvent` property whose value is a function. @@ -1000,7 +1000,7 @@ passed to the `eventTarget.dispatchEvent()` function. Async functions may be used as event listeners. If an async handler function rejects, the rejection will be captured and be will handled as described in -[`EventTarget` Error Handling][]. +[`EventTarget` error handling][]. An error thrown by one handler function will not prevent the other handlers from being invoked. @@ -1042,7 +1042,7 @@ target.addEventListener('foo', handler3); target.addEventListener('foo', handler4, { once: true }); ``` -### `EventTarget` Error Handling +### `EventTarget` error handling When a registered event listener throws (or returns a Promise that rejects), by default the error will be forwarded to the `process.on('error')` event @@ -1401,7 +1401,7 @@ to the `EventTarget`. [`emitter.removeListener()`]: #events_emitter_removelistener_eventname_listener [`emitter.setMaxListeners(n)`]: #events_emitter_setmaxlisteners_n [`Event` Web API]: https://dom.spec.whatwg.org/#event -[`EventTarget` Error Handling]: #events_eventtarget_error_handling +[`EventTarget` error handling]: #events_eventtarget_error_handling [`EventTarget` Web API]: https://dom.spec.whatwg.org/#eventtarget [`fs.ReadStream`]: fs.html#fs_class_fs_readstream [`net.Server`]: net.html#net_class_net_server diff --git a/doc/api/fs.md b/doc/api/fs.md index 4ac95d58e20230..6b1c158b75a66c 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -1,4 +1,4 @@ -# File System +# File system @@ -242,7 +242,7 @@ fs.readFileSync(new URL('file:///C:/path/%5c')); \ or / characters */ ``` -## File Descriptors +## File descriptors On POSIX systems, for every process, the kernel maintains a table of currently open files and resources. Each open file is assigned a simple numeric @@ -277,7 +277,7 @@ at any given time so it is critical to close the descriptor when operations are completed. Failure to do so will result in a memory leak that will eventually cause an application to crash. -## Threadpool Usage +## Threadpool usage All file system APIs except `fs.FSWatcher()` and those that are explicitly synchronous use libuv's threadpool, which can have surprising and negative @@ -1030,7 +1030,7 @@ added: v0.11.13 The timestamp indicating the creation time of this file. -### Stat Time Values +### Stat time values The `atimeMs`, `mtimeMs`, `ctimeMs`, `birthtimeMs` properties are numeric values that hold the corresponding times in milliseconds. Their @@ -1155,7 +1155,7 @@ changes: Tests a user's permissions for the file or directory specified by `path`. The `mode` argument is an optional integer that specifies the accessibility -checks to be performed. Check [File Access Constants][] for possible values +checks to be performed. Check [File access constants][] for possible values of `mode`. It is possible to create a mask consisting of the bitwise OR of two or more values (e.g. `fs.constants.W_OK | fs.constants.R_OK`). @@ -1297,7 +1297,7 @@ changes: Synchronously tests a user's permissions for the file or directory specified by `path`. The `mode` argument is an optional integer that specifies the -accessibility checks to be performed. Check [File Access Constants][] for +accessibility checks to be performed. Check [File access constants][] for possible values of `mode`. It is possible to create a mask consisting of the bitwise OR of two or more values (e.g. `fs.constants.W_OK | fs.constants.R_OK`). @@ -1623,7 +1623,7 @@ through any other `fs` operation may lead to undefined behavior. Returns an object containing commonly used constants for file system operations. The specific constants currently defined are described in -[FS Constants][]. +[FS constants][]. ## `fs.copyFile(src, dest[, mode], callback)` @@ -4278,7 +4278,7 @@ It is unsafe to use `fs.writeFile()` multiple times on the same file without waiting for the callback. For this scenario, [`fs.createWriteStream()`][] is recommended. -### Using `fs.writeFile()` with File Descriptors +### Using `fs.writeFile()` with file descriptors When `file` is a file descriptor, the behavior is almost identical to directly calling `fs.write()` like: @@ -4885,7 +4885,7 @@ added: v10.0.0 Tests a user's permissions for the file or directory specified by `path`. The `mode` argument is an optional integer that specifies the accessibility -checks to be performed. Check [File Access Constants][] for possible values +checks to be performed. Check [File access constants][] for possible values of `mode`. It is possible to create a mask consisting of the bitwise OR of two or more values (e.g. `fs.constants.W_OK | fs.constants.R_OK`). @@ -5455,7 +5455,7 @@ Any specified `FileHandle` has to support writing. It is unsafe to use `fsPromises.writeFile()` multiple times on the same file without waiting for the `Promise` to be resolved (or rejected). -## FS Constants +## FS constants The following constants are exported by `fs.constants`. @@ -5479,7 +5479,7 @@ fs.open('/path/to/my/file', O_RDWR | O_CREAT | O_EXCL, (err, fd) => { }); ``` -### File Access Constants +### File access constants The following constants are meant for use with [`fs.access()`][]. @@ -5511,7 +5511,7 @@ The following constants are meant for use with [`fs.access()`][]. -### File Copy Constants +### File copy constants The following constants are meant for use with [`fs.copyFile()`][]. @@ -5539,7 +5539,7 @@ The following constants are meant for use with [`fs.copyFile()`][]. -### File Open Constants +### File open constants The following constants are meant for use with `fs.open()`. @@ -5633,7 +5633,7 @@ The following constants are meant for use with `fs.open()`. -### File Type Constants +### File type constants The following constants are meant for use with the [`fs.Stats`][] object's `mode` property for determining a file's type. @@ -5677,7 +5677,7 @@ The following constants are meant for use with the [`fs.Stats`][] object's -### File Mode Constants +### File mode constants The following constants are meant for use with the [`fs.Stats`][] object's `mode` property for determining the access permissions for a file. @@ -5737,7 +5737,7 @@ The following constants are meant for use with the [`fs.Stats`][] object's -## File System Flags +## File system flags The following flags are available wherever the `flag` option takes a string. @@ -5883,8 +5883,8 @@ the file contents. [`util.promisify()`]: util.html#util_util_promisify_original [Caveats]: #fs_caveats [Common System Errors]: errors.html#errors_common_system_errors -[FS Constants]: #fs_fs_constants_1 -[File Access Constants]: #fs_file_access_constants +[FS constants]: #fs_fs_constants_1 +[File access constants]: #fs_file_access_constants [MDN-Date]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date [MDN-Number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type [MSDN-Rel-Path]: https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#fully-qualified-vs-relative-paths diff --git a/doc/api/globals.md b/doc/api/globals.md index 096c73c72546fe..4844c86134e39c 100644 --- a/doc/api/globals.md +++ b/doc/api/globals.md @@ -1,4 +1,4 @@ -# Global Objects +# Global objects diff --git a/doc/api/http2.md b/doc/api/http2.md index 5a0f750a258a36..f4399cd91160fd 100644 --- a/doc/api/http2.md +++ b/doc/api/http2.md @@ -119,7 +119,7 @@ User code will not create `Http2Session` instances directly. Server-side new HTTP/2 connection is received. Client-side `Http2Session` instances are created using the `http2.connect()` method. -#### `Http2Session` and Sockets +#### `Http2Session` and sockets Every `Http2Session` instance is associated with exactly one [`net.Socket`][] or [`tls.TLSSocket`][] when it is created. When either the `Socket` or the @@ -2366,7 +2366,7 @@ client.close(); added: v8.4.0 --> -#### Error Codes for `RST_STREAM` and `GOAWAY` +#### Error codes for `RST_STREAM` and `GOAWAY` | Value | Name | Constant | @@ -2432,7 +2432,7 @@ added: v8.4.0 Returns a [HTTP/2 Settings Object][] containing the deserialized settings from the given `Buffer` as generated by `http2.getPackedSettings()`. -### Headers Object +### Headers object Headers are represented as own-properties on JavaScript objects. The property keys will be serialized to lower-case. Property values should be strings (if @@ -2480,7 +2480,7 @@ server.on('stream', (stream, headers) => { }); ``` -### Settings Object +### Settings object -* [About these Docs](documentation.html) -* [Usage & Example](synopsis.html) +* [About these docs](documentation.html) +* [Usage and example](synopsis.html)
-* [Assertion Testing](assert.html) -* [Async Hooks](async_hooks.html) +* [Assertion testing](assert.html) +* [Async hooks](async_hooks.html) * [Buffer](buffer.html) -* [C++ Addons](addons.html) -* [C/C++ Addons with N-API](n-api.html) -* [C++ Embedder API](embedding.html) -* [Child Processes](child_process.html) +* [C++ addons](addons.html) +* [C/C++ addons with N-API](n-api.html) +* [C++ embedder API](embedding.html) +* [Child processes](child_process.html) * [Cluster](cluster.html) -* [Command Line Options](cli.html) +* [Command line options](cli.html) * [Console](console.html) * [Crypto](crypto.html) * [Debugger](debugger.html) * [Deprecated APIs](deprecations.html) * [DNS](dns.html) * [Domain](domain.html) -* [ECMAScript Modules](esm.html) +* [ECMAScript modules](esm.html) * [Errors](errors.html) * [Events](events.html) -* [File System](fs.html) +* [File system](fs.html) * [Globals](globals.html) * [HTTP](http.html) * [HTTP/2](http2.html) @@ -39,28 +39,28 @@ * [Net](net.html) * [OS](os.html) * [Path](path.html) -* [Performance Hooks](perf_hooks.html) +* [Performance hooks](perf_hooks.html) * [Policies](policy.html) * [Process](process.html) * [Punycode](punycode.html) -* [Query Strings](querystring.html) +* [Query strings](querystring.html) * [QUIC](quic.html) * [Readline](readline.html) * [REPL](repl.html) * [Report](report.html) * [Stream](stream.html) -* [String Decoder](string_decoder.html) +* [String decoder](string_decoder.html) * [Timers](timers.html) * [TLS/SSL](tls.html) -* [Trace Events](tracing.html) +* [Trace events](tracing.html) * [TTY](tty.html) -* [UDP/Datagram](dgram.html) +* [UDP/datagram](dgram.html) * [URL](url.html) * [Utilities](util.html) * [V8](v8.html) * [VM](vm.html) * [WASI](wasi.html) -* [Worker Threads](worker_threads.html) +* [Worker threads](worker_threads.html) * [Zlib](zlib.html)
diff --git a/doc/api/inspector.md b/doc/api/inspector.md index ece84aa2c1f2a8..4f9501a5f861a7 100644 --- a/doc/api/inspector.md +++ b/doc/api/inspector.md @@ -188,7 +188,7 @@ to the run-time events. Apart from the debugger, various V8 Profilers are available through the DevTools protocol. -### CPU Profiler +### CPU profiler Here's an example showing how to use the [CPU Profiler][]: @@ -213,7 +213,7 @@ session.post('Profiler.enable', () => { }); ``` -### Heap Profiler +### Heap profiler Here's an example showing how to use the [Heap Profiler][]: diff --git a/doc/api/intl.md b/doc/api/intl.md index 25f7e61cd1808a..8b7dee489b4e9b 100644 --- a/doc/api/intl.md +++ b/doc/api/intl.md @@ -1,4 +1,4 @@ -# Internationalization Support +# Internationalization support diff --git a/doc/api/modules.md b/doc/api/modules.md index b54914e7506ee8..086055c7968193 100644 --- a/doc/api/modules.md +++ b/doc/api/modules.md @@ -78,7 +78,7 @@ Because `module` provides a `filename` property (normally equivalent to `__filename`), the entry point of the current application can be obtained by checking `require.main.filename`. -## Addenda: Package Manager Tips +## Addenda: Package manager tips @@ -139,7 +139,7 @@ Attempting to do so will throw [an error][]. The `.mjs` extension is reserved for [ECMAScript Modules][] which cannot be loaded via `require()`. See [ECMAScript Modules][] for more details. -## All Together... +## All together... @@ -254,7 +254,7 @@ allowing transitive dependencies to be loaded even when they would cause cycles. To have a module execute code multiple times, export a function, and call that function. -### Module Caching Caveats +### Module caching caveats @@ -269,7 +269,7 @@ them as different modules and will reload the file multiple times. For example, `require('./foo')` and `require('./FOO')` return two different objects, irrespective of whether or not `./foo` and `./FOO` are the same file. -## Core Modules +## Core modules @@ -347,7 +347,7 @@ in main, a.done = true, b.done = true Careful planning is required to allow cyclic module dependencies to work correctly within an application. -## File Modules +## File modules @@ -373,7 +373,7 @@ either be a core module or is loaded from a `node_modules` folder. If the given path does not exist, `require()` will throw an [`Error`][] with its `code` property set to `'MODULE_NOT_FOUND'`. -## Folders as Modules +## Folders as modules @@ -413,7 +413,7 @@ with the default error: Error: Cannot find module 'some-library' ``` -## Loading from `node_modules` Folders +## Loading from `node_modules` folders @@ -736,7 +736,7 @@ Returns an array containing the paths searched during resolution of `request` or `null` if the `request` string references a core module, for example `http` or `fs`. -## The `module` Object +## The `module` object @@ -940,7 +940,7 @@ Since `require()` returns the `module.exports`, and the `module` is typically *only* available within a specific module's code, it must be explicitly exported in order to be used. -## The `Module` Object +## The `Module` object @@ -1254,7 +1254,7 @@ The following process scheduling constants are exported by -### libuv Constants +### libuv constants diff --git a/doc/api/perf_hooks.md b/doc/api/perf_hooks.md index 5e573d038b869e..c9d2d2e99ef474 100644 --- a/doc/api/perf_hooks.md +++ b/doc/api/perf_hooks.md @@ -1,4 +1,4 @@ -# Performance Measurement APIs +# Performance measurement APIs diff --git a/doc/api/policy.md b/doc/api/policy.md index e7ccbaa9db08e6..05918500fcac21 100644 --- a/doc/api/policy.md +++ b/doc/api/policy.md @@ -49,7 +49,7 @@ node --experimental-policy=policy.json --policy-integrity="sha384-SggXRQHwCG8g+D ## Features -### Error Behavior +### Error behavior When a policy check fails, Node.js by default will throw an error. It is possible to change the error behavior to one of a few possibilities @@ -73,7 +73,7 @@ available to change the behavior: } ``` -### Integrity Checks +### Integrity checks Policy files must use integrity checks with Subresource Integrity strings compatible with the browser @@ -115,7 +115,7 @@ body for the resource which can be useful for local development. It is not recommended in production since it would allow unexpected alteration of resources to be considered valid. -### Dependency Redirection +### Dependency redirection An application may need to ship patched versions of modules or to prevent modules from allowing all modules access to all other modules. Redirection @@ -164,7 +164,7 @@ module to load any specifier without redirection. This can be useful for local development and may have some valid usage in production, but should be used only with care after auditing a module to ensure its behavior is valid. -#### Example: Patched Dependency +#### Example: Patched dependency Redirected dependencies can provide attenuated or modified functionality as fits the application. For example, log data about timing of function durations by diff --git a/doc/api/process.md b/doc/api/process.md index 0b136d0074fc87..de964f4f1e9341 100644 --- a/doc/api/process.md +++ b/doc/api/process.md @@ -12,7 +12,7 @@ accessed using `require()`: const process = require('process'); ``` -## Process Events +## Process events The `process` object is an instance of [`EventEmitter`][]. @@ -122,7 +122,7 @@ not be the same as what is originally sent. If the `serialization` option was set to `advanced` used when spawning the process, the `message` argument can contain data that JSON is not able to represent. -See [Advanced Serialization for `child_process`][] for more details. +See [Advanced serialization for `child_process`][] for more details. ### Event: `'multipleResolves'` @@ -2555,7 +2555,7 @@ Will generate an object similar to: unicode: '11.0' } ``` -## Exit Codes +## Exit codes Node.js will normally exit with a `0` status code when no more async operations are pending. The following status codes are used in other @@ -2636,7 +2636,7 @@ cases: [`require.resolve()`]: modules.html#modules_require_resolve_request_options [`subprocess.kill()`]: child_process.html#child_process_subprocess_kill_signal [`v8.setFlagsFromString()`]: v8.html#v8_v8_setflagsfromstring_flags -[Advanced Serialization for `child_process`]: child_process.html#child_process_advanced_serialization +[Advanced serialization for `child_process`]: child_process.html#child_process_advanced_serialization [Android building]: https://github.com/nodejs/node/blob/master/BUILDING.md#androidandroid-based-devices-eg-firefox-os [Child Process]: child_process.html [Cluster]: cluster.html diff --git a/doc/api/querystring.md b/doc/api/querystring.md index c7fafd23d955b8..0797e3ee220d53 100644 --- a/doc/api/querystring.md +++ b/doc/api/querystring.md @@ -1,4 +1,4 @@ -# Query String +# Query string diff --git a/doc/api/readline.md b/doc/api/readline.md index 04233f1a591d44..e20454a6cb399c 100644 --- a/doc/api/readline.md +++ b/doc/api/readline.md @@ -536,7 +536,7 @@ the best compatibility if it defines an `output.columns` property and emits a `'resize'` event on the `output` if or when the columns ever change ([`process.stdout`][] does this automatically when it is a TTY). -### Use of the `completer` Function +### Use of the `completer` function The `completer` function takes the current line entered by the user as an argument, and returns an `Array` with 2 entries: @@ -661,7 +661,7 @@ rl.on('line', (line) => { }); ``` -## Example: Read File Stream Line-by-Line +## Example: Read file stream line-by-Line A common use case for `readline` is to consume an input file one line at a time. The easiest way to do so is leveraging the [`fs.ReadStream`][] API as diff --git a/doc/api/repl.md b/doc/api/repl.md index 4a3e166e305e7b..614c577d2cfd44 100644 --- a/doc/api/repl.md +++ b/doc/api/repl.md @@ -12,7 +12,7 @@ It can be accessed using: const repl = require('repl'); ``` -## Design and Features +## Design and features The `repl` module exports the [`repl.REPLServer`][] class. While running, instances of [`repl.REPLServer`][] will accept individual lines of user input, @@ -28,7 +28,7 @@ recovery, and customizable evaluation functions. Terminals that do not support ANSI styles and Emacs-style line editing automatically fall back to a limited feature set. -### Commands and Special Keys +### Commands and special keys The following special commands are supported by all REPL instances: @@ -72,14 +72,14 @@ The following key combinations in the REPL have these special effects: For key bindings related to the reverse-i-search, see [`reverse-i-search`][]. For all other key bindings, see [TTY keybindings][]. -### Default Evaluation +### Default evaluation By default, all instances of [`repl.REPLServer`][] use an evaluation function that evaluates JavaScript expressions and provides access to Node.js built-in modules. This default behavior can be overridden by passing in an alternative evaluation function when the [`repl.REPLServer`][] instance is created. -#### JavaScript Expressions +#### JavaScript expressions The default evaluator supports direct evaluation of JavaScript expressions: @@ -96,7 +96,7 @@ Unless otherwise scoped within blocks or functions, variables declared either implicitly or using the `const`, `let`, or `var` keywords are declared at the global scope. -#### Global and Local Scope +#### Global and local scope The default evaluator provides access to any variables that exist in the global scope. It is possible to expose a variable to the REPL explicitly by assigning @@ -132,7 +132,7 @@ Object.defineProperty(r.context, 'm', { }); ``` -#### Accessing Core Node.js Modules +#### Accessing core Node.js modules The default evaluator will automatically load Node.js core modules into the REPL environment when used. For instance, unless otherwise declared as a @@ -143,7 +143,7 @@ global or scoped variable, the input `fs` will be evaluated on-demand as > fs.createReadStream('./some/file'); ``` -#### Global Uncaught Exceptions +#### Global uncaught exceptions @@ -577,7 +577,7 @@ NODE_OPTIONS="--report-uncaught-exception \ Specific API documentation can be found under [`process API documentation`][] section. -## Interaction with Workers +## Interaction with workers @@ -120,8 +120,8 @@ that implements an HTTP server: const http = require('http'); const server = http.createServer((req, res) => { - // `req` is an http.IncomingMessage, which is a Readable Stream. - // `res` is an http.ServerResponse, which is a Writable Stream. + // `req` is an http.IncomingMessage, which is a readable stream. + // `res` is an http.ServerResponse, which is a writable stream. let body = ''; // Get the data as utf8 strings. @@ -176,9 +176,9 @@ are not required to implement the stream interfaces directly and will generally have no reason to call `require('stream')`. Developers wishing to implement new types of streams should refer to the -section [API for Stream Implementers][]. +section [API for stream implementers][]. -### Writable Streams +### Writable streams Writable streams are an abstraction for a *destination* to which data is written. @@ -648,7 +648,7 @@ write('hello', () => { A `Writable` stream in object mode will always ignore the `encoding` argument. -### Readable Streams +### Readable streams Readable streams are an abstraction for a *source* from which data is consumed. @@ -667,7 +667,7 @@ Examples of `Readable` streams include: All [`Readable`][] streams implement the interface defined by the `stream.Readable` class. -#### Two Reading Modes +#### Two reading modes `Readable` streams effectively operate in one of two modes: flowing and paused. These modes are separate from [object mode][object-mode]. @@ -718,7 +718,7 @@ stop flowing, and the data to be consumed via removed, then the stream will start flowing again if there is a [`'data'`][] event handler. -#### Three States +#### Three states The "two modes" of operation for a `Readable` stream are a simplified abstraction for the more complicated internal state management that is happening @@ -761,7 +761,7 @@ pass.resume(); // Must be called to make stream emit 'data'. While `readable.readableFlowing` is `false`, data may be accumulating within the stream's internal buffer. -#### Choose One API Style +#### Choose one API style The `Readable` stream API evolved across multiple Node.js versions and provides multiple methods of consuming stream data. In general, developers should choose @@ -1183,7 +1183,7 @@ added: v9.4.0 * {boolean} This property reflects the current state of a `Readable` stream as described -in the [Stream Three States][] section. +in the [Three states][] section. ##### `readable.readableHighWaterMark` @@ -1775,7 +1775,7 @@ on the type of stream being created, as detailed in the chart below: The implementation code for a stream should *never* call the "public" methods 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 +[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()`, @@ -1785,7 +1785,7 @@ 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 +### Simplified construction @@ -1811,7 +1811,7 @@ const myWritable = new Writable({ }); ``` -### Implementing a Writable Stream +### Implementing a writable stream The `stream.Writable` class is extended to implement a [`Writable`][] stream. @@ -1897,7 +1897,7 @@ function MyWritable(options) { util.inherits(MyWritable, Writable); ``` -Or, using the Simplified Constructor approach: +Or, using the simplified constructor approach: ```js const { Writable } = require('stream'); @@ -2064,7 +2064,7 @@ This optional function will be called before the stream closes, delaying the `'finish'` event until `callback` is called. This is useful to close resources or write buffered data before a stream ends. -#### Errors While Writing +#### Errors while writing Errors occurring during the processing of the [`writable._write()`][], [`writable._writev()`][] and [`writable._final()`][] methods must be propagated @@ -2089,7 +2089,7 @@ const myWritable = new Writable({ }); ``` -#### An Example Writable Stream +#### An example writable stream The following illustrates a rather simplistic (and somewhat pointless) custom `Writable` stream implementation. While this specific `Writable` stream instance @@ -2110,7 +2110,7 @@ class MyWritable extends Writable { } ``` -#### Decoding buffers in a Writable Stream +#### Decoding buffers in a writable stream Decoding buffers is a common task, for instance, when using transformers whose input is a string. This is not a trivial process when using multi-byte @@ -2150,7 +2150,7 @@ w.end(euro[1]); console.log(w.data); // currency: € ``` -### Implementing a Readable Stream +### Implementing a readable stream The `stream.Readable` class is extended to implement a [`Readable`][] stream. @@ -2218,7 +2218,7 @@ function MyReadable(options) { util.inherits(MyReadable, Readable); ``` -Or, using the Simplified Constructor approach: +Or, using the simplified constructor approach: ```js const { Readable } = require('stream'); @@ -2408,7 +2408,7 @@ For streams not operating in object mode, if the `chunk` parameter of `readable.push()` is `undefined`, it will be treated as empty string or buffer. See [`readable.push('')`][] for more information. -#### Errors While Reading +#### Errors while reading Errors occurring during processing of the [`readable._read()`][] must be propagated through the [`readable.destroy(err)`][readable-_destroy] method. @@ -2430,7 +2430,7 @@ const myReadable = new Readable({ }); ``` -#### An Example Counting Stream +#### An example counting stream @@ -2460,7 +2460,7 @@ class Counter extends Readable { } ``` -### Implementing a Duplex Stream +### Implementing a duplex stream A [`Duplex`][] stream is one that implements both [`Readable`][] and [`Writable`][], such as a TCP socket connection. @@ -2527,7 +2527,7 @@ function MyDuplex(options) { util.inherits(MyDuplex, Duplex); ``` -Or, using the Simplified Constructor approach: +Or, using the simplified constructor approach: ```js const { Duplex } = require('stream'); @@ -2582,7 +2582,7 @@ pipeline( ); ``` -#### An Example Duplex Stream +#### An example duplex stream The following illustrates a simple example of a `Duplex` stream that wraps a hypothetical lower-level source object to which data can be written, and @@ -2622,7 +2622,7 @@ The most important aspect of a `Duplex` stream is that the `Readable` and `Writable` sides operate independently of one another despite co-existing within a single object instance. -#### Object Mode Duplex Streams +#### Object mode duplex streams For `Duplex` streams, `objectMode` can be set exclusively for either the `Readable` or `Writable` side using the `readableObjectMode` and @@ -2663,7 +2663,7 @@ myTransform.write(100); // Prints: 64 ``` -### Implementing a Transform Stream +### Implementing a transform stream A [`Transform`][] stream is a [`Duplex`][] stream where the output is computed in some way from the input. Examples include [zlib][] streams or [crypto][] @@ -2722,7 +2722,7 @@ function MyTransform(options) { util.inherits(MyTransform, Transform); ``` -Or, using the Simplified Constructor approach: +Or, using the simplified constructor approach: ```js const { Transform } = require('stream'); @@ -2837,11 +2837,11 @@ stream that simply passes the input bytes across to the output. Its purpose is primarily for examples and testing, but there are some use cases where `stream.PassThrough` is useful as a building block for novel sorts of streams. -## Additional Notes +## Additional notes -### Streams Compatibility with Async Generators and Async Iterators +### Streams compatibility with async generators and async iterators With the support of async generators and iterators in JavaScript, async generators are effectively a first-class language-level stream construct at @@ -2850,7 +2850,7 @@ this point. Some common interop cases of using Node.js streams with async generators and async iterators are provided below. -#### Consuming Readable Streams with Async Iterators +#### Consuming readable streams with async iterators ```js (async function() { @@ -2863,9 +2863,9 @@ and async iterators are provided below. Async iterators register a permanent error handler on the stream to prevent any unhandled post-destroy errors. -#### Creating Readable Streams with Async Generators +#### Creating readable streams with async generators -A Node.js Readable Stream can be created from an asynchronous generator using +A Node.js readable stream can be created from an asynchronous generator using the `Readable.from()` utility method: ```js @@ -2884,7 +2884,7 @@ readable.on('data', (chunk) => { }); ``` -#### Piping to Writable Streams from Async Iterators +#### Piping to writable streams from async iterators In the scenario of writing to a writable stream from an async iterator, ensure the correct handling of backpressure and errors. @@ -2964,7 +2964,7 @@ const writable = fs.createWriteStream('./file'); -### Compatibility with Older Node.js Versions +### Compatibility with older Node.js versions @@ -3105,8 +3105,8 @@ contain multi-byte characters. [`writable.uncork()`]: #stream_writable_uncork [`writable.writableFinished`]: #stream_writable_writablefinished [`zlib.createDeflate()`]: zlib.html#zlib_zlib_createdeflate_options -[API for Stream Consumers]: #stream_api_for_stream_consumers -[API for Stream Implementers]: #stream_api_for_stream_implementers +[API for stream consumers]: #stream_api_for_stream_consumers +[API for stream implementers]: #stream_api_for_stream_implementers [Compatibility]: #stream_compatibility_with_older_node_js_versions [HTTP requests, on the client]: http.html#http_class_http_clientrequest [HTTP responses, on the server]: http.html#http_class_http_serverresponse @@ -3135,7 +3135,7 @@ contain multi-byte characters. [stream-resume]: #stream_readable_resume [stream-uncork]: #stream_writable_uncork [stream-write]: #stream_writable_write_chunk_encoding_callback -[Stream Three States]: #stream_three_states +[Three states]: #stream_three_states [writable-_construct]: #stream_writable_construct_callback [writable-_destroy]: #stream_writable_destroy_err_callback [writable-destroy]: #stream_writable_destroy_error diff --git a/doc/api/string_decoder.md b/doc/api/string_decoder.md index 54fcc876c9a9e9..5cd6121d50ddb9 100644 --- a/doc/api/string_decoder.md +++ b/doc/api/string_decoder.md @@ -1,4 +1,4 @@ -# String Decoder +# String decoder diff --git a/doc/api/synopsis.md b/doc/api/synopsis.md index c296ffb0fa43c7..8ae933a499a16c 100644 --- a/doc/api/synopsis.md +++ b/doc/api/synopsis.md @@ -1,4 +1,4 @@ -# Usage & Example +# Usage and example ## Usage diff --git a/doc/api/timers.md b/doc/api/timers.md index 3dcd1f4e475fbb..cb26e4b16a3bae 100644 --- a/doc/api/timers.md +++ b/doc/api/timers.md @@ -123,7 +123,7 @@ Calling `timeout.unref()` creates an internal timer that will wake the Node.js event loop. Creating too many of these can adversely impact performance of the Node.js application. -## Scheduling Timers +## Scheduling timers A timer in Node.js is an internal construct that calls a given function after a certain period of time. When a timer's function is called varies depending on @@ -226,7 +226,7 @@ setTimeoutPromise(40, 'foobar').then((value) => { }); ``` -## Cancelling Timers +## Cancelling timers The [`setImmediate()`][], [`setInterval()`][], and [`setTimeout()`][] methods each return objects that represent the scheduled timers. These can be used to diff --git a/doc/api/tls.md b/doc/api/tls.md index 96e054e533a666..e294a75f919a3d 100644 --- a/doc/api/tls.md +++ b/doc/api/tls.md @@ -12,7 +12,7 @@ The module can be accessed using: const tls = require('tls'); ``` -## TLS/SSL Concepts +## TLS/SSL concepts The TLS/SSL is a public/private key infrastructure (PKI). For most common cases, each client and server must have a *private key*. @@ -64,11 +64,11 @@ Where: * `certfile`: is a concatenation of all Certificate Authority (CA) certs into a single file, e.g. `cat ca1-cert.pem ca2-cert.pem > ca-cert.pem` -### Perfect Forward Secrecy +### Perfect forward secrecy -The term "[Forward Secrecy][]" or "Perfect Forward Secrecy" describes a feature +The term _[forward secrecy][]_ or _perfect forward secrecy_ describes a feature of key-agreement (i.e., key-exchange) methods. That is, the server and client keys are used to negotiate new temporary keys that are used specifically and only for the current communication session. Practically, this means that even @@ -76,11 +76,11 @@ if the server's private key is compromised, communication can only be decrypted by eavesdroppers if the attacker manages to obtain the key-pair specifically generated for the session. -Perfect Forward Secrecy is achieved by randomly generating a key pair for +Perfect forward secrecy is achieved by randomly generating a key pair for key-agreement on every TLS/SSL handshake (in contrast to using the same key for all sessions). Methods implementing this technique are called "ephemeral". -Currently two methods are commonly used to achieve Perfect Forward Secrecy (note +Currently two methods are commonly used to achieve perfect forward secrecy (note the character "E" appended to the traditional abbreviations): * [DHE][]: An ephemeral version of the Diffie Hellman key-agreement protocol. @@ -90,7 +90,7 @@ the character "E" appended to the traditional abbreviations): Ephemeral methods may have some performance drawbacks, because key generation is expensive. -To use Perfect Forward Secrecy using `DHE` with the `tls` module, it is required +To use perfect forward secrecy using `DHE` with the `tls` module, it is required to generate Diffie-Hellman parameters and specify them with the `dhparam` option to [`tls.createSecureContext()`][]. The following illustrates the use of the OpenSSL command-line interface to generate such parameters: @@ -99,12 +99,12 @@ the OpenSSL command-line interface to generate such parameters: openssl dhparam -outform PEM -out dhparam.pem 2048 ``` -If using Perfect Forward Secrecy using `ECDHE`, Diffie-Hellman parameters are +If using perfect forward secrecy using `ECDHE`, Diffie-Hellman parameters are not required and a default ECDHE curve will be used. The `ecdhCurve` property can be used when creating a TLS Server to specify the list of names of supported curves to use, see [`tls.createServer()`][] for more info. -Perfect Forward Secrecy was optional up to TLSv1.2, but it is not optional for +Perfect forward secrecy was optional up to TLSv1.2, but it is not optional for TLSv1.3, because all TLSv1.3 cipher suites use ECDHE. ### ALPN and SNI @@ -175,13 +175,15 @@ understanding of the implications and risks. TLSv1.3 does not support renegotiation. -### Session Resumption +### Session resumption Establishing a TLS session can be relatively slow. The process can be sped up by saving and later reusing the session state. There are several mechanisms to do so, discussed here from oldest to newest (and preferred). -***Session Identifiers*** Servers generate a unique ID for new connections and +#### Session identifiers + +Servers generate a unique ID for new connections and send it to the client. Clients and servers save the session state. When reconnecting, clients send the ID of their saved session state and if the server also has the state for that ID, it can agree to use it. Otherwise, the server @@ -200,7 +202,9 @@ reuse sessions. To reuse sessions across load balancers or cluster workers, servers must use a shared session cache (such as Redis) in their session handlers. -***Session Tickets*** The servers encrypt the entire session state and send it +#### Session tickets + +The servers encrypt the entire session state and send it to the client as a "ticket". When reconnecting, the state is sent to the server in the initial connection. This mechanism avoids the need for server-side session cache. If the server doesn't use the ticket, for any reason (failure @@ -267,7 +271,7 @@ Subsequent connections should say "Reused", for example: Reused, TLSv1.2, Cipher is ECDHE-RSA-AES128-GCM-SHA256 ``` -## Modifying the Default TLS Cipher suite +## Modifying the default TLS cipher suite Node.js is built with a default suite of enabled and disabled TLS ciphers. This default cipher list can be configured when building Node.js to allow @@ -340,8 +344,8 @@ of an application. The `--tls-cipher-list` switch and `ciphers` option should by used only if absolutely necessary. The default cipher suite prefers GCM ciphers for [Chrome's 'modern -cryptography' setting][] and also prefers ECDHE and DHE ciphers for Perfect -Forward Secrecy, while offering *some* backward compatibility. +cryptography' setting][] and also prefers ECDHE and DHE ciphers for perfect +forward secrecy, while offering *some* backward compatibility. 128 bit AES is preferred over 192 and 256 bit AES in light of [specific attacks affecting larger AES key sizes][]. @@ -892,7 +896,7 @@ added: v5.0.0 * Returns: {Object} Returns an object representing the type, name, and size of parameter of -an ephemeral key exchange in [Perfect Forward Secrecy][] on a client +an ephemeral key exchange in [perfect forward secrecy][] on a client connection. It returns an empty object when the key exchange is not ephemeral. As this is only supported on a client socket; `null` is returned if called on a server socket. The supported types are `'DH'` and `'ECDH'`. The @@ -934,7 +938,7 @@ If the full certificate chain was requested, each certificate will include an `issuerCertificate` property containing an object representing its issuer's certificate. -#### Certificate Object +#### Certificate object > Stability: 1 - Experimental -Trace Event provides a mechanism to centralize tracing information generated by -V8, Node.js core, and userspace code. +The `trace_events` module provides a mechanism to centralize tracing information +generated by V8, Node.js core, and userspace code. Tracing can be enabled with the `--trace-event-categories` command-line flag or by using the `trace_events` module. The `--trace-event-categories` flag diff --git a/doc/api/url.md b/doc/api/url.md index 2fddd749d0d06b..d2d512a17d8ded 100644 --- a/doc/api/url.md +++ b/doc/api/url.md @@ -11,7 +11,7 @@ accessed using: const url = require('url'); ``` -## URL Strings and URL Objects +## URL strings and URL objects A URL string is a structured string containing multiple meaningful components. When parsed, a URL object is returned containing properties for each of these @@ -418,7 +418,7 @@ console.log(myURL.href); Invalid URL protocol values assigned to the `protocol` property are ignored. -##### Special Schemes +##### Special schemes @@ -1054,7 +1054,7 @@ while (buffer = getNextChunkSomehow()) { string += decoder.decode(); // end-of-stream ``` -### WHATWG Supported Encodings +### WHATWG supported encodings Per the [WHATWG Encoding Standard][], the encodings supported by the `TextDecoder` API are outlined in the tables below. For each encoding, @@ -1063,7 +1063,7 @@ one or more aliases may be used. Different Node.js build configurations support different sets of encodings. (see [Internationalization][]) -#### Encodings Supported by Default (With Full ICU Data) +#### Encodings supported by default (with full ICU data) | Encoding | Aliases | | ----------------- | -------------------------------- | @@ -1102,7 +1102,7 @@ Different Node.js build configurations support different sets of encodings. | `'shift_jis'` | `'csshiftjis'`, `'ms932'`, `'ms_kanji'`, `'shift-jis'`, `'sjis'`, `'windows-31j'`, `'x-sjis'` | | `'euc-kr'` | `'cseuckr'`, `'csksc56011987'`, `'iso-ir-149'`, `'korean'`, `'ks_c_5601-1987'`, `'ks_c_5601-1989'`, `'ksc5601'`, `'ksc_5601'`, `'windows-949'` | -#### Encodings Supported when Node.js is built with the `small-icu` option +#### Encodings supported when Node.js is built with the `small-icu` option | Encoding | Aliases | | ----------- | --------------------------------- | @@ -1110,7 +1110,7 @@ Different Node.js build configurations support different sets of encodings. | `'utf-16le'` | `'utf-16'` | | `'utf-16be'` | | -#### Encodings Supported when ICU is disabled +#### Encodings supported when ICU is disabled | Encoding | Aliases | | ----------- | --------------------------------- | @@ -2434,7 +2434,7 @@ util.log('Timestamped message.'); [`util.types.isNativeError()`]: #util_util_types_isnativeerror_value [`util.types.isSharedArrayBuffer()`]: #util_util_types_issharedarraybuffer_value [Common System Errors]: errors.html#errors_common_system_errors -[Custom inspection functions on Objects]: #util_custom_inspection_functions_on_objects +[Custom inspection functions on objects]: #util_custom_inspection_functions_on_objects [Custom promisified functions]: #util_custom_promisified_functions [Customizing `util.inspect` colors]: #util_customizing_util_inspect_colors [Internationalization]: intl.html diff --git a/doc/api/v8.md b/doc/api/v8.md index ec6a6ea9a0508f..531612330f061b 100644 --- a/doc/api/v8.md +++ b/doc/api/v8.md @@ -231,7 +231,7 @@ DevTools. The JSON schema is undocumented and specific to the V8 engine, and may change from one version of V8 to the next. A heap snapshot is specific to a single V8 isolate. When using -[Worker Threads][], a heap snapshot generated from the main thread will +[worker threads][], a heap snapshot generated from the main thread will not contain any information about the workers, and vice versa. ```js @@ -519,4 +519,4 @@ A subclass of [`Deserializer`][] corresponding to the format written by [`vm.Script`]: vm.html#vm_new_vm_script_code_options [HTML structured clone algorithm]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm [V8]: https://developers.google.com/v8/ -[Worker Threads]: worker_threads.html +[worker threads]: worker_threads.html diff --git a/doc/api/vm.md b/doc/api/vm.md index 675a88ca05ec86..f5fcd7d8bfb1ab 100644 --- a/doc/api/vm.md +++ b/doc/api/vm.md @@ -1,4 +1,4 @@ -# VM (Executing JavaScript) +# VM (executing JavaScript) @@ -1172,7 +1172,7 @@ local scope, so the value `localVar` is changed. In this way `vm.runInThisContext()` is much like an [indirect `eval()` call][], e.g. `(0,eval)('code')`. -## Example: Running an HTTP Server within a VM +## Example: Running an HTTP server within a VM When using either [`script.runInThisContext()`][] or [`vm.runInThisContext()`][], the code is executed within the current V8 global @@ -1222,7 +1222,7 @@ within which it can operate. The process of creating the V8 Context and associating it with the `contextObject` is what this document refers to as "contextifying" the object. -## Timeout limitations when using `process.nextTick()`, Promises, and `queueMicrotask()` +## Timeout limitations when using `process.nextTick()`, promises, and `queueMicrotask()` Because of the internal mechanics of how the `process.nextTick()` queue and the microtask queue that underlies Promises are implemented within V8 and diff --git a/doc/api/worker_threads.md b/doc/api/worker_threads.md index 702abca0a01dd9..67823a527509fb 100644 --- a/doc/api/worker_threads.md +++ b/doc/api/worker_threads.md @@ -1,4 +1,4 @@ -# Worker Threads +# Worker threads diff --git a/doc/api/zlib.md b/doc/api/zlib.md index f845ce5e52677e..d333b99b75d6eb 100644 --- a/doc/api/zlib.md +++ b/doc/api/zlib.md @@ -93,7 +93,7 @@ do_unzip(buffer) }); ``` -## Threadpool Usage and Performance Considerations +## Threadpool usage and performance considerations All `zlib` APIs, except those that are explicitly synchronous, use the Node.js internal threadpool. This can lead to surprising effects and performance @@ -133,7 +133,7 @@ message. The examples given below are drastically simplified to show the basic concept. Using `zlib` encoding can be expensive, and the results ought to be cached. -See [Memory Usage Tuning][] for more information on the speed/memory/compression +See [Memory usage tuning][] for more information on the speed/memory/compression tradeoffs involved in `zlib` usage. ```js @@ -252,7 +252,7 @@ possible to determine whether the input ended prematurely or lacks the integrity checks, making it necessary to manually check that the decompressed result is valid. -## Memory Usage Tuning +## Memory usage tuning @@ -837,7 +837,7 @@ added: v0.5.8 Creates and returns a new [`Unzip`][] object. -## Convenience Methods +## Convenience methods @@ -1186,7 +1186,7 @@ Decompress a chunk of data with [`Unzip`][]. [`stream.Transform`]: stream.html#stream_class_stream_transform [`zlib.bytesWritten`]: #zlib_zlib_byteswritten [Brotli parameters]: #zlib_brotli_constants -[Memory Usage Tuning]: #zlib_memory_usage_tuning +[Memory usage tuning]: #zlib_memory_usage_tuning [RFC 7932]: https://www.rfc-editor.org/rfc/rfc7932.txt [Streams API]: stream.md [convenience methods]: #zlib_convenience_methods From 563062eddf3ee9582730b6ef61c439b6cfb2d849 Mon Sep 17 00:00:00 2001 From: Rich Trott Date: Sun, 14 Jun 2020 22:51:06 -0700 Subject: [PATCH 26/43] doc: standardize on sentence case for headers Previously, our documentation headers were a mixture of title case, sentence case, and things that were neither. For technical documentation, the _de facto_ standard seems to be sentence case. (See refs below.) So let's standardize on that. This commit follows a commit implementing this standard. This commit adds it to the style guide. Refs: https://developers.google.com/style/capitalization#capitalization-in-titles-and-headings Refs: https://docs.microsoft.com/en-us/style-guide/capitalization PR-URL: https://github.com/nodejs/node/pull/33889 Reviewed-By: Luigi Pinca Reviewed-By: James M Snell --- doc/guides/doc-style-guide.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/guides/doc-style-guide.md b/doc/guides/doc-style-guide.md index 2e8acf02546763..59d9d493b770c6 100644 --- a/doc/guides/doc-style-guide.md +++ b/doc/guides/doc-style-guide.md @@ -68,6 +68,9 @@ * OK: _Node.js 14.x_, _Node.js 14.3.1_ * NOT OK: _Node.js v14_ +* For headings, use sentence case, not title case. + * OK: _## Everybody to the limit_ + * NOT OK: _## Everybody To The Limit_ See also API documentation structure overview in [doctools README][]. From 2899588f28f4d02a61a19ecc8454ddaf14c7b6b3 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Mon, 15 Jun 2020 00:56:09 +0200 Subject: [PATCH 27/43] src: simplify alignment-handling code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use a common function to handle alignment computations in multiple places. PR-URL: https://github.com/nodejs/node/pull/33884 Reviewed-By: James M Snell Reviewed-By: Daniel Bevenius Reviewed-By: Tobias Nießen --- src/node_http2.cc | 4 +--- src/node_http_common-inl.h | 3 +-- src/string_bytes.cc | 6 ++---- src/util-inl.h | 9 +++------ src/util.h | 7 +++++++ 5 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/node_http2.cc b/src/node_http2.cc index 4d1309e08a1a78..de7a7f044e28c2 100644 --- a/src/node_http2.cc +++ b/src/node_http2.cc @@ -373,9 +373,7 @@ Origins::Origins( origin_string_len); // Make sure the start address is aligned appropriately for an nghttp2_nv*. - char* start = reinterpret_cast( - RoundUp(reinterpret_cast(buf_.data()), - alignof(nghttp2_origin_entry))); + char* start = AlignUp(buf_.data(), alignof(nghttp2_origin_entry)); char* origin_contents = start + (count_ * sizeof(nghttp2_origin_entry)); nghttp2_origin_entry* const nva = reinterpret_cast(start); diff --git a/src/node_http_common-inl.h b/src/node_http_common-inl.h index 54f2faf39c49bc..7cd4bdec99c3d6 100644 --- a/src/node_http_common-inl.h +++ b/src/node_http_common-inl.h @@ -31,8 +31,7 @@ NgHeaders::NgHeaders(Environment* env, v8::Local headers) { count_ * sizeof(nv_t) + header_string_len); - char* start = reinterpret_cast( - RoundUp(reinterpret_cast(*buf_), alignof(nv_t))); + char* start = AlignUp(buf_.out(), alignof(nv_t)); char* header_contents = start + (count_ * sizeof(nv_t)); nv_t* const nva = reinterpret_cast(start); diff --git a/src/string_bytes.cc b/src/string_bytes.cc index e0c2d6901fc429..68c7c06d8df1de 100644 --- a/src/string_bytes.cc +++ b/src/string_bytes.cc @@ -273,16 +273,14 @@ size_t StringBytes::WriteUCS2(Isolate* isolate, return 0; } + uint16_t* const aligned_dst = AlignUp(dst, sizeof(*dst)); size_t nchars; - size_t alignment = reinterpret_cast(dst) % sizeof(*dst); - if (alignment == 0) { + if (aligned_dst == dst) { nchars = str->Write(isolate, dst, 0, max_chars, flags); *chars_written = nchars; return nchars * sizeof(*dst); } - uint16_t* aligned_dst = - reinterpret_cast(buf + sizeof(*dst) - alignment); CHECK_EQ(reinterpret_cast(aligned_dst) % sizeof(*dst), 0); // Write all but the last char diff --git a/src/util-inl.h b/src/util-inl.h index 6b4bdfe034d64f..1af87d492ade06 100644 --- a/src/util-inl.h +++ b/src/util-inl.h @@ -208,8 +208,7 @@ void SwapBytes16(char* data, size_t nbytes) { CHECK_EQ(nbytes % 2, 0); #if defined(_MSC_VER) - int align = reinterpret_cast(data) % sizeof(uint16_t); - if (align == 0) { + if (AlignUp(data, sizeof(uint16_t)) == data) { // MSVC has no strict aliasing, and is able to highly optimize this case. uint16_t* data16 = reinterpret_cast(data); size_t len16 = nbytes / sizeof(*data16); @@ -232,9 +231,8 @@ void SwapBytes32(char* data, size_t nbytes) { CHECK_EQ(nbytes % 4, 0); #if defined(_MSC_VER) - int align = reinterpret_cast(data) % sizeof(uint32_t); // MSVC has no strict aliasing, and is able to highly optimize this case. - if (align == 0) { + if (AlignUp(data, sizeof(uint32_t)) == data) { uint32_t* data32 = reinterpret_cast(data); size_t len32 = nbytes / sizeof(*data32); for (size_t i = 0; i < len32; i++) { @@ -256,8 +254,7 @@ void SwapBytes64(char* data, size_t nbytes) { CHECK_EQ(nbytes % 8, 0); #if defined(_MSC_VER) - int align = reinterpret_cast(data) % sizeof(uint64_t); - if (align == 0) { + if (AlignUp(data, sizeof(uint64_t)) == data) { // MSVC has no strict aliasing, and is able to highly optimize this case. uint64_t* data64 = reinterpret_cast(data); size_t len64 = nbytes / sizeof(*data64); diff --git a/src/util.h b/src/util.h index 8cec9a8aab956b..2a4d6e27d59d9e 100644 --- a/src/util.h +++ b/src/util.h @@ -729,6 +729,13 @@ constexpr T RoundUp(T a, T b) { return a % b != 0 ? a + b - (a % b) : a; } +// Align ptr to an `alignment`-bytes boundary. +template +constexpr T* AlignUp(T* ptr, U alignment) { + return reinterpret_cast( + RoundUp(reinterpret_cast(ptr), alignment)); +} + class SlicedArguments : public MaybeStackBuffer> { public: inline explicit SlicedArguments( From 52de4cb107a6fc1a06f7c98f4fd36c7f7fd539d5 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Fri, 12 Jun 2020 00:23:16 +0200 Subject: [PATCH 28/43] src: minor updates to FastHrtime MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Don’t use 12 as a magic number for the buffer size - Mark the object as weak (which is conceptually the right thing to do, even if there is little practical impact) - Keep a reference to the `ArrayBuffer` in question for memory tracking PR-URL: https://github.com/nodejs/node/pull/33851 Reviewed-By: Gus Caplan Reviewed-By: Colin Ihrig Reviewed-By: David Carlier Reviewed-By: James M Snell --- src/node_process_methods.cc | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/node_process_methods.cc b/src/node_process_methods.cc index 800ce0313647d4..88d5f843f3072f 100644 --- a/src/node_process_methods.cc +++ b/src/node_process_methods.cc @@ -1,6 +1,7 @@ #include "base_object-inl.h" #include "debug_utils-inl.h" #include "env-inl.h" +#include "memory_tracker-inl.h" #include "node.h" #include "node_errors.h" #include "node_internals.h" @@ -451,8 +452,10 @@ class FastHrtime : public BaseObject { Local obj = otmpl->NewInstance(env->context()).ToLocalChecked(); - Local ab = ArrayBuffer::New(env->isolate(), 12); - new FastHrtime(env, obj, ab->GetBackingStore()); + Local ab = + ArrayBuffer::New(env->isolate(), + std::max(sizeof(uint64_t), sizeof(uint32_t) * 3)); + new FastHrtime(env, obj, ab); obj->Set( env->context(), FIXED_ONE_BYTE_STRING(env->isolate(), "buffer"), ab) .ToChecked(); @@ -463,11 +466,16 @@ class FastHrtime : public BaseObject { private: FastHrtime(Environment* env, Local object, - std::shared_ptr backing_store) - : BaseObject(env, object), backing_store_(backing_store) {} - - void MemoryInfo(MemoryTracker* tracker) const override {} + Local ab) + : BaseObject(env, object), + array_buffer_(env->isolate(), ab), + backing_store_(ab->GetBackingStore()) { + MakeWeak(); + } + void MemoryInfo(MemoryTracker* tracker) const override { + tracker->TrackField("array_buffer", array_buffer_); + } SET_MEMORY_INFO_NAME(FastHrtime) SET_SELF_SIZE(FastHrtime) @@ -502,6 +510,7 @@ class FastHrtime : public BaseObject { FastBigInt(FromJSObject(args.Holder())); } + v8::Global array_buffer_; std::shared_ptr backing_store_; }; From 2c4864762d2b97a55ef15fe44b7691d67de51766 Mon Sep 17 00:00:00 2001 From: Ruben Bridgewater Date: Mon, 8 Jun 2020 20:30:20 +0200 Subject: [PATCH 29/43] util: gracefully handle unknown colors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes sure colors that are unknown won't cause an error. This is especially important in case a library wants to use colors defined by Node.js core, if available and fall back to the default otherwise. Signed-off-by: Ruben Bridgewater PR-URL: https://github.com/nodejs/node/pull/33797 Reviewed-By: Anna Henningsen Reviewed-By: Luigi Pinca Reviewed-By: Michaël Zasso Reviewed-By: James M Snell Reviewed-By: Evan Lucas Reviewed-By: Yongsheng Zhang Reviewed-By: Trivikram Kamat --- lib/internal/util/inspect.js | 3 ++- test/parallel/test-util-inspect.js | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index 07e0f658e28353..012f358e2ac90a 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -508,7 +508,8 @@ function stylizeWithColor(str, styleType) { const style = inspect.styles[styleType]; if (style !== undefined) { const color = inspect.colors[style]; - return `\u001b[${color[0]}m${str}\u001b[${color[1]}m`; + if (color !== undefined) + return `\u001b[${color[0]}m${str}\u001b[${color[1]}m`; } return str; } diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index a16acc64de3456..78184d85f6e9f3 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -2245,6 +2245,12 @@ assert.strictEqual( assert.deepStrictEqual(inspect.colors[bgColor], [40 + i, 49]); assert.deepStrictEqual(inspect.colors[`${bgColor}Bright`], [100 + i, 49]); }); + + // Unknown colors are handled gracefully: + const stringStyle = inspect.styles.string; + inspect.styles.string = 'UNKNOWN'; + assert.strictEqual(inspect('foobar', { colors: true }), "'foobar'"); + inspect.styles.string = stringStyle; } assert.strictEqual( From 59e6230a30954f78836048b581d8db61582242ef Mon Sep 17 00:00:00 2001 From: Stephen Belanger Date: Sat, 6 Jun 2020 12:06:06 -0700 Subject: [PATCH 30/43] async_hooks: callback trampoline for MakeCallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/33801 Reviewed-By: Anna Henningsen Reviewed-By: Vladimir de Turckheim Reviewed-By: Gus Caplan Reviewed-By: Andrey Pechkurov Reviewed-By: Gerhard Stöbich --- lib/internal/async_hooks.js | 23 +++++++++++++++++++++ src/api/callback.cc | 40 ++++++++++++++++++++++++++++++------- src/async_wrap.cc | 9 +++++++++ src/async_wrap.h | 2 ++ src/env.h | 1 + 5 files changed, 68 insertions(+), 7 deletions(-) diff --git a/lib/internal/async_hooks.js b/lib/internal/async_hooks.js index da7b2745e53bbc..e400f24689fbcc 100644 --- a/lib/internal/async_hooks.js +++ b/lib/internal/async_hooks.js @@ -1,15 +1,18 @@ 'use strict'; const { + ArrayPrototypeUnshift, Error, FunctionPrototypeBind, ObjectPrototypeHasOwnProperty, ObjectDefineProperty, Promise, + ReflectApply, Symbol, } = primordials; const async_wrap = internalBinding('async_wrap'); +const { setCallbackTrampoline } = async_wrap; /* async_hook_fields is a Uint32Array wrapping the uint32_t array of * Environment::AsyncHooks::fields_[]. Each index tracks the number of active * hooks for each type. @@ -103,6 +106,26 @@ const emitDestroyNative = emitHookFactory(destroy_symbol, 'emitDestroyNative'); const emitPromiseResolveNative = emitHookFactory(promise_resolve_symbol, 'emitPromiseResolveNative'); +function callbackTrampoline(asyncId, cb, domain_cb, ...args) { + if (hasHooks(kBefore)) + emitBeforeNative(asyncId); + + let result; + if (typeof domain_cb === 'function') { + ArrayPrototypeUnshift(args, cb); + result = ReflectApply(domain_cb, this, args); + } else { + result = ReflectApply(cb, this, args); + } + + if (hasHooks(kAfter)) + emitAfterNative(asyncId); + + return result; +} + +setCallbackTrampoline(callbackTrampoline); + const topLevelResource = {}; function executionAsyncResource() { diff --git a/src/api/callback.cc b/src/api/callback.cc index a03a2587b4c796..7daddcd977e33f 100644 --- a/src/api/callback.cc +++ b/src/api/callback.cc @@ -158,20 +158,46 @@ MaybeLocal InternalMakeCallback(Environment* env, CHECK(!argv[i].IsEmpty()); #endif - InternalCallbackScope scope(env, resource, asyncContext); + Local hook_cb = env->async_hooks_callback_trampoline(); + int flags = InternalCallbackScope::kNoFlags; + int hook_count = 0; + if (!hook_cb.IsEmpty()) { + flags = InternalCallbackScope::kSkipAsyncHooks; + AsyncHooks* async_hooks = env->async_hooks(); + hook_count = async_hooks->fields()[AsyncHooks::kBefore] + + async_hooks->fields()[AsyncHooks::kAfter]; + } + + InternalCallbackScope scope(env, resource, asyncContext, flags); if (scope.Failed()) { return MaybeLocal(); } Local domain_cb = env->domain_callback(); MaybeLocal ret; - if (asyncContext.async_id != 0 || domain_cb.IsEmpty()) { - ret = callback->Call(env->context(), recv, argc, argv); - } else { - std::vector> args(1 + argc); + + if (asyncContext.async_id != 0 && hook_count != 0) { + MaybeStackBuffer, 16> args(3 + argc); + args[0] = v8::Number::New(env->isolate(), asyncContext.async_id); + args[1] = callback; + if (domain_cb.IsEmpty()) { + args[2] = Undefined(env->isolate()); + } else { + args[2] = domain_cb; + } + for (int i = 0; i < argc; i++) { + args[i + 3] = argv[i]; + } + ret = hook_cb->Call(env->context(), recv, args.length(), &args[0]); + } else if (asyncContext.async_id == 0 && !domain_cb.IsEmpty()) { + MaybeStackBuffer, 16> args(1 + argc); args[0] = callback; - std::copy(&argv[0], &argv[argc], args.begin() + 1); - ret = domain_cb->Call(env->context(), recv, args.size(), &args[0]); + for (int i = 0; i < argc; i++) { + args[i + 1] = argv[i]; + } + ret = domain_cb->Call(env->context(), recv, args.length(), &args[0]); + } else { + ret = callback->Call(env->context(), recv, argc, argv); } if (ret.IsEmpty()) { diff --git a/src/async_wrap.cc b/src/async_wrap.cc index 80342b78284892..123f85f38a715f 100644 --- a/src/async_wrap.cc +++ b/src/async_wrap.cc @@ -552,6 +552,14 @@ void AsyncWrap::QueueDestroyAsyncId(const FunctionCallbackInfo& args) { args[0].As()->Value()); } +void AsyncWrap::SetCallbackTrampoline(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + CHECK(args[0]->IsFunction()); + + env->set_async_hooks_callback_trampoline(args[0].As()); +} + Local AsyncWrap::GetConstructorTemplate(Environment* env) { Local tmpl = env->async_wrap_ctor_template(); if (tmpl.IsEmpty()) { @@ -575,6 +583,7 @@ void AsyncWrap::Initialize(Local target, HandleScope scope(isolate); env->SetMethod(target, "setupHooks", SetupHooks); + env->SetMethod(target, "setCallbackTrampoline", SetCallbackTrampoline); env->SetMethod(target, "pushAsyncContext", PushAsyncContext); env->SetMethod(target, "popAsyncContext", PopAsyncContext); env->SetMethod(target, "queueDestroyAsyncId", QueueDestroyAsyncId); diff --git a/src/async_wrap.h b/src/async_wrap.h index fa6634b6d3ab4c..2a8c80c2f0028a 100644 --- a/src/async_wrap.h +++ b/src/async_wrap.h @@ -146,6 +146,8 @@ class AsyncWrap : public BaseObject { static void GetProviderType(const v8::FunctionCallbackInfo& args); static void QueueDestroyAsyncId( const v8::FunctionCallbackInfo& args); + static void SetCallbackTrampoline( + const v8::FunctionCallbackInfo& args); static void EmitAsyncInit(Environment* env, v8::Local object, diff --git a/src/env.h b/src/env.h index 706a2de16a0073..09c8ff9db45ce4 100644 --- a/src/env.h +++ b/src/env.h @@ -479,6 +479,7 @@ constexpr size_t kFsStatsBufferLength = #define ENVIRONMENT_STRONG_PERSISTENT_VALUES(V) \ V(async_hooks_after_function, v8::Function) \ V(async_hooks_before_function, v8::Function) \ + V(async_hooks_callback_trampoline, v8::Function) \ V(async_hooks_binding, v8::Object) \ V(async_hooks_destroy_function, v8::Function) \ V(async_hooks_init_function, v8::Function) \ From 646e5a471766e27e8317bb54d1fd1d2c72cffb69 Mon Sep 17 00:00:00 2001 From: Stephen Belanger Date: Tue, 9 Jun 2020 14:50:03 -0700 Subject: [PATCH 31/43] domain: remove native domain code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the async_hooks callback trampoline, domains no longer need any native code. With this, domains can exist in pure JavaScript. PR-URL: https://github.com/nodejs/node/pull/33801 Reviewed-By: Anna Henningsen Reviewed-By: Vladimir de Turckheim Reviewed-By: Gus Caplan Reviewed-By: Andrey Pechkurov Reviewed-By: Gerhard Stöbich --- lib/domain.js | 3 ++- lib/internal/async_hooks.js | 12 +++++++++--- node.gyp | 1 - src/api/callback.cc | 19 +++---------------- src/env.h | 1 - src/node_binding.cc | 1 - src/node_domain.cc | 35 ----------------------------------- 7 files changed, 14 insertions(+), 58 deletions(-) delete mode 100644 src/node_domain.cc diff --git a/lib/domain.js b/lib/domain.js index f18823960cde02..febd7620f21500 100644 --- a/lib/domain.js +++ b/lib/domain.js @@ -42,6 +42,7 @@ const { ERR_UNHANDLED_ERROR } = require('internal/errors').codes; const { createHook } = require('async_hooks'); +const { useDomainTrampoline } = require('internal/async_hooks'); // TODO(addaleax): Use a non-internal solution for this. const kWeak = Symbol('kWeak'); @@ -145,7 +146,7 @@ function topLevelDomainCallback(cb, ...args) { // another one. The stack is each entered domain. let stack = []; exports._stack = stack; -internalBinding('domain').enable(topLevelDomainCallback); +useDomainTrampoline(topLevelDomainCallback); function updateExceptionCapture() { if (stack.every((domain) => domain.listenerCount('error') === 0)) { diff --git a/lib/internal/async_hooks.js b/lib/internal/async_hooks.js index e400f24689fbcc..fc894ebe4d773f 100644 --- a/lib/internal/async_hooks.js +++ b/lib/internal/async_hooks.js @@ -106,8 +106,13 @@ const emitDestroyNative = emitHookFactory(destroy_symbol, 'emitDestroyNative'); const emitPromiseResolveNative = emitHookFactory(promise_resolve_symbol, 'emitPromiseResolveNative'); -function callbackTrampoline(asyncId, cb, domain_cb, ...args) { - if (hasHooks(kBefore)) +let domain_cb; +function useDomainTrampoline(fn) { + domain_cb = fn; +} + +function callbackTrampoline(asyncId, cb, ...args) { + if (asyncId && hasHooks(kBefore)) emitBeforeNative(asyncId); let result; @@ -118,7 +123,7 @@ function callbackTrampoline(asyncId, cb, domain_cb, ...args) { result = ReflectApply(cb, this, args); } - if (hasHooks(kAfter)) + if (asyncId && hasHooks(kAfter)) emitAfterNative(asyncId); return result; @@ -564,6 +569,7 @@ module.exports = { emitAfter: emitAfterScript, emitDestroy: emitDestroyScript, registerDestroyHook, + useDomainTrampoline, nativeHooks: { init: emitInitNative, before: emitBeforeNative, diff --git a/node.gyp b/node.gyp index ddf59f4a0273cf..0af72d48c25150 100644 --- a/node.gyp +++ b/node.gyp @@ -590,7 +590,6 @@ 'src/node_contextify.cc', 'src/node_credentials.cc', 'src/node_dir.cc', - 'src/node_domain.cc', 'src/node_env_var.cc', 'src/node_errors.cc', 'src/node_file.cc', diff --git a/src/api/callback.cc b/src/api/callback.cc index 7daddcd977e33f..9f52c25cf0d900 100644 --- a/src/api/callback.cc +++ b/src/api/callback.cc @@ -173,29 +173,16 @@ MaybeLocal InternalMakeCallback(Environment* env, return MaybeLocal(); } - Local domain_cb = env->domain_callback(); MaybeLocal ret; - if (asyncContext.async_id != 0 && hook_count != 0) { - MaybeStackBuffer, 16> args(3 + argc); + if (hook_count != 0) { + MaybeStackBuffer, 16> args(2 + argc); args[0] = v8::Number::New(env->isolate(), asyncContext.async_id); args[1] = callback; - if (domain_cb.IsEmpty()) { - args[2] = Undefined(env->isolate()); - } else { - args[2] = domain_cb; - } for (int i = 0; i < argc; i++) { - args[i + 3] = argv[i]; + args[i + 2] = argv[i]; } ret = hook_cb->Call(env->context(), recv, args.length(), &args[0]); - } else if (asyncContext.async_id == 0 && !domain_cb.IsEmpty()) { - MaybeStackBuffer, 16> args(1 + argc); - args[0] = callback; - for (int i = 0; i < argc; i++) { - args[i + 1] = argv[i]; - } - ret = domain_cb->Call(env->context(), recv, args.length(), &args[0]); } else { ret = callback->Call(env->context(), recv, argc, argv); } diff --git a/src/env.h b/src/env.h index 09c8ff9db45ce4..ba3306a43347b8 100644 --- a/src/env.h +++ b/src/env.h @@ -486,7 +486,6 @@ constexpr size_t kFsStatsBufferLength = V(async_hooks_promise_resolve_function, v8::Function) \ V(buffer_prototype_object, v8::Object) \ V(crypto_key_object_constructor, v8::Function) \ - V(domain_callback, v8::Function) \ V(domexception_function, v8::Function) \ V(enhance_fatal_stack_after_inspector, v8::Function) \ V(enhance_fatal_stack_before_inspector, v8::Function) \ diff --git a/src/node_binding.cc b/src/node_binding.cc index e3014657bbe25b..80ab4e69e110d0 100644 --- a/src/node_binding.cc +++ b/src/node_binding.cc @@ -48,7 +48,6 @@ V(config) \ V(contextify) \ V(credentials) \ - V(domain) \ V(errors) \ V(fs) \ V(fs_dir) \ diff --git a/src/node_domain.cc b/src/node_domain.cc deleted file mode 100644 index 9075845442fd4d..00000000000000 --- a/src/node_domain.cc +++ /dev/null @@ -1,35 +0,0 @@ -#include "env-inl.h" -#include "v8.h" - -namespace node { -namespace domain { - -using v8::Context; -using v8::Function; -using v8::FunctionCallbackInfo; -using v8::Local; -using v8::Object; -using v8::Value; - - -void Enable(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - CHECK(args[0]->IsFunction()); - - env->set_domain_callback(args[0].As()); -} - -void Initialize(Local target, - Local unused, - Local context, - void* priv) { - Environment* env = Environment::GetCurrent(context); - - env->SetMethod(target, "enable", Enable); -} - -} // namespace domain -} // namespace node - -NODE_MODULE_CONTEXT_AWARE_INTERNAL(domain, node::domain::Initialize) From a86a295fd7162a7fdf406a21b3c3c679819c60b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= Date: Fri, 12 Jun 2020 16:04:18 +0200 Subject: [PATCH 32/43] lib: remove NodeError from the prototype of errors with code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michaël Zasso PR-URL: https://github.com/nodejs/node/pull/33857 Refs: https://github.com/nodejs/node/pull/33770 Reviewed-By: Anna Henningsen Reviewed-By: Rich Trott Reviewed-By: Zeyu Yang Reviewed-By: Tobias Nießen --- lib/internal/errors.js | 52 ++++++++++--------- test/message/esm_loader_not_found.out | 4 +- .../esm_loader_not_found_cjs_hint_bare.out | 1 + ...esm_loader_not_found_cjs_hint_relative.out | 4 +- test/message/internal_assert.out | 1 + test/message/internal_assert_fail.out | 1 + 6 files changed, 35 insertions(+), 28 deletions(-) diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 34ef6368218aac..e7563515f6ee4b 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -248,31 +248,35 @@ function makeSystemErrorWithCode(key) { } function makeNodeErrorWithCode(Base, key) { - return class NodeError extends Base { - constructor(...args) { - if (excludedStackFn === undefined) { - super(); - } else { - const limit = Error.stackTraceLimit; - Error.stackTraceLimit = 0; - super(); - // Reset the limit and setting the name property. - Error.stackTraceLimit = limit; - } - const message = getMessage(key, args, this); - ObjectDefineProperty(this, 'message', { - value: message, - enumerable: false, - writable: true, - configurable: true - }); - addCodeToName(this, super.name, key); - this.code = key; - } - - toString() { - return `${this.name} [${key}]: ${this.message}`; + return function NodeError(...args) { + let error; + if (excludedStackFn === undefined) { + error = new Base(); + } else { + const limit = Error.stackTraceLimit; + Error.stackTraceLimit = 0; + error = new Base(); + // Reset the limit and setting the name property. + Error.stackTraceLimit = limit; } + const message = getMessage(key, args, error); + ObjectDefineProperty(error, 'message', { + value: message, + enumerable: false, + writable: true, + configurable: true, + }); + ObjectDefineProperty(error, 'toString', { + value() { + return `${this.name} [${key}]: ${this.message}`; + }, + enumerable: false, + writable: true, + configurable: true, + }); + addCodeToName(error, Base.name, key); + error.code = key; + return error; }; } diff --git a/test/message/esm_loader_not_found.out b/test/message/esm_loader_not_found.out index 1d2aa957150082..967acb648e4ae2 100644 --- a/test/message/esm_loader_not_found.out +++ b/test/message/esm_loader_not_found.out @@ -4,6 +4,7 @@ internal/modules/run_main.js:* internalBinding('errors').triggerUncaughtException( ^ Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'i-dont-exist' imported from * + at new NodeError (internal/errors.js:*:*) at packageResolve (internal/modules/esm/resolve.js:*:*) at moduleResolve (internal/modules/esm/resolve.js:*:*) at Loader.defaultResolve [as _resolve] (internal/modules/esm/resolve.js:*:*) @@ -12,7 +13,6 @@ Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'i-dont-exist' imported from * at Loader.import (internal/modules/esm/loader.js:*:*) at internal/process/esm_loader.js:*:* at Object.initializeLoader (internal/process/esm_loader.js:*:*) - at runMainESM (internal/modules/run_main.js:*:*) - at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:*:*) { + at runMainESM (internal/modules/run_main.js:*:*) { code: 'ERR_MODULE_NOT_FOUND' } diff --git a/test/message/esm_loader_not_found_cjs_hint_bare.out b/test/message/esm_loader_not_found_cjs_hint_bare.out index e56f1da0f6e76e..543c3e4a4a8133 100644 --- a/test/message/esm_loader_not_found_cjs_hint_bare.out +++ b/test/message/esm_loader_not_found_cjs_hint_bare.out @@ -4,6 +4,7 @@ internal/modules/run_main.js:* Error [ERR_MODULE_NOT_FOUND]: Cannot find module '*test*fixtures*node_modules*some_module*obj' imported from *test*fixtures*esm_loader_not_found_cjs_hint_bare.mjs Did you mean to import some_module/obj.js? + at new NodeError (internal/errors.js:*:*) at finalizeResolution (internal/modules/esm/resolve.js:*:*) at packageResolve (internal/modules/esm/resolve.js:*:*) at moduleResolve (internal/modules/esm/resolve.js:*:*) diff --git a/test/message/esm_loader_not_found_cjs_hint_relative.out b/test/message/esm_loader_not_found_cjs_hint_relative.out index 76df3163bb728c..20d81355e91f21 100644 --- a/test/message/esm_loader_not_found_cjs_hint_relative.out +++ b/test/message/esm_loader_not_found_cjs_hint_relative.out @@ -6,6 +6,7 @@ internal/modules/run_main.js:* Error [ERR_MODULE_NOT_FOUND]: Cannot find module '*test*common*fixtures' imported from * Did you mean to import ./test/common/fixtures.js? + at new NodeError (internal/errors.js:*:*) at finalizeResolution (internal/modules/esm/resolve.js:*:*) at moduleResolve (internal/modules/esm/resolve.js:*:*) at Loader.defaultResolve [as _resolve] (internal/modules/esm/resolve.js:*:*) @@ -14,7 +15,6 @@ Did you mean to import ./test/common/fixtures.js? at Loader.import (internal/modules/esm/loader.js:*:*) at internal/process/esm_loader.js:*:* at Object.initializeLoader (internal/process/esm_loader.js:*:*) - at runMainESM (internal/modules/run_main.js:*:*) - at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:*:*) { + at runMainESM (internal/modules/run_main.js:*:*) { code: 'ERR_MODULE_NOT_FOUND' } diff --git a/test/message/internal_assert.out b/test/message/internal_assert.out index cf09fdcb605269..9ca8350756c9ad 100644 --- a/test/message/internal_assert.out +++ b/test/message/internal_assert.out @@ -5,6 +5,7 @@ internal/assert.js:* Error [ERR_INTERNAL_ASSERTION]: This is caused by either a bug in Node.js or incorrect usage of Node.js internals. Please open an issue with this stack trace at https://github.com/nodejs/node/issues + at new NodeError (internal/errors.js:*:*) at assert (internal/assert.js:*:*) at * (*test*message*internal_assert.js:7:1) at * diff --git a/test/message/internal_assert_fail.out b/test/message/internal_assert_fail.out index 11b532b7b2af3c..11e253703170d2 100644 --- a/test/message/internal_assert_fail.out +++ b/test/message/internal_assert_fail.out @@ -6,6 +6,7 @@ Error [ERR_INTERNAL_ASSERTION]: Unreachable! This is caused by either a bug in Node.js or incorrect usage of Node.js internals. Please open an issue with this stack trace at https://github.com/nodejs/node/issues + at new NodeError (internal/errors.js:*:*) at Function.fail (internal/assert.js:*:*) at * (*test*message*internal_assert_fail.js:7:8) at * From 8f000ea09f6ee20b50cb83e6f7b2cb25671ef6f2 Mon Sep 17 00:00:00 2001 From: Gus Caplan Date: Fri, 12 Jun 2020 14:45:40 -0500 Subject: [PATCH 33/43] deps: V8: cherry-pick 767e65f945e7 Original commit message: [API] Fix microtask message reporting RunSingleMicrotask calls Runtime::ReportMessage, but the implementation of ReportMessage would unconditionally discard these exceptions. This CL removes all of the intermediate logic and directly calls MessageHandler::ReportMessage, restoring the ability of RunSingleMicrotask to report exceptions that occur in microtasks. Bug: v8:8326 Change-Id: I493de74383b2ab191d786611fb9eba9d27e7a243 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2162121 Commit-Queue: Gus Caplan Reviewed-by: Jakob Gruber Cr-Commit-Position: refs/heads/master@{#67630} Refs: https://github.com/v8/v8/commit/767e65f945e7caa4becc2bd4b5e9ea8562da0ca4 PR-URL: https://github.com/nodejs/node/pull/33859 Reviewed-By: Anna Henningsen Reviewed-By: David Carlier Reviewed-By: James M Snell --- common.gypi | 2 +- .../builtins/builtins-microtask-queue-gen.cc | 2 +- deps/v8/src/builtins/promise-reaction-job.tq | 14 +- deps/v8/src/execution/isolate.cc | 170 ++++++------------ deps/v8/src/execution/isolate.h | 6 +- deps/v8/src/runtime/runtime-internal.cc | 16 +- deps/v8/src/runtime/runtime.h | 2 +- deps/v8/test/cctest/test-api.cc | 20 +++ 8 files changed, 91 insertions(+), 141 deletions(-) diff --git a/common.gypi b/common.gypi index 2c32f9e6c3c3ea..e2e7eaf00db3db 100644 --- a/common.gypi +++ b/common.gypi @@ -36,7 +36,7 @@ # Reset this number to 0 on major V8 upgrades. # Increment by one for each non-official patch applied to deps/v8. - 'v8_embedder_string': '-node.19', + 'v8_embedder_string': '-node.20', ##### V8 defaults for Node.js ##### diff --git a/deps/v8/src/builtins/builtins-microtask-queue-gen.cc b/deps/v8/src/builtins/builtins-microtask-queue-gen.cc index c71faa116c1d2e..e6787b2da8c34b 100644 --- a/deps/v8/src/builtins/builtins-microtask-queue-gen.cc +++ b/deps/v8/src/builtins/builtins-microtask-queue-gen.cc @@ -327,7 +327,7 @@ void MicrotaskQueueBuiltinsAssembler::RunSingleMicrotask( BIND(&if_exception); { // Report unhandled exceptions from microtasks. - CallRuntime(Runtime::kReportMessage, current_context, + CallRuntime(Runtime::kReportMessageFromMicrotask, current_context, var_exception.value()); RewindEnteredContext(saved_entered_context_count); SetCurrentContext(current_context); diff --git a/deps/v8/src/builtins/promise-reaction-job.tq b/deps/v8/src/builtins/promise-reaction-job.tq index 1d20d22efbbacc..f17886c0d18643 100644 --- a/deps/v8/src/builtins/promise-reaction-job.tq +++ b/deps/v8/src/builtins/promise-reaction-job.tq @@ -4,11 +4,6 @@ #include 'src/builtins/builtins-promise-gen.h' -namespace runtime { - extern transitioning runtime - ReportMessage(implicit context: Context)(JSAny): JSAny; -} - namespace promise { transitioning @@ -30,13 +25,8 @@ namespace promise { case (capability: PromiseCapability): { // In the general case we need to call the (user provided) // promiseCapability.[[Reject]] function. - try { - const reject = UnsafeCast(capability.reject); - return Call(context, reject, Undefined, reason); - } catch (e) { - // Swallow the exception here. - return runtime::ReportMessage(e); - } + const reject = UnsafeCast(capability.reject); + return Call(context, reject, Undefined, reason); } } } else { diff --git a/deps/v8/src/execution/isolate.cc b/deps/v8/src/execution/isolate.cc index 5c21b0982e9315..033d23d85b824c 100644 --- a/deps/v8/src/execution/isolate.cc +++ b/deps/v8/src/execution/isolate.cc @@ -1412,6 +1412,8 @@ void Isolate::InvokeApiInterruptCallbacks() { } } +namespace { + void ReportBootstrappingException(Handle exception, MessageLocation* location) { base::OS::PrintError("Exception thrown during bootstrapping\n"); @@ -1467,6 +1469,36 @@ void ReportBootstrappingException(Handle exception, #endif } +} // anonymous namespace + +Handle Isolate::CreateMessageOrAbort( + Handle exception, MessageLocation* location) { + Handle message_obj = CreateMessage(exception, location); + + // If the abort-on-uncaught-exception flag is specified, and if the + // embedder didn't specify a custom uncaught exception callback, + // or if the custom callback determined that V8 should abort, then + // abort. + if (FLAG_abort_on_uncaught_exception) { + CatchType prediction = PredictExceptionCatcher(); + if ((prediction == NOT_CAUGHT || prediction == CAUGHT_BY_EXTERNAL) && + (!abort_on_uncaught_exception_callback_ || + abort_on_uncaught_exception_callback_( + reinterpret_cast(this)))) { + // Prevent endless recursion. + FLAG_abort_on_uncaught_exception = false; + // This flag is intended for use by JavaScript developers, so + // print a user-friendly stack trace (not an internal one). + PrintF(stderr, "%s\n\nFROM\n", + MessageHandler::GetLocalizedMessage(this, message_obj).get()); + PrintCurrentStackTrace(stderr); + base::OS::Abort(); + } + } + + return message_obj; +} + Object Isolate::Throw(Object raw_exception, MessageLocation* location) { DCHECK(!has_pending_exception()); @@ -1538,38 +1570,14 @@ Object Isolate::Throw(Object raw_exception, MessageLocation* location) { if (location == nullptr && ComputeLocation(&computed_location)) { location = &computed_location; } - if (bootstrapper()->IsActive()) { // It's not safe to try to make message objects or collect stack traces // while the bootstrapper is active since the infrastructure may not have // been properly initialized. ReportBootstrappingException(exception, location); } else { - Handle message_obj = CreateMessage(exception, location); + Handle message_obj = CreateMessageOrAbort(exception, location); thread_local_top()->pending_message_obj_ = *message_obj; - - // For any exception not caught by JavaScript, even when an external - // handler is present: - // If the abort-on-uncaught-exception flag is specified, and if the - // embedder didn't specify a custom uncaught exception callback, - // or if the custom callback determined that V8 should abort, then - // abort. - if (FLAG_abort_on_uncaught_exception) { - CatchType prediction = PredictExceptionCatcher(); - if ((prediction == NOT_CAUGHT || prediction == CAUGHT_BY_EXTERNAL) && - (!abort_on_uncaught_exception_callback_ || - abort_on_uncaught_exception_callback_( - reinterpret_cast(this)))) { - // Prevent endless recursion. - FLAG_abort_on_uncaught_exception = false; - // This flag is intended for use by JavaScript developers, so - // print a user-friendly stack trace (not an internal one). - PrintF(stderr, "%s\n\nFROM\n", - MessageHandler::GetLocalizedMessage(this, message_obj).get()); - PrintCurrentStackTrace(stderr); - base::OS::Abort(); - } - } } } @@ -2106,7 +2114,7 @@ bool Isolate::ComputeLocationFromStackTrace(MessageLocation* target, bool is_at_number_conversion = elements->IsAsmJsWasmFrame(i) && elements->Flags(i).value() & FrameArray::kAsmJsAtNumberConversion; - if (elements->IsWasmCompiledFrame(i)) { + if (elements->IsWasmCompiledFrame(i) || elements->IsAsmJsWasmFrame(i)) { // WasmCode* held alive by the {GlobalWasmCodeRef}. wasm::WasmCode* code = Managed::cast(elements->WasmCodeObject(i)) @@ -2230,9 +2238,28 @@ bool Isolate::IsExternalHandlerOnTop(Object exception) { return (entry_handler > external_handler); } -void Isolate::ReportPendingMessagesImpl(bool report_externally) { +std::vector* Isolate::GetCodePages() const { + return code_pages_.load(std::memory_order_acquire); +} + +void Isolate::SetCodePages(std::vector* new_code_pages) { + code_pages_.store(new_code_pages, std::memory_order_release); +} + +void Isolate::ReportPendingMessages() { + DCHECK(AllowExceptions::IsAllowed(this)); + + // The embedder might run script in response to an exception. + AllowJavascriptExecutionDebugOnly allow_script(this); + Object exception_obj = pending_exception(); + // Try to propagate the exception to an external v8::TryCatch handler. If + // propagation was unsuccessful, then we will get another chance at reporting + // the pending message if the exception is re-thrown. + bool has_been_propagated = PropagatePendingExceptionToExternalTryCatch(); + if (!has_been_propagated) return; + // Clear the pending message object early to avoid endless recursion. Object message_obj = thread_local_top()->pending_message_obj_; clear_pending_message(); @@ -2245,7 +2272,7 @@ void Isolate::ReportPendingMessagesImpl(bool report_externally) { // depending on whether and external v8::TryCatch or an internal JavaScript // handler is on top. bool should_report_exception; - if (report_externally) { + if (IsExternalHandlerOnTop(exception_obj)) { // Only report the exception if the external handler is verbose. should_report_exception = try_catch_handler()->is_verbose_; } else { @@ -2271,93 +2298,6 @@ void Isolate::ReportPendingMessagesImpl(bool report_externally) { } } -std::vector* Isolate::GetCodePages() const { - return code_pages_.load(std::memory_order_acquire); -} - -void Isolate::SetCodePages(std::vector* new_code_pages) { - code_pages_.store(new_code_pages, std::memory_order_release); -} - -void Isolate::ReportPendingMessages() { - DCHECK(AllowExceptions::IsAllowed(this)); - - // The embedder might run script in response to an exception. - AllowJavascriptExecutionDebugOnly allow_script(this); - - Object exception = pending_exception(); - - // Try to propagate the exception to an external v8::TryCatch handler. If - // propagation was unsuccessful, then we will get another chance at reporting - // the pending message if the exception is re-thrown. - bool has_been_propagated = PropagatePendingExceptionToExternalTryCatch(); - if (!has_been_propagated) return; - - ReportPendingMessagesImpl(IsExternalHandlerOnTop(exception)); -} - -void Isolate::ReportPendingMessagesFromJavaScript() { - DCHECK(AllowExceptions::IsAllowed(this)); - - auto IsHandledByJavaScript = [=]() { - // In this situation, the exception is always a non-terminating exception. - - // Get the top-most JS_ENTRY handler, cannot be on top if it doesn't exist. - Address entry_handler = Isolate::handler(thread_local_top()); - DCHECK_NE(entry_handler, kNullAddress); - entry_handler = StackHandler::FromAddress(entry_handler)->next_address(); - - // Get the address of the external handler so we can compare the address to - // determine which one is closer to the top of the stack. - Address external_handler = thread_local_top()->try_catch_handler_address(); - if (external_handler == kNullAddress) return true; - - return (entry_handler < external_handler); - }; - - auto IsHandledExternally = [=]() { - Address external_handler = thread_local_top()->try_catch_handler_address(); - if (external_handler == kNullAddress) return false; - - // Get the top-most JS_ENTRY handler, cannot be on top if it doesn't exist. - Address entry_handler = Isolate::handler(thread_local_top()); - DCHECK_NE(entry_handler, kNullAddress); - entry_handler = StackHandler::FromAddress(entry_handler)->next_address(); - return (entry_handler > external_handler); - }; - - auto PropagateToExternalHandler = [=]() { - if (IsHandledByJavaScript()) { - thread_local_top()->external_caught_exception_ = false; - return false; - } - - if (!IsHandledExternally()) { - thread_local_top()->external_caught_exception_ = false; - return true; - } - - thread_local_top()->external_caught_exception_ = true; - v8::TryCatch* handler = try_catch_handler(); - DCHECK(thread_local_top()->pending_message_obj_.IsJSMessageObject() || - thread_local_top()->pending_message_obj_.IsTheHole(this)); - handler->can_continue_ = true; - handler->has_terminated_ = false; - handler->exception_ = reinterpret_cast(pending_exception().ptr()); - // Propagate to the external try-catch only if we got an actual message. - if (thread_local_top()->pending_message_obj_.IsTheHole(this)) return true; - - handler->message_obj_ = - reinterpret_cast(thread_local_top()->pending_message_obj_.ptr()); - return true; - }; - - // Try to propagate to an external v8::TryCatch handler. - if (!PropagateToExternalHandler()) return; - - ReportPendingMessagesImpl(true); -} - bool Isolate::OptionalRescheduleException(bool clear_exception) { DCHECK(has_pending_exception()); PropagatePendingExceptionToExternalTryCatch(); diff --git a/deps/v8/src/execution/isolate.h b/deps/v8/src/execution/isolate.h index 037e6ea7f59d0b..edf9a1a95ad9d8 100644 --- a/deps/v8/src/execution/isolate.h +++ b/deps/v8/src/execution/isolate.h @@ -822,10 +822,6 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory { // Un-schedule an exception that was caught by a TryCatch handler. void CancelScheduledExceptionFromTryCatch(v8::TryCatch* handler); void ReportPendingMessages(); - void ReportPendingMessagesFromJavaScript(); - - // Implements code shared between the two above methods - void ReportPendingMessagesImpl(bool report_externally); // Promote a scheduled exception to pending. Asserts has_scheduled_exception. Object PromoteScheduledException(); @@ -842,6 +838,8 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory { Handle CreateMessage(Handle exception, MessageLocation* location); + Handle CreateMessageOrAbort(Handle exception, + MessageLocation* location); // Out of resource exception helpers. Object StackOverflow(); diff --git a/deps/v8/src/runtime/runtime-internal.cc b/deps/v8/src/runtime/runtime-internal.cc index b3d9f26ee54b71..4806922a970f6b 100644 --- a/deps/v8/src/runtime/runtime-internal.cc +++ b/deps/v8/src/runtime/runtime-internal.cc @@ -578,19 +578,21 @@ RUNTIME_FUNCTION(Runtime_GetTemplateObject) { isolate, native_context, description, shared_info, slot_id); } -RUNTIME_FUNCTION(Runtime_ReportMessage) { +RUNTIME_FUNCTION(Runtime_ReportMessageFromMicrotask) { // Helper to report messages and continue JS execution. This is intended to - // behave similarly to reporting exceptions which reach the top-level in - // Execution.cc, but allow the JS code to continue. This is useful for - // implementing algorithms such as RunMicrotasks in JS. + // behave similarly to reporting exceptions which reach the top-level, but + // allow the JS code to continue. HandleScope scope(isolate); DCHECK_EQ(1, args.length()); - CONVERT_ARG_HANDLE_CHECKED(Object, message_obj, 0); + CONVERT_ARG_HANDLE_CHECKED(Object, exception, 0); DCHECK(!isolate->has_pending_exception()); - isolate->set_pending_exception(*message_obj); - isolate->ReportPendingMessagesFromJavaScript(); + isolate->set_pending_exception(*exception); + MessageLocation* no_location = nullptr; + Handle message = + isolate->CreateMessageOrAbort(exception, no_location); + MessageHandler::ReportMessage(isolate, no_location, message); isolate->clear_pending_exception(); return ReadOnlyRoots(isolate).undefined_value(); } diff --git a/deps/v8/src/runtime/runtime.h b/deps/v8/src/runtime/runtime.h index 57500be5107790..c9ee6d88ac1917 100644 --- a/deps/v8/src/runtime/runtime.h +++ b/deps/v8/src/runtime/runtime.h @@ -225,7 +225,7 @@ namespace internal { F(NewTypeError, 2, 1) \ F(OrdinaryHasInstance, 2, 1) \ F(PromoteScheduledException, 0, 1) \ - F(ReportMessage, 1, 1) \ + F(ReportMessageFromMicrotask, 1, 1) \ F(ReThrow, 1, 1) \ F(RunMicrotaskCallback, 2, 1) \ F(PerformMicrotaskCheckpoint, 0, 1) \ diff --git a/deps/v8/test/cctest/test-api.cc b/deps/v8/test/cctest/test-api.cc index a12d4788e4b5b5..191bb63b8e5aaf 100644 --- a/deps/v8/test/cctest/test-api.cc +++ b/deps/v8/test/cctest/test-api.cc @@ -19845,11 +19845,30 @@ static void MicrotaskExceptionTwo( v8::Exception::Error(v8_str("second"))); } +int handler_call_count = 0; +static void MicrotaskExceptionHandler(Local message, + Local exception) { + CHECK(exception->IsNativeError()); + Local context = message->GetIsolate()->GetCurrentContext(); + Local str = exception->ToString(context).ToLocalChecked(); + switch (handler_call_count++) { + case 0: + CHECK(str->StrictEquals(v8_str("Error: first"))); + break; + case 1: + CHECK(str->StrictEquals(v8_str("Error: second"))); + break; + default: + UNREACHABLE(); + } +} TEST(RunMicrotasksIgnoresThrownExceptions) { LocalContext env; v8::Isolate* isolate = env->GetIsolate(); v8::HandleScope scope(isolate); + isolate->AddMessageListenerWithErrorLevel(MicrotaskExceptionHandler, + v8::Isolate::kMessageAll); CompileRun( "var exception1Calls = 0;" "var exception2Calls = 0;"); @@ -19860,6 +19879,7 @@ TEST(RunMicrotasksIgnoresThrownExceptions) { TryCatch try_catch(isolate); CompileRun("1+1;"); CHECK(!try_catch.HasCaught()); + CHECK_EQ(handler_call_count, 2); CHECK_EQ(1, CompileRun("exception1Calls")->Int32Value(env.local()).FromJust()); CHECK_EQ(1, From 178e52a7ead2ef9ffb6eb5e17be167f17beb45a8 Mon Sep 17 00:00:00 2001 From: Gus Caplan Date: Fri, 12 Jun 2020 16:12:17 -0500 Subject: [PATCH 34/43] lib: remove manual exception handling in queueMicrotask PR-URL: https://github.com/nodejs/node/pull/33859 Reviewed-By: Anna Henningsen Reviewed-By: David Carlier Reviewed-By: James M Snell --- lib/internal/process/task_queues.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/lib/internal/process/task_queues.js b/lib/internal/process/task_queues.js index c07942587ca9c2..3cf07601a23aa4 100644 --- a/lib/internal/process/task_queues.js +++ b/lib/internal/process/task_queues.js @@ -15,10 +15,6 @@ const { enqueueMicrotask } = internalBinding('task_queue'); -const { - triggerUncaughtException -} = internalBinding('errors'); - const { setHasRejectionToWarn, hasRejectionToWarn, @@ -151,10 +147,6 @@ function runMicrotask() { const callback = this.callback; try { callback(); - } catch (error) { - // runInAsyncScope() swallows the error so we need to catch - // it and handle it here. - triggerUncaughtException(error, false /* fromPromise */); } finally { this.emitDestroy(); } From e199fc553454c9c10841bf960dc0051608668a47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Santos?= Date: Sun, 14 Jun 2020 22:01:18 +0100 Subject: [PATCH 35/43] module: fix error message about importing names from cjs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When importing specific names from a CJS module, and renaming them using `as`, the example fix in the error message erroneously contains the keyword `as` in the destructuring variable declaration. Example of this issue: import { parse as acornParse } from "acorn"; ^^^^^ SyntaxError: The requested module 'acorn' is expected to be of type CommonJS, which does not support named exports. CommonJS modules can be imported by importing the default export. For example: import pkg from 'acorn'; const { parse as acornParse } = pkg; PR-URL: https://github.com/nodejs/node/pull/33882 Reviewed-By: Anna Henningsen Reviewed-By: Michaël Zasso Reviewed-By: Guy Bedford Reviewed-By: Myles Borins --- lib/internal/modules/esm/module_job.js | 4 +++- test/es-module/test-esm-cjs-named-error.mjs | 14 ++++++++++++++ .../package-cjs-named-error/renamed-import.mjs | 1 + 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 test/fixtures/es-modules/package-cjs-named-error/renamed-import.mjs diff --git a/lib/internal/modules/esm/module_job.js b/lib/internal/modules/esm/module_job.js index b00a4fb2a58a7f..4ffa8db9dab903 100644 --- a/lib/internal/modules/esm/module_job.js +++ b/lib/internal/modules/esm/module_job.js @@ -8,6 +8,7 @@ const { SafePromise, StringPrototypeIncludes, StringPrototypeMatch, + StringPrototypeReplace, StringPrototypeSplit, } = primordials; @@ -109,13 +110,14 @@ class ModuleJob { if (format === 'commonjs') { const importStatement = splitStack[1]; const namedImports = StringPrototypeMatch(importStatement, /{.*}/)[0]; + const destructuringAssignment = StringPrototypeReplace(namedImports, /\s+as\s+/g, ': '); e.message = `The requested module '${childSpecifier}' is expected ` + 'to be of type CommonJS, which does not support named exports. ' + 'CommonJS modules can be imported by importing the default ' + 'export.\n' + 'For example:\n' + `import pkg from '${childSpecifier}';\n` + - `const ${namedImports} = pkg;`; + `const ${destructuringAssignment} = pkg;`; const newStack = StringPrototypeSplit(e.stack, '\n'); newStack[3] = `SyntaxError: ${e.message}`; e.stack = ArrayPrototypeJoin(newStack, '\n'); diff --git a/test/es-module/test-esm-cjs-named-error.mjs b/test/es-module/test-esm-cjs-named-error.mjs index d34f762dfb4e26..d71dc959e21fb7 100644 --- a/test/es-module/test-esm-cjs-named-error.mjs +++ b/test/es-module/test-esm-cjs-named-error.mjs @@ -10,6 +10,13 @@ const expectedRelative = 'The requested module \'./fail.cjs\' is expected to ' + 'import pkg from \'./fail.cjs\';\n' + 'const { comeOn } = pkg;'; +const expectedRenamed = 'The requested module \'./fail.cjs\' is expected to ' + + 'be of type CommonJS, which does not support named exports. CommonJS ' + + 'modules can be imported by importing the default export.\n' + + 'For example:\n' + + 'import pkg from \'./fail.cjs\';\n' + + 'const { comeOn: comeOnRenamed } = pkg;'; + const expectedPackageHack = 'The requested module \'./json-hack/fail.js\' is ' + 'expected to be of type CommonJS, which does not support named exports. ' + 'CommonJS modules can be imported by importing the default export.\n' + @@ -38,6 +45,13 @@ rejects(async () => { message: expectedRelative }, 'should support relative specifiers with double quotes'); +rejects(async () => { + await import(`${fixtureBase}/renamed-import.mjs`); +}, { + name: 'SyntaxError', + message: expectedRenamed +}, 'should correctly format named imports with renames'); + rejects(async () => { await import(`${fixtureBase}/json-hack.mjs`); }, { diff --git a/test/fixtures/es-modules/package-cjs-named-error/renamed-import.mjs b/test/fixtures/es-modules/package-cjs-named-error/renamed-import.mjs new file mode 100644 index 00000000000000..dc601f28fe2f99 --- /dev/null +++ b/test/fixtures/es-modules/package-cjs-named-error/renamed-import.mjs @@ -0,0 +1 @@ +import { comeOn as comeOnRenamed } from "./fail.cjs" From fdcd4893ff91f359433fb7526d4e3133260de777 Mon Sep 17 00:00:00 2001 From: Pranshu Srivastava Date: Tue, 16 Jun 2020 23:22:15 +0530 Subject: [PATCH 36/43] http2: always call callback on Http2ServerResponse#end Fixes: https://github.com/nodejs/node/issues/28001 PR-URL: https://github.com/nodejs/node/pull/33911 Reviewed-By: Anna Henningsen Reviewed-By: Denys Otrishko Reviewed-By: Luigi Pinca --- lib/internal/http2/compat.js | 13 ++++++++----- .../test-http2-compat-serverresponse-end.js | 12 ++++++------ 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/lib/internal/http2/compat.js b/lib/internal/http2/compat.js index 6a2d5097116cc2..373311547a3350 100644 --- a/lib/internal/http2/compat.js +++ b/lib/internal/http2/compat.js @@ -709,11 +709,6 @@ class Http2ServerResponse extends Stream { const stream = this[kStream]; const state = this[kState]; - if ((state.closed || state.ending) && - state.headRequest === stream.headRequest) { - return this; - } - if (typeof chunk === 'function') { cb = chunk; chunk = null; @@ -722,6 +717,14 @@ class Http2ServerResponse extends Stream { encoding = 'utf8'; } + if ((state.closed || state.ending) && + state.headRequest === stream.headRequest) { + if (typeof cb === 'function') { + process.nextTick(cb); + } + return this; + } + if (chunk !== null && chunk !== undefined) this.write(chunk, encoding); diff --git a/test/parallel/test-http2-compat-serverresponse-end.js b/test/parallel/test-http2-compat-serverresponse-end.js index 8505d6c4969db1..52d4c603e95e04 100644 --- a/test/parallel/test-http2-compat-serverresponse-end.js +++ b/test/parallel/test-http2-compat-serverresponse-end.js @@ -25,16 +25,16 @@ const { // but callback will only be called once const server = createServer(mustCall((request, response) => { response.end('end', 'utf8', mustCall(() => { - response.end(mustNotCall()); + response.end(mustCall()); process.nextTick(() => { - response.end(mustNotCall()); + response.end(mustCall()); server.close(); }); })); response.on('finish', mustCall(() => { - response.end(mustNotCall()); + response.end(mustCall()); })); - response.end(mustNotCall()); + response.end(mustCall()); })); server.listen(0, mustCall(() => { let data = ''; @@ -294,7 +294,7 @@ const { })); response.end('data', mustCall(() => { strictEqual(finished, false); - response.end('data', mustNotCall()); + response.end('data', mustCall()); })); })); server.listen(0, mustCall(() => { @@ -328,7 +328,7 @@ const { // Should be able to respond to HEAD with just .end const server = createServer(mustCall((request, response) => { response.end('data', mustCall()); - response.end(mustNotCall()); + response.end(mustCall()); })); server.listen(0, mustCall(() => { const { port } = server.address(); From 343cf1b3c176c01ea992d5db0145f34b3e2066b6 Mon Sep 17 00:00:00 2001 From: Pragyan Das Date: Fri, 29 May 2020 01:07:50 +0530 Subject: [PATCH 37/43] doc: update WASM code sample - Code sample updated by adding a hello-world (`demo.wat`) code example - Step for compiling `.wat` to `.wasm` added (with reference to `wabt` tools) - The sample code prints "hello world\n" in the console This update adds a very minimal change to the existing sample and can be treated as an extension. PR-URL: https://github.com/nodejs/node/pull/33626 Reviewed-By: Anna Henningsen Reviewed-By: Rich Trott --- doc/api/wasi.md | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/doc/api/wasi.md b/doc/api/wasi.md index 3f9c6c7f51bcb9..b46aacfbcb1ecb 100644 --- a/doc/api/wasi.md +++ b/doc/api/wasi.md @@ -22,15 +22,54 @@ const wasi = new WASI({ const importObject = { wasi_snapshot_preview1: wasi.wasiImport }; (async () => { - const wasm = await WebAssembly.compile(fs.readFileSync('./binary.wasm')); + const wasm = await WebAssembly.compile(fs.readFileSync('./demo.wasm')); const instance = await WebAssembly.instantiate(wasm, importObject); wasi.start(instance); })(); ``` +To run the above example, create a new WebAssembly text format file named +`demo.wat`: + +```text +(module + ;; Import the required fd_write WASI function which will write the given io vectors to stdout + ;; The function signature for fd_write is: + ;; (File Descriptor, *iovs, iovs_len, nwritten) -> Returns number of bytes written + (import "wasi_snapshot_preview1" "fd_write" (func $fd_write (param i32 i32 i32 i32) (result i32))) + + (memory 1) + (export "memory" (memory 0)) + + ;; Write 'hello world\n' to memory at an offset of 8 bytes + ;; Note the trailing newline which is required for the text to appear + (data (i32.const 8) "hello world\n") + + (func $main (export "_start") + ;; Creating a new io vector within linear memory + (i32.store (i32.const 0) (i32.const 8)) ;; iov.iov_base - This is a pointer to the start of the 'hello world\n' string + (i32.store (i32.const 4) (i32.const 12)) ;; iov.iov_len - The length of the 'hello world\n' string + + (call $fd_write + (i32.const 1) ;; file_descriptor - 1 for stdout + (i32.const 0) ;; *iovs - The pointer to the iov array, which is stored at memory location 0 + (i32.const 1) ;; iovs_len - We're printing 1 string stored in an iov - so one. + (i32.const 20) ;; nwritten - A place in memory to store the number of bytes written + ) + drop ;; Discard the number of bytes written from the top of the stack + ) +) +``` + +Use [wabt](https://github.com/WebAssembly/wabt) to compile `.wat` to `.wasm` + +```console +$ wat2wasm demo.wat +``` + The `--experimental-wasi-unstable-preview1` and `--experimental-wasm-bigint` -CLI arguments are needed for the previous example to run. +CLI arguments are needed for this example to run. ## Class: `WASI`