Skip to content

Commit fd51ebf

Browse files
committed
Minor stringify cleanup, added cycle detection for AssertionLevel.Aggresive only.
1 parent aa5e576 commit fd51ebf

1 file changed

Lines changed: 51 additions & 7 deletions

File tree

src/compiler/utilities.ts

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2414,26 +2414,70 @@ namespace ts {
24142414
return output;
24152415
}
24162416

2417+
/**
2418+
* Serialize an object graph into a JSON string. This is intended only for use on an acyclic graph
2419+
* as the fallback implementation does not check for circular references by default.
2420+
*/
24172421
export const stringify: (value: any) => string = JSON && JSON.stringify ? JSON.stringify : function stringify(value: any): string {
2422+
if (Debug.shouldAssert(AssertionLevel.Aggressive)) {
2423+
Debug.assert(!hasCycles(value, []), "Detected circular reference before serializing object graph.");
2424+
}
2425+
24182426
return value === undefined ? undefined : stringifyValue(value);
24192427
};
24202428

2421-
function stringifyValue(value: any): string {
2429+
function hasCycles(value: any, stack: any[]) {
24222430
/* tslint:disable:no-null */
2423-
return value === null ? "null" // explicit test for `null` as `typeof null` is "object"
2424-
: typeof value === "string" ? `"${escapeString(value)}"`
2425-
: typeof value === "number" ? String(value)
2431+
if (typeof value !== "object" || value === null) {
2432+
return false;
2433+
}
2434+
/* tslint:enable:no-null */
2435+
2436+
if (stack.lastIndexOf(value) !== -1) {
2437+
return true;
2438+
}
2439+
2440+
stack.push(value);
2441+
2442+
if (isArray(value)) {
2443+
for (const entry of value) {
2444+
if (hasCycles(entry, stack)) {
2445+
return true;
2446+
}
2447+
}
2448+
}
2449+
else {
2450+
for (const key in value) {
2451+
if (hasProperty(value, key) && hasCycles(value[key], stack)) {
2452+
return true;
2453+
}
2454+
}
2455+
}
2456+
2457+
stack.pop();
2458+
return false;
2459+
}
2460+
2461+
function stringifyValue(value: any): string {
2462+
return typeof value === "string" ? `"${escapeString(value)}"`
2463+
: typeof value === "number" ? isFinite(value) ? String(value) : "null"
24262464
: typeof value === "boolean" ? value ? "true" : "false"
2427-
: isArray(value) ? `[${reduceLeft(value, stringifyElement, "")}]`
2428-
: typeof value === "object" ? `{${reduceProperties(value, stringifyProperty, "")}}`
2465+
: typeof value === "object" ? isArray(value) ? stringifyArray(value) : stringifyObject(value)
24292466
: /*fallback*/ "null";
2430-
/* tslint:enable:no-null */
2467+
}
2468+
2469+
function stringifyArray(value: any) {
2470+
return `[${reduceLeft(value, stringifyElement, "")}]`;
24312471
}
24322472

24332473
function stringifyElement(memo: string, value: any) {
24342474
return (memo ? memo + "," : memo) + stringifyValue(value);
24352475
}
24362476

2477+
function stringifyObject(value: any) {
2478+
return value ? `{${reduceProperties(value, stringifyProperty, "")}}` : "null";
2479+
}
2480+
24372481
function stringifyProperty(memo: string, value: any, key: string) {
24382482
return value === undefined || typeof value === "function" ? memo
24392483
: (memo ? memo + "," : memo) + `"${escapeString(key)}":${stringifyValue(value)}`;

0 commit comments

Comments
 (0)