You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
178 lines
6.3 KiB
178 lines
6.3 KiB
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; |
|
};
|
|
|