diff --git a/lib/_http_outgoing.js b/lib/_http_outgoing.js index fdd0f2f77eaac3..e624f0ae22b8c6 100644 --- a/lib/_http_outgoing.js +++ b/lib/_http_outgoing.js @@ -288,7 +288,8 @@ OutgoingMessage.prototype.uncork = function uncork() { } } this._send(crlf_buf, null, callbacks.length ? (err) => { - for (const callback of callbacks) { + for (let i = 0; i < callbacks.length; i++) { + const callback = callbacks[i]; callback(err); } } : null); @@ -675,7 +676,8 @@ OutgoingMessage.prototype.setHeaders = function setHeaders(headers) { // set-cookie values in array and set them all at once. const cookies = []; - for (const { 0: key, 1: value } of headers) { + for (let i = 0; i < headers.length; i++) { + const { 0: key, 1: value } = headers[i]; if (key === 'set-cookie') { if (ArrayIsArray(value)) { cookies.push(...value); diff --git a/lib/_http_server.js b/lib/_http_server.js index dd743baa306183..abbddffab72ffb 100644 --- a/lib/_http_server.js +++ b/lib/_http_server.js @@ -330,7 +330,9 @@ ServerResponse.prototype.writeEarlyHints = function writeEarlyHints(hints, cb) { head += 'Link: ' + link + '\r\n'; - for (const key of ObjectKeys(hints)) { + const hintKeys = ObjectKeys(hints); + for (let i = 0; i < hintKeys.length; i++) { + const key = hintKeys[i]; if (key !== 'link') { head += key + ': ' + hints[key] + '\r\n'; } diff --git a/lib/assert.js b/lib/assert.js index 0f1d1436fe7295..639aec97cc3574 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -350,7 +350,8 @@ assert.partialDeepStrictEqual = function partialDeepStrictEqual( class Comparison { constructor(obj, keys, actual) { - for (const key of keys) { + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; if (key in obj) { if (actual !== undefined && typeof actual[key] === 'string' && @@ -433,7 +434,8 @@ function expectedException(actual, expected, message, fn) { expected, 'may not be an empty object'); } if (isDeepEqual === undefined) lazyLoadComparison(); - for (const key of keys) { + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; if (typeof actual[key] === 'string' && isRegExp(expected[key]) && RegExpPrototypeExec(expected[key], actual[key]) !== null) { @@ -722,7 +724,8 @@ assert.ifError = function ifError(err) { ); // Filter all frames existing in err.stack. let newFrames = StringPrototypeSplit(newErr.stack, '\n'); - for (const errFrame of originalFrames) { + for (let i = 0; i < originalFrames.length; i++) { + const errFrame = originalFrames[i]; // Find the first occurrence of the frame. const pos = ArrayPrototypeIndexOf(newFrames, errFrame); if (pos !== -1) { diff --git a/lib/child_process.js b/lib/child_process.js index baa0a56d1ecdc7..5d823e87b30cf5 100644 --- a/lib/child_process.js +++ b/lib/child_process.js @@ -554,8 +554,10 @@ function copyPermissionModelFlagsToEnv(env, key, args) { } const flagsToCopy = getPermissionModelFlagsToCopy(); - for (const arg of process.execArgv) { - for (const flag of flagsToCopy) { + for (let i = 0; i < process.execArgv.length; i++) { + const arg = process.execArgv[i]; + for (let j = 0; j < flagsToCopy.length; j++) { + const flag = flagsToCopy[j]; if (arg.startsWith(flag)) { env[key] = `${env[key] ? env[key] + ' ' + arg : arg}`; } @@ -727,7 +729,8 @@ function normalizeSpawnArguments(file, args, options) { ); } - for (const key of envKeys) { + for (let i = 0; i < envKeys.length; i++) { + const key = envKeys[i]; const value = env[key]; if (value !== undefined) { validateArgumentNullCheck(key, `options.env['${key}']`); diff --git a/lib/dgram.js b/lib/dgram.js index 95184035a53024..3e6bef506397d3 100644 --- a/lib/dgram.js +++ b/lib/dgram.js @@ -591,8 +591,10 @@ function clearQueue() { state.queue = undefined; // Flush the send queue. - for (const queueEntry of queue) + for (let i = 0; i < queue.length; i++) { + const queueEntry = queue[i]; queueEntry(); + } } // valid combinations diff --git a/lib/diagnostics_channel.js b/lib/diagnostics_channel.js index 312bd258f5844a..b0f9fb9eba9a1b 100644 --- a/lib/diagnostics_channel.js +++ b/lib/diagnostics_channel.js @@ -1,6 +1,7 @@ 'use strict'; const { + ArrayFrom, ArrayPrototypeAt, ArrayPrototypeIndexOf, ArrayPrototypePush, @@ -162,7 +163,9 @@ class ActiveChannel { return ReflectApply(fn, thisArg, args); }; - for (const entry of this._stores.entries()) { + const storeEntries = ArrayFrom(this._stores.entries()); + for (let i = 0; i < storeEntries.length; i++) { + const entry = storeEntries[i]; const store = entry[0]; const transform = entry[1]; run = wrapStoreRun(store, data, run, transform); diff --git a/lib/eslint.config_partial.mjs b/lib/eslint.config_partial.mjs index fce886bbc936fb..54148b8d6a9ba9 100644 --- a/lib/eslint.config_partial.mjs +++ b/lib/eslint.config_partial.mjs @@ -388,6 +388,7 @@ export default [ 'node-core/lowercase-name-for-primitive': 'error', 'node-core/non-ascii-character': 'error', 'node-core/no-array-destructuring': 'error', + 'node-core/no-unsafe-array-iteration': 'error', 'node-core/prefer-primordials': [ 'error', { name: 'AggregateError' }, diff --git a/lib/events.js b/lib/events.js index 1c732802ef28aa..bcea18542c2b28 100644 --- a/lib/events.js +++ b/lib/events.js @@ -751,7 +751,9 @@ EventEmitter.prototype.removeAllListeners = // Emit removeListener for all listeners on all events if (arguments.length === 0) { - for (const key of ReflectOwnKeys(events)) { + const eventKeys = ReflectOwnKeys(events); + for (let i = 0; i < eventKeys.length; i++) { + const key = eventKeys[i]; if (key === 'removeListener') continue; this.removeAllListeners(key); } diff --git a/lib/inspector.js b/lib/inspector.js index 4bf3ef7b61e99f..06739dc8117b88 100644 --- a/lib/inspector.js +++ b/lib/inspector.js @@ -1,6 +1,7 @@ 'use strict'; const { + ArrayFrom, JSONParse, JSONStringify, SafeMap, @@ -150,8 +151,9 @@ class Session extends EventEmitter { return; this.#connection.disconnect(); this.#connection = null; - const remainingCallbacks = this.#messageCallbacks.values(); - for (const callback of remainingCallbacks) { + const remainingCallbacks = ArrayFrom(this.#messageCallbacks.values()); + for (let i = 0; i < remainingCallbacks.length; i++) { + const callback = remainingCallbacks[i]; process.nextTick(callback, new ERR_INSPECTOR_CLOSED()); } this.#messageCallbacks.clear(); diff --git a/lib/internal/abort_controller.js b/lib/internal/abort_controller.js index ef77e0af30e40a..d3efebf8f20636 100644 --- a/lib/internal/abort_controller.js +++ b/lib/internal/abort_controller.js @@ -4,6 +4,7 @@ // in https://github.com/mysticatea/abort-controller (MIT license) const { + ArrayFrom, ArrayPrototypePush, ObjectAssign, ObjectDefineProperties, @@ -296,7 +297,9 @@ class AbortSignal extends EventTarget { } else if (!signal[kSourceSignals]) { continue; } else { - for (const sourceSignalWeakRef of signal[kSourceSignals]) { + const sourceSignalWeakRefs = ArrayFrom(signal[kSourceSignals]); + for (let i = 0; i < sourceSignalWeakRefs.length; i++) { + const sourceSignalWeakRef = sourceSignalWeakRefs[i]; const sourceSignal = sourceSignalWeakRef.deref(); if (!sourceSignal) { continue; diff --git a/lib/internal/assert/utils.js b/lib/internal/assert/utils.js index d059fa89baf7d4..aada4cc1fe2ada 100644 --- a/lib/internal/assert/utils.js +++ b/lib/internal/assert/utils.js @@ -1,6 +1,7 @@ 'use strict'; const { + ArrayFrom, ArrayPrototypeShift, Error, ErrorCaptureStackTrace, @@ -126,7 +127,9 @@ function parseCode(code, offset) { let node; let start; // Parse the read code until the correct expression is found. - for (const token of tokenizer(code, { ecmaVersion: 'latest' })) { + const tokens = ArrayFrom(tokenizer(code, { ecmaVersion: 'latest' })); + for (let i = 0; i < tokens.length; i++) { + const token = tokens[i]; start = token.start; if (start > offset) { // No matching expression found. This could happen if the assert @@ -222,7 +225,8 @@ function getErrMessage(message, fn) { } const frames = StringPrototypeSplit(message, '\n'); message = ArrayPrototypeShift(frames); - for (const frame of frames) { + for (let i = 0; i < frames.length; i++) { + const frame = frames[i]; let pos = 0; while (pos < column && (frame[pos] === ' ' || frame[pos] === '\t')) { pos++; diff --git a/lib/internal/blob.js b/lib/internal/blob.js index 7825ea15de1e6d..c0aa3585a0b237 100644 --- a/lib/internal/blob.js +++ b/lib/internal/blob.js @@ -499,7 +499,8 @@ function createBlobReaderStream(reader) { }, cancel(reason) { // Reject any currently pending pulls here. - for (const pending of this.pendingPulls) { + for (let i = 0; i < this.pendingPulls.length; i++) { + const pending = this.pendingPulls[i]; pending.reject(reason); } this.pendingPulls = []; diff --git a/lib/internal/blocklist.js b/lib/internal/blocklist.js index 552819405a1a60..3f5d61058ac89c 100644 --- a/lib/internal/blocklist.js +++ b/lib/internal/blocklist.js @@ -163,7 +163,8 @@ class BlockList { * ]; */ #parseIPInfo(data) { - for (const item of data) { + for (let i = 0; i < data.length; i++) { + const item = data[i]; if (item.includes('IPv4')) { const subnetMatch = item.match( /Subnet: IPv4 (\d{1,3}(?:\.\d{1,3}){3})\/(\d{1,2})/, @@ -241,7 +242,8 @@ class BlockList { // The data argument must be a string, or an array of strings that // is JSON parseable. if (ArrayIsArray(data)) { - for (const n of data) { + for (let i = 0; i < data.length; i++) { + const n = data[i]; if (typeof n !== 'string') { throw new ERR_INVALID_ARG_TYPE('data', ['string', 'string[]'], data); } @@ -253,7 +255,8 @@ class BlockList { if (!ArrayIsArray(data)) { throw new ERR_INVALID_ARG_TYPE('data', ['string', 'string[]'], data); } - for (const n of data) { + for (let i = 0; i < data.length; i++) { + const n = data[i]; if (typeof n !== 'string') { throw new ERR_INVALID_ARG_TYPE('data', ['string', 'string[]'], data); } diff --git a/lib/internal/child_process.js b/lib/internal/child_process.js index f110557a9374f7..e304e7ee972cb6 100644 --- a/lib/internal/child_process.js +++ b/lib/internal/child_process.js @@ -1,6 +1,7 @@ 'use strict'; const { + ArrayFrom, ArrayIsArray, ArrayPrototypePush, ArrayPrototypeReduce, @@ -604,7 +605,9 @@ function setupChannel(target, channel, serializationMode) { if (recvHandle) pendingHandle = recvHandle; - for (const message of parseChannelMessages(channel, pool)) { + const messages = ArrayFrom(parseChannelMessages(channel, pool)); + for (let i = 0; i < messages.length; i++) { + const message = messages[i]; // There will be at most one NODE_HANDLE message in every chunk we // read because SCM_RIGHTS messages don't get coalesced. Make sure // that we deliver the handle with the right message however. diff --git a/lib/internal/cli_table.js b/lib/internal/cli_table.js index be0bd44bbc36fe..13b3c86c5e40fa 100644 --- a/lib/internal/cli_table.js +++ b/lib/internal/cli_table.js @@ -81,8 +81,10 @@ const table = (head, columns) => { ArrayPrototypeJoin(divider, tableChars.rowMiddle) + tableChars.rightMiddle + '\n'; - for (const row of rows) + for (let i = 0; i < rows.length; i++) { + const row = rows[i]; result += `${renderRow(row, columnWidths)}\n`; + } result += tableChars.bottomLeft + ArrayPrototypeJoin(divider, tableChars.bottomMiddle) + diff --git a/lib/internal/cluster/child.js b/lib/internal/cluster/child.js index 7c132310a81874..47521bf7c5fa87 100644 --- a/lib/internal/cluster/child.js +++ b/lib/internal/cluster/child.js @@ -1,6 +1,7 @@ 'use strict'; const { + ArrayFrom, ArrayPrototypeJoin, FunctionPrototype, ObjectAssign, @@ -270,7 +271,9 @@ function _disconnect(primaryInitiated) { } } - for (const handle of handles.values()) { + const handleValues = ArrayFrom(handles.values()); + for (let i = 0; i < handleValues.length; i++) { + const handle = handleValues[i]; waitingCount++; if (handle[owner_symbol]) diff --git a/lib/internal/cluster/primary.js b/lib/internal/cluster/primary.js index af17d39de44964..850a7ab1632487 100644 --- a/lib/internal/cluster/primary.js +++ b/lib/internal/cluster/primary.js @@ -1,6 +1,7 @@ 'use strict'; const { + ArrayFrom, ArrayPrototypePush, ArrayPrototypeSlice, ArrayPrototypeSome, @@ -96,7 +97,9 @@ cluster.setupPrimary = function(options) { if (message.cmd !== 'NODE_DEBUG_ENABLED') return; - for (const worker of ObjectValues(cluster.workers)) { + const workers = ObjectValues(cluster.workers); + for (let i = 0; i < workers.length; i++) { + const worker = workers[i]; if (worker.state === 'online' || worker.state === 'listening') { process._debugProcess(worker.process.pid); } else { @@ -152,7 +155,9 @@ function removeWorker(worker) { function removeHandlesForWorker(worker) { assert(worker); - for (const { 0: key, 1: handle } of handles) { + const handleEntries = ArrayFrom(handles); + for (let i = 0; i < handleEntries.length; i++) { + const { 0: key, 1: handle } = handleEntries[i]; if (handle.remove(worker)) handles.delete(key); } @@ -226,7 +231,8 @@ cluster.disconnect = function(cb) { if (workers.length === 0) { process.nextTick(() => intercom.emit('disconnect')); } else { - for (const worker of workers) { + for (let i = 0; i < workers.length; i++) { + const worker = workers[i]; if (worker.isConnected()) { worker.disconnect(); } diff --git a/lib/internal/console/constructor.js b/lib/internal/console/constructor.js index 26f2e837d74f6f..5d42ca44ef3de2 100644 --- a/lib/internal/console/constructor.js +++ b/lib/internal/console/constructor.js @@ -566,7 +566,8 @@ const consoleMethods = { length++; } } else { - for (const { 0: k, 1: v } of tabularData) { + for (let i = 0; i < tabularData.length; i++) { + const { 0: k, 1: v } = tabularData[i]; ArrayPrototypePush(keys, _inspect(k)); ArrayPrototypePush(values, _inspect(v)); length++; @@ -589,7 +590,9 @@ const consoleMethods = { if (setlike) { const values = []; let length = 0; - for (const v of tabularData) { + const tabularArray = ArrayFrom(tabularData); + for (let i = 0; i < tabularArray.length; i++) { + const v = tabularArray[i]; ArrayPrototypePush(values, _inspect(v)); length++; } @@ -610,7 +613,8 @@ const consoleMethods = { valuesKeyArray[i] = _inspect(item); } else { const keys = properties || ObjectKeys(item); - for (const key of keys) { + for (let j = 0; j < keys.length; j++) { + const key = keys[j]; map[key] ??= []; if ((primitive && properties) || !ObjectPrototypeHasOwnProperty(item, key)) @@ -643,8 +647,11 @@ const isArray = (v) => ArrayIsArray(v) || isTypedArray(v) || isBuffer(v); function noop() {} -for (const method of ReflectOwnKeys(consoleMethods)) +const methodKeys = ReflectOwnKeys(consoleMethods); +for (let i = 0; i < methodKeys.length; i++) { + const method = methodKeys[i]; Console.prototype[method] = consoleMethods[method]; +} Console.prototype.dirxml = Console.prototype.log; Console.prototype.groupCollapsed = Console.prototype.group; @@ -666,7 +673,9 @@ function initializeGlobalConsole(globalConsole) { const vmConsoleKeys = ObjectKeys(consoleFromVM); const originalKeys = new SafeSet(vmConsoleKeys.concat(nodeConsoleKeys)); const inspectorConsoleKeys = new SafeSet(); - for (const key of ObjectKeys(globalConsole)) { + const globalConsoleKeys = ObjectKeys(globalConsole); + for (let i = 0; i < globalConsoleKeys.length; i++) { + const key = globalConsoleKeys[i]; if (!originalKeys.has(key)) { inspectorConsoleKeys.add(key); } @@ -674,7 +683,9 @@ function initializeGlobalConsole(globalConsole) { // During deserialization these should be reinstalled to console by // V8 when the inspector client is created. addSerializeCallback(() => { - for (const key of inspectorConsoleKeys) { + const keysArray = ArrayFrom(inspectorConsoleKeys); + for (let i = 0; i < keysArray.length; i++) { + const key = keysArray[i]; globalConsole[key] = undefined; } }); diff --git a/lib/internal/console/global.js b/lib/internal/console/global.js index 908471ff44d74e..28e22ab6367236 100644 --- a/lib/internal/console/global.js +++ b/lib/internal/console/global.js @@ -31,7 +31,9 @@ const globalConsole = { __proto__: {} }; // the global console itself. In addition, we need to make the global // console a namespace by binding the console methods directly onto // the global console with the receiver fixed. -for (const prop of ReflectOwnKeys(Console.prototype)) { +const prototypeKeys = ReflectOwnKeys(Console.prototype); +for (let i = 0; i < prototypeKeys.length; i++) { + const prop = prototypeKeys[i]; if (prop === 'constructor') { continue; } const desc = ReflectGetOwnPropertyDescriptor(Console.prototype, prop); if (typeof desc.value === 'function') { // fix the receiver diff --git a/lib/internal/crypto/keys.js b/lib/internal/crypto/keys.js index 7529254685fb17..a5047e67c9e13d 100644 --- a/lib/internal/crypto/keys.js +++ b/lib/internal/crypto/keys.js @@ -86,9 +86,12 @@ const kCreatePublic = 2; const kCreatePrivate = 3; const encodingNames = []; -for (const m of [[kKeyEncodingPKCS1, 'pkcs1'], [kKeyEncodingPKCS8, 'pkcs8'], - [kKeyEncodingSPKI, 'spki'], [kKeyEncodingSEC1, 'sec1']]) +const encodings = [[kKeyEncodingPKCS1, 'pkcs1'], [kKeyEncodingPKCS8, 'pkcs8'], + [kKeyEncodingSPKI, 'spki'], [kKeyEncodingSEC1, 'sec1']]; +for (let i = 0; i < encodings.length; i++) { + const m = encodings[i]; encodingNames[m[0]] = m[1]; +} // Creating the KeyObject class is a little complicated due to inheritance // and the fact that KeyObjects should be transferable between threads, diff --git a/lib/internal/crypto/util.js b/lib/internal/crypto/util.js index 2eba29333bcba4..81c9cd0e8b3cca 100644 --- a/lib/internal/crypto/util.js +++ b/lib/internal/crypto/util.js @@ -3,6 +3,7 @@ const { ArrayBufferIsView, ArrayBufferPrototypeGetByteLength, + ArrayFrom, ArrayPrototypeIncludes, ArrayPrototypePush, BigInt, @@ -408,9 +409,12 @@ function getDataViewOrTypedArrayByteLength(V) { } function hasAnyNotIn(set, checks) { - for (const s of set) + const setArray = ArrayFrom(set); + for (let i = 0; i < setArray.length; i++) { + const s = setArray[i]; if (!ArrayPrototypeIncludes(checks, s)) return true; + } return false; } @@ -554,7 +558,9 @@ function validateKeyOps(keyOps, usagesSet) { } if (usagesSet !== undefined) { - for (const use of usagesSet) { + const usagesArray = ArrayFrom(usagesSet); + for (let i = 0; i < usagesArray.length; i++) { + const use = usagesArray[i]; if (!ArrayPrototypeIncludes(keyOps, use)) { throw lazyDOMException( 'Key operations and usage mismatch', diff --git a/lib/internal/errors.js b/lib/internal/errors.js index da3da89dd267ba..d9db2d290e9b77 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -659,7 +659,9 @@ class UVException extends Error { super(message); - for (const prop of ObjectKeys(ctx)) { + const ctxKeys = ObjectKeys(ctx); + for (let i = 0; i < ctxKeys.length; i++) { + const prop = ctxKeys[i]; if (prop === 'message' || prop === 'path' || prop === 'dest') { continue; } @@ -1408,7 +1410,8 @@ E('ERR_INVALID_ARG_TYPE', const instances = []; const other = []; - for (const value of expected) { + for (let i = 0; i < expected.length; i++) { + const value = expected[i]; assert(typeof value === 'string', 'All expected entries have to be of type string'); if (ArrayPrototypeIncludes(kTypes, value)) { diff --git a/lib/internal/fs/cp/cp.js b/lib/internal/fs/cp/cp.js index b632c650681e77..9feacb182f105f 100644 --- a/lib/internal/fs/cp/cp.js +++ b/lib/internal/fs/cp/cp.js @@ -314,7 +314,9 @@ async function mkDirAndCopy(srcMode, src, dest, opts) { async function copyDir(src, dest, opts) { const dir = await opendir(src); - for await (const { name } of dir) { + let entry; + while ((entry = await dir.read()) !== null) { + const { name } = entry; const srcItem = join(src, name); const destItem = join(dest, name); const { destStat, skipped } = await checkPaths(srcItem, destItem, opts); diff --git a/lib/internal/fs/dir.js b/lib/internal/fs/dir.js index 03f585bab2afaf..147079123dcc1c 100644 --- a/lib/internal/fs/dir.js +++ b/lib/internal/fs/dir.js @@ -151,7 +151,10 @@ class Dir { process.nextTick(() => { const queue = this.#operationQueue; this.#operationQueue = null; - for (const op of queue) op(); + for (let i = 0; i < queue.length; i++) { + const op = queue[i]; + op(); + } }); if (err || result === null) { diff --git a/lib/internal/fs/glob.js b/lib/internal/fs/glob.js index 14180f936ac1c8..da051f589cf84d 100644 --- a/lib/internal/fs/glob.js +++ b/lib/internal/fs/glob.js @@ -432,7 +432,9 @@ class Glob { const subPatterns = new SafeSet(); const nSymlinks = new SafeSet(); - for (const index of pattern.indexes) { + const indexesArray = ArrayFrom(pattern.indexes); + for (let j = 0; j < indexesArray.length; j++) { + const index = indexesArray[j]; // For each child, check potential patterns if (this.#cache.seen(entryPath, pattern, index) || this.#cache.seen(entryPath, pattern, index + 1)) { return; @@ -632,7 +634,9 @@ class Glob { const subPatterns = new SafeSet(); const nSymlinks = new SafeSet(); - for (const index of pattern.indexes) { + const indexesArray = ArrayFrom(pattern.indexes); + for (let j = 0; j < indexesArray.length; j++) { + const index = indexesArray[j]; // For each child, check potential patterns if (this.#cache.seen(entryPath, pattern, index) || this.#cache.seen(entryPath, pattern, index + 1)) { return; diff --git a/lib/internal/fs/promises.js b/lib/internal/fs/promises.js index 5184a28d326f3e..0c199aabf3ef60 100644 --- a/lib/internal/fs/promises.js +++ b/lib/internal/fs/promises.js @@ -1,6 +1,7 @@ 'use strict'; const { + ArrayFrom, ArrayPrototypePop, ArrayPrototypePush, Error, @@ -479,7 +480,9 @@ function checkAborted(signal) { async function writeFileHandle(filehandle, data, signal, encoding) { checkAborted(signal); if (isCustomIterable(data)) { - for await (const buf of data) { + const dataArray = ArrayFrom(data); + for (let i = 0; i < dataArray.length; i++) { + const buf = await dataArray[i]; checkAborted(signal); const toWrite = isArrayBufferView(buf) ? buf : Buffer.from(buf, encoding || 'utf8'); @@ -893,7 +896,9 @@ async function readdirRecursive(originalPath, options) { while (queue.length > 0) { // If we want to implement BFS make this a `shift` call instead of `pop` const { 0: path, 1: readdir } = ArrayPrototypePop(queue); - for (const dirent of getDirents(path, readdir)) { + const dirents = getDirents(path, readdir); + for (let i = 0; i < dirents.length; i++) { + const dirent = dirents[i]; ArrayPrototypePush(result, dirent); if (dirent.isDirectory()) { const direntPath = pathModule.join(path, dirent.name); @@ -916,7 +921,8 @@ async function readdirRecursive(originalPath, options) { } else { while (queue.length > 0) { const { 0: path, 1: readdir } = ArrayPrototypePop(queue); - for (const ent of readdir) { + for (let i = 0; i < readdir.length; i++) { + const ent = readdir[i]; const direntPath = pathModule.join(path, ent); const stat = binding.internalModuleStat(direntPath); ArrayPrototypePush( diff --git a/lib/internal/fs/recursive_watch.js b/lib/internal/fs/recursive_watch.js index 29d8c23fdfbe31..237aca43337999 100644 --- a/lib/internal/fs/recursive_watch.js +++ b/lib/internal/fs/recursive_watch.js @@ -82,7 +82,9 @@ class FSWatcher extends EventEmitter { this.#closed = true; - for (const file of this.#files.keys()) { + const filesKeys = [...this.#files.keys()]; + for (let i = 0; i < filesKeys.length; i++) { + const file = filesKeys[i]; this.#watchers.get(file)?.close(); this.#watchers.delete(file); } @@ -95,7 +97,9 @@ class FSWatcher extends EventEmitter { #unwatchFiles(file) { this.#symbolicFiles.delete(file); - for (const filename of this.#files.keys()) { + const filesKeys = [...this.#files.keys()]; + for (let i = 0; i < filesKeys.length; i++) { + const filename = filesKeys[i]; if (StringPrototypeStartsWith(filename, file)) { this.#files.delete(filename); this.#watchers.get(filename)?.close(); @@ -112,7 +116,8 @@ class FSWatcher extends EventEmitter { withFileTypes: true, }); - for (const file of files) { + for (let i = 0; i < files.length; i++) { + const file = files[i]; if (this.#closed) { break; } diff --git a/lib/internal/fs/utils.js b/lib/internal/fs/utils.js index 0f788ce4dbc3a3..851cbd097ce599 100644 --- a/lib/internal/fs/utils.js +++ b/lib/internal/fs/utils.js @@ -200,7 +200,9 @@ class DirentFromStats extends Dirent { } } -for (const name of ReflectOwnKeys(Dirent.prototype)) { +const direntKeys = ReflectOwnKeys(Dirent.prototype); +for (let i = 0; i < direntKeys.length; i++) { + const name = direntKeys[i]; if (name === 'constructor') { continue; } diff --git a/lib/internal/http2/compat.js b/lib/internal/http2/compat.js index a02d3b0c5844fa..7d2431f9b8464c 100644 --- a/lib/internal/http2/compat.js +++ b/lib/internal/http2/compat.js @@ -912,7 +912,9 @@ class Http2ServerResponse extends Stream { const linkHeaderValue = validateLinkHeaderValue(hints.link); - for (const key of ObjectKeys(hints)) { + const hintsKeys = ObjectKeys(hints); + for (let i = 0; i < hintsKeys.length; i++) { + const key = hintsKeys[i]; if (key !== 'link') { headers[key] = hints[key]; } diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js index 6bf2edd1487d49..1b6076df6ae4dd 100644 --- a/lib/internal/http2/core.js +++ b/lib/internal/http2/core.js @@ -1001,7 +1001,8 @@ const validateSettings = hideStackFrames((settings) => { const entries = ObjectEntries(settings.customSettings); if (entries.length > MAX_ADDITIONAL_SETTINGS) throw new ERR_HTTP2_TOO_MANY_CUSTOM_SETTINGS(); - for (const { 0: key, 1: value } of entries) { + for (let i = 0; i < entries.length; i++) { + const { 0: key, 1: value } = entries[i]; assertWithinRange.withoutStackTrace('customSettings:id', Number(key), 0, 0xffff); assertWithinRange.withoutStackTrace('customSettings:value', Number(value), 0, kMaxInt); } @@ -3291,7 +3292,9 @@ function onErrorSecureServerSession(err, socket) { function closeAllSessions(server) { const sessions = server[kSessions]; if (sessions.size > 0) { - for (const session of sessions) { + const sessionsArray = [...sessions]; + for (let i = 0; i < sessionsArray.length; i++) { + const session = sessionsArray[i]; session.close(); } } @@ -3403,7 +3406,9 @@ Http2Server.prototype[EventEmitter.captureRejectionSymbol] = function( const { 1: res } = args; if (!res.headersSent && !res.finished) { // Don't leak headers. - for (const name of res.getHeaderNames()) { + const headerNames = res.getHeaderNames(); + for (let i = 0; i < headerNames.length; i++) { + const name = headerNames[i]; res.removeHeader(name); } res.statusCode = 500; diff --git a/lib/internal/inspector/network_http.js b/lib/internal/inspector/network_http.js index f2a372c61f9f7e..59c2a91fa33d54 100644 --- a/lib/internal/inspector/network_http.js +++ b/lib/internal/inspector/network_http.js @@ -27,7 +27,9 @@ const convertHeaderObject = (headers = {}) => { let charset; let mimeType; const dict = {}; - for (const { 0: key, 1: value } of ObjectEntries(headers)) { + const headerEntries = ObjectEntries(headers); + for (let i = 0; i < headerEntries.length; i++) { + const { 0: key, 1: value } = headerEntries[i]; const lowerCasedKey = key.toLowerCase(); if (lowerCasedKey === 'host') { host = value; diff --git a/lib/internal/main/print_help.js b/lib/internal/main/print_help.js index 62269956be9faf..f53bbcb055e38b 100644 --- a/lib/internal/main/print_help.js +++ b/lib/internal/main/print_help.js @@ -27,8 +27,11 @@ const { const { getCLIOptionsInfo, getOptionValue } = require('internal/options'); const typeLookup = []; -for (const key of ObjectKeys(types)) +const typeKeys = ObjectKeys(types); +for (let i = 0; i < typeKeys.length; i++) { + const key = typeKeys[i]; typeLookup[types[key]] = key; +} // Environment variables are parsed ad-hoc throughout the code base, // so we gather the documentation here. @@ -135,9 +138,10 @@ function format( }, ); - for (const { - 0: name, 1: { helpText, type, defaultIsTrue }, - } of sortedOptions) { + for (let i = 0; i < sortedOptions.length; i++) { + const { + 0: name, 1: { helpText, type, defaultIsTrue }, + } = sortedOptions[i]; if (!helpText) continue; const value = getOptionValue(name); @@ -151,7 +155,8 @@ function format( if (argDescription) displayName += `=${argDescription}`; - for (const { 0: from, 1: to } of aliases) { + for (let j = 0; j < aliases.length; j++) { + const { 0: from, 1: to } = aliases[j]; // For cases like e.g. `-e, --eval`. if (to[0] === name && to.length === 1) { displayName = `${from}, ${displayName}`; diff --git a/lib/internal/mime.js b/lib/internal/mime.js index efe6a2212b0483..3d5dd505d78e2c 100644 --- a/lib/internal/mime.js +++ b/lib/internal/mime.js @@ -207,7 +207,8 @@ class MIMEParams { toString() { this.#parse(); let ret = ''; - for (const { 0: key, 1: value } of this.#data) { + for (let i = 0; i < this.#data.length; i++) { + const { 0: key, 1: value } = this.#data[i]; const encoded = encode(value); // Ensure they are separated if (ret.length) ret += ';'; diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js index 3b16099848a3a9..1ee32e72a48bd6 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js @@ -2030,7 +2030,9 @@ Module._preloadModules = function(requests) { * both CommonJS and ES module contexts. */ Module.syncBuiltinESMExports = function syncBuiltinESMExports() { - for (const mod of BuiltinModule.map.values()) { + const builtinModules = [...BuiltinModule.map.values()]; + for (let i = 0; i < builtinModules.length; i++) { + const mod = builtinModules[i]; if (BuiltinModule.canBeRequiredWithoutScheme(mod.id)) { mod.syncExports(); } diff --git a/lib/internal/modules/esm/create_dynamic_module.js b/lib/internal/modules/esm/create_dynamic_module.js index 068893ce4361f1..b50c530672e6ba 100644 --- a/lib/internal/modules/esm/create_dynamic_module.js +++ b/lib/internal/modules/esm/create_dynamic_module.js @@ -81,7 +81,9 @@ import.meta.done(); meta.done = () => { evaluate(reflect); reflect.onReady = (cb) => cb(reflect); - for (const fn of readyfns) { + const readyfnsArray = [...readyfns]; + for (let i = 0; i < readyfnsArray.length; i++) { + const fn = readyfnsArray[i]; readyfns.delete(fn); fn(reflect); } diff --git a/lib/internal/modules/esm/module_job.js b/lib/internal/modules/esm/module_job.js index 268d8154457295..64bd3603d8a19a 100644 --- a/lib/internal/modules/esm/module_job.js +++ b/lib/internal/modules/esm/module_job.js @@ -311,7 +311,8 @@ class ModuleJob extends ModuleJobBase { throw e; } - for (const dependencyJob of jobsInGraph) { + for (let i = 0; i < jobsInGraph.length; i++) { + const dependencyJob = jobsInGraph[i]; // Calling `this.module.instantiate()` instantiates not only the // ModuleWrap in this module, but all modules in the graph. dependencyJob.instantiated = resolvedPromise; diff --git a/lib/internal/modules/esm/translators.js b/lib/internal/modules/esm/translators.js index b6ac42302a126b..227ba66f95dc80 100644 --- a/lib/internal/modules/esm/translators.js +++ b/lib/internal/modules/esm/translators.js @@ -1,6 +1,7 @@ 'use strict'; const { + ArrayIsArray, ArrayPrototypePush, FunctionPrototypeCall, JSONParse, @@ -216,7 +217,9 @@ function createCJSModuleWrap(url, source, isMain, format, loadCJS = loadCJSModul } else { ({ exports } = module); } - for (const exportName of exportNames) { + const exportNamesArray = ArrayIsArray(exportNames) ? exportNames : [...exportNames]; + for (let i = 0; i < exportNamesArray.length; i++) { + const exportName = exportNamesArray[i]; if (exportName === 'default' || exportName === 'module.exports' || !ObjectPrototypeHasOwnProperty(exports, exportName)) { continue; @@ -400,7 +403,9 @@ function cjsPreparseModuleExports(filename, source, format) { if (format === 'commonjs' || (!BuiltinModule.normalizeRequirableId(resolved) && findLongestRegisteredExtension(resolved) === '.js')) { const { exportNames: reexportNames } = cjsPreparseModuleExports(resolved, undefined, format); - for (const name of reexportNames) { + const reexportNamesArray = ArrayIsArray(reexportNames) ? reexportNames : [...reexportNames]; + for (let i = 0; i < reexportNamesArray.length; i++) { + const name = reexportNamesArray[i]; exportNames.add(name); } } @@ -515,7 +520,9 @@ translators.set('wasm', async function(url, source) { const importsList = new SafeSet(); const wasmGlobalImports = []; - for (const impt of WebAssembly.Module.imports(compiled)) { + const moduleImports = WebAssembly.Module.imports(compiled); + for (let i = 0; i < moduleImports.length; i++) { + const impt = moduleImports[i]; if (impt.kind === 'global') { ArrayPrototypePush(wasmGlobalImports, impt); } @@ -531,7 +538,9 @@ translators.set('wasm', async function(url, source) { const exportsList = new SafeSet(); const wasmGlobalExports = new SafeSet(); - for (const expt of WebAssembly.Module.exports(compiled)) { + const moduleExports = WebAssembly.Module.exports(compiled); + for (let i = 0; i < moduleExports.length; i++) { + const expt = moduleExports[i]; if (expt.kind === 'global') { wasmGlobalExports.add(expt.name); } @@ -545,12 +554,15 @@ translators.set('wasm', async function(url, source) { const { module } = createDynamicModule([...importsList], [...exportsList], url, (reflect) => { emitExperimentalWarning('Importing WebAssembly module instances'); - for (const impt of importsList) { + const importsArray = [...importsList]; + for (let i = 0; i < importsArray.length; i++) { + const impt = importsArray[i]; const importNs = reflect.imports[impt]; const wasmInstance = wasmInstances.get(importNs); if (wasmInstance) { const wrappedModule = ObjectAssign({ __proto__: null }, reflect.imports[impt]); - for (const { module, name } of wasmGlobalImports) { + for (let j = 0; j < wasmGlobalImports.length; j++) { + const { module, name } = wasmGlobalImports[j]; if (module !== impt) { continue; } @@ -565,7 +577,9 @@ translators.set('wasm', async function(url, source) { // instantiation, since all bindings will be in the Temporal Deadzone (TDZ). const { exports } = new WebAssembly.Instance(compiled, reflect.imports); wasmInstances.set(module.getNamespace(), exports); - for (const expt of exportsList) { + const exportsArray = [...exportsList]; + for (let i = 0; i < exportsArray.length; i++) { + const expt = exportsArray[i]; let val = exports[expt]; // Unwrap WebAssembly.Global for JS bindings if (wasmGlobalExports.has(expt)) { diff --git a/lib/internal/modules/esm/worker.js b/lib/internal/modules/esm/worker.js index 6624b740d0a98b..caef5cbb93a1b0 100644 --- a/lib/internal/modules/esm/worker.js +++ b/lib/internal/modules/esm/worker.js @@ -147,7 +147,9 @@ async function customizedModuleWorker(lock, syncCommPort, errorHandler) { const unsettledResponsePorts = new SafeSet(); process.on('beforeExit', () => { - for (const port of unsettledResponsePorts) { + const portsArray = [...unsettledResponsePorts]; + for (let i = 0; i < portsArray.length; i++) { + const port = portsArray[i]; port.postMessage(wrapMessage('never-settle')); } unsettledResponsePorts.clear(); diff --git a/lib/internal/options.js b/lib/internal/options.js index fef0d61d143335..fc6645e1b97660 100644 --- a/lib/internal/options.js +++ b/lib/internal/options.js @@ -81,13 +81,17 @@ function generateConfigJsonSchema() { const nodeOptions = rootProperties.nodeOptions.properties; // Add env options to nodeOptions (backward compatibility) - for (const { 0: key, 1: type } of envOptionsMap) { + const envOptionsArray = [...envOptionsMap]; + for (let i = 0; i < envOptionsArray.length; i++) { + const { 0: key, 1: type } = envOptionsArray[i]; const keyWithoutPrefix = StringPrototypeReplace(key, '--', ''); nodeOptions[keyWithoutPrefix] = createPropertyForType(type); } // Add namespace properties at the root level - for (const { 0: namespace, 1: optionsMap } of namespaceOptionsMap) { + const namespaceOptionsArray = [...namespaceOptionsMap]; + for (let i = 0; i < namespaceOptionsArray.length; i++) { + const { 0: namespace, 1: optionsMap } = namespaceOptionsArray[i]; // Create namespace object at the root level rootProperties[namespace] = { __proto__: null, @@ -99,7 +103,9 @@ function generateConfigJsonSchema() { const namespaceProperties = rootProperties[namespace].properties; // Add all options for this namespace - for (const { 0: optionName, 1: optionType } of optionsMap) { + const optionsArray = [...optionsMap]; + for (let j = 0; j < optionsArray.length; j++) { + const { 0: optionName, 1: optionType } = optionsArray[j]; const keyWithoutPrefix = StringPrototypeReplace(optionName, '--', ''); namespaceProperties[keyWithoutPrefix] = createPropertyForType(optionType); } diff --git a/lib/internal/per_context/domexception.js b/lib/internal/per_context/domexception.js index 1bc46616556612..c8c8edbd5835f9 100644 --- a/lib/internal/per_context/domexception.js +++ b/lib/internal/per_context/domexception.js @@ -167,7 +167,7 @@ ObjectDefineProperties(DOMExceptionPrototype, { code: { __proto__: null, enumerable: true, configurable: true }, }); -for (const { 0: name, 1: codeName, 2: value } of [ +const errorDefinitions = [ ['IndexSizeError', 'INDEX_SIZE_ERR', 1], ['DOMStringSizeError', 'DOMSTRING_SIZE_ERR', 2], ['HierarchyRequestError', 'HIERARCHY_REQUEST_ERR', 3], @@ -195,7 +195,9 @@ for (const { 0: name, 1: codeName, 2: value } of [ ['DataCloneError', 'DATA_CLONE_ERR', 25], // There are some more error names, but since they don't have codes assigned, // we don't need to care about them. -]) { +]; +for (let i = 0; i < errorDefinitions.length; i++) { + const { 0: name, 1: codeName, 2: value } = errorDefinitions[i]; const desc = { enumerable: true, value }; ObjectDefineProperty(DOMException, codeName, desc); ObjectDefineProperty(DOMExceptionPrototype, codeName, desc); diff --git a/lib/internal/per_context/primordials.js b/lib/internal/per_context/primordials.js index ecee4cd66eba44..6f462658ffa752 100644 --- a/lib/internal/per_context/primordials.js +++ b/lib/internal/per_context/primordials.js @@ -73,7 +73,9 @@ function copyAccessor(dest, prefix, key, { enumerable, get, set }) { } function copyPropsRenamed(src, dest, prefix) { - for (const key of ReflectOwnKeys(src)) { + const keys = ReflectOwnKeys(src); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; const newKey = getNewKey(key); const desc = ReflectGetOwnPropertyDescriptor(src, key); if ('get' in desc) { @@ -95,7 +97,9 @@ function copyPropsRenamed(src, dest, prefix) { } function copyPropsRenamedBound(src, dest, prefix) { - for (const key of ReflectOwnKeys(src)) { + const keys = ReflectOwnKeys(src); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; const newKey = getNewKey(key); const desc = ReflectGetOwnPropertyDescriptor(src, key); if ('get' in desc) { @@ -119,7 +123,9 @@ function copyPropsRenamedBound(src, dest, prefix) { } function copyPrototype(src, dest, prefix) { - for (const key of ReflectOwnKeys(src)) { + const keys = ReflectOwnKeys(src); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; const newKey = getNewKey(key); const desc = ReflectGetOwnPropertyDescriptor(src, key); if ('get' in desc) { @@ -143,47 +149,55 @@ function copyPrototype(src, dest, prefix) { } // Create copies of configurable value properties of the global object -[ +const globalNames = [ 'Proxy', 'globalThis', -].forEach((name) => { +]; +for (let i = 0; i < globalNames.length; i++) { + const name = globalNames[i]; // eslint-disable-next-line no-restricted-globals primordials[name] = globalThis[name]; -}); +} // Create copies of URI handling functions -[ +const uriFunctions = [ decodeURI, decodeURIComponent, encodeURI, encodeURIComponent, -].forEach((fn) => { +]; +for (let i = 0; i < uriFunctions.length; i++) { + const fn = uriFunctions[i]; primordials[fn.name] = fn; -}); +} // Create copies of legacy functions -[ +const legacyFunctions = [ escape, eval, unescape, -].forEach((fn) => { +]; +for (let i = 0; i < legacyFunctions.length; i++) { + const fn = legacyFunctions[i]; primordials[fn.name] = fn; -}); +} // Create copies of the namespace objects -[ +const namespaceNames = [ 'Atomics', 'JSON', 'Math', 'Proxy', 'Reflect', -].forEach((name) => { +]; +for (let i = 0; i < namespaceNames.length; i++) { + const name = namespaceNames[i]; // eslint-disable-next-line no-restricted-globals copyPropsRenamed(globalThis[name], primordials, name); -}); +} // Create copies of intrinsic objects -[ +const intrinsicObjectNames = [ 'AggregateError', 'Array', 'ArrayBuffer', @@ -221,32 +235,36 @@ function copyPrototype(src, dest, prefix) { 'WeakMap', 'WeakRef', 'WeakSet', -].forEach((name) => { +]; +for (let i = 0; i < intrinsicObjectNames.length; i++) { + const name = intrinsicObjectNames[i]; // eslint-disable-next-line no-restricted-globals const original = globalThis[name]; primordials[name] = original; copyPropsRenamed(original, primordials, name); copyPrototype(original.prototype, primordials, `${name}Prototype`); -}); +} // Create copies of intrinsic objects that require a valid `this` to call // static methods. // Refs: https://www.ecma-international.org/ecma-262/#sec-promise.all -[ +const thisRequiredObjectNames = [ 'Promise', -].forEach((name) => { +]; +for (let i = 0; i < thisRequiredObjectNames.length; i++) { + const name = thisRequiredObjectNames[i]; // eslint-disable-next-line no-restricted-globals const original = globalThis[name]; primordials[name] = original; copyPropsRenamedBound(original, primordials, name); copyPrototype(original.prototype, primordials, `${name}Prototype`); -}); +} // Create copies of abstract intrinsic objects that are not directly exposed // on the global object. // Refs: https://tc39.es/ecma262/#sec-%typedarray%-intrinsic-object -[ +const abstractIntrinsicObjects = [ { name: 'TypedArray', original: Reflect.getPrototypeOf(Uint8Array) }, { name: 'ArrayIterator', original: { prototype: Reflect.getPrototypeOf(Array.prototype[Symbol.iterator]()), @@ -254,13 +272,15 @@ function copyPrototype(src, dest, prefix) { { name: 'StringIterator', original: { prototype: Reflect.getPrototypeOf(String.prototype[Symbol.iterator]()), } }, -].forEach(({ name, original }) => { +]; +for (let i = 0; i < abstractIntrinsicObjects.length; i++) { + const { name, original } = abstractIntrinsicObjects[i]; primordials[name] = original; // The static %TypedArray% methods require a valid `this`, but can't be bound, // as they need a subclass constructor as the receiver: copyPrototype(original, primordials, name); copyPrototype(original.prototype, primordials, `${name}Prototype`); -}); +} primordials.IteratorPrototype = Reflect.getPrototypeOf(primordials.ArrayIteratorPrototype); diff --git a/lib/internal/perf/observe.js b/lib/internal/perf/observe.js index 3e992b8f3f9576..292c0bb016db6d 100644 --- a/lib/internal/perf/observe.js +++ b/lib/internal/perf/observe.js @@ -119,8 +119,10 @@ function queuePending() { isPending = false; const pendings = ArrayFrom(kPending.values()); kPending.clear(); - for (const pending of pendings) + for (let i = 0; i < pendings.length; i++) { + const pending = pendings[i]; pending[kDispatch](); + } }); } @@ -135,7 +137,8 @@ function getObserverType(type) { } function maybeDecrementObserverCounts(entryTypes) { - for (const type of entryTypes) { + for (let i = 0; i < entryTypes.length; i++) { + const type = entryTypes[i]; const observerType = getObserverType(type); if (observerType !== undefined) { @@ -385,7 +388,9 @@ function enqueue(entry) { if (!isPerformanceEntry(entry)) throw new ERR_INVALID_ARG_TYPE('entry', 'PerformanceEntry', entry); - for (const obs of kObservers) { + const observersArray = [...kObservers]; + for (let i = 0; i < observersArray.length; i++) { + const obs = observersArray[i]; obs[kMaybeBuffer](entry); } } diff --git a/lib/internal/process/finalization.js b/lib/internal/process/finalization.js index e5f748c37642fd..095a1ef998b992 100644 --- a/lib/internal/process/finalization.js +++ b/lib/internal/process/finalization.js @@ -59,7 +59,9 @@ function createFinalization() { } function callRefsToFree(event) { - for (const ref of refs[event]) { + const refsArray = refs[event]; + for (let i = 0; i < refsArray.length; i++) { + const ref = refsArray[i]; const obj = ref.deref(); const fn = ref.fn; @@ -74,7 +76,9 @@ function createFinalization() { } function clear(ref) { - for (const event of ['exit', 'beforeExit']) { + const events = ['exit', 'beforeExit']; + for (let i = 0; i < events.length; i++) { + const event = events[i]; const index = ArrayPrototypeIndexOf(refs[event], ref); ArrayPrototypeSplice(refs[event], index, index + 1); uninstall(event); @@ -129,7 +133,9 @@ function createFinalization() { return; } registry.unregister(obj); - for (const event of ['exit', 'beforeExit']) { + const events = ['exit', 'beforeExit']; + for (let i = 0; i < events.length; i++) { + const event = events[i]; refs[event] = ArrayPrototypeFilter(refs[event], (ref) => { const _obj = ref.deref(); return _obj && _obj !== obj; diff --git a/lib/internal/process/per_thread.js b/lib/internal/process/per_thread.js index 07a94486169a7c..a21f60f074ee7a 100644 --- a/lib/internal/process/per_thread.js +++ b/lib/internal/process/per_thread.js @@ -304,7 +304,9 @@ function wrapProcessMethods(binding) { if (env !== undefined) { validateObject(env, 'env'); - for (const { 0: key, 1: value } of ObjectEntries(env)) { + const envEntries = ObjectEntries(env); + for (let i = 0; i < envEntries.length; i++) { + const { 0: key, 1: value } = envEntries[i]; if ( typeof key !== 'string' || typeof value !== 'string' || @@ -393,7 +395,9 @@ function buildAllowedFlags() { const { options, aliases } = getCLIOptionsInfo(); const allowedNodeEnvironmentFlags = []; - for (const { 0: name, 1: info } of options) { + const optionsArray = [...options]; + for (let i = 0; i < optionsArray.length; i++) { + const { 0: name, 1: info } = optionsArray[i]; if (info.envVarSettings === kAllowedInEnvvar) { ArrayPrototypePush(allowedNodeEnvironmentFlags, name); if (info.type === kBoolean) { @@ -413,7 +417,9 @@ function buildAllowedFlags() { } return options.get(to).envVarSettings === kAllowedInEnvvar; } - for (const { 0: from, 1: expansion } of aliases) { + const aliasesArray = [...aliases]; + for (let i = 0; i < aliasesArray.length; i++) { + const { 0: from, 1: expansion } = aliasesArray[i]; if (ArrayPrototypeEvery(expansion, isAccepted)) { let canonical = from; if (StringPrototypeEndsWith(canonical, '=')) diff --git a/lib/internal/process/pre_execution.js b/lib/internal/process/pre_execution.js index 480e150496d2db..34e1b853ed6612 100644 --- a/lib/internal/process/pre_execution.js +++ b/lib/internal/process/pre_execution.js @@ -492,7 +492,7 @@ function initializeDeprecations() { // TODO(addaleax): Turn into a full runtime deprecation. const utilBinding = internalBinding('util'); const types = require('internal/util/types'); - for (const name of [ + const typeNames = [ 'isArrayBuffer', 'isArrayBufferView', 'isAsyncFunction', @@ -509,7 +509,9 @@ function initializeDeprecations() { 'isTypedArray', 'isUint8Array', 'isAnyArrayBuffer', - ]) { + ]; + for (let i = 0; i < typeNames.length; i++) { + const name = typeNames[i]; utilBinding[name] = pendingDeprecation ? deprecate(types[name], 'Accessing native typechecking bindings of Node ' + @@ -586,7 +588,8 @@ function initializePermission() { '--allow-wasi', '--allow-worker', ]; - for (const flag of warnFlags) { + for (let i = 0; i < warnFlags.length; i++) { + const flag = warnFlags[i]; if (getOptionValue(flag)) { process.emitWarning( `The flag ${flag} must be used with extreme caution. ` + @@ -597,7 +600,8 @@ function initializePermission() { '--allow-fs-read', '--allow-fs-write', ]; - for (const flag of warnCommaFlags) { + for (let i = 0; i < warnCommaFlags.length; i++) { + const flag = warnCommaFlags[i]; const value = getOptionValue(flag); if (value.length === 1 && value[0].includes(',')) { process.emitWarning( @@ -613,7 +617,8 @@ function initializePermission() { const experimentalWarnFlags = [ '--allow-net', ]; - for (const flag of experimentalWarnFlags) { + for (let i = 0; i < experimentalWarnFlags.length; i++) { + const flag = experimentalWarnFlags[i]; if (getOptionValue(flag)) { process.emitWarning( `The flag ${flag} is under experimental phase.`, diff --git a/lib/internal/process/promises.js b/lib/internal/process/promises.js index 56a9f287a0a86e..5fc8bdd5102285 100644 --- a/lib/internal/process/promises.js +++ b/lib/internal/process/promises.js @@ -390,7 +390,9 @@ function processPromiseRejections() { const pending = pendingUnhandledRejections; pendingUnhandledRejections = new SafeMap(); - for (const { 0: promise, 1: promiseInfo } of pending.entries()) { + const pendingEntries = [...pending.entries()]; + for (let i = 0; i < pendingEntries.length; i++) { + const { 0: promise, 1: promiseInfo } = pendingEntries[i]; maybeUnhandledPromises.set(promise, promiseInfo); promiseInfo.warned = true; diff --git a/lib/internal/promise_hooks.js b/lib/internal/promise_hooks.js index 7a29229ddbe5cd..0f90012c187043 100644 --- a/lib/internal/promise_hooks.js +++ b/lib/internal/promise_hooks.js @@ -111,7 +111,8 @@ function createHook({ init, before, after, settled } = kEmptyObject) { if (settled) ArrayPrototypePush(hooks, onSettled(settled)); return () => { - for (const stop of hooks) { + for (let i = 0; i < hooks.length; i++) { + const stop = hooks[i]; stop(); } }; diff --git a/lib/internal/quic/quic.js b/lib/internal/quic/quic.js index 379a94c38b7d42..a2810798c481b8 100644 --- a/lib/internal/quic/quic.js +++ b/lib/internal/quic/quic.js @@ -1150,7 +1150,9 @@ class QuicSession { debug('destroying the session'); // First, forcefully and immediately destroy all open streams, if any. - for (const stream of this.#streams) { + const streamsArray = [...this.#streams]; + for (let i = 0; i < streamsArray.length; i++) { + const stream = streamsArray[i]; stream.destroy(error); } // The streams should remove themselves when they are destroyed but let's @@ -1750,7 +1752,9 @@ class QuicEndpoint { this.close(); } // Now, force all sessions to be abruptly closed... - for (const session of this.#sessions) { + const sessionsArray = [...this.#sessions]; + for (let i = 0; i < sessionsArray.length; i++) { + const session = sessionsArray[i]; session.destroy(error); } return this.closed; @@ -1953,7 +1957,8 @@ function processTlsOptions(tls, forServer) { if (certs !== undefined) { const certInputs = ArrayIsArray(certs) ? certs : [certs]; - for (const cert of certInputs) { + for (let i = 0; i < certInputs.length; i++) { + const cert = certInputs[i]; if (!isArrayBufferView(cert) && !isArrayBuffer(cert)) { throw new ERR_INVALID_ARG_TYPE('options.certs', ['ArrayBufferView', 'ArrayBuffer'], cert); @@ -1963,7 +1968,8 @@ function processTlsOptions(tls, forServer) { if (ca !== undefined) { const caInputs = ArrayIsArray(ca) ? ca : [ca]; - for (const caCert of caInputs) { + for (let i = 0; i < caInputs.length; i++) { + const caCert = caInputs[i]; if (!isArrayBufferView(caCert) && !isArrayBuffer(caCert)) { throw new ERR_INVALID_ARG_TYPE('options.ca', ['ArrayBufferView', 'ArrayBuffer'], caCert); @@ -1973,7 +1979,8 @@ function processTlsOptions(tls, forServer) { if (crl !== undefined) { const crlInputs = ArrayIsArray(crl) ? crl : [crl]; - for (const crlCert of crlInputs) { + for (let i = 0; i < crlInputs.length; i++) { + const crlCert = crlInputs[i]; if (!isArrayBufferView(crlCert) && !isArrayBuffer(crlCert)) { throw new ERR_INVALID_ARG_TYPE('options.crl', ['ArrayBufferView', 'ArrayBuffer'], crlCert); @@ -1984,7 +1991,8 @@ function processTlsOptions(tls, forServer) { const keyHandles = []; if (keys !== undefined) { const keyInputs = ArrayIsArray(keys) ? keys : [keys]; - for (const key of keyInputs) { + for (let i = 0; i < keyInputs.length; i++) { + const key = keyInputs[i]; if (isKeyObject(key)) { if (key.type !== 'private') { throw new ERR_INVALID_ARG_VALUE('options.keys', key, 'must be a private key'); diff --git a/lib/internal/repl/await.js b/lib/internal/repl/await.js index 1252df343334f5..8312159d8945d6 100644 --- a/lib/internal/repl/await.js +++ b/lib/internal/repl/await.js @@ -137,7 +137,9 @@ const visitorsWithoutAncestors = { }; const visitors = {}; -for (const nodeType of ObjectKeys(walk.base)) { +const nodeTypes = ObjectKeys(walk.base); +for (let i = 0; i < nodeTypes.length; i++) { + const nodeType = nodeTypes[i]; const callback = visitorsWithoutAncestors[nodeType] || walk.base[nodeType]; visitors[nodeType] = (node, state, c) => { const isNew = node !== state.ancestors[state.ancestors.length - 1]; diff --git a/lib/internal/source_map/source_map_cache.js b/lib/internal/source_map/source_map_cache.js index 670e53890a3099..df0eb7facfcf32 100644 --- a/lib/internal/source_map/source_map_cache.js +++ b/lib/internal/source_map/source_map_cache.js @@ -344,7 +344,9 @@ function sourceMapCacheToObject() { } const obj = { __proto__: null }; - for (const { 0: k, 1: v } of moduleSourceMapCache) { + const entries = [...moduleSourceMapCache]; + for (let i = 0; i < entries.length; i++) { + const { 0: k, 1: v } = entries[i]; obj[k] = { __proto__: null, lineLengths: v.lineLengths, diff --git a/lib/internal/streams/legacy.js b/lib/internal/streams/legacy.js index fdd95e5c25bb39..f323609ec1691e 100644 --- a/lib/internal/streams/legacy.js +++ b/lib/internal/streams/legacy.js @@ -96,7 +96,9 @@ Stream.prototype.pipe = function(dest, options) { Stream.prototype.eventNames = function eventNames() { const names = []; - for (const key of ReflectOwnKeys(this._events)) { + const keys = ReflectOwnKeys(this._events); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; if (typeof this._events[key] === 'function' || (ArrayIsArray(this._events[key]) && this._events[key].length > 0)) { names.push(key); } diff --git a/lib/internal/streams/operators.js b/lib/internal/streams/operators.js index 80a0f9f731e89a..7a08ff7ac3bd70 100644 --- a/lib/internal/streams/operators.js +++ b/lib/internal/streams/operators.js @@ -11,6 +11,7 @@ const { PromiseReject, PromiseResolve, Symbol, + SymbolAsyncIterator, } = primordials; const { AbortController, AbortSignal } = require('internal/abort_controller'); @@ -127,7 +128,10 @@ function map(fn, options) { async function pump() { try { - for await (let val of stream) { + const streamIterator = stream[SymbolAsyncIterator](); + let streamResult = await streamIterator.next(); + while (!streamResult.done) { + let val = streamResult.value; if (done) { return; } @@ -163,6 +167,7 @@ function map(fn, options) { resume = resolve; }); } + streamResult = await streamIterator.next(); } queue.push(kEof); } catch (err) { @@ -216,7 +221,9 @@ function map(fn, options) { } async function some(fn, options = undefined) { - for await (const unused of filter.call(this, fn, options)) { + const filterIterator = filter.call(this, fn, options)[SymbolAsyncIterator](); + const filterResult = await filterIterator.next(); + while (!filterResult.done) { return true; } return false; @@ -234,7 +241,10 @@ async function every(fn, options = undefined) { } async function find(fn, options) { - for await (const result of filter.call(this, fn, options)) { + const filterIterator = filter.call(this, fn, options)[SymbolAsyncIterator](); + const filterResult = await filterIterator.next(); + while (!filterResult.done) { + const result = filterResult.value; return result; } return undefined; @@ -249,8 +259,11 @@ async function forEach(fn, options) { await fn(value, options); return kEmpty; } - // eslint-disable-next-line no-unused-vars - for await (const unused of map.call(this, forEachFn, options)); + const mapIterator = map.call(this, forEachFn, options)[SymbolAsyncIterator](); + let mapResult = await mapIterator.next(); + while (!mapResult.done) { + mapResult = await mapIterator.next(); + } } function filter(fn, options) { @@ -303,7 +316,10 @@ async function reduce(reducer, initialValue, options) { } let gotAnyItemFromStream = false; try { - for await (const value of this) { + const iterator = this[SymbolAsyncIterator](); + let result = await iterator.next(); + while (!result.done) { + const value = result.value; gotAnyItemFromStream = true; if (options?.signal?.aborted) { throw new AbortError(); @@ -314,6 +330,7 @@ async function reduce(reducer, initialValue, options) { } else { initialValue = await reducer(initialValue, value, { signal }); } + result = await iterator.next(); } if (!gotAnyItemFromStream && !hasInitialValue) { throw new ReduceAwareErrMissingArgs(); @@ -333,11 +350,15 @@ async function toArray(options) { } const result = []; - for await (const val of this) { + const iterator = this[SymbolAsyncIterator](); + let iterResult = await iterator.next(); + while (!iterResult.done) { + const val = iterResult.value; if (options?.signal?.aborted) { throw new AbortError(undefined, { cause: options.signal.reason }); } ArrayPrototypePush(result, val); + iterResult = await iterator.next(); } return result; } @@ -345,8 +366,12 @@ async function toArray(options) { function flatMap(fn, options) { const values = map.call(this, fn, options); return async function* flatMap() { - for await (const val of values) { + const valuesIterator = values[SymbolAsyncIterator](); + let valuesResult = await valuesIterator.next(); + while (!valuesResult.done) { + const val = valuesResult.value; yield* val; + valuesResult = await valuesIterator.next(); } }.call(this); } @@ -377,13 +402,17 @@ function drop(number, options = undefined) { if (options?.signal?.aborted) { throw new AbortError(); } - for await (const val of this) { + const iterator = this[SymbolAsyncIterator](); + let result = await iterator.next(); + while (!result.done) { + const val = result.value; if (options?.signal?.aborted) { throw new AbortError(); } if (number-- <= 0) { yield val; } + result = await iterator.next(); } }.call(this); } @@ -401,7 +430,10 @@ function take(number, options = undefined) { if (options?.signal?.aborted) { throw new AbortError(); } - for await (const val of this) { + const iterator = this[SymbolAsyncIterator](); + let result = await iterator.next(); + while (!result.done) { + const val = result.value; if (options?.signal?.aborted) { throw new AbortError(); } @@ -413,6 +445,7 @@ function take(number, options = undefined) { if (number <= 0) { return; } + result = await iterator.next(); } }.call(this); } diff --git a/lib/internal/streams/pipeline.js b/lib/internal/streams/pipeline.js index 60d050d54e5cdb..35ee4a22a4e813 100644 --- a/lib/internal/streams/pipeline.js +++ b/lib/internal/streams/pipeline.js @@ -130,10 +130,14 @@ async function pumpToNode(iterable, writable, finish, { end }) { await wait(); } - for await (const chunk of iterable) { + const iterator = iterable[SymbolAsyncIterator](); + let result = await iterator.next(); + while (!result.done) { + const chunk = result.value; if (!writable.write(chunk)) { await wait(); } + result = await iterator.next(); } if (end) { @@ -157,9 +161,13 @@ async function pumpToWeb(readable, writable, finish, { end }) { // https://streams.spec.whatwg.org/#example-manual-write-with-backpressure const writer = writable.getWriter(); try { - for await (const chunk of readable) { + const iterator = readable[SymbolAsyncIterator](); + let result = await iterator.next(); + while (!result.done) { + const chunk = result.value; await writer.ready; writer.write(chunk).catch(() => {}); + result = await iterator.next(); } await writer.ready; @@ -244,7 +252,9 @@ function pipelineImpl(streams, callback, opts) { if (final) { if (!error) { - lastStreamCleanup.forEach((fn) => fn()); + for (let i = 0; i < lastStreamCleanup.length; i++) { + lastStreamCleanup[i](); + } } process.nextTick(callback, error, value); } diff --git a/lib/internal/streams/readable.js b/lib/internal/streams/readable.js index d4096a30994f44..677c91d95fd984 100644 --- a/lib/internal/streams/readable.js +++ b/lib/internal/streams/readable.js @@ -592,7 +592,9 @@ Readable.prototype.setEncoding = function(enc) { // Iterate over current buffer to convert already stored Buffers: let content = ''; - for (const data of state.buffer.slice(state.bufferIndex)) { + const bufferSlice = state.buffer.slice(state.bufferIndex); + for (let i = 0; i < bufferSlice.length; i++) { + const data = bufferSlice[i]; content += decoder.write(data); } state.buffer.length = 0; diff --git a/lib/internal/test_runner/mock/mock_timers.js b/lib/internal/test_runner/mock/mock_timers.js index f955bea8089deb..5429eec8b6deb8 100644 --- a/lib/internal/test_runner/mock/mock_timers.js +++ b/lib/internal/test_runner/mock/mock_timers.js @@ -15,6 +15,7 @@ const { ObjectGetOwnPropertyDescriptors, PromiseWithResolvers, Symbol, + SymbolAsyncIterator, SymbolDispose, globalThis, } = primordials; @@ -444,9 +445,11 @@ class MockTimers { options); try { - // eslint-disable-next-line no-unused-vars - for await (const event of eventIt) { + const eventIterator = eventIt[SymbolAsyncIterator](); + let eventResult = await eventIterator.next(); + while (!eventResult.done) { yield result; + eventResult = await eventIterator.next(); } } finally { abortListener?.[SymbolDispose](); diff --git a/lib/internal/test_runner/reporter/dot.js b/lib/internal/test_runner/reporter/dot.js index 45ff047bc4e5a0..c0ae3b34c7871a 100644 --- a/lib/internal/test_runner/reporter/dot.js +++ b/lib/internal/test_runner/reporter/dot.js @@ -2,6 +2,7 @@ const { ArrayPrototypePush, MathMax, + SymbolAsyncIterator, } = primordials; const colors = require('internal/util/colors'); const { formatTestReport } = require('internal/test_runner/reporter/utils'); @@ -10,7 +11,10 @@ module.exports = async function* dot(source) { let count = 0; let columns = getLineLength(); const failedTests = []; - for await (const { type, data } of source) { + const sourceIterator = source[SymbolAsyncIterator](); + let sourceResult = await sourceIterator.next(); + while (!sourceResult.done) { + const { type, data } = sourceResult.value; if (type === 'test:pass') { yield `${colors.green}.${colors.reset}`; } @@ -25,11 +29,13 @@ module.exports = async function* dot(source) { columns = getLineLength(); count = 0; } + sourceResult = await sourceIterator.next(); } yield '\n'; if (failedTests.length > 0) { yield `\n${colors.red}Failed tests:${colors.white}\n\n`; - for (const test of failedTests) { + for (let i = 0; i < failedTests.length; i++) { + const test = failedTests[i]; yield formatTestReport('test:fail', test); } } diff --git a/lib/internal/test_runner/reporter/junit.js b/lib/internal/test_runner/reporter/junit.js index 0b2efe119fedee..ec0f9dd1085948 100644 --- a/lib/internal/test_runner/reporter/junit.js +++ b/lib/internal/test_runner/reporter/junit.js @@ -10,6 +10,7 @@ const { RegExpPrototypeSymbolReplace, String, StringPrototypeRepeat, + SymbolAsyncIterator, } = primordials; const { inspectWithNoCustomRetry } = require('internal/errors'); @@ -84,7 +85,10 @@ module.exports = async function* junitReporter(source) { } } - for await (const event of source) { + const sourceIterator = source[SymbolAsyncIterator](); + let sourceResult = await sourceIterator.next(); + while (!sourceResult.done) { + const event = sourceResult.value; switch (event.type) { case 'test:start': { startTest(event); @@ -152,8 +156,10 @@ module.exports = async function* junitReporter(source) { } default: break; } + sourceResult = await sourceIterator.next(); } - for (const suite of roots) { + for (let i = 0; i < roots.length; i++) { + const suite = roots[i]; yield treeToXML(suite); } yield '\n'; diff --git a/lib/internal/test_runner/reporter/tap.js b/lib/internal/test_runner/reporter/tap.js index 0f2aa6a722f858..11489b50cc8026 100644 --- a/lib/internal/test_runner/reporter/tap.js +++ b/lib/internal/test_runner/reporter/tap.js @@ -11,6 +11,7 @@ const { SafeSet, StringPrototypeRepeat, StringPrototypeReplaceAll, + SymbolAsyncIterator, } = primordials; const { inspectWithNoCustomRetry } = require('internal/errors'); const { isError, kEmptyObject } = require('internal/util'); @@ -30,7 +31,10 @@ function lazyLoadTest() { async function * tapReporter(source) { yield `TAP version ${kDefaultTAPVersion}\n`; - for await (const { type, data } of source) { + const sourceIterator = source[SymbolAsyncIterator](); + let sourceResult = await sourceIterator.next(); + while (!sourceResult.done) { + const { type, data } = sourceResult.value; switch (type) { case 'test:fail': { yield reportTest(data.nesting, data.testNumber, 'not ok', data.name, data.skip, data.todo); @@ -62,6 +66,7 @@ async function * tapReporter(source) { yield getCoverageReport(indent(data.nesting), data.summary, '# ', '', true); break; } + sourceResult = await sourceIterator.next(); } } diff --git a/lib/internal/test_runner/reporter/v8-serializer.js b/lib/internal/test_runner/reporter/v8-serializer.js index c75bfcdac478cf..c92b198d9ec10c 100644 --- a/lib/internal/test_runner/reporter/v8-serializer.js +++ b/lib/internal/test_runner/reporter/v8-serializer.js @@ -1,6 +1,7 @@ 'use strict'; const { + SymbolAsyncIterator, TypedArrayPrototypeGetLength, } = primordials; const { DefaultSerializer } = require('v8'); @@ -13,7 +14,10 @@ module.exports = async function* v8Reporter(source) { serializer.writeHeader(); const headerLength = TypedArrayPrototypeGetLength(serializer.releaseBuffer()); - for await (const item of source) { + const sourceIterator = source[SymbolAsyncIterator](); + let sourceResult = await sourceIterator.next(); + while (!sourceResult.done) { + const item = sourceResult.value; const originalError = item.data.details?.error; if (originalError) { // Error is overridden with a serialized version, so that it can be @@ -41,5 +45,6 @@ module.exports = async function* v8Reporter(source) { serializedMessageLength & 0xFF, ], headerLength); yield serializedMessage; + sourceResult = await sourceIterator.next(); } }; diff --git a/lib/internal/tls/secure-context.js b/lib/internal/tls/secure-context.js index 84e74105fdbba9..62333760141740 100644 --- a/lib/internal/tls/secure-context.js +++ b/lib/internal/tls/secure-context.js @@ -260,7 +260,8 @@ function configSecureContext(context, options = kEmptyObject, name = 'options') if (crl !== undefined && crl !== null) { if (ArrayIsArray(crl)) { - for (const val of crl) { + for (let i = 0; i < crl.length; ++i) { + const val = crl[i]; validateKeyOrCertOption(`${name}.crl`, val); context.addCRL(val); } diff --git a/lib/internal/tls/wrap.js b/lib/internal/tls/wrap.js index ceb770ab336646..b37cd63a3a6cbf 100644 --- a/lib/internal/tls/wrap.js +++ b/lib/internal/tls/wrap.js @@ -622,7 +622,8 @@ function makeMethodProxy(name) { return ReflectApply(this._parent[name], this._parent, args); }; } -for (const proxiedMethod of proxiedMethods) { +for (let i = 0; i < proxiedMethods.length; ++i) { + const proxiedMethod = proxiedMethods[i]; tls_wrap.TLSWrap.prototype[proxiedMethod] = makeMethodProxy(proxiedMethod); } diff --git a/lib/internal/tty.js b/lib/internal/tty.js index 5363ba1f3b865f..e4030e1df054e7 100644 --- a/lib/internal/tty.js +++ b/lib/internal/tty.js @@ -181,7 +181,9 @@ function getColorDepth(env = process.env) { } if (hasOwn(env, 'CI')) { - for (const { 0: envName, 1: colors } of CI_ENVS_MAP) { + const entries = [...CI_ENVS_MAP]; + for (let i = 0; i < entries.length; ++i) { + const { 0: envName, 1: colors } = entries[i]; if (hasOwn(env, envName)) { return colors; } diff --git a/lib/internal/url.js b/lib/internal/url.js index 77f148144b391d..e2287718e2c106 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -368,7 +368,9 @@ class URLSearchParams { // with a significant cost (~40-80%). In order optimize URLSearchParams // creation duration, Node.js merges the iteration and converting // iterations into a single iteration. - for (const pair of init) { + const iterable = [...init]; + for (let i = 0; i < iterable.length; ++i) { + const pair = iterable[i]; if (pair == null) { throw new ERR_INVALID_TUPLE('Each query pair', '[name, value]'); } else if (ArrayIsArray(pair)) { @@ -390,7 +392,9 @@ class URLSearchParams { let length = 0; - for (const element of pair) { + const elementIterable = [...pair]; + for (let j = 0; j < elementIterable.length; ++j) { + const element = elementIterable[j]; length++; ArrayPrototypePush(this.#searchParams, StringPrototypeToWellFormed(`${element}`)); } diff --git a/lib/internal/util/comparisons.js b/lib/internal/util/comparisons.js index 6d36ffe17438b4..0c213cdfd872a0 100644 --- a/lib/internal/util/comparisons.js +++ b/lib/internal/util/comparisons.js @@ -423,7 +423,8 @@ function getEnumerables(val, keys) { function partialSymbolEquiv(val1, val2, keys2) { const symbolKeys = getOwnSymbols(val2); if (symbolKeys.length !== 0) { - for (const key of symbolKeys) { + for (let i = 0; i < symbolKeys.length; ++i) { + const key = symbolKeys[i]; if (hasEnumerable(val2, key)) { if (!hasEnumerable(val1, key)) { return false; @@ -462,7 +463,8 @@ function keyCheck(val1, val2, mode, memos, iterationType, keys2) { const symbolKeysA = getOwnSymbols(val1); if (symbolKeysA.length !== 0) { let count = 0; - for (const key of symbolKeysA) { + for (let i = 0; i < symbolKeysA.length; ++i) { + const key = symbolKeysA[i]; if (hasEnumerable(val1, key)) { if (!hasEnumerable(val2, key)) { return false; @@ -607,7 +609,9 @@ function partialObjectSetEquiv(array, a, b, mode, memo) { let direction = 1; let start = 0; let end = array.length - 1; - for (const val1 of a) { + const aValues = [...a]; + for (let i = 0; i < aValues.length; ++i) { + const val1 = aValues[i]; aPos++; if (!b.has(val1)) { let innerStart = start; @@ -664,7 +668,9 @@ function setObjectEquiv(array, a, b, mode, memo) { let end = array.length - 1; const comparator = mode !== kLoose ? objectComparisonStart : innerDeepEqual; const extraChecks = mode === kLoose || array.length !== a.size; - for (const val1 of a) { + const aValues = [...a]; + for (let i = 0; i < aValues.length; ++i) { + const val1 = aValues[i]; if (extraChecks) { if (typeof val1 === 'object') { if (b.has(val1)) { @@ -722,7 +728,9 @@ function setEquiv(a, b, mode, memo) { let array; const iteratorB = b.values(); - for (const val of iteratorB) { + const iteratorBValues = [...iteratorB]; + for (let i = 0; i < iteratorBValues.length; ++i) { + const val = iteratorBValues[i]; if (!a.has(val)) { if ((typeof val !== 'object' || val === null) && (mode !== kLoose || !setMightHaveLoosePrim(a, b, val))) { @@ -756,7 +764,9 @@ function partialObjectMapEquiv(array, a, b, mode, memo) { let direction = 1; let start = 0; let end = array.length - 1; - for (const { 0: key1, 1: item1 } of a) { + const aEntries = [...a]; + for (let i = 0; i < aEntries.length; ++i) { + const { 0: key1, 1: item1 } = aEntries[i]; aPos++; if (typeof key1 === 'object' && key1 !== null) { let innerStart = start; @@ -818,7 +828,9 @@ function mapObjectEquiv(array, a, b, mode, memo) { const comparator = mode !== kLoose ? objectComparisonStart : innerDeepEqual; const extraChecks = mode === kLoose || array.length !== a.size; - for (const { 0: key1, 1: item1 } of a) { + const aEntries = [...a]; + for (let i = 0; i < aEntries.length; ++i) { + const { 0: key1, 1: item1 } = aEntries[i]; if (extraChecks && (typeof key1 !== 'object' || key1 === null) && (mode !== kLoose || @@ -854,7 +866,9 @@ function mapObjectEquiv(array, a, b, mode, memo) { function mapEquiv(a, b, mode, memo) { let array; - for (const { 0: key2, 1: item2 } of b) { + const bEntries = [...b]; + for (let i = 0; i < bEntries.length; ++i) { + const { 0: key2, 1: item2 } = bEntries[i]; if (typeof key2 === 'object' && key2 !== null) { if (array === undefined) { if (a.size === 1) { diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index 78318243e65eca..d5fb6bda670f78 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -316,7 +316,9 @@ function getUserOptions(ctx, isCrossContext) { // and remove all other non-primitives, including non-primitive user options. if (isCrossContext) { ObjectSetPrototypeOf(ret, null); - for (const key of ObjectKeys(ret)) { + const retKeys = ObjectKeys(ret); + for (let i = 0; i < retKeys.length; i++) { + const key = retKeys[i]; if ((typeof ret[key] === 'object' || typeof ret[key] === 'function') && ret[key] !== null) { delete ret[key]; @@ -734,7 +736,8 @@ function addPrototypeProperties(ctx, main, obj, recurseTimes, output) { // Get all own property names and symbols. keys = ReflectOwnKeys(obj); ArrayPrototypePush(ctx.seen, main); - for (const key of keys) { + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; // Ignore the `constructor` property and keys that exist on layers above. if (key === 'constructor' || ObjectPrototypeHasOwnProperty(main, key) || @@ -1400,7 +1403,9 @@ function improveStack(stack, constructor, name, tag) { function removeDuplicateErrorKeys(ctx, keys, err, stack) { if (!ctx.showHidden && keys.length !== 0) { - for (const name of ['name', 'message', 'stack']) { + const errorProps = ['name', 'message', 'stack']; + for (let i = 0; i < errorProps.length; i++) { + const name = errorProps[i]; const index = ArrayPrototypeIndexOf(keys, name); // Only hide the property if it's a string and if it's part of the original stack if (index !== -1 && (typeof err[name] !== 'string' || StringPrototypeIncludes(stack, err[name]))) { @@ -1497,7 +1502,8 @@ function formatError(err, constructor, tag, ctx, keys) { // Highlight userland code and node modules. const workingDirectory = safeGetCWD(); let esmWorkingDirectory; - for (let line of lines) { + for (let i = 0; i < lines.length; i++) { + let line = lines[i]; const core = RegExpPrototypeExec(coreModuleRegExp, line); if (core !== null && BuiltinModule.exists(core[1])) { newStack += `\n${ctx.stylize(line, 'undefined')}`; @@ -1869,13 +1875,15 @@ function formatTypedArray(value, length, ctx, ignored, recurseTimes) { // .buffer goes last, it's not a primitive like the others. // All besides `BYTES_PER_ELEMENT` are actually getters. ctx.indentationLvl += 2; - for (const key of [ + const typedArrayKeys = [ 'BYTES_PER_ELEMENT', 'length', 'byteLength', 'byteOffset', 'buffer', - ]) { + ]; + for (let i = 0; i < typedArrayKeys.length; i++) { + const key = typedArrayKeys[i]; const str = formatValue(ctx, value[key], recurseTimes, true); ArrayPrototypePush(output, `[${key}]: ${str}`); } @@ -1891,10 +1899,12 @@ function formatSet(value, ctx, ignored, recurseTimes) { const output = []; ctx.indentationLvl += 2; let i = 0; - for (const v of value) { - if (i >= maxLength) break; - ArrayPrototypePush(output, formatValue(ctx, v, recurseTimes)); + const setIterator = value.values(); + let setResult = setIterator.next(); + while (!setResult.done && i < maxLength) { + ArrayPrototypePush(output, formatValue(ctx, setResult.value, recurseTimes)); i++; + setResult = setIterator.next(); } if (remaining > 0) { ArrayPrototypePush(output, remainingText(remaining)); @@ -1910,13 +1920,17 @@ function formatMap(value, ctx, ignored, recurseTimes) { const output = []; ctx.indentationLvl += 2; let i = 0; - for (const { 0: k, 1: v } of value) { - if (i >= maxLength) break; + const mapIterator = value.entries(); + let mapResult = mapIterator.next(); + while (!mapResult.done && i < maxLength) { + const k = mapResult.value[0]; + const v = mapResult.value[1]; ArrayPrototypePush( output, `${formatValue(ctx, k, recurseTimes)} => ${formatValue(ctx, v, recurseTimes)}`, ); i++; + mapResult = mapIterator.next(); } if (remaining > 0) { ArrayPrototypePush(output, remainingText(remaining)); @@ -2455,13 +2469,17 @@ if (internalBinding('config').hasIntl) { if (removeControlChars) str = stripVTControlCharacters(str); str = StringPrototypeNormalize(str, 'NFC'); - for (const char of new SafeStringIterator(str)) { + const iterator = new SafeStringIterator(str); + let result = iterator.next(); + while (!result.done) { + const char = result.value; const code = StringPrototypeCodePointAt(char, 0); if (isFullWidthCodePoint(code)) { width += 2; } else if (!isZeroWidthCodePoint(code)) { width++; } + result = iterator.next(); } return width; diff --git a/lib/internal/util/inspector.js b/lib/internal/util/inspector.js index 8e2ae548d86b51..ed79684f1fe867 100644 --- a/lib/internal/util/inspector.js +++ b/lib/internal/util/inspector.js @@ -83,7 +83,9 @@ function installConsoleExtensions(commandLineApi) { // Wrap a console implemented by Node.js with features from the VM inspector function wrapConsole(consoleFromNode) { const { consoleCall, console: consoleFromVM } = internalBinding('inspector'); - for (const key of ObjectKeys(consoleFromVM)) { + const consoleKeys = ObjectKeys(consoleFromVM); + for (let i = 0; i < consoleKeys.length; i++) { + const key = consoleKeys[i]; // If global console has the same method as inspector console, // then wrap these two methods into one. Native wrapper will preserve // the original stack. diff --git a/lib/internal/wasm_web_api.js b/lib/internal/wasm_web_api.js index 9c21864fa56998..ea69114c73cfdc 100644 --- a/lib/internal/wasm_web_api.js +++ b/lib/internal/wasm_web_api.js @@ -47,8 +47,15 @@ function wasmStreamingCallback(streamState, source) { // Pass all data from the response body to the WebAssembly compiler. const { body } = response; if (body != null) { - for await (const chunk of body) { - streamState.push(chunk); + const reader = body.getReader(); + try { + while (true) { + const { done, value: chunk } = await reader.read(); + if (done) break; + streamState.push(chunk); + } + } finally { + reader.releaseLock(); } } })().then(() => { diff --git a/lib/internal/watch_mode/files_watcher.js b/lib/internal/watch_mode/files_watcher.js index 9c0eb1ed817c29..653c1ed6a6297c 100644 --- a/lib/internal/watch_mode/files_watcher.js +++ b/lib/internal/watch_mode/files_watcher.js @@ -65,21 +65,33 @@ class FilesWatcher extends EventEmitter { return true; } - for (const { 0: watchedPath, 1: watcher } of this.#watchers.entries()) { + const watcherEntries = this.#watchers.entries(); + let result = watcherEntries.next(); + while (!result.done) { + const entry = result.value; + const watchedPath = entry[0]; + const watcher = entry[1]; if (watcher.recursive && isParentPath(watchedPath, path)) { return true; } + result = watcherEntries.next(); } return false; } #removeWatchedChildren(path) { - for (const { 0: watchedPath, 1: watcher } of this.#watchers.entries()) { + const watcherEntries = this.#watchers.entries(); + let result = watcherEntries.next(); + while (!result.done) { + const entry = result.value; + const watchedPath = entry[0]; + const watcher = entry[1]; if (path !== watchedPath && isParentPath(path, watchedPath)) { this.#unwatch(watcher); this.#watchers.delete(watchedPath); } + result = watcherEntries.next(); } } @@ -94,8 +106,11 @@ class FilesWatcher extends EventEmitter { } const owners = this.#dependencyOwners.get(trigger); if (owners) { - for (const owner of owners) { - this.#debounceOwners.add(owner); + const ownersIterator = owners.values(); + let result = ownersIterator.next(); + while (!result.done) { + this.#debounceOwners.add(result.value); + result = ownersIterator.next(); } } clearTimeout(this.#debounceTimer); diff --git a/lib/net.js b/lib/net.js index a391e9da30f861..3cfcebb3801db0 100644 --- a/lib/net.js +++ b/lib/net.js @@ -994,7 +994,8 @@ protoGetter('bytesWritten', function bytesWritten() { if (!writableBuffer) return undefined; - for (const el of writableBuffer) { + for (let i = 0; i < writableBuffer.length; i++) { + const el = writableBuffer[i]; bytes += el.chunk instanceof Buffer ? el.chunk.length : Buffer.byteLength(el.chunk, el.encoding); @@ -2178,7 +2179,8 @@ function isIpv6LinkLocal(ip) { function filterOnlyValidAddress(addresses) { // Return the first non IPV6 link-local address if present - for (const address of addresses) { + for (let i = 0; i < addresses.length; i++) { + const address = addresses[i]; if (!isIpv6LinkLocal(address.address)) { return address; } diff --git a/lib/repl.js b/lib/repl.js index 443971df63b0e8..6b565ed3679deb 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -43,6 +43,7 @@ 'use strict'; const { + ArrayFrom, ArrayPrototypeAt, ArrayPrototypeFilter, ArrayPrototypeFindLastIndex, @@ -854,7 +855,9 @@ function REPLServer(prompt, const prioritizedSigintQueue = new SafeSet(); self.on('SIGINT', function onSigInt() { if (prioritizedSigintQueue.size > 0) { - for (const task of prioritizedSigintQueue) { + const tasks = ArrayFrom(prioritizedSigintQueue); + for (let i = 0; i < tasks.length; i++) { + const task = tasks[i]; task(); } return; diff --git a/lib/stream/consumers.js b/lib/stream/consumers.js index 4566eff6a30f7f..4e89de246fa430 100644 --- a/lib/stream/consumers.js +++ b/lib/stream/consumers.js @@ -2,6 +2,7 @@ const { JSONParse, + SymbolAsyncIterator, } = primordials; const { @@ -28,8 +29,12 @@ const { */ async function blob(stream) { const chunks = []; - for await (const chunk of stream) - chunks.push(chunk); + const iterator = stream[SymbolAsyncIterator](); + let result = await iterator.next(); + while (!result.done) { + chunks.push(result.value); + result = await iterator.next(); + } return new Blob(chunks); } @@ -57,11 +62,15 @@ async function buffer(stream) { async function text(stream) { const dec = new TextDecoder(); let str = ''; - for await (const chunk of stream) { + const iterator = stream[SymbolAsyncIterator](); + let result = await iterator.next(); + while (!result.done) { + const chunk = result.value; if (typeof chunk === 'string') str += chunk; else str += dec.decode(chunk, { stream: true }); + result = await iterator.next(); } // Flush the streaming TextDecoder so that any pending // incomplete multibyte characters are handled. diff --git a/lib/util.js b/lib/util.js index 0884a8d518602d..44e2087f9fc949 100644 --- a/lib/util.js +++ b/lib/util.js @@ -141,7 +141,8 @@ function styleText(format, text, { validateStream = true, stream = process.stdou const formatArray = ArrayIsArray(format) ? format : [format]; const codes = []; - for (const key of formatArray) { + for (let i = 0; i < formatArray.length; i++) { + const key = formatArray[i]; if (key === 'none') continue; const formatCodes = inspect.colors[key]; // If the format is not a valid style, throw an error diff --git a/lib/zlib.js b/lib/zlib.js index 57d0bcfd84c0f9..6c110acdf29582 100644 --- a/lib/zlib.js +++ b/lib/zlib.js @@ -106,7 +106,9 @@ const codes = { Z_VERSION_ERROR: constants.Z_VERSION_ERROR, }; -for (const ckey of ObjectKeys(codes)) { +const codeKeys = ObjectKeys(codes); +for (let i = 0; i < codeKeys.length; i++) { + const ckey = codeKeys[i]; codes[codes[ckey]] = ckey; } @@ -337,7 +339,8 @@ function maxFlush(a, b) { const kFlushBuffers = []; { const dummyArrayBuffer = new ArrayBuffer(); - for (const flushFlag of kFlushFlagList) { + for (let i = 0; i < kFlushFlagList.length; i++) { + const flushFlag = kFlushFlagList[i]; kFlushBuffers[flushFlag] = Buffer.from(dummyArrayBuffer); kFlushBuffers[flushFlag][kFlushFlag] = flushFlag; } @@ -1027,7 +1030,10 @@ ObjectDefineProperties(module.exports, { // These should be considered deprecated // expose all the zlib constants -for (const { 0: key, 1: value } of ObjectEntries(constants)) { +const constantEntries = ObjectEntries(constants); +for (let i = 0; i < constantEntries.length; i++) { + const key = constantEntries[i][0]; + const value = constantEntries[i][1]; if (key.startsWith('BROTLI')) continue; ObjectDefineProperty(module.exports, key, { __proto__: null, diff --git a/tools/eslint-rules/no-unsafe-array-iteration.js b/tools/eslint-rules/no-unsafe-array-iteration.js new file mode 100644 index 00000000000000..3da38bb1df015b --- /dev/null +++ b/tools/eslint-rules/no-unsafe-array-iteration.js @@ -0,0 +1,63 @@ +/** + * @file Rule to prevent unsafe array iteration patterns like for...of loops + * which rely on user-mutable global methods (Array.prototype[Symbol.iterator]). + * Instead, traditional for loops should be used for safer iteration. + */ +'use strict'; + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +const USE_FOR_LOOP = 'Use traditional for loop instead of for...of for ' + + 'array iteration to avoid relying on user-mutable Symbol.iterator.'; + +/** + * Checks if a node represents an array-like expression + * @param {object} node - The AST node to check + * @returns {boolean} - True if the node appears to be an array + */ +function isArrayLike(node) { + // Direct array literals + if (node.type === 'ArrayExpression') { + return true; + } + // Variables/identifiers that might be arrays (we'll be conservative and flag all) + if (node.type === 'Identifier') { + return true; + } + // Member expressions like obj.array, obj['array'] + if (node.type === 'MemberExpression') { + return true; + } + // Call expressions that might return arrays + if (node.type === 'CallExpression') { + return true; + } + return false; +} + +module.exports = { + meta: { + type: 'problem', + docs: { + description: 'disallow for...of loops on arrays to prevent unsafe iteration', + category: 'Possible Errors', + }, + fixable: null, // Not auto-fixable due to complexity of conversion + schema: [], + }, + create(context) { + return { + ForOfStatement(node) { + // Check if we're iterating over something that looks like an array + if (isArrayLike(node.right)) { + context.report({ + node, + message: USE_FOR_LOOP, + }); + } + }, + }; + }, +};