• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const {
4  Array,
5  ArrayIsArray,
6  ArrayPrototypeFilter,
7  ArrayPrototypePushApply,
8  BigIntPrototypeValueOf,
9  BooleanPrototypeValueOf,
10  DatePrototypeGetTime,
11  DatePrototypeToISOString,
12  DatePrototypeToString,
13  ErrorPrototypeToString,
14  FunctionPrototypeCall,
15  FunctionPrototypeToString,
16  JSONStringify,
17  Map,
18  MapPrototypeGetSize,
19  MapPrototypeEntries,
20  MathFloor,
21  MathMax,
22  MathMin,
23  MathRound,
24  MathSqrt,
25  Number,
26  NumberIsNaN,
27  NumberParseFloat,
28  NumberParseInt,
29  NumberPrototypeValueOf,
30  Object,
31  ObjectAssign,
32  ObjectCreate,
33  ObjectDefineProperty,
34  ObjectGetOwnPropertyDescriptor,
35  ObjectGetOwnPropertyNames,
36  ObjectGetOwnPropertySymbols,
37  ObjectGetPrototypeOf,
38  ObjectIs,
39  ObjectKeys,
40  ObjectPrototypeHasOwnProperty,
41  ObjectPrototypePropertyIsEnumerable,
42  ObjectSeal,
43  ObjectSetPrototypeOf,
44  ReflectApply,
45  RegExp,
46  RegExpPrototypeExec,
47  RegExpPrototypeToString,
48  SafeSet,
49  SafeStringIterator,
50  Set,
51  SetPrototypeGetSize,
52  SetPrototypeValues,
53  String,
54  StringPrototypeValueOf,
55  SymbolPrototypeToString,
56  SymbolPrototypeValueOf,
57  SymbolIterator,
58  SymbolToStringTag,
59  TypedArrayPrototypeGetLength,
60  TypedArrayPrototypeGetSymbolToStringTag,
61  Uint8Array,
62  globalThis,
63  uncurryThis,
64} = primordials;
65
66const {
67  getOwnNonIndexProperties,
68  getPromiseDetails,
69  getProxyDetails,
70  kPending,
71  kRejected,
72  previewEntries,
73  getConstructorName: internalGetConstructorName,
74  getExternalValue,
75  propertyFilter: {
76    ALL_PROPERTIES,
77    ONLY_ENUMERABLE
78  }
79} = internalBinding('util');
80
81const {
82  customInspectSymbol,
83  isError,
84  join,
85  removeColors
86} = require('internal/util');
87
88const {
89  codes: {
90    ERR_INVALID_ARG_TYPE
91  },
92  isStackOverflowError
93} = require('internal/errors');
94
95const {
96  isAsyncFunction,
97  isGeneratorFunction,
98  isAnyArrayBuffer,
99  isArrayBuffer,
100  isArgumentsObject,
101  isBoxedPrimitive,
102  isDataView,
103  isExternal,
104  isMap,
105  isMapIterator,
106  isModuleNamespaceObject,
107  isNativeError,
108  isPromise,
109  isSet,
110  isSetIterator,
111  isWeakMap,
112  isWeakSet,
113  isRegExp,
114  isDate,
115  isTypedArray,
116  isStringObject,
117  isNumberObject,
118  isBooleanObject,
119  isBigIntObject,
120} = require('internal/util/types');
121
122const assert = require('internal/assert');
123
124const { NativeModule } = require('internal/bootstrap/loaders');
125
126let hexSlice;
127
128const builtInObjects = new SafeSet(
129  ArrayPrototypeFilter(
130    ObjectGetOwnPropertyNames(globalThis),
131    (e) => RegExpPrototypeExec(/^[A-Z][a-zA-Z0-9]+$/, e) !== null
132  )
133);
134
135// https://tc39.es/ecma262/#sec-IsHTMLDDA-internal-slot
136const isUndetectableObject = (v) => typeof v === 'undefined' && v !== undefined;
137
138// These options must stay in sync with `getUserOptions`. So if any option will
139// be added or removed, `getUserOptions` must also be updated accordingly.
140const inspectDefaultOptions = ObjectSeal({
141  showHidden: false,
142  depth: 2,
143  colors: false,
144  customInspect: true,
145  showProxy: false,
146  maxArrayLength: 100,
147  maxStringLength: Infinity,
148  breakLength: 80,
149  compact: 3,
150  sorted: false,
151  getters: false
152});
153
154const kObjectType = 0;
155const kArrayType = 1;
156const kArrayExtrasType = 2;
157
158/* eslint-disable no-control-regex */
159const strEscapeSequencesRegExp = /[\x00-\x1f\x27\x5c\x7f-\x9f]/;
160const strEscapeSequencesReplacer = /[\x00-\x1f\x27\x5c\x7f-\x9f]/g;
161const strEscapeSequencesRegExpSingle = /[\x00-\x1f\x5c\x7f-\x9f]/;
162const strEscapeSequencesReplacerSingle = /[\x00-\x1f\x5c\x7f-\x9f]/g;
163/* eslint-enable no-control-regex */
164
165const keyStrRegExp = /^[a-zA-Z_][a-zA-Z_0-9]*$/;
166const numberRegExp = /^(0|[1-9][0-9]*)$/;
167
168const coreModuleRegExp = /^    at (?:[^/\\(]+ \(|)((?<![/\\]).+)\.js:\d+:\d+\)?$/;
169const nodeModulesRegExp = /[/\\]node_modules[/\\](.+?)(?=[/\\])/g;
170
171const classRegExp = /^(\s+[^(]*?)\s*{/;
172// eslint-disable-next-line node-core/no-unescaped-regexp-dot
173const stripCommentsRegExp = /(\/\/.*?\n)|(\/\*(.|\n)*?\*\/)/g;
174
175const kMinLineLength = 16;
176
177// Constants to map the iterator state.
178const kWeak = 0;
179const kIterator = 1;
180const kMapEntries = 2;
181
182// Escaped control characters (plus the single quote and the backslash). Use
183// empty strings to fill up unused entries.
184const meta = [
185  '\\x00', '\\x01', '\\x02', '\\x03', '\\x04', '\\x05', '\\x06', '\\x07', // x07
186  '\\b', '\\t', '\\n', '\\x0B', '\\f', '\\r', '\\x0E', '\\x0F',           // x0F
187  '\\x10', '\\x11', '\\x12', '\\x13', '\\x14', '\\x15', '\\x16', '\\x17', // x17
188  '\\x18', '\\x19', '\\x1A', '\\x1B', '\\x1C', '\\x1D', '\\x1E', '\\x1F', // x1F
189  '', '', '', '', '', '', '', "\\'", '', '', '', '', '', '', '', '',      // x2F
190  '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',         // x3F
191  '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',         // x4F
192  '', '', '', '', '', '', '', '', '', '', '', '', '\\\\', '', '', '',     // x5F
193  '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',         // x6F
194  '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '\\x7F',    // x7F
195  '\\x80', '\\x81', '\\x82', '\\x83', '\\x84', '\\x85', '\\x86', '\\x87', // x87
196  '\\x88', '\\x89', '\\x8A', '\\x8B', '\\x8C', '\\x8D', '\\x8E', '\\x8F', // x8F
197  '\\x90', '\\x91', '\\x92', '\\x93', '\\x94', '\\x95', '\\x96', '\\x97', // x97
198  '\\x98', '\\x99', '\\x9A', '\\x9B', '\\x9C', '\\x9D', '\\x9E', '\\x9F', // x9F
199];
200
201// Regex used for ansi escape code splitting
202// Adopted from https://github.com/chalk/ansi-regex/blob/HEAD/index.js
203// License: MIT, authors: @sindresorhus, Qix-, arjunmehta and LitoMore
204// Matches all ansi escape code sequences in a string
205const ansiPattern = '[\\u001B\\u009B][[\\]()#;?]*' +
206  '(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)' +
207  '|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))';
208const ansi = new RegExp(ansiPattern, 'g');
209
210let getStringWidth;
211
212function getUserOptions(ctx, isCrossContext) {
213  const ret = {
214    stylize: ctx.stylize,
215    showHidden: ctx.showHidden,
216    depth: ctx.depth,
217    colors: ctx.colors,
218    customInspect: ctx.customInspect,
219    showProxy: ctx.showProxy,
220    maxArrayLength: ctx.maxArrayLength,
221    maxStringLength: ctx.maxStringLength,
222    breakLength: ctx.breakLength,
223    compact: ctx.compact,
224    sorted: ctx.sorted,
225    getters: ctx.getters,
226    ...ctx.userOptions
227  };
228
229  // Typically, the target value will be an instance of `Object`. If that is
230  // *not* the case, the object may come from another vm.Context, and we want
231  // to avoid passing it objects from this Context in that case, so we remove
232  // the prototype from the returned object itself + the `stylize()` function,
233  // and remove all other non-primitives, including non-primitive user options.
234  if (isCrossContext) {
235    ObjectSetPrototypeOf(ret, null);
236    for (const key of ObjectKeys(ret)) {
237      if ((typeof ret[key] === 'object' || typeof ret[key] === 'function') &&
238          ret[key] !== null) {
239        delete ret[key];
240      }
241    }
242    ret.stylize = ObjectSetPrototypeOf((value, flavour) => {
243      let stylized;
244      try {
245        stylized = `${ctx.stylize(value, flavour)}`;
246      } catch {}
247
248      if (typeof stylized !== 'string') return value;
249      // `stylized` is a string as it should be, which is safe to pass along.
250      return stylized;
251    }, null);
252  }
253
254  return ret;
255}
256
257/**
258 * Echos the value of any input. Tries to print the value out
259 * in the best way possible given the different types.
260 *
261 * @param {any} value The value to print out.
262 * @param {Object} opts Optional options object that alters the output.
263 */
264/* Legacy: value, showHidden, depth, colors */
265function inspect(value, opts) {
266  // Default options
267  const ctx = {
268    budget: {},
269    indentationLvl: 0,
270    seen: [],
271    currentDepth: 0,
272    stylize: stylizeNoColor,
273    showHidden: inspectDefaultOptions.showHidden,
274    depth: inspectDefaultOptions.depth,
275    colors: inspectDefaultOptions.colors,
276    customInspect: inspectDefaultOptions.customInspect,
277    showProxy: inspectDefaultOptions.showProxy,
278    maxArrayLength: inspectDefaultOptions.maxArrayLength,
279    maxStringLength: inspectDefaultOptions.maxStringLength,
280    breakLength: inspectDefaultOptions.breakLength,
281    compact: inspectDefaultOptions.compact,
282    sorted: inspectDefaultOptions.sorted,
283    getters: inspectDefaultOptions.getters
284  };
285  if (arguments.length > 1) {
286    // Legacy...
287    if (arguments.length > 2) {
288      if (arguments[2] !== undefined) {
289        ctx.depth = arguments[2];
290      }
291      if (arguments.length > 3 && arguments[3] !== undefined) {
292        ctx.colors = arguments[3];
293      }
294    }
295    // Set user-specified options
296    if (typeof opts === 'boolean') {
297      ctx.showHidden = opts;
298    } else if (opts) {
299      const optKeys = ObjectKeys(opts);
300      for (let i = 0; i < optKeys.length; ++i) {
301        const key = optKeys[i];
302        // TODO(BridgeAR): Find a solution what to do about stylize. Either make
303        // this function public or add a new API with a similar or better
304        // functionality.
305        if (
306          ObjectPrototypeHasOwnProperty(inspectDefaultOptions, key) ||
307          key === 'stylize') {
308          ctx[key] = opts[key];
309        } else if (ctx.userOptions === undefined) {
310          // This is required to pass through the actual user input.
311          ctx.userOptions = opts;
312        }
313      }
314    }
315  }
316  if (ctx.colors) ctx.stylize = stylizeWithColor;
317  if (ctx.maxArrayLength === null) ctx.maxArrayLength = Infinity;
318  if (ctx.maxStringLength === null) ctx.maxStringLength = Infinity;
319  return formatValue(ctx, value, 0);
320}
321inspect.custom = customInspectSymbol;
322
323ObjectDefineProperty(inspect, 'defaultOptions', {
324  get() {
325    return inspectDefaultOptions;
326  },
327  set(options) {
328    if (options === null || typeof options !== 'object') {
329      throw new ERR_INVALID_ARG_TYPE('options', 'Object', options);
330    }
331    return ObjectAssign(inspectDefaultOptions, options);
332  }
333});
334
335// Set Graphics Rendition https://en.wikipedia.org/wiki/ANSI_escape_code#graphics
336// Each color consists of an array with the color code as first entry and the
337// reset code as second entry.
338const defaultFG = 39;
339const defaultBG = 49;
340inspect.colors = ObjectAssign(ObjectCreate(null), {
341  reset: [0, 0],
342  bold: [1, 22],
343  dim: [2, 22], // Alias: faint
344  italic: [3, 23],
345  underline: [4, 24],
346  blink: [5, 25],
347  // Swap foreground and background colors
348  inverse: [7, 27], // Alias: swapcolors, swapColors
349  hidden: [8, 28], // Alias: conceal
350  strikethrough: [9, 29], // Alias: strikeThrough, crossedout, crossedOut
351  doubleunderline: [21, 24], // Alias: doubleUnderline
352  black: [30, defaultFG],
353  red: [31, defaultFG],
354  green: [32, defaultFG],
355  yellow: [33, defaultFG],
356  blue: [34, defaultFG],
357  magenta: [35, defaultFG],
358  cyan: [36, defaultFG],
359  white: [37, defaultFG],
360  bgBlack: [40, defaultBG],
361  bgRed: [41, defaultBG],
362  bgGreen: [42, defaultBG],
363  bgYellow: [43, defaultBG],
364  bgBlue: [44, defaultBG],
365  bgMagenta: [45, defaultBG],
366  bgCyan: [46, defaultBG],
367  bgWhite: [47, defaultBG],
368  framed: [51, 54],
369  overlined: [53, 55],
370  gray: [90, defaultFG], // Alias: grey, blackBright
371  redBright: [91, defaultFG],
372  greenBright: [92, defaultFG],
373  yellowBright: [93, defaultFG],
374  blueBright: [94, defaultFG],
375  magentaBright: [95, defaultFG],
376  cyanBright: [96, defaultFG],
377  whiteBright: [97, defaultFG],
378  bgGray: [100, defaultBG], // Alias: bgGrey, bgBlackBright
379  bgRedBright: [101, defaultBG],
380  bgGreenBright: [102, defaultBG],
381  bgYellowBright: [103, defaultBG],
382  bgBlueBright: [104, defaultBG],
383  bgMagentaBright: [105, defaultBG],
384  bgCyanBright: [106, defaultBG],
385  bgWhiteBright: [107, defaultBG],
386});
387
388function defineColorAlias(target, alias) {
389  ObjectDefineProperty(inspect.colors, alias, {
390    get() {
391      return this[target];
392    },
393    set(value) {
394      this[target] = value;
395    },
396    configurable: true,
397    enumerable: false
398  });
399}
400
401defineColorAlias('gray', 'grey');
402defineColorAlias('gray', 'blackBright');
403defineColorAlias('bgGray', 'bgGrey');
404defineColorAlias('bgGray', 'bgBlackBright');
405defineColorAlias('dim', 'faint');
406defineColorAlias('strikethrough', 'crossedout');
407defineColorAlias('strikethrough', 'strikeThrough');
408defineColorAlias('strikethrough', 'crossedOut');
409defineColorAlias('hidden', 'conceal');
410defineColorAlias('inverse', 'swapColors');
411defineColorAlias('inverse', 'swapcolors');
412defineColorAlias('doubleunderline', 'doubleUnderline');
413
414// TODO(BridgeAR): Add function style support for more complex styles.
415// Don't use 'blue' not visible on cmd.exe
416inspect.styles = ObjectAssign(ObjectCreate(null), {
417  special: 'cyan',
418  number: 'yellow',
419  bigint: 'yellow',
420  boolean: 'yellow',
421  undefined: 'grey',
422  null: 'bold',
423  string: 'green',
424  symbol: 'green',
425  date: 'magenta',
426  // "name": intentionally not styling
427  // TODO(BridgeAR): Highlight regular expressions properly.
428  regexp: 'red',
429  module: 'underline'
430});
431
432function addQuotes(str, quotes) {
433  if (quotes === -1) {
434    return `"${str}"`;
435  }
436  if (quotes === -2) {
437    return `\`${str}\``;
438  }
439  return `'${str}'`;
440}
441
442const escapeFn = (str) => meta[str.charCodeAt(0)];
443
444// Escape control characters, single quotes and the backslash.
445// This is similar to JSON stringify escaping.
446function strEscape(str) {
447  let escapeTest = strEscapeSequencesRegExp;
448  let escapeReplace = strEscapeSequencesReplacer;
449  let singleQuote = 39;
450
451  // Check for double quotes. If not present, do not escape single quotes and
452  // instead wrap the text in double quotes. If double quotes exist, check for
453  // backticks. If they do not exist, use those as fallback instead of the
454  // double quotes.
455  if (str.includes("'")) {
456    // This invalidates the charCode and therefore can not be matched for
457    // anymore.
458    if (!str.includes('"')) {
459      singleQuote = -1;
460    } else if (!str.includes('`') && !str.includes('${')) {
461      singleQuote = -2;
462    }
463    if (singleQuote !== 39) {
464      escapeTest = strEscapeSequencesRegExpSingle;
465      escapeReplace = strEscapeSequencesReplacerSingle;
466    }
467  }
468
469  // Some magic numbers that worked out fine while benchmarking with v8 6.0
470  if (str.length < 5000 && !escapeTest.test(str))
471    return addQuotes(str, singleQuote);
472  if (str.length > 100) {
473    str = str.replace(escapeReplace, escapeFn);
474    return addQuotes(str, singleQuote);
475  }
476
477  let result = '';
478  let last = 0;
479  const lastIndex = str.length;
480  for (let i = 0; i < lastIndex; i++) {
481    const point = str.charCodeAt(i);
482    if (point === singleQuote ||
483        point === 92 ||
484        point < 32 ||
485        (point > 126 && point < 160)) {
486      if (last === i) {
487        result += meta[point];
488      } else {
489        result += `${str.slice(last, i)}${meta[point]}`;
490      }
491      last = i + 1;
492    }
493  }
494
495  if (last !== lastIndex) {
496    result += str.slice(last);
497  }
498  return addQuotes(result, singleQuote);
499}
500
501function stylizeWithColor(str, styleType) {
502  const style = inspect.styles[styleType];
503  if (style !== undefined) {
504    const color = inspect.colors[style];
505    if (color !== undefined)
506      return `\u001b[${color[0]}m${str}\u001b[${color[1]}m`;
507  }
508  return str;
509}
510
511function stylizeNoColor(str) {
512  return str;
513}
514
515// Return a new empty array to push in the results of the default formatter.
516function getEmptyFormatArray() {
517  return [];
518}
519
520function isInstanceof(object, proto) {
521  try {
522    return object instanceof proto;
523  } catch {
524    return false;
525  }
526}
527
528function getConstructorName(obj, ctx, recurseTimes, protoProps) {
529  let firstProto;
530  const tmp = obj;
531  while (obj || isUndetectableObject(obj)) {
532    const descriptor = ObjectGetOwnPropertyDescriptor(obj, 'constructor');
533    if (descriptor !== undefined &&
534        typeof descriptor.value === 'function' &&
535        descriptor.value.name !== '' &&
536        isInstanceof(tmp, descriptor.value)) {
537      if (protoProps !== undefined &&
538         (firstProto !== obj ||
539         !builtInObjects.has(descriptor.value.name))) {
540        addPrototypeProperties(
541          ctx, tmp, firstProto || tmp, recurseTimes, protoProps);
542      }
543      return descriptor.value.name;
544    }
545
546    obj = ObjectGetPrototypeOf(obj);
547    if (firstProto === undefined) {
548      firstProto = obj;
549    }
550  }
551
552  if (firstProto === null) {
553    return null;
554  }
555
556  const res = internalGetConstructorName(tmp);
557
558  if (recurseTimes > ctx.depth && ctx.depth !== null) {
559    return `${res} <Complex prototype>`;
560  }
561
562  const protoConstr = getConstructorName(
563    firstProto, ctx, recurseTimes + 1, protoProps);
564
565  if (protoConstr === null) {
566    return `${res} <${inspect(firstProto, {
567      ...ctx,
568      customInspect: false,
569      depth: -1
570    })}>`;
571  }
572
573  return `${res} <${protoConstr}>`;
574}
575
576// This function has the side effect of adding prototype properties to the
577// `output` argument (which is an array). This is intended to highlight user
578// defined prototype properties.
579function addPrototypeProperties(ctx, main, obj, recurseTimes, output) {
580  let depth = 0;
581  let keys;
582  let keySet;
583  do {
584    if (depth !== 0 || main === obj) {
585      obj = ObjectGetPrototypeOf(obj);
586      // Stop as soon as a null prototype is encountered.
587      if (obj === null) {
588        return;
589      }
590      // Stop as soon as a built-in object type is detected.
591      const descriptor = ObjectGetOwnPropertyDescriptor(obj, 'constructor');
592      if (descriptor !== undefined &&
593          typeof descriptor.value === 'function' &&
594          builtInObjects.has(descriptor.value.name)) {
595        return;
596      }
597    }
598
599    if (depth === 0) {
600      keySet = new Set();
601    } else {
602      keys.forEach((key) => keySet.add(key));
603    }
604    // Get all own property names and symbols.
605    keys = ObjectGetOwnPropertyNames(obj);
606    const symbols = ObjectGetOwnPropertySymbols(obj);
607    if (symbols.length !== 0) {
608      keys.push(...symbols);
609    }
610    for (const key of keys) {
611      // Ignore the `constructor` property and keys that exist on layers above.
612      if (key === 'constructor' ||
613          ObjectPrototypeHasOwnProperty(main, key) ||
614          (depth !== 0 && keySet.has(key))) {
615        continue;
616      }
617      const desc = ObjectGetOwnPropertyDescriptor(obj, key);
618      if (typeof desc.value === 'function') {
619        continue;
620      }
621      const value = formatProperty(
622        ctx, obj, recurseTimes, key, kObjectType, desc, main);
623      if (ctx.colors) {
624        // Faint!
625        output.push(`\u001b[2m${value}\u001b[22m`);
626      } else {
627        output.push(value);
628      }
629    }
630  // Limit the inspection to up to three prototype layers. Using `recurseTimes`
631  // is not a good choice here, because it's as if the properties are declared
632  // on the current object from the users perspective.
633  } while (++depth !== 3);
634}
635
636function getPrefix(constructor, tag, fallback, size = '') {
637  if (constructor === null) {
638    if (tag !== '' && fallback !== tag) {
639      return `[${fallback}${size}: null prototype] [${tag}] `;
640    }
641    return `[${fallback}${size}: null prototype] `;
642  }
643
644  if (tag !== '' && constructor !== tag) {
645    return `${constructor}${size} [${tag}] `;
646  }
647  return `${constructor}${size} `;
648}
649
650// Look up the keys of the object.
651function getKeys(value, showHidden) {
652  let keys;
653  const symbols = ObjectGetOwnPropertySymbols(value);
654  if (showHidden) {
655    keys = ObjectGetOwnPropertyNames(value);
656    if (symbols.length !== 0)
657      ArrayPrototypePushApply(keys, symbols);
658  } else {
659    // This might throw if `value` is a Module Namespace Object from an
660    // unevaluated module, but we don't want to perform the actual type
661    // check because it's expensive.
662    // TODO(devsnek): track https://github.com/tc39/ecma262/issues/1209
663    // and modify this logic as needed.
664    try {
665      keys = ObjectKeys(value);
666    } catch (err) {
667      assert(isNativeError(err) && err.name === 'ReferenceError' &&
668             isModuleNamespaceObject(value));
669      keys = ObjectGetOwnPropertyNames(value);
670    }
671    if (symbols.length !== 0) {
672      const filter = (key) => ObjectPrototypePropertyIsEnumerable(value, key);
673      ArrayPrototypePushApply(keys, ArrayPrototypeFilter(symbols, filter));
674    }
675  }
676  return keys;
677}
678
679function getCtxStyle(value, constructor, tag) {
680  let fallback = '';
681  if (constructor === null) {
682    fallback = internalGetConstructorName(value);
683    if (fallback === tag) {
684      fallback = 'Object';
685    }
686  }
687  return getPrefix(constructor, tag, fallback);
688}
689
690function formatProxy(ctx, proxy, recurseTimes) {
691  if (recurseTimes > ctx.depth && ctx.depth !== null) {
692    return ctx.stylize('Proxy [Array]', 'special');
693  }
694  recurseTimes += 1;
695  ctx.indentationLvl += 2;
696  const res = [
697    formatValue(ctx, proxy[0], recurseTimes),
698    formatValue(ctx, proxy[1], recurseTimes),
699  ];
700  ctx.indentationLvl -= 2;
701  return reduceToSingleString(
702    ctx, res, '', ['Proxy [', ']'], kArrayExtrasType, recurseTimes);
703}
704
705// Note: using `formatValue` directly requires the indentation level to be
706// corrected by setting `ctx.indentationLvL += diff` and then to decrease the
707// value afterwards again.
708function formatValue(ctx, value, recurseTimes, typedArray) {
709  // Primitive types cannot have properties.
710  if (typeof value !== 'object' &&
711      typeof value !== 'function' &&
712      !isUndetectableObject(value)) {
713    return formatPrimitive(ctx.stylize, value, ctx);
714  }
715  if (value === null) {
716    return ctx.stylize('null', 'null');
717  }
718
719  // Memorize the context for custom inspection on proxies.
720  const context = value;
721  // Always check for proxies to prevent side effects and to prevent triggering
722  // any proxy handlers.
723  const proxy = getProxyDetails(value, !!ctx.showProxy);
724  if (proxy !== undefined) {
725    if (ctx.showProxy) {
726      return formatProxy(ctx, proxy, recurseTimes);
727    }
728    value = proxy;
729  }
730
731  // Provide a hook for user-specified inspect functions.
732  // Check that value is an object with an inspect function on it.
733  if (ctx.customInspect) {
734    const maybeCustom = value[customInspectSymbol];
735    if (typeof maybeCustom === 'function' &&
736        // Filter out the util module, its inspect function is special.
737        maybeCustom !== inspect &&
738        // Also filter out any prototype objects using the circular check.
739        !(value.constructor && value.constructor.prototype === value)) {
740      // This makes sure the recurseTimes are reported as before while using
741      // a counter internally.
742      const depth = ctx.depth === null ? null : ctx.depth - recurseTimes;
743      const isCrossContext =
744        proxy !== undefined || !(context instanceof Object);
745      const ret = FunctionPrototypeCall(
746        maybeCustom, context, depth, getUserOptions(ctx, isCrossContext));
747      // If the custom inspection method returned `this`, don't go into
748      // infinite recursion.
749      if (ret !== context) {
750        if (typeof ret !== 'string') {
751          return formatValue(ctx, ret, recurseTimes);
752        }
753        return ret.replace(/\n/g, `\n${' '.repeat(ctx.indentationLvl)}`);
754      }
755    }
756  }
757
758  // Using an array here is actually better for the average case than using
759  // a Set. `seen` will only check for the depth and will never grow too large.
760  if (ctx.seen.includes(value)) {
761    let index = 1;
762    if (ctx.circular === undefined) {
763      ctx.circular = new Map([[value, index]]);
764    } else {
765      index = ctx.circular.get(value);
766      if (index === undefined) {
767        index = ctx.circular.size + 1;
768        ctx.circular.set(value, index);
769      }
770    }
771    return ctx.stylize(`[Circular *${index}]`, 'special');
772  }
773
774  return formatRaw(ctx, value, recurseTimes, typedArray);
775}
776
777function formatRaw(ctx, value, recurseTimes, typedArray) {
778  let keys;
779  let protoProps;
780  if (ctx.showHidden && (recurseTimes <= ctx.depth || ctx.depth === null)) {
781    protoProps = [];
782  }
783
784  const constructor = getConstructorName(value, ctx, recurseTimes, protoProps);
785  // Reset the variable to check for this later on.
786  if (protoProps !== undefined && protoProps.length === 0) {
787    protoProps = undefined;
788  }
789
790  let tag = value[SymbolToStringTag];
791  // Only list the tag in case it's non-enumerable / not an own property.
792  // Otherwise we'd print this twice.
793  if (typeof tag !== 'string' ||
794      (tag !== '' &&
795      (ctx.showHidden ?
796        ObjectPrototypeHasOwnProperty :
797        ObjectPrototypePropertyIsEnumerable)(
798        value, SymbolToStringTag
799      ))) {
800    tag = '';
801  }
802  let base = '';
803  let formatter = getEmptyFormatArray;
804  let braces;
805  let noIterator = true;
806  let i = 0;
807  const filter = ctx.showHidden ? ALL_PROPERTIES : ONLY_ENUMERABLE;
808
809  let extrasType = kObjectType;
810
811  // Iterators and the rest are split to reduce checks.
812  // We have to check all values in case the constructor is set to null.
813  // Otherwise it would not possible to identify all types properly.
814  if (value[SymbolIterator] || constructor === null) {
815    noIterator = false;
816    if (ArrayIsArray(value)) {
817      // Only set the constructor for non ordinary ("Array [...]") arrays.
818      const prefix = (constructor !== 'Array' || tag !== '') ?
819        getPrefix(constructor, tag, 'Array', `(${value.length})`) :
820        '';
821      keys = getOwnNonIndexProperties(value, filter);
822      braces = [`${prefix}[`, ']'];
823      if (value.length === 0 && keys.length === 0 && protoProps === undefined)
824        return `${braces[0]}]`;
825      extrasType = kArrayExtrasType;
826      formatter = formatArray;
827    } else if (isSet(value)) {
828      const size = SetPrototypeGetSize(value);
829      const prefix = getPrefix(constructor, tag, 'Set', `(${size})`);
830      keys = getKeys(value, ctx.showHidden);
831      formatter = constructor !== null ?
832        formatSet.bind(null, value) :
833        formatSet.bind(null, SetPrototypeValues(value));
834      if (size === 0 && keys.length === 0 && protoProps === undefined)
835        return `${prefix}{}`;
836      braces = [`${prefix}{`, '}'];
837    } else if (isMap(value)) {
838      const size = MapPrototypeGetSize(value);
839      const prefix = getPrefix(constructor, tag, 'Map', `(${size})`);
840      keys = getKeys(value, ctx.showHidden);
841      formatter = constructor !== null ?
842        formatMap.bind(null, value) :
843        formatMap.bind(null, MapPrototypeEntries(value));
844      if (size === 0 && keys.length === 0 && protoProps === undefined)
845        return `${prefix}{}`;
846      braces = [`${prefix}{`, '}'];
847    } else if (isTypedArray(value)) {
848      keys = getOwnNonIndexProperties(value, filter);
849      let bound = value;
850      let fallback = '';
851      if (constructor === null) {
852        fallback = TypedArrayPrototypeGetSymbolToStringTag(value);
853        // Reconstruct the array information.
854        bound = new primordials[fallback](value);
855      }
856      const size = TypedArrayPrototypeGetLength(value);
857      const prefix = getPrefix(constructor, tag, fallback, `(${size})`);
858      braces = [`${prefix}[`, ']'];
859      if (value.length === 0 && keys.length === 0 && !ctx.showHidden)
860        return `${braces[0]}]`;
861      // Special handle the value. The original value is required below. The
862      // bound function is required to reconstruct missing information.
863      formatter = formatTypedArray.bind(null, bound, size);
864      extrasType = kArrayExtrasType;
865    } else if (isMapIterator(value)) {
866      keys = getKeys(value, ctx.showHidden);
867      braces = getIteratorBraces('Map', tag);
868      // Add braces to the formatter parameters.
869      formatter = formatIterator.bind(null, braces);
870    } else if (isSetIterator(value)) {
871      keys = getKeys(value, ctx.showHidden);
872      braces = getIteratorBraces('Set', tag);
873      // Add braces to the formatter parameters.
874      formatter = formatIterator.bind(null, braces);
875    } else {
876      noIterator = true;
877    }
878  }
879  if (noIterator) {
880    keys = getKeys(value, ctx.showHidden);
881    braces = ['{', '}'];
882    if (constructor === 'Object') {
883      if (isArgumentsObject(value)) {
884        braces[0] = '[Arguments] {';
885      } else if (tag !== '') {
886        braces[0] = `${getPrefix(constructor, tag, 'Object')}{`;
887      }
888      if (keys.length === 0 && protoProps === undefined) {
889        return `${braces[0]}}`;
890      }
891    } else if (typeof value === 'function') {
892      base = getFunctionBase(value, constructor, tag);
893      if (keys.length === 0 && protoProps === undefined)
894        return ctx.stylize(base, 'special');
895    } else if (isRegExp(value)) {
896      // Make RegExps say that they are RegExps
897      base = RegExpPrototypeToString(
898        constructor !== null ? value : new RegExp(value)
899      );
900      const prefix = getPrefix(constructor, tag, 'RegExp');
901      if (prefix !== 'RegExp ')
902        base = `${prefix}${base}`;
903      if ((keys.length === 0 && protoProps === undefined) ||
904          (recurseTimes > ctx.depth && ctx.depth !== null)) {
905        return ctx.stylize(base, 'regexp');
906      }
907    } else if (isDate(value)) {
908      // Make dates with properties first say the date
909      base = NumberIsNaN(DatePrototypeGetTime(value)) ?
910        DatePrototypeToString(value) :
911        DatePrototypeToISOString(value);
912      const prefix = getPrefix(constructor, tag, 'Date');
913      if (prefix !== 'Date ')
914        base = `${prefix}${base}`;
915      if (keys.length === 0 && protoProps === undefined) {
916        return ctx.stylize(base, 'date');
917      }
918    } else if (isError(value)) {
919      base = formatError(value, constructor, tag, ctx, keys);
920      if (keys.length === 0 && protoProps === undefined)
921        return base;
922    } else if (isAnyArrayBuffer(value)) {
923      // Fast path for ArrayBuffer and SharedArrayBuffer.
924      // Can't do the same for DataView because it has a non-primitive
925      // .buffer property that we need to recurse for.
926      const arrayType = isArrayBuffer(value) ? 'ArrayBuffer' :
927        'SharedArrayBuffer';
928      const prefix = getPrefix(constructor, tag, arrayType);
929      if (typedArray === undefined) {
930        formatter = formatArrayBuffer;
931      } else if (keys.length === 0 && protoProps === undefined) {
932        return prefix +
933              `{ byteLength: ${formatNumber(ctx.stylize, value.byteLength)} }`;
934      }
935      braces[0] = `${prefix}{`;
936      keys.unshift('byteLength');
937    } else if (isDataView(value)) {
938      braces[0] = `${getPrefix(constructor, tag, 'DataView')}{`;
939      // .buffer goes last, it's not a primitive like the others.
940      keys.unshift('byteLength', 'byteOffset', 'buffer');
941    } else if (isPromise(value)) {
942      braces[0] = `${getPrefix(constructor, tag, 'Promise')}{`;
943      formatter = formatPromise;
944    } else if (isWeakSet(value)) {
945      braces[0] = `${getPrefix(constructor, tag, 'WeakSet')}{`;
946      formatter = ctx.showHidden ? formatWeakSet : formatWeakCollection;
947    } else if (isWeakMap(value)) {
948      braces[0] = `${getPrefix(constructor, tag, 'WeakMap')}{`;
949      formatter = ctx.showHidden ? formatWeakMap : formatWeakCollection;
950    } else if (isModuleNamespaceObject(value)) {
951      braces[0] = `${getPrefix(constructor, tag, 'Module')}{`;
952      // Special handle keys for namespace objects.
953      formatter = formatNamespaceObject.bind(null, keys);
954    } else if (isBoxedPrimitive(value)) {
955      base = getBoxedBase(value, ctx, keys, constructor, tag);
956      if (keys.length === 0 && protoProps === undefined) {
957        return base;
958      }
959    } else {
960      if (keys.length === 0 && protoProps === undefined) {
961        if (isExternal(value)) {
962          const address = getExternalValue(value).toString(16);
963          return ctx.stylize(`[External: ${address}]`, 'special');
964        }
965        return `${getCtxStyle(value, constructor, tag)}{}`;
966      }
967      braces[0] = `${getCtxStyle(value, constructor, tag)}{`;
968    }
969  }
970
971  if (recurseTimes > ctx.depth && ctx.depth !== null) {
972    let constructorName = getCtxStyle(value, constructor, tag).slice(0, -1);
973    if (constructor !== null)
974      constructorName = `[${constructorName}]`;
975    return ctx.stylize(constructorName, 'special');
976  }
977  recurseTimes += 1;
978
979  ctx.seen.push(value);
980  ctx.currentDepth = recurseTimes;
981  let output;
982  const indentationLvl = ctx.indentationLvl;
983  try {
984    output = formatter(ctx, value, recurseTimes);
985    for (i = 0; i < keys.length; i++) {
986      output.push(
987        formatProperty(ctx, value, recurseTimes, keys[i], extrasType));
988    }
989    if (protoProps !== undefined) {
990      output.push(...protoProps);
991    }
992  } catch (err) {
993    const constructorName = getCtxStyle(value, constructor, tag).slice(0, -1);
994    return handleMaxCallStackSize(ctx, err, constructorName, indentationLvl);
995  }
996  if (ctx.circular !== undefined) {
997    const index = ctx.circular.get(value);
998    if (index !== undefined) {
999      const reference = ctx.stylize(`<ref *${index}>`, 'special');
1000      // Add reference always to the very beginning of the output.
1001      if (ctx.compact !== true) {
1002        base = base === '' ? reference : `${reference} ${base}`;
1003      } else {
1004        braces[0] = `${reference} ${braces[0]}`;
1005      }
1006    }
1007  }
1008  ctx.seen.pop();
1009
1010  if (ctx.sorted) {
1011    const comparator = ctx.sorted === true ? undefined : ctx.sorted;
1012    if (extrasType === kObjectType) {
1013      output = output.sort(comparator);
1014    } else if (keys.length > 1) {
1015      const sorted = output.slice(output.length - keys.length).sort(comparator);
1016      output.splice(output.length - keys.length, keys.length, ...sorted);
1017    }
1018  }
1019
1020  const res = reduceToSingleString(
1021    ctx, output, base, braces, extrasType, recurseTimes, value);
1022  const budget = ctx.budget[ctx.indentationLvl] || 0;
1023  const newLength = budget + res.length;
1024  ctx.budget[ctx.indentationLvl] = newLength;
1025  // If any indentationLvl exceeds this limit, limit further inspecting to the
1026  // minimum. Otherwise the recursive algorithm might continue inspecting the
1027  // object even though the maximum string size (~2 ** 28 on 32 bit systems and
1028  // ~2 ** 30 on 64 bit systems) exceeded. The actual output is not limited at
1029  // exactly 2 ** 27 but a bit higher. This depends on the object shape.
1030  // This limit also makes sure that huge objects don't block the event loop
1031  // significantly.
1032  if (newLength > 2 ** 27) {
1033    ctx.depth = -1;
1034  }
1035  return res;
1036}
1037
1038function getIteratorBraces(type, tag) {
1039  if (tag !== `${type} Iterator`) {
1040    if (tag !== '')
1041      tag += '] [';
1042    tag += `${type} Iterator`;
1043  }
1044  return [`[${tag}] {`, '}'];
1045}
1046
1047function getBoxedBase(value, ctx, keys, constructor, tag) {
1048  let fn;
1049  let type;
1050  if (isNumberObject(value)) {
1051    fn = NumberPrototypeValueOf;
1052    type = 'Number';
1053  } else if (isStringObject(value)) {
1054    fn = StringPrototypeValueOf;
1055    type = 'String';
1056    // For boxed Strings, we have to remove the 0-n indexed entries,
1057    // since they just noisy up the output and are redundant
1058    // Make boxed primitive Strings look like such
1059    keys.splice(0, value.length);
1060  } else if (isBooleanObject(value)) {
1061    fn = BooleanPrototypeValueOf;
1062    type = 'Boolean';
1063  } else if (isBigIntObject(value)) {
1064    fn = BigIntPrototypeValueOf;
1065    type = 'BigInt';
1066  } else {
1067    fn = SymbolPrototypeValueOf;
1068    type = 'Symbol';
1069  }
1070  let base = `[${type}`;
1071  if (type !== constructor) {
1072    if (constructor === null) {
1073      base += ' (null prototype)';
1074    } else {
1075      base += ` (${constructor})`;
1076    }
1077  }
1078  base += `: ${formatPrimitive(stylizeNoColor, fn(value), ctx)}]`;
1079  if (tag !== '' && tag !== constructor) {
1080    base += ` [${tag}]`;
1081  }
1082  if (keys.length !== 0 || ctx.stylize === stylizeNoColor)
1083    return base;
1084  return ctx.stylize(base, type.toLowerCase());
1085}
1086
1087function getClassBase(value, constructor, tag) {
1088  const hasName = ObjectPrototypeHasOwnProperty(value, 'name');
1089  const name = (hasName && value.name) || '(anonymous)';
1090  let base = `class ${name}`;
1091  if (constructor !== 'Function' && constructor !== null) {
1092    base += ` [${constructor}]`;
1093  }
1094  if (tag !== '' && constructor !== tag) {
1095    base += ` [${tag}]`;
1096  }
1097  if (constructor !== null) {
1098    const superName = ObjectGetPrototypeOf(value).name;
1099    if (superName) {
1100      base += ` extends ${superName}`;
1101    }
1102  } else {
1103    base += ' extends [null prototype]';
1104  }
1105  return `[${base}]`;
1106}
1107
1108function getFunctionBase(value, constructor, tag) {
1109  const stringified = FunctionPrototypeToString(value);
1110  if (stringified.slice(0, 5) === 'class' && stringified.endsWith('}')) {
1111    const slice = stringified.slice(5, -1);
1112    const bracketIndex = slice.indexOf('{');
1113    if (bracketIndex !== -1 &&
1114        (!slice.slice(0, bracketIndex).includes('(') ||
1115          // Slow path to guarantee that it's indeed a class.
1116          classRegExp.test(slice.replace(stripCommentsRegExp)))) {
1117      return getClassBase(value, constructor, tag);
1118    }
1119  }
1120  let type = 'Function';
1121  if (isGeneratorFunction(value)) {
1122    type = `Generator${type}`;
1123  }
1124  if (isAsyncFunction(value)) {
1125    type = `Async${type}`;
1126  }
1127  let base = `[${type}`;
1128  if (constructor === null) {
1129    base += ' (null prototype)';
1130  }
1131  if (value.name === '') {
1132    base += ' (anonymous)';
1133  } else {
1134    base += `: ${value.name}`;
1135  }
1136  base += ']';
1137  if (constructor !== type && constructor !== null) {
1138    base += ` ${constructor}`;
1139  }
1140  if (tag !== '' && constructor !== tag) {
1141    base += ` [${tag}]`;
1142  }
1143  return base;
1144}
1145
1146function formatError(err, constructor, tag, ctx, keys) {
1147  const name = err.name != null ? String(err.name) : 'Error';
1148  let len = name.length;
1149  let stack = err.stack ? String(err.stack) : ErrorPrototypeToString(err);
1150
1151  // Do not "duplicate" error properties that are already included in the output
1152  // otherwise.
1153  if (!ctx.showHidden && keys.length !== 0) {
1154    for (const name of ['name', 'message', 'stack']) {
1155      const index = keys.indexOf(name);
1156      // Only hide the property in case it's part of the original stack
1157      if (index !== -1 && stack.includes(err[name])) {
1158        keys.splice(index, 1);
1159      }
1160    }
1161  }
1162
1163  // A stack trace may contain arbitrary data. Only manipulate the output
1164  // for "regular errors" (errors that "look normal") for now.
1165  if (constructor === null ||
1166      (name.endsWith('Error') &&
1167      stack.startsWith(name) &&
1168      (stack.length === len || stack[len] === ':' || stack[len] === '\n'))) {
1169    let fallback = 'Error';
1170    if (constructor === null) {
1171      const start = stack.match(/^([A-Z][a-z_ A-Z0-9[\]()-]+)(?::|\n {4}at)/) ||
1172        stack.match(/^([a-z_A-Z0-9-]*Error)$/);
1173      fallback = (start && start[1]) || '';
1174      len = fallback.length;
1175      fallback = fallback || 'Error';
1176    }
1177    const prefix = getPrefix(constructor, tag, fallback).slice(0, -1);
1178    if (name !== prefix) {
1179      if (prefix.includes(name)) {
1180        if (len === 0) {
1181          stack = `${prefix}: ${stack}`;
1182        } else {
1183          stack = `${prefix}${stack.slice(len)}`;
1184        }
1185      } else {
1186        stack = `${prefix} [${name}]${stack.slice(len)}`;
1187      }
1188    }
1189  }
1190  // Ignore the error message if it's contained in the stack.
1191  let pos = (err.message && stack.indexOf(err.message)) || -1;
1192  if (pos !== -1)
1193    pos += err.message.length;
1194  // Wrap the error in brackets in case it has no stack trace.
1195  const stackStart = stack.indexOf('\n    at', pos);
1196  if (stackStart === -1) {
1197    stack = `[${stack}]`;
1198  } else if (ctx.colors) {
1199    // Highlight userland code and node modules.
1200    let newStack = stack.slice(0, stackStart);
1201    const lines = stack.slice(stackStart + 1).split('\n');
1202    for (const line of lines) {
1203      const core = line.match(coreModuleRegExp);
1204      if (core !== null && NativeModule.exists(core[1])) {
1205        newStack += `\n${ctx.stylize(line, 'undefined')}`;
1206      } else {
1207        // This adds underscores to all node_modules to quickly identify them.
1208        let nodeModule;
1209        newStack += '\n';
1210        let pos = 0;
1211        while (nodeModule = nodeModulesRegExp.exec(line)) {
1212          // '/node_modules/'.length === 14
1213          newStack += line.slice(pos, nodeModule.index + 14);
1214          newStack += ctx.stylize(nodeModule[1], 'module');
1215          pos = nodeModule.index + nodeModule[0].length;
1216        }
1217        newStack += pos === 0 ? line : line.slice(pos);
1218      }
1219    }
1220    stack = newStack;
1221  }
1222  // The message and the stack have to be indented as well!
1223  if (ctx.indentationLvl !== 0) {
1224    const indentation = ' '.repeat(ctx.indentationLvl);
1225    stack = stack.replace(/\n/g, `\n${indentation}`);
1226  }
1227  return stack;
1228}
1229
1230function groupArrayElements(ctx, output, value) {
1231  let totalLength = 0;
1232  let maxLength = 0;
1233  let i = 0;
1234  let outputLength = output.length;
1235  if (ctx.maxArrayLength < output.length) {
1236    // This makes sure the "... n more items" part is not taken into account.
1237    outputLength--;
1238  }
1239  const separatorSpace = 2; // Add 1 for the space and 1 for the separator.
1240  const dataLen = new Array(outputLength);
1241  // Calculate the total length of all output entries and the individual max
1242  // entries length of all output entries. We have to remove colors first,
1243  // otherwise the length would not be calculated properly.
1244  for (; i < outputLength; i++) {
1245    const len = getStringWidth(output[i], ctx.colors);
1246    dataLen[i] = len;
1247    totalLength += len + separatorSpace;
1248    if (maxLength < len)
1249      maxLength = len;
1250  }
1251  // Add two to `maxLength` as we add a single whitespace character plus a comma
1252  // in-between two entries.
1253  const actualMax = maxLength + separatorSpace;
1254  // Check if at least three entries fit next to each other and prevent grouping
1255  // of arrays that contains entries of very different length (i.e., if a single
1256  // entry is longer than 1/5 of all other entries combined). Otherwise the
1257  // space in-between small entries would be enormous.
1258  if (actualMax * 3 + ctx.indentationLvl < ctx.breakLength &&
1259      (totalLength / actualMax > 5 || maxLength <= 6)) {
1260
1261    const approxCharHeights = 2.5;
1262    const averageBias = MathSqrt(actualMax - totalLength / output.length);
1263    const biasedMax = MathMax(actualMax - 3 - averageBias, 1);
1264    // Dynamically check how many columns seem possible.
1265    const columns = MathMin(
1266      // Ideally a square should be drawn. We expect a character to be about 2.5
1267      // times as high as wide. This is the area formula to calculate a square
1268      // which contains n rectangles of size `actualMax * approxCharHeights`.
1269      // Divide that by `actualMax` to receive the correct number of columns.
1270      // The added bias increases the columns for short entries.
1271      MathRound(
1272        MathSqrt(
1273          approxCharHeights * biasedMax * outputLength
1274        ) / biasedMax
1275      ),
1276      // Do not exceed the breakLength.
1277      MathFloor((ctx.breakLength - ctx.indentationLvl) / actualMax),
1278      // Limit array grouping for small `compact` modes as the user requested
1279      // minimal grouping.
1280      ctx.compact * 4,
1281      // Limit the columns to a maximum of fifteen.
1282      15
1283    );
1284    // Return with the original output if no grouping should happen.
1285    if (columns <= 1) {
1286      return output;
1287    }
1288    const tmp = [];
1289    const maxLineLength = [];
1290    for (let i = 0; i < columns; i++) {
1291      let lineMaxLength = 0;
1292      for (let j = i; j < output.length; j += columns) {
1293        if (dataLen[j] > lineMaxLength)
1294          lineMaxLength = dataLen[j];
1295      }
1296      lineMaxLength += separatorSpace;
1297      maxLineLength[i] = lineMaxLength;
1298    }
1299    let order = 'padStart';
1300    if (value !== undefined) {
1301      for (let i = 0; i < output.length; i++) {
1302        if (typeof value[i] !== 'number' && typeof value[i] !== 'bigint') {
1303          order = 'padEnd';
1304          break;
1305        }
1306      }
1307    }
1308    // Each iteration creates a single line of grouped entries.
1309    for (let i = 0; i < outputLength; i += columns) {
1310      // The last lines may contain less entries than columns.
1311      const max = MathMin(i + columns, outputLength);
1312      let str = '';
1313      let j = i;
1314      for (; j < max - 1; j++) {
1315        // Calculate extra color padding in case it's active. This has to be
1316        // done line by line as some lines might contain more colors than
1317        // others.
1318        const padding = maxLineLength[j - i] + output[j].length - dataLen[j];
1319        str += `${output[j]}, `[order](padding, ' ');
1320      }
1321      if (order === 'padStart') {
1322        const padding = maxLineLength[j - i] +
1323                        output[j].length -
1324                        dataLen[j] -
1325                        separatorSpace;
1326        str += output[j].padStart(padding, ' ');
1327      } else {
1328        str += output[j];
1329      }
1330      tmp.push(str);
1331    }
1332    if (ctx.maxArrayLength < output.length) {
1333      tmp.push(output[outputLength]);
1334    }
1335    output = tmp;
1336  }
1337  return output;
1338}
1339
1340function handleMaxCallStackSize(ctx, err, constructorName, indentationLvl) {
1341  if (isStackOverflowError(err)) {
1342    ctx.seen.pop();
1343    ctx.indentationLvl = indentationLvl;
1344    return ctx.stylize(
1345      `[${constructorName}: Inspection interrupted ` +
1346        'prematurely. Maximum call stack size exceeded.]',
1347      'special'
1348    );
1349  }
1350  /* c8 ignore next */
1351  assert.fail(err.stack);
1352}
1353
1354function formatNumber(fn, value) {
1355  // Format -0 as '-0'. Checking `value === -0` won't distinguish 0 from -0.
1356  return fn(ObjectIs(value, -0) ? '-0' : `${value}`, 'number');
1357}
1358
1359function formatBigInt(fn, value) {
1360  return fn(`${value}n`, 'bigint');
1361}
1362
1363function formatPrimitive(fn, value, ctx) {
1364  if (typeof value === 'string') {
1365    let trailer = '';
1366    if (value.length > ctx.maxStringLength) {
1367      const remaining = value.length - ctx.maxStringLength;
1368      value = value.slice(0, ctx.maxStringLength);
1369      trailer = `... ${remaining} more character${remaining > 1 ? 's' : ''}`;
1370    }
1371    if (ctx.compact !== true &&
1372        // TODO(BridgeAR): Add unicode support. Use the readline getStringWidth
1373        // function.
1374        value.length > kMinLineLength &&
1375        value.length > ctx.breakLength - ctx.indentationLvl - 4) {
1376      return value
1377        .split(/(?<=\n)/)
1378        .map((line) => fn(strEscape(line), 'string'))
1379        .join(` +\n${' '.repeat(ctx.indentationLvl + 2)}`) + trailer;
1380    }
1381    return fn(strEscape(value), 'string') + trailer;
1382  }
1383  if (typeof value === 'number')
1384    return formatNumber(fn, value);
1385  if (typeof value === 'bigint')
1386    return formatBigInt(fn, value);
1387  if (typeof value === 'boolean')
1388    return fn(`${value}`, 'boolean');
1389  if (typeof value === 'undefined')
1390    return fn('undefined', 'undefined');
1391  // es6 symbol primitive
1392  return fn(SymbolPrototypeToString(value), 'symbol');
1393}
1394
1395function formatNamespaceObject(keys, ctx, value, recurseTimes) {
1396  const output = new Array(keys.length);
1397  for (let i = 0; i < keys.length; i++) {
1398    try {
1399      output[i] = formatProperty(ctx, value, recurseTimes, keys[i],
1400                                 kObjectType);
1401    } catch (err) {
1402      assert(isNativeError(err) && err.name === 'ReferenceError');
1403      // Use the existing functionality. This makes sure the indentation and
1404      // line breaks are always correct. Otherwise it is very difficult to keep
1405      // this aligned, even though this is a hacky way of dealing with this.
1406      const tmp = { [keys[i]]: '' };
1407      output[i] = formatProperty(ctx, tmp, recurseTimes, keys[i], kObjectType);
1408      const pos = output[i].lastIndexOf(' ');
1409      // We have to find the last whitespace and have to replace that value as
1410      // it will be visualized as a regular string.
1411      output[i] = output[i].slice(0, pos + 1) +
1412                  ctx.stylize('<uninitialized>', 'special');
1413    }
1414  }
1415  // Reset the keys to an empty array. This prevents duplicated inspection.
1416  keys.length = 0;
1417  return output;
1418}
1419
1420// The array is sparse and/or has extra keys
1421function formatSpecialArray(ctx, value, recurseTimes, maxLength, output, i) {
1422  const keys = ObjectKeys(value);
1423  let index = i;
1424  for (; i < keys.length && output.length < maxLength; i++) {
1425    const key = keys[i];
1426    const tmp = +key;
1427    // Arrays can only have up to 2^32 - 1 entries
1428    if (tmp > 2 ** 32 - 2) {
1429      break;
1430    }
1431    if (`${index}` !== key) {
1432      if (!numberRegExp.test(key)) {
1433        break;
1434      }
1435      const emptyItems = tmp - index;
1436      const ending = emptyItems > 1 ? 's' : '';
1437      const message = `<${emptyItems} empty item${ending}>`;
1438      output.push(ctx.stylize(message, 'undefined'));
1439      index = tmp;
1440      if (output.length === maxLength) {
1441        break;
1442      }
1443    }
1444    output.push(formatProperty(ctx, value, recurseTimes, key, kArrayType));
1445    index++;
1446  }
1447  const remaining = value.length - index;
1448  if (output.length !== maxLength) {
1449    if (remaining > 0) {
1450      const ending = remaining > 1 ? 's' : '';
1451      const message = `<${remaining} empty item${ending}>`;
1452      output.push(ctx.stylize(message, 'undefined'));
1453    }
1454  } else if (remaining > 0) {
1455    output.push(`... ${remaining} more item${remaining > 1 ? 's' : ''}`);
1456  }
1457  return output;
1458}
1459
1460function formatArrayBuffer(ctx, value) {
1461  let buffer;
1462  try {
1463    buffer = new Uint8Array(value);
1464  } catch {
1465    return [ctx.stylize('(detached)', 'special')];
1466  }
1467  if (hexSlice === undefined)
1468    hexSlice = uncurryThis(require('buffer').Buffer.prototype.hexSlice);
1469  let str = hexSlice(buffer, 0, MathMin(ctx.maxArrayLength, buffer.length))
1470    .replace(/(.{2})/g, '$1 ').trim();
1471  const remaining = buffer.length - ctx.maxArrayLength;
1472  if (remaining > 0)
1473    str += ` ... ${remaining} more byte${remaining > 1 ? 's' : ''}`;
1474  return [`${ctx.stylize('[Uint8Contents]', 'special')}: <${str}>`];
1475}
1476
1477function formatArray(ctx, value, recurseTimes) {
1478  const valLen = value.length;
1479  const len = MathMin(MathMax(0, ctx.maxArrayLength), valLen);
1480
1481  const remaining = valLen - len;
1482  const output = [];
1483  for (let i = 0; i < len; i++) {
1484    // Special handle sparse arrays.
1485    if (!ObjectPrototypeHasOwnProperty(value, i)) {
1486      return formatSpecialArray(ctx, value, recurseTimes, len, output, i);
1487    }
1488    output.push(formatProperty(ctx, value, recurseTimes, i, kArrayType));
1489  }
1490  if (remaining > 0)
1491    output.push(`... ${remaining} more item${remaining > 1 ? 's' : ''}`);
1492  return output;
1493}
1494
1495function formatTypedArray(value, length, ctx, ignored, recurseTimes) {
1496  const maxLength = MathMin(MathMax(0, ctx.maxArrayLength), length);
1497  const remaining = value.length - maxLength;
1498  const output = new Array(maxLength);
1499  const elementFormatter = value.length > 0 && typeof value[0] === 'number' ?
1500    formatNumber :
1501    formatBigInt;
1502  for (let i = 0; i < maxLength; ++i)
1503    output[i] = elementFormatter(ctx.stylize, value[i]);
1504  if (remaining > 0) {
1505    output[maxLength] = `... ${remaining} more item${remaining > 1 ? 's' : ''}`;
1506  }
1507  if (ctx.showHidden) {
1508    // .buffer goes last, it's not a primitive like the others.
1509    // All besides `BYTES_PER_ELEMENT` are actually getters.
1510    ctx.indentationLvl += 2;
1511    for (const key of [
1512      'BYTES_PER_ELEMENT',
1513      'length',
1514      'byteLength',
1515      'byteOffset',
1516      'buffer',
1517    ]) {
1518      const str = formatValue(ctx, value[key], recurseTimes, true);
1519      output.push(`[${key}]: ${str}`);
1520    }
1521    ctx.indentationLvl -= 2;
1522  }
1523  return output;
1524}
1525
1526function formatSet(value, ctx, ignored, recurseTimes) {
1527  const output = [];
1528  ctx.indentationLvl += 2;
1529  for (const v of value) {
1530    output.push(formatValue(ctx, v, recurseTimes));
1531  }
1532  ctx.indentationLvl -= 2;
1533  return output;
1534}
1535
1536function formatMap(value, ctx, ignored, recurseTimes) {
1537  const output = [];
1538  ctx.indentationLvl += 2;
1539  for (const { 0: k, 1: v } of value) {
1540    output.push(
1541      `${formatValue(ctx, k, recurseTimes)} => ${formatValue(ctx, v, recurseTimes)}`
1542    );
1543  }
1544  ctx.indentationLvl -= 2;
1545  return output;
1546}
1547
1548function formatSetIterInner(ctx, recurseTimes, entries, state) {
1549  const maxArrayLength = MathMax(ctx.maxArrayLength, 0);
1550  const maxLength = MathMin(maxArrayLength, entries.length);
1551  let output = new Array(maxLength);
1552  ctx.indentationLvl += 2;
1553  for (let i = 0; i < maxLength; i++) {
1554    output[i] = formatValue(ctx, entries[i], recurseTimes);
1555  }
1556  ctx.indentationLvl -= 2;
1557  if (state === kWeak && !ctx.sorted) {
1558    // Sort all entries to have a halfway reliable output (if more entries than
1559    // retrieved ones exist, we can not reliably return the same output) if the
1560    // output is not sorted anyway.
1561    output = output.sort();
1562  }
1563  const remaining = entries.length - maxLength;
1564  if (remaining > 0) {
1565    output.push(`... ${remaining} more item${remaining > 1 ? 's' : ''}`);
1566  }
1567  return output;
1568}
1569
1570function formatMapIterInner(ctx, recurseTimes, entries, state) {
1571  const maxArrayLength = MathMax(ctx.maxArrayLength, 0);
1572  // Entries exist as [key1, val1, key2, val2, ...]
1573  const len = entries.length / 2;
1574  const remaining = len - maxArrayLength;
1575  const maxLength = MathMin(maxArrayLength, len);
1576  let output = new Array(maxLength);
1577  let i = 0;
1578  ctx.indentationLvl += 2;
1579  if (state === kWeak) {
1580    for (; i < maxLength; i++) {
1581      const pos = i * 2;
1582      output[i] =
1583        `${formatValue(ctx, entries[pos], recurseTimes)} => ${formatValue(ctx, entries[pos + 1], recurseTimes)}`;
1584    }
1585    // Sort all entries to have a halfway reliable output (if more entries than
1586    // retrieved ones exist, we can not reliably return the same output) if the
1587    // output is not sorted anyway.
1588    if (!ctx.sorted)
1589      output = output.sort();
1590  } else {
1591    for (; i < maxLength; i++) {
1592      const pos = i * 2;
1593      const res = [
1594        formatValue(ctx, entries[pos], recurseTimes),
1595        formatValue(ctx, entries[pos + 1], recurseTimes),
1596      ];
1597      output[i] = reduceToSingleString(
1598        ctx, res, '', ['[', ']'], kArrayExtrasType, recurseTimes);
1599    }
1600  }
1601  ctx.indentationLvl -= 2;
1602  if (remaining > 0) {
1603    output.push(`... ${remaining} more item${remaining > 1 ? 's' : ''}`);
1604  }
1605  return output;
1606}
1607
1608function formatWeakCollection(ctx) {
1609  return [ctx.stylize('<items unknown>', 'special')];
1610}
1611
1612function formatWeakSet(ctx, value, recurseTimes) {
1613  const entries = previewEntries(value);
1614  return formatSetIterInner(ctx, recurseTimes, entries, kWeak);
1615}
1616
1617function formatWeakMap(ctx, value, recurseTimes) {
1618  const entries = previewEntries(value);
1619  return formatMapIterInner(ctx, recurseTimes, entries, kWeak);
1620}
1621
1622function formatIterator(braces, ctx, value, recurseTimes) {
1623  const { 0: entries, 1: isKeyValue } = previewEntries(value, true);
1624  if (isKeyValue) {
1625    // Mark entry iterators as such.
1626    braces[0] = braces[0].replace(/ Iterator] {$/, ' Entries] {');
1627    return formatMapIterInner(ctx, recurseTimes, entries, kMapEntries);
1628  }
1629
1630  return formatSetIterInner(ctx, recurseTimes, entries, kIterator);
1631}
1632
1633function formatPromise(ctx, value, recurseTimes) {
1634  let output;
1635  const { 0: state, 1: result } = getPromiseDetails(value);
1636  if (state === kPending) {
1637    output = [ctx.stylize('<pending>', 'special')];
1638  } else {
1639    ctx.indentationLvl += 2;
1640    const str = formatValue(ctx, result, recurseTimes);
1641    ctx.indentationLvl -= 2;
1642    output = [
1643      state === kRejected ?
1644        `${ctx.stylize('<rejected>', 'special')} ${str}` :
1645        str,
1646    ];
1647  }
1648  return output;
1649}
1650
1651function formatProperty(ctx, value, recurseTimes, key, type, desc,
1652                        original = value) {
1653  let name, str;
1654  let extra = ' ';
1655  desc = desc || ObjectGetOwnPropertyDescriptor(value, key) ||
1656    { value: value[key], enumerable: true };
1657  if (desc.value !== undefined) {
1658    const diff = (ctx.compact !== true || type !== kObjectType) ? 2 : 3;
1659    ctx.indentationLvl += diff;
1660    str = formatValue(ctx, desc.value, recurseTimes);
1661    if (diff === 3 && ctx.breakLength < getStringWidth(str, ctx.colors)) {
1662      extra = `\n${' '.repeat(ctx.indentationLvl)}`;
1663    }
1664    ctx.indentationLvl -= diff;
1665  } else if (desc.get !== undefined) {
1666    const label = desc.set !== undefined ? 'Getter/Setter' : 'Getter';
1667    const s = ctx.stylize;
1668    const sp = 'special';
1669    if (ctx.getters && (ctx.getters === true ||
1670          (ctx.getters === 'get' && desc.set === undefined) ||
1671          (ctx.getters === 'set' && desc.set !== undefined))) {
1672      try {
1673        const tmp = ReflectApply(desc.get, original, []);
1674        ctx.indentationLvl += 2;
1675        if (tmp === null) {
1676          str = `${s(`[${label}:`, sp)} ${s('null', 'null')}${s(']', sp)}`;
1677        } else if (typeof tmp === 'object') {
1678          str = `${s(`[${label}]`, sp)} ${formatValue(ctx, tmp, recurseTimes)}`;
1679        } else {
1680          const primitive = formatPrimitive(s, tmp, ctx);
1681          str = `${s(`[${label}:`, sp)} ${primitive}${s(']', sp)}`;
1682        }
1683        ctx.indentationLvl -= 2;
1684      } catch (err) {
1685        const message = `<Inspection threw (${err.message})>`;
1686        str = `${s(`[${label}:`, sp)} ${message}${s(']', sp)}`;
1687      }
1688    } else {
1689      str = ctx.stylize(`[${label}]`, sp);
1690    }
1691  } else if (desc.set !== undefined) {
1692    str = ctx.stylize('[Setter]', 'special');
1693  } else {
1694    str = ctx.stylize('undefined', 'undefined');
1695  }
1696  if (type === kArrayType) {
1697    return str;
1698  }
1699  if (typeof key === 'symbol') {
1700    const tmp = key.toString().replace(strEscapeSequencesReplacer, escapeFn);
1701    name = `[${ctx.stylize(tmp, 'symbol')}]`;
1702  } else if (key === '__proto__') {
1703    name = "['__proto__']";
1704  } else if (desc.enumerable === false) {
1705    name = `[${key.replace(strEscapeSequencesReplacer, escapeFn)}]`;
1706  } else if (keyStrRegExp.test(key)) {
1707    name = ctx.stylize(key, 'name');
1708  } else {
1709    name = ctx.stylize(strEscape(key), 'string');
1710  }
1711  return `${name}:${extra}${str}`;
1712}
1713
1714function isBelowBreakLength(ctx, output, start, base) {
1715  // Each entry is separated by at least a comma. Thus, we start with a total
1716  // length of at least `output.length`. In addition, some cases have a
1717  // whitespace in-between each other that is added to the total as well.
1718  // TODO(BridgeAR): Add unicode support. Use the readline getStringWidth
1719  // function. Check the performance overhead and make it an opt-in in case it's
1720  // significant.
1721  let totalLength = output.length + start;
1722  if (totalLength + output.length > ctx.breakLength)
1723    return false;
1724  for (let i = 0; i < output.length; i++) {
1725    if (ctx.colors) {
1726      totalLength += removeColors(output[i]).length;
1727    } else {
1728      totalLength += output[i].length;
1729    }
1730    if (totalLength > ctx.breakLength) {
1731      return false;
1732    }
1733  }
1734  // Do not line up properties on the same line if `base` contains line breaks.
1735  return base === '' || !base.includes('\n');
1736}
1737
1738function reduceToSingleString(
1739  ctx, output, base, braces, extrasType, recurseTimes, value) {
1740  if (ctx.compact !== true) {
1741    if (typeof ctx.compact === 'number' && ctx.compact >= 1) {
1742      // Memorize the original output length. In case the output is grouped,
1743      // prevent lining up the entries on a single line.
1744      const entries = output.length;
1745      // Group array elements together if the array contains at least six
1746      // separate entries.
1747      if (extrasType === kArrayExtrasType && entries > 6) {
1748        output = groupArrayElements(ctx, output, value);
1749      }
1750      // `ctx.currentDepth` is set to the most inner depth of the currently
1751      // inspected object part while `recurseTimes` is the actual current depth
1752      // that is inspected.
1753      //
1754      // Example:
1755      //
1756      // const a = { first: [ 1, 2, 3 ], second: { inner: [ 1, 2, 3 ] } }
1757      //
1758      // The deepest depth of `a` is 2 (a.second.inner) and `a.first` has a max
1759      // depth of 1.
1760      //
1761      // Consolidate all entries of the local most inner depth up to
1762      // `ctx.compact`, as long as the properties are smaller than
1763      // `ctx.breakLength`.
1764      if (ctx.currentDepth - recurseTimes < ctx.compact &&
1765          entries === output.length) {
1766        // Line up all entries on a single line in case the entries do not
1767        // exceed `breakLength`. Add 10 as constant to start next to all other
1768        // factors that may reduce `breakLength`.
1769        const start = output.length + ctx.indentationLvl +
1770                      braces[0].length + base.length + 10;
1771        if (isBelowBreakLength(ctx, output, start, base)) {
1772          return `${base ? `${base} ` : ''}${braces[0]} ${join(output, ', ')}` +
1773            ` ${braces[1]}`;
1774        }
1775      }
1776    }
1777    // Line up each entry on an individual line.
1778    const indentation = `\n${' '.repeat(ctx.indentationLvl)}`;
1779    return `${base ? `${base} ` : ''}${braces[0]}${indentation}  ` +
1780      `${join(output, `,${indentation}  `)}${indentation}${braces[1]}`;
1781  }
1782  // Line up all entries on a single line in case the entries do not exceed
1783  // `breakLength`.
1784  if (isBelowBreakLength(ctx, output, 0, base)) {
1785    return `${braces[0]}${base ? ` ${base}` : ''} ${join(output, ', ')} ` +
1786      braces[1];
1787  }
1788  const indentation = ' '.repeat(ctx.indentationLvl);
1789  // If the opening "brace" is too large, like in the case of "Set {",
1790  // we need to force the first item to be on the next line or the
1791  // items will not line up correctly.
1792  const ln = base === '' && braces[0].length === 1 ?
1793    ' ' : `${base ? ` ${base}` : ''}\n${indentation}  `;
1794  // Line up each entry on an individual line.
1795  return `${braces[0]}${ln}${join(output, `,\n${indentation}  `)} ${braces[1]}`;
1796}
1797
1798function hasBuiltInToString(value) {
1799  // Prevent triggering proxy traps.
1800  const getFullProxy = false;
1801  const proxyTarget = getProxyDetails(value, getFullProxy);
1802  if (proxyTarget !== undefined) {
1803    value = proxyTarget;
1804  }
1805
1806  // Count objects that have no `toString` function as built-in.
1807  if (typeof value.toString !== 'function') {
1808    return true;
1809  }
1810
1811  // The object has a own `toString` property. Thus it's not not a built-in one.
1812  if (ObjectPrototypeHasOwnProperty(value, 'toString')) {
1813    return false;
1814  }
1815
1816  // Find the object that has the `toString` property as own property in the
1817  // prototype chain.
1818  let pointer = value;
1819  do {
1820    pointer = ObjectGetPrototypeOf(pointer);
1821  } while (!ObjectPrototypeHasOwnProperty(pointer, 'toString'));
1822
1823  // Check closer if the object is a built-in.
1824  const descriptor = ObjectGetOwnPropertyDescriptor(pointer, 'constructor');
1825  return descriptor !== undefined &&
1826    typeof descriptor.value === 'function' &&
1827    builtInObjects.has(descriptor.value.name);
1828}
1829
1830const firstErrorLine = (error) => error.message.split('\n')[0];
1831let CIRCULAR_ERROR_MESSAGE;
1832function tryStringify(arg) {
1833  try {
1834    return JSONStringify(arg);
1835  } catch (err) {
1836    // Populate the circular error message lazily
1837    if (!CIRCULAR_ERROR_MESSAGE) {
1838      try {
1839        const a = {}; a.a = a; JSONStringify(a);
1840      } catch (err) {
1841        CIRCULAR_ERROR_MESSAGE = firstErrorLine(err);
1842      }
1843    }
1844    if (err.name === 'TypeError' &&
1845        firstErrorLine(err) === CIRCULAR_ERROR_MESSAGE) {
1846      return '[Circular]';
1847    }
1848    throw err;
1849  }
1850}
1851
1852function format(...args) {
1853  return formatWithOptionsInternal(undefined, args);
1854}
1855
1856function formatWithOptions(inspectOptions, ...args) {
1857  if (typeof inspectOptions !== 'object' || inspectOptions === null) {
1858    throw new ERR_INVALID_ARG_TYPE(
1859      'inspectOptions', 'object', inspectOptions);
1860  }
1861  return formatWithOptionsInternal(inspectOptions, args);
1862}
1863
1864function formatWithOptionsInternal(inspectOptions, args) {
1865  const first = args[0];
1866  let a = 0;
1867  let str = '';
1868  let join = '';
1869
1870  if (typeof first === 'string') {
1871    if (args.length === 1) {
1872      return first;
1873    }
1874    let tempStr;
1875    let lastPos = 0;
1876
1877    for (let i = 0; i < first.length - 1; i++) {
1878      if (first.charCodeAt(i) === 37) { // '%'
1879        const nextChar = first.charCodeAt(++i);
1880        if (a + 1 !== args.length) {
1881          switch (nextChar) {
1882            case 115: // 's'
1883              const tempArg = args[++a];
1884              if (typeof tempArg === 'number') {
1885                tempStr = formatNumber(stylizeNoColor, tempArg);
1886              } else if (typeof tempArg === 'bigint') {
1887                tempStr = `${tempArg}n`;
1888              } else if (typeof tempArg !== 'object' ||
1889                         tempArg === null ||
1890                         !hasBuiltInToString(tempArg)) {
1891                tempStr = String(tempArg);
1892              } else {
1893                tempStr = inspect(tempArg, {
1894                  ...inspectOptions,
1895                  compact: 3,
1896                  colors: false,
1897                  depth: 0
1898                });
1899              }
1900              break;
1901            case 106: // 'j'
1902              tempStr = tryStringify(args[++a]);
1903              break;
1904            case 100: // 'd'
1905              const tempNum = args[++a];
1906              if (typeof tempNum === 'bigint') {
1907                tempStr = `${tempNum}n`;
1908              } else if (typeof tempNum === 'symbol') {
1909                tempStr = 'NaN';
1910              } else {
1911                tempStr = formatNumber(stylizeNoColor, Number(tempNum));
1912              }
1913              break;
1914            case 79: // 'O'
1915              tempStr = inspect(args[++a], inspectOptions);
1916              break;
1917            case 111: // 'o'
1918              tempStr = inspect(args[++a], {
1919                ...inspectOptions,
1920                showHidden: true,
1921                showProxy: true,
1922                depth: 4
1923              });
1924              break;
1925            case 105: // 'i'
1926              const tempInteger = args[++a];
1927              if (typeof tempInteger === 'bigint') {
1928                tempStr = `${tempInteger}n`;
1929              } else if (typeof tempInteger === 'symbol') {
1930                tempStr = 'NaN';
1931              } else {
1932                tempStr = formatNumber(stylizeNoColor,
1933                                       NumberParseInt(tempInteger));
1934              }
1935              break;
1936            case 102: // 'f'
1937              const tempFloat = args[++a];
1938              if (typeof tempFloat === 'symbol') {
1939                tempStr = 'NaN';
1940              } else {
1941                tempStr = formatNumber(stylizeNoColor,
1942                                       NumberParseFloat(tempFloat));
1943              }
1944              break;
1945            case 99: // 'c'
1946              a += 1;
1947              tempStr = '';
1948              break;
1949            case 37: // '%'
1950              str += first.slice(lastPos, i);
1951              lastPos = i + 1;
1952              continue;
1953            default: // Any other character is not a correct placeholder
1954              continue;
1955          }
1956          if (lastPos !== i - 1) {
1957            str += first.slice(lastPos, i - 1);
1958          }
1959          str += tempStr;
1960          lastPos = i + 1;
1961        } else if (nextChar === 37) {
1962          str += first.slice(lastPos, i);
1963          lastPos = i + 1;
1964        }
1965      }
1966    }
1967    if (lastPos !== 0) {
1968      a++;
1969      join = ' ';
1970      if (lastPos < first.length) {
1971        str += first.slice(lastPos);
1972      }
1973    }
1974  }
1975
1976  while (a < args.length) {
1977    const value = args[a];
1978    str += join;
1979    str += typeof value !== 'string' ? inspect(value, inspectOptions) : value;
1980    join = ' ';
1981    a++;
1982  }
1983  return str;
1984}
1985
1986if (internalBinding('config').hasIntl) {
1987  const icu = internalBinding('icu');
1988  // icu.getStringWidth(string, ambiguousAsFullWidth, expandEmojiSequence)
1989  // Defaults: ambiguousAsFullWidth = false; expandEmojiSequence = true;
1990  // TODO(BridgeAR): Expose the options to the user. That is probably the
1991  // best thing possible at the moment, since it's difficult to know what
1992  // the receiving end supports.
1993  getStringWidth = function getStringWidth(str, removeControlChars = true) {
1994    let width = 0;
1995
1996    if (removeControlChars)
1997      str = stripVTControlCharacters(str);
1998    for (let i = 0; i < str.length; i++) {
1999      // Try to avoid calling into C++ by first handling the ASCII portion of
2000      // the string. If it is fully ASCII, we skip the C++ part.
2001      const code = str.charCodeAt(i);
2002      if (code >= 127) {
2003        width += icu.getStringWidth(str.slice(i).normalize('NFC'));
2004        break;
2005      }
2006      width += code >= 32 ? 1 : 0;
2007    }
2008    return width;
2009  };
2010} else {
2011  /**
2012   * Returns the number of columns required to display the given string.
2013   */
2014  getStringWidth = function getStringWidth(str, removeControlChars = true) {
2015    let width = 0;
2016
2017    if (removeControlChars)
2018      str = stripVTControlCharacters(str);
2019    str = str.normalize('NFC');
2020    for (const char of new SafeStringIterator(str)) {
2021      const code = char.codePointAt(0);
2022      if (isFullWidthCodePoint(code)) {
2023        width += 2;
2024      } else if (!isZeroWidthCodePoint(code)) {
2025        width++;
2026      }
2027    }
2028
2029    return width;
2030  };
2031
2032  /**
2033   * Returns true if the character represented by a given
2034   * Unicode code point is full-width. Otherwise returns false.
2035   */
2036  const isFullWidthCodePoint = (code) => {
2037    // Code points are partially derived from:
2038    // https://www.unicode.org/Public/UNIDATA/EastAsianWidth.txt
2039    return code >= 0x1100 && (
2040      code <= 0x115f ||  // Hangul Jamo
2041      code === 0x2329 || // LEFT-POINTING ANGLE BRACKET
2042      code === 0x232a || // RIGHT-POINTING ANGLE BRACKET
2043      // CJK Radicals Supplement .. Enclosed CJK Letters and Months
2044      (code >= 0x2e80 && code <= 0x3247 && code !== 0x303f) ||
2045      // Enclosed CJK Letters and Months .. CJK Unified Ideographs Extension A
2046      (code >= 0x3250 && code <= 0x4dbf) ||
2047      // CJK Unified Ideographs .. Yi Radicals
2048      (code >= 0x4e00 && code <= 0xa4c6) ||
2049      // Hangul Jamo Extended-A
2050      (code >= 0xa960 && code <= 0xa97c) ||
2051      // Hangul Syllables
2052      (code >= 0xac00 && code <= 0xd7a3) ||
2053      // CJK Compatibility Ideographs
2054      (code >= 0xf900 && code <= 0xfaff) ||
2055      // Vertical Forms
2056      (code >= 0xfe10 && code <= 0xfe19) ||
2057      // CJK Compatibility Forms .. Small Form Variants
2058      (code >= 0xfe30 && code <= 0xfe6b) ||
2059      // Halfwidth and Fullwidth Forms
2060      (code >= 0xff01 && code <= 0xff60) ||
2061      (code >= 0xffe0 && code <= 0xffe6) ||
2062      // Kana Supplement
2063      (code >= 0x1b000 && code <= 0x1b001) ||
2064      // Enclosed Ideographic Supplement
2065      (code >= 0x1f200 && code <= 0x1f251) ||
2066      // Miscellaneous Symbols and Pictographs 0x1f300 - 0x1f5ff
2067      // Emoticons 0x1f600 - 0x1f64f
2068      (code >= 0x1f300 && code <= 0x1f64f) ||
2069      // CJK Unified Ideographs Extension B .. Tertiary Ideographic Plane
2070      (code >= 0x20000 && code <= 0x3fffd)
2071    );
2072  };
2073
2074  const isZeroWidthCodePoint = (code) => {
2075    return code <= 0x1F || // C0 control codes
2076      (code >= 0x7F && code <= 0x9F) || // C1 control codes
2077      (code >= 0x300 && code <= 0x36F) || // Combining Diacritical Marks
2078      (code >= 0x200B && code <= 0x200F) || // Modifying Invisible Characters
2079      // Combining Diacritical Marks for Symbols
2080      (code >= 0x20D0 && code <= 0x20FF) ||
2081      (code >= 0xFE00 && code <= 0xFE0F) || // Variation Selectors
2082      (code >= 0xFE20 && code <= 0xFE2F) || // Combining Half Marks
2083      (code >= 0xE0100 && code <= 0xE01EF); // Variation Selectors
2084  };
2085}
2086
2087/**
2088 * Remove all VT control characters. Use to estimate displayed string width.
2089 */
2090function stripVTControlCharacters(str) {
2091  return str.replace(ansi, '');
2092}
2093
2094module.exports = {
2095  inspect,
2096  format,
2097  formatWithOptions,
2098  getStringWidth,
2099  inspectDefaultOptions,
2100  stripVTControlCharacters
2101};
2102