Skip to content

Commit becdf50

Browse files
GeoffreyBoothconnec
authored andcommitted
Fix jashkenas#4464: backticked expressions in class body should be left in the body, not hoisted
1 parent eb12792 commit becdf50

File tree

3 files changed

+65
-29
lines changed

3 files changed

+65
-29
lines changed

lib/coffeescript/nodes.js

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

src/nodes.coffee

+27-18
Original file line numberDiff line numberDiff line change
@@ -1636,17 +1636,16 @@ exports.Class = class Class extends Base
16361636
super()
16371637

16381638
compileNode: (o) ->
1639-
@name = @determineName()
1640-
executableBody = @walkBody()
1639+
@name = @determineName()
1640+
@walkBody()
16411641

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

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

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

16711670
@proxyBoundMethods() if @boundMethods.length
16721671

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

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

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

17061709
walkBody: ->
1707-
@ctor = null
1708-
@boundMethods = []
1709-
executableBody = null
1710+
@ctor = null
1711+
@boundMethods = []
17101712

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

17141717
i = 0
1715-
for expression in expressions.slice()
1716-
if expression instanceof Value and expression.isObject true
1718+
for expression, expressionIndex in expressions.slice()
1719+
if expression instanceof Value and expression.isObject yes
17171720
{ properties } = expression.base
17181721
exprs = []
17191722
end = 0
17201723
start = 0
1721-
pushSlice = -> exprs.push new Value new Obj properties[start...end], true if end > start
1724+
pushSlice = -> exprs.push new Value new Obj properties[start...end], yes if end > start
17221725

17231726
while assign = properties[end]
17241727
if initializerExpression = @addInitializerExpression assign
@@ -1731,6 +1734,9 @@ exports.Class = class Class extends Base
17311734

17321735
expressions[i..i] = exprs
17331736
i += exprs.length
1737+
else if expression instanceof Value and expression.base instanceof PassthroughLiteral
1738+
passthroughBodyExpressions.push expression
1739+
expressions.splice expressionIndex, 1
17341740
else
17351741
if initializerExpression = @addInitializerExpression expression
17361742
initializer.push initializerExpression
@@ -1746,9 +1752,12 @@ exports.Class = class Class extends Base
17461752
else if method.bound
17471753
@boundMethods.push method
17481754

1755+
@passthroughBody = new Block passthroughBodyExpressions
1756+
17491757
if initializer.length isnt expressions.length
17501758
@body.expressions = (expression.hoist() for expression in initializer)
1751-
new Block expressions
1759+
@executableBody = new Block expressions
1760+
17521761

17531762
# Add an expression to the class initializer
17541763
#

test/classes.coffee

+15
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)