Forward deopt node path in NodePath#evaludate (#11832)

* Forward deopt node path

* Move deopt evaluation tests

* Document evaluate deopt property
This commit is contained in:
Johan Holmerin 2020-08-20 22:20:19 +02:00 committed by GitHub
parent bd07cda4a5
commit b9407d7660
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 36 additions and 8 deletions

View File

@ -219,7 +219,7 @@ function _evaluate(path, state) {
if (elemValue.confident) {
arr.push(elemValue.value);
} else {
return deopt(elem, state);
return deopt(elemValue.deopt, state);
}
}
return arr;
@ -237,7 +237,7 @@ function _evaluate(path, state) {
if (prop.node.computed) {
key = key.evaluate();
if (!key.confident) {
return deopt(keyPath, state);
return deopt(key.deopt, state);
}
key = key.value;
} else if (key.isIdentifier()) {
@ -248,7 +248,7 @@ function _evaluate(path, state) {
const valuePath = prop.get("value");
let value = valuePath.evaluate();
if (!value.confident) {
return deopt(valuePath, state);
return deopt(value.deopt, state);
}
value = value.value;
obj[key] = value;
@ -407,19 +407,24 @@ function evaluateQuasis(path, quasis: Array<Object>, state, raw = false) {
/**
* Walk the input `node` and statically evaluate it.
*
* Returns an object in the form `{ confident, value }`. `confident` indicates
* whether or not we had to drop out of evaluating the expression because of
* hitting an unknown node that we couldn't confidently find the value of.
* Returns an object in the form `{ confident, value, deopt }`. `confident`
* indicates whether or not we had to drop out of evaluating the expression
* because of hitting an unknown node that we couldn't confidently find the
* value of, in which case `deopt` is the path of said node.
*
* Example:
*
* t.evaluate(parse("5 + 5")) // { confident: true, value: 10 }
* t.evaluate(parse("!true")) // { confident: true, value: false }
* t.evaluate(parse("foo + foo")) // { confident: false, value: undefined }
* t.evaluate(parse("foo + foo")) // { confident: false, value: undefined, deopt: NodePath }
*
*/
export function evaluate(): { confident: boolean, value: any } {
export function evaluate(): {
confident: boolean,
value: any,
deopt?: NodePath,
} {
const state = {
confident: true,
deoptPath: null,

View File

@ -13,6 +13,25 @@ function getPath(code) {
return path;
}
function addDeoptTest(code, type, expectedType) {
it(type + " deopt: " + code, function () {
const visitor = {};
visitor[type] = function (path) {
const evaluate = path.evaluate();
expect(evaluate.confident).toBeFalsy();
expect(evaluate.deopt.type).toEqual(expectedType);
};
traverse(
parse(code, {
plugins: ["*"],
}),
visitor,
);
});
}
describe("evaluation", function () {
describe("evaluateTruthy", function () {
it("it should work with null", function () {
@ -228,4 +247,8 @@ describe("evaluation", function () {
expect(result.deopt).toBeNull();
expect(result.value).toEqual(["foo", "bar"]);
});
addDeoptTest("({a:{b}})", "ObjectExpression", "Identifier");
addDeoptTest("({[a + 'b']: 1})", "ObjectExpression", "Identifier");
addDeoptTest("[{a}]", "ArrayExpression", "Identifier");
});