mirror of
https://github.com/jashkenas/coffeescript.git
synced 2022-11-09 12:23:24 -05:00
[CS2] Compile all super calls to ES2015 super (#4424)
* Compile all super calls to ES2015 super This breaks using `super` in non-methods, meaning several tests are failing. Self-compilation still works. * Use bound functions for IIFEs containing `super` `super` can only be called directly in a method, or in an arrow function. * Fix handling of `class @A extends A` This behaviour worked 'for free' when the parent reference was being cached by the executable class body wrapper. There now needs to be special handling in place to check if the parent name matches the class name, and if so to cache the parent reference. * Fix tests broken by compiling ES2015 `super` * Disallow bare super This removes syntax support for 'bare' super calls, e.g.: class B extends A constructor: -> super `super` must now always be followed with arguments like a regular function call. This also removes the capability of implicitly forwarding arguments. The above can be equivalently be written as: class B extends A constructor: -> super arguments... * Support super with accessors `super` with following accessor(s) is now compiled to ES2015 equivalents. In particular, expressions such as `super.name`, `super[name]`, and also `super.name.prop` are all now valid, and can be used as expected as calls (i.e. `super.name()`) or in expressions (i.e. `if super.name? ...`). `super` without accessors is compiled to a constructor super call in a constructor, and otherwise, as before, to a super call to the method of the same name, i.e. speak: -> super() ...is equivalent to speak: -> super.speak() A neat side-effect of the changes is that existential calls now work properly with super, meaning `super?()` will only call if the super property exists (and is a function). This is not valid for super in constructors. * Prevent calling `super` methods with `new` This fixes a bug in the previous super handling whereby using the `new` operator with a `super` call would silently drop the `new`. This is now an explicit compiler error, as it is invalid JS at runtime. * Clean up some old super handling code This was mostly code for tracking the source classes and variables for methods, which were needed to build the old lookups on `__super__`. * Add TODO to improve bare super parse error * Add some TODOs to improve some of the class tests
This commit is contained in:
parent
cbea7b5d1c
commit
396bd4f2f2
11 changed files with 710 additions and 1024 deletions
|
@ -235,7 +235,14 @@
|
|||
return new Value($1);
|
||||
}), o('Range', function() {
|
||||
return new Value($1);
|
||||
}), o('This')
|
||||
}), o('This'), o('Super')
|
||||
],
|
||||
Super: [
|
||||
o('SUPER . Property', function() {
|
||||
return new Super(LOC(3)(new Access($3)));
|
||||
}), o('SUPER INDEX_START Expression INDEX_END', function() {
|
||||
return new Super(LOC(3)(new Index($3)));
|
||||
})
|
||||
],
|
||||
Accessor: [
|
||||
o('. Property', function() {
|
||||
|
@ -412,13 +419,8 @@
|
|||
return new Call($1, $3, $2);
|
||||
}), o('Invocation OptFuncExist Arguments', function() {
|
||||
return new Call($1, $3, $2);
|
||||
}), o('Super')
|
||||
],
|
||||
Super: [
|
||||
o('SUPER', function() {
|
||||
return new SuperCall;
|
||||
}), o('SUPER Arguments', function() {
|
||||
return new SuperCall($2);
|
||||
}), o('SUPER OptFuncExist Arguments', function() {
|
||||
return new SuperCall(LOC(1)(new Super), $3, $2);
|
||||
})
|
||||
],
|
||||
OptFuncExist: [
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
|
@ -309,6 +309,13 @@ grammar =
|
|||
o 'Parenthetical', -> new Value $1
|
||||
o 'Range', -> new Value $1
|
||||
o 'This'
|
||||
o 'Super'
|
||||
]
|
||||
|
||||
# A `super`-based expression that can be used as a value.
|
||||
Super: [
|
||||
o 'SUPER . Property', -> new Super LOC(3) new Access $3
|
||||
o 'SUPER INDEX_START Expression INDEX_END', -> new Super LOC(3) new Index $3
|
||||
]
|
||||
|
||||
# The general group of accessors into an object, by property, by prototype
|
||||
|
@ -429,12 +436,7 @@ grammar =
|
|||
o 'Value OptFuncExist String', -> new TaggedTemplateCall $1, $3, $2
|
||||
o 'Value OptFuncExist Arguments', -> new Call $1, $3, $2
|
||||
o 'Invocation OptFuncExist Arguments', -> new Call $1, $3, $2
|
||||
o 'Super'
|
||||
]
|
||||
|
||||
Super: [
|
||||
o 'SUPER', -> new SuperCall
|
||||
o 'SUPER Arguments', -> new SuperCall $2
|
||||
o 'SUPER OptFuncExist Arguments', -> new SuperCall LOC(1)(new Super), $3, $2
|
||||
]
|
||||
|
||||
# An optional existence check on a function.
|
||||
|
|
136
src/nodes.coffee
136
src/nodes.coffee
|
@ -81,7 +81,9 @@ exports.Base = class Base
|
|||
o.sharedScope = yes
|
||||
func = new Code [], Block.wrap [this]
|
||||
args = []
|
||||
if (argumentsNode = @contains isLiteralArguments) or @contains isLiteralThis
|
||||
if @contains ((node) -> node instanceof SuperCall)
|
||||
func.bound = yes
|
||||
else if (argumentsNode = @contains isLiteralArguments) or @contains isLiteralThis
|
||||
args = [new ThisLiteral]
|
||||
if argumentsNode
|
||||
meth = 'apply'
|
||||
|
@ -516,7 +518,7 @@ exports.Literal = class Literal extends Base
|
|||
[@makeCode @value]
|
||||
|
||||
toString: ->
|
||||
" #{if @isStatement() then super else @constructor.name}: #{@value}"
|
||||
" #{if @isStatement() then super() else @constructor.name}: #{@value}"
|
||||
|
||||
exports.NumberLiteral = class NumberLiteral extends Literal
|
||||
|
||||
|
@ -610,14 +612,14 @@ exports.YieldReturn = class YieldReturn extends Return
|
|||
compileNode: (o) ->
|
||||
unless o.scope.parent?
|
||||
@error 'yield can only occur inside functions'
|
||||
super
|
||||
super o
|
||||
|
||||
|
||||
exports.AwaitReturn = class AwaitReturn extends Return
|
||||
compileNode: (o) ->
|
||||
unless o.scope.parent?
|
||||
@error 'await can only occur inside functions'
|
||||
super
|
||||
super o
|
||||
|
||||
|
||||
#### Value
|
||||
|
@ -780,9 +782,10 @@ exports.Call = class Call extends Base
|
|||
# Soaked chained invocations unfold into if/else ternary structures.
|
||||
unfoldSoak: (o) ->
|
||||
if @soak
|
||||
if this instanceof SuperCall
|
||||
left = new Literal @superReference o
|
||||
if @variable instanceof Super
|
||||
left = new Literal @variable.compile o
|
||||
rite = new Value left
|
||||
@variable.error "Unsupported reference to 'super'" unless @variable.accessor?
|
||||
else
|
||||
return ifn if ifn = unfoldSoak o, this, 'variable'
|
||||
[left, rite] = new Value(@variable).cacheReference o
|
||||
|
@ -818,20 +821,11 @@ exports.Call = class Call extends Base
|
|||
compiledArgs.push (arg.compileToFragments o, LEVEL_LIST)...
|
||||
|
||||
fragments = []
|
||||
if this instanceof SuperCall
|
||||
preface = @superReference o
|
||||
if preface is 'super'
|
||||
preface += '('
|
||||
else
|
||||
preface += ".call(#{@superThis(o)}"
|
||||
if compiledArgs.length then preface += ", "
|
||||
fragments.push @makeCode preface
|
||||
else
|
||||
if @isNew then fragments.push @makeCode 'new '
|
||||
fragments.push @variable.compileToFragments(o, LEVEL_ACCESS)...
|
||||
fragments.push @makeCode "("
|
||||
fragments.push compiledArgs...
|
||||
fragments.push @makeCode ")"
|
||||
if @isNew
|
||||
@variable.error "Unsupported reference to 'super'" if @variable instanceof Super
|
||||
fragments.push @makeCode 'new '
|
||||
fragments.push @variable.compileToFragments(o, LEVEL_ACCESS)...
|
||||
fragments.push @makeCode('('), compiledArgs..., @makeCode(')')
|
||||
fragments
|
||||
|
||||
#### Super
|
||||
|
@ -842,20 +836,15 @@ exports.Call = class Call extends Base
|
|||
# expressions are evaluated without altering the return value of the `SuperCall`
|
||||
# expression.
|
||||
exports.SuperCall = class SuperCall extends Call
|
||||
children: ['expressions']
|
||||
|
||||
constructor: (args) ->
|
||||
super null, args ? [new Splat new IdentifierLiteral 'arguments']
|
||||
# Allow to recognize a bare `super` call without parentheses and arguments.
|
||||
@isBare = args?
|
||||
children: Call::children.concat ['expressions']
|
||||
|
||||
isStatement: (o) ->
|
||||
@expressions?.length and o.level is LEVEL_TOP
|
||||
|
||||
compileNode: (o) ->
|
||||
return super unless @expressions?.length
|
||||
return super o unless @expressions?.length
|
||||
|
||||
superCall = new Literal fragmentsToText super
|
||||
superCall = new Literal fragmentsToText super o
|
||||
replacement = new Block @expressions.slice()
|
||||
|
||||
if o.level > LEVEL_TOP
|
||||
|
@ -866,33 +855,26 @@ exports.SuperCall = class SuperCall extends Call
|
|||
replacement.unshift superCall
|
||||
replacement.compileToFragments o, if o.level is LEVEL_TOP then o.level else LEVEL_LIST
|
||||
|
||||
# Grab the reference to the superclass's implementation of the current
|
||||
# method.
|
||||
superReference: (o) ->
|
||||
exports.Super = class Super extends Base
|
||||
children: ['accessor']
|
||||
|
||||
constructor: (@accessor) ->
|
||||
super()
|
||||
|
||||
compileNode: (o) ->
|
||||
method = o.scope.namedMethod()
|
||||
if method?.ctor
|
||||
'super'
|
||||
else if method?.klass
|
||||
{klass, name, variable} = method
|
||||
if klass.shouldCache()
|
||||
bref = new IdentifierLiteral o.scope.parent.freeVariable 'base'
|
||||
base = new Value new Parens new Assign bref, klass
|
||||
variable.base = base
|
||||
variable.properties.splice 0, klass.properties.length
|
||||
@error 'cannot use super outside of an instance method' unless method?.isMethod
|
||||
|
||||
@inCtor = !!method.ctor
|
||||
|
||||
unless @inCtor or @accessor?
|
||||
{name, variable} = method
|
||||
if name.shouldCache() or (name instanceof Index and name.index.isAssignable())
|
||||
nref = new IdentifierLiteral o.scope.parent.freeVariable 'name'
|
||||
name.index = new Assign nref, name.index
|
||||
accesses = [new Access new PropertyName '__super__']
|
||||
accesses.push new Access new PropertyName 'constructor' if method.isStatic
|
||||
accesses.push if nref? then new Index nref else name
|
||||
(new Value bref ? klass, accesses).compile o
|
||||
else
|
||||
@error 'cannot call super outside of an instance method.'
|
||||
@accessor = if nref? then new Index nref else name
|
||||
|
||||
# The appropriate `this` value for a `super` call.
|
||||
superThis : (o) ->
|
||||
method = o.scope.method
|
||||
(method and not method.klass and method.context) or "this"
|
||||
(new Value (new Literal 'super'), if @accessor then [ @accessor ] else []).compileToFragments o
|
||||
|
||||
#### RegexWithInterpolations
|
||||
|
||||
|
@ -1195,7 +1177,11 @@ exports.Class = class Class extends Base
|
|||
@name = @determineName()
|
||||
executableBody = @walkBody()
|
||||
|
||||
if executableBody
|
||||
# Special handling to allow `class expr.A extends A` declarations
|
||||
parentName = @parent.base.value if @parent instanceof Value and not @parent.hasProperties()
|
||||
@hasNameClash = @name? and @name == parentName
|
||||
|
||||
if executableBody or @hasNameClash
|
||||
@compileNode = @compileClassDeclaration
|
||||
result = new ExecutableClassBody(@, executableBody).compileToFragments o
|
||||
@compileNode = @constructor::compileNode
|
||||
|
@ -1302,8 +1288,7 @@ exports.Class = class Class extends Base
|
|||
@boundMethods.push method.name
|
||||
method.bound = false
|
||||
|
||||
# TODO Once `super` has been changed over to ES, the check for @parent can be removed
|
||||
if @parent or initializer.length != expressions.length
|
||||
if initializer.length != expressions.length
|
||||
@body.expressions = (expression.hoist() for expression in initializer)
|
||||
new Block expressions
|
||||
|
||||
|
@ -1328,18 +1313,16 @@ exports.Class = class Class extends Base
|
|||
|
||||
# Returns a configured class initializer method
|
||||
addInitializerMethod: (assign) ->
|
||||
variable = assign.variable
|
||||
method = assign.value
|
||||
{ variable, value: method } = assign
|
||||
method.isMethod = yes
|
||||
method.isStatic = variable.looksStatic @name
|
||||
method.klass = new IdentifierLiteral @name
|
||||
method.variable = variable
|
||||
|
||||
if method.isStatic
|
||||
method.name = variable.properties[0]
|
||||
else
|
||||
methodName = variable.base
|
||||
method.name = new (if methodName.shouldCache() then Index else Access) methodName
|
||||
method.name.updateLocationDataIfMissing methodName.locationData
|
||||
method.ctor = (if @parent then 'derived' else 'base') if methodName.value is 'constructor'
|
||||
method.error 'Cannot define a constructor as a bound function' if method.bound and method.ctor
|
||||
|
||||
|
@ -1349,7 +1332,8 @@ exports.Class = class Class extends Base
|
|||
ctor = @addInitializerMethod new Assign (new Value new PropertyName 'constructor'), new Code
|
||||
@body.unshift ctor
|
||||
|
||||
ctor.body.push new SuperCall if @parent
|
||||
if @parent
|
||||
ctor.body.push new SuperCall new Super, [new Splat new IdentifierLiteral 'arguments']
|
||||
|
||||
if @externalCtor
|
||||
applyCtor = new Value @externalCtor, [ new Access new PropertyName 'apply' ]
|
||||
|
@ -1394,19 +1378,17 @@ exports.ExecutableClassBody = class ExecutableClassBody extends Base
|
|||
|
||||
o.classScope = wrapper.makeScope o.scope
|
||||
|
||||
if @class.hasNameClash
|
||||
parent = new IdentifierLiteral o.classScope.freeVariable 'superClass'
|
||||
wrapper.params.push new Param parent
|
||||
args.push @class.parent
|
||||
@class.parent = parent
|
||||
|
||||
if @externalCtor
|
||||
externalCtor = new IdentifierLiteral o.classScope.freeVariable 'ctor', reserve: no
|
||||
@class.externalCtor = externalCtor
|
||||
@externalCtor.variable.base = externalCtor
|
||||
|
||||
if @class.parent
|
||||
parent = new IdentifierLiteral o.classScope.freeVariable 'superClass', reserve: no
|
||||
params.push new Param parent
|
||||
args.push @class.parent
|
||||
|
||||
@class.parent = parent
|
||||
@body.unshift new Literal "#{@name}.__super__ = #{parent.value}.prototype"
|
||||
|
||||
if @name != @class.name
|
||||
@body.expressions.unshift new Assign (new IdentifierLiteral @name), @class
|
||||
else
|
||||
|
@ -1442,8 +1424,6 @@ exports.ExecutableClassBody = class ExecutableClassBody extends Base
|
|||
child.expressions[i] = @addProperties node.base.properties
|
||||
else if node instanceof Assign and node.variable.looksStatic @name
|
||||
node.value.isStatic = yes
|
||||
else if node instanceof Code and node.isMethod
|
||||
node.klass = new IdentifierLiteral @name
|
||||
child.expressions = flatten child.expressions
|
||||
cont
|
||||
|
||||
|
@ -1670,15 +1650,10 @@ exports.Assign = class Assign extends Base
|
|||
return @compileSpecialMath o if @context in ['**=', '//=', '%%=']
|
||||
if @value instanceof Code
|
||||
if @value.isStatic
|
||||
@value.klass = @variable.base
|
||||
@value.name = @variable.properties[0]
|
||||
@value.variable = @variable
|
||||
@value.name = @variable.properties[0]
|
||||
else if @variable.properties?.length >= 2
|
||||
[properties..., prototype, name] = @variable.properties
|
||||
if prototype.name?.value is 'prototype'
|
||||
@value.klass = new Value @variable.base, properties
|
||||
@value.name = name
|
||||
@value.variable = @variable
|
||||
@value.name = name if prototype.name?.value is 'prototype'
|
||||
unless @context
|
||||
varBase = @variable.unwrapAll()
|
||||
unless varBase.isAssignable()
|
||||
|
@ -1904,8 +1879,8 @@ exports.Code = class Code extends Base
|
|||
# function body.
|
||||
compileNode: (o) ->
|
||||
if @ctor
|
||||
@variable.error 'Class constructor may not be async' if @isAsync
|
||||
@variable.error 'Class constructor may not be a generator' if @isGenerator
|
||||
@name.error 'Class constructor may not be async' if @isAsync
|
||||
@name.error 'Class constructor may not be a generator' if @isGenerator
|
||||
|
||||
if @bound
|
||||
@context = o.scope.method.context if o.scope.method?.bound
|
||||
|
@ -2263,7 +2238,7 @@ exports.While = class While extends Base
|
|||
|
||||
makeReturn: (res) ->
|
||||
if res
|
||||
super
|
||||
super res
|
||||
else
|
||||
@returns = not @jumps loop: yes
|
||||
this
|
||||
|
@ -2985,7 +2960,6 @@ UTILITIES =
|
|||
}
|
||||
ctor.prototype = parent.prototype;
|
||||
child.prototype = new ctor();
|
||||
child.__super__ = parent.prototype;
|
||||
return child;
|
||||
}
|
||||
"
|
||||
|
@ -3052,9 +3026,7 @@ isLiteralArguments = (node) ->
|
|||
node instanceof IdentifierLiteral and node.value is 'arguments'
|
||||
|
||||
isLiteralThis = (node) ->
|
||||
node instanceof ThisLiteral or
|
||||
(node instanceof Code and node.bound) or
|
||||
node instanceof SuperCall
|
||||
node instanceof ThisLiteral or (node instanceof Code and node.bound)
|
||||
|
||||
shouldCacheOrIsAssignable = (node) -> node.shouldCache() or node.isAssignable?()
|
||||
|
||||
|
|
|
@ -40,13 +40,6 @@ test "classes with a four-level inheritance chain", ->
|
|||
ok result is 'zero/one/two/three/four'
|
||||
ok Base.static('word') is 'static/word'
|
||||
|
||||
FirstChild::func = (string) ->
|
||||
super('one/').length + string
|
||||
|
||||
result = (new ThirdChild).func 'four'
|
||||
|
||||
ok result is '9two/three/four'
|
||||
|
||||
ok (new ThirdChild).array.join(' ') is '1 2 3'
|
||||
|
||||
|
||||
|
@ -69,56 +62,46 @@ test "constructors with inheritance and super", ->
|
|||
ok (new SubClass).prop is 'top-super-sub'
|
||||
|
||||
|
||||
test "basic classes, again, but in the manual prototype style", ->
|
||||
test "'super' with accessors", ->
|
||||
class Base
|
||||
m: -> 4
|
||||
n: -> 5
|
||||
o: -> 6
|
||||
|
||||
Base = ->
|
||||
Base::func = (string) ->
|
||||
'zero/' + string
|
||||
Base::['func-func'] = (string) ->
|
||||
"dynamic-#{string}"
|
||||
name = 'o'
|
||||
class A extends Base
|
||||
m: -> super()
|
||||
n: -> super.n()
|
||||
"#{name}": -> super()
|
||||
p: -> super[name]()
|
||||
|
||||
FirstChild = ->
|
||||
SecondChild = ->
|
||||
ThirdChild = ->
|
||||
@array = [1, 2, 3]
|
||||
this
|
||||
|
||||
ThirdChild extends SecondChild extends FirstChild extends Base
|
||||
|
||||
FirstChild::func = (string) ->
|
||||
super('one/') + string
|
||||
|
||||
SecondChild::func = (string) ->
|
||||
super('two/') + string
|
||||
|
||||
ThirdChild::func = (string) ->
|
||||
super('three/') + string
|
||||
|
||||
result = (new ThirdChild).func 'four'
|
||||
|
||||
ok result is 'zero/one/two/three/four'
|
||||
|
||||
ok (new ThirdChild)['func-func']('thing') is 'dynamic-thing'
|
||||
a = new A
|
||||
eq 4, a.m()
|
||||
eq 5, a.n()
|
||||
eq 6, a.o()
|
||||
eq 6, a.p()
|
||||
|
||||
|
||||
test "super with plain ol' prototypes", ->
|
||||
test "soaked 'super' invocation", ->
|
||||
class Base
|
||||
method: -> 2
|
||||
|
||||
TopClass = ->
|
||||
TopClass::func = (arg) ->
|
||||
'top-' + arg
|
||||
class A extends Base
|
||||
method: -> super?()
|
||||
noMethod: -> super?()
|
||||
|
||||
SuperClass = ->
|
||||
SuperClass extends TopClass
|
||||
SuperClass::func = (arg) ->
|
||||
super 'super-' + arg
|
||||
a = new A
|
||||
eq 2, a.method()
|
||||
eq undefined, a.noMethod()
|
||||
|
||||
SubClass = ->
|
||||
SubClass extends SuperClass
|
||||
SubClass::func = ->
|
||||
super 'sub'
|
||||
|
||||
eq (new SubClass).func(), 'top-super-sub'
|
||||
name = 'noMethod'
|
||||
class B extends Base
|
||||
"#{'method'}": -> super?()
|
||||
"#{'noMethod'}": -> super?() ? super['method']()
|
||||
|
||||
b = new B
|
||||
eq 2, b.method()
|
||||
eq 2, b.noMethod()
|
||||
|
||||
test "'@' referring to the current instance, and not being coerced into a call", ->
|
||||
|
||||
|
@ -136,7 +119,7 @@ test "super() calls in constructors of classes that are defined as object proper
|
|||
constructor: (name) -> @name = name
|
||||
|
||||
class Hive.Bee extends Hive
|
||||
constructor: (name) -> super
|
||||
constructor: (name) -> super name
|
||||
|
||||
maya = new Hive.Bee 'Maya'
|
||||
ok maya.name is 'Maya'
|
||||
|
@ -210,7 +193,7 @@ test "calling super and passing along all arguments", ->
|
|||
method: (args...) -> @args = args
|
||||
|
||||
class Child extends Parent
|
||||
method: -> super
|
||||
method: -> super arguments...
|
||||
|
||||
c = new Child
|
||||
c.method 1, 2, 3, 4
|
||||
|
@ -421,7 +404,7 @@ test "`class extends this`", ->
|
|||
B = null
|
||||
makeClass = ->
|
||||
B = class extends this
|
||||
func: -> super + ' B'
|
||||
func: -> super() + ' B'
|
||||
|
||||
makeClass.call A
|
||||
|
||||
|
@ -483,7 +466,7 @@ test "#1313: misplaced __extends", ->
|
|||
class A
|
||||
class B extends A
|
||||
prop: nonce
|
||||
constructor: -> super
|
||||
constructor: -> super()
|
||||
eq nonce, B::prop
|
||||
|
||||
test "#1182: execution order needs to be considered as well", ->
|
||||
|
@ -510,11 +493,11 @@ test "#1372: bound class methods with reserved names", ->
|
|||
|
||||
test "#1380: `super` with reserved names", ->
|
||||
class C
|
||||
do: -> super
|
||||
do: -> super()
|
||||
ok C::do
|
||||
|
||||
class B
|
||||
0: -> super
|
||||
0: -> super()
|
||||
ok B::[0]
|
||||
|
||||
test "#1464: bound class methods should keep context", ->
|
||||
|
@ -550,7 +533,7 @@ test "#1598: super works for static methods too", ->
|
|||
|
||||
class Child extends Parent
|
||||
@method: ->
|
||||
'pass? ' + super
|
||||
'pass? ' + super()
|
||||
|
||||
eq Child.method(), 'pass? yes'
|
||||
|
||||
|
@ -732,7 +715,7 @@ test "extending native objects works with and without defining a constructor", -
|
|||
ok 'yes!', myArray.method()
|
||||
|
||||
class OverrideArray extends Array
|
||||
constructor: -> super
|
||||
constructor: -> super()
|
||||
method: -> 'yes!'
|
||||
|
||||
overrideArray = new OverrideArray
|
||||
|
@ -802,7 +785,7 @@ test "#2949: super in static method with reserved name", ->
|
|||
@static: -> 'baz'
|
||||
|
||||
class Bar extends Foo
|
||||
@static: -> super
|
||||
@static: -> super()
|
||||
|
||||
eq Bar.static(), 'baz'
|
||||
|
||||
|
@ -812,8 +795,8 @@ test "#3232: super in static methods (not object-assigned)", ->
|
|||
@qux = -> true
|
||||
|
||||
class Bar extends Foo
|
||||
@baz = -> super
|
||||
Bar.qux = -> super
|
||||
@baz = -> super()
|
||||
Bar.qux = -> super()
|
||||
|
||||
ok Bar.baz()
|
||||
ok Bar.qux()
|
||||
|
@ -825,24 +808,17 @@ test "#1392 calling `super` in methods defined on namespaced classes", ->
|
|||
namespace =
|
||||
A: ->
|
||||
B: ->
|
||||
namespace.A extends Base
|
||||
class namespace.A extends Base
|
||||
m: -> super()
|
||||
|
||||
namespace.A::m = -> super
|
||||
eq 5, (new namespace.A).m()
|
||||
namespace.B::m = namespace.A::m
|
||||
namespace.A::m = null
|
||||
eq 5, (new namespace.B).m()
|
||||
|
||||
count = 0
|
||||
getNamespace = -> count++; namespace
|
||||
getNamespace().A::n = -> super
|
||||
eq 4, (new namespace.A).n()
|
||||
eq 1, count
|
||||
|
||||
class C
|
||||
@a: (->)
|
||||
@a extends Base
|
||||
@a::m = -> super
|
||||
@a: class extends Base
|
||||
m: -> super()
|
||||
eq 5, (new C.a).m()
|
||||
|
||||
|
||||
|
@ -852,7 +828,7 @@ test "dynamic method names", ->
|
|||
eq 1, new A().m()
|
||||
|
||||
class B extends A
|
||||
"#{name = 'm'}": -> super
|
||||
"#{name = 'm'}": -> super()
|
||||
eq 1, new B().m()
|
||||
|
||||
getName = -> 'm'
|
||||
|
@ -867,18 +843,18 @@ test "dynamic method names and super", ->
|
|||
m: -> 5
|
||||
m2: -> 4.5
|
||||
n: -> 4
|
||||
A = ->
|
||||
A extends Base
|
||||
|
||||
name = -> count++; 'n'
|
||||
count = 0
|
||||
|
||||
m = 'm'
|
||||
A::[m] = -> super
|
||||
class A extends Base
|
||||
"#{m}": -> super()
|
||||
"#{name()}": -> super()
|
||||
|
||||
m = 'n'
|
||||
eq 5, (new A).m()
|
||||
|
||||
name = -> count++; 'n'
|
||||
|
||||
count = 0
|
||||
A::[name()] = -> super
|
||||
eq 4, (new A).n()
|
||||
eq 1, count
|
||||
|
||||
|
@ -886,9 +862,9 @@ test "dynamic method names and super", ->
|
|||
m2 = 'm2'
|
||||
count = 0
|
||||
class B extends Base
|
||||
@[name()] = -> super
|
||||
@::[m] = -> super
|
||||
"#{m2}": -> super
|
||||
@[name()] = -> super()
|
||||
"#{m}": -> super()
|
||||
"#{m2}": -> super()
|
||||
b = new B
|
||||
m = m2 = 'n'
|
||||
eq 6, B.m()
|
||||
|
@ -897,7 +873,7 @@ test "dynamic method names and super", ->
|
|||
eq 1, count
|
||||
|
||||
class C extends B
|
||||
m: -> super
|
||||
m: -> super()
|
||||
eq 5, (new C).m()
|
||||
|
||||
# ES2015+ class interoperability
|
||||
|
@ -1066,11 +1042,16 @@ test "`@`-params and bound methods with multiple `super` paths (expressions)", -
|
|||
test "constructor super in arrow functions", ->
|
||||
class Test extends (class)
|
||||
constructor: (@param) ->
|
||||
do => super
|
||||
do => super()
|
||||
eq @param, nonce
|
||||
|
||||
new Test nonce = {}
|
||||
|
||||
# TODO Some of these tests use CoffeeScript.compile and CoffeeScript.run when they could use
|
||||
# regular test mechanics.
|
||||
# TODO Some of these tests might be better placed in `test/error_messages.coffee`.
|
||||
# TODO Some of these tests are duplicates.
|
||||
|
||||
# Ensure that we always throw if we experience more than one super()
|
||||
# call in a constructor. This ends up being a runtime error.
|
||||
# Should be caught at compile time.
|
||||
|
@ -1230,24 +1211,6 @@ test "super and external constructors", ->
|
|||
throws -> CoffeeScript.compile throwsC, bare: yes
|
||||
|
||||
|
||||
test "super in external prototype", ->
|
||||
class A
|
||||
constructor: (@drink) ->
|
||||
make: -> "Making a #{@drink}"
|
||||
|
||||
class B extends A
|
||||
B::make = (@flavor) -> super() + " with #{@flavor}"
|
||||
b = new B('Machiato')
|
||||
eq b.make('caramel'), "Making a Machiato with caramel"
|
||||
|
||||
# Fails, bound
|
||||
# TODO: Could this throw a compile error?
|
||||
class C extends A
|
||||
C::make = (@flavor) => super() + " with #{@flavor}"
|
||||
c = new C('Machiato')
|
||||
ok c.make('caramel') isnt "Making a Machiato with caramel"
|
||||
|
||||
|
||||
test "bound functions without super", ->
|
||||
# Bound function with @
|
||||
# Throw on compile, since bound
|
||||
|
@ -1275,7 +1238,7 @@ test "super in a bound function", ->
|
|||
|
||||
class B extends A
|
||||
make: (@flavor) =>
|
||||
super + " with #{@flavor}"
|
||||
super() + " with #{@flavor}"
|
||||
|
||||
b = new B('Machiato')
|
||||
eq b.make('vanilla'), "Making a Machiato with vanilla"
|
||||
|
@ -1284,7 +1247,7 @@ test "super in a bound function", ->
|
|||
class C extends A
|
||||
make: (@flavor) =>
|
||||
func = () =>
|
||||
super + " with #{@flavor}"
|
||||
super() + " with #{@flavor}"
|
||||
func()
|
||||
|
||||
c = new C('Machiato')
|
||||
|
@ -1312,13 +1275,13 @@ test "super in a try/catch", ->
|
|||
class B extends A
|
||||
constructor: ->
|
||||
try
|
||||
super
|
||||
super()
|
||||
"""
|
||||
|
||||
throwsC = """
|
||||
ctor = ->
|
||||
try
|
||||
super
|
||||
super()
|
||||
|
||||
class C extends A
|
||||
constructor: ctor
|
||||
|
@ -1702,7 +1665,7 @@ test "CS6 Class extends a CS1 compiled class with super()", ->
|
|||
constructor: (@shots) ->
|
||||
super('caramel')
|
||||
make: () ->
|
||||
super + " and #{@shots} shots of espresso"
|
||||
super() + " and #{@shots} shots of espresso"
|
||||
|
||||
eq B.className(), 'ExtendedCS1'
|
||||
b = new B('three')
|
||||
|
|
|
@ -1284,10 +1284,10 @@ test "constructor functions can't be generators", ->
|
|||
'''
|
||||
|
||||
test "non-derived constructors can't call super", ->
|
||||
assertErrorFormat 'class then constructor: -> super', '''
|
||||
assertErrorFormat 'class then constructor: -> super()', '''
|
||||
[stdin]:1:28: error: 'super' is only allowed in derived class constructors
|
||||
class then constructor: -> super
|
||||
^^^^^
|
||||
class then constructor: -> super()
|
||||
^^^^^^^
|
||||
'''
|
||||
|
||||
test "derived constructors can't reference `this` before calling super", ->
|
||||
|
@ -1305,10 +1305,10 @@ test "derived constructors can't use @params without calling super", ->
|
|||
'''
|
||||
|
||||
test "'super' is not allowed in constructor parameter defaults", ->
|
||||
assertErrorFormat 'class extends A then constructor: (a = super) ->', '''
|
||||
assertErrorFormat 'class extends A then constructor: (a = super()) ->', '''
|
||||
[stdin]:1:40: error: 'super' is not allowed in constructor parameter defaults
|
||||
class extends A then constructor: (a = super) ->
|
||||
^^^^^
|
||||
class extends A then constructor: (a = super()) ->
|
||||
^^^^^^^
|
||||
'''
|
||||
|
||||
test "can't use pattern matches for loop indices", ->
|
||||
|
@ -1317,3 +1317,25 @@ test "can't use pattern matches for loop indices", ->
|
|||
a for b, {c} in d
|
||||
^^^
|
||||
'''
|
||||
|
||||
test "bare 'super' is no longer allowed", ->
|
||||
# TODO Improve this error message (it should at least be 'unexpected super')
|
||||
assertErrorFormat 'class extends A then constructor: -> super', '''
|
||||
[stdin]:1:35: error: unexpected ->
|
||||
class extends A then constructor: -> super
|
||||
^^
|
||||
'''
|
||||
|
||||
test "soaked 'super' in constructor", ->
|
||||
assertErrorFormat 'class extends A then constructor: -> super?()', '''
|
||||
[stdin]:1:38: error: Unsupported reference to 'super'
|
||||
class extends A then constructor: -> super?()
|
||||
^^^^^
|
||||
'''
|
||||
|
||||
test "new with 'super'", ->
|
||||
assertErrorFormat 'class extends A then foo: -> new super()', '''
|
||||
[stdin]:1:34: error: Unsupported reference to 'super'
|
||||
class extends A then foo: -> new super()
|
||||
^^^^^
|
||||
'''
|
||||
|
|
|
@ -337,8 +337,8 @@ test "generator methods in classes", ->
|
|||
arrayEq [2], Array.from new Base().method()
|
||||
|
||||
class Child extends Base
|
||||
@static: -> super
|
||||
method: -> super
|
||||
@static: -> super()
|
||||
method: -> super()
|
||||
|
||||
arrayEq [1], Array.from Child.static()
|
||||
arrayEq [2], Array.from new Child().method()
|
||||
|
|
|
@ -465,7 +465,7 @@ test "export class that extends", ->
|
|||
baz: ->
|
||||
console.log 'hello, world!'"""
|
||||
output = toJS input
|
||||
ok /export (class foo|var foo = \(function)/.test(output) and \
|
||||
ok /export var foo = class foo/.test(output) and \
|
||||
not /var foo(;|,)/.test output
|
||||
|
||||
test "export default class that extends", ->
|
||||
|
@ -473,7 +473,7 @@ test "export default class that extends", ->
|
|||
export default class foo extends bar
|
||||
baz: ->
|
||||
console.log 'hello, world!'"""
|
||||
ok /export default (class foo|foo = \(function)/.test toJS input
|
||||
ok /export default foo = class foo/.test toJS input
|
||||
|
||||
test "export default named member, within an object", ->
|
||||
input = "export { foo as default, bar }"
|
||||
|
|
|
@ -110,9 +110,9 @@ test "division vs regex after a callable token", ->
|
|||
p: (regex) -> if regex then r regex else 4
|
||||
class B extends A
|
||||
p: ->
|
||||
eq 2, super / b/g
|
||||
eq 2, super/b/g
|
||||
eq 2, super/ b/g
|
||||
eq 2, super() / b/g
|
||||
eq 2, super()/b/g
|
||||
eq 2, super()/ b/g
|
||||
eq true, super /b/g
|
||||
new B().p()
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ test "#1183: super + fat arrows", ->
|
|||
|
||||
class B extends A
|
||||
constructor : ->
|
||||
super
|
||||
super()
|
||||
foo : (cb) ->
|
||||
dolater =>
|
||||
dolater =>
|
||||
|
@ -87,9 +87,9 @@ test "#1183: super + wrap", ->
|
|||
m : -> 10
|
||||
|
||||
class B extends A
|
||||
constructor : -> super
|
||||
|
||||
B::m = -> r = try super()
|
||||
constructor : -> super()
|
||||
m: -> r = try super()
|
||||
m: -> r = super()
|
||||
|
||||
eq (new B).m(), 10
|
||||
|
||||
|
@ -113,7 +113,7 @@ test "#2331: bound super regression", ->
|
|||
method: -> @constructor.value
|
||||
|
||||
class B extends A
|
||||
method: => super
|
||||
method: => super()
|
||||
|
||||
eq (new B).method(), 'A'
|
||||
|
||||
|
|
Loading…
Reference in a new issue