All files constant-folding.js

100% Statements 28/28
100% Branches 22/22
100% Functions 6/6
100% Lines 26/26

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 781x 1x 1x 1x   1x   1x               12x 12x   114x       5x     114x         2x 2x     114x           7x 7x             12x               5x   5x 5x   5x 5x                   27x 9x 9x 9x    
const escodegen = require("escodegen");
const espree = require("espree");
const estraverse = require("estraverse");
const { isConstant, newArrayNode, newNode, getValue } = require("./utils");
 
"use strict";
 
module.exports = constantFolding;
 
/**
 * A function that does constant folding onto the code it receives
 * @param {string} code A string containing the code to do the constant folding on
 * @returns {string} Returns the resulting code
 */
function constantFolding(code) {
  const t = espree.parse(code, { ecmaVersion: 6, loc: false });
  estraverse.traverse(t, {
    leave: function (n, p) {
      if (
        n.type == "BinaryExpression" &&
        n.left.type == "Literal" && n.right.type == "Literal"
      ) {
        binaryConstantFolding(n);
      }
 
      if (
        (n.type === "MemberExpression") &&
        (n.object.type === "ArrayExpression") &&
        (p.type !== "CallExpression")
      ) {
        let value = eval(arrayConstantFolding(n));
        Object.assign(n, newNode(value));
      }
 
      if (
        (n.type === "CallExpression") &&
        (n.callee.object.type === "ArrayExpression") &&
        isConstant(n.arguments) &&
        isConstant(n.callee.object.elements)
      ) {
        let value = arrayConstantFolding(n.callee, n.arguments);
        Array.isArray(value) ?
          Object.assign(n, newArrayNode(value)) :
          Object.assign(n, newNode(value));
      }
    }
  }
  );
  return escodegen.generate(t);
}
 
/**
 * Does ConstantFolding on binaryExpression nodes. I.E. turns 2+3 into 5.
 * @param {Object} n an AST node representing a binaryExpression.
 */
function binaryConstantFolding(n) {
  n.type = "Literal";
 
  n.value = eval(`${n.left.raw} ${n.operator} ${n.right.raw}`);
  n.raw = String(n.value);
 
  delete n.left;
  delete n.right;
}
 
/**
 * Does constant folding on Array operations.
 * @param {Object} code an AST node containing the expression statement
 * @param {Array} args arguments used on the function call. I.E. for [1].concat('d', 'e'), args = ['d', 'e']
 * @returns {string} the resulting js code to be used by escodegen
 */
function arrayConstantFolding(code, args) {
  let result = `[${code.object.elements.map(e => getValue(e))}]`;
  result += code.property.type === "Literal" ? `[${getValue(code.property)}]` : `.${getValue(code.property)}`;
  result += (args ? `(${args.map(e => getValue(e))})` : "");
  return eval(result);
}