Skip to content

Commit

Permalink
Avoid infinite loops in type inference logic (babel#12390)
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolo-ribaudo committed Dec 2, 2020
1 parent 898f322 commit 6402dd9
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 6 deletions.
27 changes: 21 additions & 6 deletions packages/babel-traverse/src/path/inference/index.js
Expand Up @@ -14,6 +14,11 @@ export function getTypeAnnotation(): Object {
return (this.typeAnnotation = type);
}

// Used to avoid infinite recursion in cases like
// var b, c; if (0) { c = 1; b = c; } c = b;
// It also works with indirect recursion.
const typeAnnotationInferringNodes = new WeakSet();

/**
* todo: split up this method
*/
Expand Down Expand Up @@ -47,14 +52,24 @@ export function _getTypeAnnotation(): ?Object {
return node.typeAnnotation;
}

let inferer = inferers[node.type];
if (inferer) {
return inferer.call(this, node);
if (typeAnnotationInferringNodes.has(node)) {
// Bail out from type inference to avoid infinite loops
return;
}
typeAnnotationInferringNodes.add(node);

try {
let inferer = inferers[node.type];
if (inferer) {
return inferer.call(this, node);
}

inferer = inferers[this.parentPath.type];
if (inferer?.validParent) {
return this.parentPath.getTypeAnnotation();
inferer = inferers[this.parentPath.type];
if (inferer?.validParent) {
return this.parentPath.getTypeAnnotation();
}
} finally {
typeAnnotationInferringNodes.delete(node);
}
}

Expand Down
16 changes: 16 additions & 0 deletions packages/babel-traverse/test/inference.js
Expand Up @@ -289,5 +289,21 @@ describe("inference", function () {
const type = path.getTypeAnnotation();
expect(t.isAnyTypeAnnotation(type)).toBeTruthy();
});
it("should not cause a stack overflow when two variable depend on eachother", function () {
const path = getPath(`
var b, c;
while (0) {
c = 1;
b = c;
}
c = b;
`).get("body.2.expression");

expect(path.toString()).toBe("c = b");

// Note: this could technically be "number | void", but the cycle detection
// logic just bails out to "any" to avoid infinite loops.
expect(path.getTypeAnnotation()).toEqual({ type: "AnyTypeAnnotation" });
});
});
});

0 comments on commit 6402dd9

Please sign in to comment.