From a6e0ec83716685882af4328e5f622523d3df237f Mon Sep 17 00:00:00 2001 From: Andrew Imm Date: Mon, 31 Aug 2015 17:38:38 -0700 Subject: [PATCH] Allow more certainty when evaluating Logical Expressions --- .../babel/src/traversal/path/evaluation.js | 20 ++- packages/babel/test/evaluation.js | 164 ++++++++++++++++++ 2 files changed, 182 insertions(+), 2 deletions(-) create mode 100644 packages/babel/test/evaluation.js diff --git a/packages/babel/src/traversal/path/evaluation.js b/packages/babel/src/traversal/path/evaluation.js index 36c6f159f6..50ab6ce623 100644 --- a/packages/babel/src/traversal/path/evaluation.js +++ b/packages/babel/src/traversal/path/evaluation.js @@ -134,12 +134,28 @@ export function evaluate(): { confident: boolean; value: any } { } if (path.isLogicalExpression()) { + // If we are confident that one side of an && is false, or one side of + // an || is true, we can be confident about the entire expression + let wasConfident = confident; let left = evaluate(path.get("left")); + let leftConfident = confident; + confident = wasConfident; let right = evaluate(path.get("right")); + let rightConfident = confident; + let uncertain = leftConfident !== rightConfident; + confident = leftConfident && rightConfident; switch (node.operator) { - case "||": return left || right; - case "&&": return left && right; + case "||": + if ((left || right) && uncertain) { + confident = true; + } + return left || right; + case "&&": + if ((!left && leftConfident) || (!right && rightConfident)) { + confident = true; + } + return left && right; } } diff --git a/packages/babel/test/evaluation.js b/packages/babel/test/evaluation.js new file mode 100644 index 0000000000..632691bb0d --- /dev/null +++ b/packages/babel/test/evaluation.js @@ -0,0 +1,164 @@ +var evaluation = require("../lib/traversal/path/evaluation"); +var traverse = require('../lib/traversal'); +var parse = require("../lib/helpers/parse"); +var assert = require("assert"); + +suite("evaluation", function () { + test("binary expression", function () { + traverse(parse("5 + 5"), { + enter: function (node) { + if (this.isBinaryExpression()) { + assert.deepEqual(this.evaluate(), { confident: true, value: 10 }); + } + } + }); + + traverse(parse("'str' === 'str'"), { + enter: function(node) { + if (this.isBinaryExpression()) { + assert.deepEqual(this.evaluate(), { confident: true, value: true }); + } + } + }); + + traverse(parse("'four' === 4"), { + enter: function(node) { + if (this.isBinaryExpression()) { + assert.deepEqual(this.evaluate(), { confident: true, value: false }); + } + } + }); + }); + + test("logical expression", function () { + traverse(parse("'abc' === 'abc' && 1 === 1"), { + enter: function(node) { + if (this.isLogicalExpression()) { + assert.deepEqual(this.evaluate(), { confident: true, value: true }); + } + } + }); + + traverse(parse("'abc' === 'abc' && 1 === 10"), { + enter: function(node) { + if (this.isLogicalExpression()) { + assert.deepEqual(this.evaluate(), { confident: true, value: false }); + } + } + }); + + traverse(parse("'abc' === 'xyz' && 1 === 1"), { + enter: function(node) { + if (this.isLogicalExpression()) { + assert.deepEqual(this.evaluate(), { confident: true, value: false }); + } + } + }); + + traverse(parse("'abc' === 'xyz' && 1 === 10"), { + enter: function(node) { + if (this.isLogicalExpression()) { + assert.deepEqual(this.evaluate(), { confident: true, value: false }); + } + } + }); + + traverse(parse("'abc' === 'abc' || 1 === 1"), { + enter: function(node) { + if (this.isLogicalExpression()) { + assert.deepEqual(this.evaluate(), { confident: true, value: true }); + } + } + }); + + traverse(parse("'abc' === 'abc' || 1 === 10"), { + enter: function(node) { + if (this.isLogicalExpression()) { + assert.deepEqual(this.evaluate(), { confident: true, value: true }); + } + } + }); + + traverse(parse("'abc' === 'xyz' || 1 === 1"), { + enter: function(node) { + if (this.isLogicalExpression()) { + assert.deepEqual(this.evaluate(), { confident: true, value: true }); + } + } + }); + + traverse(parse("'abc' === 'xyz' || 1 === 10"), { + enter: function(node) { + if (this.isLogicalExpression()) { + assert.deepEqual(this.evaluate(), { confident: true, value: false }); + } + } + }); + }); + + test("logical expression without certainty", function () { + traverse(parse("'abc' === 'abc' || config.flag === 1"), { + enter: function(node) { + if (this.isLogicalExpression()) { + assert.deepEqual(this.evaluate(), { confident: true, value: true }); + } + } + }); + + traverse(parse("obj.a === 'abc' || config.flag === 1"), { + enter: function(node) { + if (this.isLogicalExpression()) { + assert.deepEqual(this.evaluate(), { confident: false, value: undefined }); + } + } + }); + + traverse(parse("'abc' !== 'abc' && config.flag === 1"), { + enter: function(node) { + if (this.isLogicalExpression()) { + assert.deepEqual(this.evaluate(), { confident: true, value: false }); + } + } + }); + + traverse(parse("obj.a === 'abc' && 1 === 1"), { + enter: function(node) { + if (this.isLogicalExpression()) { + assert.deepEqual(this.evaluate(), { confident: false, value: undefined }); + } + } + }); + + traverse(parse("'abc' === 'abc' && (1 === 1 || config.flag)"), { + enter: function(node) { + if (this.isLogicalExpression()) { + assert.deepEqual(this.evaluate(), { confident: true, value: true }); + } + } + }); + + traverse(parse("'abc' === 'xyz' || (1 === 1 && config.flag)"), { + enter: function(node) { + if (this.isLogicalExpression()) { + assert.deepEqual(this.evaluate(), { confident: false, value: undefined }); + } + } + }); + + traverse(parse("'abc' === 'xyz' || (1 === 1 && 'four' === 'four')"), { + enter: function(node) { + if (this.isLogicalExpression()) { + assert.deepEqual(this.evaluate(), { confident: true, value: true }); + } + } + }); + + traverse(parse("'abc' === 'abc' && (1 === 1 && 'four' === 'four')"), { + enter: function(node) { + if (this.isLogicalExpression()) { + assert.deepEqual(this.evaluate(), { confident: true, value: true }); + } + } + }); + }); +});