diff --git a/lib/nodes.js b/lib/nodes.js index 6999a763..30e2922c 100644 --- a/lib/nodes.js +++ b/lib/nodes.js @@ -582,7 +582,7 @@ return (new CallNode(ref, [this.meth, this.context, literal(this.arguments(o))])).compile(o); }; return CurryNode; - }).call(this); + }).apply(this, arguments); //### ExtendsNode // Node to extend an object's prototype with an ancestor object. // After `goog.inherits` from the @@ -1697,14 +1697,23 @@ // A faux-node used to wrap an expressions body in a closure. ClosureNode = (exports.ClosureNode = { // Wrap the expressions body, unless it contains a pure statement, - // in which case, no dice. + // in which case, no dice. If the body mentions `arguments`, then make sure + // that the closure wrapper preserves the original arguments. wrap: function wrap(expressions, statement) { - var call, func; + var args, call, func, mentions_args, meth; if (expressions.contains_pure_statement()) { return expressions; } + mentions_args = expressions.contains(function(n) { + return (n instanceof LiteralNode) && (n.value === 'arguments'); + }); + meth = literal(mentions_args ? 'apply' : 'call'); + args = [literal('this')]; + if (mentions_args) { + args.push(literal('arguments')); + } func = new ParentheticalNode(new CodeNode([], Expressions.wrap([expressions]))); - call = new CallNode(new ValueNode(func, [new AccessorNode(literal('call'))]), [literal('this')]); + call = new CallNode(new ValueNode(func, [new AccessorNode(meth)]), args); if (statement) { return Expressions.wrap([call]); } else { diff --git a/src/nodes.coffee b/src/nodes.coffee index 3e73a510..c68b50c2 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -1263,11 +1263,16 @@ PushNode: exports.PushNode: { ClosureNode: exports.ClosureNode: { # Wrap the expressions body, unless it contains a pure statement, - # in which case, no dice. + # in which case, no dice. If the body mentions `arguments`, then make sure + # that the closure wrapper preserves the original arguments. wrap: (expressions, statement) -> return expressions if expressions.contains_pure_statement() + mentions_args: expressions.contains (n) -> (n instanceof LiteralNode) and (n.value is 'arguments') + meth: literal(if mentions_args then 'apply' else 'call') + args: [literal('this')] + args.push literal 'arguments' if mentions_args func: new ParentheticalNode(new CodeNode([], Expressions.wrap([expressions]))) - call: new CallNode(new ValueNode(func, [new AccessorNode(literal('call'))]), [literal('this')]) + call: new CallNode(new ValueNode(func, [new AccessorNode(meth)]), args) if statement then Expressions.wrap([call]) else call } diff --git a/test/test_comprehensions.coffee b/test/test_comprehensions.coffee index 10be8273..35f9bea8 100644 --- a/test/test_comprehensions.coffee +++ b/test/test_comprehensions.coffee @@ -92,3 +92,10 @@ store: (obj) -> result: obj store (x * 2 for x in [3, 2, 1]) ok result.join(' ') is '6 4 2' + + +# Closure-wrapped comprehensions that refer to the "arguments" object. +expr: -> + result: item * item for item in arguments + +ok expr(2, 4, 8).join(' ') is '4 16 64'