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