88import java .util .List ;
99import java .util .Map ;
1010import java .util .Set ;
11+ import java .util .stream .Collectors ;
1112
1213import org .jetbrains .annotations .Nullable ;
1314import org .slf4j .Logger ;
1415import org .slf4j .LoggerFactory ;
1516
17+ import jadx .api .plugins .input .data .attributes .IJadxAttrType ;
18+ import jadx .api .plugins .input .data .attributes .IJadxAttribute ;
1619import jadx .core .dex .attributes .AFlag ;
1720import jadx .core .dex .attributes .AType ;
1821import jadx .core .dex .attributes .nodes .LoopInfo ;
3639public class BlockProcessor extends AbstractVisitor {
3740 private static final Logger LOG = LoggerFactory .getLogger (BlockProcessor .class );
3841
42+ private static final boolean DEBUG_MODS = false ;
43+
3944 @ Override
4045 public void visit (MethodNode mth ) {
4146 if (mth .isNoCode () || mth .getBasicBlocks ().isEmpty ()) {
@@ -57,13 +62,27 @@ private static void processBlocksTree(MethodNode mth) {
5762 }
5863 updateCleanSuccessors (mth );
5964
65+ int blocksCount = mth .getBasicBlocks ().size ();
66+ int modLimit = Math .max (100 , blocksCount );
67+ if (DEBUG_MODS ) {
68+ mth .addAttr (new DebugModAttr ());
69+ }
6070 int i = 0 ;
6171 while (modifyBlocksTree (mth )) {
6272 computeDominators (mth );
63- if (i ++ > 100 ) {
64- throw new JadxRuntimeException ("CFG modification limit reached, blocks count: " + mth .getBasicBlocks ().size ());
73+ if (i ++ > modLimit ) {
74+ mth .addWarn ("CFG modification limit reached, blocks count: " + blocksCount );
75+ break ;
6576 }
6677 }
78+ if (DEBUG_MODS && i != 0 ) {
79+ String stats = "CFG modifications count: " + i
80+ + ", blocks count: " + blocksCount + '\n'
81+ + mth .get (DebugModAttr .TYPE ).formatStats () + '\n' ;
82+ mth .addDebugComment (stats );
83+ LOG .debug ("Method: {}\n {}" , mth , stats );
84+ mth .remove (DebugModAttr .TYPE );
85+ }
6786 checkForUnreachableBlocks (mth );
6887
6988 DominatorTree .computeDominanceFrontier (mth );
@@ -298,6 +317,9 @@ private static boolean mergeConstReturn(MethodNode mth) {
298317 }
299318 if (changed ) {
300319 removeMarkedBlocks (mth );
320+ if (DEBUG_MODS ) {
321+ mth .get (DebugModAttr .TYPE ).addEvent ("Merge const return" );
322+ }
301323 }
302324 return changed ;
303325 }
@@ -338,17 +360,20 @@ private static boolean independentBlockTreeMod(MethodNode mth) {
338360
339361 private static boolean simplifyLoopEnd (MethodNode mth , LoopInfo loop ) {
340362 BlockNode loopEnd = loop .getEnd ();
341- if (loopEnd .getSuccessors ().size () > 1 ) {
342- // make loop end a simple path block
343- BlockNode newLoopEnd = BlockSplitter .startNewBlock (mth , -1 );
344- newLoopEnd .add (AFlag .SYNTHETIC );
345- newLoopEnd .add (AFlag .LOOP_END );
346- BlockNode loopStart = loop .getStart ();
347- BlockSplitter .replaceConnection (loopEnd , loopStart , newLoopEnd );
348- BlockSplitter .connect (newLoopEnd , loopStart );
349- return true ;
363+ if (loopEnd .getSuccessors ().size () <= 1 ) {
364+ return false ;
350365 }
351- return false ;
366+ // make loop end a simple path block
367+ BlockNode newLoopEnd = BlockSplitter .startNewBlock (mth , -1 );
368+ newLoopEnd .add (AFlag .SYNTHETIC );
369+ newLoopEnd .add (AFlag .LOOP_END );
370+ BlockNode loopStart = loop .getStart ();
371+ BlockSplitter .replaceConnection (loopEnd , loopStart , newLoopEnd );
372+ BlockSplitter .connect (newLoopEnd , loopStart );
373+ if (DEBUG_MODS ) {
374+ mth .get (DebugModAttr .TYPE ).addEvent ("Simplify loop end" );
375+ }
376+ return true ;
352377 }
353378
354379 private static boolean checkLoops (MethodNode mth , BlockNode block ) {
@@ -371,7 +396,6 @@ private static boolean checkLoops(MethodNode mth, BlockNode block) {
371396 if (loopsCount == 1 ) {
372397 LoopInfo loop = loops .get (0 );
373398 return insertBlocksForContinue (mth , loop )
374- || insertBlockForPredecessors (mth , loop )
375399 || insertPreHeader (mth , loop )
376400 || simplifyLoopEnd (mth , loop );
377401 }
@@ -398,15 +422,21 @@ private static boolean insertPreHeader(MethodNode mth, LoopInfo loop) {
398422 mth .setEnterBlock (newEnterBlock );
399423 start .remove (AFlag .MTH_ENTER_BLOCK );
400424 BlockSplitter .connect (newEnterBlock , start );
401- return true ;
425+ } else {
426+ // multiple predecessors
427+ BlockNode preHeader = BlockSplitter .startNewBlock (mth , -1 );
428+ preHeader .add (AFlag .SYNTHETIC );
429+ BlockNode loopEnd = loop .getEnd ();
430+ for (BlockNode pred : new ArrayList <>(preds )) {
431+ if (pred != loopEnd ) {
432+ BlockSplitter .replaceConnection (pred , start , preHeader );
433+ }
434+ }
435+ BlockSplitter .connect (preHeader , start );
402436 }
403- // multiple predecessors
404- BlockNode preHeader = BlockSplitter .startNewBlock (mth , -1 );
405- preHeader .add (AFlag .SYNTHETIC );
406- for (BlockNode pred : new ArrayList <>(preds )) {
407- BlockSplitter .replaceConnection (pred , start , preHeader );
437+ if (DEBUG_MODS ) {
438+ mth .get (DebugModAttr .TYPE ).addEvent ("Insert loop pre header" );
408439 }
409- BlockSplitter .connect (preHeader , start );
410440 return true ;
411441 }
412442
@@ -426,6 +456,9 @@ private static boolean insertBlocksForBreak(MethodNode mth, LoopInfo loop) {
426456 }
427457 }
428458 }
459+ if (DEBUG_MODS && change ) {
460+ mth .get (DebugModAttr .TYPE ).addEvent ("Insert loop break blocks" );
461+ }
429462 return change ;
430463 }
431464
@@ -444,24 +477,10 @@ private static boolean insertBlocksForContinue(MethodNode mth, LoopInfo loop) {
444477 }
445478 }
446479 }
447- return change ;
448- }
449-
450- /**
451- * Insert additional block if loop header has several predecessors (exclude back edges)
452- */
453- private static boolean insertBlockForPredecessors (MethodNode mth , LoopInfo loop ) {
454- BlockNode loopHeader = loop .getStart ();
455- List <BlockNode > preds = loopHeader .getPredecessors ();
456- if (preds .size () > 2 ) {
457- List <BlockNode > blocks = new ArrayList <>(preds );
458- blocks .removeIf (block -> block .contains (AFlag .LOOP_END ));
459- BlockNode first = blocks .remove (0 );
460- BlockNode preHeader = BlockSplitter .insertBlockBetween (mth , first , loopHeader );
461- blocks .forEach (block -> BlockSplitter .replaceConnection (block , loopHeader , preHeader ));
462- return true ;
480+ if (DEBUG_MODS && change ) {
481+ mth .get (DebugModAttr .TYPE ).addEvent ("Insert loop continue block" );
463482 }
464- return false ;
483+ return change ;
465484 }
466485
467486 private static boolean splitLoops (MethodNode mth , BlockNode block , List <LoopInfo > loops ) {
@@ -472,17 +491,20 @@ private static boolean splitLoops(MethodNode mth, BlockNode block, List<LoopInfo
472491 break ;
473492 }
474493 }
475- if (oneHeader ) {
476- // several back edges connected to one loop header => make additional block
477- BlockNode newLoopEnd = BlockSplitter .startNewBlock (mth , block .getStartOffset ());
478- newLoopEnd .add (AFlag .SYNTHETIC );
479- connect (newLoopEnd , block );
480- for (LoopInfo la : loops ) {
481- BlockSplitter .replaceConnection (la .getEnd (), block , newLoopEnd );
482- }
483- return true ;
494+ if (!oneHeader ) {
495+ return false ;
484496 }
485- return false ;
497+ // several back edges connected to one loop header => make additional block
498+ BlockNode newLoopEnd = BlockSplitter .startNewBlock (mth , block .getStartOffset ());
499+ newLoopEnd .add (AFlag .SYNTHETIC );
500+ connect (newLoopEnd , block );
501+ for (LoopInfo la : loops ) {
502+ BlockSplitter .replaceConnection (la .getEnd (), block , newLoopEnd );
503+ }
504+ if (DEBUG_MODS ) {
505+ mth .get (DebugModAttr .TYPE ).addEvent ("Split loops" );
506+ }
507+ return true ;
486508 }
487509
488510 private static boolean splitExitBlocks (MethodNode mth ) {
@@ -496,6 +518,9 @@ private static boolean splitExitBlocks(MethodNode mth) {
496518 }
497519 if (changed ) {
498520 updateExitBlockConnections (mth );
521+ if (DEBUG_MODS ) {
522+ mth .get (DebugModAttr .TYPE ).addEvent ("Split exit block" );
523+ }
499524 }
500525 return changed ;
501526 }
@@ -691,4 +716,25 @@ private static void clearBlocksState(MethodNode mth) {
691716 block .getDominatesOn ().clear ();
692717 });
693718 }
719+
720+ private static final class DebugModAttr implements IJadxAttribute {
721+ static final IJadxAttrType <DebugModAttr > TYPE = IJadxAttrType .create ("DebugModAttr" );
722+
723+ private final Map <String , Integer > statMap = new HashMap <>();
724+
725+ public void addEvent (String name ) {
726+ statMap .merge (name , 1 , Integer ::sum );
727+ }
728+
729+ public String formatStats () {
730+ return statMap .entrySet ().stream ()
731+ .map (entry -> " " + entry .getKey () + ": " + entry .getValue ())
732+ .collect (Collectors .joining ("\n " ));
733+ }
734+
735+ @ Override
736+ public IJadxAttrType <DebugModAttr > getAttrType () {
737+ return TYPE ;
738+ }
739+ }
694740}
0 commit comments