Skip to content

Commit bac4f17

Browse files
Avoid infinite recursion in resolveSource (#256)
1 parent e6ab125 commit bac4f17

3 files changed

Lines changed: 45 additions & 13 deletions

File tree

packages/babel-helper-define-polyfill-provider/src/utils.ts

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,20 @@ export function has(object: any, key: string) {
2222

2323
function resolve(
2424
path: NodePath,
25-
resolved: Set<NodePath> = new Set(),
25+
seen: Set<NodePath> = new Set(),
2626
): NodePath | undefined {
27-
if (resolved.has(path)) return;
28-
resolved.add(path);
27+
if (seen.has(path)) return;
28+
seen.add(path);
2929

3030
if (path.isVariableDeclarator()) {
3131
if (path.get("id").isIdentifier()) {
32-
return resolve(path.get("init"), resolved);
32+
return resolve(path.get("init"), seen);
3333
}
3434
} else if (path.isReferencedIdentifier()) {
3535
const binding = path.scope.getBinding(path.node.name);
3636
if (!binding) return path;
3737
if (!binding.constant) return;
38-
return resolve(binding.path, resolved);
38+
return resolve(binding.path, seen);
3939
}
4040
return path;
4141
}
@@ -104,15 +104,23 @@ export function resolveKey(
104104
}
105105
}
106106

107-
export function resolveInstance(obj: NodePath): string | null {
108-
const source = resolveSource(obj);
107+
function resolveInstance(obj: NodePath, seen: Set<NodePath>): string | null {
108+
const source = resolveSource(obj, seen);
109109
return source.placement === "prototype" ? source.id : null;
110110
}
111111

112-
export function resolveSource(obj: NodePath): {
112+
export function resolveSource(
113+
obj: NodePath,
114+
seen: Set<NodePath>,
115+
): {
113116
id: string | null;
114117
placement: "prototype" | "static" | null;
115118
} {
119+
if (seen.has(obj)) {
120+
return { id: null, placement: null };
121+
}
122+
seen.add(obj);
123+
116124
if (
117125
obj.isMemberExpression() &&
118126
obj.get("property").isIdentifier({ name: "prototype" })
@@ -175,6 +183,7 @@ export function resolveSource(obj: NodePath): {
175183
if (operator === "-" || operator === "~") {
176184
const arg = resolveInstance(
177185
(path as NodePath<t.UnaryExpression>).get("argument"),
186+
seen,
178187
);
179188
if (arg === "BigInt") return { id: "BigInt", placement: "prototype" };
180189
if (arg !== null) return { id: "Number", placement: "prototype" };
@@ -186,6 +195,7 @@ export function resolveSource(obj: NodePath): {
186195
case "UpdateExpression": {
187196
const arg = resolveInstance(
188197
(path as NodePath<t.UpdateExpression>).get("argument"),
198+
seen,
189199
);
190200
if (arg === "BigInt") return { id: "BigInt", placement: "prototype" };
191201
if (arg !== null) return { id: "Number", placement: "prototype" };
@@ -227,9 +237,11 @@ export function resolveSource(obj: NodePath): {
227237
) {
228238
const left = resolveInstance(
229239
(path as NodePath<t.BinaryExpression>).get("left"),
240+
seen,
230241
);
231242
const right = resolveInstance(
232243
(path as NodePath<t.BinaryExpression>).get("right"),
244+
seen,
233245
);
234246
if (left === "BigInt" && right === "BigInt") {
235247
return { id: "BigInt", placement: "prototype" };
@@ -243,9 +255,11 @@ export function resolveSource(obj: NodePath): {
243255
if (operator === "+") {
244256
const left = resolveInstance(
245257
(path as NodePath<t.BinaryExpression>).get("left"),
258+
seen,
246259
);
247260
const right = resolveInstance(
248261
(path as NodePath<t.BinaryExpression>).get("right"),
262+
seen,
249263
);
250264
if (left === "String" || right === "String") {
251265
return { id: "String", placement: "prototype" };
@@ -264,13 +278,14 @@ export function resolveSource(obj: NodePath): {
264278
const expressions = (path as NodePath<t.SequenceExpression>).get(
265279
"expressions",
266280
);
267-
return resolveSource(expressions[expressions.length - 1]);
281+
return resolveSource(expressions[expressions.length - 1], seen);
268282
}
269283
// a = b -> the result is the right side
270284
case "AssignmentExpression": {
271285
if ((path.node as t.AssignmentExpression).operator === "=") {
272286
return resolveSource(
273287
(path as NodePath<t.AssignmentExpression>).get("right"),
288+
seen,
274289
);
275290
}
276291
return { id: null, placement: null };
@@ -279,9 +294,11 @@ export function resolveSource(obj: NodePath): {
279294
case "ConditionalExpression": {
280295
const consequent = resolveSource(
281296
(path as NodePath<t.ConditionalExpression>).get("consequent"),
297+
seen,
282298
);
283299
const alternate = resolveSource(
284300
(path as NodePath<t.ConditionalExpression>).get("alternate"),
301+
seen,
285302
);
286303
if (consequent.id && consequent.id === alternate.id) {
287304
return consequent;
@@ -292,6 +309,7 @@ export function resolveSource(obj: NodePath): {
292309
case "ParenthesizedExpression":
293310
return resolveSource(
294311
(path as NodePath<t.ParenthesizedExpression>).get("expression"),
312+
seen,
295313
);
296314
// TypeScript / Flow type wrappers -> unwrap to the inner expression
297315
case "TSAsExpression":
@@ -300,7 +318,7 @@ export function resolveSource(obj: NodePath): {
300318
case "TSInstantiationExpression":
301319
case "TSTypeAssertion":
302320
case "TypeCastExpression":
303-
return resolveSource(path.get("expression") as NodePath);
321+
return resolveSource(path.get("expression") as NodePath, seen);
304322
}
305323

306324
return { id: null, placement: null };

packages/babel-helper-define-polyfill-provider/src/visitors/usage.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ export default (callProvider: CallProvider) => {
6868
}
6969
}
7070

71-
const source = resolveSource(object);
71+
const source = resolveSource(object, new Set());
7272
const skipObject = property(source.id, key, source.placement, path);
7373
const canHandleObject =
7474
objectIsGlobalIdentifier &&
@@ -107,7 +107,7 @@ export default (callProvider: CallProvider) => {
107107

108108
let id = null;
109109
let placement = null;
110-
if (obj) ({ id, placement } = resolveSource(obj));
110+
if (obj) ({ id, placement } = resolveSource(obj, new Set()));
111111

112112
for (const prop of path.get("properties")) {
113113
if (prop.isObjectProperty()) {
@@ -120,7 +120,7 @@ export default (callProvider: CallProvider) => {
120120
BinaryExpression(path: NodePath<t.BinaryExpression>) {
121121
if (path.node.operator !== "in") return;
122122

123-
const source = resolveSource(path.get("right"));
123+
const source = resolveSource(path.get("right"), new Set());
124124
const key = resolveKey(path.get("left"), true);
125125

126126
if (!key) return;

packages/babel-helper-define-polyfill-provider/test/descriptors.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -895,4 +895,18 @@ describe("descriptors", () => {
895895
placement: null,
896896
});
897897
});
898+
899+
it("instance property on self-referencing variable", () => {
900+
const [desc] = getDescriptor(
901+
"var n = n ? n : {}; n.foo;",
902+
d => d.kind === "property" && d.key === "foo",
903+
);
904+
905+
expect(desc).toEqual({
906+
kind: "property",
907+
object: null,
908+
key: "foo",
909+
placement: null,
910+
});
911+
});
898912
});

0 commit comments

Comments
 (0)