From b940b99e7575f3883098cbb177548e70853b79c6 Mon Sep 17 00:00:00 2001 From: Skylot Date: Tue, 3 Jun 2014 23:08:40 +0400 Subject: [PATCH] core: fix issues in variable names and try/catch blocks --- .../java/jadx/core/codegen/MethodGen.java | 3 +- .../java/jadx/core/codegen/RegionGen.java | 4 +- .../java/jadx/core/dex/nodes/MethodNode.java | 2 +- .../core/dex/trycatch/ExceptionHandler.java | 10 +-- .../jadx/core/dex/trycatch/TryCatchBlock.java | 34 ++++---- .../core/dex/visitors/BlockMakerVisitor.java | 4 +- .../dex/visitors/BlockProcessingHelper.java | 2 +- .../regions/ProcessTryCatchRegions.java | 56 ++++++------- .../visitors/regions/ProcessVariables.java | 80 +++++++++++++++++-- .../dex/visitors/regions/RegionMaker.java | 51 ++++++++++-- .../visitors/regions/RegionMakerVisitor.java | 6 +- .../dex/visitors/regions/RegionStack.java | 7 ++ .../main/java/jadx/core/utils/BlockUtils.java | 2 +- .../java/jadx/core/utils/RegionUtils.java | 23 +++++- .../jadx/tests/internal/TestClassGen.java | 40 ++++++++++ .../jadx/tests/internal/TestWrongCode.java | 10 ++- .../internal/debuginfo/TestLineNumbers2.java | 2 +- .../internal/trycatch/TestTryCatch5.java | 56 +++++++++++++ .../internal/variables/TestVariables2.java | 32 ++++++++ .../internal/variables/TestVariables3.java | 38 +++++++++ .../internal/variables/TestVariables4.java | 71 ++++++++++++++++ .../TestVariablesDefinitions.java | 2 +- 22 files changed, 456 insertions(+), 79 deletions(-) create mode 100644 jadx-core/src/test/java/jadx/tests/internal/TestClassGen.java create mode 100644 jadx-core/src/test/java/jadx/tests/internal/trycatch/TestTryCatch5.java create mode 100644 jadx-core/src/test/java/jadx/tests/internal/variables/TestVariables2.java create mode 100644 jadx-core/src/test/java/jadx/tests/internal/variables/TestVariables3.java create mode 100644 jadx-core/src/test/java/jadx/tests/internal/variables/TestVariables4.java rename jadx-core/src/test/java/jadx/tests/internal/{ => variables}/TestVariablesDefinitions.java (96%) diff --git a/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java b/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java index 79d7fe4c1..7d5684dfd 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java @@ -77,9 +77,10 @@ public class MethodGen { AccessInfo clsAccFlags = mth.getParentClass().getAccessFlags(); AccessInfo ai = mth.getAccessFlags(); - // don't add 'abstract' to methods in interface + // don't add 'abstract' and 'public' to methods in interface if (clsAccFlags.isInterface()) { ai = ai.remove(AccessFlags.ACC_ABSTRACT); + ai = ai.remove(AccessFlags.ACC_PUBLIC); } // don't add 'public' for annotations if (clsAccFlags.isAnnotation()) { diff --git a/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java b/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java index ceaec06fa..dfa361abf 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java @@ -259,9 +259,9 @@ public class RegionGen extends InsnGen { if (allHandler != null) { makeCatchBlock(code, allHandler); } - if (tryCatchBlock.getFinalBlock() != null) { + if (tryCatchBlock.getFinalRegion() != null) { code.startLine("} finally {"); - makeRegionIndent(code, tryCatchBlock.getFinalBlock()); + makeRegionIndent(code, tryCatchBlock.getFinalRegion()); } code.startLine('}'); } diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java index 0ecb2912b..1df98a1a0 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java @@ -273,7 +273,7 @@ public class MethodNode extends LineAttrNode implements ILoadable { // each handler must be only in one try/catch block for (TryCatchBlock ct1 : catches) { for (TryCatchBlock ct2 : catches) { - if (ct1 != ct2 && ct2.getHandlers().containsAll(ct1.getHandlers())) { + if (ct1 != ct2 && ct2.containsAllHandlers(ct1)) { for (ExceptionHandler h : ct1.getHandlers()) { ct2.removeHandler(this, h); } diff --git a/jadx-core/src/main/java/jadx/core/dex/trycatch/ExceptionHandler.java b/jadx-core/src/main/java/jadx/core/dex/trycatch/ExceptionHandler.java index 0a1b0d92d..347c37f59 100644 --- a/jadx-core/src/main/java/jadx/core/dex/trycatch/ExceptionHandler.java +++ b/jadx-core/src/main/java/jadx/core/dex/trycatch/ExceptionHandler.java @@ -15,7 +15,7 @@ public class ExceptionHandler { private final ClassInfo catchType; private final int handleOffset; - private BlockNode handleBlock; + private BlockNode handlerBlock; private final List blocks = new ArrayList(); private IContainer handlerRegion; private NamedArg arg; @@ -39,12 +39,12 @@ public class ExceptionHandler { return handleOffset; } - public BlockNode getHandleBlock() { - return handleBlock; + public BlockNode getHandlerBlock() { + return handlerBlock; } - public void setHandleBlock(BlockNode handleBlock) { - this.handleBlock = handleBlock; + public void setHandlerBlock(BlockNode handlerBlock) { + this.handlerBlock = handlerBlock; } public List getBlocks() { diff --git a/jadx-core/src/main/java/jadx/core/dex/trycatch/TryCatchBlock.java b/jadx-core/src/main/java/jadx/core/dex/trycatch/TryCatchBlock.java index 00bdb5ec8..8eff2ef26 100644 --- a/jadx-core/src/main/java/jadx/core/dex/trycatch/TryCatchBlock.java +++ b/jadx-core/src/main/java/jadx/core/dex/trycatch/TryCatchBlock.java @@ -12,8 +12,6 @@ import jadx.core.utils.InstructionRemover; import jadx.core.utils.Utils; import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -21,7 +19,7 @@ import java.util.List; public class TryCatchBlock { private final List handlers; - private IContainer finalBlock; + private IContainer finalRegion; // references for fast remove/modify private final List insns; @@ -33,8 +31,16 @@ public class TryCatchBlock { attr = new CatchAttr(this); } - public Collection getHandlers() { - return Collections.unmodifiableCollection(handlers); + public Iterable getHandlers() { + return handlers; + } + + public int getHandlersCount() { + return handlers.size(); + } + + public boolean containsAllHandlers(TryCatchBlock tb) { + return handlers.containsAll(tb.handlers); } public ExceptionHandler addHandler(MethodNode mth, int addr, ClassInfo type) { @@ -59,7 +65,7 @@ public class TryCatchBlock { } private void removeWholeBlock(MethodNode mth) { - if (finalBlock != null) { + if (finalRegion != null) { // search catch attr for (BlockNode block : mth.getBasicBlocks()) { CatchAttr cb = block.get(AType.CATCH_BLOCK); @@ -67,7 +73,7 @@ public class TryCatchBlock { for (ExceptionHandler eh : mth.getExceptionHandlers()) { if (eh.getBlocks().contains(block)) { TryCatchBlock tb = eh.getTryBlock(); - tb.setFinalBlockFromInsns(mth, ((IBlock) finalBlock).getInstructions()); + tb.setFinalRegionFromInsns(mth, ((IBlock) finalRegion).getInstructions()); } } } @@ -102,17 +108,17 @@ public class TryCatchBlock { return attr; } - public IContainer getFinalBlock() { - return finalBlock; + public IContainer getFinalRegion() { + return finalRegion; } - public void setFinalBlock(IContainer finalBlock) { - this.finalBlock = finalBlock; + public void setFinalRegion(IContainer finalRegion) { + this.finalRegion = finalRegion; } - public void setFinalBlockFromInsns(MethodNode mth, List insns) { + public void setFinalRegionFromInsns(MethodNode mth, List insns) { List finalBlockInsns = new ArrayList(insns); - setFinalBlock(new InsnContainer(finalBlockInsns)); + setFinalRegion(new InsnContainer(finalBlockInsns)); InstructionRemover.unbindInsnList(mth, finalBlockInsns); @@ -135,7 +141,7 @@ public class TryCatchBlock { for (InsnNode insn : tryBlock.getInsns()) { this.addInsn(insn); } - this.handlers.addAll(tryBlock.getHandlers()); + this.handlers.addAll(tryBlock.handlers); for (ExceptionHandler eh : handlers) { eh.setTryBlock(this); } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/BlockMakerVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/BlockMakerVisitor.java index 466b897ae..ba44be1da 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/BlockMakerVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/BlockMakerVisitor.java @@ -119,8 +119,8 @@ public class BlockMakerVisitor extends AbstractVisitor { List jumps = insn.getAll(AType.JUMP); for (JumpInfo jump : jumps) { BlockNode srcBlock = getBlock(jump.getSrc(), blocksMap); - BlockNode thisblock = getBlock(jump.getDest(), blocksMap); - connect(srcBlock, thisblock); + BlockNode thisBlock = getBlock(jump.getDest(), blocksMap); + connect(srcBlock, thisBlock); } // connect exception handlers diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/BlockProcessingHelper.java b/jadx-core/src/main/java/jadx/core/dex/visitors/BlockProcessingHelper.java index ec6c771c3..7a8052ece 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/BlockProcessingHelper.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/BlockProcessingHelper.java @@ -137,7 +137,7 @@ public class BlockProcessingHelper { for (BlockNode block : mth.getBasicBlocks()) { ExcHandlerAttr bh = block.get(AType.EXC_HANDLER); if (bh != null && bh.getHandler().getHandleOffset() == addr) { - handler.setHandleBlock(block); + handler.setHandlerBlock(block); break; } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ProcessTryCatchRegions.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ProcessTryCatchRegions.java index 77e0bd98e..88b699909 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ProcessTryCatchRegions.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ProcessTryCatchRegions.java @@ -119,41 +119,43 @@ public class ProcessTryCatchRegions extends AbstractRegionVisitor { assert tb != null; for (IContainer cont : region.getSubBlocks()) { - if (RegionUtils.isDominaterBy(dominator, cont)) { - boolean pathFromExcHandler = false; - for (ExceptionHandler h : tb.getHandlers()) { - if (RegionUtils.hasPathThruBlock(h.getHandleBlock(), cont)) { - pathFromExcHandler = true; - break; - } - } - if (!pathFromExcHandler) { - newRegion.getSubBlocks().add(cont); - } else { + if (RegionUtils.isDominatedBy(dominator, cont)) { + if (isHandlerPath(tb, cont)) { break; } + newRegion.getSubBlocks().add(cont); } } - if (!newRegion.getSubBlocks().isEmpty()) { - if (DEBUG) { - LOG.debug("ProcessTryCatchRegions mark: {}", newRegion); - } - // replace first node by region - IContainer firstNode = newRegion.getSubBlocks().get(0); - int i = region.getSubBlocks().indexOf(firstNode); - region.getSubBlocks().set(i, newRegion); - region.getSubBlocks().removeAll(newRegion.getSubBlocks()); + if (newRegion.getSubBlocks().isEmpty()) { + return; + } + if (DEBUG) { + LOG.debug("ProcessTryCatchRegions mark: {}", newRegion); + } + // replace first node by region + IContainer firstNode = newRegion.getSubBlocks().get(0); + int i = region.getSubBlocks().indexOf(firstNode); + region.getSubBlocks().set(i, newRegion); + region.getSubBlocks().removeAll(newRegion.getSubBlocks()); - newRegion.addAttr(tb.getCatchAttr()); + newRegion.addAttr(tb.getCatchAttr()); - // fix parents - for (IContainer cont : newRegion.getSubBlocks()) { - if (cont instanceof AbstractRegion) { - AbstractRegion aReg = (AbstractRegion) cont; - aReg.setParent(newRegion); - } + // fix parents + for (IContainer cont : newRegion.getSubBlocks()) { + if (cont instanceof AbstractRegion) { + AbstractRegion aReg = (AbstractRegion) cont; + aReg.setParent(newRegion); } } } + private boolean isHandlerPath(TryCatchBlock tb, IContainer cont) { + for (ExceptionHandler h : tb.getHandlers()) { + if (RegionUtils.hasPathThruBlock(h.getHandlerBlock(), cont)) { + return true; + } + } + return false; + } + } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ProcessVariables.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ProcessVariables.java index 477f1e7f3..ae8c9f6f7 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ProcessVariables.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ProcessVariables.java @@ -10,11 +10,14 @@ import jadx.core.dex.nodes.IContainer; import jadx.core.dex.nodes.IRegion; import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; +import jadx.core.dex.regions.IfRegion; +import jadx.core.dex.regions.SwitchRegion; import jadx.core.dex.visitors.AbstractVisitor; import jadx.core.utils.RegionUtils; import jadx.core.utils.exceptions.JadxException; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; @@ -137,6 +140,13 @@ public class ProcessVariables extends AbstractVisitor { usageMap.remove(new Variable(arg)); } + if (usageMap.isEmpty()) { + return; + } + + Map regionsOrder = new HashMap(); + calculateOrder(mth.getRegion(), regionsOrder, 0, true); + for (Iterator> it = usageMap.entrySet().iterator(); it.hasNext(); ) { Entry entry = it.next(); Usage u = entry.getValue(); @@ -147,10 +157,10 @@ public class ProcessVariables extends AbstractVisitor { continue; } - // check if we can declare variable at current assigns + // check if variable can be declared at current assigns for (IRegion assignRegion : u.getAssigns()) { if (u.getArgRegion() == assignRegion - && canDeclareInRegion(u, assignRegion)) { + && canDeclareInRegion(u, assignRegion, regionsOrder)) { u.getArg().getParentInsn().add(AFlag.DECLARE_VAR); it.remove(); break; @@ -178,7 +188,7 @@ public class ProcessVariables extends AbstractVisitor { IRegion parent = region; boolean declare = false; while (parent != null) { - if (canDeclareInRegion(u, region)) { + if (canDeclareInRegion(u, region, regionsOrder)) { declareVar(region, u.getArg()); declare = true; break; @@ -212,13 +222,67 @@ public class ProcessVariables extends AbstractVisitor { dv.addVar(arg); } - private static boolean canDeclareInRegion(Usage u, IRegion region) { - for (IRegion r : u.getAssigns()) { - if (!RegionUtils.isRegionContainsRegion(region, r)) { - return false; + private static int calculateOrder(IContainer container, Map regionsOrder, + int id, boolean inc) { + if (!(container instanceof IRegion)) { + return id; + } + IRegion region = (IRegion) container; + Integer previous = regionsOrder.put(region, id); + if (previous != null) { + return id; + } + for (IContainer c : region.getSubBlocks()) { + if (c instanceof IfRegion + || c instanceof SwitchRegion) { + // on branch set for all inner regions same order id + id = calculateOrder(c, regionsOrder, inc ? id + 1 : id, false); + } else { + List handlers = RegionUtils.getExcHandlersForRegion(c); + if (!handlers.isEmpty()) { + for (IContainer handler : handlers) { + id = calculateOrder(handler, regionsOrder, inc ? id + 1 : id, inc); + } + } + id = calculateOrder(c, regionsOrder, inc ? id + 1 : id, inc); } } - for (IRegion r : u.getUseRegions()) { + return id; + } + + private static boolean canDeclareInRegion(Usage u, IRegion region, Map regionsOrder) { + Integer pos = regionsOrder.get(region); + if (pos == null) { + LOG.debug("TODO: Not found order for region {} for {}", region, u); + return false; + } + return isAllRegionsAfter(region, pos, u.getAssigns(), regionsOrder) + && isAllRegionsAfter(region, pos, u.getUseRegions(), regionsOrder); + } + + private static boolean isAllRegionsAfter(IRegion region, Integer pos, + Set regions, Map regionsOrder) { + for (IRegion r : regions) { + if (r == region) { + continue; + } + Integer rPos = regionsOrder.get(r); + if (rPos == null) { + LOG.debug("TODO: Not found order for region {} in {}", r, regionsOrder); + return false; + } + if (pos > rPos) { + return false; + } + if (pos.equals(rPos)) { + return isAllRegionsAfterRecursive(region, regions); + } + } + return true; + } + + private static boolean isAllRegionsAfterRecursive(IRegion region, Set others) { + for (IRegion r : others) { if (!RegionUtils.isRegionContainsRegion(region, r)) { return false; } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java index d797cc25f..390e7aa98 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMaker.java @@ -23,6 +23,7 @@ import jadx.core.dex.regions.SwitchRegion; import jadx.core.dex.regions.SynchronizedRegion; import jadx.core.dex.trycatch.ExcHandlerAttr; import jadx.core.dex.trycatch.ExceptionHandler; +import jadx.core.dex.trycatch.TryCatchBlock; import jadx.core.utils.BlockUtils; import jadx.core.utils.ErrorsCounter; import jadx.core.utils.InstructionRemover; @@ -410,7 +411,7 @@ public class RegionMaker { ifRegion.setCondition(mergedIf.getCondition()); thenBlock = mergedIf.getThenBlock(); elseBlock = mergedIf.getElseBlock(); - out = BlockUtils.getPathCrossBlockFor(mth, thenBlock, elseBlock); + out = BlockUtils.getPathCross(mth, thenBlock, elseBlock); } else { // invert condition (compiler often do it) ifnode.invertCondition(); @@ -432,7 +433,7 @@ public class RegionMaker { } else if (block.getDominatesOn().size() == 2) { thenBlock = bThen; elseBlock = bElse; - out = BlockUtils.getPathCrossBlockFor(mth, bThen, bElse); + out = BlockUtils.getPathCross(mth, bThen, bElse); } else if (bElse.getPredecessors().size() != 1) { thenBlock = bThen; elseBlock = null; @@ -679,14 +680,54 @@ public class RegionMaker { return out; } - public void processExcHandler(ExceptionHandler handler, RegionStack stack) { - BlockNode start = handler.getHandleBlock(); + public void processTryCatchBlocks(MethodNode mth) { + Set tcs = new HashSet(); + for (ExceptionHandler handler : mth.getExceptionHandlers()) { + tcs.add(handler.getTryBlock()); + } + for (TryCatchBlock tc : tcs) { + List blocks = new ArrayList(tc.getHandlersCount()); + Set splitters = new HashSet(); + for (ExceptionHandler handler : tc.getHandlers()) { + BlockNode handlerBlock = handler.getHandlerBlock(); + if (handlerBlock != null) { + blocks.add(handlerBlock); + splitters.addAll(handlerBlock.getPredecessors()); + } else { + LOG.debug(ErrorsCounter.formatErrorMsg(mth, "No exception handler block: " + handler)); + } + } + Set exits = new HashSet(); + for (BlockNode splitter : splitters) { + for (BlockNode handler : blocks) { + List s = splitter.getCleanSuccessors(); + if (s.isEmpty()) { + LOG.debug(ErrorsCounter.formatErrorMsg(mth, "No successors for splitter: " + splitter)); + continue; + } + BlockNode cross = BlockUtils.getPathCross(mth, s.get(0), handler); + if (cross != null) { + exits.add(cross); + } + } + } + for (ExceptionHandler handler : tc.getHandlers()) { + processExcHandler(handler, exits); + } + } + } + + private void processExcHandler(ExceptionHandler handler, Set exits) { + BlockNode start = handler.getHandlerBlock(); if (start == null) { - LOG.debug(ErrorsCounter.formatErrorMsg(mth, "No exception handler block: " + handler)); return; } + // TODO extract finally part which exists in all handlers from same try block // TODO add blocks common for several handlers to some region + + RegionStack stack = new RegionStack(mth); + stack.addExits(exits); handler.setHandlerRegion(makeRegion(start, stack)); ExcHandlerAttr excHandlerAttr = start.get(AType.EXC_HANDLER); diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMakerVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMakerVisitor.java index 74c8faa23..f64e1fdbf 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMakerVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionMakerVisitor.java @@ -7,7 +7,6 @@ import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.regions.LoopRegion; import jadx.core.dex.regions.Region; import jadx.core.dex.regions.SynchronizedRegion; -import jadx.core.dex.trycatch.ExceptionHandler; import jadx.core.dex.visitors.AbstractVisitor; import jadx.core.utils.InstructionRemover; import jadx.core.utils.exceptions.JadxException; @@ -35,10 +34,7 @@ public class RegionMakerVisitor extends AbstractVisitor { mth.setRegion(rm.makeRegion(mth.getEnterBlock(), state)); if (!mth.isNoExceptionHandlers()) { - state = new RegionStack(mth); - for (ExceptionHandler handler : mth.getExceptionHandlers()) { - rm.processExcHandler(handler, state); - } + rm.processTryCatchBlocks(mth); } postProcessRegions(mth); diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionStack.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionStack.java index 79a2bbf44..c6f47266f 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionStack.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/RegionStack.java @@ -5,6 +5,7 @@ import jadx.core.dex.nodes.IRegion; import jadx.core.dex.nodes.MethodNode; import java.util.ArrayDeque; +import java.util.Collection; import java.util.Deque; import java.util.HashSet; import java.util.Set; @@ -85,6 +86,12 @@ final class RegionStack { } } + public void addExits(Collection exits) { + for (BlockNode exit : exits) { + addExit(exit); + } + } + public boolean containsExit(BlockNode exit) { return curState.exits.contains(exit); } diff --git a/jadx-core/src/main/java/jadx/core/utils/BlockUtils.java b/jadx-core/src/main/java/jadx/core/utils/BlockUtils.java index ba01e22f0..9219ae41d 100644 --- a/jadx-core/src/main/java/jadx/core/utils/BlockUtils.java +++ b/jadx-core/src/main/java/jadx/core/utils/BlockUtils.java @@ -245,7 +245,7 @@ public class BlockUtils { return null; } - public static BlockNode getPathCrossBlockFor(MethodNode mth, BlockNode b1, BlockNode b2) { + public static BlockNode getPathCross(MethodNode mth, BlockNode b1, BlockNode b2) { if (b1 == null || b2 == null) { return null; } diff --git a/jadx-core/src/main/java/jadx/core/utils/RegionUtils.java b/jadx-core/src/main/java/jadx/core/utils/RegionUtils.java index d833bdb41..737ad5e55 100644 --- a/jadx-core/src/main/java/jadx/core/utils/RegionUtils.java +++ b/jadx-core/src/main/java/jadx/core/utils/RegionUtils.java @@ -10,6 +10,8 @@ import jadx.core.dex.trycatch.ExceptionHandler; import jadx.core.dex.trycatch.TryCatchBlock; import jadx.core.utils.exceptions.JadxRuntimeException; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Set; @@ -111,6 +113,19 @@ public class RegionUtils { } } + public static List getExcHandlersForRegion(IContainer region) { + CatchAttr cb = region.get(AType.CATCH_BLOCK); + if (cb != null) { + TryCatchBlock tb = cb.getTryBlock(); + List list = new ArrayList(tb.getHandlersCount()); + for (ExceptionHandler eh : tb.getHandlers()) { + list.add(eh.getHandlerRegion()); + } + return list; + } + return Collections.emptyList(); + } + private static boolean isRegionContainsExcHandlerRegion(IContainer container, IRegion region) { if (container == region) { return true; @@ -129,8 +144,8 @@ public class RegionUtils { return true; } } - if (tb.getFinalBlock() != null - && isRegionContainsRegion(tb.getFinalBlock(), region)) { + if (tb.getFinalRegion() != null + && isRegionContainsRegion(tb.getFinalRegion(), region)) { return true; } } @@ -169,7 +184,7 @@ public class RegionUtils { return true; } - public static boolean isDominaterBy(BlockNode dom, IContainer cont) { + public static boolean isDominatedBy(BlockNode dom, IContainer cont) { if (dom == cont) { return true; } @@ -179,7 +194,7 @@ public class RegionUtils { } else if (cont instanceof IRegion) { IRegion region = (IRegion) cont; for (IContainer c : region.getSubBlocks()) { - if (!isDominaterBy(dom, c)) { + if (!isDominatedBy(dom, c)) { return false; } } diff --git a/jadx-core/src/test/java/jadx/tests/internal/TestClassGen.java b/jadx-core/src/test/java/jadx/tests/internal/TestClassGen.java new file mode 100644 index 000000000..8f9d2ff26 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/internal/TestClassGen.java @@ -0,0 +1,40 @@ +package jadx.tests.internal; + +import jadx.api.InternalJadxTest; +import jadx.core.dex.nodes.ClassNode; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertThat; + +public class TestClassGen extends InternalJadxTest { + + public static class TestCls { + public static interface I { + int test(); + + public int test3(); + } + + public static abstract class A { + public abstract int test2(); + } + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + System.out.println(code); + + assertThat(code, containsString("public static interface I {")); + assertThat(code, containsString(indent(2) + "int test();")); + assertThat(code, not(containsString("public int test();"))); + assertThat(code, containsString(indent(2) + "int test3();")); + + assertThat(code, containsString("public static abstract class A {")); + assertThat(code, containsString(indent(2) + "public abstract int test2();")); + } +} diff --git a/jadx-core/src/test/java/jadx/tests/internal/TestWrongCode.java b/jadx-core/src/test/java/jadx/tests/internal/TestWrongCode.java index cdabb953d..62be68397 100644 --- a/jadx-core/src/test/java/jadx/tests/internal/TestWrongCode.java +++ b/jadx-core/src/test/java/jadx/tests/internal/TestWrongCode.java @@ -12,10 +12,16 @@ import static org.junit.Assert.assertThat; public class TestWrongCode extends InternalJadxTest { public static class TestCls { - private int f() { + private int test() { int[] a = null; return a.length; } + + @SuppressWarnings("empty") + private int test2(int a) { + if (a == 0); + return a; + } } @Test @@ -26,5 +32,7 @@ public class TestWrongCode extends InternalJadxTest { assertThat(code, not(containsString("return false.length;"))); assertThat(code, containsString("return null.length;")); + + assertThat(code, containsString("return a == 0 ? a : a;")); } } diff --git a/jadx-core/src/test/java/jadx/tests/internal/debuginfo/TestLineNumbers2.java b/jadx-core/src/test/java/jadx/tests/internal/debuginfo/TestLineNumbers2.java index 234b241d5..16b41d272 100644 --- a/jadx-core/src/test/java/jadx/tests/internal/debuginfo/TestLineNumbers2.java +++ b/jadx-core/src/test/java/jadx/tests/internal/debuginfo/TestLineNumbers2.java @@ -41,7 +41,7 @@ public class TestLineNumbers2 extends InternalJadxTest { System.out.println(code); Map lineMapping = codeWriter.getLineMapping(); - assertEquals("{8=18, 11=22, 13=23, 14=24, 15=28, 17=25, 18=26, 19=28, 22=31, 23=32}", + assertEquals("{8=18, 11=22, 12=23, 13=24, 14=28, 16=25, 17=26, 18=28, 21=31, 22=32}", lineMapping.toString()); } } diff --git a/jadx-core/src/test/java/jadx/tests/internal/trycatch/TestTryCatch5.java b/jadx-core/src/test/java/jadx/tests/internal/trycatch/TestTryCatch5.java new file mode 100644 index 000000000..861c3d9cf --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/internal/trycatch/TestTryCatch5.java @@ -0,0 +1,56 @@ +package jadx.tests.internal.trycatch; + +import jadx.api.InternalJadxTest; +import jadx.core.dex.nodes.ClassNode; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; + +public class TestTryCatch5 extends InternalJadxTest { + + public static class TestCls { + private Object test(Object obj) { + File file = new File("r"); + FileOutputStream output = null; + try { + output = new FileOutputStream(file); + if (obj.equals("a")) { + return new Object(); + } else { + return null; + } + } catch (IOException e) { + System.out.println("Exception"); + return null; + } finally { + if (output != null) { + try { + output.close(); + } catch (IOException e) { + // Ignored + } + } + file.delete(); + } + } + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + System.out.println(code); + + assertThat(code, containsString("try {")); + // TODO: +// assertThat(code, containsString("output = new FileOutputStream(file);")); +// assertThat(code, containsString("} catch (IOException e) {")); + assertThat(code, containsString("file.delete();")); + } +} diff --git a/jadx-core/src/test/java/jadx/tests/internal/variables/TestVariables2.java b/jadx-core/src/test/java/jadx/tests/internal/variables/TestVariables2.java new file mode 100644 index 000000000..1140f014e --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/internal/variables/TestVariables2.java @@ -0,0 +1,32 @@ +package jadx.tests.internal.variables; + +import jadx.api.InternalJadxTest; +import jadx.core.dex.nodes.ClassNode; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; + +public class TestVariables2 extends InternalJadxTest { + + public static class TestCls { + Object test(Object s) { + Object store = s != null ? s : null; + if (store == null) { + store = new Object(); + s = store; + } + return store; + } + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + System.out.println(code); + + assertThat(code, containsString("Object store = s != null ? s : null;")); + } +} diff --git a/jadx-core/src/test/java/jadx/tests/internal/variables/TestVariables3.java b/jadx-core/src/test/java/jadx/tests/internal/variables/TestVariables3.java new file mode 100644 index 000000000..3ccb9ca40 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/internal/variables/TestVariables3.java @@ -0,0 +1,38 @@ +package jadx.tests.internal.variables; + +import jadx.api.InternalJadxTest; +import jadx.core.dex.nodes.ClassNode; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; + +public class TestVariables3 extends InternalJadxTest { + + public static class TestCls { + String test(Object s) { + int i; + if (s == null) { + i = 2; + } else { + i = 3; + s = null; + } + return s + " " + i; + } + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + System.out.println(code); + + assertThat(code, containsString("int i;")); + assertThat(code, containsString("i = 2;")); + assertThat(code, containsString("i = 3;")); + assertThat(code, containsString("s = null;")); + assertThat(code, containsString("return s + \" \" + i;")); + } +} diff --git a/jadx-core/src/test/java/jadx/tests/internal/variables/TestVariables4.java b/jadx-core/src/test/java/jadx/tests/internal/variables/TestVariables4.java new file mode 100644 index 000000000..387f1fea0 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/internal/variables/TestVariables4.java @@ -0,0 +1,71 @@ +package jadx.tests.internal.variables; + +import jadx.api.InternalJadxTest; +import jadx.core.dex.nodes.ClassNode; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; + +public class TestVariables4 extends InternalJadxTest { + + public static class TestCls { + private static boolean runTest(String clsName) { + try { + boolean pass = false; + String msg = null; + Throwable exc = null; + + Class cls = Class.forName(clsName); + if (cls.getSuperclass() == AbstractTest.class) { + Method mth = cls.getMethod("testRun"); + try { + AbstractTest test = (AbstractTest) cls.getConstructor().newInstance(); + pass = (Boolean) mth.invoke(test); + } catch (InvocationTargetException e) { + pass = false; + exc = e.getCause(); + } catch (Throwable e) { + pass = false; + exc = e; + } + } else { + msg = "not extends AbstractTest"; + } + System.err.println(">> " + + (pass ? "PASS" : "FAIL") + "\t" + + clsName + + (msg == null ? "" : "\t - " + msg)); + if (exc != null) { + exc.printStackTrace(); + } + return pass; + } catch (ClassNotFoundException e) { + System.err.println("Class '" + clsName + "' not found"); + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } + + private static class AbstractTest { + } + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + System.out.println(code); + + assertThat(code, containsString("} catch (InvocationTargetException e) {")); + assertThat(code, containsString("pass = false;")); + assertThat(code, containsString("exc = e.getCause();")); + assertThat(code, containsString("System.err.println(\"Class '\" + clsName + \"' not found\");")); + assertThat(code, containsString("return pass;")); + } +} diff --git a/jadx-core/src/test/java/jadx/tests/internal/TestVariablesDefinitions.java b/jadx-core/src/test/java/jadx/tests/internal/variables/TestVariablesDefinitions.java similarity index 96% rename from jadx-core/src/test/java/jadx/tests/internal/TestVariablesDefinitions.java rename to jadx-core/src/test/java/jadx/tests/internal/variables/TestVariablesDefinitions.java index bcbec24eb..eecd2aa2c 100644 --- a/jadx-core/src/test/java/jadx/tests/internal/TestVariablesDefinitions.java +++ b/jadx-core/src/test/java/jadx/tests/internal/variables/TestVariablesDefinitions.java @@ -1,4 +1,4 @@ -package jadx.tests.internal; +package jadx.tests.internal.variables; import jadx.api.InternalJadxTest; import jadx.core.dex.nodes.ClassNode;