/
evaluation.js
122 lines (109 loc) 路 3.97 KB
/
evaluation.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
const traverse = require("../lib").default;
const assert = require("assert");
const parse = require("babylon").parse;
function getPath(code) {
const ast = parse(code);
let path;
traverse(ast, {
Program: function (_path) {
path = _path;
_path.stop();
}
});
return path;
}
describe("evaluation", function () {
describe("evaluateTruthy", function () {
it("it should work with null", function () {
assert.strictEqual(
getPath("false || a.length === 0;").get("body")[0].evaluateTruthy(),
undefined
);
});
it("it should not mistake lack of confidence for falsy", function () {
assert.strictEqual(
getPath("foo || 'bar'").get("body")[0].evaluate().value,
undefined
);
});
});
it("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
);
});
it("should work with repeated, indeterminate identifiers", function () {
assert.strictEqual(
getPath("var num = foo(); (num > 0 && num < 100);").get("body")[1].evaluateTruthy(),
undefined
);
});
it("should work with repeated, determinate identifiers", function () {
assert.strictEqual(
getPath("var num = 5; (num > 0 && num < 100);").get("body")[1].evaluateTruthy(),
true
);
});
it("should deopt when var is redeclared in the same scope", function () {
assert.strictEqual(
getPath("var x = 2; var y = x + 2; { var x = 3 }")
.get("body.1.declarations.0.init").evaluate().confident,
false
);
});
it("it should not deopt vars in different scope", function () {
const input = "var a = 5; function x() { var a = 5; var b = a + 1; } var b = a + 2";
assert.strictEqual(
getPath(input).get("body.1.body.body.1.declarations.0.init").evaluate().value,
6
);
assert.strictEqual(
getPath(input).get("body.2.declarations.0.init").evaluate().value,
7
);
});
it("it should not deopt let/const inside blocks", function () {
assert.strictEqual(
getPath("let x = 5; { let x = 1; } let y = x + 5")
.get("body.2.declarations.0.init").evaluate().value,
10
);
const constExample = "const d = true; if (d && true || false) { const d = false; d && 5; }";
assert.strictEqual(
getPath(constExample).get("body.1.test").evaluate().value,
true
);
assert.strictEqual(
getPath(constExample).get("body.1.consequent.body.1").evaluate().value,
false
);
});
it("should deopt ids that are referenced before the bindings", function () {
assert.strictEqual(
getPath("let x = y + 5; let y = 5;").get("body.0.declarations.0.init").evaluate().confident,
false
);
assert.strictEqual(
getPath("if (typeof x === 'undefined') var x = {}")
.get("body.0.test").evaluate().confident,
false
);
});
it("should evaluate undefined, NaN and Infinity", () => {
assert.strictEqual(getPath("undefined").get("body.0.expression").evaluate().confident, true);
assert.strictEqual(getPath("NaN").get("body.0.expression").evaluate().confident, true);
assert.strictEqual(getPath("Infinity").get("body.0.expression").evaluate().confident, true);
});
it("should deopt redefined primitives - undefined, NaN and Infinity", () => {
const eval_undef = getPath("let undefined; undefined;").get("body.1.expression").evaluate();
const eval_nan = getPath("let NaN; NaN;").get("body.1.expression").evaluate();
const eval_inf = getPath("let Infinity; Infinity;").get("body.1.expression").evaluate();
assert.strictEqual(eval_undef.confident, false);
assert.strictEqual(eval_nan.confident, false);
assert.strictEqual(eval_inf.confident, false);
assert.strictEqual(eval_undef.deopt.type, "VariableDeclarator");
assert.strictEqual(eval_undef.deopt.parentPath.node.kind, "let");
});
});