mirror of
https://github.com/jashkenas/coffeescript.git
synced 2022-11-09 12:23:24 -05:00
commit
34be878257
5 changed files with 65 additions and 8 deletions
|
@ -736,7 +736,7 @@
|
|||
|
||||
Call.prototype.superReference = function(o) {
|
||||
var accesses, method, name;
|
||||
method = o.scope.method;
|
||||
method = o.scope.namedMethod();
|
||||
if (!method) {
|
||||
throw SyntaxError('cannot call super outside of a function.');
|
||||
}
|
||||
|
@ -756,6 +756,11 @@
|
|||
}
|
||||
};
|
||||
|
||||
Call.prototype.superThis = function(o) {
|
||||
var _ref2;
|
||||
return ((_ref2 = o.scope.method) != null ? _ref2.context : void 0) || "this";
|
||||
};
|
||||
|
||||
Call.prototype.unfoldSoak = function(o) {
|
||||
var call, ifn, left, list, rite, _i, _len, _ref2, _ref3;
|
||||
if (this.soak) {
|
||||
|
@ -852,20 +857,20 @@
|
|||
return _results;
|
||||
})()).join(', ');
|
||||
if (this.isSuper) {
|
||||
return this.superReference(o) + (".call(this" + (args && ', ' + args) + ")");
|
||||
return this.superReference(o) + (".call(" + (this.superThis(o)) + (args && ', ' + args) + ")");
|
||||
} else {
|
||||
return (this.isNew ? 'new ' : '') + this.variable.compile(o, LEVEL_ACCESS) + ("(" + args + ")");
|
||||
}
|
||||
};
|
||||
|
||||
Call.prototype.compileSuper = function(args, o) {
|
||||
return "" + (this.superReference(o)) + ".call(this" + (args.length ? ', ' : '') + args + ")";
|
||||
return "" + (this.superReference(o)) + ".call(" + (this.superThis(o)) + (args.length ? ', ' : '') + args + ")";
|
||||
};
|
||||
|
||||
Call.prototype.compileSplat = function(o, splatArgs) {
|
||||
var base, fun, idt, name, ref;
|
||||
if (this.isSuper) {
|
||||
return "" + (this.superReference(o)) + ".apply(this, " + splatArgs + ")";
|
||||
return "" + (this.superReference(o)) + ".apply(" + (this.superThis(o)) + ", " + splatArgs + ")";
|
||||
}
|
||||
if (this.isNew) {
|
||||
idt = this.tab + TAB;
|
||||
|
|
|
@ -38,6 +38,13 @@
|
|||
}
|
||||
};
|
||||
|
||||
Scope.prototype.namedMethod = function() {
|
||||
if (this.method.name || !this.parent) {
|
||||
return this.method;
|
||||
}
|
||||
return this.parent.namedMethod();
|
||||
};
|
||||
|
||||
Scope.prototype.find = function(name) {
|
||||
if (this.check(name)) {
|
||||
return true;
|
||||
|
|
|
@ -496,7 +496,7 @@ exports.Call = class Call extends Base
|
|||
# Grab the reference to the superclass's implementation of the current
|
||||
# method.
|
||||
superReference: (o) ->
|
||||
{method} = o.scope
|
||||
method = o.scope.namedMethod()
|
||||
throw SyntaxError 'cannot call super outside of a function.' unless method
|
||||
{name} = method
|
||||
throw SyntaxError 'cannot call super on an anonymous function.' unless name?
|
||||
|
@ -508,6 +508,10 @@ exports.Call = class Call extends Base
|
|||
else
|
||||
"#{name}.__super__.constructor"
|
||||
|
||||
# The appropriate `this` value for a `super` call.
|
||||
superThis : (o) ->
|
||||
o.scope.method?.context or "this"
|
||||
|
||||
# Soaked chained invocations unfold into if/else ternary structures.
|
||||
unfoldSoak: (o) ->
|
||||
if @soak
|
||||
|
@ -566,21 +570,21 @@ exports.Call = class Call extends Base
|
|||
args = @filterImplicitObjects @args
|
||||
args = (arg.compile o, LEVEL_LIST for arg in args).join ', '
|
||||
if @isSuper
|
||||
@superReference(o) + ".call(this#{ args and ', ' + args })"
|
||||
@superReference(o) + ".call(#{@superThis(o)}#{ args and ', ' + args })"
|
||||
else
|
||||
(if @isNew then 'new ' else '') + @variable.compile(o, LEVEL_ACCESS) + "(#{args})"
|
||||
|
||||
# `super()` is converted into a call against the superclass's implementation
|
||||
# of the current function.
|
||||
compileSuper: (args, o) ->
|
||||
"#{@superReference(o)}.call(this#{ if args.length then ', ' else '' }#{args})"
|
||||
"#{@superReference(o)}.call(#{@superThis(o)}#{ if args.length then ', ' else '' }#{args})"
|
||||
|
||||
# If you call a function with a splat, it's converted into a JavaScript
|
||||
# `.apply()` call to allow an array of arguments to be passed.
|
||||
# If it's a constructor, then things get real tricky. We have to inject an
|
||||
# inner constructor in order to be able to pass the varargs.
|
||||
compileSplat: (o, splatArgs) ->
|
||||
return "#{ @superReference o }.apply(this, #{splatArgs})" if @isSuper
|
||||
return "#{ @superReference o }.apply(#{@superThis(o)}, #{splatArgs})" if @isSuper
|
||||
if @isNew
|
||||
idt = @tab + TAB
|
||||
return """
|
||||
|
|
|
@ -30,6 +30,15 @@ exports.Scope = class Scope
|
|||
else
|
||||
@positions[name] = @variables.push({name, type}) - 1
|
||||
|
||||
# When `super` is called, we need to find the name of the current method we're
|
||||
# in, so that we know how to invoke the same method of the parent class. This
|
||||
# can get complicated if super is being called from an inner function.
|
||||
# `namedMethod` will walk up the scope tree until it either finds the first
|
||||
# function object that has a name filled in, or bottoms out.
|
||||
namedMethod: ->
|
||||
return @method if @method.name or !@parent
|
||||
@parent.namedMethod()
|
||||
|
||||
# Look up a variable name in lexical scope, and declare it if it does not
|
||||
# already exist.
|
||||
find: (name) ->
|
||||
|
|
|
@ -46,3 +46,35 @@ test "#2255: global leak with splatted @-params", ->
|
|||
ok not x?
|
||||
arrayEq [0], ((@x...) -> @x).call {}, 0
|
||||
ok not x?
|
||||
|
||||
test "#1183: super + fat arrows", ->
|
||||
dolater = (cb) -> cb()
|
||||
|
||||
class A
|
||||
constructor: ->
|
||||
@_i = 0
|
||||
foo : (cb) ->
|
||||
dolater =>
|
||||
@_i += 1
|
||||
cb()
|
||||
|
||||
class B extends A
|
||||
constructor : ->
|
||||
super
|
||||
foo : (cb) ->
|
||||
dolater =>
|
||||
dolater =>
|
||||
@_i += 2
|
||||
super cb
|
||||
|
||||
b = new B()
|
||||
b.foo => eq b._i, 3
|
||||
|
||||
test "#1183: super + wrap", ->
|
||||
class A
|
||||
m : -> 10
|
||||
class B extends A
|
||||
constructor : -> super
|
||||
B::m = -> r = try super()
|
||||
eq (new B()).m(), 10
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue