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

Adopting coco-style efficient bound functions for the common case ... but not for class/prototypes.

This commit is contained in:
Jeremy Ashkenas 2011-09-25 21:44:23 -04:00
parent 52dd348289
commit 981db17b8f
3 changed files with 48 additions and 21 deletions

View file

@ -1,6 +1,6 @@
(function() {
var Access, Arr, Assign, Base, Block, Call, Class, Closure, Code, Comment, Existence, Extends, For, IDENTIFIER, IDENTIFIER_STR, IS_STRING, If, In, Index, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, Literal, METHOD_DEF, NEGATE, NO, Obj, Op, Param, Parens, RESERVED, Range, Return, SIMPLENUM, Scope, Slice, Splat, Switch, TAB, THIS, Throw, Try, UTILITIES, Value, While, YES, compact, del, ends, extend, flatten, last, merge, multident, starts, unfoldSoak, utility, _ref;
var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; }, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __indexOf = Array.prototype.indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (__hasProp.call(this, i) && this[i] === item) return i; } return -1; };
var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; }, __indexOf = Array.prototype.indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (__hasProp.call(this, i) && this[i] === item) return i; } return -1; };
Scope = require('./scope').Scope;
@ -397,8 +397,8 @@
};
Literal.prototype.compileNode = function(o) {
var code, _ref2;
code = this.isUndefined ? o.level >= LEVEL_ACCESS ? '(void 0)' : 'void 0' : this.value.reserved && ((_ref2 = "" + this.value) !== 'eval' && _ref2 !== 'arguments') ? "\"" + this.value + "\"" : this.value;
var code, _ref2, _ref3;
code = this.isUndefined ? o.level >= LEVEL_ACCESS ? '(void 0)' : 'void 0' : this.value === 'this' ? ((_ref2 = o.scope.method) != null ? _ref2.bound : void 0) ? o.scope.method.context : this.value : this.value.reserved && ((_ref3 = "" + this.value) !== 'eval' && _ref3 !== 'arguments') ? "\"" + this.value + "\"" : this.value;
if (this.isStatement()) {
return "" + this.tab + code + ";";
} else {
@ -563,20 +563,21 @@
Value.prototype.unfoldSoak = function(o) {
var result;
var _this = this;
if (this.unfoldedSoak != null) return this.unfoldedSoak;
result = __bind(function() {
result = (function() {
var fst, i, ifn, prop, ref, snd, _len, _ref2;
if (ifn = this.base.unfoldSoak(o)) {
Array.prototype.push.apply(ifn.body.properties, this.properties);
if (ifn = _this.base.unfoldSoak(o)) {
Array.prototype.push.apply(ifn.body.properties, _this.properties);
return ifn;
}
_ref2 = this.properties;
_ref2 = _this.properties;
for (i = 0, _len = _ref2.length; i < _len; i++) {
prop = _ref2[i];
if (!prop.soak) continue;
prop.soak = false;
fst = new Value(this.base, this.properties.slice(0, i));
snd = new Value(this.base, this.properties.slice(i));
fst = new Value(_this.base, _this.properties.slice(0, i));
snd = new Value(_this.base, _this.properties.slice(i));
if (fst.isComplex()) {
ref = new Literal(o.scope.freeVariable('ref'));
fst = new Parens(new Assign(ref, fst));
@ -587,7 +588,7 @@
});
}
return null;
}, this)();
})();
return this.unfoldedSoak = result || false;
};
@ -1166,7 +1167,8 @@
};
Class.prototype.walkBody = function(name, o) {
return this.traverseChildren(false, __bind(function(child) {
var _this = this;
return this.traverseChildren(false, function(child) {
var exps, i, node, _len, _ref2;
if (child instanceof Class) return false;
if (child instanceof Block) {
@ -1174,12 +1176,12 @@
for (i = 0, _len = _ref2.length; i < _len; i++) {
node = _ref2[i];
if (node instanceof Value && node.isObject(true)) {
exps[i] = this.addProperties(node, name, o);
exps[i] = _this.addProperties(node, name, o);
}
}
return child.expressions = exps = flatten(exps);
}
}, this));
});
};
Class.prototype.ensureConstructor = function(name) {
@ -1429,7 +1431,7 @@
this.params = params || [];
this.body = body || new Block;
this.bound = tag === 'boundfunc';
if (this.bound) this.context = 'this';
if (this.bound) this.context = '_this';
}
Code.prototype.children = ['params', 'body'];
@ -1441,7 +1443,7 @@
Code.prototype.jumps = NO;
Code.prototype.compileNode = function(o) {
var code, exprs, i, idt, lit, p, param, ref, splats, v, val, vars, wasEmpty, _i, _j, _k, _len, _len2, _len3, _len4, _ref2, _ref3, _ref4, _ref5;
var code, exprs, i, idt, lit, p, param, ref, splats, v, val, vars, wasEmpty, _i, _j, _k, _len, _len2, _len3, _len4, _ref2, _ref3, _ref4, _ref5, _ref6;
o.scope = new Scope(o.scope, this.body, this);
o.scope.shared = del(o, 'sharedScope');
o.indent += TAB;
@ -1500,6 +1502,13 @@
}
}
if (!(wasEmpty || this.noReturn)) this.body.makeReturn();
if (this.bound) {
if ((_ref6 = o.scope.parent.method) != null ? _ref6.bound : void 0) {
this.bound = o.scope.parent.method.context;
} else {
o.scope.parent.assign('_this', 'this');
}
}
idt = o.indent;
code = 'function';
if (this.ctor) code += ' ' + this.name;
@ -1509,9 +1518,6 @@
}
code += '}';
if (this.ctor) return this.tab + code;
if (this.bound) {
return utility('bind') + ("(" + code + ", " + this.context + ")");
}
if (this.front || (o.level >= LEVEL_ACCESS)) {
return "(" + code + ")";
} else {

View file

@ -310,6 +310,8 @@ exports.Literal = class Literal extends Base
compileNode: (o) ->
code = if @isUndefined
if o.level >= LEVEL_ACCESS then '(void 0)' else 'void 0'
else if @value is 'this'
if o.scope.method?.bound then o.scope.method.context else @value
else if @value.reserved and "#{@value}" not in ['eval', 'arguments']
"\"#{@value}\""
else
@ -1080,7 +1082,7 @@ exports.Code = class Code extends Base
@params = params or []
@body = body or new Block
@bound = tag is 'boundfunc'
@context = 'this' if @bound
@context = '_this' if @bound
children: ['params', 'body']
@ -1095,7 +1097,7 @@ exports.Code = class Code extends Base
# a closure.
compileNode: (o) ->
o.scope = new Scope o.scope, @body, this
o.scope.shared = del o, 'sharedScope'
o.scope.shared = del(o, 'sharedScope')
o.indent += TAB
delete o.bare
vars = []
@ -1122,6 +1124,11 @@ exports.Code = class Code extends Base
@body.expressions.unshift exprs... if exprs.length
o.scope.parameter vars[i] = v.compile o for v, i in vars unless splats
@body.makeReturn() unless wasEmpty or @noReturn
if @bound
if o.scope.parent.method?.bound
@bound = o.scope.parent.method.context
else
o.scope.parent.assign '_this', 'this'
idt = o.indent
code = 'function'
code += ' ' + @name if @ctor
@ -1129,7 +1136,6 @@ exports.Code = class Code extends Base
code += "\n#{ @body.compileWithDeclarations o }\n#{@tab}" unless @body.isEmpty()
code += '}'
return @tab + code if @ctor
return utility('bind') + "(#{code}, #{@context})" if @bound
if @front or (o.level >= LEVEL_ACCESS) then "(#{code})" else code
# Short-circuit `traverseChildren` method to prevent it from crossing scope boundaries

View file

@ -64,6 +64,21 @@ ok obj isnt obj.unbound()
eq obj, obj.nested()
test "even more fancy bound functions", ->
obj =
one: ->
do =>
return this.two()
two: ->
do =>
do =>
do =>
return this.three
three: 3
eq obj.one(), 3
test "self-referencing functions", ->
changeMe = ->
changeMe = 2