Projects STRLCPY jadx Commits a0d8d9fc
🤬
  • core: fix 'break' detection in loops

  • Loading...
  • Skylot committed 1 decade ago
    a0d8d9fc
    1 parent a2142b2f
Revision indexing in progress... (symbol navigation in revisions will be accurate after indexed)
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/codegen/RegionGen.java
    skipped 92 lines
    93 93   for (InsnNode insn : block.getInstructions()) {
    94 94   makeInsn(insn, code);
    95 95   }
    96  - if (block.getAttributes().contains(AttributeFlag.BREAK)) {
    97  - code.startLine("break;");
    98  - } else {
    99  - IAttribute attr;
    100  - if ((attr = block.getAttributes().get(AttributeType.FORCE_RETURN)) != null) {
    101  - ForceReturnAttr retAttr = (ForceReturnAttr) attr;
    102  - makeInsn(retAttr.getReturnInsn(), code);
    103  - }
     96 + IAttribute attr;
     97 + if ((attr = block.getAttributes().get(AttributeType.FORCE_RETURN)) != null) {
     98 + ForceReturnAttr retAttr = (ForceReturnAttr) attr;
     99 + makeInsn(retAttr.getReturnInsn(), code);
    104 100   }
    105 101   }
    106 102   
    skipped 178 lines
  • ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/dex/attributes/AttributeFlag.java
    skipped 8 lines
    9 9   
    10 10   SYNTHETIC,
    11 11   
    12  - BREAK,
    13 12   RETURN, // block contains only return instruction
    14 13   
    15 14   DECLARE_VAR,
    skipped 14 lines
  • ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/dex/attributes/LoopAttr.java
    1 1  package jadx.core.dex.attributes;
    2 2   
    3 3  import jadx.core.dex.nodes.BlockNode;
     4 +import jadx.core.dex.nodes.Edge;
    4 5  import jadx.core.utils.BlockUtils;
    5 6   
    6 7  import java.util.Collections;
    7 8  import java.util.HashSet;
     9 +import java.util.LinkedList;
     10 +import java.util.List;
    8 11  import java.util.Set;
    9 12   
    10 13  public class LoopAttr implements IAttribute {
    skipped 26 lines
    37 40   }
    38 41   
    39 42   /**
    40  - * Return block nodes with exit edges from loop <br>
     43 + * Return source blocks of exit edges. <br>
    41 44   * Exit nodes belongs to loop (contains in {@code loopBlocks})
    42 45   */
    43 46   public Set<BlockNode> getExitNodes() {
    44 47   Set<BlockNode> nodes = new HashSet<BlockNode>();
    45  - Set<BlockNode> inloop = getLoopBlocks();
    46  - for (BlockNode block : inloop) {
     48 + Set<BlockNode> blocks = getLoopBlocks();
     49 + for (BlockNode block : blocks) {
    47 50   // exit: successor node not from this loop, (don't change to getCleanSuccessors)
    48 51   for (BlockNode s : block.getSuccessors()) {
    49  - if (!inloop.contains(s) && !s.getAttributes().contains(AttributeType.EXC_HANDLER)) {
     52 + if (!blocks.contains(s) && !s.getAttributes().contains(AttributeType.EXC_HANDLER)) {
    50 53   nodes.add(block);
    51 54   }
    52 55   }
    53 56   }
    54 57   return nodes;
     58 + }
     59 + 
     60 + /**
     61 + * Return loop exit edges.
     62 + */
     63 + public List<Edge> getExitEdges() {
     64 + List<Edge> edges = new LinkedList<Edge>();
     65 + Set<BlockNode> blocks = getLoopBlocks();
     66 + for (BlockNode block : blocks) {
     67 + for (BlockNode s : block.getSuccessors()) {
     68 + if (!blocks.contains(s) && !s.getAttributes().contains(AttributeType.EXC_HANDLER)) {
     69 + edges.add(new Edge(block, s));
     70 + }
     71 + }
     72 + }
     73 + return edges;
    55 74   }
    56 75   
    57 76   @Override
    skipped 5 lines
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/dex/nodes/BlockNode.java
    1 1  package jadx.core.dex.nodes;
    2 2   
    3 3  import jadx.core.dex.attributes.AttrNode;
     4 +import jadx.core.dex.attributes.AttributeFlag;
    4 5  import jadx.core.dex.attributes.AttributeType;
    5 6  import jadx.core.dex.attributes.BlockRegState;
    6 7  import jadx.core.dex.attributes.LoopAttr;
    skipped 139 lines
    146 147   
    147 148   public void setEndState(BlockRegState endState) {
    148 149   this.endState = endState;
     150 + }
     151 + 
     152 + public boolean isSynthetic() {
     153 + return getAttributes().contains(AttributeFlag.SYNTHETIC);
     154 + }
     155 + 
     156 + public boolean isReturnBlock() {
     157 + return getAttributes().contains(AttributeFlag.RETURN);
    149 158   }
    150 159   
    151 160   @Override
    skipped 34 lines
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/dex/nodes/Edge.java
     1 +package jadx.core.dex.nodes;
     2 + 
     3 +public class Edge {
     4 + private final BlockNode source;
     5 + private final BlockNode target;
     6 + 
     7 + public Edge(BlockNode source, BlockNode target) {
     8 + this.source = source;
     9 + this.target = target;
     10 + }
     11 + 
     12 + public BlockNode getSource() {
     13 + return source;
     14 + }
     15 + 
     16 + public BlockNode getTarget() {
     17 + return target;
     18 + }
     19 + 
     20 + @Override
     21 + public boolean equals(Object o) {
     22 + if (this == o) {
     23 + return true;
     24 + }
     25 + if (o == null || getClass() != o.getClass()) {
     26 + return false;
     27 + }
     28 + Edge edge = (Edge) o;
     29 + return source.equals(edge.source) && target.equals(edge.target);
     30 + 
     31 + }
     32 + 
     33 + @Override
     34 + public int hashCode() {
     35 + return source.hashCode() + 31 * target.hashCode();
     36 + }
     37 + 
     38 + @Override
     39 + public String toString() {
     40 + return "Edge: " + source + " -> " + target;
     41 + }
     42 +}
     43 + 
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/dex/visitors/BlockMakerVisitor.java
    skipped 11 lines
    12 12  import jadx.core.dex.instructions.args.InsnArg;
    13 13  import jadx.core.dex.instructions.args.RegisterArg;
    14 14  import jadx.core.dex.nodes.BlockNode;
     15 +import jadx.core.dex.nodes.Edge;
    15 16  import jadx.core.dex.nodes.InsnNode;
    16 17  import jadx.core.dex.nodes.MethodNode;
    17 18  import jadx.core.dex.trycatch.CatchAttr;
    skipped 281 lines
    299 300   }
    300 301   }
    301 302   
     303 + private static void markReturnBlocks(MethodNode mth) {
     304 + mth.getExitBlocks().clear();
     305 + for (BlockNode block : mth.getBasicBlocks()) {
     306 + if (BlockUtils.lastInsnType(block, InsnType.RETURN)) {
     307 + block.getAttributes().add(AttributeFlag.RETURN);
     308 + mth.getExitBlocks().add(block);
     309 + }
     310 + }
     311 + }
     312 + 
    302 313   private static void markLoops(MethodNode mth) {
    303 314   for (BlockNode block : mth.getBasicBlocks()) {
    304 315   for (BlockNode succ : block.getSuccessors()) {
    skipped 11 lines
    316 327   }
    317 328   }
    318 329   
    319  - private static void markReturnBlocks(MethodNode mth) {
    320  - mth.getExitBlocks().clear();
    321  - for (BlockNode block : mth.getBasicBlocks()) {
    322  - if (BlockUtils.lastInsnType(block, InsnType.RETURN)) {
    323  - block.getAttributes().add(AttributeFlag.RETURN);
    324  - mth.getExitBlocks().add(block);
    325  - }
    326  - }
    327  - }
    328  - 
    329 330   private static void registerLoops(MethodNode mth) {
    330 331   for (BlockNode block : mth.getBasicBlocks()) {
    331 332   AttributesList attributes = block.getAttributes();
    skipped 24 lines
    356 357   if (oneHeader) {
    357 358   // several back edges connected to one loop header => make additional block
    358 359   BlockNode newLoopHeader = startNewBlock(mth, block.getStartOffset());
     360 + newLoopHeader.getAttributes().add(AttributeFlag.SYNTHETIC);
    359 361   connect(newLoopHeader, block);
    360 362   for (IAttribute a : loops) {
    361 363   LoopAttr la = (LoopAttr) a;
    skipped 4 lines
    366 368   return true;
    367 369   }
    368 370   }
     371 + // insert additional blocks if loop has several exits
     372 + if (loops.size() == 1) {
     373 + LoopAttr loop = (LoopAttr) loops.get(0);
     374 + List<Edge> edges = loop.getExitEdges();
     375 + if (edges.size() > 1) {
     376 + boolean change = false;
     377 + for (Edge edge : edges) {
     378 + BlockNode target = edge.getTarget();
     379 + if (!target.getAttributes().contains(AttributeFlag.SYNTHETIC)) {
     380 + insertBlockBetween(mth, edge.getSource(), target);
     381 + change = true;
     382 + }
     383 + }
     384 + if (change) {
     385 + return true;
     386 + }
     387 + }
     388 + }
    369 389   }
    370 390   if (splitReturn(mth)) {
    371 391   return true;
    skipped 4 lines
    376 396   return false;
    377 397   }
    378 398   
     399 + private static BlockNode insertBlockBetween(MethodNode mth, BlockNode source, BlockNode target) {
     400 + BlockNode newBlock = startNewBlock(mth, target.getStartOffset());
     401 + newBlock.getAttributes().add(AttributeFlag.SYNTHETIC);
     402 + removeConnection(source, target);
     403 + connect(source, newBlock);
     404 + connect(newBlock, target);
     405 + return newBlock;
     406 + }
     407 + 
    379 408   /**
    380 409   * Merge return blocks for void methods
    381 410   */
    skipped 32 lines
    414 443   if (mth.getExitBlocks().size() != 1) {
    415 444   return false;
    416 445   }
    417  - boolean split = false;
    418 446   BlockNode exitBlock = mth.getExitBlocks().get(0);
    419 447   if (exitBlock.getPredecessors().size() > 1
    420 448   && exitBlock.getInstructions().size() == 1
    skipped 1 lines
    422 450   && !exitBlock.getAttributes().contains(AttributeFlag.SYNTHETIC)) {
    423 451   InsnNode returnInsn = exitBlock.getInstructions().get(0);
    424 452   List<BlockNode> preds = new ArrayList<BlockNode>(exitBlock.getPredecessors());
    425  - if (returnInsn.getArgsCount() != 0 && !isReturnArgAssignInPred(mth, preds, returnInsn)) {
     453 + if (returnInsn.getArgsCount() != 0 && !isReturnArgAssignInPred(preds, returnInsn)) {
    426 454   return false;
    427 455   }
    428  - split = true;
    429 456   for (BlockNode pred : preds) {
    430 457   BlockNode newRetBlock = startNewBlock(mth, exitBlock.getStartOffset());
    431 458   newRetBlock.getAttributes().add(AttributeFlag.SYNTHETIC);
    skipped 1 lines
    433 460   removeConnection(pred, exitBlock);
    434 461   connect(pred, newRetBlock);
    435 462   }
    436  - }
    437  - if (split) {
    438 463   cleanExitNodes(mth);
     464 + return true;
    439 465   }
    440  - return split;
     466 + return false;
    441 467   }
    442 468   
    443  - private static boolean isReturnArgAssignInPred(MethodNode mth, List<BlockNode> preds, InsnNode returnInsn) {
     469 + private static boolean isReturnArgAssignInPred(List<BlockNode> preds, InsnNode returnInsn) {
    444 470   RegisterArg arg = (RegisterArg) returnInsn.getArg(0);
    445 471   int regNum = arg.getRegNum();
    446 472   for (BlockNode pred : preds) {
    skipped 45 lines
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/dex/visitors/ConstInlinerVisitor.java
    skipped 45 lines
    46 46   long lit = ((LiteralArg) arg).getLiteral();
    47 47   return replaceConst(mth, block, insn, lit);
    48 48   }
    49  - // TODO process string and class const
    50 49   }
    51 50   return false;
    52 51   }
    53 52   
    54 53   private static boolean replaceConst(MethodNode mth, BlockNode block, InsnNode insn, long literal) {
    55 54   List<InsnArg> use = insn.getResult().getTypedVar().getUseList();
    56  - int replace = 0;
     55 + int replaceCount = 0;
    57 56   for (InsnArg arg : use) {
    58 57   InsnNode useInsn = arg.getParentInsn();
    59  - if (useInsn == null) {
     58 + if (arg == insn.getResult() || useInsn == null) {
    60 59   continue;
    61 60   }
    62 61   BlockNode useBlock = BlockUtils.getBlockByInsn(mth, useInsn);
    63  - if (useBlock == block || useBlock.isDominator(block)) {
    64  - if (arg != insn.getResult() && !registerReassignOnPath(block, useBlock, insn)) {
    65  - LiteralArg litArg;
    66  - if (use.size() == 2) {
    67  - // arg used only in one place
    68  - litArg = InsnArg.lit(literal, arg.getType());
    69  - } else {
    70  - // in most cases type not equal arg.getType()
    71  - // just set unknown type and run type fixer
    72  - litArg = InsnArg.lit(literal, ArgType.UNKNOWN);
    73  - }
    74  - if (useInsn.replaceArg(arg, litArg)) {
    75  - fixTypes(mth, useInsn, litArg);
    76  - replace++;
    77  - }
     62 + boolean isDominator = useBlock == block || useBlock.isDominator(block);
     63 + if (isDominator && !registerReassignOnPath(block, useBlock, insn)) {
     64 + LiteralArg litArg;
     65 + if (use.size() == 2) {
     66 + // arg used only in one place
     67 + litArg = InsnArg.lit(literal, arg.getType());
     68 + } else {
     69 + // in most cases type not equal arg.getType()
     70 + // just set unknown type and run type fixer
     71 + litArg = InsnArg.lit(literal, ArgType.UNKNOWN);
     72 + }
     73 + if (useInsn.replaceArg(arg, litArg)) {
     74 + fixTypes(mth, useInsn, litArg);
     75 + replaceCount++;
    78 76   }
    79 77   }
    80 78   }
    81  - return (replace + 1) == use.size();
     79 + return replaceCount == use.size() - 1;
    82 80   }
    83 81   
    84 82   private static boolean registerReassignOnPath(BlockNode block, BlockNode useBlock, InsnNode assignInsn) {
    skipped 1 lines
    86 84   return false;
    87 85   }
    88 86   Set<BlockNode> blocks = BlockUtils.getAllPathsBlocks(block, useBlock);
    89  - // TODO store list of assign insn for each register
    90 87   int regNum = assignInsn.getResult().getRegNum();
    91 88   for (BlockNode b : blocks) {
    92 89   for (InsnNode insn : b.getInstructions()) {
    skipped 99 lines
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java
    skipped 11 lines
    12 12  import jadx.core.dex.instructions.args.InsnArg;
    13 13  import jadx.core.dex.instructions.args.RegisterArg;
    14 14  import jadx.core.dex.nodes.BlockNode;
     15 +import jadx.core.dex.nodes.Edge;
    15 16  import jadx.core.dex.nodes.IRegion;
    16 17  import jadx.core.dex.nodes.InsnNode;
    17 18  import jadx.core.dex.nodes.MethodNode;
    skipped 125 lines
    143 144   
    144 145   private BlockNode processLoop(IRegion curRegion, LoopAttr loop, RegionStack stack) {
    145 146   BlockNode loopStart = loop.getStart();
    146  - IfNode ifnode = null;
    147  - LoopRegion loopRegion = null;
    148 147   Set<BlockNode> exitBlocksSet = loop.getExitNodes();
    149 148   
    150 149   // set exit blocks scan order by priority
    skipped 12 lines
    163 162   exitBlocks.addAll(exitBlocksSet);
    164 163   exitBlocksSet = null;
    165 164   
     165 + IfNode ifnode = null;
     166 + LoopRegion loopRegion = null;
     167 + 
    166 168   // exit block with loop condition
    167 169   BlockNode condBlock = null;
    168 170   
    skipped 5 lines
    174 176   || exit.getInstructions().size() != 1) {
    175 177   continue;
    176 178   }
    177  - 
    178 179   InsnNode insn = exit.getInstructions().get(0);
    179  - if (insn.getType() == InsnType.IF) {
    180  - boolean found = true;
    181  - ifnode = (IfNode) insn;
    182  - condBlock = exit;
    183  - loopRegion = new LoopRegion(curRegion, condBlock, condBlock == loop.getEnd());
     180 + if (insn.getType() != InsnType.IF) {
     181 + continue;
     182 + }
     183 + ifnode = (IfNode) insn;
     184 + condBlock = exit;
    184 185   
    185  - if (loopRegion.isConditionAtEnd()) {
    186  - // TODO: add some checks
     186 + loopRegion = new LoopRegion(curRegion, condBlock, condBlock == loop.getEnd());
     187 + boolean found = true;
     188 + if (condBlock != loopStart) {
     189 + if (condBlock.getPredecessors().contains(loopStart)) {
     190 + loopRegion.setPreCondition(loopStart);
     191 + // if we can't merge pre-condition this is not correct header
     192 + found = loopRegion.checkPreCondition();
    187 193   } else {
    188  - if (condBlock != loopStart) {
    189  - if (condBlock.getPredecessors().contains(loopStart)) {
    190  - loopRegion.setPreCondition(loopStart);
    191  - // if we can't merge pre-condition this is not correct header
    192  - found = loopRegion.checkPreCondition();
    193  - } else {
    194  - found = false;
    195  - }
    196  - }
     194 + found = false;
    197 195   }
    198  - if (!found) {
    199  - ifnode = null;
    200  - loopRegion = null;
    201  - condBlock = null;
    202  - // try another exit
     196 + }
     197 + if (!found) {
     198 + ifnode = null;
     199 + loopRegion = null;
     200 + condBlock = null;
     201 + // try another exit
     202 + continue;
     203 + }
     204 + 
     205 + List<BlockNode> merged = new ArrayList<BlockNode>(2);
     206 + IfInfo mergedIf = mergeNestedIfNodes(condBlock,
     207 + ifnode.getThenBlock(), ifnode.getElseBlock(), merged);
     208 + if (mergedIf != null) {
     209 + condBlock = mergedIf.getIfnode();
     210 + if (!loop.getLoopBlocks().contains(mergedIf.getThenBlock())) {
     211 + // invert loop condition if it points to exit
     212 + loopRegion.setCondition(IfCondition.invert(mergedIf.getCondition()));
     213 + bThen = mergedIf.getElseBlock();
    203 214   } else {
    204  - List<BlockNode> merged = new ArrayList<BlockNode>(2);
    205  - IfInfo mergedIf = mergeNestedIfNodes(condBlock,
    206  - ifnode.getThenBlock(), ifnode.getElseBlock(), merged);
    207  - if (mergedIf != null) {
    208  - condBlock = mergedIf.getIfnode();
    209  - if (!loop.getLoopBlocks().contains(mergedIf.getThenBlock())) {
    210  - // invert loop condition if it points to exit
    211  - loopRegion.setCondition(IfCondition.invert(mergedIf.getCondition()));
    212  - bThen = mergedIf.getElseBlock();
    213  - } else {
    214  - loopRegion.setCondition(mergedIf.getCondition());
    215  - bThen = mergedIf.getThenBlock();
    216  - }
    217  - exitBlocks.removeAll(merged);
    218  - }
    219  - break;
     215 + loopRegion.setCondition(mergedIf.getCondition());
     216 + bThen = mergedIf.getThenBlock();
    220 217   }
     218 + exitBlocks.removeAll(merged);
    221 219   }
     220 + break;
    222 221   }
    223 222   
    224 223   // endless loop
    225 224   if (loopRegion == null) {
    226  - loopRegion = new LoopRegion(curRegion, null, false);
    227  - curRegion.getSubBlocks().add(loopRegion);
     225 + return makeEndlessLoop(curRegion, stack, loop, loopStart);
     226 + }
    228 227   
    229  - loopStart.getAttributes().remove(AttributeType.LOOP);
    230  - stack.push(loopRegion);
    231  - Region body = makeRegion(loopStart, stack);
    232  - if (!RegionUtils.isRegionContainsBlock(body, loop.getEnd())) {
    233  - body.getSubBlocks().add(loop.getEnd());
    234  - }
    235  - loopRegion.setBody(body);
    236  - stack.pop();
    237  - loopStart.getAttributes().add(loop);
    238  - 
    239  - BlockNode next = BlockUtils.getNextBlock(loop.getEnd());
    240  - if (!RegionUtils.isRegionContainsBlock(body, next)) {
    241  - return next;
    242  - } else {
    243  - return null;
     228 + if (bThen == null) {
     229 + bThen = ifnode.getThenBlock();
     230 + }
     231 + BlockNode loopBody = null;
     232 + for (BlockNode s : condBlock.getSuccessors()) {
     233 + if (loop.getLoopBlocks().contains(s)) {
     234 + loopBody = s;
     235 + break;
    244 236   }
    245 237   }
    246 238   
    247 239   curRegion.getSubBlocks().add(loopRegion);
     240 + stack.push(loopRegion);
    248 241   
    249 242   exitBlocks.remove(condBlock);
    250 243   if (exitBlocks.size() > 0) {
    251  - // set BREAK or FORCE_RETURN attributes
    252  - // before path cross between main loop exit and subexit
    253  - BlockNode loopExit = BlockUtils.getNextBlock(condBlock);
    254  - for (BlockNode exit : exitBlocks) {
    255  - BlockNode next = BlockUtils.getNextBlock(exit);
    256  - while (next != null) {
    257  - if (isPathExists(loopExit, next)) {
    258  - // found cross
    259  - /*
    260  - if (next.getCleanSuccessors().size() == 1) {
    261  - BlockNode r = BlockUtils.getNextBlock(next);
    262  - if (r != null
    263  - && r.getAttributes().contains(AttributeFlag.RETURN)
    264  - && r.getInstructions().size() > 0
    265  - && r.getInstructions().get(0).getType() == InsnType.RETURN) {
    266  - next.getAttributes().add(new ForceReturnAttr(r.getInstructions().get(0)));
    267  - } else {
    268  - next.getAttributes().add(AttributeFlag.BREAK);
    269  - stack.addExit(r);
    270  - }
    271  - } else {
    272  - stack.addExit(next);
    273  - }
    274  - */
    275  - break;
    276  - }
    277  - next = BlockUtils.getNextBlock(next);
     244 + // add 'break' instruction before path cross between main loop exit and subexit
     245 + BlockNode loopExit = BlockUtils.selectOther(loopBody, condBlock.getCleanSuccessors());
     246 + for (Edge exitEdge : loop.getExitEdges()) {
     247 + if (!exitBlocks.contains(exitEdge.getSource())) {
     248 + continue;
    278 249   }
     250 + insertBreak(stack, loopExit, exitEdge);
    279 251   }
    280 252   }
    281 253   
    282  - if (bThen == null) {
    283  - bThen = ifnode.getThenBlock();
    284  - }
    285  - 
    286 254   BlockNode out;
    287 255   if (loopRegion.isConditionAtEnd()) {
    288 256   BlockNode bElse = ifnode.getElseBlock();
    skipped 1 lines
    290 258   
    291 259   loopStart.getAttributes().remove(AttributeType.LOOP);
    292 260   
    293  - stack.push(loopRegion);
    294 261   stack.addExit(loop.getEnd());
    295 262   loopRegion.setBody(makeRegion(loopStart, stack));
    296 263   loopStart.getAttributes().add(loop);
    297  - stack.pop();
    298 264   } else {
    299  - Set<BlockNode> loopBlocks = loop.getLoopBlocks();
    300  - BlockNode loopBody = null;
    301  - for (BlockNode s : condBlock.getSuccessors()) {
    302  - if (loopBlocks.contains(s)) {
    303  - loopBody = s;
    304  - break;
    305  - }
    306  - }
    307 265   if (bThen != loopBody) {
    308 266   loopRegion.setCondition(IfCondition.invert(loopRegion.getCondition()));
    309 267   }
    skipped 3 lines
    313 271   && outAttrs.get(AttributeType.LOOP) != loop
    314 272   && stack.peekRegion() instanceof LoopRegion) {
    315 273   LoopRegion outerLoop = (LoopRegion) stack.peekRegion();
    316  - if (outerLoop.getBody() == null /* processing not yet finished */
    317  - || RegionUtils.isRegionContainsBlock(outerLoop, out)) {
     274 + boolean notYetProcessed = outerLoop.getBody() == null;
     275 + if (notYetProcessed || RegionUtils.isRegionContainsBlock(outerLoop, out)) {
    318 276   // exit to outer loop which already processed
    319 277   out = null;
    320 278   }
    321 279   }
    322  - 
    323  - stack.push(loopRegion);
    324 280   stack.addExit(out);
    325 281   loopRegion.setBody(makeRegion(loopBody, stack));
    326  - stack.pop();
    327 282   }
     283 + stack.pop();
    328 284   return out;
    329 285   }
    330 286   
     287 + private BlockNode makeEndlessLoop(IRegion curRegion, RegionStack stack, LoopAttr loop, BlockNode loopStart) {
     288 + LoopRegion loopRegion;
     289 + loopRegion = new LoopRegion(curRegion, null, false);
     290 + curRegion.getSubBlocks().add(loopRegion);
     291 + 
     292 + loopStart.getAttributes().remove(AttributeType.LOOP);
     293 + stack.push(loopRegion);
     294 + Region body = makeRegion(loopStart, stack);
     295 + if (!RegionUtils.isRegionContainsBlock(body, loop.getEnd())) {
     296 + body.getSubBlocks().add(loop.getEnd());
     297 + }
     298 + loopRegion.setBody(body);
     299 + stack.pop();
     300 + loopStart.getAttributes().add(loop);
     301 + 
     302 + BlockNode next = BlockUtils.getNextBlock(loop.getEnd());
     303 + return RegionUtils.isRegionContainsBlock(body, next) ? null : next;
     304 + }
     305 + 
     306 + private void insertBreak(RegionStack stack, BlockNode loopExit, Edge exitEdge) {
     307 + BlockNode prev = null;
     308 + BlockNode exit = exitEdge.getTarget();
     309 + while (exit != null) {
     310 + if (prev != null && isPathExists(loopExit, exit)) {
     311 + // found cross
     312 + if (!exit.getAttributes().contains(AttributeFlag.RETURN)) {
     313 + prev.getInstructions().add(new InsnNode(InsnType.BREAK, 0));
     314 + stack.addExit(exit);
     315 + }
     316 + return;
     317 + }
     318 + prev = exit;
     319 + exit = BlockUtils.getNextBlock(exit);
     320 + }
     321 + }
     322 + 
    331 323   private final Set<BlockNode> cacheSet = new HashSet<BlockNode>();
    332 324   
    333 325   private BlockNode processMonitorEnter(IRegion curRegion, BlockNode block, InsnNode insn, RegionStack stack) {
    skipped 175 lines
    509 501   IfCondition nestedCondition = IfCondition.fromIfNode(nestedIfInsn);
    510 502   if (isPathExists(bElse, nestedIfBlock)) {
    511 503   // else branch
    512  - if (!isEqualReturns(bThen, nbThen)) {
    513  - if (!isEqualReturns(bThen, nbElse)) {
     504 + if (!isEqualPaths(bThen, nbThen)) {
     505 + if (!isEqualPaths(bThen, nbElse)) {
    514 506   // not connected conditions
    515 507   break;
    516 508   }
    skipped 3 lines
    520 512   condition = IfCondition.merge(Mode.OR, condition, nestedCondition);
    521 513   } else {
    522 514   // then branch
    523  - if (!isEqualReturns(bElse, nbElse)) {
    524  - if (!isEqualReturns(bElse, nbThen)) {
     515 + if (!isEqualPaths(bElse, nbElse)) {
     516 + if (!isEqualPaths(bElse, nbThen)) {
    525 517   // not connected conditions
    526 518   break;
    527 519   }
    skipped 5 lines
    533 525   result = new IfInfo();
    534 526   result.setCondition(condition);
    535 527   nestedIfBlock.getAttributes().add(AttributeFlag.SKIP);
    536  - bThen.getAttributes().add(AttributeFlag.SKIP);
     528 + skipSimplePath(bThen);
    537 529   
    538 530   if (merged != null) {
    539 531   merged.add(nestedIfBlock);
    skipped 162 lines
    702 694   handler.getHandlerRegion().getAttributes().add(excHandlerAttr);
    703 695   }
    704 696   
    705  - private static boolean isEqualReturns(BlockNode b1, BlockNode b2) {
     697 + private void skipSimplePath(BlockNode block) {
     698 + while (block != null
     699 + && block.getCleanSuccessors().size() < 2
     700 + && block.getPredecessors().size() == 1) {
     701 + block.getAttributes().add(AttributeFlag.SKIP);
     702 + block = BlockUtils.getNextBlock(block);
     703 + }
     704 + }
     705 + 
     706 + private static boolean isEqualPaths(BlockNode b1, BlockNode b2) {
    706 707   if (b1 == b2) {
    707 708   return true;
    708 709   }
    709 710   if (b1 == null || b2 == null) {
    710 711   return false;
    711 712   }
     713 + if (isReturnBlocks(b1, b2)) {
     714 + return true;
     715 + }
     716 + if (isSyntheticPath(b1, b2)) {
     717 + return true;
     718 + }
     719 + return false;
     720 + }
     721 + 
     722 + private static boolean isSyntheticPath(BlockNode b1, BlockNode b2) {
     723 + if (!b1.isSynthetic() || !b2.isSynthetic()) {
     724 + return false;
     725 + }
     726 + BlockNode n1 = BlockUtils.getNextBlock(b1);
     727 + BlockNode n2 = BlockUtils.getNextBlock(b2);
     728 + return isEqualPaths(n1, n2);
     729 + }
     730 + 
     731 + private static boolean isReturnBlocks(BlockNode b1, BlockNode b2) {
     732 + if (!b1.isReturnBlock() || !b2.isReturnBlock()) {
     733 + return false;
     734 + }
    712 735   List<InsnNode> b1Insns = b1.getInstructions();
    713 736   List<InsnNode> b2Insns = b2.getInstructions();
    714 737   if (b1Insns.size() != 1 || b2Insns.size() != 1) {
    715 738   return false;
    716 739   }
    717  - if (!b1.getAttributes().contains(AttributeFlag.RETURN)
    718  - || !b2.getAttributes().contains(AttributeFlag.RETURN)) {
     740 + InsnNode i1 = b1Insns.get(0);
     741 + InsnNode i2 = b2Insns.get(0);
     742 + if (i1.getArgsCount() == 0 || i2.getArgsCount() == 0) {
    719 743   return false;
    720 744   }
    721  - if (b1Insns.get(0).getArgsCount() > 0) {
    722  - if (b1Insns.get(0).getArg(0) != b2Insns.get(0).getArg(0)) {
    723  - return false;
    724  - }
    725  - }
    726  - return true;
     745 + return i1.getArg(0).equals(i2.getArg(0));
    727 746   }
    728 747  }
    729 748   
  • ■ ■ ■ ■ ■
    jadx-core/src/main/java/jadx/core/dex/visitors/typeresolver/TypeResolver.java
    skipped 9 lines
    10 10   
    11 11  import java.util.List;
    12 12   
     13 +import org.slf4j.Logger;
     14 +import org.slf4j.LoggerFactory;
     15 + 
    13 16  public class TypeResolver extends AbstractVisitor {
     17 + private static final Logger LOG = LoggerFactory.getLogger(TypeResolver.class);
    14 18   
    15 19   @Override
    16 20   public void visit(MethodNode mth) {
    skipped 50 lines
    67 71   }
    68 72   } while (changed);
    69 73   
    70  - for (BlockNode block : mth.getBasicBlocks()) {
    71  - for (BlockNode dest : block.getSuccessors()) {
    72  - connectEdges(mth, block, dest, false);
     74 + int i = 0;
     75 + do {
     76 + changed = false;
     77 + for (BlockNode block : mth.getBasicBlocks()) {
     78 + for (BlockNode dest : block.getSuccessors()) {
     79 + if (connectEdges(mth, block, dest, false)) {
     80 + changed = true;
     81 + }
     82 + }
     83 + }
     84 + i++;
     85 + if (i > 10) {
     86 + LOG.warn("Can't resolve types (forward connectEdges pass) in method: {}", mth);
     87 + break;
    73 88   }
    74  - }
     89 + } while (changed && i < 10);
    75 90   }
    76 91   
    77 92   private static boolean connectEdges(MethodNode mth, BlockNode from, BlockNode to, boolean back) {
    skipped 24 lines
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/test/java/jadx/tests/internal/TestReturnWrapping.java
    skipped 9 lines
    10 10   
    11 11  public class TestReturnWrapping extends InternalJadxTest {
    12 12   public static class TestCls {
    13  - /**/
     13 + 
    14 14   public static int f1(int arg0) {
    15 15   switch (arg0) {
    16 16   case 1:
    17 17   return 255;
    18 18   }
    19 19   return arg0 + 1;
    20  - }/**/
     20 + }
    21 21   
    22  - /**/
    23 22   public static Object f2(Object arg0, int arg1) {
    24 23   Object ret = null;
    25 24   int i = arg1;
    skipped 8 lines
    34 33   }
    35 34   return i > 128 ? arg0.toString() + ret.toString() : i;
    36 35   }
    37  - }/**/
     36 + }
    38 37   
    39  - /**/
    40 38   public static int f3(int arg0) {
    41 39   while (arg0 > 10) {
    42 40   int abc = 951;
    skipped 3 lines
    46 44   arg0 -= abc;
    47 45   }
    48 46   return arg0;
    49  - }/**/
     47 + }
    50 48   }
    51 49   
    52 50   @Test
    skipped 3 lines
    56 54   
    57 55   assertThat(code, containsString("return 255;"));
    58 56   assertThat(code, containsString("return arg0 + 1;"));
    59  - //assertThat(code, containsString("return Integer.toHexString(i);"));
    60 57   assertThat(code, containsString("return i > 128 ? arg0.toString() + ret.toString() : Integer.valueOf(i);"));
    61 58   assertThat(code, containsString("return arg0 + 2;"));
    62 59   assertThat(code, containsString("arg0 -= 951;"));
    skipped 3 lines
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/test/java/jadx/tests/internal/loops/TestBreakInLoop.java
     1 +package jadx.tests.internal.loops;
     2 + 
     3 +import jadx.api.InternalJadxTest;
     4 +import jadx.core.dex.nodes.ClassNode;
     5 + 
     6 +import org.junit.Test;
     7 + 
     8 +import static org.hamcrest.CoreMatchers.containsString;
     9 +import static org.junit.Assert.assertEquals;
     10 +import static org.junit.Assert.assertThat;
     11 + 
     12 +public class TestBreakInLoop extends InternalJadxTest {
     13 + 
     14 + public static class TestCls {
     15 + private int f;
     16 + 
     17 + private void test(int[] a, int b) {
     18 + int i = 0;
     19 + while (i < a.length) {
     20 + a[i]++;
     21 + if (i < b) {
     22 + break;
     23 + }
     24 + i++;
     25 + }
     26 + this.f++;
     27 + }
     28 + }
     29 + 
     30 + @Test
     31 + public void test() {
     32 + ClassNode cls = getClassNode(TestCls.class);
     33 + String code = cls.getCode().toString();
     34 + System.out.println(code);
     35 + 
     36 + assertEquals(count(code, "this.f++;"), 1);
     37 + assertThat(code, containsString("if (i < b) {"));
     38 + assertThat(code, containsString("break;"));
     39 + }
     40 +}
     41 + 
  • ■ ■ ■ ■ ■
    jadx-core/src/test/java/jadx/tests/internal/loops/TestLoopCondition.java
    skipped 9 lines
    10 10   
    11 11  public class TestLoopCondition extends InternalJadxTest {
    12 12   
    13  - @SuppressWarnings("serial")
    14  - public static class TestCls extends Exception {
     13 + public static class TestCls {
    15 14   public String f;
    16 15   
    17 16   private void setEnabled(boolean r1z) {
    skipped 14 lines
    32 31   setEnabled(false);
    33 32   }
    34 33   
    35  - public int testComplexIfInLoop(boolean a) {
    36  - int i = 0;
    37  - while (a && i < 10) {
    38  - i++;
    39  - }
    40  - return i;
    41  - }
    42  - 
    43 34   private void testMoreComplexIfInLoop(java.util.ArrayList<String> list) throws Exception {
    44 35   for (int i = 0; i != 16 && i < 255; i++) {
    45 36   list.set(i, "ABC");
    skipped 9 lines
    55 46   public void test() {
    56 47   ClassNode cls = getClassNode(TestCls.class);
    57 48   String code = cls.getCode().toString();
     49 + System.out.println(code);
    58 50   
    59 51   assertThat(code, containsString("i < this.f.length()"));
    60  - assertThat(code, containsString("while (a && i < 10) {"));
    61 52   assertThat(code, containsString("list.set(i, \"ABC\")"));
    62 53   assertThat(code, containsString("list.set(i, \"DEF\")"));
    63 54   }
    skipped 2 lines
  • ■ ■ ■ ■ ■ ■
    jadx-core/src/test/java/jadx/tests/internal/loops/TestLoopCondition2.java
     1 +package jadx.tests.internal.loops;
     2 + 
     3 +import jadx.api.InternalJadxTest;
     4 +import jadx.core.dex.nodes.ClassNode;
     5 + 
     6 +import org.junit.Test;
     7 + 
     8 +import static org.hamcrest.CoreMatchers.containsString;
     9 +import static org.junit.Assert.assertThat;
     10 + 
     11 +public class TestLoopCondition2 extends InternalJadxTest {
     12 + 
     13 + public static class TestCls {
     14 + 
     15 + public int test(boolean a) {
     16 + int i = 0;
     17 + while (a && i < 10) {
     18 + i++;
     19 + }
     20 + return i;
     21 + }
     22 + }
     23 + 
     24 + @Test
     25 + public void test() {
     26 + ClassNode cls = getClassNode(TestCls.class);
     27 + String code = cls.getCode().toString();
     28 + System.out.println(code);
     29 + 
     30 + assertThat(code, containsString("while (a && i < 10) {"));
     31 + }
     32 +}
     33 + 
  • ■ ■ ■ ■ ■ ■
    jadx-samples/src/main/java/jadx/samples/TestCF3.java
    skipped 221 lines
    222 222   new ArrayList<String>(Arrays.asList("a1", "b2"))));
    223 223   
    224 224   List<String> list1 = Arrays.asList(null, "a", "b");
    225  - 
    226  - // TODO this line required to omit generic information because it create List<Object>
    227  - // List<String> list2 = Arrays.asList(null, null, null);
    228  - 
    229 225   assertEquals(testReturnInLoop(list1), "a");
    230 226   assertEquals(testReturnInLoop2(list1), "a");
    231 227   
    232  - // assertEquals(testReturnInLoop(list2), "error");
    233  - // assertEquals(testReturnInLoop2(list2), "error");
     228 + // TODO this line required to omit generic information because it create List<Object>
     229 +// List<String> list2 = Arrays.asList(null, null, null);
     230 +// assertEquals(testReturnInLoop(list2), "error");
     231 +// assertEquals(testReturnInLoop2(list2), "error");
    234 232   
    235 233   // assertTrue(testLabeledBreakContinue());
    236 234   
    skipped 22 lines
Please wait...
Page is in error, reload to recover