@0ria/constant-folding
Version:
114 lines (104 loc) • 3.96 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");
;
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;
}