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 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
|
var unparse = require('escodegen').generate;
module.exports = function (ast, vars) {
if (!vars) vars = {};
var FAIL = {};
var result = (function walk (node, scopeVars) {
if (node.type === 'Literal') {
return node.value;
}
else if (node.type === 'UnaryExpression'){
var val = walk(node.argument)
if (node.operator === '+') return +val
if (node.operator === '-') return -val
if (node.operator === '~') return ~val
if (node.operator === '!') return !val
return FAIL
}
else if (node.type === 'ArrayExpression') {
var xs = [];
for (var i = 0, l = node.elements.length; i < l; i++) {
var x = walk(node.elements[i]);
if (x === FAIL) return FAIL;
xs.push(x);
}
return xs;
}
else if (node.type === 'ObjectExpression') {
var obj = {};
for (var i = 0; i < node.properties.length; i++) {
var prop = node.properties[i];
var value = prop.value === null
? prop.value
: walk(prop.value)
;
if (value === FAIL) return FAIL;
obj[prop.key.value || prop.key.name] = value;
}
return obj;
}
else if (node.type === 'BinaryExpression' ||
node.type === 'LogicalExpression') {
var l = walk(node.left);
if (l === FAIL) return FAIL;
var r = walk(node.right);
if (r === FAIL) return FAIL;
var op = node.operator;
if (op === '==') return l == r;
if (op === '===') return l === r;
if (op === '!=') return l != r;
if (op === '!==') return l !== r;
if (op === '+') return l + r;
if (op === '-') return l - r;
if (op === '*') return l * r;
if (op === '/') return l / r;
if (op === '%') return l % r;
if (op === '<') return l < r;
if (op === '<=') return l <= r;
if (op === '>') return l > r;
if (op === '>=') return l >= r;
if (op === '|') return l | r;
if (op === '&') return l & r;
if (op === '^') return l ^ r;
if (op === '&&') return l && r;
if (op === '||') return l || r;
return FAIL;
}
else if (node.type === 'Identifier') {
if ({}.hasOwnProperty.call(vars, node.name)) {
return vars[node.name];
}
else return FAIL;
}
else if (node.type === 'ThisExpression') {
if ({}.hasOwnProperty.call(vars, 'this')) {
return vars['this'];
}
else return FAIL;
}
else if (node.type === 'CallExpression') {
var callee = walk(node.callee);
if (callee === FAIL) return FAIL;
if (typeof callee !== 'function') return FAIL;
var ctx = node.callee.object ? walk(node.callee.object) : FAIL;
if (ctx === FAIL) ctx = null;
var args = [];
for (var i = 0, l = node.arguments.length; i < l; i++) {
var x = walk(node.arguments[i]);
if (x === FAIL) return FAIL;
args.push(x);
}
return callee.apply(ctx, args);
}
else if (node.type === 'MemberExpression') {
var obj = walk(node.object);
// do not allow access to methods on Function
if((obj === FAIL) || (typeof obj == 'function')){
return FAIL;
}
if (node.property.type === 'Identifier') {
return obj[node.property.name];
}
var prop = walk(node.property);
if (prop === FAIL) return FAIL;
return obj[prop];
}
else if (node.type === 'ConditionalExpression') {
var val = walk(node.test)
if (val === FAIL) return FAIL;
return val ? walk(node.consequent) : walk(node.alternate)
}
else if (node.type === 'ExpressionStatement') {
var val = walk(node.expression)
if (val === FAIL) return FAIL;
return val;
}
else if (node.type === 'ReturnStatement') {
return walk(node.argument)
}
else if (node.type === 'FunctionExpression') {
var bodies = node.body.body;
// Create a "scope" for our arguments
var oldVars = {};
Object.keys(vars).forEach(function(element){
oldVars[element] = vars[element];
})
node.params.forEach(function(key) {
if(key.type == 'Identifier'){
vars[key.name] = null;
}
});
for(var i in bodies){
if(walk(bodies[i]) === FAIL){
return FAIL;
}
}
// restore the vars and scope after we walk
vars = oldVars;
var keys = Object.keys(vars);
var vals = keys.map(function(key) {
return vars[key];
});
return Function(keys.join(', '), 'return ' + unparse(node)).apply(null, vals);
}
else if (node.type === 'TemplateLiteral') {
var str = '';
for (var i = 0; i < node.expressions.length; i++) {
str += walk(node.quasis[i]);
str += walk(node.expressions[i]);
}
str += walk(node.quasis[i]);
return str;
}
else if (node.type === 'TaggedTemplateExpression') {
var tag = walk(node.tag);
var quasi = node.quasi;
var strings = quasi.quasis.map(walk);
var values = quasi.expressions.map(walk);
return tag.apply(null, [strings].concat(values));
}
else if (node.type === 'TemplateElement') {
return node.value.cooked;
}
else return FAIL;
})(ast);
return result === FAIL ? undefined : result;
};
|