123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178 |
- 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;
- };
|