From ee9e2a2eb6143fa7b4b1454f7aed009a8703d4d7 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Thu, 15 Apr 2021 15:05:42 +0200 Subject: [PATCH] lib: revert primordials in a hot path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Evidence has shown that use of primordials have sometimes an impact of performance. This commit reverts the changes who are most likely to be responsible for performance regression in the HTTP response path. PR-URL: https://github.com/nodejs/node/pull/38248 Reviewed-By: Benjamin Gruenbaum Reviewed-By: Matteo Collina Reviewed-By: Michaƫl Zasso Reviewed-By: Rich Trott --- lib/_http_common.js | 6 +- lib/_http_incoming.js | 6 +- lib/_http_outgoing.js | 23 +++---- lib/_http_server.js | 88 ++++++++++++------------- lib/events.js | 24 +++---- lib/internal/async_hooks.js | 20 +++--- lib/internal/per_context/primordials.js | 4 ++ lib/internal/streams/destroy.js | 7 +- lib/internal/streams/end-of-stream.js | 27 ++++---- lib/internal/streams/lazy_transform.js | 3 +- lib/internal/streams/legacy.js | 6 +- lib/internal/streams/passthrough.js | 3 +- lib/internal/streams/pipeline.js | 31 ++++----- lib/internal/streams/readable.js | 43 ++++++------ lib/internal/streams/transform.js | 9 +-- lib/internal/streams/writable.js | 39 ++++++----- lib/internal/timers.js | 2 +- lib/internal/util.js | 17 +++-- lib/internal/util/debuglog.js | 19 ++++-- lib/net.js | 27 ++++---- 20 files changed, 182 insertions(+), 222 deletions(-) diff --git a/lib/_http_common.js b/lib/_http_common.js index 229353ec735288..e3e732a8a180c6 100644 --- a/lib/_http_common.js +++ b/lib/_http_common.js @@ -22,11 +22,9 @@ 'use strict'; const { - ArrayPrototypePushApply, MathMin, Symbol, RegExpPrototypeTest, - TypedArrayPrototypeSlice, } = primordials; const { setImmediate } = require('timers'); @@ -66,7 +64,7 @@ function parserOnHeaders(headers, url) { // Once we exceeded headers limit - stop collecting them if (this.maxHeaderPairs <= 0 || this._headers.length < this.maxHeaderPairs) { - ArrayPrototypePushApply(this._headers, headers); + this._headers.push(...headers); } this._url += url; } @@ -138,7 +136,7 @@ function parserOnBody(b, start, len) { // Pretend this was the result of a stream._read call. if (len > 0 && !stream._dumped) { - const slice = TypedArrayPrototypeSlice(b, start, start + len); + const slice = b.slice(start, start + len); const ret = stream.push(slice); if (!ret) readStop(this.socket); diff --git a/lib/_http_incoming.js b/lib/_http_incoming.js index d09683c9a8f1b1..e0f1354e6c969c 100644 --- a/lib/_http_incoming.js +++ b/lib/_http_incoming.js @@ -22,8 +22,6 @@ 'use strict'; const { - ArrayPrototypePush, - FunctionPrototypeCall, ObjectDefineProperty, ObjectSetPrototypeOf, StringPrototypeCharCodeAt, @@ -59,7 +57,7 @@ function IncomingMessage(socket) { }; } - FunctionPrototypeCall(Readable, this, streamOptions); + Readable.call(this, streamOptions); this._readableState.readingMore = true; @@ -350,7 +348,7 @@ function _addHeaderLine(field, value, dest) { } else if (flag === 1) { // Array header -- only Set-Cookie at the moment if (dest['set-cookie'] !== undefined) { - ArrayPrototypePush(dest['set-cookie'], value); + dest['set-cookie'].push(value); } else { dest['set-cookie'] = [value]; } diff --git a/lib/_http_outgoing.js b/lib/_http_outgoing.js index b627608a527201..4d3b58cc84d00c 100644 --- a/lib/_http_outgoing.js +++ b/lib/_http_outgoing.js @@ -24,13 +24,7 @@ const { Array, ArrayIsArray, - ArrayPrototypeForEach, ArrayPrototypeJoin, - ArrayPrototypePush, - ArrayPrototypeUnshift, - FunctionPrototype, - FunctionPrototypeBind, - FunctionPrototypeCall, MathFloor, NumberPrototypeToString, ObjectCreate, @@ -88,7 +82,7 @@ const { CRLF } = common; const kCorked = Symbol('corked'); -const nop = FunctionPrototype; +const nop = () => {}; const RE_CONN_CLOSE = /(?:^|\W)close(?:$|\W)/i; const RE_TE_CHUNKED = common.chunkExpression; @@ -101,7 +95,7 @@ function isCookieField(s) { } function OutgoingMessage() { - FunctionPrototypeCall(Stream, this); + Stream.call(this); // Queue that holds all currently pending data, until the response will be // assigned to the socket (until it will its turn in the HTTP pipeline). @@ -331,7 +325,7 @@ OutgoingMessage.prototype._send = function _send(data, encoding, callback) { data = this._header + data; } else { const header = this._header; - ArrayPrototypeUnshift(this.outputData, { + this.outputData.unshift({ data: header, encoding: 'latin1', callback: null @@ -368,7 +362,7 @@ function _writeRaw(data, encoding, callback) { return conn.write(data, encoding, callback); } // Buffer, as long as we're not destroyed. - ArrayPrototypePush(this.outputData, { data, encoding, callback }); + this.outputData.push({ data, encoding, callback }); this.outputSize += data.length; this._onPendingData(data.length); return this.outputSize < HIGH_WATER_MARK; @@ -397,9 +391,10 @@ function _storeHeader(firstLine, headers) { } } else if (ArrayIsArray(headers)) { if (headers.length && ArrayIsArray(headers[0])) { - ArrayPrototypeForEach(headers, (entry) => - processHeader(this, state, entry[0], entry[1], true) - ); + for (let i = 0; i < headers.length; i++) { + const entry = headers[i]; + processHeader(this, state, entry[0], entry[1], true); + } } else { if (headers.length % 2 !== 0) { throw new ERR_INVALID_ARG_VALUE('headers', headers); @@ -877,7 +872,7 @@ OutgoingMessage.prototype.end = function end(chunk, encoding, callback) { if (typeof callback === 'function') this.once('finish', callback); - const finish = FunctionPrototypeBind(onFinish, undefined, this); + const finish = onFinish.bind(undefined, this); if (this._hasBody && this.chunkedEncoding) { this._send('0\r\n' + this._trailer + '\r\n', 'latin1', finish); diff --git a/lib/_http_server.js b/lib/_http_server.js index 8ada9e8cfe987a..97df58a007daba 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -23,20 +23,12 @@ const { ArrayIsArray, - ArrayPrototypeForEach, - ArrayPrototypePush, - ArrayPrototypeShift, Error, - FunctionPrototype, - FunctionPrototypeBind, - FunctionPrototypeCall, ObjectKeys, ObjectSetPrototypeOf, - ReflectApply, RegExpPrototypeTest, Symbol, SymbolFor, - TypedArrayPrototypeSlice, } = primordials; const net = require('net'); @@ -185,7 +177,7 @@ class HTTPServerAsyncResource { } function ServerResponse(req) { - FunctionPrototypeCall(OutgoingMessage, this); + OutgoingMessage.call(this); if (req.method === 'HEAD') this._hasBody = false; @@ -212,7 +204,7 @@ ObjectSetPrototypeOf(ServerResponse, OutgoingMessage); ServerResponse.prototype._finish = function _finish() { DTRACE_HTTP_SERVER_RESPONSE(this.socket); emitStatistics(this[kServerResponseStatistics]); - FunctionPrototypeCall(OutgoingMessage.prototype._finish, this); + OutgoingMessage.prototype._finish.call(this); }; @@ -386,7 +378,7 @@ function Server(options, requestListener) { validateBoolean(insecureHTTPParser, 'options.insecureHTTPParser'); this.insecureHTTPParser = insecureHTTPParser; - FunctionPrototypeCall(net.Server, this, { allowHalfOpen: true }); + net.Server.call(this, { allowHalfOpen: true }); if (requestListener) { this.on('request', requestListener); @@ -422,8 +414,10 @@ Server.prototype[EE.captureRejectionSymbol] = function(err, event, ...args) { const { 1: res } = args; if (!res.headersSent && !res.writableEnded) { // Don't leak headers. - ArrayPrototypeForEach(res.getHeaderNames(), - (name) => res.removeHeader(name)); + const names = res.getHeaderNames(); + for (let i = 0; i < names.length; i++) { + res.removeHeader(names[i]); + } res.statusCode = 500; res.end(STATUS_CODES[500]); } else { @@ -431,8 +425,8 @@ Server.prototype[EE.captureRejectionSymbol] = function(err, event, ...args) { } break; default: - ReflectApply(net.Server.prototype[SymbolFor('nodejs.rejection')], - this, arguments); + net.Server.prototype[SymbolFor('nodejs.rejection')] + .apply(this, arguments); } }; @@ -493,20 +487,20 @@ function connectionListenerInternal(server, socket) { outgoingData: 0, keepAliveTimeoutSet: false }; - state.onData = FunctionPrototypeBind(socketOnData, undefined, - server, socket, parser, state); - state.onEnd = FunctionPrototypeBind(socketOnEnd, undefined, - server, socket, parser, state); - state.onClose = FunctionPrototypeBind(socketOnClose, undefined, - socket, state); - state.onDrain = FunctionPrototypeBind(socketOnDrain, undefined, - socket, state); + state.onData = socketOnData.bind(undefined, + server, socket, parser, state); + state.onEnd = socketOnEnd.bind(undefined, + server, socket, parser, state); + state.onClose = socketOnClose.bind(undefined, + socket, state); + state.onDrain = socketOnDrain.bind(undefined, + socket, state); socket.on('data', state.onData); socket.on('error', socketOnError); socket.on('end', state.onEnd); socket.on('close', state.onClose); socket.on('drain', state.onDrain); - parser.onIncoming = FunctionPrototypeBind(parserOnIncoming, undefined, + parser.onIncoming = parserOnIncoming.bind(undefined, server, socket, state); // We are consuming socket, so it won't get any actual data @@ -527,18 +521,18 @@ function connectionListenerInternal(server, socket) { parser.consume(socket._handle); } parser[kOnExecute] = - FunctionPrototypeBind(onParserExecute, undefined, - server, socket, parser, state); + onParserExecute.bind(undefined, + server, socket, parser, state); parser[kOnTimeout] = - FunctionPrototypeBind(onParserTimeout, undefined, - server, socket); + onParserTimeout.bind(undefined, + server, socket); // When receiving new requests on the same socket (pipelining or keep alive) // make sure the requestTimeout is active. parser[kOnMessageBegin] = - FunctionPrototypeBind(setRequestTimeout, undefined, - server, socket); + setRequestTimeout.bind(undefined, + server, socket); // This protects from DOS attack where an attacker establish the connection // without sending any data on applications where server.timeout is left to @@ -594,7 +588,7 @@ function socketOnClose(socket, state) { function abortIncoming(incoming) { while (incoming.length) { - const req = ArrayPrototypeShift(incoming); + const req = incoming.shift(); req.destroy(connResetException('aborted')); } // Abort socket._httpMessage ? @@ -606,7 +600,7 @@ function socketOnEnd(server, socket, parser, state) { if (ret instanceof Error) { debug('parse error'); // socketOnError has additional logic and will call socket.destroy(err). - FunctionPrototypeCall(socketOnError, socket, ret); + socketOnError.call(socket, ret); } else if (!server.httpAllowHalfOpen) { socket.end(); } else if (state.outgoing.length) { @@ -629,7 +623,7 @@ function socketOnData(server, socket, parser, state, d) { function onRequestTimeout(socket) { socket[kRequestTimeout] = undefined; // socketOnError has additional logic and will call socket.destroy(err). - ReflectApply(socketOnError, socket, [new ERR_HTTP_REQUEST_TIMEOUT()]); + socketOnError.call(socket, new ERR_HTTP_REQUEST_TIMEOUT()); } function onParserExecute(server, socket, parser, state, ret) { @@ -649,7 +643,7 @@ function onParserTimeout(server, socket) { socket.destroy(); } -const noop = FunctionPrototype; +const noop = () => {}; const badRequestResponse = Buffer.from( `HTTP/1.1 400 ${STATUS_CODES[400]}${CRLF}` + `Connection: close${CRLF}${CRLF}`, 'ascii' @@ -696,7 +690,7 @@ function onParserExecuteCommon(server, socket, parser, state, ret, d) { prepareError(ret, parser, d); ret.rawPacket = d || parser.getCurrentBuffer(); debug('parse error', ret); - FunctionPrototypeCall(socketOnError, socket, ret); + socketOnError.call(socket, ret); } else if (parser.incoming && parser.incoming.upgrade) { // Upgrade or CONNECT const req = parser.incoming; @@ -719,7 +713,7 @@ function onParserExecuteCommon(server, socket, parser, state, ret, d) { const eventName = req.method === 'CONNECT' ? 'connect' : 'upgrade'; if (eventName === 'upgrade' || server.listenerCount(eventName) > 0) { debug('SERVER have listener for %s', eventName); - const bodyHead = TypedArrayPrototypeSlice(d, ret, d.length); + const bodyHead = d.slice(ret, d.length); socket.readableFlowing = null; @@ -738,7 +732,7 @@ function onParserExecuteCommon(server, socket, parser, state, ret, d) { // When receiving new requests on the same socket (pipelining or keep alive) // make sure the requestTimeout is active. parser[kOnMessageBegin] = - FunctionPrototypeBind(setRequestTimeout, undefined, server, socket); + setRequestTimeout.bind(undefined, server, socket); } if (socket._paused && socket.parser) { @@ -802,7 +796,7 @@ function resOnFinish(req, res, socket, state, server) { // array will be empty. assert(state.incoming.length === 0 || state.incoming[0] === req); - ArrayPrototypeShift(state.incoming); + state.incoming.shift(); // If the user never called req.read(), and didn't pipe() or // .resume() or .on('data'), then we call req._dump() so that the @@ -835,7 +829,7 @@ function resOnFinish(req, res, socket, state, server) { } } else { // Start sending the next message - const m = ArrayPrototypeShift(state.outgoing); + const m = state.outgoing.shift(); if (m) { m.assignSocket(socket); } @@ -861,7 +855,7 @@ function parserOnIncoming(server, socket, state, req, keepAlive) { return 2; } - ArrayPrototypePush(state.incoming, req); + state.incoming.push(req); // If the writable end isn't consuming, then stop reading // so that we don't become overwhelmed by a flood of @@ -879,8 +873,8 @@ function parserOnIncoming(server, socket, state, req, keepAlive) { const res = new server[kServerResponse](req); res._keepAliveTimeout = server.keepAliveTimeout; - res._onPendingData = FunctionPrototypeBind(updateOutgoingData, undefined, - socket, state); + res._onPendingData = updateOutgoingData.bind(undefined, + socket, state); res.shouldKeepAlive = keepAlive; DTRACE_HTTP_SERVER_REQUEST(req, socket); @@ -896,7 +890,7 @@ function parserOnIncoming(server, socket, state, req, keepAlive) { if (socket._httpMessage) { // There are already pending outgoing res, append. - ArrayPrototypePush(state.outgoing, res); + state.outgoing.push(res); } else { res.assignSocket(socket); } @@ -904,8 +898,8 @@ function parserOnIncoming(server, socket, state, req, keepAlive) { // When we're finished writing the response, check if this is the last // response, if so destroy the socket. res.on('finish', - FunctionPrototypeBind(resOnFinish, undefined, - req, res, socket, state, server)); + resOnFinish.bind(undefined, + req, res, socket, state, server)); if (req.headers.expect !== undefined && (req.httpVersionMajor === 1 && req.httpVersionMinor === 1)) { @@ -977,8 +971,8 @@ function unconsume(parser, socket) { function generateSocketListenerWrapper(originalFnName) { return function socketListenerWrap(ev, fn) { - const res = ReflectApply(net.Socket.prototype[originalFnName], this, - [ev, fn]); + const res = net.Socket.prototype[originalFnName].call(this, + ev, fn); if (!this.parser) { this.on = net.Socket.prototype.on; this.addListener = net.Socket.prototype.addListener; diff --git a/lib/events.js b/lib/events.js index 15c69c30271aa6..cc29b3358e0ce2 100644 --- a/lib/events.js +++ b/lib/events.js @@ -22,13 +22,10 @@ 'use strict'; const { - ArrayPrototypeForEach, - ArrayPrototypePush, ArrayPrototypeSlice, Boolean, Error, ErrorCaptureStackTrace, - FunctionPrototypeCall, MathMin, NumberIsNaN, ObjectCreate, @@ -39,7 +36,6 @@ const { Promise, PromiseReject, PromiseResolve, - ReflectApply, ReflectOwnKeys, String, Symbol, @@ -76,7 +72,7 @@ const kMaxEventTargetListenersWarned = Symbol('events.maxEventTargetListenersWarned'); function EventEmitter(opts) { - FunctionPrototypeCall(EventEmitter.init, this, opts); + EventEmitter.init.call(this, opts); } module.exports = EventEmitter; module.exports.once = once; @@ -165,8 +161,8 @@ EventEmitter.setMaxListeners = if (isEventTarget === undefined) isEventTarget = require('internal/event_target').isEventTarget; - // Performance for forEach is now comparable with regular for-loop - ArrayPrototypeForEach(eventTargets, (target) => { + for (let i = 0; i < eventTargets.length; i++) { + const target = eventTargets[i]; if (isEventTarget(target)) { target[kMaxEventTargetListeners] = n; target[kMaxEventTargetListenersWarned] = false; @@ -178,7 +174,7 @@ EventEmitter.setMaxListeners = ['EventEmitter', 'EventTarget'], target); } - }); + } } }; @@ -217,7 +213,7 @@ function addCatch(that, promise, type, args) { const then = promise.then; if (typeof then === 'function') { - FunctionPrototypeCall(then, promise, undefined, function(err) { + then.call(promise, undefined, function(err) { // The callback is called with nextTick to avoid a follow-up // rejection from this promise. process.nextTick(emitUnhandledRejectionOrErr, that, err, type, args); @@ -366,7 +362,7 @@ EventEmitter.prototype.emit = function emit(type, ...args) { return false; if (typeof handler === 'function') { - const result = ReflectApply(handler, this, args); + const result = handler.apply(this, args); // We check if result is undefined first because that // is the most common case so we do not pay any perf @@ -378,7 +374,7 @@ EventEmitter.prototype.emit = function emit(type, ...args) { const len = handler.length; const listeners = arrayClone(handler); for (let i = 0; i < len; ++i) { - const result = ReflectApply(listeners[i], this, args); + const result = listeners[i].apply(this, args); // We check if result is undefined first because that // is the most common case so we do not pay any perf @@ -690,7 +686,7 @@ function getEventListeners(emitterOrTarget, type) { while (handler?.listener !== undefined) { const listener = handler.listener?.deref ? handler.listener.deref() : handler.listener; - ArrayPrototypePush(listeners, listener); + listeners.push(listener); handler = handler.next; } return listeners; @@ -807,7 +803,7 @@ function on(emitter, event, options) { // Wait until an event happens return new Promise(function(resolve, reject) { - ArrayPrototypePush(unconsumedPromises, { resolve, reject }); + unconsumedPromises.push({ resolve, reject }); }); }, @@ -871,7 +867,7 @@ function on(emitter, event, options) { if (promise) { promise.resolve(createIterResult(args, false)); } else { - ArrayPrototypePush(unconsumedEvents, args); + unconsumedEvents.push(args); } } diff --git a/lib/internal/async_hooks.js b/lib/internal/async_hooks.js index 69ca8d6db987ed..b8955f644d15d7 100644 --- a/lib/internal/async_hooks.js +++ b/lib/internal/async_hooks.js @@ -1,15 +1,11 @@ 'use strict'; const { - ArrayPrototypePop, ArrayPrototypeSlice, - ArrayPrototypeUnshift, ErrorCaptureStackTrace, - FunctionPrototypeBind, ObjectPrototypeHasOwnProperty, ObjectDefineProperty, Promise, - ReflectApply, Symbol, } = primordials; @@ -129,16 +125,16 @@ function callbackTrampoline(asyncId, resource, cb, ...args) { let result; if (asyncId === 0 && typeof domain_cb === 'function') { - ArrayPrototypeUnshift(args, cb); - result = ReflectApply(domain_cb, this, args); + args.unshift(cb); + result = domain_cb.apply(this, args); } else { - result = ReflectApply(cb, this, args); + result = cb.apply(this, args); } if (asyncId !== 0 && hasHooks(kAfter)) emitAfterNative(asyncId); - ArrayPrototypePop(execution_async_resources); + execution_async_resources.pop(); return result; } @@ -256,7 +252,7 @@ function emitHook(symbol, asyncId) { } function emitHookFactory(symbol, name) { - const fn = FunctionPrototypeBind(emitHook, undefined, symbol); + const fn = emitHook.bind(undefined, symbol); // Set the name property of the function as it looks good in the stack trace. ObjectDefineProperty(fn, 'name', { @@ -429,14 +425,14 @@ function clearDefaultTriggerAsyncId() { function defaultTriggerAsyncIdScope(triggerAsyncId, block, ...args) { if (triggerAsyncId === undefined) - return ReflectApply(block, null, args); + return block.apply(null, args); // CHECK(NumberIsSafeInteger(triggerAsyncId)) // CHECK(triggerAsyncId > 0) const oldDefaultTriggerAsyncId = async_id_fields[kDefaultTriggerAsyncId]; async_id_fields[kDefaultTriggerAsyncId] = triggerAsyncId; try { - return ReflectApply(block, null, args); + return block.apply(null, args); } finally { async_id_fields[kDefaultTriggerAsyncId] = oldDefaultTriggerAsyncId; } @@ -533,7 +529,7 @@ function popAsyncContext(asyncId) { const offset = stackLength - 1; async_id_fields[kExecutionAsyncId] = async_wrap.async_ids_stack[2 * offset]; async_id_fields[kTriggerAsyncId] = async_wrap.async_ids_stack[2 * offset + 1]; - ArrayPrototypePop(execution_async_resources); + execution_async_resources.pop(); async_hook_fields[kStackLength] = offset; return offset > 0; } diff --git a/lib/internal/per_context/primordials.js b/lib/internal/per_context/primordials.js index 457cd498c15903..f6832030381b6b 100644 --- a/lib/internal/per_context/primordials.js +++ b/lib/internal/per_context/primordials.js @@ -6,6 +6,10 @@ // so that Node.js's builtin modules do not need to later look these up from // the global proxy, which can be mutated by users. +// Use of primordials have sometimes a dramatic impact on performance, please +// benchmark all changes made in performance-sensitive areas of the codebase. +// See: https://github.com/nodejs/node/pull/38248 + const { defineProperty: ReflectDefineProperty, getOwnPropertyDescriptor: ReflectGetOwnPropertyDescriptor, diff --git a/lib/internal/streams/destroy.js b/lib/internal/streams/destroy.js index 467275c2edac35..a2892c67a0fcfa 100644 --- a/lib/internal/streams/destroy.js +++ b/lib/internal/streams/destroy.js @@ -7,7 +7,6 @@ const { }, } = require('internal/errors'); const { - FunctionPrototypeCall, Symbol, } = primordials; @@ -99,8 +98,7 @@ function _destroy(self, err, cb) { try { const then = result.then; if (typeof then === 'function') { - FunctionPrototypeCall( - then, + then.call( result, function() { if (called) @@ -318,8 +316,7 @@ function constructNT(stream) { try { const then = result.then; if (typeof then === 'function') { - FunctionPrototypeCall( - then, + then.call( result, function() { // If the callback was invoked, do nothing further. diff --git a/lib/internal/streams/end-of-stream.js b/lib/internal/streams/end-of-stream.js index da48831a405dbe..318ab4c2e6a8b7 100644 --- a/lib/internal/streams/end-of-stream.js +++ b/lib/internal/streams/end-of-stream.js @@ -3,11 +3,6 @@ 'use strict'; -const { - FunctionPrototype, - FunctionPrototypeCall, - ReflectApply, -} = primordials; const { AbortError, codes, @@ -55,7 +50,7 @@ function isWritableFinished(stream) { return wState.finished || (wState.ended && wState.length === 0); } -const nop = FunctionPrototype; +const nop = () => {}; function isReadableEnded(stream) { if (stream.readableEnded) return true; @@ -113,7 +108,7 @@ function eos(stream, options, callback) { if (stream.destroyed) willEmitClose = false; if (willEmitClose && (!stream.readable || readable)) return; - if (!readable || readableEnded) FunctionPrototypeCall(callback, stream); + if (!readable || readableEnded) callback.call(stream); }; let readableEnded = stream.readableEnded || @@ -126,25 +121,25 @@ function eos(stream, options, callback) { if (stream.destroyed) willEmitClose = false; if (willEmitClose && (!stream.writable || writable)) return; - if (!writable || writableFinished) FunctionPrototypeCall(callback, stream); + if (!writable || writableFinished) callback.call(stream); }; const onerror = (err) => { - FunctionPrototypeCall(callback, stream, err); + callback.call(stream, err); }; const onclose = () => { if (readable && !readableEnded) { if (!isReadableEnded(stream)) - return FunctionPrototypeCall(callback, stream, - new ERR_STREAM_PREMATURE_CLOSE()); + return callback.call(stream, + new ERR_STREAM_PREMATURE_CLOSE()); } if (writable && !writableFinished) { if (!isWritableFinished(stream)) - return FunctionPrototypeCall(callback, stream, - new ERR_STREAM_PREMATURE_CLOSE()); + return callback.call(stream, + new ERR_STREAM_PREMATURE_CLOSE()); } - FunctionPrototypeCall(callback, stream); + callback.call(stream); }; const onrequest = () => { @@ -218,7 +213,7 @@ function eos(stream, options, callback) { // Keep it because cleanup removes it. const endCallback = callback; cleanup(); - FunctionPrototypeCall(endCallback, stream, new AbortError()); + endCallback.call(stream, new AbortError()); }; if (options.signal.aborted) { process.nextTick(abort); @@ -226,7 +221,7 @@ function eos(stream, options, callback) { const originalCallback = callback; callback = once((...args) => { options.signal.removeEventListener('abort', abort); - ReflectApply(originalCallback, stream, args); + originalCallback.apply(stream, args); }); options.signal.addEventListener('abort', abort); } diff --git a/lib/internal/streams/lazy_transform.js b/lib/internal/streams/lazy_transform.js index ad072e3474b3e1..555e6430e33588 100644 --- a/lib/internal/streams/lazy_transform.js +++ b/lib/internal/streams/lazy_transform.js @@ -4,7 +4,6 @@ 'use strict'; const { - FunctionPrototypeCall, ObjectDefineProperties, ObjectDefineProperty, ObjectSetPrototypeOf, @@ -26,7 +25,7 @@ ObjectSetPrototypeOf(LazyTransform, stream.Transform); function makeGetter(name) { return function() { - FunctionPrototypeCall(stream.Transform, this, this._options); + stream.Transform.call(this, this._options); this._writableState.decodeStrings = false; if (!this._options || !this._options.defaultEncoding) { diff --git a/lib/internal/streams/legacy.js b/lib/internal/streams/legacy.js index d08df00259033b..0a0d0571c46378 100644 --- a/lib/internal/streams/legacy.js +++ b/lib/internal/streams/legacy.js @@ -2,15 +2,13 @@ const { ArrayIsArray, - ArrayPrototypeUnshift, - FunctionPrototypeCall, ObjectSetPrototypeOf, } = primordials; const EE = require('events'); function Stream(opts) { - FunctionPrototypeCall(EE, this, opts); + EE.call(this, opts); } ObjectSetPrototypeOf(Stream.prototype, EE.prototype); ObjectSetPrototypeOf(Stream, EE); @@ -108,7 +106,7 @@ function prependListener(emitter, event, fn) { if (!emitter._events || !emitter._events[event]) emitter.on(event, fn); else if (ArrayIsArray(emitter._events[event])) - ArrayPrototypeUnshift(emitter._events[event], fn); + emitter._events[event].unshift(fn); else emitter._events[event] = [fn, emitter._events[event]]; } diff --git a/lib/internal/streams/passthrough.js b/lib/internal/streams/passthrough.js index acc1a148a7d7c1..d37f9caf0116b5 100644 --- a/lib/internal/streams/passthrough.js +++ b/lib/internal/streams/passthrough.js @@ -26,7 +26,6 @@ 'use strict'; const { - FunctionPrototypeCall, ObjectSetPrototypeOf, } = primordials; @@ -40,7 +39,7 @@ function PassThrough(options) { if (!(this instanceof PassThrough)) return new PassThrough(options); - FunctionPrototypeCall(Transform, this, options); + Transform.call(this, options); } PassThrough.prototype._transform = function(chunk, encoding, cb) { diff --git a/lib/internal/streams/pipeline.js b/lib/internal/streams/pipeline.js index 5215d986ef1f1c..441fcb471858bc 100644 --- a/lib/internal/streams/pipeline.js +++ b/lib/internal/streams/pipeline.js @@ -5,11 +5,6 @@ const { ArrayIsArray, - ArrayPrototypePop, - ArrayPrototypePush, - ArrayPrototypeShift, - FunctionPrototypeCall, - ReflectApply, SymbolAsyncIterator, } = primordials; @@ -84,7 +79,7 @@ function popCallback(streams) { // a single stream. Therefore optimize for the average case instead of // checking for length === 0 as well. validateCallback(streams[streams.length - 1]); - return ArrayPrototypePop(streams); + return streams.pop(); } function makeAsyncIterable(val) { @@ -103,7 +98,7 @@ async function* fromReadable(val) { Readable = require('internal/streams/readable'); } - yield* FunctionPrototypeCall(Readable.prototype[SymbolAsyncIterator], val); + yield* Readable.prototype[SymbolAsyncIterator].call(val); } async function pump(iterable, writable, finish) { @@ -160,7 +155,7 @@ function pipeline(...streams) { } while (destroys.length) { - ArrayPrototypeShift(destroys)(error); + destroys.shift()(error); } if (final) { @@ -176,7 +171,7 @@ function pipeline(...streams) { if (isStream(stream)) { finishCount++; - ArrayPrototypePush(destroys, destroyer(stream, reading, writing, finish)); + destroys.push(destroyer(stream, reading, writing, finish)); } if (i === 0) { @@ -220,14 +215,14 @@ function pipeline(...streams) { // second use. const then = ret?.then; if (typeof then === 'function') { - ReflectApply(then, ret, [ - (val) => { - value = val; - pt.end(val); - }, (err) => { - pt.destroy(err); - }, - ]); + then.call(ret, + (val) => { + value = val; + pt.end(val); + }, (err) => { + pt.destroy(err); + }, + ); } else if (isIterable(ret, true)) { finishCount++; pump(ret, pt, finish); @@ -239,7 +234,7 @@ function pipeline(...streams) { ret = pt; finishCount++; - ArrayPrototypePush(destroys, destroyer(ret, false, true, finish)); + destroys.push(destroyer(ret, false, true, finish)); } } else if (isStream(stream)) { if (isReadable(ret)) { diff --git a/lib/internal/streams/readable.js b/lib/internal/streams/readable.js index cc3f5e93fd0201..aa0f5f94886427 100644 --- a/lib/internal/streams/readable.js +++ b/lib/internal/streams/readable.js @@ -22,13 +22,7 @@ 'use strict'; const { - ArrayPrototypeForEach, ArrayPrototypeIndexOf, - ArrayPrototypePush, - ArrayPrototypeSplice, - FunctionPrototype, - FunctionPrototypeBind, - FunctionPrototypeCall, NumberIsInteger, NumberIsNaN, NumberParseInt, @@ -36,7 +30,6 @@ const { ObjectKeys, ObjectSetPrototypeOf, Promise, - ReflectApply, SafeSet, SymbolAsyncIterator, Symbol @@ -78,7 +71,7 @@ let from; ObjectSetPrototypeOf(Readable.prototype, Stream.prototype); ObjectSetPrototypeOf(Readable, Stream); -const nop = FunctionPrototype; +const nop = () => {}; const { errorOrDestroy } = destroyImpl; @@ -208,7 +201,7 @@ function Readable(options) { addAbortSignalNoValidate(options.signal, this); } - FunctionPrototypeCall(Stream, this, options); + Stream.call(this, options); destroyImpl.construct(this, () => { if (this._readableState.needReadable) { @@ -668,7 +661,7 @@ Readable.prototype.pipe = function(dest, pipeOpts) { } } - ArrayPrototypePush(state.pipes, dest); + state.pipes.push(dest); debug('pipe count=%d opts=%j', state.pipes.length, pipeOpts); const doEnd = (!pipeOpts || pipeOpts.end !== false) && @@ -855,8 +848,8 @@ Readable.prototype.unpipe = function(dest) { state.pipes = []; this.pause(); - ArrayPrototypeForEach(dests, (dest) => - dest.emit('unpipe', this, { hasUnpiped: false })); + for (let i = 0; i < dests.length; i++) + dests[i].emit('unpipe', this, { hasUnpiped: false }); return this; } @@ -865,7 +858,7 @@ Readable.prototype.unpipe = function(dest) { if (index === -1) return this; - ArrayPrototypeSplice(state.pipes, index, 1); + state.pipes.splice(index, 1); if (state.pipes.length === 0) this.pause(); @@ -877,7 +870,7 @@ Readable.prototype.unpipe = function(dest) { // Set up data events if they are asked for // Ensure readable listeners eventually get something. Readable.prototype.on = function(ev, fn) { - const res = FunctionPrototypeCall(Stream.prototype.on, this, ev, fn); + const res = Stream.prototype.on.call(this, ev, fn); const state = this._readableState; if (ev === 'data') { @@ -907,8 +900,8 @@ Readable.prototype.on = function(ev, fn) { Readable.prototype.addListener = Readable.prototype.on; Readable.prototype.removeListener = function(ev, fn) { - const res = FunctionPrototypeCall(Stream.prototype.removeListener, this, - ev, fn); + const res = Stream.prototype.removeListener.call(this, + ev, fn); if (ev === 'readable') { // We need to check if there is someone still listening to @@ -925,8 +918,8 @@ Readable.prototype.removeListener = function(ev, fn) { Readable.prototype.off = Readable.prototype.removeListener; Readable.prototype.removeAllListeners = function(ev) { - const res = ReflectApply(Stream.prototype.removeAllListeners, this, - arguments); + const res = Stream.prototype.removeAllListeners.apply(this, + arguments); if (ev === 'readable' || ev === undefined) { // We need to check if there is someone still listening to @@ -1057,11 +1050,13 @@ Readable.prototype.wrap = function(stream) { }; // Proxy all the other methods. Important when wrapping filters and duplexes. - ArrayPrototypeForEach(ObjectKeys(stream), (i) => { + const streamKeys = ObjectKeys(stream); + for (let j = 1; j < streamKeys.length; j++) { + const i = streamKeys[j]; if (this[i] === undefined && typeof stream[i] === 'function') { - this[i] = FunctionPrototypeBind(stream[i], stream); + this[i] = stream[i].bind(stream); } - }); + } return this; }; @@ -1110,15 +1105,15 @@ async function* createAsyncIterator(stream) { .on('error', function(err) { error = err; errorEmitted = true; - FunctionPrototypeCall(next, this); + next.call(this); }) .on('end', function() { endEmitted = true; - FunctionPrototypeCall(next, this); + next.call(this); }) .on('close', function() { closeEmitted = true; - FunctionPrototypeCall(next, this); + next.call(this); }); try { diff --git a/lib/internal/streams/transform.js b/lib/internal/streams/transform.js index 971bf5126f250d..26e0b07c2956c8 100644 --- a/lib/internal/streams/transform.js +++ b/lib/internal/streams/transform.js @@ -64,7 +64,6 @@ 'use strict'; const { - FunctionPrototypeCall, ObjectSetPrototypeOf, Symbol } = primordials; @@ -133,8 +132,7 @@ function final(cb) { try { const then = result.then; if (typeof then === 'function') { - FunctionPrototypeCall( - then, + then.call( result, (data) => { if (called) @@ -167,7 +165,7 @@ function final(cb) { function prefinish() { if (this._final !== final) { - FunctionPrototypeCall(final, this); + final.call(this); } } @@ -209,8 +207,7 @@ Transform.prototype._write = function(chunk, encoding, callback) { try { const then = result.then; if (typeof then === 'function') { - FunctionPrototypeCall( - then, + then.call( result, (val) => { if (called) diff --git a/lib/internal/streams/writable.js b/lib/internal/streams/writable.js index d00e175ceb1c1b..03f63b16bd04ff 100644 --- a/lib/internal/streams/writable.js +++ b/lib/internal/streams/writable.js @@ -26,12 +26,8 @@ 'use strict'; const { - ArrayPrototypeForEach, - ArrayPrototypePush, ArrayPrototypeSlice, - ArrayPrototypeSplice, Error, - FunctionPrototypeCall, FunctionPrototypeSymbolHasInstance, ObjectDefineProperty, ObjectDefineProperties, @@ -257,7 +253,7 @@ function Writable(options) { addAbortSignalNoValidate(options.signal, this); } - FunctionPrototypeCall(Stream, this, options); + Stream.call(this, options); destroyImpl.construct(this, () => { const state = this._writableState; @@ -521,10 +517,10 @@ function errorBuffer(state) { callback(new ERR_STREAM_DESTROYED('write')); } - ArrayPrototypeForEach( - ArrayPrototypeSplice(state[kOnFinished], 0), - (callback) => callback(new ERR_STREAM_DESTROYED('end')) - ); + const onfinishCallbacks = state[kOnFinished].splice(0); + for (let i = 0; i < onfinishCallbacks.length; i++) { + onfinishCallbacks[i](new ERR_STREAM_DESTROYED('end')); + } resetBuffer(state); } @@ -576,7 +572,7 @@ function clearBuffer(stream, state) { if (i === buffered.length) { resetBuffer(state); } else if (i > 256) { - ArrayPrototypeSplice(buffered, 0, i); + buffered.splice(0, i); state.bufferedIndex = 0; } else { state.bufferedIndex = i; @@ -644,7 +640,7 @@ Writable.prototype.end = function(chunk, encoding, cb) { if (err || state.finished) { process.nextTick(cb, err); } else { - ArrayPrototypePush(state[kOnFinished], cb); + state[kOnFinished].push(cb); } } @@ -667,8 +663,9 @@ function callFinal(stream, state) { const result = stream._final((err) => { state.pendingcb--; if (err) { - for (const callback of ArrayPrototypeSplice(state[kOnFinished], 0)) { - callback(err); + const onfinishCallbacks = state[kOnFinished].splice(0); + for (let i = 0; i < onfinishCallbacks.length; i++) { + onfinishCallbacks[i](err); } errorOrDestroy(stream, err, state.sync); } else if (needFinish(state)) { @@ -685,8 +682,7 @@ function callFinal(stream, state) { try { const then = result.then; if (typeof then === 'function') { - FunctionPrototypeCall( - then, + then.call( result, function() { if (state.prefinished) @@ -697,8 +693,9 @@ function callFinal(stream, state) { process.nextTick(finish, stream, state); }, function(err) { - for (const cb of ArrayPrototypeSplice(state[kOnFinished], 0)) { - process.nextTick(cb, err); + const onfinishCallbacks = state[kOnFinished].splice(0); + for (let i = 0; i < onfinishCallbacks.length; i++) { + process.nextTick(onfinishCallbacks[i], err); } process.nextTick(errorOrDestroy, stream, err, state.sync); }); @@ -744,8 +741,10 @@ function finish(stream, state) { state.finished = true; - ArrayPrototypeForEach(ArrayPrototypeSplice(state[kOnFinished], 0), - (callback) => callback()); + const onfinishCallbacks = state[kOnFinished].splice(0); + for (let i = 0; i < onfinishCallbacks.length; i++) { + onfinishCallbacks[i](); + } stream.emit('finish'); @@ -859,7 +858,7 @@ Writable.prototype.destroy = function(err, cb) { process.nextTick(errorBuffer, state); } - FunctionPrototypeCall(destroy, this, err, cb); + destroy.call(this, err, cb); return this; }; diff --git a/lib/internal/timers.js b/lib/internal/timers.js index 9521c343038b4f..9a07fe3348f65e 100644 --- a/lib/internal/timers.js +++ b/lib/internal/timers.js @@ -78,8 +78,8 @@ const { NumberIsFinite, NumberMIN_SAFE_INTEGER, ObjectCreate, - Symbol, ReflectApply, + Symbol, } = primordials; const { diff --git a/lib/internal/util.js b/lib/internal/util.js index 4756459f4c5960..f77f1fc43368c4 100644 --- a/lib/internal/util.js +++ b/lib/internal/util.js @@ -3,7 +3,6 @@ const { ArrayFrom, ArrayIsArray, - ArrayPrototypePop, ArrayPrototypePush, ArrayPrototypeSlice, ArrayPrototypeSort, @@ -148,13 +147,13 @@ function slowCases(enc) { case 4: if (enc === 'UTF8') return 'utf8'; if (enc === 'ucs2' || enc === 'UCS2') return 'utf16le'; - enc = StringPrototypeToLowerCase(`${enc}`); + enc = `${enc}`.toLowerCase(); if (enc === 'utf8') return 'utf8'; if (enc === 'ucs2') return 'utf16le'; break; case 3: if (enc === 'hex' || enc === 'HEX' || - StringPrototypeToLowerCase(`${enc}`) === 'hex') + `${enc}`.toLowerCase() === 'hex') return 'hex'; break; case 5: @@ -163,7 +162,7 @@ function slowCases(enc) { if (enc === 'UTF-8') return 'utf8'; if (enc === 'ASCII') return 'ascii'; if (enc === 'UCS-2') return 'utf16le'; - enc = StringPrototypeToLowerCase(`${enc}`); + enc = `${enc}`.toLowerCase(); if (enc === 'utf-8') return 'utf8'; if (enc === 'ascii') return 'ascii'; if (enc === 'ucs-2') return 'utf16le'; @@ -173,23 +172,23 @@ function slowCases(enc) { if (enc === 'latin1' || enc === 'binary') return 'latin1'; if (enc === 'BASE64') return 'base64'; if (enc === 'LATIN1' || enc === 'BINARY') return 'latin1'; - enc = StringPrototypeToLowerCase(`${enc}`); + enc = `${enc}`.toLowerCase(); if (enc === 'base64') return 'base64'; if (enc === 'latin1' || enc === 'binary') return 'latin1'; break; case 7: if (enc === 'utf16le' || enc === 'UTF16LE' || - StringPrototypeToLowerCase(`${enc}`) === 'utf16le') + `${enc}`.toLowerCase() === 'utf16le') return 'utf16le'; break; case 8: if (enc === 'utf-16le' || enc === 'UTF-16LE' || - StringPrototypeToLowerCase(`${enc}`) === 'utf-16le') + `${enc}`.toLowerCase() === 'utf-16le') return 'utf16le'; break; case 9: if (enc === 'base64url' || enc === 'BASE64URL' || - StringPrototypeToLowerCase(`${enc}`) === 'base64url') + `${enc}`.toLowerCase() === 'base64url') return 'base64url'; break; default: @@ -370,7 +369,7 @@ function join(output, separator) { function spliceOne(list, index) { for (; index + 1 < list.length; index++) list[index] = list[index + 1]; - ArrayPrototypePop(list); + list.pop(); } const kNodeModulesRE = /^(.*)[\\/]node_modules[\\/]/; diff --git a/lib/internal/util/debuglog.js b/lib/internal/util/debuglog.js index 3dc7a5157d5e65..b1f82b957b162a 100644 --- a/lib/internal/util/debuglog.js +++ b/lib/internal/util/debuglog.js @@ -1,7 +1,6 @@ 'use strict'; const { - FunctionPrototype, FunctionPrototypeBind, ObjectCreate, ObjectDefineProperty, @@ -45,7 +44,7 @@ function emitWarningIfNeeded(set) { } } -const noop = FunctionPrototype; +const noop = () => {}; function debuglogImpl(enabled, set) { if (debugImpls[set] === undefined) { @@ -81,7 +80,12 @@ function debuglog(set, cb) { debug = debuglogImpl(enabled, set); if (typeof cb === 'function') cb(debug); - debug(...new SafeArrayIterator(args)); + switch (args.length) { + case 0: return debug(); + case 1: return debug(args[0]); + case 2: return debug(args[0], args[1]); + default: return debug(...new SafeArrayIterator(args)); + } }; let enabled; let test = () => { @@ -89,7 +93,14 @@ function debuglog(set, cb) { test = () => enabled; return enabled; }; - const logger = (...args) => debug(...new SafeArrayIterator(args)); + const logger = (...args) => { + switch (args.length) { + case 0: return debug(); + case 1: return debug(args[0]); + case 2: return debug(args[0], args[1]); + default: return debug(...new SafeArrayIterator(args)); + } + }; ObjectDefineProperty(logger, 'enabled', { get() { return test(); diff --git a/lib/net.js b/lib/net.js index 826cffd234852d..4b55366f87dae0 100644 --- a/lib/net.js +++ b/lib/net.js @@ -24,18 +24,13 @@ const { ArrayIsArray, ArrayPrototypeIndexOf, - ArrayPrototypePush, - ArrayPrototypeSplice, Boolean, Error, - FunctionPrototype, - FunctionPrototypeCall, Number, NumberIsNaN, NumberParseInt, ObjectDefineProperty, ObjectSetPrototypeOf, - ReflectApply, Symbol, } = primordials; @@ -132,7 +127,7 @@ const DEFAULT_IPV6_ADDR = '::'; const isWindows = process.platform === 'win32'; -const noop = FunctionPrototype; +const noop = () => {}; function getFlags(ipv6Only) { return ipv6Only === true ? TCPConstants.UV_TCP_IPV6ONLY : 0; @@ -305,7 +300,7 @@ function Socket(options) { options.autoDestroy = true; // Handle strings directly. options.decodeStrings = false; - ReflectApply(stream.Duplex, this, [options]); + stream.Duplex.call(this, options); if (options.handle) { this._handle = options.handle; // private @@ -441,8 +436,7 @@ function afterShutdown() { // is overly vague, and makes it seem like the user's code is to blame. function writeAfterFIN(chunk, encoding, cb) { if (!this.writableEnded) { - return ReflectApply( - stream.Duplex.prototype.write, this, [chunk, encoding, cb]); + return stream.Duplex.prototype.write.call(this, chunk, encoding, cb); } if (typeof encoding === 'function') { @@ -586,7 +580,8 @@ Socket.prototype._read = function(n) { Socket.prototype.end = function(data, encoding, callback) { - ReflectApply(stream.Duplex.prototype.end, this, [data, encoding, callback]); + stream.Duplex.prototype.end.call(this, + data, encoding, callback); DTRACE_NET_STREAM_END(this); return this; }; @@ -602,7 +597,7 @@ Socket.prototype.pause = function() { this.destroy(errnoException(err, 'read')); } } - return FunctionPrototypeCall(stream.Duplex.prototype.pause, this); + return stream.Duplex.prototype.pause.call(this); }; @@ -611,7 +606,7 @@ Socket.prototype.resume = function() { !this._handle.reading) { tryReadStart(this); } - return FunctionPrototypeCall(stream.Duplex.prototype.resume, this); + return stream.Duplex.prototype.resume.call(this); }; @@ -620,7 +615,7 @@ Socket.prototype.read = function(n) { !this._handle.reading) { tryReadStart(this); } - return ReflectApply(stream.Duplex.prototype.read, this, [n]); + return stream.Duplex.prototype.read.call(this, n); }; @@ -1169,7 +1164,7 @@ function Server(options, connectionListener) { if (!(this instanceof Server)) return new Server(options, connectionListener); - FunctionPrototypeCall(EventEmitter, this); + EventEmitter.call(this); if (typeof options === 'function') { connectionListener = options; @@ -1681,10 +1676,10 @@ ObjectDefineProperty(Socket.prototype, '_handle', { Server.prototype._setupWorker = function(socketList) { this._usingWorkers = true; - ArrayPrototypePush(this._workers, socketList); + this._workers.push(socketList); socketList.once('exit', (socketList) => { const index = ArrayPrototypeIndexOf(this._workers, socketList); - ArrayPrototypeSplice(this._workers, index, 1); + this._workers.splice(index, 1); }); };