UNPKG

@0ria/constant-folding

Version:
114 lines (104 loc) 3.96 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"); "use strict"; module.exports = constantFoldingExtended; /** * This function gets passed by a Node and depending on its type will perform * different operations. * @param {Obect} elem Single subNode * @returns {*} Return single element from passed Node */ function getValues(elem) { let types = { 'Literal' : () => {return elem.raw}, 'Identifier' : () => {return elem.name}, 'ArrayExpression' : () => {return `${elem.elements.map(l => getValues(l))}`}, 'BinaryExpression' : () => {return `${eval(`${elem.left.raw} ${elem.operator} ${elem.right.raw}`)}`} } return types[elem.type](); } /** * This function gets passed two arguments and perform a transformation in the node * depending different parameters. Then it creates a new node object that will substitute * the passed one. * @param {Object} n Node object in which constant folding will be applied * @param {*} args Possible arguments this node has ex: [a].concat([b]), [b] are the args here */ function transNode(n, args) { let argString = "" let auxstring = ""; let a = n.expression; if (args) { a = n.expression.callee; argString += `(${(args) ? args.map(arg => getValues(arg)) : ''})`; } if (a.property.type == 'Literal') auxString = `[${a.object.elements.map(el => getValues(el))}][${getValues(a.property)}]` + argString; else auxString = `[${a.object.elements.map(el => getValues(el))}].${getValues(a.property)}` + argString; n.expression = espree.parse(eval(auxString)).body[0].expression; } /** * This Function takes a Node of type BinaryExpression and perform constant Folding on it * @param {Object} n Node with a type of BinaryExpression */ function transLiteralNode(n) { n.value = Number(getValues(n)); n.raw = String(n.value); n.type = 'Literal'; delete n.left; delete n.rigth; } /** * This function get passed some code and then it forms an AST tree with it. * After it traverses the tree doing constant folding in different nodes in * which it is possible to perform it * @param {string} code The javascript input code * @returns {string} Generated js code from the resultant tree */ function constantFolding(code) { let ast = espree.parse(code, {ecmaVersion: espree.latestEcmaVersion}); estraverse.traverse(ast, { leave: function(node, parent) { if (node.type == 'ExpressionStatement' && node.expression.type == 'CallExpression' && node.expression.callee.object.type == 'ArrayExpression') { transNode(node, node.expression.arguments); } if (node.type == 'ExpressionStatement' && node.expression.type == 'MemberExpression' && node.expression.object.type == 'ArrayExpression') { transNode(node); } if(node.type == 'BinaryExpression' && node.left.type == 'Literal' && node.right.type == 'Literal') { transLiteralNode(node); } } }); //deb(ast); return escodegen.generate(ast); } function transLiteralExtendedNode(n) { n.type = "value"; n.value = `${eval(`${n.args[0].value} ${n.operator.name} ${n.args[1].value}`)}`; delete n.args; delete n.operator; } function constantFoldingExtended(ast) { binaryOperators = ["+", "-", "*", "/"] estraverse.traverse(ast, { leave: function(node, parent) { if (node.type == 'apply' && node.operator.type == 'word' && binaryOperators.includes(node.operator.name) && node.args.length === 2 && node.args[0].type === "value" && node.args[1].type === "value") { transLiteralExtendedNode(node); } }, keys: { apply: ['operator', 'args'], property: ['operator', 'args'], word: [], value: [] }, }); //deb(ast); return ast; }