diff options
author | Leo Kettmeir <crowlkats@toaxl.com> | 2022-02-04 21:10:47 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-02-04 21:10:47 +0100 |
commit | 2f438f4106e1c69f29e43221a98428213f6621e9 (patch) | |
tree | f84c92e002b1605bcc92558551677c1ec4c84563 /ext/console/02_console.js | |
parent | 3ec07f4e0686638ec55b3a1396182f42849281ea (diff) |
feat(ext/console): better circular information in object inspection (#13555)
Diffstat (limited to 'ext/console/02_console.js')
-rw-r--r-- | ext/console/02_console.js | 98 |
1 files changed, 75 insertions, 23 deletions
diff --git a/ext/console/02_console.js b/ext/console/02_console.js index 4dab799b9..d3734b6a4 100644 --- a/ext/console/02_console.js +++ b/ext/console/02_console.js @@ -72,6 +72,7 @@ ArrayPrototypePop, ArrayPrototypeSort, ArrayPrototypeSlice, + ArrayPrototypeShift, ArrayPrototypeIncludes, ArrayPrototypeFill, ArrayPrototypeFilter, @@ -340,11 +341,17 @@ // If we didn't find any properties, we will just append an // empty suffix. let suffix = ``; + let refStr = ""; if ( ObjectKeys(value).length > 0 || ObjectGetOwnPropertySymbols(value).length > 0 ) { - const propString = inspectRawObject(value, level, inspectOptions); + const [propString, refIndex] = inspectRawObject( + value, + level, + inspectOptions, + ); + refStr = refIndex; // Filter out the empty string for the case we only have // non-enumerable symbols. if ( @@ -357,9 +364,9 @@ if (value.name && value.name !== "anonymous") { // from MDN spec - return cyan(`[${cstrName}: ${value.name}]`) + suffix; + return cyan(`${refStr}[${cstrName}: ${value.name}]`) + suffix; } - return cyan(`[${cstrName}]`) + suffix; + return cyan(`${refStr}[${cstrName}]`) + suffix; } function inspectIterable( @@ -589,6 +596,23 @@ return entries; } + let circular; + function handleCircular(value, cyan) { + let index = 1; + if (circular === undefined) { + circular = new Map(); + MapPrototypeSet(circular, value, index); + } else { + index = MapPrototypeGet(circular, value); + if (index === undefined) { + index = circular.size + 1; + MapPrototypeSet(circular, value, index); + } + } + // Circular string is cyan + return cyan(`[Circular *${index}]`); + } + function _inspectValue( value, level, @@ -623,7 +647,7 @@ case "function": // Function string is cyan if (ctxHas(value)) { // Circular string is cyan - return cyan("[Circular]"); + return handleCircular(value, cyan); } return inspectFunction(value, level, inspectOptions); @@ -633,8 +657,7 @@ } if (ctxHas(value)) { - // Circular string is cyan - return cyan("[Circular]"); + return handleCircular(value, cyan); } return inspectObject(value, level, inspectOptions); default: @@ -892,25 +915,41 @@ return red(RegExpPrototypeToString(value)); // RegExps are red } - function inspectError(value) { - const causes = []; + function inspectError(value, cyan) { + const causes = [value]; let err = value; - while ( - ObjectPrototypeIsPrototypeOf(ErrorPrototype, err.cause) && - err.cause !== value && - !ArrayPrototypeIncludes(causes, err.cause) // circular check - ) { - ArrayPrototypePush(causes, err.cause); - err = err.cause; + while (err.cause) { + if (ArrayPrototypeIncludes(causes, err.cause)) { + ArrayPrototypePush(causes, handleCircular(err.cause, cyan)); + break; + } else { + ArrayPrototypePush(causes, err.cause); + err = err.cause; + } } - return `${value.stack}${ + const refMap = new Map(); + for (const cause of causes) { + if (circular !== undefined) { + const index = MapPrototypeGet(circular, cause); + if (index !== undefined) { + MapPrototypeSet(refMap, cause, cyan(`<ref *${index}> `)); + } + } + } + ArrayPrototypeShift(causes); + + return (MapPrototypeGet(refMap, value) ?? "") + value.stack + ArrayPrototypeJoin( - ArrayPrototypeMap(causes, (cause) => `\nCaused by ${cause.stack}`), + ArrayPrototypeMap( + causes, + (cause) => + "\nCaused by " + (MapPrototypeGet(refMap, cause) ?? "") + + (cause?.stack ?? cause), + ), "", - ) - }`; + ); } function inspectStringObject(value, inspectOptions) { @@ -1002,7 +1041,7 @@ const cyan = maybeColor(colors.cyan, inspectOptions); if (level >= inspectOptions.depth) { - return cyan("[Object]"); // wrappers are in cyan + return [cyan("[Object]"), ""]; // wrappers are in cyan } let baseString; @@ -1144,7 +1183,15 @@ baseString = `${displayName} ${baseString}`; } - return baseString; + let refIndex = ""; + if (circular !== undefined) { + const index = MapPrototypeGet(circular, value); + if (index !== undefined) { + refIndex = `<ref *${index}> `; + } + } + + return [baseString, refIndex]; } function inspectObject( @@ -1171,7 +1218,7 @@ return String(value[privateCustomInspect](inspect)); } if (ObjectPrototypeIsPrototypeOf(ErrorPrototype, value)) { - return inspectError(value); + return inspectError(value, maybeColor(colors.cyan, inspectOptions)); } else if (ArrayIsArray(value)) { return inspectArray(value, level, inspectOptions); } else if (ObjectPrototypeIsPrototypeOf(NumberPrototype, value)) { @@ -1207,7 +1254,9 @@ ); } else { // Otherwise, default object formatting - return inspectRawObject(value, level, inspectOptions); + let [insp, refIndex] = inspectRawObject(value, level, inspectOptions); + insp = refIndex + insp; + return insp; } } @@ -1660,6 +1709,8 @@ } function inspectArgs(args, inspectOptions = {}) { + circular = undefined; + const noColor = colors.getNoColor(); const rInspectOptions = { ...DEFAULT_INSPECT_OPTIONS, ...inspectOptions }; const first = args[0]; @@ -2076,6 +2127,7 @@ value, inspectOptions = {}, ) { + circular = undefined; return inspectValue(value, 0, { ...DEFAULT_INSPECT_OPTIONS, ...inspectOptions, |