| 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 | | |