@@ -393,12 +393,18 @@ exports.Base = class Base
393
393
includeCommentFragments : NO
394
394
395
395
# `jumps` tells you if an expression, or an internal part of an expression
396
- # has a flow control construct (like `break`, or `continue`, or `return`,
397
- # or `throw`) that jumps out of the normal flow of control and can’t be
398
- # used as a value. This is important because things like this make no sense;
399
- # we have to disallow them.
396
+ # has a flow control construct (like `break`, `continue`, or `return`)
397
+ # that jumps out of the normal flow of control and can’t be used as a value.
398
+ # This is important because things like this make no sense;
399
+ # we have to disallow them. Note that `throw` *is* allowed here though.
400
400
jumps : NO
401
401
402
+ # `alwaysJumps` tells you whether this node *always* has a flow control
403
+ # construct (like `break`, `continue`, `return`, or `throw`) that jumps out
404
+ # of the normal flow of control, so that the immediately following node
405
+ # definitely won't execute.
406
+ alwaysJumps : NO
407
+
402
408
# If `node.shouldCache() is false`, it is safe to use `node` more than once.
403
409
# Otherwise you need to store the value of `node` in a variable and output
404
410
# that variable several times instead. Kind of like this: `5` need not be
@@ -600,6 +606,13 @@ exports.Block = class Block extends Base
600
606
for exp in @expressions
601
607
return jumpNode if jumpNode = exp .jumps o
602
608
609
+ # Block is executed in sequence, so if any statement always jumps,
610
+ # then so does the block.
611
+ alwaysJumps : (o ) ->
612
+ for exp in @expressions
613
+ return yes if exp .alwaysJumps o
614
+ no
615
+
603
616
# A Block node does not return its entire body, rather it
604
617
# ensures that the final expression is returned.
605
618
makeReturn : (results , mark ) ->
@@ -1198,6 +1211,8 @@ exports.StatementLiteral = class StatementLiteral extends Literal
1198
1211
return this if @value is ' break' and not (o ? .loop or o ? .block )
1199
1212
return this if @value is ' continue' and not o ? .loop
1200
1213
1214
+ alwaysJumps : (o ) -> Boolean @ jumps o
1215
+
1201
1216
compileNode : (o ) ->
1202
1217
[@ makeCode " #{ @tab }#{ @value } ;" ]
1203
1218
@@ -1269,6 +1284,7 @@ exports.Return = class Return extends Base
1269
1284
isStatement : YES
1270
1285
makeReturn : THIS
1271
1286
jumps : THIS
1287
+ alwaysJumps : YES
1272
1288
1273
1289
compileToFragments : (o , level ) ->
1274
1290
expr = @expression ? .makeReturn ()
@@ -1398,6 +1414,7 @@ exports.Value = class Value extends Base
1398
1414
isJSXTag : -> @base instanceof JSXTag
1399
1415
assigns : (name ) -> not @properties .length and @base .assigns name
1400
1416
jumps : (o ) -> not @properties .length and @base .jumps o
1417
+ alwaysJumps : (o ) -> not @properties .length and @base .alwaysJumps o
1401
1418
1402
1419
isObject : (onlyGenerated ) ->
1403
1420
return no if @properties .length
@@ -3211,6 +3228,7 @@ exports.ModuleDeclaration = class ModuleDeclaration extends Base
3211
3228
3212
3229
isStatement : YES
3213
3230
jumps : THIS
3231
+ alwaysJumps : NO
3214
3232
makeReturn : THIS
3215
3233
3216
3234
checkSource : ->
@@ -3896,6 +3914,7 @@ exports.Code = class Code extends Base
3896
3914
isStatement : -> @isMethod
3897
3915
3898
3916
jumps : NO
3917
+ alwaysJumps : NO
3899
3918
3900
3919
makeScope : (parentScope ) -> new Scope parentScope, @body , this
3901
3920
@@ -4555,6 +4574,13 @@ exports.While = class While extends Base
4555
4574
return jumpNode if jumpNode = node .jumps loop : yes
4556
4575
no
4557
4576
4577
+ alwaysJumps : ->
4578
+ {expressions } = @body
4579
+ return no unless expressions .length
4580
+ for node in expressions
4581
+ return yes if node .alwaysJumps loop : yes
4582
+ no
4583
+
4558
4584
# The main difference from a JavaScript *while* is that the CoffeeScript
4559
4585
# *while* can be used as a part of a larger expression -- while loops may
4560
4586
# return an array containing the computed result of each iteration.
@@ -4931,6 +4957,10 @@ exports.Try = class Try extends Base
4931
4957
4932
4958
jumps : (o ) -> @attempt .jumps (o) or @catch ? .jumps (o)
4933
4959
4960
+ alwaysJumps : (o ) ->
4961
+ (@attempt .alwaysJumps (o) and @catch ? .alwaysJumps (o)) or
4962
+ @ensure ? .alwaysJumps (o)
4963
+
4934
4964
makeReturn : (results , mark ) ->
4935
4965
if mark
4936
4966
@attempt ? .makeReturn results, mark
@@ -4988,7 +5018,9 @@ exports.Catch = class Catch extends Base
4988
5018
4989
5019
isStatement : YES
4990
5020
4991
- jumps : (o ) -> @recovery .jumps (o)
5021
+ jumps : (o ) -> @recovery .jumps o
5022
+
5023
+ alwaysJumps : (o ) -> @recovery .alwaysJumps o
4992
5024
4993
5025
makeReturn : (results , mark ) ->
4994
5026
ret = @recovery .makeReturn results, mark
@@ -5037,6 +5069,7 @@ exports.Throw = class Throw extends Base
5037
5069
5038
5070
isStatement : YES
5039
5071
jumps : NO
5072
+ alwaysJumps : YES
5040
5073
5041
5074
# A **Throw** is already a return, of sorts...
5042
5075
makeReturn : THIS
@@ -5498,6 +5531,12 @@ exports.Switch = class Switch extends Base
5498
5531
return jumpNode if jumpNode = block .jumps o
5499
5532
@otherwise ? .jumps o
5500
5533
5534
+ alwaysJumps : (o = {block : yes }) ->
5535
+ return no unless @cases .length
5536
+ for {block} in @cases
5537
+ return no unless block .alwaysJumps o
5538
+ yes
5539
+
5501
5540
makeReturn : (results , mark ) ->
5502
5541
block .makeReturn (results, mark) for {block} in @cases
5503
5542
@otherwise or= new Block [new Literal ' void 0' ] if results
@@ -5516,8 +5555,8 @@ exports.Switch = class Switch extends Base
5516
5555
fragments = fragments .concat @ makeCode (idt1 + " case " ), cond .compileToFragments (o, LEVEL_PAREN), @ makeCode (" :\n " )
5517
5556
fragments = fragments .concat body, @ makeCode (' \n ' ) if (body = block .compileToFragments o, LEVEL_TOP).length > 0
5518
5557
break if i is @cases .length - 1 and not @otherwise
5519
- expr = @ lastNode block .expressions
5520
- continue if expr instanceof Return or expr instanceof Throw or (expr instanceof Literal and expr . jumps () and expr . value isnt ' debugger ' )
5558
+ # expr = @lastNode block.expressions
5559
+ continue if block . alwaysJumps ( )
5521
5560
fragments .push cond .makeCode (idt2 + ' break;\n ' )
5522
5561
if @otherwise and @otherwise .expressions .length
5523
5562
fragments .push @ makeCode (idt1 + " default:\n " ), (@otherwise .compileToFragments o, LEVEL_TOP)... , @ makeCode (" \n " )
@@ -5615,6 +5654,8 @@ exports.If = class If extends Base
5615
5654
5616
5655
jumps : (o ) -> @body .jumps (o) or @elseBody ? .jumps (o)
5617
5656
5657
+ alwaysJumps : (o ) -> @body .alwaysJumps (o) and @elseBody ? .alwaysJumps (o)
5658
+
5618
5659
compileNode : (o ) ->
5619
5660
if @ isStatement o then @ compileStatement o else @ compileExpression o
5620
5661
0 commit comments