diff --git a/.eslintignore b/.eslintignore index 9b1d8b3f35d..dc4e023866f 100644 --- a/.eslintignore +++ b/.eslintignore @@ -3,6 +3,7 @@ lib/internal/v8_prof_polyfill.js lib/punycode.js test/addons/??_* test/fixtures +test/message/esm_display_syntax_error.mjs tools/eslint tools/icu tools/remark-* diff --git a/BUILDING.md b/BUILDING.md index cd819b4b477..e6d575f8273 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -209,6 +209,8 @@ Prerequisites: * Basic Unix tools required for some tests, [Git for Windows](http://git-scm.com/download/win) includes Git Bash and tools which can be included in the global `PATH`. +* **Optional** (to build the MSI): the [WiX Toolset v3.11](http://wixtoolset.org/releases/) + and the [Wix Toolset Visual Studio 2017 Extension](https://marketplace.visualstudio.com/items?itemName=RobMensching.WixToolsetVisualStudio2017Extension). If the path to your build directory contains a space, the build will likely fail. diff --git a/COLLABORATOR_GUIDE.md b/COLLABORATOR_GUIDE.md index d8d0651ff53..c65317dd570 100644 --- a/COLLABORATOR_GUIDE.md +++ b/COLLABORATOR_GUIDE.md @@ -127,7 +127,7 @@ can be fast-tracked and may be landed after a shorter delay: * Focused changes that affect only documentation and/or the test suite. `code-and-learn` and `good-first-issue` pull requests typically fall into this category. -* Changes that revert commit(s) and/or fix regressions. +* Changes that fix regressions. When a pull request is deemed suitable to be fast-tracked, label it with `fast-track`. The pull request can be landed once 2 or more collaborators diff --git a/Makefile b/Makefile index eab0d9afadd..219ad188deb 100644 --- a/Makefile +++ b/Makefile @@ -501,6 +501,7 @@ test-v8: v8 --no-presubmit \ --shell-dir=$(PWD)/deps/v8/out/$(V8_ARCH).$(BUILDTYPE_LOWER) \ $(TAP_V8) + git clean -fdxq -- deps/v8 @echo Testing hash seed $(MAKE) test-hash-seed diff --git a/benchmark/timers/immediate.js b/benchmark/timers/immediate.js index 3ba4429260f..bbe81555cac 100644 --- a/benchmark/timers/immediate.js +++ b/benchmark/timers/immediate.js @@ -2,7 +2,7 @@ const common = require('../common.js'); const bench = common.createBenchmark(main, { - thousands: [2000], + thousands: [5000], type: ['depth', 'depth1', 'breadth', 'breadth1', 'breadth4', 'clear'] }); @@ -88,6 +88,7 @@ function breadth1(N) { // concurrent setImmediate, 4 arguments function breadth4(N) { + N /= 2; var n = 0; bench.start(); function cb(a1, a2, a3, a4) { @@ -101,6 +102,7 @@ function breadth4(N) { } function clear(N) { + N *= 4; bench.start(); function cb(a1) { if (a1 === 2) diff --git a/benchmark/timers/set-immediate-breadth-args.js b/benchmark/timers/set-immediate-breadth-args.js index 6a904e2675d..348cb62fb2c 100644 --- a/benchmark/timers/set-immediate-breadth-args.js +++ b/benchmark/timers/set-immediate-breadth-args.js @@ -19,9 +19,9 @@ function main(conf) { bench.start(); for (let i = 0; i < N; i++) { if (i % 3 === 0) - setImmediate(cb3, 512, true, null); + setImmediate(cb3, 512, true, null, 512, true, null); else if (i % 2 === 0) - setImmediate(cb2, false, 5.1); + setImmediate(cb2, false, 5.1, 512); else setImmediate(cb1, 0); } diff --git a/benchmark/timers/set-immediate-depth-args.js b/benchmark/timers/set-immediate-depth-args.js index 1f12ae6ec73..704b1814514 100644 --- a/benchmark/timers/set-immediate-depth-args.js +++ b/benchmark/timers/set-immediate-depth-args.js @@ -2,7 +2,7 @@ const common = require('../common.js'); const bench = common.createBenchmark(main, { - millions: [10] + millions: [5] }); function main(conf) { @@ -15,9 +15,9 @@ function main(conf) { function cb3(n, arg2, arg3) { if (--n) { if (n % 3 === 0) - setImmediate(cb3, n, true, null); + setImmediate(cb3, n, true, null, 5.1, null, true); else if (n % 2 === 0) - setImmediate(cb2, n, 5.1); + setImmediate(cb2, n, 5.1, true); else setImmediate(cb1, n); } @@ -25,9 +25,9 @@ function main(conf) { function cb2(n, arg2) { if (--n) { if (n % 3 === 0) - setImmediate(cb3, n, true, null); + setImmediate(cb3, n, true, null, 5.1, null, true); else if (n % 2 === 0) - setImmediate(cb2, n, 5.1); + setImmediate(cb2, n, 5.1, true); else setImmediate(cb1, n); } @@ -35,9 +35,9 @@ function main(conf) { function cb1(n) { if (--n) { if (n % 3 === 0) - setImmediate(cb3, n, true, null); + setImmediate(cb3, n, true, null, 5.1, null, true); else if (n % 2 === 0) - setImmediate(cb2, n, 5.1); + setImmediate(cb2, n, 5.1, true); else setImmediate(cb1, n); } diff --git a/benchmark/timers/timers-breadth.js b/benchmark/timers/timers-breadth.js index 036c8781914..02ebd5bb0d0 100644 --- a/benchmark/timers/timers-breadth.js +++ b/benchmark/timers/timers-breadth.js @@ -2,7 +2,7 @@ const common = require('../common.js'); const bench = common.createBenchmark(main, { - thousands: [500], + thousands: [5000], }); function main(conf) { diff --git a/benchmark/timers/timers-cancel-pooled.js b/benchmark/timers/timers-cancel-pooled.js index 2e834cc4db5..23cef153876 100644 --- a/benchmark/timers/timers-cancel-pooled.js +++ b/benchmark/timers/timers-cancel-pooled.js @@ -3,11 +3,11 @@ const common = require('../common.js'); const assert = require('assert'); const bench = common.createBenchmark(main, { - thousands: [500], + millions: [5], }); function main(conf) { - const iterations = +conf.thousands * 1e3; + const iterations = +conf.millions * 1e6; var timer = setTimeout(() => {}, 1); for (var i = 0; i < iterations; i++) { @@ -24,7 +24,7 @@ function main(conf) { clearTimeout(timer); } - bench.end(iterations / 1e3); + bench.end(iterations / 1e6); } function cb() { diff --git a/benchmark/timers/timers-cancel-unpooled.js b/benchmark/timers/timers-cancel-unpooled.js index ca3c0dbcd92..50931e35124 100644 --- a/benchmark/timers/timers-cancel-unpooled.js +++ b/benchmark/timers/timers-cancel-unpooled.js @@ -3,11 +3,11 @@ const common = require('../common.js'); const assert = require('assert'); const bench = common.createBenchmark(main, { - thousands: [100], + millions: [1], }); function main(conf) { - const iterations = +conf.thousands * 1e3; + const iterations = +conf.millions * 1e6; const timersList = []; for (var i = 0; i < iterations; i++) { @@ -18,7 +18,7 @@ function main(conf) { for (var j = 0; j < iterations + 1; j++) { clearTimeout(timersList[j]); } - bench.end(iterations / 1e3); + bench.end(iterations / 1e6); } function cb() { diff --git a/benchmark/timers/timers-insert-pooled.js b/benchmark/timers/timers-insert-pooled.js index 80335a150a2..8bbc84290ad 100644 --- a/benchmark/timers/timers-insert-pooled.js +++ b/benchmark/timers/timers-insert-pooled.js @@ -2,11 +2,11 @@ const common = require('../common.js'); const bench = common.createBenchmark(main, { - thousands: [500], + millions: [5], }); function main(conf) { - const iterations = +conf.thousands * 1e3; + const iterations = +conf.millions * 1e6; bench.start(); @@ -14,5 +14,5 @@ function main(conf) { setTimeout(() => {}, 1); } - bench.end(iterations / 1e3); + bench.end(iterations / 1e6); } diff --git a/benchmark/timers/timers-insert-unpooled.js b/benchmark/timers/timers-insert-unpooled.js index 98415625862..efe8e9aaa57 100644 --- a/benchmark/timers/timers-insert-unpooled.js +++ b/benchmark/timers/timers-insert-unpooled.js @@ -3,11 +3,11 @@ const common = require('../common.js'); const assert = require('assert'); const bench = common.createBenchmark(main, { - thousands: [100], + millions: [1], }); function main(conf) { - const iterations = +conf.thousands * 1e3; + const iterations = +conf.millions * 1e6; const timersList = []; @@ -15,7 +15,7 @@ function main(conf) { for (var i = 0; i < iterations; i++) { timersList.push(setTimeout(cb, i + 1)); } - bench.end(iterations / 1e3); + bench.end(iterations / 1e6); for (var j = 0; j < iterations + 1; j++) { clearTimeout(timersList[j]); diff --git a/benchmark/timers/timers-timeout-pooled.js b/benchmark/timers/timers-timeout-pooled.js index 2e8753a0c3c..d39c8cf969a 100644 --- a/benchmark/timers/timers-timeout-pooled.js +++ b/benchmark/timers/timers-timeout-pooled.js @@ -1,23 +1,35 @@ 'use strict'; const common = require('../common.js'); +// The following benchmark measures setting up n * 1e6 timeouts, +// which then get executed on the next uv tick + const bench = common.createBenchmark(main, { - thousands: [500], + millions: [10], }); function main(conf) { - const iterations = +conf.thousands * 1e3; - var count = 0; + const iterations = +conf.millions * 1e6; + let count = 0; - for (var i = 0; i < iterations; i++) { - setTimeout(cb, 1); - } - - bench.start(); + // Function tracking on the hidden class in V8 can cause misleading + // results in this benchmark if only a single function is used — + // alternate between two functions for a fairer benchmark function cb() { count++; if (count === iterations) - bench.end(iterations / 1e3); + bench.end(iterations / 1e6); } + function cb2() { + count++; + if (count === iterations) + bench.end(iterations / 1e6); + } + + for (var i = 0; i < iterations; i++) { + setTimeout(i % 2 ? cb : cb2, 1); + } + + bench.start(); } diff --git a/doc/api/cli.md b/doc/api/cli.md index 2d8cccb8a46..b8ea4826dec 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -183,6 +183,10 @@ added: v0.10 Aborting instead of exiting causes a core file to be generated for post-mortem analysis using a debugger (such as `lldb`, `gdb`, and `mdb`). +*Note*: If this flag is passed, the behavior can still be set to not abort +through [`process.setUncaughtExceptionCaptureCallback()`][] (and through usage +of the `domain` module that uses it). + ### `--trace-warnings` + +The `'close'` event is emitted when the `Http2Stream` is destroyed. Once +this event is emitted, the `Http2Stream` instance is no longer usable. + +The listener callback is passed a single argument specifying the HTTP/2 error +code specified when closing the stream. If the code is any value other than +`NGHTTP2_NO_ERROR` (`0`), an `'error'` event will also be emitted. + #### Event: 'error' - -The `'streamClosed'` event is emitted when the `Http2Stream` is destroyed. Once -this event is emitted, the `Http2Stream` instance is no longer usable. - -The listener callback is passed a single argument specifying the HTTP/2 error -code specified when closing the stream. If the code is any value other than -`NGHTTP2_NO_ERROR` (`0`), an `'error'` event will also be emitted. - #### Event: 'timeout' + +* Returns: {boolean} + +Indicates whether a callback has been set using +[`process.setUncaughtExceptionCaptureCallback()`][]. + ## process.hrtime([time]) + +* `fn` {Function|null} + +The `process.setUncaughtExceptionCapture` function sets a function that will +be invoked when an uncaught exception occurs, which will receive the exception +value itself as its first argument. + +If such a function is set, the [`process.on('uncaughtException')`][] event will +not be emitted. If `--abort-on-uncaught-exception` was passed from the +command line or set through [`v8.setFlagsFromString()`][], the process will +not abort. + +To unset the capture function, `process.setUncaughtExceptionCapture(null)` +may be used. Calling this method with a non-`null` argument while another +capture function is set will throw an error. + +*Note*: Using this function is mutually exclusive with using the +deprecated [`domain`][] built-in module. + ## process.stderr * {Stream} @@ -1921,6 +1954,7 @@ cases: [`JSON.stringify` spec]: https://tc39.github.io/ecma262/#sec-json.stringify [`console.error()`]: console.html#console_console_error_data_args [`console.log()`]: console.html#console_console_log_data_args +[`domain`]: domain.html [`end()`]: stream.html#stream_writable_end_chunk_encoding_callback [`net.Server`]: net.html#net_class_net_server [`net.Socket`]: net.html#net_class_net_socket @@ -1930,11 +1964,14 @@ cases: [`process.exit()`]: #process_process_exit_code [`process.exitCode`]: #process_process_exitcode [`process.kill()`]: #process_process_kill_pid_signal +[`process.on('uncaughtException')`]: process.html#process_event_uncaughtexception +[`process.setUncaughtExceptionCaptureCallback()`]: process.html#process_process_setuncaughtexceptioncapturecallback_fn [`promise.catch()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch [`require()`]: globals.html#globals_require [`require.main`]: modules.html#modules_accessing_the_main_module [`require.resolve()`]: modules.html#modules_require_resolve_request_options [`setTimeout(fn, 0)`]: timers.html#timers_settimeout_callback_delay_args +[`v8.setFlagsFromString()`]: v8.html#v8_v8_setflagsfromstring_flags [Child Process]: child_process.html [Cluster]: cluster.html [Duplex]: stream.html#stream_duplex_and_transform_streams diff --git a/doc/api/stream.md b/doc/api/stream.md index f8d9df9e6ac..dec38202221 100644 --- a/doc/api/stream.md +++ b/doc/api/stream.md @@ -2229,7 +2229,7 @@ object mode has an interesting side effect. Because it *is* a call to However, because the argument is an empty string, no data is added to the readable buffer so there is nothing for a user to consume. -### `highWaterMark` discrepency after calling `readable.setEncoding()` +### `highWaterMark` discrepancy after calling `readable.setEncoding()` The use of `readable.setEncoding()` will change the behavior of how the `highWaterMark` operates in non-object mode. @@ -2280,7 +2280,7 @@ contain multi-byte characters. [fs write streams]: fs.html#fs_class_fs_writestream [http-incoming-message]: http.html#http_class_http_incomingmessage [zlib]: zlib.html -[hwm-gotcha]: #stream_highwatermark_discrepency_after_calling_readable_setencoding +[hwm-gotcha]: #stream_highwatermark_discrepancy_after_calling_readable_setencoding [stream-_flush]: #stream_transform_flush_callback [stream-_read]: #stream_readable_read_size_1 [stream-_transform]: #stream_transform_transform_chunk_encoding_callback diff --git a/doc/api/tls.md b/doc/api/tls.md index 4274c36d20d..bba044a36d4 100644 --- a/doc/api/tls.md +++ b/doc/api/tls.md @@ -1164,8 +1164,7 @@ added: v0.11.13 --> The default curve name to use for ECDH key agreement in a tls server. The -default value is `'prime256v1'` (NIST P-256). Consult [RFC 4492] and -[FIPS.186-4] for more details. +default value is `'auto'`. See [`tls.createSecureContext()`] for further information. ## Deprecated APIs @@ -1296,13 +1295,11 @@ where `secure_socket` has the same API as `pair.cleartext`. [Chrome's 'modern cryptography' setting]: https://www.chromium.org/Home/chromium-security/education/tls#TOC-Cipher-Suites [DHE]: https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange [ECDHE]: https://en.wikipedia.org/wiki/Elliptic_curve_Diffie%E2%80%93Hellman -[FIPS.186-4]: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf [Forward secrecy]: https://en.wikipedia.org/wiki/Perfect_forward_secrecy [OCSP request]: https://en.wikipedia.org/wiki/OCSP_stapling [OpenSSL Options]: crypto.html#crypto_openssl_options [OpenSSL cipher list format documentation]: https://www.openssl.org/docs/man1.0.2/apps/ciphers.html#CIPHER-LIST-FORMAT [Perfect Forward Secrecy]: #tls_perfect_forward_secrecy -[RFC 4492]: https://www.rfc-editor.org/rfc/rfc4492.txt [SSL_CTX_set_timeout]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_set_timeout.html [SSL_METHODS]: https://www.openssl.org/docs/man1.0.2/ssl/ssl.html#DEALING-WITH-PROTOCOL-METHODS [Stream]: stream.html#stream_stream diff --git a/lib/domain.js b/lib/domain.js index dc3c550866c..6c85ca2b172 100644 --- a/lib/domain.js +++ b/lib/domain.js @@ -28,6 +28,7 @@ const util = require('util'); const EventEmitter = require('events'); +const errors = require('internal/errors'); const { createHook } = require('async_hooks'); // communicate with events module, but don't require that @@ -81,19 +82,77 @@ const asyncHook = createHook({ } }); +// When domains are in use, they claim full ownership of the +// uncaught exception capture callback. +if (process.hasUncaughtExceptionCaptureCallback()) { + throw new errors.Error('ERR_DOMAIN_CALLBACK_NOT_AVAILABLE'); +} + +// Get the stack trace at the point where `domain` was required. +const domainRequireStack = new Error('require(`domain`) at this point').stack; + +const { setUncaughtExceptionCaptureCallback } = process; +process.setUncaughtExceptionCaptureCallback = function(fn) { + const err = + new errors.Error('ERR_DOMAIN_CANNOT_SET_UNCAUGHT_EXCEPTION_CAPTURE'); + err.stack = err.stack + '\n' + '-'.repeat(40) + '\n' + domainRequireStack; + throw err; +}; + // It's possible to enter one domain while already inside // another one. The stack is each entered domain. const stack = []; exports._stack = stack; -process._setupDomainUse(stack); +process._setupDomainUse(); -class Domain extends EventEmitter { +function updateExceptionCapture() { + if (stack.every((domain) => domain.listenerCount('error') === 0)) { + setUncaughtExceptionCaptureCallback(null); + } else { + setUncaughtExceptionCaptureCallback(null); + setUncaughtExceptionCaptureCallback((er) => { + return process.domain._errorHandler(er); + }); + } +} + + +process.on('newListener', (name, listener) => { + if (name === 'uncaughtException' && + listener !== domainUncaughtExceptionClear) { + // Make sure the first listener for `uncaughtException` always clears + // the domain stack. + process.removeListener(name, domainUncaughtExceptionClear); + process.prependListener(name, domainUncaughtExceptionClear); + } +}); + +process.on('removeListener', (name, listener) => { + if (name === 'uncaughtException' && + listener !== domainUncaughtExceptionClear) { + // If the domain listener would be the only remaining one, remove it. + const listeners = process.listeners('uncaughtException'); + if (listeners.length === 1 && listeners[0] === domainUncaughtExceptionClear) + process.removeListener(name, domainUncaughtExceptionClear); + } +}); +function domainUncaughtExceptionClear() { + stack.length = 0; + exports.active = process.domain = null; + updateExceptionCapture(); +} + + +class Domain extends EventEmitter { constructor() { super(); this.members = []; asyncHook.enable(); + + this.on('removeListener', updateExceptionCapture); + this.on('newListener', updateExceptionCapture); } } @@ -131,14 +190,14 @@ Domain.prototype._errorHandler = function _errorHandler(er) { // prevent the process 'uncaughtException' event from being emitted // if a listener is set. if (EventEmitter.listenerCount(this, 'error') > 0) { + // Clear the uncaughtExceptionCaptureCallback so that we know that, even + // if technically the top-level domain is still active, it would + // be ok to abort on an uncaught exception at this point + setUncaughtExceptionCaptureCallback(null); try { - // Set the _emittingTopLevelDomainError so that we know that, even - // if technically the top-level domain is still active, it would - // be ok to abort on an uncaught exception at this point - process._emittingTopLevelDomainError = true; caught = this.emit('error', er); } finally { - process._emittingTopLevelDomainError = false; + updateExceptionCapture(); } } } else { @@ -161,11 +220,13 @@ Domain.prototype._errorHandler = function _errorHandler(er) { if (this === exports.active) { stack.pop(); } + updateExceptionCapture(); if (stack.length) { exports.active = process.domain = stack[stack.length - 1]; - caught = process._fatalException(er2); + caught = process.domain._errorHandler(er2); } else { - caught = false; + // Pass on to the next exception handler. + throw er2; } } } @@ -173,8 +234,7 @@ Domain.prototype._errorHandler = function _errorHandler(er) { // Exit all domains on the stack. Uncaught exceptions end the // current tick and no domains should be left on the stack // between ticks. - stack.length = 0; - exports.active = process.domain = null; + domainUncaughtExceptionClear(); return caught; }; @@ -185,6 +245,7 @@ Domain.prototype.enter = function() { // to push it onto the stack so that we can pop it later. exports.active = process.domain = this; stack.push(this); + updateExceptionCapture(); }; @@ -198,6 +259,7 @@ Domain.prototype.exit = function() { exports.active = stack[stack.length - 1]; process.domain = exports.active; + updateExceptionCapture(); }; diff --git a/lib/internal/bootstrap_node.js b/lib/internal/bootstrap_node.js index 9f2c09e606d..466a04963cf 100644 --- a/lib/internal/bootstrap_node.js +++ b/lib/internal/bootstrap_node.js @@ -9,6 +9,7 @@ (function(process) { let internalBinding; + const exceptionHandlerState = { captureFn: null }; function startup() { const EventEmitter = NativeModule.require('events'); @@ -49,6 +50,7 @@ const _process = NativeModule.require('internal/process'); _process.setupConfig(NativeModule._source); _process.setupSignalHandlers(); + _process.setupUncaughtExceptionCapture(exceptionHandlerState); NativeModule.require('internal/process/warning').setup(); NativeModule.require('internal/process/next_tick').setup(); NativeModule.require('internal/process/stdio').setup(); @@ -392,8 +394,10 @@ // that threw and was never cleared. So clear it now. async_id_fields[kInitTriggerAsyncId] = 0; - if (process.domain && process.domain._errorHandler) - caught = process.domain._errorHandler(er); + if (exceptionHandlerState.captureFn !== null) { + exceptionHandlerState.captureFn(er); + caught = true; + } if (!caught) caught = process.emit('uncaughtException', er); diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 619732df5d6..0a02c5e10cc 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -291,6 +291,13 @@ E('ERR_CRYPTO_SIGN_KEY_REQUIRED', 'No key provided to sign'); E('ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH', 'Input buffers must have the same length'); E('ERR_DNS_SET_SERVERS_FAILED', 'c-ares failed to set servers: "%s" [%s]'); +E('ERR_DOMAIN_CALLBACK_NOT_AVAILABLE', + 'A callback was registered through ' + + 'process.setUncaughtExceptionCaptureCallback(), which is mutually ' + + 'exclusive with using the `domain` module'); +E('ERR_DOMAIN_CANNOT_SET_UNCAUGHT_EXCEPTION_CAPTURE', + 'The `domain` module is in use, which is mutually exclusive with calling ' + + 'process.setUncaughtExceptionCaptureCallback()'); E('ERR_ENCODING_INVALID_ENCODED_DATA', 'The encoded data was not valid for encoding %s'); E('ERR_ENCODING_NOT_SUPPORTED', 'The "%s" encoding is not supported'); @@ -461,6 +468,9 @@ E('ERR_TRANSFORM_ALREADY_TRANSFORMING', 'Calling transform done when still transforming'); E('ERR_TRANSFORM_WITH_LENGTH_0', 'Calling transform done when writableState.length != 0'); +E('ERR_UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET', + '`process.setupUncaughtExceptionCapture()` was called while a capture ' + + 'callback was already active'); E('ERR_UNESCAPED_CHARACTERS', '%s contains unescaped characters'); E('ERR_UNHANDLED_ERROR', (err) => { diff --git a/lib/internal/http2/compat.js b/lib/internal/http2/compat.js index 89a22270086..ec1f0ba64ef 100644 --- a/lib/internal/http2/compat.js +++ b/lib/internal/http2/compat.js @@ -122,20 +122,6 @@ function onStreamDrain() { response.emit('drain'); } -// TODO Http2Stream does not emit 'close' -function onStreamClosedRequest() { - const request = this[kRequest]; - if (request !== undefined) - request.push(null); -} - -// TODO Http2Stream does not emit 'close' -function onStreamClosedResponse() { - const response = this[kResponse]; - if (response !== undefined) - response.emit('finish'); -} - function onStreamAbortedRequest() { const request = this[kRequest]; if (request !== undefined && request[kState].closed === false) { @@ -247,10 +233,9 @@ class Http2ServerRequest extends Readable { stream.on('trailers', onStreamTrailers); stream.on('end', onStreamEnd); stream.on('error', onStreamError); - stream.on('close', onStreamClosedRequest); stream.on('aborted', onStreamAbortedRequest); const onfinish = this[kFinish].bind(this); - stream.on('streamClosed', onfinish); + stream.on('close', onfinish); stream.on('finish', onfinish); this.on('pause', onRequestPause); this.on('resume', onRequestResume); @@ -380,10 +365,9 @@ class Http2ServerResponse extends Stream { stream[kResponse] = this; this.writable = true; stream.on('drain', onStreamDrain); - stream.on('close', onStreamClosedResponse); stream.on('aborted', onStreamAbortedResponse); const onfinish = this[kFinish].bind(this); - stream.on('streamClosed', onfinish); + stream.on('close', onfinish); stream.on('finish', onfinish); } diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js index 39f67c48238..78446d2fb6e 100644 --- a/lib/internal/http2/core.js +++ b/lib/internal/http2/core.js @@ -28,6 +28,8 @@ const { _connectionListener: httpConnectionListener } = require('http'); const { createPromise, promiseResolve } = process.binding('util'); const debug = util.debuglog('http2'); +const kMaxFrameSize = (2 ** 24) - 1; +const kMaxInt = (2 ** 32) - 1; const kMaxStreams = (2 ** 31) - 1; const { @@ -224,10 +226,8 @@ function onStreamTrailers() { return headersList; } -// Called when the stream is closed. The streamClosed event is emitted on the -// Http2Stream instance. Note that this event is distinctly different than the -// require('stream') interface 'close' event which deals with the state of the -// Readable and Writable sides of the Duplex. +// Called when the stream is closed. The close event is emitted on the +// Http2Stream instance function onStreamClose(code) { const stream = this[kOwner]; stream[kUpdateTimer](); @@ -332,9 +332,9 @@ function emitGoaway(self, code, lastStreamID, buf) { return; if (!state.shuttingDown && !state.shutdown) { self.shutdown({}, self.destroy.bind(self)); - } else { - self.destroy(); + return; } + self.destroy(); } // Called by the native layer when a goaway frame has been received @@ -582,14 +582,15 @@ function doShutdown(options) { function submitShutdown(options) { const type = this[kType]; debug(`Http2Session ${sessionName(type)}: submitting shutdown request`); + const shutdownFn = doShutdown.bind(this, options); if (type === NGHTTP2_SESSION_SERVER && options.graceful === true) { // first send a shutdown notice this[kHandle].shutdownNotice(); // then, on flip of the event loop, do the actual shutdown - setImmediate(doShutdown.bind(this), options); - } else { - doShutdown.call(this, options); + setImmediate(shutdownFn); + return; } + shutdownFn(); } function finishSessionDestroy(socket) { @@ -658,6 +659,33 @@ function pingCallback(cb) { }; } +function validateSettings(settings) { + settings = Object.assign({}, settings); + assertWithinRange('headerTableSize', + settings.headerTableSize, + 0, kMaxInt); + assertWithinRange('initialWindowSize', + settings.initialWindowSize, + 0, kMaxInt); + assertWithinRange('maxFrameSize', + settings.maxFrameSize, + 16384, kMaxFrameSize); + assertWithinRange('maxConcurrentStreams', + settings.maxConcurrentStreams, + 0, kMaxStreams); + assertWithinRange('maxHeaderListSize', + settings.maxHeaderListSize, + 0, kMaxInt); + if (settings.enablePush !== undefined && + typeof settings.enablePush !== 'boolean') { + const err = new errors.TypeError('ERR_HTTP2_INVALID_SETTING_VALUE', + 'enablePush', settings.enablePush); + err.actual = settings.enablePush; + throw err; + } + return settings; +} + // Upon creation, the Http2Session takes ownership of the socket. The session // may not be ready to use immediately if the socket is not yet fully connected. class Http2Session extends EventEmitter { @@ -707,7 +735,9 @@ class Http2Session extends EventEmitter { const setupFn = setupHandle(this, socket, type, options); if (socket.connecting) { this[kState].connecting = true; - socket.once('connect', setupFn); + const connectEvent = + socket instanceof tls.TLSSocket ? 'secureConnect' : 'connect'; + socket.once(connectEvent, setupFn); } else { setupFn(); } @@ -839,29 +869,7 @@ class Http2Session extends EventEmitter { // Validate the input first assertIsObject(settings, 'settings'); - settings = Object.assign(Object.create(null), settings); - assertWithinRange('headerTableSize', - settings.headerTableSize, - 0, 2 ** 32 - 1); - assertWithinRange('initialWindowSize', - settings.initialWindowSize, - 0, 2 ** 32 - 1); - assertWithinRange('maxFrameSize', - settings.maxFrameSize, - 16384, 2 ** 24 - 1); - assertWithinRange('maxConcurrentStreams', - settings.maxConcurrentStreams, - 0, kMaxStreams); - assertWithinRange('maxHeaderListSize', - settings.maxHeaderListSize, - 0, 2 ** 32 - 1); - if (settings.enablePush !== undefined && - typeof settings.enablePush !== 'boolean') { - const err = new errors.TypeError('ERR_HTTP2_INVALID_SETTING_VALUE', - 'enablePush', settings.enablePush); - err.actual = settings.enablePush; - throw err; - } + settings = validateSettings(settings); if (state.pendingAck === state.maxPendingAck) { throw new errors.Error('ERR_HTTP2_MAX_PENDING_SETTINGS_ACK', this[kState].pendingAck); @@ -869,11 +877,12 @@ class Http2Session extends EventEmitter { debug(`Http2Session ${sessionName(this[kType])}: sending settings`); state.pendingAck++; + const settingsFn = submitSettings.bind(this, settings); if (state.connecting) { - this.once('connect', submitSettings.bind(this, settings)); + this.once('connect', settingsFn); return; } - submitSettings.call(this, settings); + settingsFn(); } // Destroy the Http2Session @@ -959,13 +968,14 @@ class Http2Session extends EventEmitter { this.on('shutdown', callback); } + const shutdownFn = submitShutdown.bind(this, options); if (state.connecting) { - this.once('connect', submitShutdown.bind(this, options)); + this.once('connect', shutdownFn); return; } debug(`Http2Session ${sessionName(type)}: sending shutdown`); - submitShutdown.call(this, options); + shutdownFn(); } _onTimeout() { @@ -1366,15 +1376,15 @@ class Http2Stream extends Duplex { rstStream(code = NGHTTP2_NO_ERROR) { if (typeof code !== 'number') throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'code', 'number'); - if (code < 0 || code > 2 ** 32 - 1) + if (code < 0 || code > kMaxInt) throw new errors.RangeError('ERR_OUT_OF_RANGE', 'code'); - const fn = submitRstStream.bind(this, code); + const rstStreamFn = submitRstStream.bind(this, code); if (this[kID] === undefined) { - this.once('ready', fn); + this.once('ready', rstStreamFn); return; } - fn(); + rstStreamFn(); } rstWithNoError() { @@ -1405,12 +1415,12 @@ class Http2Stream extends Duplex { options = Object.assign({}, options); validatePriorityOptions(options); - const fn = submitPriority.bind(this, options); + const priorityFn = submitPriority.bind(this, options); if (this[kID] === undefined) { - this.once('ready', fn); + this.once('ready', priorityFn); return; } - fn(); + priorityFn(); } // Called by this.destroy(). @@ -1473,7 +1483,7 @@ function continueStreamDestroy(err, callback) { abort(this); this.push(null); // Close the readable side this.end(); // Close the writable side - process.nextTick(emit, this, 'streamClosed', code); + process.nextTick(emit, this, 'close', code); } function finishStreamDestroy() { @@ -2357,30 +2367,7 @@ function createServer(options, handler) { // HTTP2-Settings header frame. function getPackedSettings(settings) { assertIsObject(settings, 'settings'); - settings = settings || Object.create(null); - assertWithinRange('headerTableSize', - settings.headerTableSize, - 0, 2 ** 32 - 1); - assertWithinRange('initialWindowSize', - settings.initialWindowSize, - 0, 2 ** 32 - 1); - assertWithinRange('maxFrameSize', - settings.maxFrameSize, - 16384, 2 ** 24 - 1); - assertWithinRange('maxConcurrentStreams', - settings.maxConcurrentStreams, - 0, kMaxStreams); - assertWithinRange('maxHeaderListSize', - settings.maxHeaderListSize, - 0, 2 ** 32 - 1); - if (settings.enablePush !== undefined && - typeof settings.enablePush !== 'boolean') { - const err = new errors.TypeError('ERR_HTTP2_INVALID_SETTING_VALUE', - 'enablePush', settings.enablePush); - err.actual = settings.enablePush; - throw err; - } - updateSettingsBuffer(settings); + updateSettingsBuffer(validateSettings(settings)); return binding.packSettings(); } @@ -2391,7 +2378,7 @@ function getUnpackedSettings(buf, options = {}) { } if (buf.length % 6 !== 0) throw new errors.RangeError('ERR_HTTP2_INVALID_PACKED_SETTINGS_LENGTH'); - const settings = Object.create(null); + const settings = {}; let offset = 0; while (offset < buf.length) { const id = buf.readUInt16BE(offset); @@ -2402,7 +2389,7 @@ function getUnpackedSettings(buf, options = {}) { settings.headerTableSize = value; break; case NGHTTP2_SETTINGS_ENABLE_PUSH: - settings.enablePush = value; + settings.enablePush = value !== 0; break; case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: settings.maxConcurrentStreams = value; @@ -2420,30 +2407,8 @@ function getUnpackedSettings(buf, options = {}) { offset += 4; } - if (options != null && options.validate) { - assertWithinRange('headerTableSize', - settings.headerTableSize, - 0, 2 ** 32 - 1); - assertWithinRange('enablePush', - settings.enablePush, - 0, 1); - assertWithinRange('initialWindowSize', - settings.initialWindowSize, - 0, 2 ** 32 - 1); - assertWithinRange('maxFrameSize', - settings.maxFrameSize, - 16384, 2 ** 24 - 1); - assertWithinRange('maxConcurrentStreams', - settings.maxConcurrentStreams, - 0, kMaxStreams); - assertWithinRange('maxHeaderListSize', - settings.maxHeaderListSize, - 0, 2 ** 32 - 1); - } - - if (settings.enablePush !== undefined) { - settings.enablePush = !!settings.enablePush; - } + if (options != null && options.validate) + validateSettings(settings); return settings; } diff --git a/lib/internal/process.js b/lib/internal/process.js index 7c4a8a75dc2..08e33e276cd 100644 --- a/lib/internal/process.js +++ b/lib/internal/process.js @@ -284,6 +284,34 @@ function setupRawDebug() { }; } + +function setupUncaughtExceptionCapture(exceptionHandlerState) { + // This is a typed array for faster communication with JS. + const shouldAbortOnUncaughtToggle = process._shouldAbortOnUncaughtToggle; + delete process._shouldAbortOnUncaughtToggle; + + process.setUncaughtExceptionCaptureCallback = function(fn) { + if (fn === null) { + exceptionHandlerState.captureFn = fn; + shouldAbortOnUncaughtToggle[0] = 1; + return; + } + if (typeof fn !== 'function') { + throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'fn', + ['Function', 'null']); + } + if (exceptionHandlerState.captureFn !== null) { + throw new errors.Error('ERR_UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET'); + } + exceptionHandlerState.captureFn = fn; + shouldAbortOnUncaughtToggle[0] = 0; + }; + + process.hasUncaughtExceptionCaptureCallback = function() { + return exceptionHandlerState.captureFn !== null; + }; +} + module.exports = { setup_performance, setup_cpuUsage, @@ -293,5 +321,6 @@ module.exports = { setupKillAndExit, setupSignalHandlers, setupChannel, - setupRawDebug + setupRawDebug, + setupUncaughtExceptionCapture }; diff --git a/lib/module.js b/lib/module.js index b22ba4fe663..f404c4317d7 100644 --- a/lib/module.js +++ b/lib/module.js @@ -23,6 +23,7 @@ const NativeModule = require('native_module'); const util = require('util'); +const { decorateErrorStack } = require('internal/util'); const internalModule = require('internal/module'); const { getURLFromFilePath } = require('internal/url'); const vm = require('vm'); @@ -31,7 +32,7 @@ const fs = require('fs'); const internalFS = require('internal/fs'); const path = require('path'); const { - internalModuleReadFile, + internalModuleReadJSON, internalModuleStat } = process.binding('fs'); const preserveSymlinks = !!process.binding('config').preserveSymlinks; @@ -122,7 +123,7 @@ function readPackage(requestPath) { return entry; const jsonPath = path.resolve(requestPath, 'package.json'); - const json = internalModuleReadFile(path.toNamespacedPath(jsonPath)); + const json = internalModuleReadJSON(path.toNamespacedPath(jsonPath)); if (json === undefined) { return false; @@ -474,6 +475,7 @@ Module._load = function(request, parent, isMain) { await ESMLoader.import(getURLFromFilePath(request).pathname); })() .catch((e) => { + decorateErrorStack(e); console.error(e); process.exit(1); }); diff --git a/lib/timers.js b/lib/timers.js index 3c522e76f1a..d0828669d77 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -182,10 +182,12 @@ function insert(item, unrefed) { item._destroyed = false; item[async_id_symbol] = ++async_id_fields[kAsyncIdCounter]; item[trigger_async_id_symbol] = initTriggerId(); - if (async_hook_fields[kInit] > 0) - emitInit( - item[async_id_symbol], 'Timeout', item[trigger_async_id_symbol], item - ); + if (async_hook_fields[kInit] > 0) { + emitInit(item[async_id_symbol], + 'Timeout', + item[trigger_async_id_symbol], + item); + } } L.append(list, item); @@ -430,74 +432,47 @@ function setTimeout(callback, after, arg1, arg2, arg3) { throw new errors.TypeError('ERR_INVALID_CALLBACK'); } - var len = arguments.length; - var args; - if (len === 3) { - args = [arg1]; - } else if (len === 4) { - args = [arg1, arg2]; - } else if (len > 4) { - args = [arg1, arg2, arg3]; - for (var i = 5; i < len; i++) - // extend array dynamically, makes .apply run much faster in v6.0.0 - args[i - 2] = arguments[i]; + var i, args; + switch (arguments.length) { + // fast cases + case 1: + case 2: + break; + case 3: + args = [arg1]; + break; + case 4: + args = [arg1, arg2]; + break; + default: + args = [arg1, arg2, arg3]; + for (i = 5; i < arguments.length; i++) { + // extend array dynamically, makes .apply run much faster in v6.0.0 + args[i - 2] = arguments[i]; + } + break; } - return createSingleTimeout(callback, after, args); + return new Timeout(callback, after, args, false); } setTimeout[internalUtil.promisify.custom] = function(after, value) { const promise = createPromise(); - createSingleTimeout(promise, after, [value]); + new Timeout(promise, after, [value], false); return promise; }; exports.setTimeout = setTimeout; -function createSingleTimeout(callback, after, args) { - after *= 1; // coalesce to number or NaN - if (!(after >= 1 && after <= TIMEOUT_MAX)) { - if (after > TIMEOUT_MAX) { - process.emitWarning(`${after} does not fit into` + - ' a 32-bit signed integer.' + - '\nTimeout duration was set to 1.', - 'TimeoutOverflowWarning'); - } - after = 1; // schedule on next tick, follows browser behavior - } - - var timer = new Timeout(after, callback, args); - if (process.domain) - timer.domain = process.domain; - - active(timer); - - return timer; -} - function ontimeout(timer) { var args = timer._timerArgs; - var callback = timer._onTimeout; - if (typeof callback !== 'function') - return promiseResolve(callback, args[0]); + if (typeof timer._onTimeout !== 'function') + return promiseResolve(timer._onTimeout, args[0]); if (!args) timer._onTimeout(); - else { - switch (args.length) { - case 1: - timer._onTimeout(args[0]); - break; - case 2: - timer._onTimeout(args[0], args[1]); - break; - case 3: - timer._onTimeout(args[0], args[1], args[2]); - break; - default: - Function.prototype.apply.call(callback, timer, args); - } - } + else + Reflect.apply(timer._onTimeout, timer, args); if (timer._repeat) rearm(timer); } @@ -534,44 +509,30 @@ exports.setInterval = function(callback, repeat, arg1, arg2, arg3) { throw new errors.TypeError('ERR_INVALID_CALLBACK'); } - var len = arguments.length; - var args; - if (len === 3) { - args = [arg1]; - } else if (len === 4) { - args = [arg1, arg2]; - } else if (len > 4) { - args = [arg1, arg2, arg3]; - for (var i = 5; i < len; i++) - // extend array dynamically, makes .apply run much faster in v6.0.0 - args[i - 2] = arguments[i]; + var i, args; + switch (arguments.length) { + // fast cases + case 1: + case 2: + break; + case 3: + args = [arg1]; + break; + case 4: + args = [arg1, arg2]; + break; + default: + args = [arg1, arg2, arg3]; + for (i = 5; i < arguments.length; i++) { + // extend array dynamically, makes .apply run much faster in v6.0.0 + args[i - 2] = arguments[i]; + } + break; } - return createRepeatTimeout(callback, repeat, args); + return new Timeout(callback, repeat, args, true); }; -function createRepeatTimeout(callback, repeat, args) { - repeat *= 1; // coalesce to number or NaN - if (!(repeat >= 1 && repeat <= TIMEOUT_MAX)) { - if (repeat > TIMEOUT_MAX) { - process.emitWarning(`${repeat} does not fit into` + - ' a 32-bit signed integer.' + - '\nInterval duration was set to 1.', - 'TimeoutOverflowWarning'); - } - repeat = 1; // schedule on next tick, follows browser behavior - } - - var timer = new Timeout(repeat, callback, args); - timer._repeat = repeat; - if (process.domain) - timer.domain = process.domain; - - active(timer); - - return timer; -} - exports.clearInterval = function(timer) { if (timer && timer._repeat) { timer._repeat = null; @@ -580,22 +541,41 @@ exports.clearInterval = function(timer) { }; -function Timeout(after, callback, args) { +function Timeout(callback, after, args, isRepeat) { + after *= 1; // coalesce to number or NaN + if (!(after >= 1 && after <= TIMEOUT_MAX)) { + if (after > TIMEOUT_MAX) { + process.emitWarning(`${after} does not fit into` + + ' a 32-bit signed integer.' + + '\nTimeout duration was set to 1.', + 'TimeoutOverflowWarning'); + } + after = 1; // schedule on next tick, follows browser behavior + } + this._called = false; this._idleTimeout = after; this._idlePrev = this; this._idleNext = this; this._idleStart = null; + // this must be set to null first to avoid function tracking + // on the hidden class, revisit in V8 versions after 6.2 + this._onTimeout = null; this._onTimeout = callback; this._timerArgs = args; - this._repeat = null; + this._repeat = isRepeat ? after : null; this._destroyed = false; + this[async_id_symbol] = ++async_id_fields[kAsyncIdCounter]; this[trigger_async_id_symbol] = initTriggerId(); - if (async_hook_fields[kInit] > 0) - emitInit( - this[async_id_symbol], 'Timeout', this[trigger_async_id_symbol], this - ); + if (async_hook_fields[kInit] > 0) { + emitInit(this[async_id_symbol], + 'Timeout', + this[trigger_async_id_symbol], + this); + } + + active(this); } @@ -653,9 +633,7 @@ Timeout.prototype.ref = function() { Timeout.prototype.close = function() { this._onTimeout = null; if (this._handle) { - // Fewer checks may be possible, but these cover everything. if (async_hook_fields[kDestroy] > 0 && - this && typeof this[async_id_symbol] === 'number' && !this._destroyed) { emitDestroy(this[async_id_symbol]); @@ -796,42 +774,38 @@ function tryOnImmediate(immediate, oldTail) { function runCallback(timer) { const argv = timer._argv; - const argc = argv ? argv.length : 0; if (typeof timer._onImmediate !== 'function') return promiseResolve(timer._onImmediate, argv[0]); - switch (argc) { - // fast-path callbacks with 0-3 arguments - case 0: - return timer._onImmediate(); - case 1: - return timer._onImmediate(argv[0]); - case 2: - return timer._onImmediate(argv[0], argv[1]); - case 3: - return timer._onImmediate(argv[0], argv[1], argv[2]); - // more than 3 arguments run slower with .apply - default: - return Function.prototype.apply.call(timer._onImmediate, timer, argv); - } + if (!argv) + return timer._onImmediate(); + Reflect.apply(timer._onImmediate, timer, argv); } -function Immediate() { - // assigning the callback here can cause optimize/deoptimize thrashing - // so have caller annotate the object (node v6.0.0, v8 5.0.71.35) +function Immediate(callback, args) { this._idleNext = null; this._idlePrev = null; + // this must be set to null first to avoid function tracking + // on the hidden class, revisit in V8 versions after 6.2 this._onImmediate = null; - this._argv = null; - this._onImmediate = null; + this._onImmediate = callback; + this._argv = args; this._destroyed = false; - this.domain = process.domain; + this[async_id_symbol] = ++async_id_fields[kAsyncIdCounter]; this[trigger_async_id_symbol] = initTriggerId(); - if (async_hook_fields[kInit] > 0) - emitInit( - this[async_id_symbol], 'Immediate', this[trigger_async_id_symbol], this - ); + if (async_hook_fields[kInit] > 0) { + emitInit(this[async_id_symbol], + 'Immediate', + this[trigger_async_id_symbol], + this); + } + + if (scheduledImmediateCount[0] === 0) + activateImmediateCheck(); + scheduledImmediateCount[0]++; + + immediateQueue.append(this); } function setImmediate(callback, arg1, arg2, arg3) { @@ -840,7 +814,6 @@ function setImmediate(callback, arg1, arg2, arg3) { } var i, args; - switch (arguments.length) { // fast cases case 1: @@ -853,37 +826,24 @@ function setImmediate(callback, arg1, arg2, arg3) { break; default: args = [arg1, arg2, arg3]; - for (i = 4; i < arguments.length; i++) + for (i = 4; i < arguments.length; i++) { // extend array dynamically, makes .apply run much faster in v6.0.0 args[i - 1] = arguments[i]; + } break; } - return createImmediate(args, callback); + + return new Immediate(callback, args); } setImmediate[internalUtil.promisify.custom] = function(value) { const promise = createPromise(); - createImmediate([value], promise); + new Immediate(promise, [value]); return promise; }; exports.setImmediate = setImmediate; -function createImmediate(args, callback) { - // declaring it `const immediate` causes v6.0.0 to deoptimize this function - var immediate = new Immediate(); - immediate._argv = args; - immediate._onImmediate = callback; - - if (scheduledImmediateCount[0] === 0) - activateImmediateCheck(); - scheduledImmediateCount[0]++; - - immediateQueue.append(immediate); - - return immediate; -} - exports.clearImmediate = function(immediate) { if (!immediate) return; diff --git a/lib/tls.js b/lib/tls.js index a82535df618..5b20cade2e3 100644 --- a/lib/tls.js +++ b/lib/tls.js @@ -45,7 +45,7 @@ exports.SLAB_BUFFER_SIZE = 10 * 1024 * 1024; exports.DEFAULT_CIPHERS = process.binding('constants').crypto.defaultCipherList; -exports.DEFAULT_ECDH_CURVE = 'prime256v1'; +exports.DEFAULT_ECDH_CURVE = 'auto'; exports.getCiphers = internalUtil.cachedResult( () => internalUtil.filterDuplicateStrings(binding.getSSLCiphers(), true) diff --git a/src/async_wrap.cc b/src/async_wrap.cc index c5e97bd4a66..3f77169321a 100644 --- a/src/async_wrap.cc +++ b/src/async_wrap.cc @@ -140,7 +140,7 @@ RetainedObjectInfo* WrapperInfo(uint16_t class_id, Local wrapper) { static void DestroyAsyncIdsCallback(Environment* env, void* data) { Local fn = env->async_hooks_destroy_function(); - TryCatch try_catch(env->isolate()); + FatalTryCatch try_catch(env); do { std::vector destroy_async_id_list; @@ -153,11 +153,8 @@ static void DestroyAsyncIdsCallback(Environment* env, void* data) { MaybeLocal ret = fn->Call( env->context(), Undefined(env->isolate()), 1, &async_id_value); - if (ret.IsEmpty()) { - ClearFatalExceptionHandlers(env); - FatalException(env->isolate(), try_catch); - UNREACHABLE(); - } + if (ret.IsEmpty()) + return; } } while (!env->destroy_async_id_list()->empty()); } @@ -171,14 +168,8 @@ void AsyncWrap::EmitPromiseResolve(Environment* env, double async_id) { Local async_id_value = Number::New(env->isolate(), async_id); Local fn = env->async_hooks_promise_resolve_function(); - TryCatch try_catch(env->isolate()); - MaybeLocal ar = fn->Call( - env->context(), Undefined(env->isolate()), 1, &async_id_value); - if (ar.IsEmpty()) { - ClearFatalExceptionHandlers(env); - FatalException(env->isolate(), try_catch); - UNREACHABLE(); - } + FatalTryCatch try_catch(env); + USE(fn->Call(env->context(), Undefined(env->isolate()), 1, &async_id_value)); } @@ -205,14 +196,8 @@ void AsyncWrap::EmitBefore(Environment* env, double async_id) { Local async_id_value = Number::New(env->isolate(), async_id); Local fn = env->async_hooks_before_function(); - TryCatch try_catch(env->isolate()); - MaybeLocal ar = fn->Call( - env->context(), Undefined(env->isolate()), 1, &async_id_value); - if (ar.IsEmpty()) { - ClearFatalExceptionHandlers(env); - FatalException(env->isolate(), try_catch); - UNREACHABLE(); - } + FatalTryCatch try_catch(env); + USE(fn->Call(env->context(), Undefined(env->isolate()), 1, &async_id_value)); } @@ -241,14 +226,8 @@ void AsyncWrap::EmitAfter(Environment* env, double async_id) { // end of _fatalException(). Local async_id_value = Number::New(env->isolate(), async_id); Local fn = env->async_hooks_after_function(); - TryCatch try_catch(env->isolate()); - MaybeLocal ar = fn->Call( - env->context(), Undefined(env->isolate()), 1, &async_id_value); - if (ar.IsEmpty()) { - ClearFatalExceptionHandlers(env); - FatalException(env->isolate(), try_catch); - UNREACHABLE(); - } + FatalTryCatch try_catch(env); + USE(fn->Call(env->context(), Undefined(env->isolate()), 1, &async_id_value)); } class PromiseWrap : public AsyncWrap { @@ -748,14 +727,8 @@ void AsyncWrap::EmitAsyncInit(Environment* env, object, }; - TryCatch try_catch(env->isolate()); - MaybeLocal ret = init_fn->Call( - env->context(), object, arraysize(argv), argv); - - if (ret.IsEmpty()) { - ClearFatalExceptionHandlers(env); - FatalException(env->isolate(), try_catch); - } + FatalTryCatch try_catch(env); + USE(init_fn->Call(env->context(), object, arraysize(argv), argv)); } diff --git a/src/env-inl.h b/src/env-inl.h index 8ae24cfb285..cd248e57312 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -268,6 +268,7 @@ inline Environment::Environment(IsolateData* isolate_data, emit_napi_warning_(true), makecallback_cntr_(0), scheduled_immediate_count_(isolate_, 1), + should_abort_on_uncaught_toggle_(isolate_, 1), #if HAVE_INSPECTOR inspector_agent_(new inspector::Agent(this)), #endif @@ -305,6 +306,9 @@ inline Environment::Environment(IsolateData* isolate_data, performance_state_->milestones[ performance::NODE_PERFORMANCE_MILESTONE_V8_START] = performance::performance_v8_start; + + // By default, always abort when --abort-on-uncaught-exception was passed. + should_abort_on_uncaught_toggle_[0] = 1; } inline Environment::~Environment() { @@ -399,6 +403,11 @@ inline void Environment::set_abort_on_uncaught_exception(bool value) { abort_on_uncaught_exception_ = value; } +inline AliasedBuffer& +Environment::should_abort_on_uncaught_toggle() { + return should_abort_on_uncaught_toggle_; +} + inline std::vector* Environment::destroy_async_id_list() { return &destroy_async_id_list_; } diff --git a/src/env.h b/src/env.h index d1bf07afcad..0129a009e5b 100644 --- a/src/env.h +++ b/src/env.h @@ -134,7 +134,6 @@ class ModuleWrap; V(dns_txt_string, "TXT") \ V(domain_string, "domain") \ V(emit_string, "emit") \ - V(emitting_top_level_domain_error_string, "_emittingTopLevelDomainError") \ V(exchange_string, "exchange") \ V(enumerable_string, "enumerable") \ V(idle_string, "idle") \ @@ -309,7 +308,6 @@ class ModuleWrap; V(internal_binding_cache_object, v8::Object) \ V(buffer_prototype_object, v8::Object) \ V(context, v8::Context) \ - V(domains_stack_array, v8::Array) \ V(http2ping_constructor_template, v8::ObjectTemplate) \ V(http2stream_constructor_template, v8::ObjectTemplate) \ V(inspector_console_api_object, v8::Object) \ @@ -569,8 +567,15 @@ class Environment { void PrintSyncTrace() const; inline void set_trace_sync_io(bool value); + // This stores whether the --abort-on-uncaught-exception flag was passed + // to Node. inline bool abort_on_uncaught_exception() const; inline void set_abort_on_uncaught_exception(bool value); + // This is a pseudo-boolean that keeps track of whether an uncaught exception + // should abort the process or not if --abort-on-uncaught-exception was + // passed to Node. If the flag was not passed, it is ignored. + inline AliasedBuffer& + should_abort_on_uncaught_toggle(); // The necessary API for async_hooks. inline double new_async_id(); @@ -718,6 +723,7 @@ class Environment { std::vector destroy_async_id_list_; AliasedBuffer scheduled_immediate_count_; + AliasedBuffer should_abort_on_uncaught_toggle_; performance::performance_state* performance_state_ = nullptr; std::map performance_marks_; diff --git a/src/module_wrap.cc b/src/module_wrap.cc index dfba4d5b300..0e1f7c9eaf8 100644 --- a/src/module_wrap.cc +++ b/src/module_wrap.cc @@ -103,9 +103,17 @@ void ModuleWrap::New(const FunctionCallbackInfo& args) { False(isolate), // is opaque (?) False(isolate), // is WASM True(isolate)); // is ES6 module + TryCatch try_catch(isolate); ScriptCompiler::Source source(source_text, origin); - if (!ScriptCompiler::CompileModule(isolate, &source).ToLocal(&module)) + if (!ScriptCompiler::CompileModule(isolate, &source).ToLocal(&module)) { + CHECK(try_catch.HasCaught()); + CHECK(!try_catch.Message().IsEmpty()); + CHECK(!try_catch.Exception().IsEmpty()); + AppendExceptionLine(env, try_catch.Exception(), try_catch.Message(), + ErrorHandlingMode::MODULE_ERROR); + try_catch.ReThrow(); return; + } } Local that = args.This(); diff --git a/src/node.cc b/src/node.cc index 1763b59e618..a4ed073be78 100644 --- a/src/node.cc +++ b/src/node.cc @@ -790,66 +790,13 @@ void* ArrayBufferAllocator::Allocate(size_t size) { namespace { -bool DomainHasErrorHandler(const Environment* env, - const Local& domain) { - HandleScope scope(env->isolate()); - - Local domain_event_listeners_v = domain->Get(env->events_string()); - if (!domain_event_listeners_v->IsObject()) - return false; - - Local domain_event_listeners_o = - domain_event_listeners_v.As(); - - Local domain_error_listeners_v = - domain_event_listeners_o->Get(env->error_string()); - - if (domain_error_listeners_v->IsFunction() || - (domain_error_listeners_v->IsArray() && - domain_error_listeners_v.As()->Length() > 0)) - return true; - - return false; -} - -bool DomainsStackHasErrorHandler(const Environment* env) { - HandleScope scope(env->isolate()); - - if (!env->using_domains()) - return false; - - Local domains_stack_array = env->domains_stack_array().As(); - if (domains_stack_array->Length() == 0) - return false; - - uint32_t domains_stack_length = domains_stack_array->Length(); - for (uint32_t i = domains_stack_length; i > 0; --i) { - Local domain_v = domains_stack_array->Get(i - 1); - if (!domain_v->IsObject()) - return false; - - Local domain = domain_v.As(); - if (DomainHasErrorHandler(env, domain)) - return true; - } - - return false; -} - - bool ShouldAbortOnUncaughtException(Isolate* isolate) { HandleScope scope(isolate); - Environment* env = Environment::GetCurrent(isolate); - Local process_object = env->process_object(); - Local emitting_top_level_domain_error_key = - env->emitting_top_level_domain_error_string(); - bool isEmittingTopLevelDomainError = - process_object->Get(emitting_top_level_domain_error_key)->BooleanValue(); - - return isEmittingTopLevelDomainError || !DomainsStackHasErrorHandler(env); + return env->should_abort_on_uncaught_toggle()[0]; } + Local GetDomainProperty(Environment* env, Local object) { Local domain_v = object->GetPrivate(env->context(), env->domain_private_symbol()) @@ -899,9 +846,6 @@ void SetupDomainUse(const FunctionCallbackInfo& args) { HandleScope scope(env->isolate()); - CHECK(args[0]->IsArray()); - env->set_domains_stack_array(args[0].As()); - // Do a little housekeeping. env->process_object()->Delete( env->context(), @@ -1482,6 +1426,8 @@ void AppendExceptionLine(Environment* env, static void ReportException(Environment* env, Local er, Local message) { + CHECK(!er.IsEmpty()); + CHECK(!message.IsEmpty()); HandleScope scope(env->isolate()); AppendExceptionLine(env, er, message, FATAL_ERROR); @@ -1551,6 +1497,10 @@ static void ReportException(Environment* env, } fflush(stderr); + +#if HAVE_INSPECTOR + env->inspector_agent()->FatalException(er, message); +#endif } @@ -2433,6 +2383,15 @@ NO_RETURN void FatalError(const char* location, const char* message) { } +FatalTryCatch::~FatalTryCatch() { + if (HasCaught()) { + HandleScope scope(env_->isolate()); + ReportException(env_, *this); + exit(7); + } +} + + void FatalException(Isolate* isolate, Local error, Local message) { @@ -2475,9 +2434,6 @@ void FatalException(Isolate* isolate, } if (exit_code) { -#if HAVE_INSPECTOR - env->inspector_agent()->FatalException(error, message); -#endif exit(exit_code); } } @@ -2497,25 +2453,6 @@ static void OnMessage(Local message, Local error) { FatalException(Isolate::GetCurrent(), error, message); } - -void ClearFatalExceptionHandlers(Environment* env) { - Local process = env->process_object(); - Local events = - process->Get(env->context(), env->events_string()).ToLocalChecked(); - - if (events->IsObject()) { - events.As()->Set( - env->context(), - OneByteString(env->isolate(), "uncaughtException"), - Undefined(env->isolate())).FromJust(); - } - - process->Set( - env->context(), - env->domain_string(), - Undefined(env->isolate())).FromJust(); -} - // Call process.emitWarning(str), fmt is a snprintf() format string void ProcessEmitWarning(Environment* env, const char* fmt, ...) { char warning[1024]; @@ -3207,6 +3144,13 @@ void SetupProcessObject(Environment* env, scheduled_immediate_count, env->scheduled_immediate_count().GetJSArray()).FromJust()); + auto should_abort_on_uncaught_toggle = + FIXED_ONE_BYTE_STRING(env->isolate(), "_shouldAbortOnUncaughtToggle"); + CHECK(process->Set(env->context(), + should_abort_on_uncaught_toggle, + env->should_abort_on_uncaught_toggle().GetJSArray()) + .FromJust()); + // -e, --eval if (eval_string) { READONLY_PROPERTY(process, diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 1b3e5004f71..23dd4a21b8c 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -1820,7 +1820,7 @@ static Local X509ToObject(Environment* env, X509* cert) { String::NewFromUtf8(env->isolate(), mem->data, String::kNormalString, mem->length)); } - (void) BIO_reset(bio); + USE(BIO_reset(bio)); X509_NAME* issuer_name = X509_get_issuer_name(cert); if (X509_NAME_print_ex(bio, issuer_name, 0, X509_NAME_FLAGS) > 0) { @@ -1829,7 +1829,7 @@ static Local X509ToObject(Environment* env, X509* cert) { String::NewFromUtf8(env->isolate(), mem->data, String::kNormalString, mem->length)); } - (void) BIO_reset(bio); + USE(BIO_reset(bio)); int nids[] = { NID_subject_alt_name, NID_info_access }; Local keys[] = { env->subjectaltname_string(), @@ -1856,7 +1856,7 @@ static Local X509ToObject(Environment* env, X509* cert) { String::NewFromUtf8(env->isolate(), mem->data, String::kNormalString, mem->length)); - (void) BIO_reset(bio); + USE(BIO_reset(bio)); } EVP_PKEY* pkey = X509_get_pubkey(cert); @@ -1873,7 +1873,7 @@ static Local X509ToObject(Environment* env, X509* cert) { info->Set(env->modulus_string(), String::NewFromUtf8(env->isolate(), mem->data, String::kNormalString, mem->length)); - (void) BIO_reset(bio); + USE(BIO_reset(bio)); uint64_t exponent_word = static_cast(BN_get_word(e)); uint32_t lo = static_cast(exponent_word); @@ -1887,7 +1887,7 @@ static Local X509ToObject(Environment* env, X509* cert) { info->Set(env->exponent_string(), String::NewFromUtf8(env->isolate(), mem->data, String::kNormalString, mem->length)); - (void) BIO_reset(bio); + USE(BIO_reset(bio)); } if (pkey != nullptr) { @@ -1904,7 +1904,7 @@ static Local X509ToObject(Environment* env, X509* cert) { info->Set(env->valid_from_string(), String::NewFromUtf8(env->isolate(), mem->data, String::kNormalString, mem->length)); - (void) BIO_reset(bio); + USE(BIO_reset(bio)); ASN1_TIME_print(bio, X509_get_notAfter(cert)); BIO_get_mem_ptr(bio, &mem); @@ -2882,7 +2882,7 @@ int Connection::HandleBIOError(BIO *bio, const char* func, int rv) { return rv; int retry = BIO_should_retry(bio); - (void) retry; // unused if !defined(SSL_PRINT_DEBUG) + USE(retry); // unused if !defined(SSL_PRINT_DEBUG) if (BIO_should_write(bio)) { DEBUG_PRINT("[%p] BIO: %s want write. should retry %d\n", @@ -5358,7 +5358,7 @@ void ECDH::SetPrivateKey(const FunctionCallbackInfo& args) { EC_KEY_set_public_key(ecdh->key_, nullptr); MarkPopErrorOnReturn mark_pop_error_on_return; - (void) &mark_pop_error_on_return; // Silence compiler warning. + USE(&mark_pop_error_on_return); const BIGNUM* priv_key = EC_KEY_get0_private_key(ecdh->key_); CHECK_NE(priv_key, nullptr); @@ -5421,7 +5421,7 @@ bool ECDH::IsKeyValidForCurve(const BIGNUM* private_key) { bool ECDH::IsKeyPairValid() { MarkPopErrorOnReturn mark_pop_error_on_return; - (void) &mark_pop_error_on_return; // Silence compiler warning. + USE(&mark_pop_error_on_return); return 1 == EC_KEY_check_key(key_); } diff --git a/src/node_file.cc b/src/node_file.cc index 384215d5f6b..643846d2735 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -542,7 +542,7 @@ void FillStatsArray(v8::Local fields_array, // a string or undefined when the file cannot be opened. Returns an empty // string when the file does not contain the substring '"main"' because that // is the property we care about. -static void InternalModuleReadFile(const FunctionCallbackInfo& args) { +static void InternalModuleReadJSON(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); uv_loop_t* loop = env->event_loop(); @@ -1514,7 +1514,7 @@ void InitFs(Local target, env->SetMethod(target, "rmdir", RMDir); env->SetMethod(target, "mkdir", MKDir); env->SetMethod(target, "readdir", ReadDir); - env->SetMethod(target, "internalModuleReadFile", InternalModuleReadFile); + env->SetMethod(target, "internalModuleReadJSON", InternalModuleReadJSON); env->SetMethod(target, "internalModuleStat", InternalModuleStat); env->SetMethod(target, "stat", Stat); env->SetMethod(target, "lstat", LStat); diff --git a/src/node_internals.h b/src/node_internals.h index 1c2cd47ac15..0561bc07465 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -273,7 +273,7 @@ constexpr size_t arraysize(const T(&)[N]) { return N; } bool IsExceptionDecorated(Environment* env, v8::Local er); -enum ErrorHandlingMode { FATAL_ERROR, CONTEXTIFY_ERROR }; +enum ErrorHandlingMode { CONTEXTIFY_ERROR, FATAL_ERROR, MODULE_ERROR }; void AppendExceptionLine(Environment* env, v8::Local er, v8::Local message, @@ -281,6 +281,17 @@ void AppendExceptionLine(Environment* env, NO_RETURN void FatalError(const char* location, const char* message); +// Like a `TryCatch` but exits the process if an exception was caught. +class FatalTryCatch : public v8::TryCatch { + public: + explicit FatalTryCatch(Environment* env) + : TryCatch(env->isolate()), env_(env) {} + ~FatalTryCatch(); + + private: + Environment* env_; +}; + void ProcessEmitWarning(Environment* env, const char* fmt, ...); void FillStatsArray(v8::Local fields_array, @@ -336,11 +347,6 @@ class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator { uint32_t zero_fill_field_ = 1; // Boolean but exposed as uint32 to JS land. }; -// Clear any domain and/or uncaughtException handlers to force the error's -// propagation and shutdown the process. Use this to force the process to exit -// by clearing all callbacks that could handle the error. -void ClearFatalExceptionHandlers(Environment* env); - namespace Buffer { v8::MaybeLocal Copy(Environment* env, const char* data, size_t len); v8::MaybeLocal New(Environment* env, size_t size); diff --git a/src/node_url.cc b/src/node_url.cc index 67c6986da87..c9c8ccd5797 100644 --- a/src/node_url.cc +++ b/src/node_url.cc @@ -2172,19 +2172,16 @@ const Local URL::ToObject(Environment* env) const { }; SetArgs(env, argv, &context_); - TryCatch try_catch(isolate); - - // The SetURLConstructor method must have been called already to - // set the constructor function used below. SetURLConstructor is - // called automatically when the internal/url.js module is loaded - // during the internal/bootstrap_node.js processing. - MaybeLocal ret = - env->url_constructor_function() - ->Call(env->context(), undef, 9, argv); - - if (ret.IsEmpty()) { - ClearFatalExceptionHandlers(env); - FatalException(isolate, try_catch); + MaybeLocal ret; + { + FatalTryCatch try_catch(env); + + // The SetURLConstructor method must have been called already to + // set the constructor function used below. SetURLConstructor is + // called automatically when the internal/url.js module is loaded + // during the internal/bootstrap_node.js processing. + ret = env->url_constructor_function() + ->Call(env->context(), undef, arraysize(argv), argv); } return ret.ToLocalChecked(); diff --git a/src/util.h b/src/util.h index 08308d837fb..eb060a57b88 100644 --- a/src/util.h +++ b/src/util.h @@ -428,6 +428,9 @@ class BufferValue : public MaybeStackBuffer { if (name##_length > 0) \ CHECK_NE(name##_data, nullptr); +// Use this when a variable or parameter is unused in order to explicitly +// silence a compiler warning about that. +template inline void USE(T&&) {} } // namespace node diff --git a/test/common/README.md b/test/common/README.md index 54604568681..e6371265d8b 100644 --- a/test/common/README.md +++ b/test/common/README.md @@ -376,11 +376,6 @@ Synchronous version of `spawnPwd`. The realpath of the 'tmp' directory. -### tmpDirName -* return [<String>] - -Name of the temp directory used by tests. - ## Countdown Module The `Countdown` module provides a simple countdown mechanism for tests that diff --git a/test/common/index.js b/test/common/index.js index 047434a252f..a76e641141d 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -40,7 +40,7 @@ exports.fixturesDir = fixturesDir; // Using a `.` prefixed name, which is the convention for "hidden" on POSIX, // gets tools to ignore it by default or by simple rules, especially eslint. -exports.tmpDirName = '.tmp'; +let tmpDirName = '.tmp'; // PORT should match the definition in test/testpy/__init__.py. exports.PORT = +process.env.NODE_COMMON_PORT || 12346; exports.isWindows = process.platform === 'win32'; @@ -166,9 +166,9 @@ exports.refreshTmpDir = function() { if (process.env.TEST_THREAD_ID) { exports.PORT += process.env.TEST_THREAD_ID * 100; - exports.tmpDirName += `.${process.env.TEST_THREAD_ID}`; + tmpDirName += `.${process.env.TEST_THREAD_ID}`; } -exports.tmpDir = path.join(testRoot, exports.tmpDirName); +exports.tmpDir = path.join(testRoot, tmpDirName); let opensslCli = null; let inFreeBSDJail = null; diff --git a/test/fixtures/es-module-loaders/syntax-error.mjs b/test/fixtures/es-module-loaders/syntax-error.mjs new file mode 100644 index 00000000000..bda4a7e6ebe --- /dev/null +++ b/test/fixtures/es-module-loaders/syntax-error.mjs @@ -0,0 +1,2 @@ +'use strict'; +await async () => 0; diff --git a/test/message/esm_display_syntax_error.mjs b/test/message/esm_display_syntax_error.mjs new file mode 100644 index 00000000000..82918672555 --- /dev/null +++ b/test/message/esm_display_syntax_error.mjs @@ -0,0 +1,3 @@ +// Flags: --experimental-modules +'use strict'; +await async () => 0; diff --git a/test/message/esm_display_syntax_error.out b/test/message/esm_display_syntax_error.out new file mode 100644 index 00000000000..0ca2bba5494 --- /dev/null +++ b/test/message/esm_display_syntax_error.out @@ -0,0 +1,7 @@ +(node:*) ExperimentalWarning: The ESM module loader is experimental. +file:///*/test/message/esm_display_syntax_error.mjs:3 +await async () => 0; +^^^^^ +SyntaxError: Unexpected reserved word + at loaders.set (internal/loader/ModuleRequest.js:*:*) + at diff --git a/test/message/esm_display_syntax_error_module.mjs b/test/message/esm_display_syntax_error_module.mjs new file mode 100644 index 00000000000..e74b70bec8c --- /dev/null +++ b/test/message/esm_display_syntax_error_module.mjs @@ -0,0 +1,3 @@ +// Flags: --experimental-modules +import '../common'; +import '../fixtures/es-module-loaders/syntax-error'; diff --git a/test/message/esm_display_syntax_error_module.out b/test/message/esm_display_syntax_error_module.out new file mode 100644 index 00000000000..a76b72bdb69 --- /dev/null +++ b/test/message/esm_display_syntax_error_module.out @@ -0,0 +1,7 @@ +(node:*) ExperimentalWarning: The ESM module loader is experimental. +file:///*/test/fixtures/es-module-loaders/syntax-error.mjs:2 +await async () => 0; +^^^^^ +SyntaxError: Unexpected reserved word + at loaders.set (internal/loader/ModuleRequest.js:*:*) + at diff --git a/test/message/testcfg.py b/test/message/testcfg.py index bfc8e005c0c..df2cbf3150e 100644 --- a/test/message/testcfg.py +++ b/test/message/testcfg.py @@ -114,19 +114,20 @@ def __init__(self, context, root): def Ls(self, path): if isdir(path): - return [f[:-3] for f in os.listdir(path) if f.endswith('.js')] + return [f for f in os.listdir(path) + if f.endswith('.js') or f.endswith('.mjs')] else: - return [] + return [] def ListTests(self, current_path, path, arch, mode, jsEngine): all_tests = [current_path + [t] for t in self.Ls(self.root)] result = [] for test in all_tests: if self.Contains(path, test): - file_prefix = join(self.root, reduce(join, test[1:], "")) - file_path = file_prefix + ".js" + file_path = join(self.root, reduce(join, test[1:], '')) + file_prefix = file_path[:file_path.rfind('.')] engine_output_path = file_prefix + (".%s.out" % jsEngine) - output_path = file_prefix + ".out" + output_path = file_prefix + '.out' if exists(engine_output_path): output_path = engine_output_path else: diff --git a/test/parallel/test-assert.js b/test/parallel/test-assert.js index 057e14e7907..cf7a05b8298 100644 --- a/test/parallel/test-assert.js +++ b/test/parallel/test-assert.js @@ -26,8 +26,8 @@ const a = assert; function makeBlock(f) { const args = Array.prototype.slice.call(arguments, 1); - return function() { - return f.apply(this, args); + return () => { + return f.apply(null, args); }; } @@ -186,7 +186,7 @@ assert.doesNotThrow(makeBlock(a.deepEqual, a1, a2)); // having an identical prototype property const nbRoot = { - toString: function() { return `${this.first} ${this.last}`; } + toString() { return `${this.first} ${this.last}`; } }; function nameBuilder(first, last) { @@ -461,10 +461,10 @@ assert.throws(makeBlock(thrower, TypeError)); 'a.doesNotThrow is not catching type matching errors'); } -assert.throws(function() { assert.ifError(new Error('test error')); }, +assert.throws(() => { assert.ifError(new Error('test error')); }, /^Error: test error$/); -assert.doesNotThrow(function() { assert.ifError(null); }); -assert.doesNotThrow(function() { assert.ifError(); }); +assert.doesNotThrow(() => { assert.ifError(null); }); +assert.doesNotThrow(() => { assert.ifError(); }); assert.throws(() => { assert.doesNotThrow(makeBlock(thrower, Error), 'user message'); @@ -504,7 +504,7 @@ assert.throws(() => { let threw = false; try { assert.throws( - function() { + () => { throw ({}); // eslint-disable-line no-throw-literal }, Array @@ -519,7 +519,7 @@ assert.throws(() => { a.throws(makeBlock(thrower, TypeError), /\[object Object\]/); // use a fn to validate error object -a.throws(makeBlock(thrower, TypeError), function(err) { +a.throws(makeBlock(thrower, TypeError), (err) => { if ((err instanceof TypeError) && /\[object Object\]/.test(err)) { return true; } @@ -622,7 +622,7 @@ testAssertionMessage({ a: NaN, b: Infinity, c: -Infinity }, let threw = false; try { // eslint-disable-next-line no-restricted-syntax - assert.throws(function() { + assert.throws(() => { assert.ifError(null); }); } catch (e) { diff --git a/test/parallel/test-domain-load-after-set-uncaught-exception-capture.js b/test/parallel/test-domain-load-after-set-uncaught-exception-capture.js new file mode 100644 index 00000000000..9e438368d63 --- /dev/null +++ b/test/parallel/test-domain-load-after-set-uncaught-exception-capture.js @@ -0,0 +1,18 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +process.setUncaughtExceptionCaptureCallback(common.mustNotCall()); + +common.expectsError( + () => require('domain'), + { + code: 'ERR_DOMAIN_CALLBACK_NOT_AVAILABLE', + type: Error, + message: /^A callback was registered.*with using the `domain` module/ + } +); + +process.setUncaughtExceptionCaptureCallback(null); + +assert.doesNotThrow(() => require('domain')); diff --git a/test/parallel/test-domain-set-uncaught-exception-capture-after-load.js b/test/parallel/test-domain-set-uncaught-exception-capture-after-load.js new file mode 100644 index 00000000000..e7cbffd0075 --- /dev/null +++ b/test/parallel/test-domain-set-uncaught-exception-capture-after-load.js @@ -0,0 +1,28 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +(function foobar() { + require('domain'); +})(); + +assert.throws( + () => process.setUncaughtExceptionCaptureCallback(common.mustNotCall()), + (err) => { + common.expectsError( + { + code: 'ERR_DOMAIN_CANNOT_SET_UNCAUGHT_EXCEPTION_CAPTURE', + type: Error, + message: /^The `domain` module is in use, which is mutually/ + } + )(err); + + assert(err.stack.includes('-'.repeat(40)), + `expected ${err.stack} to contain dashes`); + + const location = `at foobar (${__filename}:`; + assert(err.stack.includes(location), + `expected ${err.stack} to contain ${location}`); + return true; + } +); diff --git a/test/parallel/test-domain-top-level-error-handler-clears-stack.js b/test/parallel/test-domain-top-level-error-handler-clears-stack.js index f2095f09b78..05d5fca4671 100644 --- a/test/parallel/test-domain-top-level-error-handler-clears-stack.js +++ b/test/parallel/test-domain-top-level-error-handler-clears-stack.js @@ -9,8 +9,8 @@ const domain = require('domain'); */ const d = domain.create(); -d.on('error', common.mustCall(function() { - process.nextTick(function() { +d.on('error', common.mustCall(() => { + process.nextTick(() => { // Scheduling a callback with process.nextTick will enter a _new_ domain, // and the callback will be called after the domain that handled the error // was exited. So there should be only one domain on the domains stack if @@ -29,6 +29,6 @@ d.on('error', common.mustCall(function() { }); })); -d.run(function() { +d.run(() => { throw new Error('Error from domain'); }); diff --git a/test/parallel/test-fs-realpath.js b/test/parallel/test-fs-realpath.js index 013e8015147..4ea9bfdf48e 100644 --- a/test/parallel/test-fs-realpath.js +++ b/test/parallel/test-fs-realpath.js @@ -111,7 +111,7 @@ function test_simple_relative_symlink(realpath, realpathSync, callback) { const entry = `${tmpDir}/symlink`; const expected = `${tmpDir}/cycles/root.js`; [ - [entry, `../${common.tmpDirName}/cycles/root.js`] + [entry, `../${path.basename(tmpDir)}/cycles/root.js`] ].forEach(function(t) { try { fs.unlinkSync(t[0]); } catch (e) {} console.log('fs.symlinkSync(%j, %j, %j)', t[1], t[0], 'file'); diff --git a/test/parallel/test-http2-client-http1-server.js b/test/parallel/test-http2-client-http1-server.js index 44d8d392f4e..53f7bf42c46 100644 --- a/test/parallel/test-http2-client-http1-server.js +++ b/test/parallel/test-http2-client-http1-server.js @@ -13,7 +13,7 @@ server.listen(0, common.mustCall(() => { const client = http2.connect(`http://localhost:${server.address().port}`); const req = client.request(); - req.on('streamClosed', common.mustCall()); + req.on('close', common.mustCall()); client.on('error', common.expectsError({ code: 'ERR_HTTP2_ERROR', diff --git a/test/parallel/test-http2-client-rststream-before-connect.js b/test/parallel/test-http2-client-rststream-before-connect.js index e4aff87be98..eb3a0087d78 100644 --- a/test/parallel/test-http2-client-rststream-before-connect.js +++ b/test/parallel/test-http2-client-rststream-before-connect.js @@ -27,7 +27,7 @@ server.on('listening', common.mustCall(() => { // second call doesn't do anything assert.doesNotThrow(() => req.rstStream(8)); - req.on('streamClosed', common.mustCall((code) => { + req.on('close', common.mustCall((code) => { assert.strictEqual(req.destroyed, true); assert.strictEqual(code, 0); server.close(); diff --git a/test/parallel/test-http2-client-stream-destroy-before-connect.js b/test/parallel/test-http2-client-stream-destroy-before-connect.js index 4eefc4b9f32..06afbf3ce8c 100644 --- a/test/parallel/test-http2-client-stream-destroy-before-connect.js +++ b/test/parallel/test-http2-client-stream-destroy-before-connect.js @@ -41,7 +41,7 @@ server.on('listening', common.mustCall(() => { })(err); })); - req.on('streamClosed', common.mustCall((code) => { + req.on('close', common.mustCall((code) => { assert.strictEqual(req.rstCode, NGHTTP2_INTERNAL_ERROR); assert.strictEqual(code, NGHTTP2_INTERNAL_ERROR); server.close(); diff --git a/test/parallel/test-http2-client-unescaped-path.js b/test/parallel/test-http2-client-unescaped-path.js index 3a7a607bc35..adfbd61fe76 100644 --- a/test/parallel/test-http2-client-unescaped-path.js +++ b/test/parallel/test-http2-client-unescaped-path.js @@ -30,7 +30,7 @@ server.listen(0, common.mustCall(() => { type: Error, message: 'Stream closed with error code 1' })); - req.on('streamClosed', common.mustCall(maybeClose)); + req.on('close', common.mustCall(maybeClose)); } for (let i = 0; i <= count; i += 1) diff --git a/test/parallel/test-http2-compat-serverresponse-end.js b/test/parallel/test-http2-compat-serverresponse-end.js index fafb3ea76dd..366d5232155 100644 --- a/test/parallel/test-http2-compat-serverresponse-end.js +++ b/test/parallel/test-http2-compat-serverresponse-end.js @@ -183,10 +183,10 @@ const { { - // Should be able to call .end with cb from stream 'streamClosed' + // Should be able to call .end with cb from stream 'close' const server = createServer(mustCall((request, response) => { response.writeHead(HTTP_STATUS_OK, { foo: 'bar' }); - response.stream.on('streamClosed', mustCall(() => { + response.stream.on('close', mustCall(() => { response.end(mustCall()); })); })); diff --git a/test/parallel/test-http2-compat-socket.js b/test/parallel/test-http2-compat-socket.js index aed74149f72..5d97872dfb6 100644 --- a/test/parallel/test-http2-compat-socket.js +++ b/test/parallel/test-http2-compat-socket.js @@ -63,8 +63,8 @@ server.on('request', common.mustCall(function(request, response) { assert.strictEqual(request.socket.connecting, false); // socket events are bound and emitted on Http2Stream - request.socket.on('streamClosed', common.mustCall()); - request.socket.once('streamClosed', common.mustCall()); + request.socket.on('close', common.mustCall()); + request.socket.once('close', common.mustCall()); request.socket.on('testEvent', common.mustCall()); request.socket.emit('testEvent'); })); diff --git a/test/parallel/test-http2-create-client-secure-session.js b/test/parallel/test-http2-create-client-secure-session.js index 62a79148dca..811ef772d59 100644 --- a/test/parallel/test-http2-create-client-secure-session.js +++ b/test/parallel/test-http2-create-client-secure-session.js @@ -20,10 +20,7 @@ function loadKey(keyname) { function onStream(stream, headers) { const socket = stream.session[kSocket]; assert(headers[':authority'].startsWith(socket.servername)); - stream.respond({ - 'content-type': 'text/html', - ':status': 200 - }); + stream.respond({ 'content-type': 'application/json' }); stream.end(JSON.stringify({ servername: socket.servername, alpnProtocol: socket.alpnProtocol @@ -33,35 +30,32 @@ function onStream(stream, headers) { function verifySecureSession(key, cert, ca, opts) { const server = h2.createSecureServer({ cert, key }); server.on('stream', common.mustCall(onStream)); - server.listen(0); - server.on('listening', common.mustCall(function() { - const headers = { ':path': '/' }; - if (!opts) { - opts = {}; - } + server.listen(0, common.mustCall(() => { + opts = opts || { }; opts.secureContext = tls.createSecureContext({ ca }); - const client = h2.connect(`https://localhost:${this.address().port}`, opts, function() { - const req = client.request(headers); + const client = h2.connect(`https://localhost:${server.address().port}`, + opts); + // Verify that a 'secureConnect' listener is attached + assert.strictEqual(client.socket.listenerCount('secureConnect'), 1); + const req = client.request(); - req.on('response', common.mustCall(function(headers) { - assert.strictEqual(headers[':status'], 200, 'status code is set'); - assert.strictEqual(headers['content-type'], 'text/html', - 'content type is set'); - assert(headers['date'], 'there is a date'); - })); + req.on('response', common.mustCall((headers) => { + assert.strictEqual(headers[':status'], 200); + assert.strictEqual(headers['content-type'], 'application/json'); + assert(headers['date']); + })); - let data = ''; - req.setEncoding('utf8'); - req.on('data', (d) => data += d); - req.on('end', common.mustCall(() => { - const jsonData = JSON.parse(data); - assert.strictEqual(jsonData.servername, opts.servername || 'localhost'); - assert.strictEqual(jsonData.alpnProtocol, 'h2'); - server.close(); - client[kSocket].destroy(); - })); - req.end(); - }); + let data = ''; + req.setEncoding('utf8'); + req.on('data', (d) => data += d); + req.on('end', common.mustCall(() => { + const jsonData = JSON.parse(data); + assert.strictEqual(jsonData.servername, + opts.servername || 'localhost'); + assert.strictEqual(jsonData.alpnProtocol, 'h2'); + server.close(); + client[kSocket].destroy(); + })); })); } diff --git a/test/parallel/test-http2-getpackedsettings.js b/test/parallel/test-http2-getpackedsettings.js index 7461176c5fc..16c84913893 100644 --- a/test/parallel/test-http2-getpackedsettings.js +++ b/test/parallel/test-http2-getpackedsettings.js @@ -128,7 +128,6 @@ assert.doesNotThrow(() => http2.getPackedSettings({ enablePush: false })); assert.strictEqual(settings.enablePush, true); } -//should throw if enablePush is not 0 or 1 { const packed = Buffer.from([ 0x00, 0x02, 0x00, 0x00, 0x00, 0x00]); @@ -140,13 +139,8 @@ assert.doesNotThrow(() => http2.getPackedSettings({ enablePush: false })); const packed = Buffer.from([ 0x00, 0x02, 0x00, 0x00, 0x00, 0x64]); - assert.throws(() => { - http2.getUnpackedSettings(packed, { validate: true }); - }, common.expectsError({ - code: 'ERR_HTTP2_INVALID_SETTING_VALUE', - type: RangeError, - message: 'Invalid value for setting "enablePush": 100' - })); + const settings = http2.getUnpackedSettings(packed, { validate: true }); + assert.strictEqual(settings.enablePush, true); } //check for what happens if passing {validate: true} and no errors happen diff --git a/test/parallel/test-http2-multiheaders-raw.js b/test/parallel/test-http2-multiheaders-raw.js index 5db253da3c3..c06bf23bff3 100644 --- a/test/parallel/test-http2-multiheaders-raw.js +++ b/test/parallel/test-http2-multiheaders-raw.js @@ -42,7 +42,7 @@ server.on('stream', common.mustCall((stream, headers, flags, rawHeaders) => { server.listen(0, common.mustCall(() => { const client = http2.connect(`http://localhost:${server.address().port}`); const req = client.request(src); - req.on('streamClosed', common.mustCall(() => { + req.on('close', common.mustCall(() => { server.close(); client.destroy(); })); diff --git a/test/parallel/test-http2-multiheaders.js b/test/parallel/test-http2-multiheaders.js index 7a118214e8f..5e477104091 100644 --- a/test/parallel/test-http2-multiheaders.js +++ b/test/parallel/test-http2-multiheaders.js @@ -54,7 +54,7 @@ server.listen(0, common.mustCall(() => { const client = http2.connect(`http://localhost:${server.address().port}`); const req = client.request(src); req.on('response', common.mustCall(checkHeaders)); - req.on('streamClosed', common.mustCall(() => { + req.on('close', common.mustCall(() => { server.close(); client.destroy(); })); diff --git a/test/parallel/test-http2-options-max-reserved-streams.js b/test/parallel/test-http2-options-max-reserved-streams.js index 17009a4c11e..d54ca6a7886 100644 --- a/test/parallel/test-http2-options-max-reserved-streams.js +++ b/test/parallel/test-http2-options-max-reserved-streams.js @@ -34,7 +34,7 @@ server.on('stream', common.mustCall((stream) => { pushedStream.respond({ ':status': 200 }); pushedStream.on('aborted', common.mustCall()); pushedStream.on('error', common.mustNotCall()); - pushedStream.on('streamClosed', + pushedStream.on('close', common.mustCall((code) => assert.strictEqual(code, 8))); })); @@ -67,12 +67,12 @@ server.on('listening', common.mustCall(() => { client.on('stream', common.mustCall((stream) => { stream.resume(); stream.on('end', common.mustCall()); - stream.on('streamClosed', common.mustCall(maybeClose)); + stream.on('close', common.mustCall(maybeClose)); })); req.on('response', common.mustCall()); req.resume(); req.on('end', common.mustCall()); - req.on('streamClosed', common.mustCall(maybeClose)); + req.on('close', common.mustCall(maybeClose)); })); diff --git a/test/parallel/test-http2-respond-file-errors.js b/test/parallel/test-http2-respond-file-errors.js index b57d9b1046c..c2c749873c8 100644 --- a/test/parallel/test-http2-respond-file-errors.js +++ b/test/parallel/test-http2-respond-file-errors.js @@ -117,7 +117,7 @@ server.listen(0, common.mustCall(() => { const client = http2.connect(`http://localhost:${server.address().port}`); const req = client.request(); - req.on('streamClosed', common.mustCall(() => { + req.on('close', common.mustCall(() => { client.destroy(); server.close(); })); diff --git a/test/parallel/test-http2-respond-file-fd-errors.js b/test/parallel/test-http2-respond-file-fd-errors.js index e5cc280613f..9458b2f49af 100644 --- a/test/parallel/test-http2-respond-file-fd-errors.js +++ b/test/parallel/test-http2-respond-file-fd-errors.js @@ -144,7 +144,7 @@ server.listen(0, common.mustCall(() => { const client = http2.connect(`http://localhost:${server.address().port}`); const req = client.request(); - req.on('streamClosed', common.mustCall(() => { + req.on('close', common.mustCall(() => { client.destroy(); server.close(); })); diff --git a/test/parallel/test-http2-respond-file-fd-range.js b/test/parallel/test-http2-respond-file-fd-range.js index fdf9b5e2cd3..8479dca5185 100644 --- a/test/parallel/test-http2-respond-file-fd-range.js +++ b/test/parallel/test-http2-respond-file-fd-range.js @@ -71,7 +71,7 @@ server.listen(0, () => { req.on('end', common.mustCall(() => { assert.strictEqual(check, data.toString('utf8', 8, 11)); })); - req.on('streamClosed', common.mustCall(maybeClose)); + req.on('close', common.mustCall(maybeClose)); req.end(); } @@ -88,7 +88,7 @@ server.listen(0, () => { req.on('end', common.mustCall(() => { assert.strictEqual(check, data.toString('utf8', 8, 28)); })); - req.on('streamClosed', common.mustCall(maybeClose)); + req.on('close', common.mustCall(maybeClose)); req.end(); } diff --git a/test/parallel/test-http2-respond-no-data.js b/test/parallel/test-http2-respond-no-data.js index 74f85a9cd0c..d891fe4e8dd 100644 --- a/test/parallel/test-http2-respond-no-data.js +++ b/test/parallel/test-http2-respond-no-data.js @@ -13,7 +13,7 @@ const server = http2.createServer(); const status = [204, 205, 304]; server.on('stream', common.mustCall((stream) => { - stream.on('streamClosed', common.mustCall(() => { + stream.on('close', common.mustCall(() => { assert.strictEqual(stream.destroyed, true); })); stream.respond({ ':status': status.shift() }); diff --git a/test/parallel/test-http2-response-splitting.js b/test/parallel/test-http2-response-splitting.js index 8aae2156e43..1d9b616105f 100644 --- a/test/parallel/test-http2-response-splitting.js +++ b/test/parallel/test-http2-response-splitting.js @@ -67,7 +67,7 @@ server.listen(0, common.mustCall(() => { })); req.resume(); req.on('end', common.mustCall()); - req.on('streamClosed', common.mustCall(maybeClose)); + req.on('close', common.mustCall(maybeClose)); } doTest(str, 'location', str); diff --git a/test/parallel/test-http2-server-rst-before-respond.js b/test/parallel/test-http2-server-rst-before-respond.js index 25dfa7e0106..47ba68bd29e 100644 --- a/test/parallel/test-http2-server-rst-before-respond.js +++ b/test/parallel/test-http2-server-rst-before-respond.js @@ -35,7 +35,7 @@ server.on('listening', common.mustCall(() => { req.on('headers', common.mustNotCall()); - req.on('streamClosed', common.mustCall((code) => { + req.on('close', common.mustCall((code) => { assert.strictEqual(h2.constants.NGHTTP2_NO_ERROR, code); server.close(); client.destroy(); diff --git a/test/parallel/test-http2-server-rst-stream.js b/test/parallel/test-http2-server-rst-stream.js index dd38efb42f9..4b04f29c8ec 100644 --- a/test/parallel/test-http2-server-rst-stream.js +++ b/test/parallel/test-http2-server-rst-stream.js @@ -43,7 +43,7 @@ server.listen(0, common.mustCall(() => { ':method': 'POST', rstmethod: test[0] }); - req.on('streamClosed', common.mustCall((code) => { + req.on('close', common.mustCall((code) => { assert.strictEqual(code, test[1]); countdown.dec(); })); diff --git a/test/parallel/test-http2-server-socketerror.js b/test/parallel/test-http2-server-socketerror.js index 24945e531af..9f52b9280d2 100644 --- a/test/parallel/test-http2-server-socketerror.js +++ b/test/parallel/test-http2-server-socketerror.js @@ -49,7 +49,7 @@ server.listen(0, common.mustCall(() => { const req = client.request(); req.resume(); req.on('end', common.mustCall()); - req.on('streamClosed', common.mustCall(() => { + req.on('close', common.mustCall(() => { client.destroy(); server.close(); })); diff --git a/test/parallel/test-http2-stream-client.js b/test/parallel/test-http2-stream-client.js index 3b802f6e6d5..aa722c5ff2b 100644 --- a/test/parallel/test-http2-stream-client.js +++ b/test/parallel/test-http2-stream-client.js @@ -21,7 +21,7 @@ server.listen(0, common.mustCall(() => { const client = http2.connect(`http://localhost:${server.address().port}`); const req = client.request(); req.resume(); - req.on('streamClosed', common.mustCall(() => { + req.on('close', common.mustCall(() => { client.destroy(); server.close(); })); diff --git a/test/parallel/test-http2-stream-destroy-event-order.js b/test/parallel/test-http2-stream-destroy-event-order.js index 16bb5e6a622..c375bd80b38 100644 --- a/test/parallel/test-http2-stream-destroy-event-order.js +++ b/test/parallel/test-http2-stream-destroy-event-order.js @@ -11,7 +11,7 @@ let req; const server = http2.createServer(); server.on('stream', common.mustCall((stream) => { stream.on('error', common.mustCall(() => { - stream.on('streamClosed', common.mustCall((code) => { + stream.on('close', common.mustCall((code) => { assert.strictEqual(code, 2); client.destroy(); server.close(); diff --git a/test/parallel/test-http2-too-large-headers.js b/test/parallel/test-http2-too-large-headers.js index fa15b6ea114..f7ac25170b8 100644 --- a/test/parallel/test-http2-too-large-headers.js +++ b/test/parallel/test-http2-too-large-headers.js @@ -22,7 +22,7 @@ server.listen(0, common.mustCall(() => { type: Error, message: 'Stream closed with error code 11' })); - req.on('streamClosed', common.mustCall((code) => { + req.on('close', common.mustCall((code) => { assert.strictEqual(code, NGHTTP2_ENHANCE_YOUR_CALM); server.close(); client.destroy(); diff --git a/test/parallel/test-http2-too-many-headers.js b/test/parallel/test-http2-too-many-headers.js index 3d63727ed50..eff0fa9c351 100644 --- a/test/parallel/test-http2-too-many-headers.js +++ b/test/parallel/test-http2-too-many-headers.js @@ -25,7 +25,7 @@ server.listen(0, common.mustCall(() => { type: Error, message: 'Stream closed with error code 11' })); - req.on('streamClosed', common.mustCall((code) => { + req.on('close', common.mustCall((code) => { assert.strictEqual(code, NGHTTP2_ENHANCE_YOUR_CALM); server.close(); client.destroy(); diff --git a/test/parallel/test-http2-write-callbacks.js b/test/parallel/test-http2-write-callbacks.js index e196ca8d3c5..44e33573a68 100644 --- a/test/parallel/test-http2-write-callbacks.js +++ b/test/parallel/test-http2-write-callbacks.js @@ -30,7 +30,7 @@ server.listen(0, common.mustCall(() => { req.setEncoding('utf8'); req.on('data', (chunk) => actual += chunk); req.on('end', common.mustCall(() => assert.strictEqual(actual, 'abcxyz'))); - req.on('streamClosed', common.mustCall(() => { + req.on('close', common.mustCall(() => { client.destroy(); server.close(); })); diff --git a/test/parallel/test-module-binding.js b/test/parallel/test-module-binding.js index f5c2a794b11..bea0c91f0c5 100644 --- a/test/parallel/test-module-binding.js +++ b/test/parallel/test-module-binding.js @@ -1,14 +1,14 @@ 'use strict'; require('../common'); const fixtures = require('../common/fixtures'); -const { internalModuleReadFile } = process.binding('fs'); +const { internalModuleReadJSON } = process.binding('fs'); const { readFileSync } = require('fs'); const { strictEqual } = require('assert'); -strictEqual(internalModuleReadFile('nosuchfile'), undefined); -strictEqual(internalModuleReadFile(fixtures.path('empty.txt')), ''); -strictEqual(internalModuleReadFile(fixtures.path('empty-with-bom.txt')), ''); +strictEqual(internalModuleReadJSON('nosuchfile'), undefined); +strictEqual(internalModuleReadJSON(fixtures.path('empty.txt')), ''); +strictEqual(internalModuleReadJSON(fixtures.path('empty-with-bom.txt')), ''); { const filename = fixtures.path('require-bin/package.json'); - strictEqual(internalModuleReadFile(filename), readFileSync(filename, 'utf8')); + strictEqual(internalModuleReadJSON(filename), readFileSync(filename, 'utf8')); } diff --git a/test/parallel/test-process-exception-capture-errors.js b/test/parallel/test-process-exception-capture-errors.js new file mode 100644 index 00000000000..7053497adaf --- /dev/null +++ b/test/parallel/test-process-exception-capture-errors.js @@ -0,0 +1,22 @@ +'use strict'; +const common = require('../common'); + +common.expectsError( + () => process.setUncaughtExceptionCaptureCallback(42), + { + code: 'ERR_INVALID_ARG_TYPE', + type: TypeError, + message: 'The "fn" argument must be one of type Function or null' + } +); + +process.setUncaughtExceptionCaptureCallback(common.mustNotCall()); + +common.expectsError( + () => process.setUncaughtExceptionCaptureCallback(common.mustNotCall()), + { + code: 'ERR_UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET', + type: Error, + message: /setupUncaughtExceptionCapture.*called while a capture callback/ + } +); diff --git a/test/parallel/test-process-exception-capture-should-abort-on-uncaught-setflagsfromstring.js b/test/parallel/test-process-exception-capture-should-abort-on-uncaught-setflagsfromstring.js new file mode 100644 index 00000000000..de14177b45a --- /dev/null +++ b/test/parallel/test-process-exception-capture-should-abort-on-uncaught-setflagsfromstring.js @@ -0,0 +1,13 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const v8 = require('v8'); + +assert.strictEqual(process.hasUncaughtExceptionCaptureCallback(), false); + +v8.setFlagsFromString('--abort-on-uncaught-exception'); +// This should make the process not crash even though the flag was passed. +process.setUncaughtExceptionCaptureCallback(common.mustCall((err) => { + assert.strictEqual(err.message, 'foo'); +})); +throw new Error('foo'); diff --git a/test/parallel/test-process-exception-capture-should-abort-on-uncaught.js b/test/parallel/test-process-exception-capture-should-abort-on-uncaught.js new file mode 100644 index 00000000000..f9e685a86ea --- /dev/null +++ b/test/parallel/test-process-exception-capture-should-abort-on-uncaught.js @@ -0,0 +1,12 @@ +// Flags: --abort-on-uncaught-exception +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +assert.strictEqual(process.hasUncaughtExceptionCaptureCallback(), false); + +// This should make the process not crash even though the flag was passed. +process.setUncaughtExceptionCaptureCallback(common.mustCall((err) => { + assert.strictEqual(err.message, 'foo'); +})); +throw new Error('foo'); diff --git a/test/parallel/test-process-exception-capture.js b/test/parallel/test-process-exception-capture.js new file mode 100644 index 00000000000..c84d3459e23 --- /dev/null +++ b/test/parallel/test-process-exception-capture.js @@ -0,0 +1,13 @@ +// Flags: --abort-on-uncaught-exception +'use strict'; +const common = require('../common'); +const assert = require('assert'); + +assert.strictEqual(process.hasUncaughtExceptionCaptureCallback(), false); + +// This should make the process not crash even though the flag was passed. +process.setUncaughtExceptionCaptureCallback(common.mustCall((err) => { + assert.strictEqual(err.message, 'foo'); +})); +process.on('uncaughtException', common.mustNotCall()); +throw new Error('foo'); diff --git a/test/parallel/test-querystring.js b/test/parallel/test-querystring.js index 400431fd2cb..76049584dbe 100644 --- a/test/parallel/test-querystring.js +++ b/test/parallel/test-querystring.js @@ -129,7 +129,7 @@ const qsWeirdObjects = [ [{ regexp: /./g }, 'regexp=', { 'regexp': '' }], // eslint-disable-next-line no-unescaped-regexp-dot [{ regexp: new RegExp('.', 'g') }, 'regexp=', { 'regexp': '' }], - [{ fn: function() {} }, 'fn=', { 'fn': '' }], + [{ fn: () => {} }, 'fn=', { 'fn': '' }], [{ fn: new Function('') }, 'fn=', { 'fn': '' }], [{ math: Math }, 'math=', { 'math': '' }], [{ e: extendedFunction }, 'e=', { 'e': '' }], @@ -192,7 +192,7 @@ function check(actual, expected, input) { `Expected keys: ${inspect(expectedKeys)}`; } assert.deepStrictEqual(actualKeys, expectedKeys, msg); - expectedKeys.forEach(function(key) { + expectedKeys.forEach((key) => { if (typeof input === 'string') { msg = `Input: ${inspect(input)}\n` + `Key: ${inspect(key)}\n` + @@ -206,21 +206,21 @@ function check(actual, expected, input) { } // test that the canonical qs is parsed properly. -qsTestCases.forEach(function(testCase) { +qsTestCases.forEach((testCase) => { check(qs.parse(testCase[0]), testCase[2], testCase[0]); }); // test that the colon test cases can do the same -qsColonTestCases.forEach(function(testCase) { +qsColonTestCases.forEach((testCase) => { check(qs.parse(testCase[0], ';', ':'), testCase[2], testCase[0]); }); // test the weird objects, that they get parsed properly -qsWeirdObjects.forEach(function(testCase) { +qsWeirdObjects.forEach((testCase) => { check(qs.parse(testCase[1]), testCase[2], testCase[1]); }); -qsNoMungeTestCases.forEach(function(testCase) { +qsNoMungeTestCases.forEach((testCase) => { assert.deepStrictEqual(testCase[0], qs.stringify(testCase[1], '&', '=')); }); @@ -258,15 +258,15 @@ qsNoMungeTestCases.forEach(function(testCase) { // now test stringifying // basic -qsTestCases.forEach(function(testCase) { +qsTestCases.forEach((testCase) => { assert.strictEqual(testCase[1], qs.stringify(testCase[2])); }); -qsColonTestCases.forEach(function(testCase) { +qsColonTestCases.forEach((testCase) => { assert.strictEqual(testCase[1], qs.stringify(testCase[2], ';', ':')); }); -qsWeirdObjects.forEach(function(testCase) { +qsWeirdObjects.forEach((testCase) => { assert.strictEqual(testCase[1], qs.stringify(testCase[0])); }); @@ -300,7 +300,7 @@ assert.strictEqual('foo=', qs.stringify({ foo: Infinity })); assert.strictEqual(f, 'a=b&q=x%3Dy%26y%3Dz'); } -assert.doesNotThrow(function() { +assert.doesNotThrow(() => { qs.parse(undefined); }); @@ -432,7 +432,7 @@ check(qs.parse('%\u0100=%\u0101'), { '%Ā': '%ā' }); } // Test QueryString.unescapeBuffer -qsUnescapeTestCases.forEach(function(testCase) { +qsUnescapeTestCases.forEach((testCase) => { assert.strictEqual(qs.unescape(testCase[0]), testCase[1]); assert.strictEqual(qs.unescapeBuffer(testCase[0]).toString(), testCase[1]); }); @@ -440,7 +440,7 @@ qsUnescapeTestCases.forEach(function(testCase) { // test overriding .unescape { const prevUnescape = qs.unescape; - qs.unescape = function(str) { + qs.unescape = (str) => { return str.replace(/o/g, '_'); }; check( diff --git a/test/parallel/test-tls-client-getephemeralkeyinfo.js b/test/parallel/test-tls-client-getephemeralkeyinfo.js index fcb93aa3b6f..d62e1ac0e17 100644 --- a/test/parallel/test-tls-client-getephemeralkeyinfo.js +++ b/test/parallel/test-tls-client-getephemeralkeyinfo.js @@ -80,7 +80,7 @@ function testDHE2048() { } function testECDHE256() { - test(256, 'ECDH', tls.DEFAULT_ECDH_CURVE, testECDHE512); + test(256, 'ECDH', 'prime256v1', testECDHE512); ntests++; } diff --git a/test/parallel/test-writeint.js b/test/parallel/test-writeint.js index 66dc13997e2..1e0a8e8812e 100644 --- a/test/parallel/test-writeint.js +++ b/test/parallel/test-writeint.js @@ -41,10 +41,10 @@ function test8(clazz) { assert.strictEqual(0xfb, buffer[1]); /* Make sure we handle truncation correctly */ - assert.throws(function() { + assert.throws(() => { buffer.writeInt8(0xabc, 0); }, errorOutOfBounds); - assert.throws(function() { + assert.throws(() => { buffer.writeInt8(0xabc, 0); }, errorOutOfBounds); @@ -54,10 +54,10 @@ function test8(clazz) { assert.strictEqual(0x7f, buffer[0]); assert.strictEqual(0x80, buffer[1]); - assert.throws(function() { + assert.throws(() => { buffer.writeInt8(0x7f + 1, 0); }, errorOutOfBounds); - assert.throws(function() { + assert.throws(() => { buffer.writeInt8(-0x80 - 1, 0); }, errorOutOfBounds); } @@ -94,10 +94,10 @@ function test16(clazz) { assert.strictEqual(0xff, buffer[1]); assert.strictEqual(0x80, buffer[2]); assert.strictEqual(0x00, buffer[3]); - assert.throws(function() { + assert.throws(() => { buffer.writeInt16BE(0x7fff + 1, 0); }, errorOutOfBounds); - assert.throws(function() { + assert.throws(() => { buffer.writeInt16BE(-0x8000 - 1, 0); }, errorOutOfBounds); @@ -107,10 +107,10 @@ function test16(clazz) { assert.strictEqual(0x7f, buffer[1]); assert.strictEqual(0x00, buffer[2]); assert.strictEqual(0x80, buffer[3]); - assert.throws(function() { + assert.throws(() => { buffer.writeInt16LE(0x7fff + 1, 0); }, errorOutOfBounds); - assert.throws(function() { + assert.throws(() => { buffer.writeInt16LE(-0x8000 - 1, 0); }, errorOutOfBounds); } @@ -163,10 +163,10 @@ function test32(clazz) { assert.strictEqual(0x00, buffer[5]); assert.strictEqual(0x00, buffer[6]); assert.strictEqual(0x00, buffer[7]); - assert.throws(function() { + assert.throws(() => { buffer.writeInt32BE(0x7fffffff + 1, 0); }, errorOutOfBounds); - assert.throws(function() { + assert.throws(() => { buffer.writeInt32BE(-0x80000000 - 1, 0); }, errorOutOfBounds); @@ -180,10 +180,10 @@ function test32(clazz) { assert.strictEqual(0x00, buffer[5]); assert.strictEqual(0x00, buffer[6]); assert.strictEqual(0x80, buffer[7]); - assert.throws(function() { + assert.throws(() => { buffer.writeInt32LE(0x7fffffff + 1, 0); }, errorOutOfBounds); - assert.throws(function() { + assert.throws(() => { buffer.writeInt32LE(-0x80000000 - 1, 0); }, errorOutOfBounds); } diff --git a/test/parallel/test-zerolengthbufferbug.js b/test/parallel/test-zerolengthbufferbug.js index 75aaa2f48dd..0e1e976e568 100644 --- a/test/parallel/test-zerolengthbufferbug.js +++ b/test/parallel/test-zerolengthbufferbug.js @@ -4,7 +4,7 @@ const common = require('../common'); const http = require('http'); -const server = http.createServer(function(req, res) { +const server = http.createServer((req, res) => { const buffer = Buffer.alloc(0); // FIXME: WTF gjslint want this? res.writeHead(200, { 'Content-Type': 'text/html', @@ -12,12 +12,12 @@ const server = http.createServer(function(req, res) { res.end(buffer); }); -server.listen(0, common.mustCall(function() { - http.get({ port: this.address().port }, common.mustCall(function(res) { +server.listen(0, common.mustCall(() => { + http.get({ port: server.address().port }, common.mustCall((res) => { res.on('data', common.mustNotCall()); - res.on('end', function(d) { + res.on('end', (d) => { server.close(); }); })); diff --git a/vcbuild.bat b/vcbuild.bat index 78809c99002..819686f1f40 100644 --- a/vcbuild.bat +++ b/vcbuild.bat @@ -213,12 +213,24 @@ if %target_arch%==x64 if %msvs_host_arch%==amd64 set vcvarsall_arg=amd64 :vs-set-2017 if defined target_env if "%target_env%" NEQ "vs2017" goto vs-set-2015 echo Looking for Visual Studio 2017 +call tools\msvs\vswhere_usability_wrapper.cmd +if "_%VCINSTALLDIR%_" == "__" goto vs-set-2015 +if defined msi ( + echo Looking for WiX installation for Visual Studio 2017... + if not exist "%WIX%\SDK\VS2017" ( + echo Failed to find WiX install for Visual Studio 2017 + echo VS2017 support for WiX is only present starting at version 3.11 + goto vs-set-2015 + ) + if not exist "%VCINSTALLDIR%\..\MSBuild\Microsoft\WiX" ( + echo Failed to find the Wix Toolset Visual Studio 2017 Extension + goto vs-set-2015 + ) +) @rem check if VS2017 is already setup, and for the requested arch if "_%VisualStudioVersion%_" == "_15.0_" if "_%VSCMD_ARG_TGT_ARCH%_"=="_%target_arch%_" goto found_vs2017 @rem need to clear VSINSTALLDIR for vcvarsall to work as expected set "VSINSTALLDIR=" -call tools\msvs\vswhere_usability_wrapper.cmd -if "_%VCINSTALLDIR%_" == "__" goto vs-set-2015 @rem prevent VsDevCmd.bat from changing the current working directory set "VSCMD_START_DIR=%CD%" set vcvars_call="%VCINSTALLDIR%\Auxiliary\Build\vcvarsall.bat" %vcvarsall_arg% @@ -387,7 +399,9 @@ if not defined msi goto upload :msibuild echo Building node-v%FULLVERSION%-%target_arch%.msi -msbuild "%~dp0tools\msvs\msi\nodemsi.sln" /m /t:Clean,Build /p:PlatformToolset=%PLATFORM_TOOLSET% /p:GypMsvsVersion=%GYP_MSVS_VERSION% /p:Configuration=%config% /p:Platform=%target_arch% /p:NodeVersion=%NODE_VERSION% /p:FullVersion=%FULLVERSION% /p:DistTypeDir=%DISTTYPEDIR% %noetw_msi_arg% %noperfctr_msi_arg% /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo +set "msbsdk=" +if defined WindowsSDKVersion set "msbsdk=/p:WindowsTargetPlatformVersion=%WindowsSDKVersion:~0,-1%" +msbuild "%~dp0tools\msvs\msi\nodemsi.sln" /m /t:Clean,Build %msbsdk% /p:PlatformToolset=%PLATFORM_TOOLSET% /p:GypMsvsVersion=%GYP_MSVS_VERSION% /p:Configuration=%config% /p:Platform=%target_arch% /p:NodeVersion=%NODE_VERSION% /p:FullVersion=%FULLVERSION% /p:DistTypeDir=%DISTTYPEDIR% %noetw_msi_arg% %noperfctr_msi_arg% /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo if errorlevel 1 goto exit if not defined sign goto upload