From b2390cca0233bf38ea081ca756211b4fdb74f68c Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Sun, 19 Jun 2016 22:08:39 +0100 Subject: [PATCH] deopt on recursion in path.evaluate - fixes T7397 --- .../babel-traverse/src/path/evaluation.js | 30 +++++++++++++++++++ packages/babel-traverse/test/evaluation.js | 7 +++++ 2 files changed, 37 insertions(+) diff --git a/packages/babel-traverse/src/path/evaluation.js b/packages/babel-traverse/src/path/evaluation.js index 904fc6f720..6f63987b2a 100644 --- a/packages/babel-traverse/src/path/evaluation.js +++ b/packages/babel-traverse/src/path/evaluation.js @@ -51,6 +51,7 @@ export function evaluateTruthy(): boolean { export function evaluate(): { confident: boolean; value: any } { let confident = true; let deoptPath: ?NodePath; + let seen = new Map; function deopt(path) { if (!confident) return; @@ -66,7 +67,36 @@ export function evaluate(): { confident: boolean; value: any } { value: value }; + // we wrap the _evaluate method so we can track `seen` nodes, we push an item + // to the map before we actually evaluate it so we can deopt on self recursive + // nodes such as: + // + // var g = a ? 1 : 2, + // a = g * this.foo + // function evaluate(path) { + let { node } = path; + + if (seen.has(node)) { + let existing = seen.get(node); + if (existing.resolved) { + return existing.value; + } else { + deopt(path); + return; + } + } else { + let item = { resolved: false }; + seen.set(node, item); + + let val = _evaluate(path); + item.resolved = true; + item.value = value; + return val; + } + } + + function _evaluate(path) { if (!confident) return; let { node } = path; diff --git a/packages/babel-traverse/test/evaluation.js b/packages/babel-traverse/test/evaluation.js index 2b69108c80..ecc8c6eba7 100644 --- a/packages/babel-traverse/test/evaluation.js +++ b/packages/babel-traverse/test/evaluation.js @@ -30,4 +30,11 @@ suite("evaluation", function () { ); }); }); + + test("should bail out on recursive evaluation", function () { + assert.strictEqual( + getPath("function fn(a) { var g = a ? 1 : 2, a = g * this.foo; }").get("body.0.body.body.0.declarations.1.init").evaluate().confident, + false + ); + }); });