@alu0101353647/constant-folding
Version:
Constant Folding javascript code
198 lines (184 loc) • 7.06 kB
JavaScript
// See https://github.com/babel/minify/tree/master/packages/babel-plugin-minify-constant-folding
const fs = require("fs");
const deb = require('../src/deb.js');
const escodegen = require("escodegen");
const espree = require("espree");
const estraverse = require("estraverse");
const visit = require("ast-types").visit;
const recast = require("recast");
;
module.exports = {constantFolding, squashArray};
/**
* Function that takes an array which may have more arrays within and sets them all in the same
* cardinality. This is done recursively.
*
* For example, [3, [4, 5], [6], [[7], 8]] is transformed into [3, 4, 5, 6, 7, 8]
* @param {Node[]} array array of node which has all the arrays to be squashed within.
* @return {Node[]} array of Literal nodes.
*/
function squashArray(array, result = []) {
// console.log("DING DONG NEW CALL");
// console.log(array);
for (let element of array) {
// console.log(element);
// console.log("\n");
if (element.type == "ArrayExpression") {
// console.log("Result state rn: \n");
// for (let result_element of result) {
// console.log(result_element);
// }
squashArray(element.elements, result);
} else {
result.push(element);
}
}
// console.log("===========================");
return result;
}
/**
* A function that does the usual constant folding.
* However, it also does constant folding for methods and arguments,
* including lists as well.
* @param {string} code A string containing the code on which to fold
* constants.
* @returns {string} Returns the code, reparsed, with the constants and such
* folded.
*/
function constantFolding(code, pattern) {
let ast = espree.parse(code, { ecmaVersion: 7, loc: false });
visit(ast, {
visitBinaryExpression(path) {
const node = path.node;
this.traverse(path);
if (node.left.type == "Literal" && node.right.type == "Literal") {
let result = eval(node.left.value + node.operator + node.right.value);
node.type = "Literal";
delete node.operator;
delete node.left;
delete node.right;
node.value = result;
node.raw = "" + result;
} else if (node.operator == "*") {
if (node.left.type == "Literal" && Math.isInteger(node.left.value)) {
let log2 = Math.log2(node.left.value);
if (Math.isInteger(log2)) {
node.operator = "<<";
node.left = node.right;
node.right = {
type: "Literal",
value: log2,
raw: String(log2),
};
path.node = node;
}
} else if (node.right.type == "Literal" && Math.isInteger(node.right.value)) {
let log2 = Math.log2(node.right.value);
if (Math.isInteger(log2)) {
node.operator = "<<";
node.right.value = log2;
node.right.raw = String(log2);
path.node = node;
}
}
}
},
visitArrayExpression(path) {
const node = path.node;
path.node.elements = squashArray(node.elements);
return false;
},
visitCallExpression(path) {
const node = path.node;
this.traverse(path);
if (node.callee.type == "MemberExpression") {
const callee = node.callee;
if (callee.object.type == "ArrayExpression") {
let squashedObject = squashArray(callee.object.elements);
if (callee.property.name == "concat") {
let squashedArguments = squashArray(node.arguments);
node.type = "ArrayExpression";
for (let element of squashedArguments) {
squashedObject.push(element);
}
node.elements = squashedObject;
path.node = node;
return false;
} else if (callee.property.name == "join") {
node.type = "Literal";
let value = "";
let separator = ",";
if (node.arguments.length !== 0) {
separator = node.arguments[0].value;
}
for (let i = 0; i < squashedObject.length; i += 1) {
value += squashedObject[i].value;
if (i != squashedObject.length-1) {
value += separator;
}
}
node.value = value;
node.raw = "\\"+node.value+"\\";
path.node = node;
return false;
} else if (callee.property.name == "shift") {
node.type = "Literal";
node.value = squashedObject.shift().value;
node.raw = "\\"+node.value+"\\";
path.node = node;
return false;
} else if (callee.property.name == "slice") {
this.traverse(path);
let slicedElements;
if (node.arguments.length == 1) {
slicedElements = callee.object.elements.slice(0, node.arguments[0].value);
} else {
slicedElements = callee.object.elements.slice(node.arguments[0].value, node.arguments[1].value);
}
node.type = "ArrayExpression";
node.elements = slicedElements;
path.node = node;
delete node.callee;
delete node.arguments;
} else if (callee.property.name == "pop") {
let poppedValue = squashedObject[squashedObject.length-1];
node.type = "Literal";
node.value = poppedValue.value;
node.raw = String(poppedValue.value);
path.node = node;
return false;
} else if (callee.property.name == "reverse") {
let reversedArray = [];
for (let i = squashedObject.length - 1; i >= 0; i -= 1) {
reversedArray.push(squashedObject[i]);
}
path.node.type = "ArrayExpression";
path.node.elements = reversedArray;
delete path.node.callee;
delete path.node.arguments;
return false;
}
}
}
},
visitMemberExpression(path) {
const node = path.node;
this.traverse(path);
if (node.object.type == "ArrayExpression" && node.property.name == "length") {
node.object = squashArray(node.object.elements);
node.type = "Literal";
node.value = node.object.length;
node.raw = `"${node.value}"`;
delete node.object;
delete node.property;
} else if (node.object.type == "ArrayExpression" && node.property.type == "Literal") {
node.object = squashArray(node.object.elements);
node.type = "Literal";
node.value = node.object[node.property.value].value;
node.raw = `"${node.value}"`;
delete node.object;
delete node.property;
}
},
});
return recast.print(ast).code;
}