From af8cceb75a1256a0d7f1ed2172f0ae322678dfd1 Mon Sep 17 00:00:00 2001 From: RoboPhred Date: Thu, 20 Feb 2020 11:34:55 -0700 Subject: [PATCH 1/4] Test for function invocation during declaration --- test/eval.js | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/test/eval.js b/test/eval.js index 4055b35..d966342 100644 --- a/test/eval.js +++ b/test/eval.js @@ -44,6 +44,37 @@ 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 invocation count debugging', function(t) { + t.plan(1); + + var invoked = false; + var variables = { + values: [], + onValue: () => { + console.log("Invoke happened") + invoked = true + } + }; + var src = 'values.forEach(function(x) { onValue() })' + var ast = parse(src).body[0].expression; + evaluate(ast, variables); + t.equal(invoked, false); +}) + test('array methods with vars', function(t) { t.plan(1); @@ -121,3 +152,17 @@ test('constructor at runtime only', function(t) { var res = evaluate(ast); t.equal(res, undefined); }); + +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); +}); \ No newline at end of file From b1be8a6c4078266f27a52e7473d28bf908ad7bdb Mon Sep 17 00:00:00 2001 From: RoboPhred Date: Thu, 20 Feb 2020 11:35:04 -0700 Subject: [PATCH 2/4] Fix functions invoked during declaration --- index.js | 47 ++++++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/index.js b/index.js index 7620e5b..7ae9a6f 100644 --- a/index.js +++ b/index.js @@ -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 @@ -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); } @@ -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; @@ -40,9 +40,9 @@ module.exports = function (ast, vars) { } else if (node.type === 'BinaryExpression' || node.type === 'LogicalExpression') { - 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; var op = node.operator; @@ -80,23 +80,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; @@ -105,26 +111,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 @@ -141,7 +146,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; } } @@ -157,14 +162,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); From 753ca8996eee1e72cf832c42abd052e57ce14386 Mon Sep 17 00:00:00 2001 From: RoboPhred Date: Thu, 20 Feb 2020 11:40:29 -0700 Subject: [PATCH 3/4] Remove debug test --- test/eval.js | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/test/eval.js b/test/eval.js index d966342..6315926 100644 --- a/test/eval.js +++ b/test/eval.js @@ -58,23 +58,6 @@ test('array methods invocation count', function(t) { t.deepEqual(variables.receiver, [1, 2, 3]); }) -test('array methods invocation count debugging', function(t) { - t.plan(1); - - var invoked = false; - var variables = { - values: [], - onValue: () => { - console.log("Invoke happened") - invoked = true - } - }; - var src = 'values.forEach(function(x) { onValue() })' - var ast = parse(src).body[0].expression; - evaluate(ast, variables); - t.equal(invoked, false); -}) - test('array methods with vars', function(t) { t.plan(1); From 6d79e78424c6c4ee48da4a59ef23a7bd01e11e31 Mon Sep 17 00:00:00 2001 From: RoboPhred Date: Thu, 20 Feb 2020 11:44:23 -0700 Subject: [PATCH 4/4] Do not use template string where not required --- test/eval.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/eval.js b/test/eval.js index 6315926..adf48a8 100644 --- a/test/eval.js +++ b/test/eval.js @@ -144,7 +144,7 @@ test('function declaration does not invoke CallExpressions', function(t) { noop: function(){}, onInvoke: function() {invoked = true} }; - var src = `noop(function(){ onInvoke(); })`; + var src = 'noop(function(){ onInvoke(); })'; var ast = parse(src).body[0].expression; evaluate(ast, variables); t.equal(invoked, false);