1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-22 15:24:46 -05:00

fix(console): circular customInspect (#10338)

This commit is contained in:
Aaron O'Mullan 2021-04-27 11:54:47 +02:00 committed by GitHub
parent 4fa0e9c652
commit 83770e898e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 69 additions and 60 deletions

View file

@ -342,6 +342,14 @@ unitTest(function consoleTestStringifyCircular(): void {
stringify({ str: 1, [Symbol.for("sym")]: 2, [Symbol.toStringTag]: "TAG" }),
'TAG { str: 1, [Symbol(sym)]: 2, [Symbol(Symbol.toStringTag)]: "TAG" }',
);
assertEquals(
stringify({
[Deno.customInspect]: function () {
return Deno.inspect(this);
},
}),
"[Circular]",
);
// test inspect is working the same
assertEquals(stripColor(Deno.inspect(nestedObj)), nestedObjExpected);
});
@ -878,12 +886,19 @@ unitTest(function consoleTestWithCustomInspectorError(): void {
}
}
const a = new A();
assertThrows(
() => stringify(new A()),
() => stringify(a),
Error,
"BOOM",
"Custom inspect won't attempt to parse if user defined function throws",
);
assertThrows(
() => stringify(a),
Error,
"BOOM",
"Inpsect should fail and maintain a clear CTX_STACK",
);
});
unitTest(function consoleTestWithCustomInspectFunction(): void {

View file

@ -200,7 +200,7 @@
return inspectOptions.colors ? fn : (s) => s;
}
function inspectFunction(value, ctx, level, inspectOptions) {
function inspectFunction(value, level, inspectOptions) {
const cyan = maybeColor(colors.cyan, inspectOptions);
if (customInspect in value && typeof value[customInspect] === "function") {
return String(value[customInspect]());
@ -222,7 +222,7 @@
Object.keys(value).length > 0 ||
Object.getOwnPropertySymbols(value).length > 0
) {
const propString = inspectRawObject(value, ctx, level, inspectOptions);
const propString = inspectRawObject(value, level, inspectOptions);
// Filter out the empty string for the case we only have
// non-enumerable symbols.
if (
@ -242,7 +242,6 @@
function inspectIterable(
value,
ctx,
level,
options,
inspectOptions,
@ -251,7 +250,6 @@
if (level >= inspectOptions.depth) {
return cyan(`[${options.typeName}]`);
}
ctx.add(value);
const entries = [];
@ -265,7 +263,6 @@
entries.push(
options.entryHandler(
el,
ctx,
level + 1,
inspectOptions,
next.bind(iter),
@ -274,7 +271,6 @@
}
entriesLength++;
}
ctx.delete(value);
if (options.sort) {
entries.sort();
@ -427,17 +423,16 @@
return entries;
}
function inspectValue(
function _inspectValue(
value,
ctx,
level,
inspectOptions,
) {
const proxyDetails = core.getProxyDetails(value);
if (proxyDetails != null) {
return inspectOptions.showProxy
? inspectProxy(proxyDetails, ctx, level, inspectOptions)
: inspectValue(proxyDetails[0], ctx, level, inspectOptions);
? inspectProxy(proxyDetails, level, inspectOptions)
: inspectValue(proxyDetails[0], level, inspectOptions);
}
const green = maybeColor(colors.green, inspectOptions);
@ -462,29 +457,43 @@
case "bigint": // Bigints are yellow
return yellow(`${value}n`);
case "function": // Function string is cyan
if (ctx.has(value)) {
if (ctxHas(value)) {
// Circular string is cyan
return cyan("[Circular]");
}
return inspectFunction(value, ctx, level, inspectOptions);
return inspectFunction(value, level, inspectOptions);
case "object": // null is bold
if (value === null) {
return bold("null");
}
if (ctx.has(value)) {
if (ctxHas(value)) {
// Circular string is cyan
return cyan("[Circular]");
}
return inspectObject(value, ctx, level, inspectOptions);
return inspectObject(value, level, inspectOptions);
default:
// Not implemented is red
return red("[Not Implemented]");
}
}
function inspectValue(
value,
level,
inspectOptions,
) {
CTX_STACK.push(value);
let x;
try {
x = _inspectValue(value, level, inspectOptions);
} finally {
CTX_STACK.pop();
}
return x;
}
// We can match Node's quoting behavior exactly by swapping the double quote and
// single quote in this array. That would give preference to single quotes.
// However, we prefer double quotes as the default.
@ -544,10 +553,15 @@
return `Symbol(${quoteString(symbol.description)})`;
}
const CTX_STACK = [];
function ctxHas(x) {
// Only check parent contexts
return CTX_STACK.slice(0, CTX_STACK.length - 1).includes(x);
}
// Print strings when they are inside of arrays or objects with quotes
function inspectValueWithQuotes(
value,
ctx,
level,
inspectOptions,
) {
@ -560,13 +574,12 @@
return green(quoteString(trunc)); // Quoted strings are green
}
default:
return inspectValue(value, ctx, level, inspectOptions);
return inspectValue(value, level, inspectOptions);
}
}
function inspectArray(
value,
ctx,
level,
inspectOptions,
) {
@ -575,7 +588,7 @@
typeName: "Array",
displayName: "",
delims: ["[", "]"],
entryHandler: (entry, ctx, level, inspectOptions, next) => {
entryHandler: (entry, level, inspectOptions, next) => {
const [index, val] = entry;
let i = index;
if (!value.hasOwnProperty(i)) {
@ -588,19 +601,18 @@
const ending = emptyItems > 1 ? "s" : "";
return dim(`<${emptyItems} empty item${ending}>`);
} else {
return inspectValueWithQuotes(val, ctx, level, inspectOptions);
return inspectValueWithQuotes(val, level, inspectOptions);
}
},
group: inspectOptions.compact,
sort: false,
};
return inspectIterable(value, ctx, level, options, inspectOptions);
return inspectIterable(value, level, options, inspectOptions);
}
function inspectTypedArray(
typedArrayName,
value,
ctx,
level,
inspectOptions,
) {
@ -609,19 +621,18 @@
typeName: typedArrayName,
displayName: `${typedArrayName}(${valueLength})`,
delims: ["[", "]"],
entryHandler: (entry, ctx, level, inspectOptions) => {
entryHandler: (entry, level, inspectOptions) => {
const val = entry[1];
return inspectValueWithQuotes(val, ctx, level + 1, inspectOptions);
return inspectValueWithQuotes(val, level + 1, inspectOptions);
},
group: inspectOptions.compact,
sort: false,
};
return inspectIterable(value, ctx, level, options, inspectOptions);
return inspectIterable(value, level, options, inspectOptions);
}
function inspectSet(
value,
ctx,
level,
inspectOptions,
) {
@ -629,19 +640,18 @@
typeName: "Set",
displayName: "Set",
delims: ["{", "}"],
entryHandler: (entry, ctx, level, inspectOptions) => {
entryHandler: (entry, level, inspectOptions) => {
const val = entry[1];
return inspectValueWithQuotes(val, ctx, level + 1, inspectOptions);
return inspectValueWithQuotes(val, level + 1, inspectOptions);
},
group: false,
sort: inspectOptions.sorted,
};
return inspectIterable(value, ctx, level, options, inspectOptions);
return inspectIterable(value, level, options, inspectOptions);
}
function inspectMap(
value,
ctx,
level,
inspectOptions,
) {
@ -649,23 +659,21 @@
typeName: "Map",
displayName: "Map",
delims: ["{", "}"],
entryHandler: (entry, ctx, level, inspectOptions) => {
entryHandler: (entry, level, inspectOptions) => {
const [key, val] = entry;
return `${
inspectValueWithQuotes(
key,
ctx,
level + 1,
inspectOptions,
)
} => ${inspectValueWithQuotes(val, ctx, level + 1, inspectOptions)}`;
} => ${inspectValueWithQuotes(val, level + 1, inspectOptions)}`;
},
group: false,
sort: inspectOptions.sorted,
};
return inspectIterable(
value,
ctx,
level,
options,
inspectOptions,
@ -716,7 +724,6 @@
function inspectPromise(
value,
ctx,
level,
inspectOptions,
) {
@ -736,7 +743,6 @@
const str = `${prefix}${
inspectValueWithQuotes(
result,
ctx,
level + 1,
inspectOptions,
)
@ -751,18 +757,14 @@
function inspectProxy(
targetAndHandler,
ctx,
level,
inspectOptions,
) {
return `Proxy ${
inspectArray(targetAndHandler, ctx, level, inspectOptions)
}`;
return `Proxy ${inspectArray(targetAndHandler, level, inspectOptions)}`;
}
function inspectRawObject(
value,
ctx,
level,
inspectOptions,
) {
@ -771,7 +773,6 @@
if (level >= inspectOptions.depth) {
return cyan("[Object]"); // wrappers are in cyan
}
ctx.add(value);
let baseString;
@ -812,7 +813,6 @@
const inspectedValue = error == null
? inspectValueWithQuotes(
propertyValue,
ctx,
level + 1,
inspectOptions,
)
@ -827,7 +827,7 @@
} else {
entries.push(
`${maybeQuoteString(key)}: ${
inspectValueWithQuotes(value[key], ctx, level + 1, inspectOptions)
inspectValueWithQuotes(value[key], level + 1, inspectOptions)
}`,
);
}
@ -853,7 +853,6 @@
const inspectedValue = error == null
? inspectValueWithQuotes(
propertyValue,
ctx,
level + 1,
inspectOptions,
)
@ -868,7 +867,7 @@
} else {
entries.push(
`[${maybeQuoteSymbol(key)}]: ${
inspectValueWithQuotes(value[key], ctx, level + 1, inspectOptions)
inspectValueWithQuotes(value[key], level + 1, inspectOptions)
}`,
);
}
@ -879,8 +878,6 @@
const totalLength = entries.length + level +
colors.stripColor(entries.join("")).length;
ctx.delete(value);
if (entries.length === 0) {
baseString = "{}";
} else if (totalLength > LINE_BREAKING_LENGTH || !inspectOptions.compact) {
@ -902,7 +899,6 @@
function inspectObject(
value,
consoleContext,
level,
inspectOptions,
) {
@ -927,7 +923,7 @@
if (value instanceof Error) {
return String(value.stack);
} else if (Array.isArray(value)) {
return inspectArray(value, consoleContext, level, inspectOptions);
return inspectArray(value, level, inspectOptions);
} else if (value instanceof Number) {
return inspectNumberObject(value, inspectOptions);
} else if (value instanceof Boolean) {
@ -935,15 +931,15 @@
} else if (value instanceof String) {
return inspectStringObject(value, inspectOptions);
} else if (value instanceof Promise) {
return inspectPromise(value, consoleContext, level, inspectOptions);
return inspectPromise(value, level, inspectOptions);
} else if (value instanceof RegExp) {
return inspectRegExp(value, inspectOptions);
} else if (value instanceof Date) {
return inspectDate(value, inspectOptions);
} else if (value instanceof Set) {
return inspectSet(value, consoleContext, level, inspectOptions);
return inspectSet(value, level, inspectOptions);
} else if (value instanceof Map) {
return inspectMap(value, consoleContext, level, inspectOptions);
return inspectMap(value, level, inspectOptions);
} else if (value instanceof WeakSet) {
return inspectWeakSet(inspectOptions);
} else if (value instanceof WeakMap) {
@ -952,13 +948,12 @@
return inspectTypedArray(
Object.getPrototypeOf(value).constructor.name,
value,
consoleContext,
level,
inspectOptions,
);
} else {
// Otherwise, default object formatting
return inspectRawObject(value, consoleContext, level, inspectOptions);
return inspectRawObject(value, level, inspectOptions);
}
}
@ -1408,7 +1403,6 @@
// Format as an object.
formattedArg = inspectValue(
args[a++],
new Set(),
0,
rInspectOptions,
);
@ -1451,7 +1445,7 @@
string += args[a];
} else {
// Use default maximum depth for null or undefined arguments.
string += inspectValue(args[a], new Set(), 0, rInspectOptions);
string += inspectValue(args[a], 0, rInspectOptions);
}
}
@ -1615,7 +1609,7 @@
}
const stringifyValue = (value) =>
inspectValueWithQuotes(value, new Set(), 0, {
inspectValueWithQuotes(value, 0, {
...DEFAULT_INSPECT_OPTIONS,
depth: 1,
});
@ -1774,7 +1768,7 @@
value,
inspectOptions = {},
) {
return inspectValue(value, new Set(), 0, {
return inspectValue(value, 0, {
...DEFAULT_INSPECT_OPTIONS,
...inspectOptions,
// TODO(nayeemrmn): Indent level is not supported.