@@ -541,13 +541,7 @@ public RubyNode visitCaseNode(Nodes.CaseNode node) {
541
541
542
542
final Nodes .WhenNode when = (Nodes .WhenNode ) conditions [n ];
543
543
final Nodes .Node [] whenConditions = when .conditions ;
544
-
545
- boolean containSplatOperator = false ;
546
- for (Nodes .Node value : whenConditions ) {
547
- if (value instanceof Nodes .SplatNode ) {
548
- containSplatOperator = true ;
549
- }
550
- }
544
+ boolean containSplatOperator = containYARPSplatNode (whenConditions );
551
545
552
546
if (containSplatOperator ) {
553
547
final RubyNode receiver = new TruffleInternalModuleLiteralNode ();
@@ -562,20 +556,32 @@ public RubyNode visitCaseNode(Nodes.CaseNode node) {
562
556
// this `if` becomes `else` branch of the outer `if`
563
557
elseNode = ifNode ;
564
558
} else {
565
- // TODO: we duplicate `then` for each when' condition, does it make sense to avoid it?
566
- for (int k = whenConditions .length - 1 ; k >= 0 ; k --) {
567
- final var whenCondition = whenConditions [k ];
559
+ // translate `when` with multiple expressions into a single `if` operator, e.g.
560
+ // case x
561
+ // when a, b, c
562
+ // is translated into
563
+ // if x === a || x === b || x === c
564
+
565
+ RubyNode predicateNode = null ;
566
+
567
+ for (var whenCondition : whenConditions ) {
568
568
final RubyNode receiver = whenCondition .accept (this );
569
569
final RubyNode [] arguments = new RubyNode []{ NodeUtil .cloneNode (readTemp ) };
570
- final RubyNode predicateNode = createCallNode (receiver , "===" , arguments );
570
+ final RubyNode nextPredicateNode = createCallNode (receiver , "===" , arguments );
571
571
572
- // create `if` node
573
- final RubyNode thenNode = translateNodeOrNil (when .statements );
574
- final IfElseNode ifNode = IfElseNodeGen .create (predicateNode , thenNode , elseNode );
575
-
576
- // this `if` becomes `else` branch of the outer `if`
577
- elseNode = ifNode ;
572
+ if (predicateNode == null ) {
573
+ predicateNode = nextPredicateNode ;
574
+ } else {
575
+ predicateNode = OrNodeGen .create (predicateNode , nextPredicateNode );
576
+ }
578
577
}
578
+
579
+ // create `if` node
580
+ final RubyNode thenNode = translateNodeOrNil (when .statements );
581
+ final IfElseNode ifNode = IfElseNodeGen .create (predicateNode , thenNode , elseNode );
582
+
583
+ // this `if` becomes `else` branch of the outer `if`
584
+ elseNode = ifNode ;
579
585
}
580
586
}
581
587
@@ -596,27 +602,33 @@ public RubyNode visitCaseNode(Nodes.CaseNode node) {
596
602
597
603
final Nodes .WhenNode when = (Nodes .WhenNode ) conditions [n ];
598
604
final Nodes .Node [] whenConditions = when .conditions ;
599
-
600
- boolean containSplatOperator = false ;
601
- for (Nodes .Node value : whenConditions ) {
602
- if (value instanceof Nodes .SplatNode ) {
603
- containSplatOperator = true ;
604
- }
605
- }
605
+ boolean containSplatOperator = containYARPSplatNode (whenConditions );
606
606
607
607
if (!containSplatOperator ) {
608
- for (int k = whenConditions .length - 1 ; k >= 0 ; k --) {
609
- final var whenCondition = whenConditions [k ];
608
+ // translate `when` with multiple expressions into a single `if` operator, e.g.
609
+ // case
610
+ // when a, b, c
611
+ // is translated into
612
+ // if a || b || c
613
+
614
+ RubyNode predicateNode = null ;
610
615
611
- // create `if` node
612
- final RubyNode predicateNode = whenCondition .accept (this );
613
- // TODO: we duplicate `then` for each when' condition, does it make sense to avoid it?
614
- final RubyNode thenNode = translateNodeOrNil (when .statements );
615
- final IfElseNode ifNode = IfElseNodeGen .create (predicateNode , thenNode , elseNode );
616
+ for (var whenCondition : whenConditions ) {
617
+ final RubyNode nextPredicateNode = whenCondition .accept (this );
616
618
617
- // this `if` becomes `else` branch of the outer `if`
618
- elseNode = ifNode ;
619
+ if (predicateNode == null ) {
620
+ predicateNode = nextPredicateNode ;
621
+ } else {
622
+ predicateNode = OrNodeGen .create (predicateNode , nextPredicateNode );
623
+ }
619
624
}
625
+
626
+ // create `if` node
627
+ final RubyNode thenNode = translateNodeOrNil (when .statements );
628
+ final IfElseNode ifNode = IfElseNodeGen .create (predicateNode , thenNode , elseNode );
629
+
630
+ // this `if` becomes `else` branch of the outer `if`
631
+ elseNode = ifNode ;
620
632
} else {
621
633
// use Array#any? to check whether there is any truthy value
622
634
// whenConditions are translated into an array-producing node
@@ -632,8 +644,6 @@ public RubyNode visitCaseNode(Nodes.CaseNode node) {
632
644
// this `if` becomes `else` branch of the outer `if`
633
645
elseNode = ifNode ;
634
646
}
635
-
636
-
637
647
}
638
648
639
649
rubyNode = elseNode ;
@@ -1533,11 +1543,11 @@ private RubyNode translateExpressionsList(Nodes.Node[] nodes) {
1533
1543
assert nodes != null ;
1534
1544
assert nodes .length > 0 ;
1535
1545
1536
- boolean isSplatNodePresent = Arrays . stream (nodes ). anyMatch ( n -> n instanceof Nodes . SplatNode );
1546
+ boolean containSplatOperator = containYARPSplatNode (nodes );
1537
1547
1538
1548
// fast path (no SplatNode)
1539
1549
1540
- if (!isSplatNodePresent ) {
1550
+ if (!containSplatOperator ) {
1541
1551
RubyNode [] rubyNodes = new RubyNode [nodes .length ];
1542
1552
1543
1553
for (int i = 0 ; i < nodes .length ; i ++) {
@@ -1961,4 +1971,13 @@ private void assignNodePositionInSource(Nodes.Node[] nodes, RubyNode rubyNode) {
1961
1971
rubyNode .unsafeSetSourceSection (first .startOffset , length );
1962
1972
}
1963
1973
1974
+ private boolean containYARPSplatNode (Nodes .Node [] nodes ) {
1975
+ for (var n : nodes ) {
1976
+ if (n instanceof Nodes .SplatNode ) {
1977
+ return true ;
1978
+ }
1979
+ }
1980
+
1981
+ return false ;
1982
+ }
1964
1983
}
0 commit comments