Skip to content

Commit 985f051

Browse files
authored
fix: "Map maximum size exceeded" in deepClone (#17142)
1 parent 62ed12a commit 985f051

2 files changed

Lines changed: 90 additions & 8 deletions

File tree

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,73 @@
1-
//https://github.com/babel/babel/pull/14583#discussion_r882828856
2-
function deepClone(value: any, cache: Map<any, any>): any {
1+
const circleSet = new Set();
2+
let depth = 0;
3+
// https://github.com/babel/babel/pull/14583#discussion_r882828856
4+
function deepClone(
5+
value: any,
6+
cache: Map<any, any>,
7+
allowCircle: boolean,
8+
): any {
39
if (value !== null) {
4-
if (cache.has(value)) return cache.get(value);
10+
if (allowCircle) {
11+
if (cache.has(value)) return cache.get(value);
12+
} else if (++depth > 250) {
13+
if (circleSet.has(value)) {
14+
depth = 0;
15+
circleSet.clear();
16+
throw new Error("Babel-deepClone: Cycles are not allowed in AST");
17+
}
18+
circleSet.add(value);
19+
}
520
let cloned: any;
621
if (Array.isArray(value)) {
722
cloned = new Array(value.length);
8-
cache.set(value, cloned);
23+
if (allowCircle) cache.set(value, cloned);
924
for (let i = 0; i < value.length; i++) {
1025
cloned[i] =
11-
typeof value[i] !== "object" ? value[i] : deepClone(value[i], cache);
26+
typeof value[i] !== "object"
27+
? value[i]
28+
: deepClone(value[i], cache, allowCircle);
1229
}
1330
} else {
1431
cloned = {};
15-
cache.set(value, cloned);
32+
if (allowCircle) cache.set(value, cloned);
1633
const keys = Object.keys(value);
1734
for (let i = 0; i < keys.length; i++) {
1835
const key = keys[i];
1936
cloned[key] =
2037
typeof value[key] !== "object"
2138
? value[key]
22-
: deepClone(value[key], cache);
39+
: deepClone(
40+
value[key],
41+
cache,
42+
allowCircle ||
43+
key === "leadingComments" ||
44+
key === "innerComments" ||
45+
key === "trailingComments" ||
46+
key === "extra",
47+
);
2348
}
2449
}
50+
if (!allowCircle) {
51+
if (depth-- > 250) circleSet.delete(value);
52+
}
2553
return cloned;
2654
}
2755
return value;
2856
}
2957

3058
export default function <T>(value: T): T {
3159
if (typeof value !== "object") return value;
32-
return deepClone(value, new Map());
60+
61+
if (process.env.BABEL_8_BREAKING) {
62+
if (!process.env.IS_PUBLISH && depth > 0) {
63+
throw new Error("depth > 0");
64+
}
65+
return deepClone(value, new Map(), false);
66+
} else {
67+
try {
68+
return deepClone(value, new Map(), true);
69+
} catch (_) {
70+
return structuredClone(value);
71+
}
72+
}
3373
}

packages/babel-core/test/transformation.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import runner from "@babel/helper-transform-fixture-test-runner";
22
import { fileURLToPath } from "url";
33
import path from "path";
4+
import cloneDeep from "../lib/transformation/util/clone-deep.js";
5+
import { itBabel8 } from "$repo-utils";
46

57
(runner.default || runner)(
68
path.join(
@@ -9,3 +11,43 @@ import path from "path";
911
),
1012
"transformation",
1113
);
14+
15+
describe("util", () => {
16+
const clone = cloneDeep.default || cloneDeep;
17+
itBabel8("deep clone", () => {
18+
const circle = {};
19+
circle.circle = circle;
20+
const object = {};
21+
22+
const ast = {
23+
type: "Program",
24+
extra: {
25+
circle,
26+
circle2: circle,
27+
},
28+
object,
29+
object2: object,
30+
};
31+
const cloned = clone(ast);
32+
33+
expect(cloned.object === ast.object).toBe(false);
34+
expect(cloned.object === cloned.object2).toBe(false);
35+
36+
expect(cloned.extra.circle === ast.extra.circle).toBe(false);
37+
expect(cloned.extra.circle === cloned.extra.circle2).toBe(true);
38+
expect(cloned.extra.circle === cloned.extra.circle.circle).toBe(true);
39+
});
40+
41+
itBabel8("deep clone circle", () => {
42+
const circle = {};
43+
circle.circle = circle;
44+
45+
const ast = {
46+
type: "Program",
47+
circle,
48+
};
49+
expect(() => {
50+
clone(ast);
51+
}).toThrow("Babel-deepClone: Cycles are not allowed in AST");
52+
});
53+
});

0 commit comments

Comments
 (0)