Skip to content

Commit

Permalink
Expand evaluation of global built-ins in @babel/traverse (#15797)
Browse files Browse the repository at this point in the history
* add global built-in

* add test for global built-in

* fix lint errors

* remove btoa and atob functions

* rename of constants and methods

* retrigger checks

* prettier changes file

* add node version check

* add tests for the node version check

* add a check for the split node version

* Update packages/babel-traverse/src/path/evaluation.ts

Co-authored-by: Hu谩ng J霉nli脿ng <jlhwung@gmail.com>

* remove unused constant

* update btoa and atob tests

* Update packages/babel-traverse/test/evaluation.js

Co-authored-by: Hu谩ng J霉nli脿ng <jlhwung@gmail.com>

---------

Co-authored-by: = <=>
Co-authored-by: Hu谩ng J霉nli脿ng <jlhwung@gmail.com>
  • Loading branch information
lorenzoferre and JLHwung committed Sep 25, 2023
1 parent 54d30f2 commit 38ee8b4
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 5 deletions.
35 changes: 30 additions & 5 deletions packages/babel-traverse/src/path/evaluation.ts
Expand Up @@ -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,
);
Expand Down Expand Up @@ -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];
}
Expand All @@ -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];
Expand Down
55 changes: 55 additions & 0 deletions packages/babel-traverse/test/evaluation.js
Expand Up @@ -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";
Expand Down

0 comments on commit 38ee8b4

Please sign in to comment.