Skip to content

Commit 34be878

Browse files
committed
Fixes #1183, Refactors #2252, super calls in inner functions
2 parents 87257ea + b03bea1 commit 34be878

File tree

5 files changed

+65
-8
lines changed

5 files changed

+65
-8
lines changed

lib/coffee-script/nodes.js

+9-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/coffee-script/scope.js

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/nodes.coffee

+8-4
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,7 @@ exports.Call = class Call extends Base
496496
# Grab the reference to the superclass's implementation of the current
497497
# method.
498498
superReference: (o) ->
499-
{method} = o.scope
499+
method = o.scope.namedMethod()
500500
throw SyntaxError 'cannot call super outside of a function.' unless method
501501
{name} = method
502502
throw SyntaxError 'cannot call super on an anonymous function.' unless name?
@@ -508,6 +508,10 @@ exports.Call = class Call extends Base
508508
else
509509
"#{name}.__super__.constructor"
510510

511+
# The appropriate `this` value for a `super` call.
512+
superThis : (o) ->
513+
o.scope.method?.context or "this"
514+
511515
# Soaked chained invocations unfold into if/else ternary structures.
512516
unfoldSoak: (o) ->
513517
if @soak
@@ -566,21 +570,21 @@ exports.Call = class Call extends Base
566570
args = @filterImplicitObjects @args
567571
args = (arg.compile o, LEVEL_LIST for arg in args).join ', '
568572
if @isSuper
569-
@superReference(o) + ".call(this#{ args and ', ' + args })"
573+
@superReference(o) + ".call(#{@superThis(o)}#{ args and ', ' + args })"
570574
else
571575
(if @isNew then 'new ' else '') + @variable.compile(o, LEVEL_ACCESS) + "(#{args})"
572576

573577
# `super()` is converted into a call against the superclass's implementation
574578
# of the current function.
575579
compileSuper: (args, o) ->
576-
"#{@superReference(o)}.call(this#{ if args.length then ', ' else '' }#{args})"
580+
"#{@superReference(o)}.call(#{@superThis(o)}#{ if args.length then ', ' else '' }#{args})"
577581

578582
# If you call a function with a splat, it's converted into a JavaScript
579583
# `.apply()` call to allow an array of arguments to be passed.
580584
# If it's a constructor, then things get real tricky. We have to inject an
581585
# inner constructor in order to be able to pass the varargs.
582586
compileSplat: (o, splatArgs) ->
583-
return "#{ @superReference o }.apply(this, #{splatArgs})" if @isSuper
587+
return "#{ @superReference o }.apply(#{@superThis(o)}, #{splatArgs})" if @isSuper
584588
if @isNew
585589
idt = @tab + TAB
586590
return """

src/scope.coffee

+9
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,15 @@ exports.Scope = class Scope
3030
else
3131
@positions[name] = @variables.push({name, type}) - 1
3232

33+
# When `super` is called, we need to find the name of the current method we're
34+
# in, so that we know how to invoke the same method of the parent class. This
35+
# can get complicated if super is being called from an inner function.
36+
# `namedMethod` will walk up the scope tree until it either finds the first
37+
# function object that has a name filled in, or bottoms out.
38+
namedMethod: ->
39+
return @method if @method.name or !@parent
40+
@parent.namedMethod()
41+
3342
# Look up a variable name in lexical scope, and declare it if it does not
3443
# already exist.
3544
find: (name) ->

test/scope.coffee

+32
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,35 @@ test "#2255: global leak with splatted @-params", ->
4646
ok not x?
4747
arrayEq [0], ((@x...) -> @x).call {}, 0
4848
ok not x?
49+
50+
test "#1183: super + fat arrows", ->
51+
dolater = (cb) -> cb()
52+
53+
class A
54+
constructor: ->
55+
@_i = 0
56+
foo : (cb) ->
57+
dolater =>
58+
@_i += 1
59+
cb()
60+
61+
class B extends A
62+
constructor : ->
63+
super
64+
foo : (cb) ->
65+
dolater =>
66+
dolater =>
67+
@_i += 2
68+
super cb
69+
70+
b = new B()
71+
b.foo => eq b._i, 3
72+
73+
test "#1183: super + wrap", ->
74+
class A
75+
m : -> 10
76+
class B extends A
77+
constructor : -> super
78+
B::m = -> r = try super()
79+
eq (new B()).m(), 10
80+

0 commit comments

Comments
 (0)