From 065bf54094f97853425aa304b5c242eaeaecaa8a Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Sat, 10 Apr 2010 14:40:05 -0400 Subject: [PATCH] generated closures should only call() or apply() when necessary. --- lib/cake.js | 2 +- lib/command.js | 4 +-- lib/grammar.js | 4 +-- lib/nodes.js | 77 ++++++++++++++++++++++++++---------------------- lib/optparse.js | 6 ++-- lib/rewriter.js | 8 ++--- src/nodes.coffee | 18 ++++++----- 7 files changed, 65 insertions(+), 54 deletions(-) diff --git a/lib/cake.js b/lib/cake.js index 5c98fcfe..9f81a311 100755 --- a/lib/cake.js +++ b/lib/cake.js @@ -90,7 +90,7 @@ _b.push(' '); } return _b; - }).call(this).join('') : ''; + })().join('') : ''; desc = task.description ? ("# " + (task.description)) : ''; puts(("cake " + name + spaces + " " + desc)); }} diff --git a/lib/command.js b/lib/command.js index 6dc97e41..64ad52a2 100644 --- a/lib/command.js +++ b/lib/command.js @@ -188,10 +188,10 @@ tag = _e[0]; value = _e[1]; return "[" + tag + " " + value + "]"; - }).call(this)); + })()); } return _a; - }).call(this); + })(); return puts(strings.join(' ')); }; // Use the [OptionParser module](optparse.html) to extract all options from diff --git a/lib/grammar.js b/lib/grammar.js index 961c30a1..409575f4 100644 --- a/lib/grammar.js +++ b/lib/grammar.js @@ -713,10 +713,10 @@ alt[1] = ("return " + (alt[1])); } return alt; - }).call(this)); + })()); } return _b; - }).call(this); + })(); }} // Initialize the **Parser** with our list of terminal **tokens**, our **grammar** // rules, and the name of the root. Reverse the operators because Jison orders diff --git a/lib/nodes.js b/lib/nodes.js index 30e2922c..2b098e6c 100644 --- a/lib/nodes.js +++ b/lib/nodes.js @@ -155,7 +155,7 @@ if (node.traverse) { return node.traverse(block); } - }).call(this)); + })()); } return _a; }; @@ -189,7 +189,7 @@ return false; }; return BaseNode; - }).call(this); + })(); //### Expressions // 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 @@ -307,7 +307,7 @@ } }; return Expressions; - }).call(this); + })(); // Wrap up the given nodes as an **Expressions**, unless it already happens // to be one. Expressions.wrap = function wrap(nodes) { @@ -343,7 +343,7 @@ return " \"" + this.value + "\""; }; return LiteralNode; - }).call(this); + })(); //### ReturnNode // A `return` is a *pure_statement* -- wrapping it in a closure wouldn't // make sense. @@ -369,7 +369,7 @@ return "" + (this.tab) + "return " + (this.expression.compile(o)) + ";"; }; return ReturnNode; - }).call(this); + })(); statement(ReturnNode, true); //### ValueNode // A value, variable or literal or parenthesized, indexed or dotted into, @@ -462,7 +462,7 @@ } }; return ValueNode; - }).call(this); + })(); //### CommentNode // CoffeeScript passes through comments as JavaScript comments at the // same position. @@ -480,7 +480,7 @@ return ("" + this.tab + "//") + this.lines.join(("\n" + this.tab + "//")); }; return CommentNode; - }).call(this); + })(); statement(CommentNode); //### CallNode // 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 CallNode; - }).call(this); + })(); //### CurryNode // Binds a context object and a list of arguments to a function, // 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 ExtendsNode; - }).call(this); + })(); //### AccessorNode // A `.` accessor into a property of a value, or the `::` shorthand for // an accessor into the object's prototype. @@ -619,7 +619,7 @@ return "." + proto_part + (this.name.compile(o)); }; return AccessorNode; - }).call(this); + })(); //### IndexNode // A `[ ... ]` indexed accessor into an array or object. exports.IndexNode = (function() { @@ -635,7 +635,7 @@ return "[" + idx + "]"; }; return IndexNode; - }).call(this); + })(); //### RangeNode // 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 @@ -690,7 +690,7 @@ return (new ParentheticalNode(new CallNode(new CodeNode([], arr.make_return())))).compile(o); }; return RangeNode; - }).call(this); + })(); //### SliceNode // 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 @@ -710,7 +710,7 @@ return ".slice(" + from + ", " + to + plus_part + ")"; }; return SliceNode; - }).call(this); + })(); //### ObjectNode // An object literal, nothing fancy. exports.ObjectNode = (function() { @@ -757,7 +757,7 @@ return "{" + inner + "}"; }; return ObjectNode; - }).call(this); + })(); //### ArrayNode // An array literal. exports.ArrayNode = (function() { @@ -790,7 +790,7 @@ return "[" + objects + ending; }; return ArrayNode; - }).call(this); + })(); //### ClassNode // The CoffeeScript class definition. exports.ClassNode = (function() { @@ -848,7 +848,7 @@ return "" + construct + extension + props + returns; }; return ClassNode; - }).call(this); + })(); statement(ClassNode); //### AssignNode // 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 AssignNode; - }).call(this); + })(); //### CodeNode // 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 @@ -1022,7 +1022,7 @@ _d.push(param.compile(o)); } return _d; - }).call(this); + })(); this.body.make_return(); _i = params; for (_h = 0, _j = _i.length; _h < _j; _h++) { @@ -1075,7 +1075,7 @@ return "\n" + idt + children; }; return CodeNode; - }).call(this); + })(); //### SplatNode // A splat, either as a parameter to a function, an argument to a call, // or as part of a destructuring assignment. @@ -1199,7 +1199,7 @@ return "" + pre + " {\n" + (this.body.compile(o)) + "\n" + this.tab + "}\n" + post; }; return WhileNode; - }).call(this); + })(); statement(WhileNode); //### OpNode // Simple Arithmetic and logical operations. Performs some conversion from @@ -1302,7 +1302,7 @@ return parts.join(''); }; return OpNode; - }).call(this); + })(); //### TryNode // A classic *try/catch/finally* block. exports.TryNode = (function() { @@ -1335,7 +1335,7 @@ return "" + (this.tab) + "try {\n" + attempt_part + "\n" + this.tab + "}" + catch_part + finally_part; }; return TryNode; - }).call(this); + })(); statement(TryNode); //### ThrowNode // Simple node to throw an exception. @@ -1353,7 +1353,7 @@ return "" + (this.tab) + "throw " + (this.expression.compile(o)) + ";"; }; return ThrowNode; - }).call(this); + })(); statement(ThrowNode); //### ExistenceNode // Checks a variable for existence -- not *null* and not *undefined*. This is @@ -1422,7 +1422,7 @@ } }; return ParentheticalNode; - }).call(this); + })(); //### ForNode // CoffeeScript's replacement for the *for* loop is our array and object // 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 ForNode; - }).call(this); + })(); statement(ForNode); //### IfNode // *If/else* statements. Our *switch/when* will be compiled into this. Acts as an @@ -1675,7 +1675,7 @@ return "" + if_part + " : " + else_part; }; return IfNode; - }).call(this); + })(); // Faux-Nodes // ---------- //### PushNode @@ -1697,23 +1697,30 @@ // 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. If the body mentions `arguments`, then make sure - // that the closure wrapper preserves the original arguments. + // in which case, no dice. If the body mentions `this` or `arguments`, + // then make sure that the closure wrapper preserves the original values. 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()) { return expressions; } + func = new ParentheticalNode(new CodeNode([], Expressions.wrap([expressions]))); + args = []; 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')); + mentions_this = expressions.contains(function(n) { + return (n instanceof LiteralNode) && (n.value === 'this'); + }); + 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(new ValueNode(func, [new AccessorNode(meth)]), args); + call = new CallNode(func, args); if (statement) { return Expressions.wrap([call]); } else { diff --git a/lib/optparse.js b/lib/optparse.js index f8454973..3b263f1f 100755 --- a/lib/optparse.js +++ b/lib/optparse.js @@ -63,14 +63,14 @@ _d.push(' '); } return _d; - }).call(this).join('') : ''; + })().join('') : ''; let_part = rule.short_flag ? rule.short_flag + ', ' : ' '; lines.push((" " + let_part + (rule.long_flag) + spaces + (rule.description))); } return "\n" + (lines.join('\n')) + "\n"; }; return OptionParser; - }).call(this); + })(); // Helpers // ------- // Regex matchers for option flags. @@ -90,7 +90,7 @@ tuple.unshift(null); } return build_rule.apply(this, tuple); - }).call(this)); + })()); } return _a; }; diff --git a/lib/rewriter.js b/lib/rewriter.js index fc13a4cb..5adc164a 100644 --- a/lib/rewriter.js +++ b/lib/rewriter.js @@ -267,7 +267,7 @@ value > 0 ? _a.push(key) : null; }} return _a; - }).call(this); + })(); if (unclosed.length) { open = unclosed[0]; line = open_line[open] + 1; @@ -323,7 +323,7 @@ }, this)); }; return Rewriter; - }).call(this); + })(); // Constants // --------- // List of the token pairs that must be balanced. @@ -345,7 +345,7 @@ _d.push(pair[0]); } return _d; - }).call(this); + })(); // The tokens that signal the end of a balanced pair. EXPRESSION_END = (function() { _h = []; _j = BALANCED_PAIRS; @@ -354,7 +354,7 @@ _h.push(pair[1]); } return _h; - }).call(this); + })(); // Tokens that indicate the close of a clause of an expression. EXPRESSION_CLOSE = ['CATCH', 'WHEN', 'ELSE', 'FINALLY'].concat(EXPRESSION_END); // Tokens that, if followed by an `IMPLICIT_CALL`, indicate a function invocation. diff --git a/src/nodes.coffee b/src/nodes.coffee index c68b50c2..ffa25272 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -1263,16 +1263,20 @@ PushNode: exports.PushNode: { ClosureNode: exports.ClosureNode: { # Wrap the expressions body, unless it contains a pure statement, - # in which case, no dice. If the body mentions `arguments`, then make sure - # that the closure wrapper preserves the original arguments. + # in which case, no dice. If the body mentions `this` or `arguments`, + # then make sure that the closure wrapper preserves the original values. 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(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 }