Skip to content

Commit aa0eaeb

Browse files
Merge pull request #30 from RoboPhred/fix-func-decl-invoke
Fix function bodies being invoked during function declaration.
2 parents 9d39dd5 + 23bc6ca commit aa0eaeb

File tree

2 files changed

+55
-22
lines changed

2 files changed

+55
-22
lines changed

index.js

+26-21
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ module.exports = function (ast, vars) {
44
if (!vars) vars = {};
55
var FAIL = {};
66

7-
var result = (function walk (node, scopeVars) {
7+
var result = (function walk (node, noExecute) {
88
if (node.type === 'Literal') {
99
return node.value;
1010
}
1111
else if (node.type === 'UnaryExpression'){
12-
var val = walk(node.argument)
12+
var val = walk(node.argument, noExecute)
1313
if (node.operator === '+') return +val
1414
if (node.operator === '-') return -val
1515
if (node.operator === '~') return ~val
@@ -19,7 +19,7 @@ module.exports = function (ast, vars) {
1919
else if (node.type === 'ArrayExpression') {
2020
var xs = [];
2121
for (var i = 0, l = node.elements.length; i < l; i++) {
22-
var x = walk(node.elements[i]);
22+
var x = walk(node.elements[i], noExecute);
2323
if (x === FAIL) return FAIL;
2424
xs.push(x);
2525
}
@@ -31,7 +31,7 @@ module.exports = function (ast, vars) {
3131
var prop = node.properties[i];
3232
var value = prop.value === null
3333
? prop.value
34-
: walk(prop.value)
34+
: walk(prop.value, noExecute)
3535
;
3636
if (value === FAIL) return FAIL;
3737
obj[prop.key.value || prop.key.name] = value;
@@ -59,9 +59,9 @@ module.exports = function (ast, vars) {
5959
return r;
6060
}
6161

62-
var l = walk(node.left);
62+
var l = walk(node.left, noExecute);
6363
if (l === FAIL) return FAIL;
64-
var r = walk(node.right);
64+
var r = walk(node.right, noExecute);
6565
if (r === FAIL) return FAIL;
6666

6767
if (op === '==') return l == r;
@@ -96,23 +96,29 @@ module.exports = function (ast, vars) {
9696
else return FAIL;
9797
}
9898
else if (node.type === 'CallExpression') {
99-
var callee = walk(node.callee);
99+
var callee = walk(node.callee, noExecute);
100100
if (callee === FAIL) return FAIL;
101101
if (typeof callee !== 'function') return FAIL;
102+
102103

103-
var ctx = node.callee.object ? walk(node.callee.object) : FAIL;
104+
var ctx = node.callee.object ? walk(node.callee.object, noExecute) : FAIL;
104105
if (ctx === FAIL) ctx = null;
105106

106107
var args = [];
107108
for (var i = 0, l = node.arguments.length; i < l; i++) {
108-
var x = walk(node.arguments[i]);
109+
var x = walk(node.arguments[i], noExecute);
109110
if (x === FAIL) return FAIL;
110111
args.push(x);
111112
}
113+
114+
if (noExecute) {
115+
return undefined;
116+
}
117+
112118
return callee.apply(ctx, args);
113119
}
114120
else if (node.type === 'MemberExpression') {
115-
var obj = walk(node.object);
121+
var obj = walk(node.object, noExecute);
116122
// do not allow access to methods on Function
117123
if((obj === FAIL) || (typeof obj == 'function')){
118124
return FAIL;
@@ -121,26 +127,25 @@ module.exports = function (ast, vars) {
121127
if (isUnsafeProperty(node.property.name)) return FAIL;
122128
return obj[node.property.name];
123129
}
124-
var prop = walk(node.property);
130+
var prop = walk(node.property, noExecute);
125131
if (prop === null || prop === FAIL) return FAIL;
126132
if (isUnsafeProperty(prop)) return FAIL;
127133
return obj[prop];
128134
}
129135
else if (node.type === 'ConditionalExpression') {
130-
var val = walk(node.test)
136+
var val = walk(node.test, noExecute)
131137
if (val === FAIL) return FAIL;
132-
return val ? walk(node.consequent) : walk(node.alternate)
138+
return val ? walk(node.consequent) : walk(node.alternate, noExecute)
133139
}
134140
else if (node.type === 'ExpressionStatement') {
135-
var val = walk(node.expression)
141+
var val = walk(node.expression, noExecute)
136142
if (val === FAIL) return FAIL;
137143
return val;
138144
}
139145
else if (node.type === 'ReturnStatement') {
140-
return walk(node.argument)
146+
return walk(node.argument, noExecute)
141147
}
142148
else if (node.type === 'FunctionExpression') {
143-
144149
var bodies = node.body.body;
145150

146151
// Create a "scope" for our arguments
@@ -157,7 +162,7 @@ module.exports = function (ast, vars) {
157162
else return FAIL;
158163
}
159164
for(var i in bodies){
160-
if(walk(bodies[i]) === FAIL){
165+
if(walk(bodies[i], true) === FAIL){
161166
return FAIL;
162167
}
163168
}
@@ -173,14 +178,14 @@ module.exports = function (ast, vars) {
173178
else if (node.type === 'TemplateLiteral') {
174179
var str = '';
175180
for (var i = 0; i < node.expressions.length; i++) {
176-
str += walk(node.quasis[i]);
177-
str += walk(node.expressions[i]);
181+
str += walk(node.quasis[i], noExecute);
182+
str += walk(node.expressions[i], noExecute);
178183
}
179-
str += walk(node.quasis[i]);
184+
str += walk(node.quasis[i], noExecute);
180185
return str;
181186
}
182187
else if (node.type === 'TaggedTemplateExpression') {
183-
var tag = walk(node.tag);
188+
var tag = walk(node.tag, noExecute);
184189
var quasi = node.quasi;
185190
var strings = quasi.quasis.map(walk);
186191
var values = quasi.expressions.map(walk);

test/eval.js

+29-1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,20 @@ test('array methods', function(t) {
4444
t.deepEqual(evaluate(ast), [2, 4, 6]);
4545
});
4646

47+
test('array methods invocation count', function(t) {
48+
t.plan(2);
49+
50+
var variables = {
51+
values: [1, 2, 3],
52+
receiver: []
53+
};
54+
var src = 'values.forEach(function(x) { receiver.push(x); })'
55+
var ast = parse(src).body[0].expression;
56+
evaluate(ast, variables);
57+
t.equal(variables.receiver.length, 3);
58+
t.deepEqual(variables.receiver, [1, 2, 3]);
59+
})
60+
4761
test('array methods with vars', function(t) {
4862
t.plan(1);
4963

@@ -146,4 +160,18 @@ test('short circuit evaluation OR', function(t) {
146160
var ast = parse(src).body[0].expression;
147161
evaluate(ast, variables);
148162
t.equals(fnInvoked, false);
149-
})
163+
})
164+
165+
test('function declaration does not invoke CallExpressions', function(t) {
166+
t.plan(1);
167+
168+
var invoked = false;
169+
var variables = {
170+
noop: function(){},
171+
onInvoke: function() {invoked = true}
172+
};
173+
var src = 'noop(function(){ onInvoke(); })';
174+
var ast = parse(src).body[0].expression;
175+
evaluate(ast, variables);
176+
t.equal(invoked, false);
177+
});

0 commit comments

Comments
 (0)