Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix function bodies being invoked during function declaration. #30

Merged
merged 5 commits into from Mar 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
47 changes: 26 additions & 21 deletions index.js
Expand Up @@ -4,12 +4,12 @@ module.exports = function (ast, vars) {
if (!vars) vars = {};
var FAIL = {};

var result = (function walk (node, scopeVars) {
var result = (function walk (node, noExecute) {
if (node.type === 'Literal') {
return node.value;
}
else if (node.type === 'UnaryExpression'){
var val = walk(node.argument)
var val = walk(node.argument, noExecute)
if (node.operator === '+') return +val
if (node.operator === '-') return -val
if (node.operator === '~') return ~val
Expand All @@ -19,7 +19,7 @@ module.exports = function (ast, vars) {
else if (node.type === 'ArrayExpression') {
var xs = [];
for (var i = 0, l = node.elements.length; i < l; i++) {
var x = walk(node.elements[i]);
var x = walk(node.elements[i], noExecute);
if (x === FAIL) return FAIL;
xs.push(x);
}
Expand All @@ -31,7 +31,7 @@ module.exports = function (ast, vars) {
var prop = node.properties[i];
var value = prop.value === null
? prop.value
: walk(prop.value)
: walk(prop.value, noExecute)
;
if (value === FAIL) return FAIL;
obj[prop.key.value || prop.key.name] = value;
Expand Down Expand Up @@ -59,9 +59,9 @@ module.exports = function (ast, vars) {
return r;
}

var l = walk(node.left);
var l = walk(node.left, noExecute);
if (l === FAIL) return FAIL;
var r = walk(node.right);
var r = walk(node.right, noExecute);
if (r === FAIL) return FAIL;

if (op === '==') return l == r;
Expand Down Expand Up @@ -96,23 +96,29 @@ module.exports = function (ast, vars) {
else return FAIL;
}
else if (node.type === 'CallExpression') {
var callee = walk(node.callee);
var callee = walk(node.callee, noExecute);
if (callee === FAIL) return FAIL;
if (typeof callee !== 'function') return FAIL;


var ctx = node.callee.object ? walk(node.callee.object) : FAIL;
var ctx = node.callee.object ? walk(node.callee.object, noExecute) : 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]);
var x = walk(node.arguments[i], noExecute);
if (x === FAIL) return FAIL;
args.push(x);
}

if (noExecute) {
return undefined;
}

return callee.apply(ctx, args);
}
else if (node.type === 'MemberExpression') {
var obj = walk(node.object);
var obj = walk(node.object, noExecute);
// do not allow access to methods on Function
if((obj === FAIL) || (typeof obj == 'function')){
return FAIL;
Expand All @@ -121,26 +127,25 @@ module.exports = function (ast, vars) {
if (isUnsafeProperty(node.property.name)) return FAIL;
return obj[node.property.name];
}
var prop = walk(node.property);
var prop = walk(node.property, noExecute);
if (prop === null || prop === FAIL) return FAIL;
if (isUnsafeProperty(prop)) return FAIL;
return obj[prop];
}
else if (node.type === 'ConditionalExpression') {
var val = walk(node.test)
var val = walk(node.test, noExecute)
if (val === FAIL) return FAIL;
return val ? walk(node.consequent) : walk(node.alternate)
return val ? walk(node.consequent) : walk(node.alternate, noExecute)
}
else if (node.type === 'ExpressionStatement') {
var val = walk(node.expression)
var val = walk(node.expression, noExecute)
if (val === FAIL) return FAIL;
return val;
}
else if (node.type === 'ReturnStatement') {
return walk(node.argument)
return walk(node.argument, noExecute)
}
else if (node.type === 'FunctionExpression') {

var bodies = node.body.body;

// Create a "scope" for our arguments
Expand All @@ -157,7 +162,7 @@ module.exports = function (ast, vars) {
else return FAIL;
}
for(var i in bodies){
if(walk(bodies[i]) === FAIL){
if(walk(bodies[i], true) === FAIL){
return FAIL;
}
}
Expand All @@ -173,14 +178,14 @@ module.exports = function (ast, vars) {
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], noExecute);
str += walk(node.expressions[i], noExecute);
}
str += walk(node.quasis[i]);
str += walk(node.quasis[i], noExecute);
return str;
}
else if (node.type === 'TaggedTemplateExpression') {
var tag = walk(node.tag);
var tag = walk(node.tag, noExecute);
var quasi = node.quasi;
var strings = quasi.quasis.map(walk);
var values = quasi.expressions.map(walk);
Expand Down
30 changes: 29 additions & 1 deletion test/eval.js
Expand Up @@ -44,6 +44,20 @@ test('array methods', function(t) {
t.deepEqual(evaluate(ast), [2, 4, 6]);
});

test('array methods invocation count', function(t) {
t.plan(2);

var variables = {
values: [1, 2, 3],
receiver: []
};
var src = 'values.forEach(function(x) { receiver.push(x); })'
var ast = parse(src).body[0].expression;
evaluate(ast, variables);
t.equal(variables.receiver.length, 3);
t.deepEqual(variables.receiver, [1, 2, 3]);
})

test('array methods with vars', function(t) {
t.plan(1);

Expand Down Expand Up @@ -146,4 +160,18 @@ test('short circuit evaluation OR', function(t) {
var ast = parse(src).body[0].expression;
evaluate(ast, variables);
t.equals(fnInvoked, false);
})
})

test('function declaration does not invoke CallExpressions', function(t) {
t.plan(1);

var invoked = false;
var variables = {
noop: function(){},
onInvoke: function() {invoked = true}
};
var src = 'noop(function(){ onInvoke(); })';
var ast = parse(src).body[0].expression;
evaluate(ast, variables);
t.equal(invoked, false);
});