Skip to content

Commit

Permalink
Fixes #1183, Refactors #2252, super calls in inner functions
Browse files Browse the repository at this point in the history
  • Loading branch information
jashkenas committed Apr 24, 2012
2 parents 87257ea + b03bea1 commit 34be878
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 8 deletions.
13 changes: 9 additions & 4 deletions lib/coffee-script/nodes.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions lib/coffee-script/scope.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 8 additions & 4 deletions src/nodes.coffee
Expand Up @@ -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?
Expand All @@ -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
Expand Down Expand Up @@ -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 """
Expand Down
9 changes: 9 additions & 0 deletions src/scope.coffee
Expand Up @@ -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) ->
Expand Down
32 changes: 32 additions & 0 deletions test/scope.coffee
Expand Up @@ -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

0 comments on commit 34be878

Please sign in to comment.