1
0
Fork 0
mirror of https://github.com/jashkenas/coffeescript.git synced 2022-11-09 12:23:24 -05:00

safely preserving the arguments object through generated closure wrappers.

This commit is contained in:
Jeremy Ashkenas 2010-04-10 14:20:32 -04:00
parent 491ad6de95
commit f36acc27e5
3 changed files with 27 additions and 6 deletions

View file

@ -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 {

View file

@ -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
}

View file

@ -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'