Enabling bound functions as static members of classes... Issue #627

This commit is contained in:
Jeremy Ashkenas
2010-08-17 21:07:36 -04:00
parent f41ca2e5e8
commit 1b05cd81f0
3 changed files with 37 additions and 18 deletions

View File

@@ -845,14 +845,18 @@
continue; continue;
} }
if (func instanceof CodeNode && func.bound) { if (func instanceof CodeNode && func.bound) {
func.bound = false; if (prop.context === 'this') {
constScope || (constScope = new Scope(o.scope, constructor.body, constructor)); func.context = className;
me || (me = constScope.freeVariable()); } else {
pname = pvar.compile(o); func.bound = false;
if (constructor.body.empty()) { constScope || (constScope = new Scope(o.scope, constructor.body, constructor));
constructor.body.push(new ReturnNode(literal('this'))); me || (me = constScope.freeVariable());
pname = pvar.compile(o);
if (constructor.body.empty()) {
constructor.body.push(new ReturnNode(literal('this')));
}
constructor.body.unshift(literal("this." + (pname) + " = function(){ return " + (className) + ".prototype." + (pname) + ".apply(" + (me) + ", arguments); }"));
} }
constructor.body.unshift(literal("this." + (pname) + " = function(){ return " + (className) + ".prototype." + (pname) + ".apply(" + (me) + ", arguments); }"));
} }
if (pvar) { if (pvar) {
access = prop.context === 'this' ? pvar.base.properties[0] : new AccessorNode(pvar, 'prototype'); access = prop.context === 'this' ? pvar.base.properties[0] : new AccessorNode(pvar, 'prototype');
@@ -1001,6 +1005,9 @@
this.params || (this.params = []); this.params || (this.params = []);
this.body || (this.body = new Expressions()); this.body || (this.body = new Expressions());
this.bound = tag === 'boundfunc'; this.bound = tag === 'boundfunc';
if (this.bound) {
this.context = 'this';
}
return this; return this;
}; };
__extends(CodeNode, BaseNode); __extends(CodeNode, BaseNode);
@@ -1066,7 +1073,7 @@
code = this.body.expressions.length ? ("\n" + (this.body.compileWithDeclarations(o)) + "\n") : ''; code = this.body.expressions.length ? ("\n" + (this.body.compileWithDeclarations(o)) + "\n") : '';
func = ("function(" + (params.join(', ')) + ") {" + (code) + (code && this.tab) + "}"); func = ("function(" + (params.join(', ')) + ") {" + (code) + (code && this.tab) + "}");
if (this.bound) { if (this.bound) {
return ("" + (utility('bind')) + "(" + (func) + ", this)"); return ("" + (utility('bind')) + "(" + (func) + ", " + (this.context) + ")");
} }
return top ? ("(" + (func) + ")") : func; return top ? ("(" + (func) + ")") : func;
}; };

View File

@@ -740,12 +740,15 @@ exports.ClassNode = class ClassNode extends BaseNode
constructor = func constructor = func
continue continue
if func instanceof CodeNode and func.bound if func instanceof CodeNode and func.bound
func.bound = false if prop.context is 'this'
constScope or= new Scope(o.scope, constructor.body, constructor) func.context = className
me or= constScope.freeVariable() else
pname = pvar.compile(o) func.bound = false
constructor.body.push new ReturnNode literal 'this' if constructor.body.empty() constScope or= new Scope(o.scope, constructor.body, constructor)
constructor.body.unshift literal "this.#{pname} = function(){ return #{className}.prototype.#{pname}.apply(#{me}, arguments); }" me or= constScope.freeVariable()
pname = pvar.compile(o)
constructor.body.push new ReturnNode literal 'this' if constructor.body.empty()
constructor.body.unshift literal "this.#{pname} = function(){ return #{className}.prototype.#{pname}.apply(#{me}, arguments); }"
if pvar if pvar
access = if prop.context is 'this' then pvar.base.properties[0] else new AccessorNode(pvar, 'prototype') access = if prop.context is 'this' then pvar.base.properties[0] else new AccessorNode(pvar, 'prototype')
val = new ValueNode(@variable, [access]) val = new ValueNode(@variable, [access])
@@ -874,9 +877,10 @@ exports.CodeNode = class CodeNode extends BaseNode
constructor: (@params, @body, tag) -> constructor: (@params, @body, tag) ->
super() super()
@params or= [] @params or= []
@body or= new Expressions @body or= new Expressions
@bound = tag is 'boundfunc' @bound = tag is 'boundfunc'
@context = 'this' if @bound
# Compilation creates a new scope unless explicitly asked to share with the # Compilation creates a new scope unless explicitly asked to share with the
# outer scope. Handles splat parameters in the parameter list by peeking at # outer scope. Handles splat parameters in the parameter list by peeking at
@@ -918,7 +922,7 @@ exports.CodeNode = class CodeNode extends BaseNode
(o.scope.parameter(param)) for param in params (o.scope.parameter(param)) for param in params
code = if @body.expressions.length then "\n#{ @body.compileWithDeclarations(o) }\n" else '' code = if @body.expressions.length then "\n#{ @body.compileWithDeclarations(o) }\n" else ''
func = "function(#{ params.join(', ') }) {#{code}#{ code and @tab }}" func = "function(#{ params.join(', ') }) {#{code}#{ code and @tab }}"
return "#{utility('bind')}(#{func}, this)" if @bound return "#{utility('bind')}(#{func}, #{@context})" if @bound
if top then "(#{func})" else func if top then "(#{func})" else func
topSensitive: -> topSensitive: ->

View File

@@ -131,6 +131,7 @@ ok instance.name() is 'class'
# Classes with methods that are pre-bound to the instance. # Classes with methods that are pre-bound to the instance.
# ... or statically, to the class.
class Dog class Dog
constructor: (name) -> constructor: (name) ->
@@ -139,12 +140,19 @@ class Dog
bark: => bark: =>
"#{@name} woofs!" "#{@name} woofs!"
@static: =>
new this('Dog')
spark = new Dog('Spark') spark = new Dog('Spark')
fido = new Dog('Fido') fido = new Dog('Fido')
fido.bark = spark.bark fido.bark = spark.bark
ok fido.bark() is 'Spark woofs!' ok fido.bark() is 'Spark woofs!'
obj = func: Dog.static
ok obj.func().name is 'Dog'
# Testing a bound function in a bound function. # Testing a bound function in a bound function.
class Mini class Mini