43
43
import java .util .ArrayDeque ;
44
44
import java .util .ArrayList ;
45
45
import java .util .Arrays ;
46
+ import java .util .Collections ;
46
47
import java .util .HashMap ;
47
48
import java .util .List ;
48
49
import java .util .Objects ;
@@ -421,6 +422,7 @@ public String toString(byte[] bytecode) {
421
422
case JUMP_IF_TRUE_OR_POP :
422
423
case MATCH_EXC_OR_JUMP :
423
424
case SEND :
425
+ case THROW :
424
426
lines .computeIfAbsent (bcBCI + oparg , k -> new String [DISASSEMBLY_NUM_COLUMNS ])[1 ] = ">>" ;
425
427
line [5 ] = String .format ("to %d" , bcBCI + oparg );
426
428
break ;
@@ -557,41 +559,182 @@ public int lineToBci(int line) {
557
559
return afterFirst ? bestBci : -2 ;
558
560
}
559
561
560
- public enum JumpBlock {
561
- None (0 , null ),
562
- With (1 , "the body of a with statement" ),
563
- Loop (2 , "the body of a for loop" ),
564
- Try (3 , "the body of a try statement" ),
565
- Except (4 , "an 'except' block as there's no exception" );
562
+ public enum StackItem {
563
+ With ("the body of a with statement" ),
564
+ Iterable ("the body of a for loop" ),
565
+ Except ("an 'except' block as there's no exception" ),
566
+ Object ("Incompatible stack" );
566
567
567
- public final int i ;
568
568
public final String error ;
569
- public static final long BITS = 3 ;
570
569
571
- JumpBlock ( int i , String error ) {
570
+ StackItem ( String error ) {
572
571
this .error = error ;
573
- this .i = i ;
574
572
}
575
573
576
- private static final JumpBlock [] VALUES = JumpBlock .values ();
574
+ ArrayList <StackItem > push (ArrayList <StackItem > v ) {
575
+ ArrayList <StackItem > ret = v == null ? new ArrayList <>() : new ArrayList <>(v );
576
+ ret .add (this );
577
+ return ret ;
578
+ }
579
+ }
580
+
581
+ private void setNextStack (ArrayDeque <Integer > todo , List <ArrayList <StackItem >> stacks , int target , ArrayList <StackItem > value ) {
582
+ ArrayList <StackItem > blocksAtTarget = stacks .get (target );
583
+ if (blocksAtTarget == null ) {
584
+ stacks .set (target , value );
585
+ todo .addLast (target );
586
+ } else {
587
+ assert value .equals (blocksAtTarget ) : "found conflicting stacks depending on code path: " + this .name ;
588
+ }
589
+ }
577
590
578
- public long push (long stack ) {
579
- return stack << BITS | this .i ;
591
+ private static ArrayList <StackItem > popStack (ArrayList <StackItem > blocks ) {
592
+ assert blocks != null : "Pop from null stack" ;
593
+ assert blocks .size () >= 1 : "Pop from empty stack" ;
594
+ return new ArrayList <>(blocks .subList (0 , blocks .size () - 1 ));
595
+ }
596
+
597
+ // returns null if the jump is fine
598
+ public String checkJump (int from , int to ) {
599
+ List <ArrayList <StackItem >> blocks = computeStackElems ();
600
+ ArrayList <StackItem > blkFrom = blocks .get (from );
601
+ System .out .println (this );
602
+
603
+ iterateBytecode (code , (bci , op , oparg , following ) -> {
604
+ System .out .println (bci + "\t " +
605
+ op .getNumberOfProducedStackItems (oparg , following , false ) + "\t " +
606
+ op .getNumberOfConsumedStackItems (oparg , following , false ) + "\t " + op + "\t " +
607
+ blocks .get (bci ));
608
+ });
609
+
610
+ // todo these should be exceptions
611
+ assert blkFrom != null : "Unreachable origin" ;
612
+ ArrayList <StackItem > blkTo = blocks .get (to );
613
+ assert blkTo != null : "Unreachable target" ;
614
+ if (blkTo .size () > blkFrom .size ()) {
615
+ return blkTo .get (blkTo .size () - 1 ).error ;
580
616
}
617
+ for (int i = blkTo .size () - 1 ; i >= 0 ; --i ) {
618
+ if (blkTo .get (i ) != blkFrom .get (i )) {
619
+ return blkTo .get (i ).error ;
620
+ }
621
+ }
622
+ return null ;
623
+ }
624
+
625
+ private List <ArrayList <StackItem >> computeStackElems () {
626
+ List <ArrayList <StackItem >> blocks = new ArrayList <>(Collections .nCopies (code .length + 1 , null ));
627
+ blocks .set (0 , new ArrayList <>());
628
+ ArrayDeque <Integer > todo = new ArrayDeque <>();
629
+ todo .addFirst (0 );
630
+ while (!todo .isEmpty ()) {
631
+ int i = todo .removeLast ();
632
+ assert blocks .get (i ) != null : "TODO message here" ;
633
+ opCodeAt (code , i , (bci , op , oparg , followingArgs ) -> {
634
+ ArrayList <StackItem > next = blocks .get (bci );
635
+ for (int j = 0 ; j < exceptionHandlerRanges .length ; j += 4 ) {
636
+ int start = exceptionHandlerRanges [j ];
637
+ int handler = exceptionHandlerRanges [j + 2 ];
638
+ int stack = exceptionHandlerRanges [j + 3 ];
639
+ if (start == bci ) {
640
+ ArrayList <StackItem > handlerStack = StackItem .Except .push (new ArrayList <>(blocks .get (bci ).subList (0 , stack )));
641
+ // an exception handler is like a jump
642
+ // the except block is added in the lines below
643
+ setNextStack (todo , blocks , handler , handlerStack );
644
+ }
645
+ }
646
+ switch (op ) {
647
+ case GET_ITER :
648
+ case GET_AITER :
649
+ next = StackItem .Iterable .push (popStack (blocks .get (bci )));
650
+ setNextStack (todo , blocks , bci + 1 , next );
651
+ break ;
652
+ case PUSH_EXC_INFO :
653
+ next = StackItem .Except .push (blocks .get (bci ));
654
+ setNextStack (todo , blocks , bci + 1 , next );
655
+ break ;
656
+ case MATCH_EXC_OR_JUMP :
657
+ next = popStack (next );
658
+ setNextStack (todo , blocks , op .getNextBci (bci , oparg , false ), next );
659
+ setNextStack (todo , blocks , op .getNextBci (bci , oparg , true ), next );
660
+ break ;
661
+ case SETUP_WITH :
662
+ case SETUP_AWITH :
663
+ next = StackItem .Object .push (StackItem .With .push (blocks .get (bci )));
664
+ setNextStack (todo , blocks , op .getNextBci (bci , oparg , false ), next );
665
+ break ;
666
+ case GET_AEXIT_CORO :
667
+ next = StackItem .Object .push (StackItem .Except .push (popStack (popStack (popStack (blocks .get (bci ))))));
668
+ setNextStack (todo , blocks , op .getNextBci (bci , oparg , false ), next );
669
+ break ;
670
+ case DUP_TOP :
671
+ next = next .get (next .size () - 1 ).push (next );
672
+ setNextStack (todo , blocks , op .getNextBci (bci , oparg , false ), next );
673
+ break ;
674
+ case ROT_TWO : {
675
+ StackItem top = next .get (next .size () - 1 );
676
+ StackItem belowTop = next .get (next .size () - 2 );
677
+ next = belowTop .push (top .push (popStack (popStack (next ))));
678
+ setNextStack (todo , blocks , op .getNextBci (bci , oparg , false ), next );
679
+ break ;
680
+ }
681
+ case ROT_THREE : {
682
+ StackItem top = next .get (next .size () - 1 );
683
+ StackItem second = next .get (next .size () - 2 );
684
+ StackItem third = next .get (next .size () - 3 );
685
+ next = second .push (third .push (top .push (top .push (popStack (popStack (popStack (next )))))));
686
+ setNextStack (todo , blocks , op .getNextBci (bci , oparg , false ), next );
687
+ break ;
688
+ }
689
+ case LOAD_NONE :
690
+ opCodeAt (code , op .getNextBci (bci , oparg , false ), (ignored , nextOp , ignored2 , ignored3 ) -> {
691
+ // used instead of exceptions in non-error paths for these opcodes
692
+ // the handler code will push the Except StackItem
693
+ if (nextOp != OpCodes .GET_AEXIT_CORO && nextOp != OpCodes .EXIT_WITH ) {
694
+ setNextStack (todo , blocks , op .getNextBci (bci , oparg , false ), StackItem .Object .push (blocks .get (bci )));
695
+ }
696
+ });
697
+ break ;
581
698
582
- public static long pop (long stack ) {
583
- return stack >> BITS ;
699
+ default : {
700
+ int nextWJump = op .getNextBci (bci , oparg , true );
701
+ int nextWOJump = op .getNextBci (bci , oparg , false );
702
+ int stackLostWJump = op .getNumberOfConsumedStackItems (oparg , followingArgs , true );
703
+ int stackLostWOJump = op .getNumberOfConsumedStackItems (oparg , followingArgs , false );
704
+ int stackGainWJump = op .getNumberOfProducedStackItems (oparg , followingArgs , true );
705
+ int stackGainWOJump = op .getNumberOfProducedStackItems (oparg , followingArgs , false );
706
+ handleGeneralOp (blocks , todo , bci , nextWJump , stackLostWJump , stackGainWJump );
707
+ if (nextWJump != nextWOJump ) {
708
+ handleGeneralOp (blocks , todo , bci , nextWOJump , stackLostWOJump , stackGainWOJump );
709
+ }
710
+ break ;
711
+ }
712
+ }
713
+ });
584
714
}
715
+ return blocks ;
716
+ }
585
717
586
- public static JumpBlock peek (long stack ) {
587
- return VALUES [(int ) (stack & ((1 << BITS ) - 1 ))];
718
+ private void handleGeneralOp (List <ArrayList <StackItem >> blocks , ArrayDeque <Integer > todo , int bci , int next , int stackLost , int stackGain ) {
719
+ if (next >= 0 ) {
720
+ ArrayList <StackItem > blocksHere = new ArrayList <>(blocks .get (bci ));
721
+ // System.out.println("Before: " + stackLost + "\t" + stackGain + "\t" + bci + "\t" +
722
+ // blocksHere);
723
+ for (int k = 0 ; k < stackLost ; ++k ) {
724
+ blocksHere .remove (blocksHere .size () - 1 );
725
+ }
726
+ for (int k = 0 ; k < stackGain ; ++k ) {
727
+ blocksHere .add (StackItem .Object );
728
+ }
729
+ // System.out.println("Before stack: " + blocks.get(next));
730
+ // System.out.println("After: " + blocksHere);
731
+ setNextStack (todo , blocks , next , blocksHere );
588
732
}
589
733
}
590
734
591
735
@ CompilerDirectives .TruffleBoundary
592
- public void computeStackLevels (long [] blocks , int [] stackLevels ) {
593
- assert code .length + 1 == blocks .length ;
594
- assert stackLevels .length == code .length ;
736
+ public int [] computeStackLevels () {
737
+ int [] stackLevels = new int [code .length ];
595
738
Arrays .fill (stackLevels , -1 );
596
739
// stackLevels has the stack depth before the corresponding opcode executes
597
740
stackLevels [0 ] = 0 ;
@@ -622,9 +765,6 @@ public void computeStackLevels(long[] blocks, int[] stackLevels) {
622
765
}
623
766
if (stackLevels [bciWJump ] < 0 ) {
624
767
stackLevels [bciWJump ] = stackHere + stackWJump ;
625
- if (op == OpCodes .GET_ITER ) {
626
- System .out .println (stackLevels [bciWJump ]);
627
- }
628
768
todo .addFirst (bciWJump );
629
769
} else {
630
770
assert stackLevels [bciWJump ] == stackHere + stackWJump ;
@@ -639,6 +779,7 @@ public void computeStackLevels(long[] blocks, int[] stackLevels) {
639
779
}
640
780
});
641
781
}
782
+ return stackLevels ;
642
783
}
643
784
644
785
@ FunctionalInterface
0 commit comments