1 | var unparse = require('escodegen').generate;
|
2 |
|
3 | module.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 |
|
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 |
|
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 |
|
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 |
|
198 | function isUnsafeProperty(name) {
|
199 | return name === 'constructor' || name === '__proto__';
|
200 | }
|