UNPKG

@alu0101353647/constant-folding

Version:
198 lines (184 loc) 7.06 kB
// 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"); "use strict"; 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; }