UNPKG

7.17 kBJavaScriptView Raw
1var unparse = require('escodegen').generate;
2
3module.exports = function (ast, vars) {
4 if (!vars) vars = {};
5 var FAIL = {};
6
7 var result = (function walk (node, scopeVars) {
8 if (node.type === 'Literal') {
9 return node.value;
10 }
11 else if (node.type === 'UnaryExpression'){
12 var val = walk(node.argument)
13 if (node.operator === '+') return +val
14 if (node.operator === '-') return -val
15 if (node.operator === '~') return ~val
16 if (node.operator === '!') return !val
17 return FAIL
18 }
19 else if (node.type === 'ArrayExpression') {
20 var xs = [];
21 for (var i = 0, l = node.elements.length; i < l; i++) {
22 var x = walk(node.elements[i]);
23 if (x === FAIL) return FAIL;
24 xs.push(x);
25 }
26 return xs;
27 }
28 else if (node.type === 'ObjectExpression') {
29 var obj = {};
30 for (var i = 0; i < node.properties.length; i++) {
31 var prop = node.properties[i];
32 var value = prop.value === null
33 ? prop.value
34 : walk(prop.value)
35 ;
36 if (value === FAIL) return FAIL;
37 obj[prop.key.value || prop.key.name] = value;
38 }
39 return obj;
40 }
41 else if (node.type === 'BinaryExpression' ||
42 node.type === 'LogicalExpression') {
43 var op = node.operator;
44
45 if (op === '&&') {
46 var l = walk(node.left);
47 if (l === FAIL) return FAIL;
48 if (!l) return l;
49 var r = walk(node.right);
50 if (r === FAIL) return FAIL;
51 return r;
52 }
53 else if (op === '||') {
54 var l = walk(node.left);
55 if (l === FAIL) return FAIL;
56 if (l) return l;
57 var r = walk(node.right);
58 if (r === FAIL) return FAIL;
59 return r;
60 }
61
62 var l = walk(node.left);
63 if (l === FAIL) return FAIL;
64 var r = walk(node.right);
65 if (r === FAIL) return FAIL;
66
67 if (op === '==') return l == r;
68 if (op === '===') return l === r;
69 if (op === '!=') return l != r;
70 if (op === '!==') return l !== r;
71 if (op === '+') return l + r;
72 if (op === '-') return l - r;
73 if (op === '*') return l * r;
74 if (op === '/') return l / r;
75 if (op === '%') return l % r;
76 if (op === '<') return l < r;
77 if (op === '<=') return l <= r;
78 if (op === '>') return l > r;
79 if (op === '>=') return l >= r;
80 if (op === '|') return l | r;
81 if (op === '&') return l & r;
82 if (op === '^') return l ^ r;
83
84 return FAIL;
85 }
86 else if (node.type === 'Identifier') {
87 if ({}.hasOwnProperty.call(vars, node.name)) {
88 return vars[node.name];
89 }
90 else return FAIL;
91 }
92 else if (node.type === 'ThisExpression') {
93 if ({}.hasOwnProperty.call(vars, 'this')) {
94 return vars['this'];
95 }
96 else return FAIL;
97 }
98 else if (node.type === 'CallExpression') {
99 var callee = walk(node.callee);
100 if (callee === FAIL) return FAIL;
101 if (typeof callee !== 'function') return FAIL;
102
103 var ctx = node.callee.object ? walk(node.callee.object) : FAIL;
104 if (ctx === FAIL) ctx = null;
105
106 var args = [];
107 for (var i = 0, l = node.arguments.length; i < l; i++) {
108 var x = walk(node.arguments[i]);
109 if (x === FAIL) return FAIL;
110 args.push(x);
111 }
112 return callee.apply(ctx, args);
113 }
114 else if (node.type === 'MemberExpression') {
115 var obj = walk(node.object);
116 // do not allow access to methods on Function
117 if((obj === FAIL) || (typeof obj == 'function')){
118 return FAIL;
119 }
120 if (node.property.type === 'Identifier' && !node.computed) {
121 if (isUnsafeProperty(node.property.name)) return FAIL;
122 return obj[node.property.name];
123 }
124 var prop = walk(node.property);
125 if (prop === null || prop === FAIL) return FAIL;
126 if (isUnsafeProperty(prop)) return FAIL;
127 return obj[prop];
128 }
129 else if (node.type === 'ConditionalExpression') {
130 var val = walk(node.test)
131 if (val === FAIL) return FAIL;
132 return val ? walk(node.consequent) : walk(node.alternate)
133 }
134 else if (node.type === 'ExpressionStatement') {
135 var val = walk(node.expression)
136 if (val === FAIL) return FAIL;
137 return val;
138 }
139 else if (node.type === 'ReturnStatement') {
140 return walk(node.argument)
141 }
142 else if (node.type === 'FunctionExpression') {
143
144 var bodies = node.body.body;
145
146 // Create a "scope" for our arguments
147 var oldVars = {};
148 Object.keys(vars).forEach(function(element){
149 oldVars[element] = vars[element];
150 })
151
152 for(var i=0; i<node.params.length; i++){
153 var key = node.params[i];
154 if(key.type == 'Identifier'){
155 vars[key.name] = null;
156 }
157 else return FAIL;
158 }
159 for(var i in bodies){
160 if(walk(bodies[i]) === FAIL){
161 return FAIL;
162 }
163 }
164 // restore the vars and scope after we walk
165 vars = oldVars;
166
167 var keys = Object.keys(vars);
168 var vals = keys.map(function(key) {
169 return vars[key];
170 });
171 return Function(keys.join(', '), 'return ' + unparse(node)).apply(null, vals);
172 }
173 else if (node.type === 'TemplateLiteral') {
174 var str = '';
175 for (var i = 0; i < node.expressions.length; i++) {
176 str += walk(node.quasis[i]);
177 str += walk(node.expressions[i]);
178 }
179 str += walk(node.quasis[i]);
180 return str;
181 }
182 else if (node.type === 'TaggedTemplateExpression') {
183 var tag = walk(node.tag);
184 var quasi = node.quasi;
185 var strings = quasi.quasis.map(walk);
186 var values = quasi.expressions.map(walk);
187 return tag.apply(null, [strings].concat(values));
188 }
189 else if (node.type === 'TemplateElement') {
190 return node.value.cooked;
191 }
192 else return FAIL;
193 })(ast);
194
195 return result === FAIL ? undefined : result;
196};
197
198function isUnsafeProperty(name) {
199 return name === 'constructor' || name === '__proto__';
200}