179 lines
6.3 KiB
JavaScript
179 lines
6.3 KiB
JavaScript
|
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];
|
||
|
})
|
||
|
|
||
|
for(var i=0; i<node.params.length; i++){
|
||
|
var key = node.params[i];
|
||
|
if(key.type == 'Identifier'){
|
||
|
vars[key.name] = null;
|
||
|
}
|
||
|
else return FAIL;
|
||
|
}
|
||
|
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;
|
||
|
};
|