mirror of
https://github.com/jashkenas/coffeescript.git
synced 2026-05-03 03:00:14 -04:00
Enabling bound functions as static members of classes... Issue #627
This commit is contained in:
23
lib/nodes.js
23
lib/nodes.js
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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: ->
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user