@@ -467,7 +467,7 @@ private Collection<LocalVariableNode> initAndHoistLocalVars(InsnList insnList) {
467467 }
468468 Map <String , LocalVariableNode > localVarsByName = new HashMap <>();
469469 Map <Integer , LocalVariableNode > localVarsBySlot = new HashMap <>();
470- Map <String , List <LocalVariableNode >> hoistableVarByName = new HashMap <>();
470+ Map <String , Set <LocalVariableNode >> hoistableVarByName = new HashMap <>();
471471 for (LocalVariableNode localVar : methodNode .localVariables ) {
472472 int idx = localVar .index - localVarBaseOffset ;
473473 if (idx < argOffset ) {
@@ -479,13 +479,14 @@ private Collection<LocalVariableNode> initAndHoistLocalVars(InsnList insnList) {
479479 removeDuplicatesFromArgs (hoistableVarByName , localVarsBySlotArray );
480480 // hoist vars
481481 List <LocalVariableNode > results = new ArrayList <>();
482- for (Map .Entry <String , List <LocalVariableNode >> entry : hoistableVarByName .entrySet ()) {
483- List <LocalVariableNode > hoistableVars = entry .getValue ();
482+ for (Map .Entry <String , Set <LocalVariableNode >> entry : hoistableVarByName .entrySet ()) {
483+ Set <LocalVariableNode > hoistableVars = entry .getValue ();
484484 LocalVariableNode newVarNode ;
485485 if (hoistableVars .size () > 1 ) {
486486 // merge variables
487- String name = hoistableVars .get (0 ).name ;
488- String desc = hoistableVars .get (0 ).desc ;
487+ LocalVariableNode firstHoistableVar = hoistableVars .iterator ().next ();
488+ String name = firstHoistableVar .name ;
489+ String desc = firstHoistableVar .desc ;
489490 Type localVarType = getType (desc );
490491 int newSlot = newVar (localVarType ); // new slot for the local variable
491492 newVarNode = new LocalVariableNode (name , desc , null , null , null , newSlot );
@@ -502,7 +503,7 @@ private Collection<LocalVariableNode> initAndHoistLocalVars(InsnList insnList) {
502503 methodNode .localVariables .add (newVarNode );
503504 } else {
504505 // hoist the single variable and rewrite all its local var instructions
505- newVarNode = hoistableVars .get ( 0 );
506+ newVarNode = hoistableVars .iterator (). next ( );
506507 int oldIndex = newVarNode .index ;
507508 newVarNode .index = newVar (getType (newVarNode .desc )); // new slot for the local variable
508509 rewriteLocalVarInsn (newVarNode , oldIndex , newVarNode .index );
@@ -514,7 +515,7 @@ private Collection<LocalVariableNode> initAndHoistLocalVars(InsnList insnList) {
514515 }
515516
516517 private void removeDuplicatesFromArgs (
517- Map <String , List <LocalVariableNode >> hoistableVarByName ,
518+ Map <String , Set <LocalVariableNode >> hoistableVarByName ,
518519 LocalVariableNode [] localVarsBySlotArray ) {
519520 for (int idx = 0 ; idx < argOffset ; idx ++) {
520521 LocalVariableNode localVar = localVarsBySlotArray [idx ];
@@ -573,13 +574,13 @@ private void checkHoistableLocalVar(
573574 LocalVariableNode localVar ,
574575 Map <String , LocalVariableNode > localVarsByName ,
575576 Map <Integer , LocalVariableNode > localVarsBySlot ,
576- Map <String , List <LocalVariableNode >> hoistableVarByName ) {
577+ Map <String , Set <LocalVariableNode >> hoistableVarByName ) {
577578 LocalVariableNode previousVarBySlot = localVarsBySlot .putIfAbsent (localVar .index , localVar );
578579 LocalVariableNode previousVarByName = localVarsByName .putIfAbsent (localVar .name , localVar );
579580 if (previousVarBySlot != null ) {
580581 // there are multiple local variables with the same slot but different names
581582 // by hoisting in a new slot, we can avoid the conflict
582- hoistableVarByName .computeIfAbsent (localVar .name , k -> new ArrayList <>()).add (localVar );
583+ hoistableVarByName .computeIfAbsent (localVar .name , k -> new HashSet <>()).add (localVar );
583584 }
584585 if (previousVarByName != null ) {
585586 // there are multiple local variables with the same name
@@ -601,18 +602,11 @@ private void checkHoistableLocalVar(
601602 // Merge variables because compatible type
602603 }
603604 // by default, there is no conflict => hoistable
604- hoistableVarByName .computeIfAbsent (localVar .name , k -> new ArrayList <>()).add (localVar );
605+ hoistableVarByName .computeIfAbsent (localVar .name , k -> new HashSet <>()).add (localVar );
605606 }
606607
607608 private void rewriteLocalVarInsn (LocalVariableNode localVar , int oldSlot , int newSlot ) {
608- // previous insn could be a store to index that need to be rewritten as well
609- AbstractInsnNode previous = localVar .start .getPrevious ();
610- if (previous instanceof VarInsnNode ) {
611- VarInsnNode varInsnNode = (VarInsnNode ) previous ;
612- if (varInsnNode .var == oldSlot ) {
613- varInsnNode .var = newSlot ;
614- }
615- }
609+ rewritePreviousStoreInsn (localVar , oldSlot , newSlot );
616610 for (AbstractInsnNode insn = localVar .start ;
617611 insn != null && insn != localVar .end ;
618612 insn = insn .getNext ()) {
@@ -631,6 +625,42 @@ private void rewriteLocalVarInsn(LocalVariableNode localVar, int oldSlot, int ne
631625 }
632626 }
633627
628+ // Previous insn(s) to local var range could be a store to index that need to be rewritten as well
629+ // LocalVariableTable ranges starts after the init of the local var.
630+ // ex:
631+ // 9: iconst_0
632+ // 10: astore_1
633+ // 11: aload_1
634+ // range for slot 1 starts at 11
635+ // javac always starts the range right after the init of the local var, so we can just look for
636+ // the previous instruction
637+ // for kotlinc, many instructions can separate the init and the range start
638+ // ex:
639+ // LocalVariableTable:
640+ // Start Length Slot Name Signature
641+ // 70 121 4 $i$f$map I
642+ //
643+ // 64: istore 4
644+ // 66: aload_2
645+ // 67: iconst_2
646+ // 68: iconst_1
647+ // 69: bastore
648+ // 70: aload_3
649+ private static void rewritePreviousStoreInsn (
650+ LocalVariableNode localVar , int oldSlot , int newSlot ) {
651+ AbstractInsnNode previous = localVar .start .getPrevious ();
652+ while (previous != null
653+ && (!(previous instanceof VarInsnNode ) || ((VarInsnNode ) previous ).var != oldSlot )) {
654+ previous = previous .getPrevious ();
655+ }
656+ if (previous != null ) {
657+ VarInsnNode varInsnNode = (VarInsnNode ) previous ;
658+ if (varInsnNode .var == oldSlot ) {
659+ varInsnNode .var = newSlot ;
660+ }
661+ }
662+ }
663+
634664 private void createInProbeFinallyHandler (LabelNode inProbeStartLabel , LabelNode inProbeEndLabel ) {
635665 LabelNode handlerLabel = new LabelNode ();
636666 InsnList handler = new InsnList ();
0 commit comments