From 576ccdf12585a1b1cf99628714f534a8935f4a65 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Thu, 16 Dec 2021 14:39:40 -0800 Subject: [PATCH] util: increase robustness with primordials (backported from #41212) Backport-PR-URL: https://github.com/nodejs/node/pull/44797 PR-URL: https://github.com/nodejs/node/pull/41212 Reviewed-By: Antoine du Hamel Reviewed-By: James M Snell Reviewed-By: Rich Trott --- lib/internal/util/inspect.js | 237 ++++++++++++++++++++--------------- 1 file changed, 135 insertions(+), 102 deletions(-) diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index c49fa070f70eeb..7f9d33a7f03497 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -5,9 +5,15 @@ const { ArrayIsArray, ArrayPrototypeFilter, ArrayPrototypeForEach, + ArrayPrototypeIncludes, + ArrayPrototypeIndexOf, + ArrayPrototypeJoin, + ArrayPrototypeMap, ArrayPrototypePop, ArrayPrototypePush, ArrayPrototypePushApply, + ArrayPrototypeSlice, + ArrayPrototypeSplice, ArrayPrototypeSort, ArrayPrototypeUnshift, BigIntPrototypeValueOf, @@ -16,6 +22,7 @@ const { DatePrototypeToISOString, DatePrototypeToString, ErrorPrototypeToString, + FunctionPrototypeBind, FunctionPrototypeCall, FunctionPrototypeToString, JSONStringify, @@ -32,6 +39,7 @@ const { NumberIsNaN, NumberParseFloat, NumberParseInt, + NumberPrototypeToString, NumberPrototypeValueOf, Object, ObjectAssign, @@ -47,10 +55,12 @@ const { ObjectPrototypePropertyIsEnumerable, ObjectSeal, ObjectSetPrototypeOf, + ReflectApply, ReflectOwnKeys, RegExp, RegExpPrototypeExec, RegExpPrototypeSymbolReplace, + RegExpPrototypeSymbolSplit, RegExpPrototypeToString, SafeStringIterator, SafeMap, @@ -61,12 +71,17 @@ const { StringPrototypeCharCodeAt, StringPrototypeCodePointAt, StringPrototypeIncludes, + StringPrototypeIndexOf, + StringPrototypeLastIndexOf, StringPrototypeNormalize, StringPrototypePadEnd, StringPrototypePadStart, StringPrototypeRepeat, + StringPrototypeReplaceAll, StringPrototypeSlice, StringPrototypeSplit, + StringPrototypeEndsWith, + StringPrototypeStartsWith, StringPrototypeToLowerCase, StringPrototypeTrim, StringPrototypeValueOf, @@ -367,7 +382,8 @@ ObjectDefineProperty(inspect, 'defaultOptions', { // reset code as second entry. const defaultFG = 39; const defaultBG = 49; -inspect.colors = ObjectAssign(ObjectCreate(null), { +inspect.colors = { + __proto__: null, reset: [0, 0], bold: [1, 22], dim: [2, 22], // Alias: faint @@ -413,7 +429,7 @@ inspect.colors = ObjectAssign(ObjectCreate(null), { bgMagentaBright: [105, defaultBG], bgCyanBright: [106, defaultBG], bgWhiteBright: [107, defaultBG], -}); +}; function defineColorAlias(target, alias) { ObjectDefineProperty(inspect.colors, alias, { @@ -472,7 +488,7 @@ function addQuotes(str, quotes) { function escapeFn(str) { const charCode = StringPrototypeCharCodeAt(str); - return meta.length > charCode ? meta[charCode] : `\\u${charCode.toString(16)}`; + return meta.length > charCode ? meta[charCode] : `\\u${NumberPrototypeToString(charCode, 16)}`; } // Escape control characters, single quotes and the backslash. @@ -531,7 +547,7 @@ function strEscape(str) { continue; } } - result += `${StringPrototypeSlice(str, last, i)}\\u${point.toString(16)}`; + result += `${StringPrototypeSlice(str, last, i)}\\u${NumberPrototypeToString(point, 16)}`; last = i + 1; } } @@ -800,7 +816,7 @@ function formatValue(ctx, value, recurseTimes, typedArray) { if (typeof ret !== 'string') { return formatValue(ctx, ret, recurseTimes); } - return ret.replace(/\n/g, `\n${' '.repeat(ctx.indentationLvl)}`); + return StringPrototypeReplaceAll(ret, '\n', `\n${StringPrototypeRepeat(' ', ctx.indentationLvl)}`); } } } @@ -880,8 +896,8 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { const prefix = getPrefix(constructor, tag, 'Set', `(${size})`); keys = getKeys(value, ctx.showHidden); formatter = constructor !== null ? - formatSet.bind(null, value) : - formatSet.bind(null, SetPrototypeValues(value)); + FunctionPrototypeBind(formatSet, null, value) : + FunctionPrototypeBind(formatSet, null, SetPrototypeValues(value)); if (size === 0 && keys.length === 0 && protoProps === undefined) return `${prefix}{}`; braces = [`${prefix}{`, '}']; @@ -890,8 +906,8 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { const prefix = getPrefix(constructor, tag, 'Map', `(${size})`); keys = getKeys(value, ctx.showHidden); formatter = constructor !== null ? - formatMap.bind(null, value) : - formatMap.bind(null, MapPrototypeEntries(value)); + FunctionPrototypeBind(formatMap, null, value) : + FunctionPrototypeBind(formatMap, null, MapPrototypeEntries(value)); if (size === 0 && keys.length === 0 && protoProps === undefined) return `${prefix}{}`; braces = [`${prefix}{`, '}']; @@ -911,18 +927,18 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { return `${braces[0]}]`; // Special handle the value. The original value is required below. The // bound function is required to reconstruct missing information. - formatter = formatTypedArray.bind(null, bound, size); + formatter = FunctionPrototypeBind(formatTypedArray, null, bound, size); extrasType = kArrayExtrasType; } else if (isMapIterator(value)) { keys = getKeys(value, ctx.showHidden); braces = getIteratorBraces('Map', tag); // Add braces to the formatter parameters. - formatter = formatIterator.bind(null, braces); + formatter = FunctionPrototypeBind(formatIterator, null, braces); } else if (isSetIterator(value)) { keys = getKeys(value, ctx.showHidden); braces = getIteratorBraces('Set', tag); // Add braces to the formatter parameters. - formatter = formatIterator.bind(null, braces); + formatter = FunctionPrototypeBind(formatIterator, null, braces); } else { noIterator = true; } @@ -1001,7 +1017,7 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { } else if (isModuleNamespaceObject(value)) { braces[0] = `${getPrefix(constructor, tag, 'Module')}{`; // Special handle keys for namespace objects. - formatter = formatNamespaceObject.bind(null, keys); + formatter = FunctionPrototypeBind(formatNamespaceObject, null, keys); } else if (isBoxedPrimitive(value)) { base = getBoxedBase(value, ctx, keys, constructor, tag); if (keys.length === 0 && protoProps === undefined) { @@ -1020,28 +1036,30 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { } if (recurseTimes > ctx.depth && ctx.depth !== null) { - let constructorName = getCtxStyle(value, constructor, tag).slice(0, -1); + let constructorName = StringPrototypeSlice(getCtxStyle(value, constructor, tag), 0, -1); if (constructor !== null) constructorName = `[${constructorName}]`; return ctx.stylize(constructorName, 'special'); } recurseTimes += 1; - ctx.seen.push(value); + ArrayPrototypePush(ctx.seen, value); ctx.currentDepth = recurseTimes; let output; const indentationLvl = ctx.indentationLvl; try { output = formatter(ctx, value, recurseTimes); for (i = 0; i < keys.length; i++) { - output.push( - formatProperty(ctx, value, recurseTimes, keys[i], extrasType)); + ArrayPrototypePush( + output, + formatProperty(ctx, value, recurseTimes, keys[i], extrasType), + ); } if (protoProps !== undefined) { - output.push(...protoProps); + ArrayPrototypePushApply(output, protoProps); } } catch (err) { - const constructorName = getCtxStyle(value, constructor, tag).slice(0, -1); + const constructorName = StringPrototypeSlice(getCtxStyle(value, constructor, tag), 0, -1); return handleMaxCallStackSize(ctx, err, constructorName, indentationLvl); } if (ctx.circular !== undefined) { @@ -1056,15 +1074,16 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { } } } - ctx.seen.pop(); + ArrayPrototypePop(ctx.seen); if (ctx.sorted) { const comparator = ctx.sorted === true ? undefined : ctx.sorted; if (extrasType === kObjectType) { - output = output.sort(comparator); + ArrayPrototypeSort(output, comparator); } else if (keys.length > 1) { - const sorted = output.slice(output.length - keys.length).sort(comparator); - output.splice(output.length - keys.length, keys.length, ...sorted); + const sorted = ArrayPrototypeSort(ArrayPrototypeSlice(output, output.length - keys.length), comparator); + ArrayPrototypeUnshift(sorted, output, output.length - keys.length, keys.length); + ReflectApply(ArrayPrototypeSplice, null, sorted); } } @@ -1158,13 +1177,14 @@ function getClassBase(value, constructor, tag) { function getFunctionBase(value, constructor, tag) { const stringified = FunctionPrototypeToString(value); - if (stringified.startsWith('class') && stringified.endsWith('}')) { - const slice = stringified.slice(5, -1); - const bracketIndex = slice.indexOf('{'); + if (StringPrototypeStartsWith(stringified, 'class') && StringPrototypeEndsWith(stringified, '}')) { + const slice = StringPrototypeSlice(stringified, 5, -1); + const bracketIndex = StringPrototypeIndexOf(slice, '{'); if (bracketIndex !== -1 && - (!slice.slice(0, bracketIndex).includes('(') || - // Slow path to guarantee that it's indeed a class. - classRegExp.test(slice.replace(stripCommentsRegExp)))) { + (!StringPrototypeIncludes(StringPrototypeSlice(slice, 0, bracketIndex), '(') || + // Slow path to guarantee that it's indeed a class. + RegExpPrototypeExec(classRegExp, RegExpPrototypeSymbolReplace(stripCommentsRegExp, slice)) !== null) + ) { return getClassBase(value, constructor, tag); } } @@ -1222,14 +1242,14 @@ function getStackString(error) { } function getStackFrames(ctx, err, stack) { - const frames = stack.split('\n'); + const frames = StringPrototypeSplit(stack, '\n'); // Remove stack frames identical to frames in cause. if (err.cause && isError(err.cause)) { const causeStack = getStackString(err.cause); - const causeStackStart = causeStack.indexOf('\n at'); + const causeStackStart = StringPrototypeIndexOf(causeStack, '\n at'); if (causeStackStart !== -1) { - const causeFrames = causeStack.slice(causeStackStart + 1).split('\n'); + const causeFrames = StringPrototypeSplit(StringPrototypeSlice(causeStack, causeStackStart + 1), '\n'); const { len, offset } = identicalSequenceRange(frames, causeFrames); if (len > 0) { const skipped = len - 2; @@ -1247,27 +1267,27 @@ function improveStack(stack, constructor, name, tag) { let len = name.length; if (constructor === null || - (name.endsWith('Error') && - stack.startsWith(name) && + (StringPrototypeEndsWith(name, 'Error') && + StringPrototypeStartsWith(stack, name) && (stack.length === len || stack[len] === ':' || stack[len] === '\n'))) { let fallback = 'Error'; if (constructor === null) { - const start = stack.match(/^([A-Z][a-z_ A-Z0-9[\]()-]+)(?::|\n {4}at)/) || - stack.match(/^([a-z_A-Z0-9-]*Error)$/); + const start = RegExpPrototypeExec(/^([A-Z][a-z_ A-Z0-9[\]()-]+)(?::|\n {4}at)/, stack) || + RegExpPrototypeExec(/^([a-z_A-Z0-9-]*Error)$/, stack); fallback = (start && start[1]) || ''; len = fallback.length; fallback = fallback || 'Error'; } - const prefix = getPrefix(constructor, tag, fallback).slice(0, -1); + const prefix = StringPrototypeSlice(getPrefix(constructor, tag, fallback), 0, -1); if (name !== prefix) { - if (prefix.includes(name)) { + if (StringPrototypeIncludes(prefix, name)) { if (len === 0) { stack = `${prefix}: ${stack}`; } else { - stack = `${prefix}${stack.slice(len)}`; + stack = `${prefix}${StringPrototypeSlice(stack, len)}`; } } else { - stack = `${prefix} [${name}]${stack.slice(len)}`; + stack = `${prefix} [${name}]${StringPrototypeSlice(stack, len)}`; } } } @@ -1277,10 +1297,10 @@ 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 index = keys.indexOf(name); + const index = ArrayPrototypeIndexOf(keys, name); // Only hide the property in case it's part of the original stack - if (index !== -1 && stack.includes(err[name])) { - keys.splice(index, 1); + if (index !== -1 && StringPrototypeIncludes(stack, err[name])) { + ArrayPrototypeSplice(keys, index, 1); } } } @@ -1292,33 +1312,33 @@ function markNodeModules(ctx, line) { let pos = 0; while ((nodeModule = nodeModulesRegExp.exec(line)) !== null) { // '/node_modules/'.length === 14 - tempLine += line.slice(pos, nodeModule.index + 14); + tempLine += StringPrototypeSlice(line, pos, nodeModule.index + 14); tempLine += ctx.stylize(nodeModule[1], 'module'); pos = nodeModule.index + nodeModule[0].length; } if (pos !== 0) { - line = tempLine + line.slice(pos); + line = tempLine + StringPrototypeSlice(line, pos); } return line; } function markCwd(ctx, line, workingDirectory) { - let cwdStartPos = line.indexOf(workingDirectory); + let cwdStartPos = StringPrototypeIndexOf(line, workingDirectory); let tempLine = ''; let cwdLength = workingDirectory.length; if (cwdStartPos !== -1) { - if (line.slice(cwdStartPos - 7, cwdStartPos) === 'file://') { + if (StringPrototypeSlice(line, cwdStartPos - 7, cwdStartPos) === 'file://') { cwdLength += 7; cwdStartPos -= 7; } const start = line[cwdStartPos - 1] === '(' ? cwdStartPos - 1 : cwdStartPos; - const end = start !== cwdStartPos && line.endsWith(')') ? -1 : line.length; + const end = start !== cwdStartPos && StringPrototypeEndsWith(line, ')') ? -1 : line.length; const workingDirectoryEndPos = cwdStartPos + cwdLength + 1; - const cwdSlice = line.slice(start, workingDirectoryEndPos); + const cwdSlice = StringPrototypeSlice(line, start, workingDirectoryEndPos); - tempLine += line.slice(0, start); + tempLine += StringPrototypeSlice(line, 0, start); tempLine += ctx.stylize(cwdSlice, 'undefined'); - tempLine += line.slice(workingDirectoryEndPos, end); + tempLine += StringPrototypeSlice(line, workingDirectoryEndPos, end); if (end === -1) { tempLine += ctx.stylize(')', 'undefined'); } @@ -1345,36 +1365,36 @@ function formatError(err, constructor, tag, ctx, keys) { removeDuplicateErrorKeys(ctx, keys, err, stack); if ('cause' in err && - (keys.length === 0 || !keys.includes('cause'))) { - keys.push('cause'); + (keys.length === 0 || !ArrayPrototypeIncludes(keys, 'cause'))) { + ArrayPrototypePush(keys, 'cause'); } // Print errors aggregated into AggregateError if (ArrayIsArray(err.errors) && - (keys.length === 0 || !keys.includes('errors'))) { - keys.push('errors'); + (keys.length === 0 || !ArrayPrototypeIncludes(keys, 'errors'))) { + ArrayPrototypePush(keys, 'errors'); } stack = improveStack(stack, constructor, name, tag); // Ignore the error message if it's contained in the stack. - let pos = (err.message && stack.indexOf(err.message)) || -1; + let pos = (err.message && StringPrototypeIndexOf(stack, err.message)) || -1; if (pos !== -1) pos += err.message.length; // Wrap the error in brackets in case it has no stack trace. - const stackStart = stack.indexOf('\n at', pos); + const stackStart = StringPrototypeIndexOf(stack, '\n at', pos); if (stackStart === -1) { stack = `[${stack}]`; } else { - let newStack = stack.slice(0, stackStart); - const stackFramePart = stack.slice(stackStart + 1); + let newStack = StringPrototypeSlice(stack, 0, stackStart); + const stackFramePart = StringPrototypeSlice(stack, stackStart + 1); const lines = getStackFrames(ctx, err, stackFramePart); if (ctx.colors) { // Highlight userland code and node modules. const workingDirectory = safeGetCWD(); let esmWorkingDirectory; for (let line of lines) { - const core = line.match(coreModuleRegExp); + const core = RegExpPrototypeExec(coreModuleRegExp, line); if (core !== null && BuiltinModule.exists(core[1])) { newStack += `\n${ctx.stylize(line, 'undefined')}`; } else { @@ -1394,14 +1414,14 @@ function formatError(err, constructor, tag, ctx, keys) { } } } else { - newStack += `\n${lines.join('\n')}`; + newStack += `\n${ArrayPrototypeJoin(lines, '\n')}`; } stack = newStack; } // The message and the stack have to be indented as well! if (ctx.indentationLvl !== 0) { - const indentation = ' '.repeat(ctx.indentationLvl); - stack = stack.replace(/\n/g, `\n${indentation}`); + const indentation = StringPrototypeRepeat(' ', ctx.indentationLvl); + stack = StringPrototypeReplaceAll(stack, '\n', `\n${indentation}`); } return stack; } @@ -1518,7 +1538,7 @@ function groupArrayElements(ctx, output, value) { function handleMaxCallStackSize(ctx, err, constructorName, indentationLvl) { if (isStackOverflowError(err)) { - ctx.seen.pop(); + ArrayPrototypePop(ctx.seen); ctx.indentationLvl = indentationLvl; return ctx.stylize( `[${constructorName}: Inspection interrupted ` + @@ -1533,24 +1553,24 @@ function handleMaxCallStackSize(ctx, err, constructorName, indentationLvl) { function addNumericSeparator(integerString) { let result = ''; let i = integerString.length; - const start = integerString.startsWith('-') ? 1 : 0; + const start = StringPrototypeStartsWith(integerString, '-') ? 1 : 0; for (; i >= start + 4; i -= 3) { - result = `_${integerString.slice(i - 3, i)}${result}`; + result = `_${StringPrototypeSlice(integerString, i - 3, i)}${result}`; } return i === integerString.length ? integerString : - `${integerString.slice(0, i)}${result}`; + `${StringPrototypeSlice(integerString, 0, i)}${result}`; } function addNumericSeparatorEnd(integerString) { let result = ''; let i = 0; for (; i < integerString.length - 3; i += 3) { - result += `${integerString.slice(i, i + 3)}_`; + result += `${StringPrototypeSlice(integerString, i, i + 3)}_`; } return i === 0 ? integerString : - `${result}${integerString.slice(i)}`; + `${result}${StringPrototypeSlice(integerString, i)}`; } function formatNumber(fn, number, numericSeparator) { @@ -1564,7 +1584,7 @@ function formatNumber(fn, number, numericSeparator) { const integer = MathTrunc(number); const string = String(integer); if (integer === number) { - if (!NumberIsFinite(number) || string.includes('e')) { + if (!NumberIsFinite(number) || StringPrototypeIncludes(string, 'e')) { return fn(string, 'number'); } return fn(`${addNumericSeparator(string)}`, 'number'); @@ -1575,7 +1595,9 @@ function formatNumber(fn, number, numericSeparator) { return fn(`${ addNumericSeparator(string) }.${ - addNumericSeparatorEnd(String(number).slice(string.length + 1)) + addNumericSeparatorEnd( + StringPrototypeSlice(String(number), string.length + 1) + ) }`, 'number'); } @@ -1592,7 +1614,7 @@ function formatPrimitive(fn, value, ctx) { let trailer = ''; if (value.length > ctx.maxStringLength) { const remaining = value.length - ctx.maxStringLength; - value = value.slice(0, ctx.maxStringLength); + value = StringPrototypeSlice(value, 0, ctx.maxStringLength); trailer = `... ${remaining} more character${remaining > 1 ? 's' : ''}`; } if (ctx.compact !== true && @@ -1601,10 +1623,13 @@ function formatPrimitive(fn, value, ctx) { // performance implications. value.length > kMinLineLength && value.length > ctx.breakLength - ctx.indentationLvl - 4) { - return value - .split(/(?<=\n)/) - .map((line) => fn(strEscape(line), 'string')) - .join(` +\n${' '.repeat(ctx.indentationLvl + 2)}`) + trailer; + return ArrayPrototypeJoin( + ArrayPrototypeMap( + RegExpPrototypeSymbolSplit(/(?<=\n)/, value), + (line) => fn(strEscape(line), 'string'), + ), + ` +\n${StringPrototypeRepeat(' ', ctx.indentationLvl + 2)}`, + ) + trailer; } return fn(strEscape(value), 'string') + trailer; } @@ -1633,10 +1658,10 @@ function formatNamespaceObject(keys, ctx, value, recurseTimes) { // this aligned, even though this is a hacky way of dealing with this. const tmp = { [keys[i]]: '' }; output[i] = formatProperty(ctx, tmp, recurseTimes, keys[i], kObjectType); - const pos = output[i].lastIndexOf(' '); + const pos = StringPrototypeLastIndexOf(output[i], ' '); // We have to find the last whitespace and have to replace that value as // it will be visualized as a regular string. - output[i] = output[i].slice(0, pos + 1) + + output[i] = StringPrototypeSlice(output[i], 0, pos + 1) + ctx.stylize('', 'special'); } } @@ -1657,19 +1682,19 @@ function formatSpecialArray(ctx, value, recurseTimes, maxLength, output, i) { break; } if (`${index}` !== key) { - if (!numberRegExp.test(key)) { + if (RegExpPrototypeExec(numberRegExp, key) === null) { break; } const emptyItems = tmp - index; const ending = emptyItems > 1 ? 's' : ''; const message = `<${emptyItems} empty item${ending}>`; - output.push(ctx.stylize(message, 'undefined')); + ArrayPrototypePush(output, ctx.stylize(message, 'undefined')); index = tmp; if (output.length === maxLength) { break; } } - output.push(formatProperty(ctx, value, recurseTimes, key, kArrayType)); + ArrayPrototypePush(output, formatProperty(ctx, value, recurseTimes, key, kArrayType)); index++; } const remaining = value.length - index; @@ -1677,10 +1702,10 @@ function formatSpecialArray(ctx, value, recurseTimes, maxLength, output, i) { if (remaining > 0) { const ending = remaining > 1 ? 's' : ''; const message = `<${remaining} empty item${ending}>`; - output.push(ctx.stylize(message, 'undefined')); + ArrayPrototypePush(output, ctx.stylize(message, 'undefined')); } } else if (remaining > 0) { - output.push(`... ${remaining} more item${remaining > 1 ? 's' : ''}`); + ArrayPrototypePush(output, `... ${remaining} more item${remaining > 1 ? 's' : ''}`); } return output; } @@ -1697,7 +1722,8 @@ function formatArrayBuffer(ctx, value) { let str = StringPrototypeTrim(RegExpPrototypeSymbolReplace( /(.{2})/g, hexSlice(buffer, 0, MathMin(ctx.maxArrayLength, buffer.length)), - '$1 ')); + '$1 ', + )); const remaining = buffer.length - ctx.maxArrayLength; if (remaining > 0) str += ` ... ${remaining} more byte${remaining > 1 ? 's' : ''}`; @@ -1715,10 +1741,11 @@ function formatArray(ctx, value, recurseTimes) { if (!ObjectPrototypeHasOwnProperty(value, i)) { return formatSpecialArray(ctx, value, recurseTimes, len, output, i); } - output.push(formatProperty(ctx, value, recurseTimes, i, kArrayType)); + ArrayPrototypePush(output, formatProperty(ctx, value, recurseTimes, i, kArrayType)); + } + if (remaining > 0) { + ArrayPrototypePush(output, `... ${remaining} more item${remaining > 1 ? 's' : ''}`); } - if (remaining > 0) - output.push(`... ${remaining} more item${remaining > 1 ? 's' : ''}`); return output; } @@ -1768,7 +1795,8 @@ function formatMap(value, ctx, ignored, recurseTimes) { const output = []; ctx.indentationLvl += 2; for (const { 0: k, 1: v } of value) { - output.push( + ArrayPrototypePush( + output, `${formatValue(ctx, k, recurseTimes)} => ${formatValue(ctx, v, recurseTimes)}` ); } @@ -1805,7 +1833,7 @@ function formatMapIterInner(ctx, recurseTimes, entries, state) { const len = entries.length / 2; const remaining = len - maxArrayLength; const maxLength = MathMin(maxArrayLength, len); - let output = new Array(maxLength); + const output = new Array(maxLength); let i = 0; ctx.indentationLvl += 2; if (state === kWeak) { @@ -1818,7 +1846,7 @@ function formatMapIterInner(ctx, recurseTimes, entries, state) { // retrieved ones exist, we can not reliably return the same output) if the // output is not sorted anyway. if (!ctx.sorted) - output = output.sort(); + ArrayPrototypeSort(output); } else { for (; i < maxLength; i++) { const pos = i * 2; @@ -1832,7 +1860,7 @@ function formatMapIterInner(ctx, recurseTimes, entries, state) { } ctx.indentationLvl -= 2; if (remaining > 0) { - output.push(`... ${remaining} more item${remaining > 1 ? 's' : ''}`); + ArrayPrototypePush(output, `... ${remaining} more item${remaining > 1 ? 's' : ''}`); } return output; } @@ -1855,7 +1883,7 @@ function formatIterator(braces, ctx, value, recurseTimes) { const { 0: entries, 1: isKeyValue } = previewEntries(value, true); if (isKeyValue) { // Mark entry iterators as such. - braces[0] = braces[0].replace(/ Iterator] {$/, ' Entries] {'); + braces[0] = RegExpPrototypeSymbolReplace(/ Iterator] {$/, braces[0], ' Entries] {'); return formatMapIterInner(ctx, recurseTimes, entries, kMapEntries); } @@ -1891,7 +1919,7 @@ function formatProperty(ctx, value, recurseTimes, key, type, desc, ctx.indentationLvl += diff; str = formatValue(ctx, desc.value, recurseTimes); if (diff === 3 && ctx.breakLength < getStringWidth(str, ctx.colors)) { - extra = `\n${' '.repeat(ctx.indentationLvl)}`; + extra = `\n${StringPrototypeRepeat(' ', ctx.indentationLvl)}`; } ctx.indentationLvl -= diff; } else if (desc.get !== undefined) { @@ -1939,7 +1967,10 @@ function formatProperty(ctx, value, recurseTimes, key, type, desc, name = "['__proto__']"; } else if (desc.enumerable === false) { const tmp = RegExpPrototypeSymbolReplace( - strEscapeSequencesReplacer, key, escapeFn); + strEscapeSequencesReplacer, + key, + escapeFn, + ); name = `[${tmp}]`; } else if (RegExpPrototypeExec(keyStrRegExp, key) !== null) { name = ctx.stylize(key, 'name'); @@ -2008,7 +2039,7 @@ function reduceToSingleString( braces[0].length + base.length + 10; if (isBelowBreakLength(ctx, output, start, base)) { const joinedOutput = join(output, ', '); - if (!joinedOutput.includes('\n')) { + if (!StringPrototypeIncludes(joinedOutput, '\n')) { return `${base ? `${base} ` : ''}${braces[0]} ${joinedOutput}` + ` ${braces[1]}`; } @@ -2071,8 +2102,7 @@ function hasBuiltInToString(value) { builtInObjects.has(descriptor.value.name); } -const firstErrorLine = (error) => - StringPrototypeSplit(error.message, '\n', 1)[0]; +const firstErrorLine = (error) => StringPrototypeSplit(error.message, '\n', 1)[0]; let CIRCULAR_ERROR_MESSAGE; function tryStringify(arg) { try { @@ -2081,7 +2111,9 @@ function tryStringify(arg) { // Populate the circular error message lazily if (!CIRCULAR_ERROR_MESSAGE) { try { - const a = {}; a.a = a; JSONStringify(a); + const a = {}; + a.a = a; + JSONStringify(a); } catch (circularError) { CIRCULAR_ERROR_MESSAGE = firstErrorLine(circularError); } @@ -2255,14 +2287,15 @@ if (internalBinding('config').hasIntl) { getStringWidth = function getStringWidth(str, removeControlChars = true) { let width = 0; - if (removeControlChars) + if (removeControlChars) { str = stripVTControlCharacters(str); + } for (let i = 0; i < str.length; i++) { // Try to avoid calling into C++ by first handling the ASCII portion of // the string. If it is fully ASCII, we skip the C++ part. - const code = str.charCodeAt(i); + const code = StringPrototypeCharCodeAt(str, i); if (code >= 127) { - width += icu.getStringWidth(str.slice(i).normalize('NFC')); + width += icu.getStringWidth(StringPrototypeNormalize(StringPrototypeSlice(str, i), 'NFC')); break; } width += code >= 32 ? 1 : 0; @@ -2352,7 +2385,7 @@ if (internalBinding('config').hasIntl) { function stripVTControlCharacters(str) { validateString(str, 'str'); - return str.replace(ansi, ''); + return RegExpPrototypeSymbolReplace(ansi, str, ''); } module.exports = {