diff --git a/packages/babel-traverse/src/path/evaluation.ts b/packages/babel-traverse/src/path/evaluation.ts index cf3bb98316d0..9223dfebfcc8 100644 --- a/packages/babel-traverse/src/path/evaluation.ts +++ b/packages/babel-traverse/src/path/evaluation.ts @@ -3,11 +3,35 @@ import type * as t from "@babel/types"; // This file contains Babels metainterpreter that can evaluate static code. -const VALID_CALLEES = ["String", "Number", "Math"] as const; +const VALID_OBJECT_CALLEES = ["Number", "String", "Math"] as const; +const VALID_IDENTIFIER_CALLEES = [ + "isFinite", + "isNaN", + "parseFloat", + "parseInt", + "decodeURI", + "decodeURIComponent", + "encodeURI", + "encodeURIComponent", + process.env.BABEL_8_BREAKING ? "btoa" : null, + process.env.BABEL_8_BREAKING ? "atob" : null, +] as const; + const INVALID_METHODS = ["random"] as const; -function isValidCallee(val: string): val is (typeof VALID_CALLEES)[number] { - return VALID_CALLEES.includes( +function isValidObjectCallee( + val: string, +): val is (typeof VALID_OBJECT_CALLEES)[number] { + return VALID_OBJECT_CALLEES.includes( + // @ts-expect-error val is a string + val, + ); +} + +function isValidIdentifierCallee( + val: string, +): val is (typeof VALID_IDENTIFIER_CALLEES)[number] { + return VALID_IDENTIFIER_CALLEES.includes( // @ts-expect-error val is a string val, ); @@ -402,7 +426,8 @@ function _evaluate(path: NodePath, state: State): any { if ( callee.isIdentifier() && !path.scope.getBinding(callee.node.name) && - isValidCallee(callee.node.name) + (isValidObjectCallee(callee.node.name) || + isValidIdentifierCallee(callee.node.name)) ) { func = global[callee.node.name]; } @@ -415,7 +440,7 @@ function _evaluate(path: NodePath, state: State): any { if ( object.isIdentifier() && property.isIdentifier() && - isValidCallee(object.node.name) && + isValidObjectCallee(object.node.name) && !isInvalidMethod(property.node.name) ) { context = global[object.node.name]; diff --git a/packages/babel-traverse/test/evaluation.js b/packages/babel-traverse/test/evaluation.js index a1e2aadca3d7..a791df014d36 100644 --- a/packages/babel-traverse/test/evaluation.js +++ b/packages/babel-traverse/test/evaluation.js @@ -152,6 +152,61 @@ describe("evaluation", function () { expect(eval_invalid_call.confident).toBe(false); }); + it("should evaluate global call expressions", function () { + expect( + getPath("isFinite(1);").get("body.0.expression").evaluate().value, + ).toBe(true); + + expect( + getPath("isFinite(Infinity);").get("body.0.expression").evaluate().value, + ).toBe(false); + + expect( + getPath("isNaN(NaN);").get("body.0.expression").evaluate().value, + ).toBe(true); + + expect(getPath("isNaN(1);").get("body.0.expression").evaluate().value).toBe( + false, + ); + + expect( + getPath("parseFloat('1.1');").get("body.0.expression").evaluate().value, + ).toBe(1.1); + + expect( + getPath("parseFloat('1');").get("body.0.expression").evaluate().value, + ).toBe(1); + + expect( + getPath("encodeURI('x 1');").get("body.0.expression").evaluate().value, + ).toBe("x%201"); + + expect( + getPath("decodeURI('x%201');").get("body.0.expression").evaluate().value, + ).toBe("x 1"); + + expect( + getPath("encodeURIComponent('?x=1');").get("body.0.expression").evaluate() + .value, + ).toBe("%3Fx%3D1"); + + expect( + getPath("decodeURIComponent('%3Fx%3D1');") + .get("body.0.expression") + .evaluate().value, + ).toBe("?x=1"); + + if (process.env.BABEL_8_BREAKING) { + expect( + getPath("btoa('babel');").get("body.0.expression").evaluate().value, + ).toBe("YmFiZWw="); + + expect( + getPath("atob('YmFiZWw=');").get("body.0.expression").evaluate().value, + ).toBe("babel"); + } + }); + 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";