diff --git a/benchmark/_http-benchmarkers.js b/benchmark/_http-benchmarkers.js index 54b7481afaa..55ebcc96ba2 100644 --- a/benchmark/_http-benchmarkers.js +++ b/benchmark/_http-benchmarkers.js @@ -89,11 +89,14 @@ class TestDoubleBenchmarker { } create(options) { + const env = Object.assign({ + duration: options.duration, + test_url: `http://127.0.0.1:${options.port}${options.path}`, + }, process.env); + const child = child_process.fork(this.executable, { silent: true, - env: Object.assign({}, process.env, { - test_url: `http://127.0.0.1:${options.port}${options.path}` - }) + env }); return child; } diff --git a/benchmark/_test-double-benchmarker.js b/benchmark/_test-double-benchmarker.js index 8c2f744fbf6..e2a0eb13126 100644 --- a/benchmark/_test-double-benchmarker.js +++ b/benchmark/_test-double-benchmarker.js @@ -2,6 +2,28 @@ const http = require('http'); -http.get(process.env.test_url, function() { - console.log(JSON.stringify({ throughput: 1 })); -}); +const duration = process.env.duration || 0; +const url = process.env.test_url; + +const start = process.hrtime(); +let throughput = 0; + +function request(res) { + res.on('data', () => {}); + res.on('error', () => {}); + res.on('end', () => { + throughput++; + const diff = process.hrtime(start); + if (duration > 0 && diff[0] < duration) { + run(); + } else { + console.log(JSON.stringify({ throughput })); + } + }); +} + +function run() { + http.get(url, request); +} + +run(); diff --git a/benchmark/compare.R b/benchmark/compare.R index 5085f4ea73b..1527d680c38 100644 --- a/benchmark/compare.R +++ b/benchmark/compare.R @@ -35,6 +35,21 @@ if (!is.null(plot.filename)) { ggsave(plot.filename, p); } +# computes the shared standard error, as used in the welch t-test +welch.sd = function (old.rate, new.rate) { + old.se.squared = var(old.rate) / length(old.rate) + new.se.squared = var(new.rate) / length(new.rate) + return(sqrt(old.se.squared + new.se.squared)) +} + +# calculate the improvement confidence interval. The improvement is calculated +# by dividing by old.mu and not new.mu, because old.mu is what the mean +# improvement is calculated relative to. +confidence.interval = function (shared.se, old.mu, w, risk) { + interval = qt(1 - (risk / 2), w$parameter) * shared.se; + return(sprintf("±%.2f%%", (interval / old.mu) * 100)) +} + # Print a table with results statistics = ddply(dat, "name", function(subdat) { old.rate = subset(subdat, binary == "old")$rate; @@ -45,33 +60,42 @@ statistics = ddply(dat, "name", function(subdat) { new.mu = mean(new.rate); improvement = sprintf("%.2f %%", ((new.mu - old.mu) / old.mu * 100)); - p.value = NA; - confidence = 'NA'; + r = list( + confidence = "NA", + improvement = improvement, + "accuracy (*)" = "NA", + "(**)" = "NA", + "(***)" = "NA" + ); + # Check if there is enough data to calculate the calculate the p-value if (length(old.rate) > 1 && length(new.rate) > 1) { # Perform a statistics test to see of there actually is a difference in # performance. w = t.test(rate ~ binary, data=subdat); - p.value = w$p.value; + shared.se = welch.sd(old.rate, new.rate) # Add user friendly stars to the table. There should be at least one star # before you can say that there is an improvement. confidence = ''; - if (p.value < 0.001) { + if (w$p.value < 0.001) { confidence = '***'; - } else if (p.value < 0.01) { + } else if (w$p.value < 0.01) { confidence = '**'; - } else if (p.value < 0.05) { + } else if (w$p.value < 0.05) { confidence = '*'; } + + r = list( + confidence = confidence, + improvement = improvement, + "accuracy (*)" = confidence.interval(shared.se, old.mu, w, 0.05), + "(**)" = confidence.interval(shared.se, old.mu, w, 0.01), + "(***)" = confidence.interval(shared.se, old.mu, w, 0.001) + ); } - r = list( - improvement = improvement, - confidence = confidence, - p.value = p.value - ); - return(data.frame(r)); + return(data.frame(r, check.names=FALSE)); }); @@ -81,3 +105,16 @@ statistics$name = NULL; options(width = 200); print(statistics); +cat("\n") +cat(sprintf( +"Be aware that when doing many comparisions the risk of a false-positive +result increases. In this case there are %d comparisions, you can thus +expect the following amount of false-positive results: + %.2f false positives, when considering a 5%% risk acceptance (*, **, ***), + %.2f false positives, when considering a 1%% risk acceptance (**, ***), + %.2f false positives, when considering a 0.1%% risk acceptance (***) +", +nrow(statistics), +nrow(statistics) * 0.05, +nrow(statistics) * 0.01, +nrow(statistics) * 0.001)) diff --git a/doc/api/cluster.md b/doc/api/cluster.md index 6ef451cb9af..1817ac82027 100644 --- a/doc/api/cluster.md +++ b/doc/api/cluster.md @@ -711,6 +711,8 @@ changes: * `exec` {string} File path to worker file. **Default:** `process.argv[1]` * `args` {Array} String arguments passed to worker. **Default:** `process.argv.slice(2)` + * `cwd` {string} Current working directory of the worker process. **Default:** + `undefined` (inherits from parent process) * `silent` {boolean} Whether or not to send output to parent's stdio. **Default:** `false` * `stdio` {Array} Configures the stdio of forked processes. Because the diff --git a/doc/api/http2.md b/doc/api/http2.md index e2f9aabfca5..3ac7dd91e5c 100644 --- a/doc/api/http2.md +++ b/doc/api/http2.md @@ -229,8 +229,8 @@ added: v8.4.0 The `'stream'` event is emitted when a new `Http2Stream` is created. When invoked, the handler function will receive a reference to the `Http2Stream` -object, a [Headers Object][], and numeric flags associated with the creation -of the stream. +object, a [HTTP2 Headers Object][], and numeric flags associated with the +creation of the stream. ```js const http2 = require('http2'); @@ -382,7 +382,7 @@ Transmits a `GOAWAY` frame to the connected peer *without* shutting down the added: v8.4.0 --> -* Value: {[Settings Object][]} +* Value: {HTTP2 Settings Object} A prototype-less object describing the current local settings of this `Http2Session`. The local settings are local to *this* `Http2Session` instance. @@ -461,7 +461,7 @@ instance's underlying [`net.Socket`]. added: v8.4.0 --> -* Value: {[Settings Object][]} +* Value: {HTTP2 Settings Object} A prototype-less object describing the current remote settings of this `Http2Session`. The remote settings are set by the *connected* HTTP/2 peer. @@ -570,7 +570,7 @@ An object describing the current status of this `Http2Session`. added: v8.4.0 --> -* `settings` {[Settings Object][]} +* `settings` {HTTP2 Settings Object} * Returns {undefined} Updates the current local settings for this `Http2Session` and sends a new @@ -707,7 +707,7 @@ client.on('altsvc', (alt, origin, stream) => { added: v8.4.0 --> -* `headers` {[Headers Object][]} +* `headers` {HTTP2 Headers Object} * `options` {Object} * `endStream` {boolean} `true` if the `Http2Stream` *writable* side should be closed initially, such as when sending a `GET` request that should not @@ -895,7 +895,7 @@ added: v8.4.0 The `'trailers'` event is emitted when a block of headers associated with trailing header fields is received. The listener callback is passed the -[Headers Object][] and flags associated with the headers. +[HTTP2 Headers Object][] and flags associated with the headers. ```js stream.on('trailers', (headers, flags) => { @@ -994,7 +994,7 @@ calling `http2stream.close()`, or `http2stream.destroy()`. Will be added: REPLACEME --> -* Value: {[Headers Object][]} +* Value: {HTTP2 Headers Object} An object containing the outbound headers sent for this `Http2Stream`. @@ -1003,7 +1003,7 @@ An object containing the outbound headers sent for this `Http2Stream`. added: REPLACEME --> -* Value: {[Headers Object][]\[\]} +* Value: {HTTP2 Headers Object[]} An array of objects containing the outbound informational (additional) headers sent for this `Http2Stream`. @@ -1013,7 +1013,7 @@ sent for this `Http2Stream`. added: REPLACEME --> -* Value: {[Headers Object][]} +* Value: {HTTP2 Headers Object} An object containing the outbound trailers sent for this this `HttpStream`. @@ -1096,8 +1096,8 @@ added: v8.4.0 The `'headers'` event is emitted when an additional block of headers is received for a stream, such as when a block of `1xx` informational headers is received. -The listener callback is passed the [Headers Object][] and flags associated with -the headers. +The listener callback is passed the [HTTP2 Headers Object][] and flags +associated with the headers. ```js stream.on('headers', (headers, flags) => { @@ -1111,8 +1111,8 @@ added: v8.4.0 --> The `'push'` event is emitted when response headers for a Server Push stream -are received. The listener callback is passed the [Headers Object][] and flags -associated with the headers. +are received. The listener callback is passed the [HTTP2 Headers Object][] and +flags associated with the headers. ```js stream.on('push', (headers, flags) => { @@ -1128,7 +1128,7 @@ added: v8.4.0 The `'response'` event is emitted when a response `HEADERS` frame has been received for this stream from the connected HTTP/2 server. The listener is invoked with two arguments: an Object containing the received -[Headers Object][], and flags associated with the headers. +[HTTP2 Headers Object][], and flags associated with the headers. For example: @@ -1158,7 +1158,7 @@ provide additional methods such as `http2stream.pushStream()` and added: v8.4.0 --> -* `headers` {[Headers Object][]} +* `headers` {HTTP2 Headers Object} * Returns: {undefined} Sends an additional informational `HEADERS` frame to the connected HTTP/2 peer. @@ -1189,7 +1189,7 @@ accepts push streams, `false` otherwise. Settings are the same for every added: v8.4.0 --> -* `headers` {[Headers Object][]} +* `headers` {HTTP2 Headers Object} * `options` {Object} * `exclusive` {boolean} When `true` and `parent` identifies a parent Stream, the created stream is made the sole direct dependency of the parent, with @@ -1200,9 +1200,9 @@ added: v8.4.0 * `callback` {Function} Callback that is called once the push stream has been initiated. * `err` {Error} - * `pushStream` {[`ServerHttp2Stream`][]} The returned pushStream object. - * `headers` {[Headers Object][]} Headers object the pushStream was initiated - with. + * `pushStream` {ServerHttp2Stream} The returned pushStream object. + * `headers` {HTTP2 Headers Object} Headers object the pushStream was + initiated with. * Returns: {undefined} Initiates a push stream. The callback is invoked with the new `Http2Stream` @@ -1232,7 +1232,7 @@ a `weight` value to `http2stream.priority` with the `silent` option set to added: v8.4.0 --> -* `headers` {[Headers Object][]} +* `headers` {HTTP2 Headers Object} * `options` {Object} * `endStream` {boolean} Set to `true` to indicate that the response will not include payload data. @@ -1278,7 +1278,7 @@ added: v8.4.0 --> * `fd` {number} A readable file descriptor. -* `headers` {[Headers Object][]} +* `headers` {HTTP2 Headers Object} * `options` {Object} * `statCheck` {Function} * `getTrailers` {Function} Callback function invoked to collect trailer @@ -1362,7 +1362,7 @@ added: v8.4.0 --> * `path` {string|Buffer|URL} -* `headers` {[Headers Object][]} +* `headers` {HTTP2 Headers Object} * `options` {Object} * `statCheck` {Function} * `onError` {Function} Callback function invoked in the case of an @@ -1728,7 +1728,7 @@ changes: * `selectPadding` {Function} When `options.paddingStrategy` is equal to `http2.constants.PADDING_STRATEGY_CALLBACK`, provides the callback function used to determine the padding. See [Using options.selectPadding][]. - * `settings` {[Settings Object][]} The initial settings to send to the + * `settings` {HTTP2 Settings Object} The initial settings to send to the remote peer upon connection. * `onRequestHandler` {Function} See [Compatibility API][] * Returns: {Http2Server} @@ -1815,7 +1815,7 @@ changes: * `selectPadding` {Function} When `options.paddingStrategy` is equal to `http2.constants.PADDING_STRATEGY_CALLBACK`, provides the callback function used to determine the padding. See [Using options.selectPadding][]. - * `settings` {[Settings Object][]} The initial settings to send to the + * `settings` {HTTP2 Settings Object} The initial settings to send to the remote peer upon connection. * ...: Any [`tls.createServer()`][] options can be provided. For servers, the identity options (`pfx` or `key`/`cert`) are usually required. @@ -1911,7 +1911,7 @@ changes: * `selectPadding` {Function} When `options.paddingStrategy` is equal to `http2.constants.PADDING_STRATEGY_CALLBACK`, provides the callback function used to determine the padding. See [Using options.selectPadding][]. - * `settings` {[Settings Object][]} The initial settings to send to the + * `settings` {HTTP2 Settings Object} The initial settings to send to the remote peer upon connection. * `createConnection` {Function} An optional callback that receives the `URL` instance passed to `connect` and the `options` object, and returns any @@ -1964,7 +1964,7 @@ a given number of milliseconds set using `http2server.setTimeout()`. added: v8.4.0 --> -* Returns: {[Settings Object][]} +* Returns: {HTTP2 Settings Object} Returns an object containing the default settings for an `Http2Session` instance. This method returns a new object instance every time it is called @@ -1975,7 +1975,7 @@ so instances returned may be safely modified for use. added: v8.4.0 --> -* `settings` {[Settings Object][]} +* `settings` {HTTP2 Settings Object} * Returns: {Buffer} Returns a `Buffer` instance containing serialized representation of the given @@ -1997,10 +1997,10 @@ added: v8.4.0 --> * `buf` {Buffer|Uint8Array} The packed settings. -* Returns: {[Settings Object][]} +* Returns: {HTTP2 Settings Object} -Returns a [Settings Object][] containing the deserialized settings from the -given `Buffer` as generated by `http2.getPackedSettings()`. +Returns a [HTTP2 Settings Object][] containing the deserialized settings from +the given `Buffer` as generated by `http2.getPackedSettings()`. ### Headers Object @@ -2372,7 +2372,7 @@ Example: console.log(request.headers); ``` -See [Headers Object][]. +See [HTTP2 Headers Object][]. *Note*: In HTTP/2, the request path, hostname, protocol, and method are represented as special headers prefixed with the `:` character (e.g. `':path'`). @@ -3094,13 +3094,13 @@ following additional properties: [Compatibility API]: #http2_compatibility_api [HTTP/1]: http.html [HTTP/2]: https://tools.ietf.org/html/rfc7540 +[HTTP2 Headers Object]: #http2_headers_object +[HTTP2 Settings Object]: #http2_settings_object [HTTPS]: https.html -[Headers Object]: #http2_headers_object [Http2Session and Sockets]: #http2_http2session_and_sockets [Performance Observer]: perf_hooks.html [Readable Stream]: stream.html#stream_class_stream_readable [RFC 7838]: https://tools.ietf.org/html/rfc7838 -[Settings Object]: #http2_settings_object [Using options.selectPadding]: #http2_using_options_selectpadding [Writable Stream]: stream.html#stream_writable_streams [`'checkContinue'`]: #http2_event_checkcontinue diff --git a/lib/_stream_readable.js b/lib/_stream_readable.js index 9cf786a15be..fb0fbbbdf4d 100644 --- a/lib/_stream_readable.js +++ b/lib/_stream_readable.js @@ -31,6 +31,7 @@ const util = require('util'); const debug = util.debuglog('stream'); const BufferList = require('internal/streams/BufferList'); const destroyImpl = require('internal/streams/destroy'); +const { getHighWaterMark } = require('internal/streams/state'); const errors = require('internal/errors'); const ReadableAsyncIterator = require('internal/streams/async_iterator'); const { emitExperimentalWarning } = require('internal/util'); @@ -77,19 +78,8 @@ function ReadableState(options, stream) { // the point at which it stops calling _read() to fill the buffer // Note: 0 is a valid value, means "don't call _read preemptively ever" - var hwm = options.highWaterMark; - var readableHwm = options.readableHighWaterMark; - var defaultHwm = this.objectMode ? 16 : 16 * 1024; - - if (hwm || hwm === 0) - this.highWaterMark = hwm; - else if (isDuplex && (readableHwm || readableHwm === 0)) - this.highWaterMark = readableHwm; - else - this.highWaterMark = defaultHwm; - - // cast to ints. - this.highWaterMark = Math.floor(this.highWaterMark); + this.highWaterMark = getHighWaterMark(this, options, 'readableHighWaterMark', + isDuplex); // A linked list is used to store data chunks instead of an array because the // linked list can remove elements from the beginning faster than @@ -520,7 +510,9 @@ function emitReadable(stream) { function emitReadable_(stream) { var state = stream._readableState; debug('emit readable'); - stream.emit('readable'); + if (!state.destroyed && (state.length || state.ended)) { + stream.emit('readable'); + } state.needReadable = !state.flowing && !state.ended; flow(stream); } diff --git a/lib/_stream_writable.js b/lib/_stream_writable.js index 549bff1599a..de96a902552 100644 --- a/lib/_stream_writable.js +++ b/lib/_stream_writable.js @@ -33,6 +33,7 @@ const internalUtil = require('internal/util'); const Stream = require('stream'); const { Buffer } = require('buffer'); const destroyImpl = require('internal/streams/destroy'); +const { getHighWaterMark } = require('internal/streams/state'); const errors = require('internal/errors'); util.inherits(Writable, Stream); @@ -59,19 +60,8 @@ function WritableState(options, stream) { // the point at which write() starts returning false // Note: 0 is a valid value, means that we always return false if // the entire buffer is not flushed immediately on write() - var hwm = options.highWaterMark; - var writableHwm = options.writableHighWaterMark; - var defaultHwm = this.objectMode ? 16 : 16 * 1024; - - if (hwm || hwm === 0) - this.highWaterMark = hwm; - else if (isDuplex && (writableHwm || writableHwm === 0)) - this.highWaterMark = writableHwm; - else - this.highWaterMark = defaultHwm; - - // cast to ints. - this.highWaterMark = Math.floor(this.highWaterMark); + this.highWaterMark = getHighWaterMark(this, options, 'writableHighWaterMark', + isDuplex); // if _final has been called this.finalCalled = false; diff --git a/lib/dns.js b/lib/dns.js index 92b55f03595..979d724c3e4 100644 --- a/lib/dns.js +++ b/lib/dns.js @@ -24,7 +24,7 @@ const util = require('util'); const cares = process.binding('cares_wrap'); -const { isLegalPort } = require('internal/net'); +const { isIP, isIPv4, isLegalPort } = require('internal/net'); const { customPromisifyArgs } = require('internal/util'); const errors = require('internal/errors'); const { @@ -38,7 +38,6 @@ const { GetNameInfoReqWrap, QueryReqWrap, ChannelWrap, - isIP } = cares; function errnoException(err, syscall, hostname) { @@ -66,30 +65,6 @@ function errnoException(err, syscall, hostname) { } const IANA_DNS_PORT = 53; -const digits = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0-15 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 32-47 - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48-63 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 64-79 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80-95 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 96-111 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 112-127 -]; -function isIPv4(str) { - if (!digits[str.charCodeAt(0)]) return false; - if (str.length === 1) return false; - if (str.charCodeAt(1) === 46/*'.'*/) - return true; - else if (!digits[str.charCodeAt(1)]) - return false; - if (str.length === 2) return false; - if (str.charCodeAt(2) === 46/*'.'*/) - return true; - else if (!digits[str.charCodeAt(2)]) - return false; - return (str.length > 3 && str.charCodeAt(3) === 46/*'.'*/); -} function onlookup(err, addresses) { diff --git a/lib/domain.js b/lib/domain.js index 9670a53629e..08fbd207f17 100644 --- a/lib/domain.js +++ b/lib/domain.js @@ -94,11 +94,21 @@ process.setUncaughtExceptionCaptureCallback = function(fn) { throw err; }; +function topLevelDomainCallback(cb, ...args) { + const domain = this.domain; + if (domain) + domain.enter(); + const ret = Reflect.apply(cb, this, args); + if (domain) + domain.exit(); + return ret; +} + // 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(); +internalBinding('domain').enable(topLevelDomainCallback); function updateExceptionCapture() { if (stack.every((domain) => domain.listenerCount('error') === 0)) { diff --git a/lib/internal/bootstrap_node.js b/lib/internal/bootstrap_node.js index 52aa6aacdf8..96d3ac1a687 100644 --- a/lib/internal/bootstrap_node.js +++ b/lib/internal/bootstrap_node.js @@ -21,9 +21,6 @@ setupProcessObject(); - internalBinding = process._internalBinding; - delete process._internalBinding; - // do this good and early, since it handles errors. setupProcessFatal(); @@ -261,6 +258,54 @@ perf.markMilestone(NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE); } + const moduleLoadList = []; + Object.defineProperty(process, 'moduleLoadList', { + value: moduleLoadList, + configurable: true, + enumerable: true, + writable: false + }); + + { + const bindingObj = Object.create(null); + + const getBinding = process.binding; + process.binding = function binding(module) { + module = String(module); + let mod = bindingObj[module]; + if (typeof mod !== 'object') { + mod = bindingObj[module] = getBinding(module); + moduleLoadList.push(`Binding ${module}`); + } + return mod; + }; + + const getLinkedBinding = process._linkedBinding; + process._linkedBinding = function _linkedBinding(module) { + module = String(module); + let mod = bindingObj[module]; + if (typeof mod !== 'object') + mod = bindingObj[module] = getLinkedBinding(module); + return mod; + }; + } + + { + const bindingObj = Object.create(null); + + const getInternalBinding = process._internalBinding; + delete process._internalBinding; + + internalBinding = function internalBinding(module) { + let mod = bindingObj[module]; + if (typeof mod !== 'object') { + mod = bindingObj[module] = getInternalBinding(module); + moduleLoadList.push(`Internal Binding ${module}`); + } + return mod; + }; + } + function setupProcessObject() { process._setupProcessObject(pushValueToArray); @@ -568,7 +613,7 @@ throw err; } - process.moduleLoadList.push(`NativeModule ${id}`); + moduleLoadList.push(`NativeModule ${id}`); const nativeModule = new NativeModule(id); diff --git a/lib/internal/cluster/master.js b/lib/internal/cluster/master.js index 280955549ad..570cf7bc6f9 100644 --- a/lib/internal/cluster/master.js +++ b/lib/internal/cluster/master.js @@ -126,6 +126,7 @@ function createWorkerProcess(id, env) { } return fork(cluster.settings.exec, cluster.settings.args, { + cwd: cluster.settings.cwd, env: workerEnv, silent: cluster.settings.silent, windowsHide: cluster.settings.windowsHide, diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 058bbfac5d4..6a52d55bb84 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -23,9 +23,16 @@ const { kMaxLength } = process.binding('buffer'); const { defineProperty } = Object; // Lazily loaded -var util = null; +var util_ = null; var buffer; +function lazyUtil() { + if (!util_) { + util_ = require('util'); + } + return util_; +} + function makeNodeError(Base) { return class NodeError extends Base { constructor(key, ...args) { @@ -142,6 +149,7 @@ function createErrDiff(actual, expected, operator) { var lastPos = 0; var end = ''; var skipped = false; + const util = lazyUtil(); const actualLines = util .inspect(actual, { compact: false }).split('\n'); const expectedLines = util @@ -262,14 +270,14 @@ class AssertionError extends Error { if (message != null) { super(message); } else { - if (util === null) { - util = require('util'); - if (process.stdout.isTTY && process.stdout.getColorDepth() !== 1) { - green = '\u001b[32m'; - white = '\u001b[39m'; - red = '\u001b[31m'; - } + if (util_ === null && + process.stdout.isTTY && + process.stdout.getColorDepth() !== 1) { + green = '\u001b[32m'; + white = '\u001b[39m'; + red = '\u001b[31m'; } + const util = lazyUtil(); if (actual && actual.stack && actual instanceof Error) actual = `${actual.name}: ${actual.message}`; @@ -333,7 +341,7 @@ function message(key, args) { if (typeof msg === 'function') { fmt = msg; } else { - if (util === null) util = require('util'); + const util = lazyUtil(); fmt = util.format; if (args === undefined || args.length === 0) return msg; @@ -537,8 +545,14 @@ E('ERR_INSPECTOR_CLOSED', 'Session was closed'); E('ERR_INSPECTOR_NOT_AVAILABLE', 'Inspector is not available'); E('ERR_INSPECTOR_NOT_CONNECTED', 'Session is not connected'); E('ERR_INVALID_ARG_TYPE', invalidArgType); -E('ERR_INVALID_ARG_VALUE', (name, value) => - `The value "${String(value)}" is invalid for argument "${name}"`); +E('ERR_INVALID_ARG_VALUE', (name, value, reason = 'is invalid') => { + const util = lazyUtil(); + let inspected = util.inspect(value); + if (inspected.length > 128) { + inspected = inspected.slice(0, 128) + '...'; + } + return `The argument '${name}' ${reason}. Received ${inspected}`; +}), E('ERR_INVALID_ARRAY_LENGTH', (name, len, actual) => { internalAssert(typeof actual === 'number', 'actual must be a number'); diff --git a/lib/internal/module.js b/lib/internal/module.js index d2140411552..c3dede40fa3 100644 --- a/lib/internal/module.js +++ b/lib/internal/module.js @@ -1,5 +1,7 @@ 'use strict'; +const errors = require('internal/errors'); + // Invoke with makeRequireFunction(module) where |module| is the Module object // to use as the context for the require() function. function makeRequireFunction(mod) { @@ -15,12 +17,20 @@ function makeRequireFunction(mod) { } function resolve(request, options) { + if (typeof request !== 'string') { + throw new errors.Error('ERR_INVALID_ARG_TYPE', + 'request', 'string', request); + } return Module._resolveFilename(request, mod, false, options); } require.resolve = resolve; function paths(request) { + if (typeof request !== 'string') { + throw new errors.Error('ERR_INVALID_ARG_TYPE', + 'request', 'string', request); + } return Module._resolveLookupPaths(request, mod, true); } diff --git a/lib/internal/net.js b/lib/internal/net.js index 847539d5769..adaa4475b02 100644 --- a/lib/internal/net.js +++ b/lib/internal/net.js @@ -1,8 +1,22 @@ 'use strict'; const Buffer = require('buffer').Buffer; +const { isIPv6 } = process.binding('cares_wrap'); const { writeBuffer } = process.binding('fs'); +const octet = '(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'; +const re = new RegExp(`^${octet}[.]${octet}[.]${octet}[.]${octet}$`); + +function isIPv4(s) { + return re.test(s); +} + +function isIP(s) { + if (isIPv4(s)) return 4; + if (isIPv6(s)) return 6; + return 0; +} + // Check that the port number is not NaN when coerced to a number, // is an integer and that it falls within the legal range of port numbers. function isLegalPort(port) { @@ -33,6 +47,9 @@ function makeSyncWrite(fd) { } module.exports = { + isIP, + isIPv4, + isIPv6, isLegalPort, makeSyncWrite, normalizedArgsSymbol: Symbol('normalizedArgs') diff --git a/lib/internal/streams/state.js b/lib/internal/streams/state.js new file mode 100644 index 00000000000..cca79c93de4 --- /dev/null +++ b/lib/internal/streams/state.js @@ -0,0 +1,26 @@ +'use strict'; + +const errors = require('internal/errors'); + +function getHighWaterMark(state, options, duplexKey, isDuplex) { + let hwm = options.highWaterMark; + if (hwm != null) { + if (typeof hwm !== 'number' || !(hwm >= 0)) + throw new errors.TypeError('ERR_INVALID_OPT_VALUE', 'highWaterMark', hwm); + return Math.floor(hwm); + } else if (isDuplex) { + hwm = options[duplexKey]; + if (hwm != null) { + if (typeof hwm !== 'number' || !(hwm >= 0)) + throw new errors.TypeError('ERR_INVALID_OPT_VALUE', duplexKey, hwm); + return Math.floor(hwm); + } + } + + // Default value + return state.objectMode ? 16 : 16 * 1024; +} + +module.exports = { + getHighWaterMark +}; diff --git a/lib/module.js b/lib/module.js index 4a8480ed1be..5ee537f1572 100644 --- a/lib/module.js +++ b/lib/module.js @@ -603,10 +603,15 @@ Module.prototype.load = function(filename) { // Loads a module at the given file path. Returns that module's // `exports` property. -Module.prototype.require = function(path) { - assert(path, 'missing path'); - assert(typeof path === 'string', 'path must be a string'); - return Module._load(path, this, /* isMain */ false); +Module.prototype.require = function(id) { + if (typeof id !== 'string') { + throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'id', 'string', id); + } + if (id === '') { + throw new errors.Error('ERR_INVALID_ARG_VALUE', + 'id', id, 'must be a non-empty string'); + } + return Module._load(id, this, /* isMain */ false); }; diff --git a/lib/net.js b/lib/net.js index 41c1cacac5e..cda613f2e23 100644 --- a/lib/net.js +++ b/lib/net.js @@ -26,12 +26,14 @@ const stream = require('stream'); const util = require('util'); const internalUtil = require('internal/util'); const { + isIP, + isIPv4, + isIPv6, isLegalPort, normalizedArgsSymbol, makeSyncWrite } = require('internal/net'); const assert = require('assert'); -const cares = process.binding('cares_wrap'); const { UV_EADDRINUSE, UV_EINVAL, @@ -1066,7 +1068,7 @@ function lookupAndConnect(self, options) { var localAddress = options.localAddress; var localPort = options.localPort; - if (localAddress && !cares.isIP(localAddress)) { + if (localAddress && !isIP(localAddress)) { throw new errors.TypeError('ERR_INVALID_IP_ADDRESS', localAddress); } @@ -1091,7 +1093,7 @@ function lookupAndConnect(self, options) { port |= 0; // If host is an IP, skip performing a lookup - var addressType = cares.isIP(host); + var addressType = isIP(host); if (addressType) { nextTick(self[async_id_symbol], function() { if (self.connecting) @@ -1775,9 +1777,9 @@ module.exports = { connect, createConnection: connect, createServer, - isIP: cares.isIP, - isIPv4: cares.isIPv4, - isIPv6: cares.isIPv6, + isIP: isIP, + isIPv4: isIPv4, + isIPv6: isIPv6, Server, Socket, Stream: Socket, // Legacy naming diff --git a/lib/timers.js b/lib/timers.js index a38829d2f02..db43a7491de 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -218,11 +218,11 @@ function TimersList(msecs, unrefed) { if (unrefed === true) timer.unref(); timer.start(msecs); - - timer[kOnTimeout] = listOnTimeout; } -function listOnTimeout() { +// adds listOnTimeout to the C++ object prototype, as +// V8 would not inline it otherwise. +TimerWrap.prototype[kOnTimeout] = function listOnTimeout() { var list = this._list; var msecs = list.msecs; @@ -291,7 +291,7 @@ function listOnTimeout() { return; this.close(); -} +}; // An optimization so that the try/finally only de-optimizes (since at least v8 diff --git a/node.gyp b/node.gyp index 8d2cf059578..dc234b26ff6 100644 --- a/node.gyp +++ b/node.gyp @@ -146,6 +146,7 @@ 'lib/internal/streams/duplexpair.js', 'lib/internal/streams/legacy.js', 'lib/internal/streams/destroy.js', + 'lib/internal/streams/state.js', 'lib/internal/wrap_js_stream.js', 'deps/v8/tools/splaytree.js', 'deps/v8/tools/codemap.js', @@ -311,6 +312,7 @@ 'src/node_constants.cc', 'src/node_contextify.cc', 'src/node_debug_options.cc', + 'src/node_domain.cc', 'src/node_file.cc', 'src/node_http2.cc', 'src/node_http_parser.cc', diff --git a/src/cares_wrap.cc b/src/cares_wrap.cc index de3cb8f89c1..191e3c92dcc 100644 --- a/src/cares_wrap.cc +++ b/src/cares_wrap.cc @@ -1876,60 +1876,32 @@ void AfterGetNameInfo(uv_getnameinfo_t* req, delete req_wrap; } - -void IsIP(const FunctionCallbackInfo& args) { - node::Utf8Value ip(args.GetIsolate(), args[0]); - char address_buffer[sizeof(struct in6_addr)]; - - int rc = 0; - if (uv_inet_pton(AF_INET, *ip, &address_buffer) == 0) - rc = 4; - else if (uv_inet_pton(AF_INET6, *ip, &address_buffer) == 0) - rc = 6; - - args.GetReturnValue().Set(rc); -} - -void IsIPv4(const FunctionCallbackInfo& args) { - node::Utf8Value ip(args.GetIsolate(), args[0]); - char address_buffer[sizeof(struct in_addr)]; - - if (uv_inet_pton(AF_INET, *ip, &address_buffer) == 0) { - args.GetReturnValue().Set(true); - } else { - args.GetReturnValue().Set(false); - } +using ParseIPResult = decltype(static_cast(0)->addr); + +int ParseIP(const char* ip, ParseIPResult* result = nullptr) { + ParseIPResult tmp; + if (result == nullptr) result = &tmp; + if (0 == uv_inet_pton(AF_INET, ip, result)) return 4; + if (0 == uv_inet_pton(AF_INET6, ip, result)) return 6; + return 0; } void IsIPv6(const FunctionCallbackInfo& args) { node::Utf8Value ip(args.GetIsolate(), args[0]); - char address_buffer[sizeof(struct in6_addr)]; - - if (uv_inet_pton(AF_INET6, *ip, &address_buffer) == 0) { - args.GetReturnValue().Set(true); - } else { - args.GetReturnValue().Set(false); - } + args.GetReturnValue().Set(6 == ParseIP(*ip)); } void CanonicalizeIP(const FunctionCallbackInfo& args) { v8::Isolate* isolate = args.GetIsolate(); node::Utf8Value ip(isolate, args[0]); - char address_buffer[sizeof(struct in6_addr)]; - char canonical_ip[INET6_ADDRSTRLEN]; - int af; - if (uv_inet_pton(AF_INET, *ip, &address_buffer) == 0) - af = AF_INET; - else if (uv_inet_pton(AF_INET6, *ip, &address_buffer) == 0) - af = AF_INET6; - else - return; - - int err = uv_inet_ntop(af, address_buffer, canonical_ip, - sizeof(canonical_ip)); - CHECK_EQ(err, 0); + ParseIPResult result; + const int rc = ParseIP(*ip, &result); + if (rc == 0) return; + char canonical_ip[INET6_ADDRSTRLEN]; + const int af = (rc == 4 ? AF_INET : AF_INET6); + CHECK_EQ(0, uv_inet_ntop(af, &result, canonical_ip, sizeof(canonical_ip))); args.GetReturnValue().Set(String::NewFromUtf8(isolate, canonical_ip)); } @@ -2156,8 +2128,6 @@ void Initialize(Local target, env->SetMethod(target, "getaddrinfo", GetAddrInfo); env->SetMethod(target, "getnameinfo", GetNameInfo); - env->SetMethod(target, "isIP", IsIP); - env->SetMethod(target, "isIPv4", IsIPv4); env->SetMethod(target, "isIPv6", IsIPv6); env->SetMethod(target, "canonicalizeIP", CanonicalizeIP); diff --git a/src/env-inl.h b/src/env-inl.h index ed459228205..67807b87d6c 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -310,7 +310,6 @@ inline Environment::Environment(IsolateData* isolate_data, immediate_info_(context->GetIsolate()), tick_info_(context->GetIsolate()), timer_base_(uv_now(isolate_data->event_loop())), - using_domains_(false), printed_error_(false), trace_sync_io_(false), abort_on_uncaught_exception_(false), @@ -329,18 +328,6 @@ inline Environment::Environment(IsolateData* isolate_data, v8::Context::Scope context_scope(context); set_as_external(v8::External::New(isolate(), this)); - v8::Local null = v8::Null(isolate()); - v8::Local binding_cache_object = v8::Object::New(isolate()); - CHECK(binding_cache_object->SetPrototype(context, null).FromJust()); - set_binding_cache_object(binding_cache_object); - - v8::Local internal_binding_cache_object = - v8::Object::New(isolate()); - CHECK(internal_binding_cache_object->SetPrototype(context, null).FromJust()); - set_internal_binding_cache_object(internal_binding_cache_object); - - set_module_load_list_array(v8::Array::New(isolate())); - AssignToContext(context, ContextInfo("")); destroy_async_id_list_.reserve(512); @@ -425,14 +412,6 @@ inline uint64_t Environment::timer_base() const { return timer_base_; } -inline bool Environment::using_domains() const { - return using_domains_; -} - -inline void Environment::set_using_domains(bool value) { - using_domains_ = value; -} - inline bool Environment::printed_error() const { return printed_error_; } diff --git a/src/env.h b/src/env.h index 6e0b74d620a..d73be8156ec 100644 --- a/src/env.h +++ b/src/env.h @@ -91,7 +91,6 @@ class ModuleWrap; V(decorated_private_symbol, "node:decorated") \ V(npn_buffer_private_symbol, "node:npnBuffer") \ V(selected_npn_buffer_private_symbol, "node:selectedNpnBuffer") \ - V(domain_private_symbol, "node:domain") \ // Strings are per-isolate primitives but Environment proxies them // for the sake of convenience. Strings should be ASCII-only. @@ -128,7 +127,6 @@ class ModuleWrap; V(dns_soa_string, "SOA") \ V(dns_srv_string, "SRV") \ V(dns_txt_string, "TXT") \ - V(domain_string, "domain") \ V(emit_warning_string, "emitWarning") \ V(exchange_string, "exchange") \ V(encoding_string, "encoding") \ @@ -244,7 +242,6 @@ class ModuleWrap; V(subject_string, "subject") \ V(subjectaltname_string, "subjectaltname") \ V(syscall_string, "syscall") \ - V(tick_domain_cb_string, "_tickDomainCallback") \ V(ticketkeycallback_string, "onticketkeycallback") \ V(timeout_string, "timeout") \ V(tls_ticket_string, "tlsTicket") \ @@ -277,17 +274,15 @@ class ModuleWrap; V(async_hooks_after_function, v8::Function) \ V(async_hooks_promise_resolve_function, v8::Function) \ V(async_hooks_binding, v8::Object) \ - V(binding_cache_object, v8::Object) \ - V(internal_binding_cache_object, v8::Object) \ V(buffer_prototype_object, v8::Object) \ V(context, v8::Context) \ + V(domain_callback, v8::Function) \ V(host_import_module_dynamically_callback, v8::Function) \ V(http2ping_constructor_template, v8::ObjectTemplate) \ V(http2stream_constructor_template, v8::ObjectTemplate) \ V(http2settings_constructor_template, v8::ObjectTemplate) \ V(immediate_callback_function, v8::Function) \ V(inspector_console_api_object, v8::Object) \ - V(module_load_list_array, v8::Array) \ V(pbkdf2_constructor_template, v8::ObjectTemplate) \ V(pipe_constructor_template, v8::FunctionTemplate) \ V(performance_entry_callback, v8::Function) \ @@ -568,9 +563,6 @@ class Environment { inline IsolateData* isolate_data() const; - inline bool using_domains() const; - inline void set_using_domains(bool value); - inline bool printed_error() const; inline void set_printed_error(bool value); @@ -746,7 +738,6 @@ class Environment { ImmediateInfo immediate_info_; TickInfo tick_info_; const uint64_t timer_base_; - bool using_domains_; bool printed_error_; bool trace_sync_io_; bool abort_on_uncaught_exception_; diff --git a/src/node.cc b/src/node.cc index 9855ffcd9b1..1522b268ac7 100644 --- a/src/node.cc +++ b/src/node.cc @@ -801,62 +801,6 @@ bool ShouldAbortOnUncaughtException(Isolate* isolate) { } -Local GetDomainProperty(Environment* env, Local object) { - Local domain_v = - object->GetPrivate(env->context(), env->domain_private_symbol()) - .ToLocalChecked(); - if (domain_v->IsObject()) { - return domain_v; - } - return object->Get(env->context(), env->domain_string()).ToLocalChecked(); -} - - -void DomainEnter(Environment* env, Local object) { - Local domain_v = GetDomainProperty(env, object); - if (domain_v->IsObject()) { - Local domain = domain_v.As(); - Local enter_v = domain->Get(env->enter_string()); - if (enter_v->IsFunction()) { - if (enter_v.As()->Call(domain, 0, nullptr).IsEmpty()) { - FatalError("node::AsyncWrap::MakeCallback", - "domain enter callback threw, please report this"); - } - } - } -} - - -void DomainExit(Environment* env, v8::Local object) { - Local domain_v = GetDomainProperty(env, object); - if (domain_v->IsObject()) { - Local domain = domain_v.As(); - Local exit_v = domain->Get(env->exit_string()); - if (exit_v->IsFunction()) { - if (exit_v.As()->Call(domain, 0, nullptr).IsEmpty()) { - FatalError("node::AsyncWrap::MakeCallback", - "domain exit callback threw, please report this"); - } - } - } -} - -void SetupDomainUse(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - - if (env->using_domains()) - return; - env->set_using_domains(true); - - HandleScope scope(env->isolate()); - - // Do a little housekeeping. - env->process_object()->Delete( - env->context(), - FIXED_ONE_BYTE_STRING(args.GetIsolate(), "_setupDomainUse")).FromJust(); -} - - void RunMicrotasks(const FunctionCallbackInfo& args) { args.GetIsolate()->RunMicrotasks(); } @@ -993,11 +937,6 @@ InternalCallbackScope::InternalCallbackScope(Environment* env, // If you hit this assertion, you forgot to enter the v8::Context first. CHECK_EQ(Environment::GetCurrent(env->isolate()), env); - if (asyncContext.async_id == 0 && env->using_domains() && - !object_.IsEmpty()) { - DomainEnter(env, object_); - } - if (asyncContext.async_id != 0) { // No need to check a return value because the application will exit if // an exception occurs. @@ -1027,11 +966,6 @@ void InternalCallbackScope::Close() { AsyncWrap::EmitAfter(env_, async_context_.async_id); } - if (async_context_.async_id == 0 && env_->using_domains() && - !object_.IsEmpty()) { - DomainExit(env_, object_); - } - if (IsInnerMakeCallback()) { return; } @@ -1053,11 +987,6 @@ void InternalCallbackScope::Close() { return; } - if (env_->async_hooks()->fields()[AsyncHooks::kTotals]) { - CHECK_EQ(env_->execution_async_id(), 0); - CHECK_EQ(env_->trigger_async_id(), 0); - } - Local process = env_->process_object(); if (env_->tick_callback_function()->Call(process, 0, nullptr).IsEmpty()) { @@ -1077,17 +1006,22 @@ MaybeLocal InternalMakeCallback(Environment* env, return Undefined(env->isolate()); } + Local domain_cb = env->domain_callback(); MaybeLocal ret; - - { + if (asyncContext.async_id != 0 || domain_cb.IsEmpty() || recv.IsEmpty()) { ret = callback->Call(env->context(), recv, argc, argv); + } else { + std::vector> args(1 + argc); + args[0] = callback; + std::copy(&argv[0], &argv[argc], &args[1]); + ret = domain_cb->Call(env->context(), recv, args.size(), &args[0]); + } - if (ret.IsEmpty()) { - // NOTE: For backwards compatibility with public API we return Undefined() - // if the top level call threw. - scope.MarkAsFailed(); - return scope.IsInnerMakeCallback() ? ret : Undefined(env->isolate()); - } + if (ret.IsEmpty()) { + // NOTE: For backwards compatibility with public API we return Undefined() + // if the top level call threw. + scope.MarkAsFailed(); + return scope.IsInnerMakeCallback() ? ret : Undefined(env->isolate()); } scope.Close(); @@ -2560,22 +2494,6 @@ Maybe ProcessEmitDeprecationWarning(Environment* env, } -static bool PullFromCache(Environment* env, - const FunctionCallbackInfo& args, - Local module, - Local cache) { - Local context = env->context(); - Local exports_v; - Local exports; - if (cache->Get(context, module).ToLocal(&exports_v) && - exports_v->IsObject() && - exports_v->ToObject(context).ToLocal(&exports)) { - args.GetReturnValue().Set(exports); - return true; - } - return false; -} - static Local InitModule(Environment* env, node_module* mod, Local module) { @@ -2603,22 +2521,10 @@ static void ThrowIfNoSuchModule(Environment* env, const char* module_v) { static void Binding(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - Local module; - if (!args[0]->ToString(env->context()).ToLocal(&module)) return; - - Local cache = env->binding_cache_object(); + CHECK(args[0]->IsString()); - if (PullFromCache(env, args, module, cache)) - return; - - // Append a string to process.moduleLoadList - char buf[1024]; + Local module = args[0].As(); node::Utf8Value module_v(env->isolate(), module); - snprintf(buf, sizeof(buf), "Binding %s", *module_v); - - Local modules = env->module_load_list_array(); - uint32_t l = modules->Length(); - modules->Set(l, OneByteString(env->isolate(), buf)); node_module* mod = get_builtin_module(*module_v); Local exports; @@ -2635,7 +2541,6 @@ static void Binding(const FunctionCallbackInfo& args) { } else { return ThrowIfNoSuchModule(env, *module_v); } - cache->Set(module, exports); args.GetReturnValue().Set(exports); } @@ -2643,27 +2548,14 @@ static void Binding(const FunctionCallbackInfo& args) { static void InternalBinding(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - Local module; - if (!args[0]->ToString(env->context()).ToLocal(&module)) return; + CHECK(args[0]->IsString()); - Local cache = env->internal_binding_cache_object(); - - if (PullFromCache(env, args, module, cache)) - return; - - // Append a string to process.moduleLoadList - char buf[1024]; + Local module = args[0].As(); node::Utf8Value module_v(env->isolate(), module); - snprintf(buf, sizeof(buf), "Internal Binding %s", *module_v); - - Local modules = env->module_load_list_array(); - uint32_t l = modules->Length(); - modules->Set(l, OneByteString(env->isolate(), buf)); node_module* mod = get_internal_module(*module_v); if (mod == nullptr) return ThrowIfNoSuchModule(env, *module_v); Local exports = InitModule(env, mod, module); - cache->Set(module, exports); args.GetReturnValue().Set(exports); } @@ -2671,14 +2563,9 @@ static void InternalBinding(const FunctionCallbackInfo& args) { static void LinkedBinding(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args.GetIsolate()); - Local module_name; - if (!args[0]->ToString(env->context()).ToLocal(&module_name)) return; + CHECK(args[0]->IsString()); - Local cache = env->binding_cache_object(); - Local exports_v = cache->Get(module_name); - - if (exports_v->IsObject()) - return args.GetReturnValue().Set(exports_v.As()); + Local module_name = args[0].As(); node::Utf8Value module_name_v(env->isolate(), module_name); node_module* mod = get_linked_module(*module_name_v); @@ -2709,7 +2596,6 @@ static void LinkedBinding(const FunctionCallbackInfo& args) { } auto effective_exports = module->Get(exports_prop); - cache->Set(module_name, effective_exports); args.GetReturnValue().Set(effective_exports); } @@ -3042,11 +2928,6 @@ void SetupProcessObject(Environment* env, "version", FIXED_ONE_BYTE_STRING(env->isolate(), NODE_VERSION)); - // process.moduleLoadList - READONLY_PROPERTY(process, - "moduleLoadList", - env->module_load_list_array()); - // process.versions Local versions = Object::New(env->isolate()); READONLY_PROPERTY(process, "versions", versions); @@ -3387,7 +3268,6 @@ void SetupProcessObject(Environment* env, env->SetMethod(process, "_setupProcessObject", SetupProcessObject); env->SetMethod(process, "_setupNextTick", SetupNextTick); env->SetMethod(process, "_setupPromises", SetupPromises); - env->SetMethod(process, "_setupDomainUse", SetupDomainUse); } diff --git a/src/node_domain.cc b/src/node_domain.cc new file mode 100644 index 00000000000..f4f585ac4f4 --- /dev/null +++ b/src/node_domain.cc @@ -0,0 +1,34 @@ +#include "v8.h" +#include "node_internals.h" + +namespace node { +namespace domain { + +using v8::Context; +using v8::Function; +using v8::FunctionCallbackInfo; +using v8::Local; +using v8::Object; +using v8::Value; + + +void Enable(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + CHECK(args[0]->IsFunction()); + + env->set_domain_callback(args[0].As()); +} + +void Initialize(Local target, + Local unused, + Local context) { + Environment* env = Environment::GetCurrent(context); + + env->SetMethod(target, "enable", Enable); +} + +} // namespace domain +} // namespace node + +NODE_MODULE_CONTEXT_AWARE_INTERNAL(domain, node::domain::Initialize) diff --git a/src/node_internals.h b/src/node_internals.h index 517358aef8c..d807a08b7c1 100644 --- a/src/node_internals.h +++ b/src/node_internals.h @@ -104,6 +104,7 @@ struct sockaddr; V(cares_wrap) \ V(config) \ V(contextify) \ + V(domain) \ V(fs) \ V(fs_event_wrap) \ V(http2) \ diff --git a/src/node_perf.cc b/src/node_perf.cc index 4c3dfbe4eb7..48b8d02b79f 100644 --- a/src/node_perf.cc +++ b/src/node_perf.cc @@ -7,7 +7,6 @@ namespace node { namespace performance { using v8::Array; -using v8::ArrayBuffer; using v8::Context; using v8::Function; using v8::FunctionCallbackInfo; @@ -324,7 +323,6 @@ void Init(Local target, Environment* env = Environment::GetCurrent(context); Isolate* isolate = env->isolate(); performance_state* state = env->performance_state(); - auto state_ab = ArrayBuffer::New(isolate, state, sizeof(*state)); target->Set(context, FIXED_ONE_BYTE_STRING(isolate, "observerCounts"), diff --git a/test/addons/repl-domain-abort/binding.cc b/test/addons/repl-domain-abort/binding.cc index 1b4dbfa84e5..d2f7560048f 100644 --- a/test/addons/repl-domain-abort/binding.cc +++ b/test/addons/repl-domain-abort/binding.cc @@ -22,6 +22,7 @@ #include #include +using v8::Boolean; using v8::Function; using v8::FunctionCallbackInfo; using v8::Local; @@ -31,11 +32,16 @@ using v8::Value; void Method(const FunctionCallbackInfo& args) { Isolate* isolate = args.GetIsolate(); - node::MakeCallback(isolate, - isolate->GetCurrentContext()->Global(), - args[0].As(), - 0, - nullptr); + Local params[] = { + Boolean::New(isolate, true), + Boolean::New(isolate, false) + }; + Local ret = node::MakeCallback(isolate, + isolate->GetCurrentContext()->Global(), + args[0].As(), + 2, + params); + assert(ret->IsTrue()); } void init(Local exports) { diff --git a/test/addons/repl-domain-abort/test.js b/test/addons/repl-domain-abort/test.js index 1d6116159c8..2049fe6e6a2 100644 --- a/test/addons/repl-domain-abort/test.js +++ b/test/addons/repl-domain-abort/test.js @@ -40,7 +40,8 @@ const lines = [ // This line shouldn't cause an assertion error. `require('${buildPath}')` + // Log output to double check callback ran. - '.method(function() { console.log(\'cb_ran\'); });', + '.method(function(v1, v2) {' + + 'console.log(\'cb_ran\'); return v1 === true && v2 === false; });', ]; const dInput = new stream.Readable(); diff --git a/test/cctest/node_module_reg.cc b/test/cctest/node_module_reg.cc index a0736d2cc3e..bd4f20bc9f8 100644 --- a/test/cctest/node_module_reg.cc +++ b/test/cctest/node_module_reg.cc @@ -5,6 +5,7 @@ void _register_cares_wrap() {} void _register_config() {} void _register_contextify() {} +void _register_domain() {} void _register_fs() {} void _register_fs_event_wrap() {} void _register_http2() {} diff --git a/test/parallel/test-cluster-cwd.js b/test/parallel/test-cluster-cwd.js new file mode 100644 index 00000000000..ce3fdca51e9 --- /dev/null +++ b/test/parallel/test-cluster-cwd.js @@ -0,0 +1,22 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const cluster = require('cluster'); + +if (cluster.isMaster) { + common.refreshTmpDir(); + + assert.strictEqual(cluster.settings.cwd, undefined); + cluster.fork().on('message', common.mustCall((msg) => { + assert.strictEqual(msg, process.cwd()); + })); + + cluster.setupMaster({ cwd: common.tmpDir }); + assert.strictEqual(cluster.settings.cwd, common.tmpDir); + cluster.fork().on('message', common.mustCall((msg) => { + assert.strictEqual(msg, common.tmpDir); + })); +} else { + process.send(process.cwd()); + process.disconnect(); +} diff --git a/test/parallel/test-fs-write.js b/test/parallel/test-fs-write.js index cf8395727ed..385ed60a20f 100644 --- a/test/parallel/test-fs-write.js +++ b/test/parallel/test-fs-write.js @@ -35,6 +35,8 @@ if (!common.isChakraEngine) { /* eslint-disable no-undef */ common.allowGlobals(externalizeString, isOneByteString, x); + common.refreshTmpDir(); + { const expected = 'ümlaut eins'; // Must be a unique string. externalizeString(expected); @@ -76,8 +78,6 @@ if (!common.isChakraEngine) { } } -common.refreshTmpDir(); - fs.open(fn, 'w', 0o644, common.mustCall(function(err, fd) { assert.ifError(err); @@ -93,10 +93,10 @@ fs.open(fn, 'w', 0o644, common.mustCall(function(err, fd) { const written = common.mustCall(function(err, written) { assert.ifError(err); assert.strictEqual(0, written); + fs.write(fd, expected, 0, 'utf8', done); }); fs.write(fd, '', 0, 'utf8', written); - fs.write(fd, expected, 0, 'utf8', done); })); const args = constants.O_CREAT | constants.O_WRONLY | constants.O_TRUNC; @@ -115,10 +115,10 @@ fs.open(fn2, args, 0o644, common.mustCall((err, fd) => { const written = common.mustCall(function(err, written) { assert.ifError(err); assert.strictEqual(0, written); + fs.write(fd, expected, 0, 'utf8', done); }); fs.write(fd, '', 0, 'utf8', written); - fs.write(fd, expected, 0, 'utf8', done); })); fs.open(fn3, 'w', 0o644, common.mustCall(function(err, fd) { diff --git a/test/parallel/test-internal-errors.js b/test/parallel/test-internal-errors.js index e8904bba0ec..67c1cd1d2f0 100644 --- a/test/parallel/test-internal-errors.js +++ b/test/parallel/test-internal-errors.js @@ -351,10 +351,20 @@ assert.strictEqual( } { - const error = new errors.Error('ERR_INVALID_ARG_VALUE', 'foo', 'bar'); + const error = new errors.Error('ERR_INVALID_ARG_VALUE', 'foo', '\u0000bar'); assert.strictEqual( error.message, - 'The value "bar" is invalid for argument "foo"' + 'The argument \'foo\' is invalid. Received \'\\u0000bar\'' + ); +} + +{ + const error = new errors.Error( + 'ERR_INVALID_ARG_VALUE', + 'foo', { a: 1 }, 'must have property \'b\''); + assert.strictEqual( + error.message, + 'The argument \'foo\' must have property \'b\'. Received { a: 1 }' ); } diff --git a/test/parallel/test-module-loading-error.js b/test/parallel/test-module-loading-error.js index f1c457af2b2..818a35eb582 100644 --- a/test/parallel/test-module-loading-error.js +++ b/test/parallel/test-module-loading-error.js @@ -59,16 +59,22 @@ assert.throws( } ); -common.expectsError( - require, - { - code: 'ERR_ASSERTION', - message: /^missing path$/ - }); +const re = /^The "id" argument must be of type string\. Received type \w+$/; +[1, false, null, undefined, {}].forEach((value) => { + common.expectsError( + () => { require(value); }, + { + type: TypeError, + code: 'ERR_INVALID_ARG_TYPE', + message: re + }); +}); + common.expectsError( - () => { require({}); }, + () => { require(''); }, { - code: 'ERR_ASSERTION', - message: /^path must be a string$/ + type: Error, + code: 'ERR_INVALID_ARG_VALUE', + message: 'The argument \'id\' must be a non-empty string. Received \'\'' }); diff --git a/test/parallel/test-require-resolve.js b/test/parallel/test-require-resolve.js index 4fbf697faf5..2916f3709e3 100644 --- a/test/parallel/test-require-resolve.js +++ b/test/parallel/test-require-resolve.js @@ -20,7 +20,7 @@ // USE OR OTHER DEALINGS IN THE SOFTWARE. 'use strict'; -require('../common'); +const common = require('../common'); const fixtures = require('../common/fixtures'); const assert = require('assert'); @@ -38,3 +38,20 @@ assert.strictEqual('path', require.resolve('path')); // Test configurable resolve() paths. require(fixtures.path('require-resolve.js')); require(fixtures.path('resolve-paths', 'default', 'verify-paths.js')); + +const re = /^The "request" argument must be of type string\. Received type \w+$/; +[1, false, null, undefined, {}].forEach((value) => { + common.expectsError( + () => { require.resolve(value); }, + { + code: 'ERR_INVALID_ARG_TYPE', + message: re + }); + + common.expectsError( + () => { require.resolve.paths(value); }, + { + code: 'ERR_INVALID_ARG_TYPE', + message: re + }); +}); diff --git a/test/parallel/test-stream-readable-no-unneeded-readable.js b/test/parallel/test-stream-readable-no-unneeded-readable.js new file mode 100644 index 00000000000..bd3e06e5f7d --- /dev/null +++ b/test/parallel/test-stream-readable-no-unneeded-readable.js @@ -0,0 +1,39 @@ +'use strict'; +const common = require('../common'); +const { Readable, PassThrough } = require('stream'); + +const source = new Readable({ + read: () => {} +}); + +source.push('foo'); +source.push('bar'); +source.push(null); + +const pt = source.pipe(new PassThrough()); + +const wrapper = new Readable({ + read: () => { + let data = pt.read(); + + if (data) { + wrapper.push(data); + return; + } + + pt.once('readable', function() { + data = pt.read(); + if (data) { + wrapper.push(data); + } + // else the end event should fire + }); + } +}); + +pt.once('end', function() { + wrapper.push(null); +}); + +wrapper.resume(); +wrapper.once('end', common.mustCall()); diff --git a/test/parallel/test-stream-transform-split-highwatermark.js b/test/parallel/test-stream-transform-split-highwatermark.js index af2558ec6de..f931d4f6ceb 100644 --- a/test/parallel/test-stream-transform-split-highwatermark.js +++ b/test/parallel/test-stream-transform-split-highwatermark.js @@ -1,5 +1,5 @@ 'use strict'; -require('../common'); +const common = require('../common'); const assert = require('assert'); const { Transform, Readable, Writable } = require('stream'); @@ -54,14 +54,33 @@ testTransform(0, 0, { writableHighWaterMark: 777, }); -// test undefined, null, NaN -[undefined, null, NaN].forEach((v) => { +// test undefined, null +[undefined, null].forEach((v) => { testTransform(DEFAULT, DEFAULT, { readableHighWaterMark: v }); testTransform(DEFAULT, DEFAULT, { writableHighWaterMark: v }); testTransform(666, DEFAULT, { highWaterMark: v, readableHighWaterMark: 666 }); testTransform(DEFAULT, 777, { highWaterMark: v, writableHighWaterMark: 777 }); }); +// test NaN +{ + common.expectsError(() => { + new Transform({ readableHighWaterMark: NaN }); + }, { + type: TypeError, + code: 'ERR_INVALID_OPT_VALUE', + message: 'The value "NaN" is invalid for option "readableHighWaterMark"' + }); + + common.expectsError(() => { + new Transform({ writableHighWaterMark: NaN }); + }, { + type: TypeError, + code: 'ERR_INVALID_OPT_VALUE', + message: 'The value "NaN" is invalid for option "writableHighWaterMark"' + }); +} + // test non Duplex streams ignore the options { const r = new Readable({ readableHighWaterMark: 666 }); diff --git a/test/parallel/test-streams-highwatermark.js b/test/parallel/test-streams-highwatermark.js index aca2415bd8e..377fe08e90d 100644 --- a/test/parallel/test-streams-highwatermark.js +++ b/test/parallel/test-streams-highwatermark.js @@ -1,8 +1,9 @@ 'use strict'; -require('../common'); +const common = require('../common'); // This test ensures that the stream implementation correctly handles values -// for highWaterMark which exceed the range of signed 32 bit integers. +// for highWaterMark which exceed the range of signed 32 bit integers and +// rejects invalid values. const assert = require('assert'); const stream = require('stream'); @@ -16,3 +17,15 @@ assert.strictEqual(readable._readableState.highWaterMark, ovfl); const writable = stream.Writable({ highWaterMark: ovfl }); assert.strictEqual(writable._writableState.highWaterMark, ovfl); + +for (const invalidHwm of [true, false, '5', {}, -5, NaN]) { + for (const type of [stream.Readable, stream.Writable]) { + common.expectsError(() => { + type({ highWaterMark: invalidHwm }); + }, { + type: TypeError, + code: 'ERR_INVALID_OPT_VALUE', + message: `The value "${invalidHwm}" is invalid for option "highWaterMark"` + }); + } +} diff --git a/test/sequential/test-benchmark-http.js b/test/sequential/test-benchmark-http.js index edb61a0601c..e23a4a1753b 100644 --- a/test/sequential/test-benchmark-http.js +++ b/test/sequential/test-benchmark-http.js @@ -25,4 +25,7 @@ runBenchmark('http', 'res=normal', 'type=asc' ], - { NODEJS_BENCHMARK_ZERO_ALLOWED: 1 }); + { + NODEJS_BENCHMARK_ZERO_ALLOWED: 1, + duration: 0 + }); diff --git a/test/sequential/test-module-loading.js b/test/sequential/test-module-loading.js index d790850951b..f0fa933a8ba 100644 --- a/test/sequential/test-module-loading.js +++ b/test/sequential/test-module-loading.js @@ -297,17 +297,6 @@ try { } -// require() must take string, and must be truthy -assert.throws(function() { - console.error('require non-string'); - require({ foo: 'bar' }); -}, /path must be a string/); - -assert.throws(function() { - console.error('require empty string'); - require(''); -}, /missing path/); - process.on('exit', function() { assert.ok(a.A instanceof Function); assert.strictEqual(a.A(), 'A done'); diff --git a/tools/doc/type-parser.js b/tools/doc/type-parser.js index 3bcdd817a2f..4ef0a6d04cb 100644 --- a/tools/doc/type-parser.js +++ b/tools/doc/type-parser.js @@ -43,9 +43,14 @@ const typeMap = { 'http.Server': 'http.html#http_class_http_server', 'http.ServerResponse': 'http.html#http_class_http_serverresponse', + 'HTTP2 Headers Object': 'http2.html#http2_headers_object', + 'HTTP2 Settings Object': 'http2.html#http2_settings_object', + 'Handle': 'net.html#net_server_listen_handle_backlog_callback', 'net.Socket': 'net.html#net_class_net_socket', + 'ServerHttp2Stream': 'http2.html#http2_class_serverhttp2stream', + 'Stream': 'stream.html#stream_stream', 'stream.Readable': 'stream.html#stream_class_stream_readable', 'stream.Writable': 'stream.html#stream_class_stream_writable',