Skip to content

Commit db5b2fa

Browse files
committed
Fix #4464: backticked expressions in class body should be left in the body, not hoisted
1 parent e54b8a1 commit db5b2fa

File tree

3 files changed

+65
-29
lines changed

3 files changed

+65
-29
lines changed

lib/coffeescript/nodes.js

Lines changed: 23 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/nodes.coffee

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1635,17 +1635,16 @@ exports.Class = class Class extends Base
16351635
super()
16361636

16371637
compileNode: (o) ->
1638-
@name = @determineName()
1639-
executableBody = @walkBody()
1638+
@name = @determineName()
1639+
@walkBody()
16401640

16411641
# Special handling to allow `class expr.A extends A` declarations
16421642
parentName = @parent.base.value if @parent instanceof Value and not @parent.hasProperties()
16431643
@hasNameClash = @name? and @name is parentName
16441644

16451645
node = @
1646-
1647-
if executableBody or @hasNameClash
1648-
node = new ExecutableClassBody node, executableBody
1646+
if @executableBody or @hasNameClash
1647+
node = new ExecutableClassBody node, @executableBody
16491648
else if not @name? and o.level is LEVEL_TOP
16501649
# Anonymous classes are only valid in expressions
16511650
node = new Parens node
@@ -1665,7 +1664,7 @@ exports.Class = class Class extends Base
16651664

16661665
compileClassDeclaration: (o) ->
16671666
@ctor ?= @makeDefaultConstructor() if @externalCtor or @boundMethods.length
1668-
@ctor?.noReturn = true
1667+
@ctor?.noReturn = yes
16691668

16701669
@proxyBoundMethods() if @boundMethods.length
16711670

@@ -1677,10 +1676,14 @@ exports.Class = class Class extends Base
16771676
result.push @makeCode('extends '), @parent.compileToFragments(o)..., @makeCode ' ' if @parent
16781677

16791678
result.push @makeCode '{'
1680-
unless @body.isEmpty()
1681-
@body.spaced = true
1679+
unless @passthroughBody.isEmpty() and @body.isEmpty()
1680+
@body.spaced = yes
16821681
result.push @makeCode '\n'
1683-
result.push @body.compileToFragments(o, LEVEL_TOP)...
1682+
unless @passthroughBody.isEmpty()
1683+
result.push @passthroughBody.compileToFragments(o, LEVEL_TOP)...
1684+
result.push @makeCode '\n\n' unless @body.isEmpty()
1685+
unless @body.isEmpty()
1686+
result.push @body.compileToFragments(o, LEVEL_TOP)...
16841687
result.push @makeCode "\n#{@tab}"
16851688
result.push @makeCode '}'
16861689

@@ -1703,21 +1706,21 @@ exports.Class = class Class extends Base
17031706
if name in JS_FORBIDDEN then "_#{name}" else name
17041707

17051708
walkBody: ->
1706-
@ctor = null
1707-
@boundMethods = []
1708-
executableBody = null
1709+
@ctor = null
1710+
@boundMethods = []
17091711

1710-
initializer = []
1711-
{ expressions } = @body
1712+
initializer = []
1713+
passthroughBodyExpressions = []
1714+
{ expressions } = @body
17121715

17131716
i = 0
1714-
for expression in expressions.slice()
1715-
if expression instanceof Value and expression.isObject true
1717+
for expression, expressionIndex in expressions.slice()
1718+
if expression instanceof Value and expression.isObject yes
17161719
{ properties } = expression.base
17171720
exprs = []
17181721
end = 0
17191722
start = 0
1720-
pushSlice = -> exprs.push new Value new Obj properties[start...end], true if end > start
1723+
pushSlice = -> exprs.push new Value new Obj properties[start...end], yes if end > start
17211724

17221725
while assign = properties[end]
17231726
if initializerExpression = @addInitializerExpression assign
@@ -1730,6 +1733,9 @@ exports.Class = class Class extends Base
17301733

17311734
expressions[i..i] = exprs
17321735
i += exprs.length
1736+
else if expression instanceof Value and expression.base instanceof PassthroughLiteral
1737+
passthroughBodyExpressions.push expression
1738+
expressions.splice expressionIndex, 1
17331739
else
17341740
if initializerExpression = @addInitializerExpression expression
17351741
initializer.push initializerExpression
@@ -1745,9 +1751,12 @@ exports.Class = class Class extends Base
17451751
else if method.bound
17461752
@boundMethods.push method
17471753

1754+
@passthroughBody = new Block passthroughBodyExpressions
1755+
17481756
if initializer.length isnt expressions.length
17491757
@body.expressions = (expression.hoist() for expression in initializer)
1750-
new Block expressions
1758+
@executableBody = new Block expressions
1759+
17511760

17521761
# Add an expression to the class initializer
17531762
#

test/classes.coffee

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1833,3 +1833,18 @@ test "#4591: super.x.y, super['x'].y", ->
18331833
eq 2, b.t
18341834
eq 2, b.s
18351835
eq 2, b.r
1836+
1837+
test "#4464: backticked expressions in class body", ->
1838+
class A
1839+
`get x() { return 42; }`
1840+
1841+
class B
1842+
`get x() { return 42; }`
1843+
constructor: ->
1844+
@y = 84
1845+
1846+
a = new A
1847+
eq 42, a.x
1848+
b = new B
1849+
eq 42, b.x
1850+
eq 84, b.y

0 commit comments

Comments
 (0)