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