diff --git a/jadx-core/src/main/java/jadx/core/dex/regions/SwitchRegion.java b/jadx-core/src/main/java/jadx/core/dex/regions/SwitchRegion.java index b481b4ddc..171d93247 100644 --- a/jadx-core/src/main/java/jadx/core/dex/regions/SwitchRegion.java +++ b/jadx-core/src/main/java/jadx/core/dex/regions/SwitchRegion.java @@ -74,7 +74,9 @@ public final class SwitchRegion extends AbstractRegion implements IBranchRegion public List getSubBlocks() { List all = new ArrayList<>(cases.size() + 1); all.add(header); - all.addAll(getCaseContainers()); + for (CaseInfo caseInfo : cases) { + all.add(caseInfo.container); + } return Collections.unmodifiableList(all); } 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 e0edcb490..bb8a277be 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 @@ -16,7 +16,7 @@ import jadx.core.dex.info.ClassInfo; import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.instructions.args.InsnArg; import jadx.core.dex.nodes.BlockNode; -import jadx.core.dex.nodes.IContainer; +import jadx.core.dex.nodes.IRegion; import jadx.core.dex.nodes.MethodNode; import jadx.core.utils.InsnUtils; import jadx.core.utils.Utils; @@ -30,7 +30,7 @@ public class ExceptionHandler { private BlockNode handlerBlock; private final List blocks = new ArrayList<>(); - private IContainer handlerRegion; + private IRegion handlerRegion; private InsnArg arg; private TryCatchBlockAttr tryBlock; @@ -122,11 +122,11 @@ public class ExceptionHandler { blocks.add(node); } - public IContainer getHandlerRegion() { + public IRegion getHandlerRegion() { return handlerRegion; } - public void setHandlerRegion(IContainer handlerRegion) { + public void setHandlerRegion(IRegion handlerRegion) { this.handlerRegion = handlerRegion; } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/DepthRegionTraversal.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/DepthRegionTraversal.java index 2ec7d52db..e5026a0e7 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/DepthRegionTraversal.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/DepthRegionTraversal.java @@ -1,11 +1,19 @@ package jadx.core.dex.visitors.regions; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import org.jetbrains.annotations.Nullable; + import jadx.core.dex.nodes.IBlock; import jadx.core.dex.nodes.IContainer; import jadx.core.dex.nodes.IRegion; +import jadx.core.dex.nodes.InsnContainer; import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.trycatch.ExceptionHandler; -import jadx.core.utils.exceptions.JadxOverflowException; +import jadx.core.utils.ListUtils; import jadx.core.utils.exceptions.JadxRuntimeException; public class DepthRegionTraversal { @@ -59,34 +67,61 @@ public class DepthRegionTraversal { } while (repeat); } - private static void traverseInternal(MethodNode mth, IRegionVisitor visitor, IContainer container) { - if (container instanceof IBlock) { - visitor.processBlock(mth, (IBlock) container); - } else if (container instanceof IRegion) { - IRegion region = (IRegion) container; - if (visitor.enterRegion(mth, region)) { - region.getSubBlocks().forEach(subCont -> traverseInternal(mth, visitor, subCont)); - } - visitor.leaveRegion(mth, region); - } - } + private static final IContainer LEAVE_REGION_MARK = new InsnContainer(Collections.emptyList()); - private static boolean traverseIterativeStepInternal(MethodNode mth, IRegionIterativeVisitor visitor, IContainer container) { - if (container instanceof IRegion) { - IRegion region = (IRegion) container; - if (visitor.visitRegion(mth, region)) { - return true; + private static void traverseInternal(MethodNode mth, IRegionVisitor visitor, IContainer startContainer) { + List stack = new ArrayList<>(); + List regionLeaveStack = new ArrayList<>(); + stack.add(startContainer); + while (true) { + IContainer current = ListUtils.removeLast(stack); + if (current == null) { + return; } - for (IContainer subCont : region.getSubBlocks()) { - try { - if (traverseIterativeStepInternal(mth, visitor, subCont)) { - return true; - } - } catch (StackOverflowError overflow) { - throw new JadxOverflowException("Region traversal failed: Recursive call in traverseIterativeStepInternal method"); + if (current == LEAVE_REGION_MARK) { + IRegion region = ListUtils.removeLast(regionLeaveStack); + visitor.leaveRegion(mth, Objects.requireNonNull(region)); + } else if (current instanceof IBlock) { + visitor.processBlock(mth, (IBlock) current); + } else if (current instanceof IRegion) { + IRegion region = (IRegion) current; + boolean visitRegion = visitor.enterRegion(mth, region); + stack.add(LEAVE_REGION_MARK); + regionLeaveStack.add(region); + if (visitRegion) { + addSubBlocksToStack(stack, region); + } + } + } + } + + private static void addSubBlocksToStack(List stack, IRegion region) { + List subBlocks = region.getSubBlocks(); + // add in reverse order to keep original order during visit + for (int i = subBlocks.size() - 1; i >= 0; i--) { + stack.add(subBlocks.get(i)); + } + } + + private static boolean traverseIterativeStepInternal(MethodNode mth, IRegionIterativeVisitor visitor, IRegion startRegion) { + List stack = new ArrayList<>(); + stack.add(startRegion); + while (true) { + IRegion region = ListUtils.removeLast(stack); + if (region == null) { + return false; + } + if (visitor.visitRegion(mth, region)) { + return true; + } + List subBlocks = region.getSubBlocks(); + // add in reverse order to keep original order during visit + for (int i = subBlocks.size() - 1; i >= 0; i--) { + IContainer subBlock = subBlocks.get(i); + if (subBlock instanceof IRegion) { + stack.add((IRegion) subBlock); } } } - return false; } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ReturnVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ReturnVisitor.java index a61807727..a2ef33417 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ReturnVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/ReturnVisitor.java @@ -61,7 +61,7 @@ public class ReturnVisitor extends AbstractVisitor { if (mth.getLoopForBlock(block) != null) { return false; } - for (IRegion region : regionStack) { + for (IRegion region : getRegionStack()) { if (region.getClass() == LoopRegion.class) { return false; } @@ -74,7 +74,7 @@ public class ReturnVisitor extends AbstractVisitor { */ private boolean noTrailInstructions(BlockNode block) { IContainer curContainer = block; - for (IRegion region : regionStack) { + for (IRegion region : getRegionStack()) { // ignore paths on other branches if (region instanceof IBranchRegion) { curContainer = region; diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/TracedRegionVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/TracedRegionVisitor.java index 3705082c3..8acae577f 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/TracedRegionVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/TracedRegionVisitor.java @@ -2,14 +2,14 @@ package jadx.core.dex.visitors.regions; import java.util.ArrayDeque; import java.util.Deque; +import java.util.Objects; import jadx.core.dex.nodes.IBlock; import jadx.core.dex.nodes.IRegion; import jadx.core.dex.nodes.MethodNode; public abstract class TracedRegionVisitor implements IRegionVisitor { - - protected final Deque regionStack = new ArrayDeque<>(); + private final Deque regionStack = new ArrayDeque<>(); @Override public boolean enterRegion(MethodNode mth, IRegion region) { @@ -19,7 +19,7 @@ public abstract class TracedRegionVisitor implements IRegionVisitor { @Override public void processBlock(MethodNode mth, IBlock block) { - IRegion curRegion = regionStack.peek(); + IRegion curRegion = Objects.requireNonNull(regionStack.peek()); processBlockTraced(mth, block, curRegion); } @@ -29,4 +29,8 @@ public abstract class TracedRegionVisitor implements IRegionVisitor { public void leaveRegion(MethodNode mth, IRegion region) { regionStack.pop(); } + + public Deque getRegionStack() { + return regionStack; + } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeUpdateInfo.java b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeUpdateInfo.java index 9d7958157..133ebc747 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeUpdateInfo.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/typeinference/TypeUpdateInfo.java @@ -43,11 +43,11 @@ public class TypeUpdateInfo { } public @Nullable TypeUpdateRequest pollNextRequest() { - return ListUtils.pollLast(queue); + return ListUtils.removeLast(queue); } public @Nullable TypeUpdateRequest pollNextCallback() { - return ListUtils.pollLast(callbackQueue); + return ListUtils.removeLast(callbackQueue); } public void requestUpdate(InsnArg arg, ArgType changeType) { diff --git a/jadx-core/src/main/java/jadx/core/utils/ListUtils.java b/jadx-core/src/main/java/jadx/core/utils/ListUtils.java index 5d94ce94c..be346a42f 100644 --- a/jadx-core/src/main/java/jadx/core/utils/ListUtils.java +++ b/jadx-core/src/main/java/jadx/core/utils/ListUtils.java @@ -67,6 +67,13 @@ public class ListUtils { return list.get(0); } + public static T firstOrNull(List list) { + if (list == null || list.isEmpty()) { + return null; + } + return list.get(0); + } + public static @Nullable T last(List list) { if (list == null || list.isEmpty()) { return null; @@ -74,7 +81,10 @@ public class ListUtils { return list.get(list.size() - 1); } - public static @Nullable T removeLast(List list) { + public static @Nullable T removeLast(@Nullable List list) { + if (list == null) { + return null; + } int size = list.size(); if (size == 0) { return null; @@ -227,15 +237,4 @@ public class ListUtils { } return list; } - - public static @Nullable T pollLast(List list) { - if (list == null) { - return null; - } - int size = list.size(); - if (size == 0) { - return null; - } - return list.remove(size - 1); - } }