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(' ');
}
return _b;
}).call(this).join('') : '';
})().join('') : '';
desc = task.description ? ("# " + (task.description)) : '';
puts(("cake " + name + spaces + " " + desc));
}}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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