generated closures should only call() or apply() when necessary.

This commit is contained in:
Jeremy Ashkenas
2010-04-10 14:40:05 -04:00
parent f36acc27e5
commit 065bf54094
7 changed files with 65 additions and 54 deletions

View File

@@ -90,7 +90,7 @@
_b.push(' '); _b.push(' ');
} }
return _b; return _b;
}).call(this).join('') : ''; })().join('') : '';
desc = task.description ? ("# " + (task.description)) : ''; desc = task.description ? ("# " + (task.description)) : '';
puts(("cake " + name + spaces + " " + desc)); puts(("cake " + name + spaces + " " + desc));
}} }}

View File

@@ -188,10 +188,10 @@
tag = _e[0]; tag = _e[0];
value = _e[1]; value = _e[1];
return "[" + tag + " " + value + "]"; return "[" + tag + " " + value + "]";
}).call(this)); })());
} }
return _a; return _a;
}).call(this); })();
return puts(strings.join(' ')); return puts(strings.join(' '));
}; };
// Use the [OptionParser module](optparse.html) to extract all options from // Use the [OptionParser module](optparse.html) to extract all options from

View File

@@ -713,10 +713,10 @@
alt[1] = ("return " + (alt[1])); alt[1] = ("return " + (alt[1]));
} }
return alt; return alt;
}).call(this)); })());
} }
return _b; return _b;
}).call(this); })();
}} }}
// Initialize the **Parser** with our list of terminal **tokens**, our **grammar** // Initialize the **Parser** with our list of terminal **tokens**, our **grammar**
// rules, and the name of the root. Reverse the operators because Jison orders // rules, and the name of the root. Reverse the operators because Jison orders

View File

@@ -155,7 +155,7 @@
if (node.traverse) { if (node.traverse) {
return node.traverse(block); return node.traverse(block);
} }
}).call(this)); })());
} }
return _a; return _a;
}; };
@@ -189,7 +189,7 @@
return false; return false;
}; };
return BaseNode; return BaseNode;
}).call(this); })();
//### Expressions //### Expressions
// The expressions body is the list of expressions that forms the body of an // The expressions body is the list of expressions that forms the body of an
// indented block of code -- the implementation of a function, a clause in an // indented block of code -- the implementation of a function, a clause in an
@@ -307,7 +307,7 @@
} }
}; };
return Expressions; return Expressions;
}).call(this); })();
// Wrap up the given nodes as an **Expressions**, unless it already happens // Wrap up the given nodes as an **Expressions**, unless it already happens
// to be one. // to be one.
Expressions.wrap = function wrap(nodes) { Expressions.wrap = function wrap(nodes) {
@@ -343,7 +343,7 @@
return " \"" + this.value + "\""; return " \"" + this.value + "\"";
}; };
return LiteralNode; return LiteralNode;
}).call(this); })();
//### ReturnNode //### ReturnNode
// A `return` is a *pure_statement* -- wrapping it in a closure wouldn't // A `return` is a *pure_statement* -- wrapping it in a closure wouldn't
// make sense. // make sense.
@@ -369,7 +369,7 @@
return "" + (this.tab) + "return " + (this.expression.compile(o)) + ";"; return "" + (this.tab) + "return " + (this.expression.compile(o)) + ";";
}; };
return ReturnNode; return ReturnNode;
}).call(this); })();
statement(ReturnNode, true); statement(ReturnNode, true);
//### ValueNode //### ValueNode
// A value, variable or literal or parenthesized, indexed or dotted into, // A value, variable or literal or parenthesized, indexed or dotted into,
@@ -462,7 +462,7 @@
} }
}; };
return ValueNode; return ValueNode;
}).call(this); })();
//### CommentNode //### CommentNode
// CoffeeScript passes through comments as JavaScript comments at the // CoffeeScript passes through comments as JavaScript comments at the
// same position. // same position.
@@ -480,7 +480,7 @@
return ("" + this.tab + "//") + this.lines.join(("\n" + this.tab + "//")); return ("" + this.tab + "//") + this.lines.join(("\n" + this.tab + "//"));
}; };
return CommentNode; return CommentNode;
}).call(this); })();
statement(CommentNode); statement(CommentNode);
//### CallNode //### CallNode
// Node for a function invocation. Takes care of converting `super()` calls into // Node for a function invocation. Takes care of converting `super()` calls into
@@ -552,7 +552,7 @@
return "" + (this.prefix()) + (meth) + ".apply(" + obj + ", " + (this.compile_splat_arguments(o)) + ")"; return "" + (this.prefix()) + (meth) + ".apply(" + obj + ", " + (this.compile_splat_arguments(o)) + ")";
}; };
return CallNode; return CallNode;
}).call(this); })();
//### CurryNode //### CurryNode
// Binds a context object and a list of arguments to a function, // Binds a context object and a list of arguments to a function,
// returning the bound function. After ECMAScript 5, Prototype.js, and // returning the bound function. After ECMAScript 5, Prototype.js, and
@@ -600,7 +600,7 @@
return (new CallNode(ref, [this.child, this.parent])).compile(o); return (new CallNode(ref, [this.child, this.parent])).compile(o);
}; };
return ExtendsNode; return ExtendsNode;
}).call(this); })();
//### AccessorNode //### AccessorNode
// A `.` accessor into a property of a value, or the `::` shorthand for // A `.` accessor into a property of a value, or the `::` shorthand for
// an accessor into the object's prototype. // an accessor into the object's prototype.
@@ -619,7 +619,7 @@
return "." + proto_part + (this.name.compile(o)); return "." + proto_part + (this.name.compile(o));
}; };
return AccessorNode; return AccessorNode;
}).call(this); })();
//### IndexNode //### IndexNode
// A `[ ... ]` indexed accessor into an array or object. // A `[ ... ]` indexed accessor into an array or object.
exports.IndexNode = (function() { exports.IndexNode = (function() {
@@ -635,7 +635,7 @@
return "[" + idx + "]"; return "[" + idx + "]";
}; };
return IndexNode; return IndexNode;
}).call(this); })();
//### RangeNode //### RangeNode
// A range literal. Ranges can be used to extract portions (slices) of arrays, // A range literal. Ranges can be used to extract portions (slices) of arrays,
// to specify a range for comprehensions, or as a value, to be expanded into the // to specify a range for comprehensions, or as a value, to be expanded into the
@@ -690,7 +690,7 @@
return (new ParentheticalNode(new CallNode(new CodeNode([], arr.make_return())))).compile(o); return (new ParentheticalNode(new CallNode(new CodeNode([], arr.make_return())))).compile(o);
}; };
return RangeNode; return RangeNode;
}).call(this); })();
//### SliceNode //### SliceNode
// An array slice literal. Unlike JavaScript's `Array#slice`, the second parameter // An array slice literal. Unlike JavaScript's `Array#slice`, the second parameter
// specifies the index of the end of the slice, just as the first parameter // specifies the index of the end of the slice, just as the first parameter
@@ -710,7 +710,7 @@
return ".slice(" + from + ", " + to + plus_part + ")"; return ".slice(" + from + ", " + to + plus_part + ")";
}; };
return SliceNode; return SliceNode;
}).call(this); })();
//### ObjectNode //### ObjectNode
// An object literal, nothing fancy. // An object literal, nothing fancy.
exports.ObjectNode = (function() { exports.ObjectNode = (function() {
@@ -757,7 +757,7 @@
return "{" + inner + "}"; return "{" + inner + "}";
}; };
return ObjectNode; return ObjectNode;
}).call(this); })();
//### ArrayNode //### ArrayNode
// An array literal. // An array literal.
exports.ArrayNode = (function() { exports.ArrayNode = (function() {
@@ -790,7 +790,7 @@
return "[" + objects + ending; return "[" + objects + ending;
}; };
return ArrayNode; return ArrayNode;
}).call(this); })();
//### ClassNode //### ClassNode
// The CoffeeScript class definition. // The CoffeeScript class definition.
exports.ClassNode = (function() { exports.ClassNode = (function() {
@@ -848,7 +848,7 @@
return "" + construct + extension + props + returns; return "" + construct + extension + props + returns;
}; };
return ClassNode; return ClassNode;
}).call(this); })();
statement(ClassNode); statement(ClassNode);
//### AssignNode //### AssignNode
// The **AssignNode** is used to assign a local variable to value, or to set the // The **AssignNode** is used to assign a local variable to value, or to set the
@@ -970,7 +970,7 @@
return "" + (name) + ".splice.apply(" + name + ", [" + from + ", " + to + "].concat(" + val + "))"; return "" + (name) + ".splice.apply(" + name + ", [" + from + ", " + to + "].concat(" + val + "))";
}; };
return AssignNode; return AssignNode;
}).call(this); })();
//### CodeNode //### CodeNode
// A function definition. This is the only node that creates a new Scope. // A function definition. This is the only node that creates a new Scope.
// When for the purposes of walking the contents of a function body, the CodeNode // When for the purposes of walking the contents of a function body, the CodeNode
@@ -1022,7 +1022,7 @@
_d.push(param.compile(o)); _d.push(param.compile(o));
} }
return _d; return _d;
}).call(this); })();
this.body.make_return(); this.body.make_return();
_i = params; _i = params;
for (_h = 0, _j = _i.length; _h < _j; _h++) { for (_h = 0, _j = _i.length; _h < _j; _h++) {
@@ -1075,7 +1075,7 @@
return "\n" + idt + children; return "\n" + idt + children;
}; };
return CodeNode; return CodeNode;
}).call(this); })();
//### SplatNode //### SplatNode
// A splat, either as a parameter to a function, an argument to a call, // A splat, either as a parameter to a function, an argument to a call,
// or as part of a destructuring assignment. // or as part of a destructuring assignment.
@@ -1199,7 +1199,7 @@
return "" + pre + " {\n" + (this.body.compile(o)) + "\n" + this.tab + "}\n" + post; return "" + pre + " {\n" + (this.body.compile(o)) + "\n" + this.tab + "}\n" + post;
}; };
return WhileNode; return WhileNode;
}).call(this); })();
statement(WhileNode); statement(WhileNode);
//### OpNode //### OpNode
// Simple Arithmetic and logical operations. Performs some conversion from // Simple Arithmetic and logical operations. Performs some conversion from
@@ -1302,7 +1302,7 @@
return parts.join(''); return parts.join('');
}; };
return OpNode; return OpNode;
}).call(this); })();
//### TryNode //### TryNode
// A classic *try/catch/finally* block. // A classic *try/catch/finally* block.
exports.TryNode = (function() { exports.TryNode = (function() {
@@ -1335,7 +1335,7 @@
return "" + (this.tab) + "try {\n" + attempt_part + "\n" + this.tab + "}" + catch_part + finally_part; return "" + (this.tab) + "try {\n" + attempt_part + "\n" + this.tab + "}" + catch_part + finally_part;
}; };
return TryNode; return TryNode;
}).call(this); })();
statement(TryNode); statement(TryNode);
//### ThrowNode //### ThrowNode
// Simple node to throw an exception. // Simple node to throw an exception.
@@ -1353,7 +1353,7 @@
return "" + (this.tab) + "throw " + (this.expression.compile(o)) + ";"; return "" + (this.tab) + "throw " + (this.expression.compile(o)) + ";";
}; };
return ThrowNode; return ThrowNode;
}).call(this); })();
statement(ThrowNode); statement(ThrowNode);
//### ExistenceNode //### ExistenceNode
// Checks a variable for existence -- not *null* and not *undefined*. This is // Checks a variable for existence -- not *null* and not *undefined*. This is
@@ -1422,7 +1422,7 @@
} }
}; };
return ParentheticalNode; return ParentheticalNode;
}).call(this); })();
//### ForNode //### ForNode
// CoffeeScript's replacement for the *for* loop is our array and object // CoffeeScript's replacement for the *for* loop is our array and object
// comprehensions, that compile into *for* loops here. They also act as an // comprehensions, that compile into *for* loops here. They also act as an
@@ -1530,7 +1530,7 @@
return "" + set_result + (source_part) + "for (" + for_part + ") {\n" + var_part + body + "\n" + this.tab + close + return_result; return "" + set_result + (source_part) + "for (" + for_part + ") {\n" + var_part + body + "\n" + this.tab + close + return_result;
}; };
return ForNode; return ForNode;
}).call(this); })();
statement(ForNode); statement(ForNode);
//### IfNode //### IfNode
// *If/else* statements. Our *switch/when* will be compiled into this. Acts as an // *If/else* statements. Our *switch/when* will be compiled into this. Acts as an
@@ -1675,7 +1675,7 @@
return "" + if_part + " : " + else_part; return "" + if_part + " : " + else_part;
}; };
return IfNode; return IfNode;
}).call(this); })();
// Faux-Nodes // Faux-Nodes
// ---------- // ----------
//### PushNode //### PushNode
@@ -1697,23 +1697,30 @@
// A faux-node used to wrap an expressions body in a closure. // A faux-node used to wrap an expressions body in a closure.
ClosureNode = (exports.ClosureNode = { ClosureNode = (exports.ClosureNode = {
// Wrap the expressions body, unless it contains a pure statement, // Wrap the expressions body, unless it contains a pure statement,
// in which case, no dice. If the body mentions `arguments`, then make sure // in which case, no dice. If the body mentions `this` or `arguments`,
// that the closure wrapper preserves the original arguments. // then make sure that the closure wrapper preserves the original values.
wrap: function wrap(expressions, statement) { wrap: function wrap(expressions, statement) {
var args, call, func, mentions_args, meth; var args, call, func, mentions_args, mentions_this, meth;
if (expressions.contains_pure_statement()) { if (expressions.contains_pure_statement()) {
return expressions; return expressions;
} }
func = new ParentheticalNode(new CodeNode([], Expressions.wrap([expressions])));
args = [];
mentions_args = expressions.contains(function(n) { mentions_args = expressions.contains(function(n) {
return (n instanceof LiteralNode) && (n.value === 'arguments'); return (n instanceof LiteralNode) && (n.value === 'arguments');
}); });
meth = literal(mentions_args ? 'apply' : 'call'); mentions_this = expressions.contains(function(n) {
args = [literal('this')]; return (n instanceof LiteralNode) && (n.value === 'this');
if (mentions_args) { });
args.push(literal('arguments')); if (mentions_args || mentions_this) {
meth = literal(mentions_args ? 'apply' : 'call');
args = [literal('this')];
if (mentions_args) {
args.push(literal('arguments'));
}
func = new ValueNode(func, [new AccessorNode(meth)]);
} }
func = new ParentheticalNode(new CodeNode([], Expressions.wrap([expressions]))); call = new CallNode(func, args);
call = new CallNode(new ValueNode(func, [new AccessorNode(meth)]), args);
if (statement) { if (statement) {
return Expressions.wrap([call]); return Expressions.wrap([call]);
} else { } else {

View File

@@ -63,14 +63,14 @@
_d.push(' '); _d.push(' ');
} }
return _d; return _d;
}).call(this).join('') : ''; })().join('') : '';
let_part = rule.short_flag ? rule.short_flag + ', ' : ' '; let_part = rule.short_flag ? rule.short_flag + ', ' : ' ';
lines.push((" " + let_part + (rule.long_flag) + spaces + (rule.description))); lines.push((" " + let_part + (rule.long_flag) + spaces + (rule.description)));
} }
return "\n" + (lines.join('\n')) + "\n"; return "\n" + (lines.join('\n')) + "\n";
}; };
return OptionParser; return OptionParser;
}).call(this); })();
// Helpers // Helpers
// ------- // -------
// Regex matchers for option flags. // Regex matchers for option flags.
@@ -90,7 +90,7 @@
tuple.unshift(null); tuple.unshift(null);
} }
return build_rule.apply(this, tuple); return build_rule.apply(this, tuple);
}).call(this)); })());
} }
return _a; return _a;
}; };

View File

@@ -267,7 +267,7 @@
value > 0 ? _a.push(key) : null; value > 0 ? _a.push(key) : null;
}} }}
return _a; return _a;
}).call(this); })();
if (unclosed.length) { if (unclosed.length) {
open = unclosed[0]; open = unclosed[0];
line = open_line[open] + 1; line = open_line[open] + 1;
@@ -323,7 +323,7 @@
}, this)); }, this));
}; };
return Rewriter; return Rewriter;
}).call(this); })();
// Constants // Constants
// --------- // ---------
// List of the token pairs that must be balanced. // List of the token pairs that must be balanced.
@@ -345,7 +345,7 @@
_d.push(pair[0]); _d.push(pair[0]);
} }
return _d; return _d;
}).call(this); })();
// The tokens that signal the end of a balanced pair. // The tokens that signal the end of a balanced pair.
EXPRESSION_END = (function() { EXPRESSION_END = (function() {
_h = []; _j = BALANCED_PAIRS; _h = []; _j = BALANCED_PAIRS;
@@ -354,7 +354,7 @@
_h.push(pair[1]); _h.push(pair[1]);
} }
return _h; return _h;
}).call(this); })();
// Tokens that indicate the close of a clause of an expression. // Tokens that indicate the close of a clause of an expression.
EXPRESSION_CLOSE = ['CATCH', 'WHEN', 'ELSE', 'FINALLY'].concat(EXPRESSION_END); EXPRESSION_CLOSE = ['CATCH', 'WHEN', 'ELSE', 'FINALLY'].concat(EXPRESSION_END);
// Tokens that, if followed by an `IMPLICIT_CALL`, indicate a function invocation. // Tokens that, if followed by an `IMPLICIT_CALL`, indicate a function invocation.

View File

@@ -1263,16 +1263,20 @@ PushNode: exports.PushNode: {
ClosureNode: exports.ClosureNode: { ClosureNode: exports.ClosureNode: {
# Wrap the expressions body, unless it contains a pure statement, # Wrap the expressions body, unless it contains a pure statement,
# in which case, no dice. If the body mentions `arguments`, then make sure # in which case, no dice. If the body mentions `this` or `arguments`,
# that the closure wrapper preserves the original arguments. # then make sure that the closure wrapper preserves the original values.
wrap: (expressions, statement) -> wrap: (expressions, statement) ->
return expressions if expressions.contains_pure_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]))) func: new ParentheticalNode(new CodeNode([], Expressions.wrap([expressions])))
call: new CallNode(new ValueNode(func, [new AccessorNode(meth)]), args) args: []
mentions_args: expressions.contains (n) -> (n instanceof LiteralNode) and (n.value is 'arguments')
mentions_this: expressions.contains (n) -> (n instanceof LiteralNode) and (n.value is 'this')
if mentions_args or mentions_this
meth: literal(if mentions_args then 'apply' else 'call')
args: [literal('this')]
args.push literal 'arguments' if mentions_args
func: new ValueNode func, [new AccessorNode(meth)]
call: new CallNode(func, args)
if statement then Expressions.wrap([call]) else call if statement then Expressions.wrap([call]) else call
} }