chore: apply common code style

This commit is contained in:
Skylot
2026-03-22 20:48:27 +00:00
parent 4051a50146
commit 14a7b63707
52 changed files with 806 additions and 1151 deletions
+2
View File
@@ -54,4 +54,6 @@ tasks.named<Test>("test") {
// exclude temp tests // exclude temp tests
exclude("**/tmp/*") exclude("**/tmp/*")
// maxHeapSize = "4g"
} }
@@ -498,7 +498,7 @@ public class JadxArgs implements Closeable {
*/ */
@Deprecated @Deprecated
public void setUseSourceNameAsClassAlias(boolean useSourceNameAsClassAlias) { public void setUseSourceNameAsClassAlias(boolean useSourceNameAsClassAlias) {
final var useSourceNameAsClassNameAlias = UseSourceNameAsClassNameAlias.create(useSourceNameAsClassAlias); var useSourceNameAsClassNameAlias = UseSourceNameAsClassNameAlias.create(useSourceNameAsClassAlias);
setUseSourceNameAsClassNameAlias(useSourceNameAsClassNameAlias); setUseSourceNameAsClassNameAlias(useSourceNameAsClassNameAlias);
} }
@@ -166,7 +166,7 @@ public class ExceptionHandler {
LOG.warn("No support yet for finding bottom block of try body with multipe inner trys"); LOG.warn("No support yet for finding bottom block of try body with multipe inner trys");
return null; return null;
} }
final TryCatchBlockAttr searchForTryBody; TryCatchBlockAttr searchForTryBody;
if (handlerTryBlock.getInnerTryBlocks().isEmpty()) { if (handlerTryBlock.getInnerTryBlocks().isEmpty()) {
searchForTryBody = handlerTryBlock; searchForTryBody = handlerTryBlock;
} else { } else {
@@ -183,17 +183,15 @@ public class TryCatchBlockAttr implements IJadxAttribute {
public void getFallthroughTryEdges(List<TryEdge> edges, List<BlockNode> exploredBlocks, List<TryCatchBlockAttr> exploredTrys) { public void getFallthroughTryEdges(List<TryEdge> edges, List<BlockNode> exploredBlocks, List<TryCatchBlockAttr> exploredTrys) {
List<ExceptionHandler> mergedHandlers = getMergedHandlers(); List<ExceptionHandler> mergedHandlers = getMergedHandlers();
Set<BlockNode> searchBlocks = new HashSet<>(); Set<BlockNode> searchBlocks = new HashSet<>(getBlocks());
searchBlocks.addAll(getBlocks());
for (ExceptionHandler handler : mergedHandlers) { for (ExceptionHandler handler : mergedHandlers) {
searchBlocks.removeAll(handler.getBlocks()); handler.getBlocks().forEach(searchBlocks::remove);
} }
BlockNode sourceBlock = BlockUtils.getTopBlock(new ArrayList<>(searchBlocks)); BlockNode sourceBlock = BlockUtils.getTopBlock(new ArrayList<>(searchBlocks));
if (sourceBlock != null) {
exploredTrys.add(this); exploredTrys.add(this);
exploreTryPath(edges, sourceBlock, searchBlocks, exploredBlocks, exploredTrys);
exploreTryPath(edges, sourceBlock, searchBlocks, exploredBlocks, exploredTrys); }
} }
public List<TryEdge> getTryEdges() { public List<TryEdge> getTryEdges() {
@@ -221,19 +219,19 @@ public class TryCatchBlockAttr implements IJadxAttribute {
exploredBlocks.add(successor); exploredBlocks.add(successor);
if (successor.contains(AFlag.LOOP_END)) { if (successor.contains(AFlag.LOOP_END)) {
final var loopsAttrList = successor.get(AType.LOOP); var loopsAttrList = successor.get(AType.LOOP);
final List<LoopInfo> loops = loopsAttrList.getList(); List<LoopInfo> loops = loopsAttrList.getList();
final List<BlockNode> loopStartBlocks = new LinkedList<>(); List<BlockNode> loopStartBlocks = new LinkedList<>();
for (final LoopInfo loop : loops) { for (LoopInfo loop : loops) {
loopStartBlocks.add(loop.getStart()); loopStartBlocks.add(loop.getStart());
final List<Edge> loopEdges = loop.getExitEdges(); List<Edge> loopEdges = loop.getExitEdges();
for (final Edge loopEdge : loopEdges) { for (Edge loopEdge : loopEdges) {
if (loopEdge.getTarget() == successor) { if (loopEdge.getTarget() == successor) {
loopStartBlocks.add(loopEdge.getSource()); loopStartBlocks.add(loopEdge.getSource());
} }
} }
} }
final boolean includesAllLoopStart = ListUtils.allMatch(loopStartBlocks, exploredBlocks::contains); boolean includesAllLoopStart = ListUtils.allMatch(loopStartBlocks, exploredBlocks::contains);
if (!includesAllLoopStart) { if (!includesAllLoopStart) {
edges.add(new TryEdge(blk, successor, TryEdgeType.LOOP_EXIT)); edges.add(new TryEdge(blk, successor, TryEdgeType.LOOP_EXIT));
continue; continue;
@@ -241,7 +239,7 @@ public class TryCatchBlockAttr implements IJadxAttribute {
} }
boolean isPathToAnySearchBlock = false; boolean isPathToAnySearchBlock = false;
for (final BlockNode searchBlock : searchBlocks) { for (BlockNode searchBlock : searchBlocks) {
if (BlockUtils.isPathExists(successor, searchBlock)) { if (BlockUtils.isPathExists(successor, searchBlock)) {
isPathToAnySearchBlock = true; isPathToAnySearchBlock = true;
break; break;
@@ -329,7 +327,7 @@ public class TryCatchBlockAttr implements IJadxAttribute {
public List<ExceptionHandler> getMergedHandlers() { public List<ExceptionHandler> getMergedHandlers() {
boolean hasInnerBlocks = !getInnerTryBlocks().isEmpty(); boolean hasInnerBlocks = !getInnerTryBlocks().isEmpty();
final List<ExceptionHandler> mergedHandlers; List<ExceptionHandler> mergedHandlers;
if (hasInnerBlocks) { if (hasInnerBlocks) {
// collect handlers from this and all inner blocks // collect handlers from this and all inner blocks
// (intentionally not using recursive collect for now) // (intentionally not using recursive collect for now)
@@ -343,7 +341,7 @@ public class TryCatchBlockAttr implements IJadxAttribute {
return Collections.unmodifiableList(mergedHandlers); return Collections.unmodifiableList(mergedHandlers);
} }
public Map<TryEdge, BlockNode> getEdgeBlockMap(MethodNode mth) { public Map<TryEdge, BlockNode> getEdgeBlockMap() {
List<TryEdge> edges = getTryEdges(); List<TryEdge> edges = getTryEdges();
Map<TryEdge, BlockNode> blockMap = new HashMap<>(); Map<TryEdge, BlockNode> blockMap = new HashMap<>();
for (TryEdge edge : edges) { for (TryEdge edge : edges) {
@@ -353,7 +351,7 @@ public class TryCatchBlockAttr implements IJadxAttribute {
} }
public TryEdgeScopeGroupMap getExecutionScopeGroups(MethodNode mth) { public TryEdgeScopeGroupMap getExecutionScopeGroups(MethodNode mth) {
Map<TryEdge, BlockNode> handlerBlocks = getEdgeBlockMap(mth); Map<TryEdge, BlockNode> handlerBlocks = getEdgeBlockMap();
TryEdgeScopeGroupMap scopeGroups = new TryEdgeScopeGroupMap(mth, this, handlerBlocks.size()); TryEdgeScopeGroupMap scopeGroups = new TryEdgeScopeGroupMap(mth, this, handlerBlocks.size());
scopeGroups.populateFromEdges(handlerBlocks); scopeGroups.populateFromEdges(handlerBlocks);
@@ -19,15 +19,15 @@ public final class TryEdge {
private final Optional<ExceptionHandler> handler; private final Optional<ExceptionHandler> handler;
private final TryEdgeType type; private final TryEdgeType type;
public TryEdge(final BlockNode source, final BlockNode target, final TryEdgeType type) { public TryEdge(BlockNode source, BlockNode target, TryEdgeType type) {
this(source, target, type, Optional.empty()); this(source, target, type, Optional.empty());
} }
public TryEdge(final BlockNode source, final BlockNode target, final @NotNull ExceptionHandler handler) { public TryEdge(BlockNode source, BlockNode target, @NotNull ExceptionHandler handler) {
this(source, target, TryEdgeType.HANDLER, Optional.of(handler)); this(source, target, TryEdgeType.HANDLER, Optional.of(handler));
} }
public TryEdge(final BlockNode source, final BlockNode target, final TryEdgeType type, final Optional<ExceptionHandler> handler) { public TryEdge(BlockNode source, BlockNode target, TryEdgeType type, Optional<ExceptionHandler> handler) {
this.source = source; this.source = source;
this.target = target; this.target = target;
this.handler = handler; this.handler = handler;
@@ -41,7 +41,7 @@ public final class TryEdge {
} }
@Override @Override
public final String toString() { public String toString() {
StringBuilder sb = new StringBuilder("TryEdge: ["); StringBuilder sb = new StringBuilder("TryEdge: [");
sb.append(type); sb.append(type);
sb.append(' '); sb.append(' ');
@@ -58,12 +58,12 @@ public final class TryEdge {
} }
@Override @Override
public final boolean equals(Object obj) { public boolean equals(Object obj) {
if (!(obj instanceof TryEdge)) { if (!(obj instanceof TryEdge)) {
return false; return false;
} }
final TryEdge other = (TryEdge) obj; TryEdge other = (TryEdge) obj;
return source.equals(other.source) return source.equals(other.source)
&& target.equals(other.target) && target.equals(other.target)
@@ -72,31 +72,31 @@ public final class TryEdge {
} }
@Override @Override
public final int hashCode() { public int hashCode() {
return Objects.hash(source, target, type, handler); return Objects.hash(source, target, type, handler);
} }
public final BlockNode getSource() { public BlockNode getSource() {
return source; return source;
} }
public final BlockNode getTarget() { public BlockNode getTarget() {
return target; return target;
} }
public final TryEdgeType getType() { public TryEdgeType getType() {
return type; return type;
} }
public final boolean isHandlerExit() { public boolean isHandlerExit() {
return type == TryEdgeType.HANDLER; return type == TryEdgeType.HANDLER;
} }
public final boolean isNotHandlerExit() { public boolean isNotHandlerExit() {
return !isHandlerExit(); return !isHandlerExit();
} }
public final ExceptionHandler getExceptionHandler() { public ExceptionHandler getExceptionHandler() {
if (!isHandlerExit()) { if (!isHandlerExit()) {
throw new JadxRuntimeException("Attempted to get the exception handler of a non-handler edge type"); throw new JadxRuntimeException("Attempted to get the exception handler of a non-handler edge type");
} }
@@ -28,7 +28,7 @@ public final class TryEdgeScopeGroupMap implements Map<TryEdge, Map<TryEdge, Blo
private final TryEdge edge; private final TryEdge edge;
private final BlockNode block; private final BlockNode block;
public TryEdgeScope(final TryEdge edge, final BlockNode block) { public TryEdgeScope(TryEdge edge, BlockNode block) {
this.edge = edge; this.edge = edge;
this.block = block; this.block = block;
} }
@@ -38,91 +38,91 @@ public final class TryEdgeScopeGroupMap implements Map<TryEdge, Map<TryEdge, Blo
private final TryCatchBlockAttr tryCatch; private final TryCatchBlockAttr tryCatch;
private final Map<TryEdge, Map<TryEdge, BlockNode>> underlyingMap; private final Map<TryEdge, Map<TryEdge, BlockNode>> underlyingMap;
public TryEdgeScopeGroupMap(final MethodNode mth, final TryCatchBlockAttr tryCatch, final int initialCapacity) { public TryEdgeScopeGroupMap(MethodNode mth, TryCatchBlockAttr tryCatch, int initialCapacity) {
this.tryCatch = tryCatch; this.tryCatch = tryCatch;
underlyingMap = new HashMap<>(initialCapacity); underlyingMap = new HashMap<>(initialCapacity);
} }
@Override @Override
public final void clear() { public void clear() {
underlyingMap.clear(); underlyingMap.clear();
} }
@Override @Override
public final boolean containsKey(Object key) { public boolean containsKey(Object key) {
return underlyingMap.containsKey(key); return underlyingMap.containsKey(key);
} }
@Override @Override
public final boolean containsValue(Object value) { public boolean containsValue(Object value) {
if (!(value instanceof TryEdge)) { if (!(value instanceof TryEdge)) {
return false; return false;
} }
final TryEdge edge = (TryEdge) value; TryEdge edge = (TryEdge) value;
return underlyingMap.containsKey(edge); return underlyingMap.containsKey(edge);
} }
@Override @Override
public final Set<Entry<TryEdge, Map<TryEdge, BlockNode>>> entrySet() { public Set<Entry<TryEdge, Map<TryEdge, BlockNode>>> entrySet() {
return underlyingMap.entrySet(); return underlyingMap.entrySet();
} }
@Override @Override
public final Map<TryEdge, BlockNode> get(Object key) { public Map<TryEdge, BlockNode> get(Object key) {
return underlyingMap.get(key); return underlyingMap.get(key);
} }
@Override @Override
public final boolean isEmpty() { public boolean isEmpty() {
return underlyingMap.isEmpty(); return underlyingMap.isEmpty();
} }
@Override @Override
public final Set<TryEdge> keySet() { public Set<TryEdge> keySet() {
return underlyingMap.keySet(); return underlyingMap.keySet();
} }
@Override @Override
public final Map<TryEdge, BlockNode> put(TryEdge key, Map<TryEdge, BlockNode> value) { public Map<TryEdge, BlockNode> put(TryEdge key, Map<TryEdge, BlockNode> value) {
return underlyingMap.put(key, value); return underlyingMap.put(key, value);
} }
@Override @Override
public final void putAll(Map<? extends TryEdge, ? extends Map<TryEdge, BlockNode>> otherMap) { public void putAll(Map<? extends TryEdge, ? extends Map<TryEdge, BlockNode>> otherMap) {
underlyingMap.putAll(otherMap); underlyingMap.putAll(otherMap);
} }
@Override @Override
public final Map<TryEdge, BlockNode> remove(Object key) { public Map<TryEdge, BlockNode> remove(Object key) {
return underlyingMap.remove(key); return underlyingMap.remove(key);
} }
@Override @Override
public final int size() { public int size() {
return underlyingMap.size(); return underlyingMap.size();
} }
@Override @Override
public final Collection<Map<TryEdge, BlockNode>> values() { public Collection<Map<TryEdge, BlockNode>> values() {
return underlyingMap.values(); return underlyingMap.values();
} }
public final boolean hasMergedEdges() { public boolean hasMergedEdges() {
return !mergedEdges.isEmpty(); return !mergedEdges.isEmpty();
} }
public final List<Pair<TryEdge>> getMergedScopes() { public List<Pair<TryEdge>> getMergedScopes() {
return mergedEdges; return mergedEdges;
} }
public final void populateFromEdges(final Map<TryEdge, BlockNode> edges) { public void populateFromEdges(Map<TryEdge, BlockNode> edges) {
mergeSameScopes(edges); mergeSameScopes(edges);
for (final TryEdge edge : edges.keySet()) { for (TryEdge edge : edges.keySet()) {
final BlockNode edgeBlock = edges.get(edge); BlockNode edgeBlock = edges.get(edge);
final Map<TryEdge, BlockNode> handlerFallthroughMap = createEdgeTerminusMap(edges, edge, edgeBlock); Map<TryEdge, BlockNode> handlerFallthroughMap = createEdgeTerminusMap(edges, edge, edgeBlock);
put(edge, handlerFallthroughMap); put(edge, handlerFallthroughMap);
} }
} }
@@ -134,30 +134,30 @@ public final class TryEdgeScopeGroupMap implements Map<TryEdge, Map<TryEdge, Blo
* @param mth * @param mth
* @return * @return
*/ */
public Map<BlockNode, List<TryEdge>> getScopeEnds(final MethodNode mth) { public Map<BlockNode, List<TryEdge>> getScopeEnds(MethodNode mth) {
final Map<BlockNode, List<TryEdge>> groups = new HashMap<>(); Map<BlockNode, List<TryEdge>> groups = new HashMap<>();
// A list containing pairs of edges where there are no shared common clean successors between the // A list containing pairs of edges where there are no shared common clean successors between the
// two handlers. This usually indicates that these edge pairs must be processed differently. // two handlers. This usually indicates that these edge pairs must be processed differently.
final List<TryEdge> isolatedEdgePairs = new LinkedList<>(); List<TryEdge> isolatedEdgePairs = new LinkedList<>();
for (final TryEdge mergeEdgeA : keySet()) { for (TryEdge mergeEdgeA : keySet()) {
final Pair<TryEdge> edgeMergedPair = getMergedNodeFromEdge(mergeEdgeA); Pair<TryEdge> edgeMergedPair = getMergedNodeFromEdge(mergeEdgeA);
if (edgeMergedPair != null) { if (edgeMergedPair != null) {
continue; continue;
} }
final Map<TryEdge, BlockNode> handlerRelations = get(mergeEdgeA); Map<TryEdge, BlockNode> handlerRelations = get(mergeEdgeA);
final List<BlockNode> scopeEnds = new ArrayList<>(handlerRelations.size()); List<BlockNode> scopeEnds = new ArrayList<>(handlerRelations.size());
for (final TryEdge mergeEdgeB : handlerRelations.keySet()) { for (TryEdge mergeEdgeB : handlerRelations.keySet()) {
final Pair<TryEdge> mergedPairFromRelation = getMergedNodeFromEdge(mergeEdgeB); Pair<TryEdge> mergedPairFromRelation = getMergedNodeFromEdge(mergeEdgeB);
if (mergedPairFromRelation != null && mergedPairFromRelation.getFirst() == mergeEdgeA) { if (mergedPairFromRelation != null && mergedPairFromRelation.getFirst() == mergeEdgeA) {
continue; continue;
} }
final BlockNode sharedTerminator = handlerRelations.get(mergeEdgeB); BlockNode sharedTerminator = handlerRelations.get(mergeEdgeB);
if (sharedTerminator == null) { if (sharedTerminator == null) {
// There are no common clean succesors between the two handlers. // There are no common clean succesors between the two handlers.
@@ -172,20 +172,20 @@ public final class TryEdgeScopeGroupMap implements Map<TryEdge, Map<TryEdge, Blo
continue; continue;
} }
final BlockNode topGrouping = BlockUtils.getTopBlock(scopeEnds); BlockNode topGrouping = BlockUtils.getTopBlock(scopeEnds);
if (groups.containsKey(topGrouping)) { if (groups.containsKey(topGrouping)) {
groups.get(topGrouping).add(mergeEdgeA); groups.get(topGrouping).add(mergeEdgeA);
} else { } else {
final List<TryEdge> groupingHandlers = new LinkedList<>(); List<TryEdge> groupingHandlers = new LinkedList<>();
groupingHandlers.add(mergeEdgeA); groupingHandlers.add(mergeEdgeA);
groups.put(topGrouping, groupingHandlers); groups.put(topGrouping, groupingHandlers);
} }
} }
for (final TryEdge isolatedEdge : isolatedEdgePairs) { for (TryEdge isolatedEdge : isolatedEdgePairs) {
boolean isInList = false; boolean isInList = false;
for (final List<TryEdge> foundEdges : groups.values()) { for (List<TryEdge> foundEdges : groups.values()) {
if (foundEdges.contains(isolatedEdge)) { if (foundEdges.contains(isolatedEdge)) {
isInList = true; isInList = true;
break; break;
@@ -203,14 +203,14 @@ public final class TryEdgeScopeGroupMap implements Map<TryEdge, Map<TryEdge, Blo
// exit node, the mentioned point will be the farthest successor of the edge target which has no // exit node, the mentioned point will be the farthest successor of the edge target which has no
// clean successors. // clean successors.
final BlockNode target = isolatedEdge.getTarget(); BlockNode target = isolatedEdge.getTarget();
final List<BlockNode> successorBlocks = BlockUtils.collectAllSuccessors(mth, target, true); List<BlockNode> successorBlocks = BlockUtils.collectAllSuccessors(mth, target, true);
final BlockNode cleanSuccessorEnd = BlockUtils.getBottomBlock(successorBlocks); BlockNode cleanSuccessorEnd = BlockUtils.getBottomBlock(successorBlocks);
if (cleanSuccessorEnd == null) { if (cleanSuccessorEnd == null) {
throw new JadxRuntimeException("Could not find bottom clean successor for isolated try edge"); throw new JadxRuntimeException("Could not find bottom clean successor for isolated try edge");
} }
final List<TryEdge> scopeTerminusList; List<TryEdge> scopeTerminusList;
if (groups.containsKey(cleanSuccessorEnd)) { if (groups.containsKey(cleanSuccessorEnd)) {
scopeTerminusList = groups.get(cleanSuccessorEnd); scopeTerminusList = groups.get(cleanSuccessorEnd);
} else { } else {
@@ -221,9 +221,9 @@ public final class TryEdgeScopeGroupMap implements Map<TryEdge, Map<TryEdge, Blo
} }
if (groups.size() == 1) { if (groups.size() == 1) {
for (final Pair<TryEdge> pair : mergedEdges) { for (Pair<TryEdge> pair : mergedEdges) {
final TryEdge keptEdge = pair.getFirst(); TryEdge keptEdge = pair.getFirst();
final TryEdge removedEdge = pair.getSecond(); TryEdge removedEdge = pair.getSecond();
if (keptEdge.isHandlerExit() && !tryCatch.getHandlers().contains(keptEdge.getExceptionHandler())) { if (keptEdge.isHandlerExit() && !tryCatch.getHandlers().contains(keptEdge.getExceptionHandler())) {
continue; continue;
@@ -238,14 +238,14 @@ public final class TryEdgeScopeGroupMap implements Map<TryEdge, Map<TryEdge, Blo
continue; continue;
} }
for (final List<TryEdge> edgesWithTerminus : groups.values()) { for (List<TryEdge> edgesWithTerminus : groups.values()) {
if (edgesWithTerminus.contains(keptEdge)) { if (edgesWithTerminus.contains(keptEdge)) {
edgesWithTerminus.remove(keptEdge); edgesWithTerminus.remove(keptEdge);
} }
} }
final BlockNode terminus = get(keptEdge).get(removedEdge); BlockNode terminus = get(keptEdge).get(removedEdge);
final List<TryEdge> terminusEdges; List<TryEdge> terminusEdges;
if (!groups.containsKey(terminus)) { if (!groups.containsKey(terminus)) {
terminusEdges = new LinkedList<>(); terminusEdges = new LinkedList<>();
terminusEdges.add(keptEdge); terminusEdges.add(keptEdge);
@@ -261,7 +261,7 @@ public final class TryEdgeScopeGroupMap implements Map<TryEdge, Map<TryEdge, Blo
} }
@Nullable @Nullable
private Pair<TryEdge> getMergedNodeFromEdge(final TryEdge edge) { private Pair<TryEdge> getMergedNodeFromEdge(TryEdge edge) {
for (Pair<TryEdge> pair : mergedEdges) { for (Pair<TryEdge> pair : mergedEdges) {
if (pair.getSecond() == edge) { if (pair.getSecond() == edge) {
return pair; return pair;
@@ -270,17 +270,17 @@ public final class TryEdgeScopeGroupMap implements Map<TryEdge, Map<TryEdge, Blo
return null; return null;
} }
private Map<TryEdge, BlockNode> createEdgeTerminusMap(final Map<TryEdge, BlockNode> edgeStartMap, final TryEdge edge, private Map<TryEdge, BlockNode> createEdgeTerminusMap(Map<TryEdge, BlockNode> edgeStartMap, TryEdge edge,
final BlockNode edgeStart) { BlockNode edgeStart) {
final Map<TryEdge, BlockNode> scopeRelations = new HashMap<>(edgeStartMap.size() - 1); Map<TryEdge, BlockNode> scopeRelations = new HashMap<>(edgeStartMap.size() - 1);
for (final TryEdge otherEdge : edgeStartMap.keySet()) { for (TryEdge otherEdge : edgeStartMap.keySet()) {
if (edge == otherEdge) { if (edge == otherEdge) {
continue; continue;
} }
final BlockNode otherEdgeStart = edgeStartMap.get(otherEdge); BlockNode otherEdgeStart = edgeStartMap.get(otherEdge);
final boolean eitherEdgeIsHandler = edge.isHandlerExit() || otherEdge.isHandlerExit(); boolean eitherEdgeIsHandler = edge.isHandlerExit() || otherEdge.isHandlerExit();
if (otherEdgeStart == edgeStart && eitherEdgeIsHandler) { if (otherEdgeStart == edgeStart && eitherEdgeIsHandler) {
continue; continue;
} }
@@ -298,14 +298,14 @@ public final class TryEdgeScopeGroupMap implements Map<TryEdge, Map<TryEdge, Blo
continue; continue;
} }
final BitSet sharedPostDominators = (BitSet) edgeStart.getPostDoms().clone(); BitSet sharedPostDominators = (BitSet) edgeStart.getPostDoms().clone();
final BitSet otherPostDoms = otherEdgeStart.getPostDoms(); BitSet otherPostDoms = otherEdgeStart.getPostDoms();
if (sharedPostDominators.isEmpty() || otherPostDoms.isEmpty()) { if (sharedPostDominators.isEmpty() || otherPostDoms.isEmpty()) {
continue; continue;
} }
sharedPostDominators.and(otherPostDoms); sharedPostDominators.and(otherPostDoms);
final List<BlockNode> postDomHandler = new LinkedList<>(); List<BlockNode> postDomHandler = new LinkedList<>();
BlockNode currentBlock = edgeStart; BlockNode currentBlock = edgeStart;
while (currentBlock != null) { while (currentBlock != null) {
postDomHandler.add(currentBlock); postDomHandler.add(currentBlock);
@@ -322,7 +322,7 @@ public final class TryEdgeScopeGroupMap implements Map<TryEdge, Map<TryEdge, Blo
currentBlock = currentBlock.getIPostDom(); currentBlock = currentBlock.getIPostDom();
} }
final BlockNode scopeEnd = commonPostDom; BlockNode scopeEnd = commonPostDom;
scopeRelations.put(otherEdge, scopeEnd); scopeRelations.put(otherEdge, scopeEnd);
} }
return scopeRelations; return scopeRelations;
@@ -335,10 +335,10 @@ public final class TryEdgeScopeGroupMap implements Map<TryEdge, Map<TryEdge, Blo
* @param handlers * @param handlers
* @return * @return
*/ */
private Map<TryEdge, BlockNode> mergeSameScopes(final Map<TryEdge, BlockNode> handlers) { private Map<TryEdge, BlockNode> mergeSameScopes(Map<TryEdge, BlockNode> handlers) {
final List<Entry<TryEdge, BlockNode>> exceptionHandlers = new ArrayList<>(handlers.entrySet()); List<Entry<TryEdge, BlockNode>> exceptionHandlers = new ArrayList<>(handlers.entrySet());
final List<Pair<TryEdgeScope>> handlerPairs = new LinkedList<>(); List<Pair<TryEdgeScope>> handlerPairs = new LinkedList<>();
for (int i = 0; i < exceptionHandlers.size(); i++) { for (int i = 0; i < exceptionHandlers.size(); i++) {
for (int j = i + 1; j < exceptionHandlers.size(); j++) { for (int j = i + 1; j < exceptionHandlers.size(); j++) {
TryEdgeScope a = new TryEdgeScope(exceptionHandlers.get(i).getKey(), exceptionHandlers.get(i).getValue()); TryEdgeScope a = new TryEdgeScope(exceptionHandlers.get(i).getKey(), exceptionHandlers.get(i).getValue());
@@ -347,22 +347,22 @@ public final class TryEdgeScopeGroupMap implements Map<TryEdge, Map<TryEdge, Blo
} }
} }
final Map<TryEdge, BlockNode> simplifiedScopes = new HashMap<>(handlers); Map<TryEdge, BlockNode> simplifiedScopes = new HashMap<>(handlers);
int i = 0; int i = 0;
while (i < handlerPairs.size()) { while (i < handlerPairs.size()) {
final Pair<TryEdgeScope> handlerPair = handlerPairs.get(i); Pair<TryEdgeScope> handlerPair = handlerPairs.get(i);
final TryEdgeScope edgeScopeA = handlerPair.getFirst(); TryEdgeScope edgeScopeA = handlerPair.getFirst();
final TryEdgeScope edgeScopeB = handlerPair.getSecond(); TryEdgeScope edgeScopeB = handlerPair.getSecond();
final BlockNode edgeBlockA = edgeScopeA.block; BlockNode edgeBlockA = edgeScopeA.block;
final BlockNode edgeBlockB = edgeScopeB.block; BlockNode edgeBlockB = edgeScopeB.block;
final boolean pathExists = BlockUtils.isPathExists(edgeBlockA, edgeBlockB) || BlockUtils.isPathExists(edgeBlockB, edgeBlockA); boolean pathExists = BlockUtils.isPathExists(edgeBlockA, edgeBlockB) || BlockUtils.isPathExists(edgeBlockB, edgeBlockA);
if (pathExists) { if (pathExists) {
BlockNode bottomBlock = BlockUtils.getBottomBlock(List.of(edgeBlockA, edgeBlockB)); BlockNode bottomBlock = BlockUtils.getBottomBlock(List.of(edgeBlockA, edgeBlockB));
// The two blocks are within the same scope - remove these from the matrix // The two blocks are within the same scope - remove these from the matrix
final TryEdge removeHandler = edgeBlockA != bottomBlock ? edgeScopeA.edge : edgeScopeB.edge; TryEdge removeHandler = edgeBlockA != bottomBlock ? edgeScopeA.edge : edgeScopeB.edge;
final TryEdge keepHandler = edgeBlockA == bottomBlock ? edgeScopeA.edge : edgeScopeB.edge; TryEdge keepHandler = edgeBlockA == bottomBlock ? edgeScopeA.edge : edgeScopeB.edge;
simplifiedScopes.remove(removeHandler); simplifiedScopes.remove(removeHandler);
handlerPairs.remove(i); handlerPairs.remove(i);
@@ -114,7 +114,7 @@ public class MethodThrowsVisitor extends AbstractVisitor {
return; return;
} }
try { try {
blocks: for (final BlockNode block : mth.getBasicBlocks()) { blocks: for (BlockNode block : mth.getBasicBlocks()) {
// Skip e.g. throw instructions of synchronized regions // Skip e.g. throw instructions of synchronized regions
boolean skipExceptions = block.contains(AFlag.REMOVE) || block.contains(AFlag.DONT_GENERATE); boolean skipExceptions = block.contains(AFlag.REMOVE) || block.contains(AFlag.DONT_GENERATE);
Set<String> excludedExceptions = new HashSet<>(); Set<String> excludedExceptions = new HashSet<>();
@@ -127,7 +127,7 @@ public class MethodThrowsVisitor extends AbstractVisitor {
excludedExceptions.add(handler.getArgType().toString()); excludedExceptions.add(handler.getArgType().toString());
} }
} }
for (final InsnNode insn : block.getInstructions()) { for (InsnNode insn : block.getInstructions()) {
checkInsn(mth, insn, excludedExceptions, skipExceptions); checkInsn(mth, insn, excludedExceptions, skipExceptions);
} }
} }
@@ -25,14 +25,14 @@ public final class CentralityState {
private boolean allowsCentral = true; private boolean allowsCentral = true;
private boolean allowsNonStartingNode; private boolean allowsNonStartingNode;
public CentralityState(final SameInstructionsStrategy sameInstructionsStrategy, final boolean allowsNonStartingNode) { public CentralityState(SameInstructionsStrategy sameInstructionsStrategy, boolean allowsNonStartingNode) {
this.sameInstructionsStrategy = sameInstructionsStrategy; this.sameInstructionsStrategy = sameInstructionsStrategy;
this.allowsNonStartingNode = allowsNonStartingNode; this.allowsNonStartingNode = allowsNonStartingNode;
} }
@Override @Override
public final String toString() { public String toString() {
final StringBuilder sb = new StringBuilder("CentralityState - "); StringBuilder sb = new StringBuilder("CentralityState - ");
if (allowsCentral) { if (allowsCentral) {
sb.append("allows central"); sb.append("allows central");
} else { } else {
@@ -46,31 +46,31 @@ public final class CentralityState {
return sb.toString(); return sb.toString();
} }
public final SameInstructionsStrategy getSameInstructionsStrategy() { public SameInstructionsStrategy getSameInstructionsStrategy() {
return sameInstructionsStrategy; return sameInstructionsStrategy;
} }
public final boolean getAllowsCentral() { public boolean getAllowsCentral() {
return allowsCentral; return allowsCentral;
} }
public final void setAllowsCentral(final boolean allowsCentral) { public void setAllowsCentral(boolean allowsCentral) {
this.allowsCentral = allowsCentral; this.allowsCentral = allowsCentral;
} }
public final boolean getAllowsNonStartingNode() { public boolean getAllowsNonStartingNode() {
return allowsNonStartingNode; return allowsNonStartingNode;
} }
public final void setAllowsNonStartingNode(final boolean allowsNonStartingNode) { public void setAllowsNonStartingNode(boolean allowsNonStartingNode) {
this.allowsNonStartingNode = allowsNonStartingNode; this.allowsNonStartingNode = allowsNonStartingNode;
} }
public final void addAllowableOutput(final RegisterArg allowableOutput) { public void addAllowableOutput(RegisterArg allowableOutput) {
allowableOutputArguments.add(allowableOutput); allowableOutputArguments.add(allowableOutput);
} }
public final void addAllowableOutputs(final Collection<RegisterArg> allowableOutputs) { public void addAllowableOutputs(Collection<RegisterArg> allowableOutputs) {
allowableOutputArguments.addAll(allowableOutputs); allowableOutputArguments.addAll(allowableOutputs);
} }
@@ -79,36 +79,31 @@ public final class CentralityState {
* *
* @param allowableOutputInsn The instruction to retrieve the list of inputs from. * @param allowableOutputInsn The instruction to retrieve the list of inputs from.
*/ */
public final void addAllowableOutputs(final InsnNode allowableOutputInsn) { public void addAllowableOutputs(InsnNode allowableOutputInsn) {
final List<RegisterArg> registerArgs = new LinkedList<>(); List<RegisterArg> registerArgs = new LinkedList<>();
for (final InsnArg arg : allowableOutputInsn.getArgList()) { for (InsnArg arg : allowableOutputInsn.getArgList()) {
if (!(arg instanceof RegisterArg)) { if (!(arg instanceof RegisterArg)) {
continue; continue;
} }
registerArgs.add((RegisterArg) arg); registerArgs.add((RegisterArg) arg);
} }
registerArgs.forEach(this::addAllowableOutput); registerArgs.forEach(this::addAllowableOutput);
} }
public final boolean hasAllowableOutput(final InsnNode insn) { public boolean hasAllowableOutput(InsnNode insn) {
if (allowableOutputArguments.isEmpty()) { if (allowableOutputArguments.isEmpty()) {
return false; return false;
} }
RegisterArg registerArg;
final RegisterArg registerArg;
if (insn.getResult() != null) { if (insn.getResult() != null) {
registerArg = insn.getResult(); registerArg = insn.getResult();
} else { } else {
registerArg = null; registerArg = null;
} }
if (registerArg == null) { if (registerArg == null) {
return false; return false;
} }
for (RegisterArg allowableOutput : allowableOutputArguments) {
for (final RegisterArg allowableOutput : allowableOutputArguments) {
if (allowableOutput.equals(registerArg)) { if (allowableOutput.equals(registerArg)) {
return true; return true;
} }
@@ -117,26 +112,22 @@ public final class CentralityState {
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
public final boolean hasAllowableInputs(final InsnNode insn) { public boolean hasAllowableInputs(InsnNode insn) {
if (allowableOutputArguments.isEmpty()) { if (allowableOutputArguments.isEmpty()) {
return false; return false;
} }
List<RegisterArg> registerArgs = new ArrayList<>();
final List<RegisterArg> registerArgs = new ArrayList<>(); for (InsnArg arg : insn.getArgList()) {
for (final InsnArg arg : insn.getArgList()) {
if (arg instanceof RegisterArg) { if (arg instanceof RegisterArg) {
registerArgs.add((RegisterArg) arg); registerArgs.add((RegisterArg) arg);
} }
} }
if (registerArgs.isEmpty() || allowableOutputArguments.isEmpty()) { if (registerArgs.isEmpty() || allowableOutputArguments.isEmpty()) {
return false; return false;
} }
for (RegisterArg regArg : registerArgs) {
for (final RegisterArg regArg : registerArgs) {
boolean foundMatch = false; boolean foundMatch = false;
for (final RegisterArg allowableOutput : allowableOutputArguments) { for (RegisterArg allowableOutput : allowableOutputArguments) {
if (regArg.equals(allowableOutput)) { if (regArg.equals(allowableOutput)) {
foundMatch = true; foundMatch = true;
break; break;
@@ -149,14 +140,14 @@ public final class CentralityState {
return true; return true;
} }
public final CentralityState duplicate() { public CentralityState duplicate() {
final CentralityState state = new CentralityState(sameInstructionsStrategy, allowsNonStartingNode); CentralityState state = new CentralityState(sameInstructionsStrategy, allowsNonStartingNode);
state.allowsCentral = allowsCentral; state.allowsCentral = allowsCentral;
state.allowableOutputArguments.addAll(allowableOutputArguments); state.allowableOutputArguments.addAll(allowableOutputArguments);
return state; return state;
} }
public final Set<RegisterArg> getAllowableOutputArguments() { public Set<RegisterArg> getAllowableOutputArguments() {
return allowableOutputArguments; return allowableOutputArguments;
} }
} }
@@ -1,90 +0,0 @@
package jadx.core.dex.visitors.finaly;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.InsnNode;
import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.trycatch.ExceptionHandler;
import jadx.core.utils.Utils;
public class FinallyExtractInfo {
private final MethodNode mth;
private final ExceptionHandler finallyHandler;
private final List<BlockNode> allHandlerBlocks;
private final List<InsnsSlice> duplicateSlices = new ArrayList<>();
private final Set<BlockNode> checkedBlocks = new HashSet<>();
private final InsnsSlice finallyInsnsSlice = new InsnsSlice();
private final BlockNode startBlock;
private InsnsSlice curDupSlice;
private List<InsnNode> curDupInsns;
private int curDupInsnsOffset;
public FinallyExtractInfo(MethodNode mth, ExceptionHandler finallyHandler, BlockNode startBlock, List<BlockNode> allHandlerBlocks) {
this.mth = mth;
this.finallyHandler = finallyHandler;
this.startBlock = startBlock;
this.allHandlerBlocks = allHandlerBlocks;
}
public MethodNode getMth() {
return mth;
}
public ExceptionHandler getFinallyHandler() {
return finallyHandler;
}
public List<BlockNode> getAllHandlerBlocks() {
return allHandlerBlocks;
}
public InsnsSlice getFinallyInsnsSlice() {
return finallyInsnsSlice;
}
public List<InsnsSlice> getDuplicateSlices() {
return duplicateSlices;
}
public Set<BlockNode> getCheckedBlocks() {
return checkedBlocks;
}
public BlockNode getStartBlock() {
return startBlock;
}
public InsnsSlice getCurDupSlice() {
return curDupSlice;
}
public void setCurDupSlice(InsnsSlice curDupSlice) {
this.curDupSlice = curDupSlice;
}
public List<InsnNode> getCurDupInsns() {
return curDupInsns;
}
public int getCurDupInsnsOffset() {
return curDupInsnsOffset;
}
public void setCurDupInsns(List<InsnNode> insns, int offset) {
this.curDupInsns = insns;
this.curDupInsnsOffset = offset;
}
@Override
public String toString() {
return "FinallyExtractInfo{"
+ "\n finally:\n " + finallyInsnsSlice
+ "\n dups:\n " + Utils.listToString(duplicateSlices, "\n ")
+ "\n}";
}
}
@@ -1,79 +0,0 @@
package jadx.core.dex.visitors.finaly;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.jetbrains.annotations.Nullable;
import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.InsnNode;
public class InsnsSlice {
private final List<InsnNode> insnsList = new ArrayList<>();
private final Map<InsnNode, BlockNode> insnMap = new IdentityHashMap<>();
private boolean complete;
public void addInsn(InsnNode insn, BlockNode block) {
insnsList.add(insn);
insnMap.put(insn, block);
}
public void addBlock(BlockNode block) {
for (InsnNode insn : block.getInstructions()) {
addInsn(insn, block);
}
}
public void addInsns(BlockNode block, int startIndex, int endIndex) {
List<InsnNode> insns = block.getInstructions();
for (int i = startIndex; i < endIndex; i++) {
addInsn(insns.get(i), block);
}
}
@Nullable
public BlockNode getBlock(InsnNode insn) {
return insnMap.get(insn);
}
public List<InsnNode> getInsnsList() {
return insnsList;
}
public Set<BlockNode> getBlocks() {
Set<BlockNode> set = new LinkedHashSet<>();
for (InsnNode insn : insnsList) {
set.add(insnMap.get(insn));
}
return set;
}
public void resetIncomplete() {
if (!complete) {
insnsList.clear();
insnMap.clear();
}
}
public boolean isComplete() {
return complete;
}
public void setComplete(boolean complete) {
this.complete = complete;
}
@Override
public String toString() {
return "{["
+ insnsList.stream().map(insn -> insn.getType().toString()).collect(Collectors.joining(", "))
+ ']'
+ (complete ? " complete" : "")
+ '}';
}
}
@@ -68,59 +68,50 @@ import jadx.core.utils.exceptions.JadxRuntimeException;
runBefore = ConstInlineVisitor.class runBefore = ConstInlineVisitor.class
) )
public class MarkFinallyVisitor extends AbstractVisitor { public class MarkFinallyVisitor extends AbstractVisitor {
private static final Logger LOG = LoggerFactory.getLogger(MarkFinallyVisitor.class); private static final Logger LOG = LoggerFactory.getLogger(MarkFinallyVisitor.class);
private static final class TryExtractInfo { private static final class TryExtractInfo {
private final TryCatchBlockAttr tryBlock; private final TryCatchBlockAttr tryBlock;
private final TryEdgeScopeGroupMap scopeGroups; private final TryEdgeScopeGroupMap scopeGroups;
private final ExceptionHandler finallyHandler; private final ExceptionHandler finallyHandler;
private final Map<BlockNode, List<TryEdge>> scopeTerminusGroups; private final Map<BlockNode, List<TryEdge>> scopeTerminusGroups;
private final TryCatchEdgeBlockMap handlerScopes; private final TryCatchEdgeBlockMap handlerScopes;
private final Set<BlockNode> allHandlerBlocks;
private final Set<BlockNode> rethrowBlocks; private final Set<BlockNode> rethrowBlocks;
private Set<BlockNode> completeFinallyBlocks = null; private Set<BlockNode> completeFinallyBlocks = null;
private Set<BlockNode> completeCandidateBlocks = null; private Set<BlockNode> completeCandidateBlocks = null;
private TryExtractInfo(final TryCatchBlockAttr tryBlock, final TryEdgeScopeGroupMap scopeGroups, private TryExtractInfo(TryCatchBlockAttr tryBlock, TryEdgeScopeGroupMap scopeGroups,
final ExceptionHandler finallyHandler, final Map<BlockNode, List<TryEdge>> fallthroughGroups, ExceptionHandler finallyHandler, Map<BlockNode, List<TryEdge>> fallthroughGroups,
final TryCatchEdgeBlockMap handlerScopes) { TryCatchEdgeBlockMap handlerScopes) {
this.tryBlock = tryBlock; this.tryBlock = tryBlock;
this.scopeGroups = scopeGroups; this.scopeGroups = scopeGroups;
this.finallyHandler = finallyHandler; this.finallyHandler = finallyHandler;
this.scopeTerminusGroups = fallthroughGroups; this.scopeTerminusGroups = fallthroughGroups;
this.handlerScopes = handlerScopes; this.handlerScopes = handlerScopes;
this.allHandlerBlocks = new HashSet<>();
this.rethrowBlocks = new HashSet<>(); this.rethrowBlocks = new HashSet<>();
for (final List<BlockNode> handlerBlocks : handlerScopes.values()) {
allHandlerBlocks.addAll(handlerBlocks);
}
} }
} }
@Override @Override
public void visit(final MethodNode mth) { public void visit(MethodNode mth) {
if (mth.isNoCode() || mth.isNoExceptionHandlers()) { if (mth.isNoCode() || mth.isNoExceptionHandlers()) {
return; return;
} }
try { try {
boolean implicitHandlerRemoved = false; boolean implicitHandlerRemoved = false;
final List<TryCatchBlockAttr> tryBlocks = mth.getAll(AType.TRY_BLOCKS_LIST); List<TryCatchBlockAttr> tryBlocks = mth.getAll(AType.TRY_BLOCKS_LIST);
List<TryCatchBlockAttr> processRequiredTryBlocks = new ArrayList<>();
final List<TryCatchBlockAttr> processRequiredTryBlocks = new ArrayList<>();
// Search through all exception handlers and: // Search through all exception handlers and:
// - Remove implicit handlers // - Remove implicit handlers
// - Mark non-implicit handlers to be searched for a finally block // - Mark non-implicit handlers to be searched for a finally block
for (final TryCatchBlockAttr tryBlock : tryBlocks) { for (TryCatchBlockAttr tryBlock : tryBlocks) {
final TryExtractInfo tryInfo = getTryBlockData(mth, tryBlock); TryExtractInfo tryInfo = getTryBlockData(mth, tryBlock);
if (tryInfo == null) { if (tryInfo == null) {
continue; continue;
} }
final List<BlockNode> cutHandlerBlocks = cutHandlerBlocks(tryInfo, tryInfo.finallyHandler); List<BlockNode> cutHandlerBlocks = cutHandlerBlocks(mth, tryInfo, tryInfo.finallyHandler);
if (cutHandlerBlocks == null) { if (cutHandlerBlocks == null) {
continue; continue;
} }
@@ -137,33 +128,30 @@ public class MarkFinallyVisitor extends AbstractVisitor {
// Search through all non-implicit handlers and search for a finally block. // Search through all non-implicit handlers and search for a finally block.
boolean finallyExtracted = false; boolean finallyExtracted = false;
for (final TryCatchBlockAttr tryBlock : processRequiredTryBlocks) { for (TryCatchBlockAttr tryBlock : processRequiredTryBlocks) {
// Refresh scope groups now due to implicit handlers // Refresh scope groups now due to implicit handlers
final TryExtractInfo tryInfo = getTryBlockData(mth, tryBlock); TryExtractInfo tryInfo = getTryBlockData(mth, tryBlock);
if (tryInfo == null) { if (tryInfo == null) {
continue; continue;
} }
cutHandlerBlocks(mth, tryInfo, tryInfo.finallyHandler);
cutHandlerBlocks(tryInfo, tryInfo.finallyHandler);
finallyExtracted |= processTryBlock(mth, tryInfo); finallyExtracted |= processTryBlock(mth, tryInfo);
} }
// If any handlers have been merged, remove them // If any handlers have been merged, remove them
if (finallyExtracted) { if (finallyExtracted) {
resetTryBlocks(mth, tryBlocks); resetTryBlocks(mth, tryBlocks);
} }
} catch (final Exception e) { } catch (Exception e) {
LOG.error(e.getMessage()); LOG.error("MarkFinallyVisitor error", e);
undoFinallyVisitor(mth); undoFinallyVisitor(mth);
mth.addWarnComment("Undo finally extract visitor", e); mth.addWarnComment("Undo finally extract visitor", e);
} }
} }
private static void resetTryBlocks(final MethodNode mth, final List<TryCatchBlockAttr> tryBlocks) { private static void resetTryBlocks(MethodNode mth, List<TryCatchBlockAttr> tryBlocks) {
mth.clearExceptionHandlers(); mth.clearExceptionHandlers();
// remove merged or empty try blocks from list in method attribute // remove merged or empty try blocks from list in method attribute
final List<TryCatchBlockAttr> clearedTryBlocks = new ArrayList<>(tryBlocks); List<TryCatchBlockAttr> clearedTryBlocks = new ArrayList<>(tryBlocks);
if (clearedTryBlocks.removeIf(TryCatchBlockAttr::isImplicitOrMerged)) { if (clearedTryBlocks.removeIf(TryCatchBlockAttr::isImplicitOrMerged)) {
mth.remove(AType.TRY_BLOCKS_LIST); mth.remove(AType.TRY_BLOCKS_LIST);
mth.addAttr(AType.TRY_BLOCKS_LIST, clearedTryBlocks); mth.addAttr(AType.TRY_BLOCKS_LIST, clearedTryBlocks);
@@ -180,28 +168,24 @@ public class MarkFinallyVisitor extends AbstractVisitor {
* @param tryBlock The try block to determine the scope information of. * @param tryBlock The try block to determine the scope information of.
* @return The handler identified as the "all" handler. * @return The handler identified as the "all" handler.
*/ */
@Nullable private static @Nullable TryExtractInfo getTryBlockData(MethodNode mth, TryCatchBlockAttr tryBlock) {
private static TryExtractInfo getTryBlockData(final MethodNode mth, final TryCatchBlockAttr tryBlock) {
if (tryBlock.isMerged()) { if (tryBlock.isMerged()) {
return null; return null;
} }
// Find the all handler // Find the all handler
ExceptionHandler allHandler = null; ExceptionHandler allHandler = null;
for (final ExceptionHandler excHandler : tryBlock.getHandlers()) { for (ExceptionHandler excHandler : tryBlock.getHandlers()) {
if (excHandler.isCatchAll()) { if (excHandler.isCatchAll()) {
allHandler = excHandler; allHandler = excHandler;
break; break;
} }
} }
if (allHandler == null) { if (allHandler == null) {
return null; return null;
} }
TryEdgeScopeGroupMap scopeGroups = tryBlock.getExecutionScopeGroups(mth);
final TryEdgeScopeGroupMap scopeGroups = tryBlock.getExecutionScopeGroups(mth); var fallthroughGroups = tryBlock.getHandlerFallthroughGroups(mth, scopeGroups);
final var fallthroughGroups = tryBlock.getHandlerFallthroughGroups(mth, scopeGroups); var handlerScopes = TryCatchEdgeBlockMap.getAllInScope(mth, tryBlock, scopeGroups, allHandler, fallthroughGroups);
final var handlerScopes = TryCatchEdgeBlockMap.getAllInScope(mth, tryBlock, scopeGroups, allHandler, fallthroughGroups);
return new TryExtractInfo(tryBlock, scopeGroups, allHandler, fallthroughGroups, handlerScopes); return new TryExtractInfo(tryBlock, scopeGroups, allHandler, fallthroughGroups, handlerScopes);
} }
@@ -214,14 +198,13 @@ public class MarkFinallyVisitor extends AbstractVisitor {
* @param tryInfo The try block information. * @param tryInfo The try block information.
* @return Whether a finally block has been successfully extracted. * @return Whether a finally block has been successfully extracted.
*/ */
private static boolean processTryBlock(final MethodNode mth, final TryExtractInfo tryInfo) { private static boolean processTryBlock(MethodNode mth, TryExtractInfo tryInfo) {
if (tryInfo.rethrowBlocks.isEmpty()) { if (tryInfo.rethrowBlocks.isEmpty()) {
return false; return false;
} }
if (extractFinally(mth, tryInfo)) { if (extractFinally(mth, tryInfo)) {
for (final BlockNode rethrowBlock : tryInfo.rethrowBlocks) { for (BlockNode rethrowBlock : tryInfo.rethrowBlocks) {
final InsnNode lastInsn = BlockUtils.getLastInsn(rethrowBlock); InsnNode lastInsn = BlockUtils.getLastInsn(rethrowBlock);
if (lastInsn == null) { if (lastInsn == null) {
continue; continue;
} }
@@ -232,30 +215,33 @@ public class MarkFinallyVisitor extends AbstractVisitor {
return false; return false;
} }
@Nullable private static @Nullable List<BlockNode> cutHandlerBlocks(MethodNode mth, TryExtractInfo tryInfo, ExceptionHandler handler) {
private static List<BlockNode> cutHandlerBlocks(final TryExtractInfo tryInfo, final ExceptionHandler handler) { BlockNode handlerBlock = handler.getHandlerBlock();
final BlockNode handlerBlock = handler.getHandlerBlock(); List<BlockNode> handlerBlocks = tryInfo.handlerScopes.getBlocksForHandler(handler);
final List<BlockNode> handlerBlocks = tryInfo.handlerScopes.getBlocksForHandler(handler);
if (handlerBlocks == null) { if (handlerBlocks == null) {
return null; return null;
} }
final InsnNode handlerFinalInsn = BlockUtils.getFirstInsn(handlerBlock); InsnNode handlerFinalInsn = BlockUtils.getFirstInsn(handlerBlock);
if (handlerFinalInsn != null && handlerFinalInsn.getType() == InsnType.MOVE_EXCEPTION) { if (handlerFinalInsn != null && handlerFinalInsn.getType() == InsnType.MOVE_EXCEPTION) {
handlerBlocks.remove(handlerBlock); // exclude block with 'move-exception' handlerBlocks.remove(handlerBlock); // exclude block with 'move-exception'
} }
final BlockNode bottomBlock = BlockUtils.getBottomBlock(handlerBlocks); BlockNode bottomBlock = BlockUtils.getBottomBlock(handlerBlocks);
final List<BlockNode> pathExits = BlockUtils.followEmptyUpPathWithinSet(bottomBlock, handlerBlocks); if (bottomBlock == null) {
mth.addWarn("Bottom block not found for handler: " + handler);
return handlerBlocks;
}
List<BlockNode> pathExits = BlockUtils.followEmptyUpPathWithinSet(bottomBlock, handlerBlocks);
if (pathExits.isEmpty()) { if (pathExits.isEmpty()) {
return handlerBlocks; return handlerBlocks;
} }
for (final BlockNode pathExit : pathExits) { for (BlockNode pathExit : pathExits) {
// For this to be able to extract a finally, we must ensure that all paths into the handler's logic // For this to be able to extract a finally, we must ensure that all paths into the handler's logic
// end with a THROW equal to the output of the move-exception instruction located at the start of // end with a THROW equal to the output of the move-exception instruction located at the start of
// this handler, if any. // this handler, if any.
final InsnNode bottomBlockLastInsn = BlockUtils.getLastInsn(pathExit); InsnNode bottomBlockLastInsn = BlockUtils.getLastInsn(pathExit);
final boolean isValidPathExit = bottomBlockLastInsn != null boolean isValidPathExit = bottomBlockLastInsn != null
&& handlerFinalInsn != null && handlerFinalInsn != null
&& bottomBlockLastInsn.getType() == InsnType.THROW && bottomBlockLastInsn.getType() == InsnType.THROW
&& bottomBlockLastInsn.getArgsCount() > 0 && bottomBlockLastInsn.getArgsCount() > 0
@@ -264,8 +250,8 @@ public class MarkFinallyVisitor extends AbstractVisitor {
return handlerBlocks; return handlerBlocks;
} }
} }
final List<BlockNode> cutHandlerBlocks = new ArrayList<>(handlerBlocks); List<BlockNode> cutHandlerBlocks = new ArrayList<>(handlerBlocks);
for (final BlockNode pathExit : pathExits) { for (BlockNode pathExit : pathExits) {
cutHandlerBlocks.remove(pathExit); cutHandlerBlocks.remove(pathExit);
removeEmptyUpPath(cutHandlerBlocks, pathExit); removeEmptyUpPath(cutHandlerBlocks, pathExit);
tryInfo.rethrowBlocks.add(pathExit); tryInfo.rethrowBlocks.add(pathExit);
@@ -279,7 +265,7 @@ public class MarkFinallyVisitor extends AbstractVisitor {
* @param cutHandlerBlocks The cut handler blocks of the all handler. * @param cutHandlerBlocks The cut handler blocks of the all handler.
* @return Whether the try block is implicit and has been removed. * @return Whether the try block is implicit and has been removed.
*/ */
private static boolean attemptRemoveImplicitHandlers(final List<BlockNode> cutHandlerBlocks, final TryExtractInfo tryInfo) { private static boolean attemptRemoveImplicitHandlers(List<BlockNode> cutHandlerBlocks, TryExtractInfo tryInfo) {
if (!(cutHandlerBlocks.isEmpty() || BlockUtils.isAllBlocksEmpty(cutHandlerBlocks))) { if (!(cutHandlerBlocks.isEmpty() || BlockUtils.isAllBlocksEmpty(cutHandlerBlocks))) {
return false; return false;
} }
@@ -291,24 +277,22 @@ public class MarkFinallyVisitor extends AbstractVisitor {
/** /**
* Search and mark common code from 'try' block and 'handlers'. * Search and mark common code from 'try' block and 'handlers'.
*/ */
private static boolean extractFinally(final MethodNode mth, final TryExtractInfo tryInfo) { private static boolean extractFinally(MethodNode mth, TryExtractInfo tryInfo) {
// Get all handlers from this and inner try blocks. // Get all handlers from this and inner try blocks.
final boolean hasInnerBlocks = !tryInfo.tryBlock.getInnerTryBlocks().isEmpty(); boolean hasInnerBlocks = !tryInfo.tryBlock.getInnerTryBlocks().isEmpty();
final List<ExceptionHandler> handlers = getHandlersForTryCatch(tryInfo.tryBlock); List<ExceptionHandler> handlers = getHandlersForTryCatch(tryInfo.tryBlock);
if (handlers.isEmpty()) { if (handlers.isEmpty()) {
return false; return false;
} }
Map<InsnNode, List<InsnNode>> insns = findCommonInsns(mth, tryInfo);
final Map<InsnNode, List<InsnNode>> insns = findCommonInsns(mth, tryInfo);
if (insns == null || insns.isEmpty()) { if (insns == null || insns.isEmpty()) {
return false; return false;
} }
Set<InsnNode> ignoredFinallyInsns = new HashSet<>();
final Set<InsnNode> ignoredFinallyInsns = new HashSet<>(); Set<InsnNode> ignoredCandidateInsns = new HashSet<>();
final Set<InsnNode> ignoredCandidateInsns = new HashSet<>(); Map<InsnNode, List<InsnNode>> insnMap = new HashMap<>();
final Map<InsnNode, List<InsnNode>> insnMap = new HashMap<>(); for (InsnNode finallyInsn : insns.keySet()) {
for (final InsnNode finallyInsn : insns.keySet()) { List<InsnNode> candidateInsns = insns.get(finallyInsn);
final List<InsnNode> candidateInsns = insns.get(finallyInsn);
// For an instruction to have matched, the number of times it has been found must be // For an instruction to have matched, the number of times it has been found must be
// equal to the number of edges that the exception handler has which aren't the // equal to the number of edges that the exception handler has which aren't the
@@ -323,17 +307,15 @@ public class MarkFinallyVisitor extends AbstractVisitor {
insnMap.put(finallyInsn, candidateInsns); insnMap.put(finallyInsn, candidateInsns);
} }
for (InsnNode finallyInsn : insnMap.keySet()) {
for (final InsnNode finallyInsn : insnMap.keySet()) {
finallyInsn.add(AFlag.FINALLY_INSNS); finallyInsn.add(AFlag.FINALLY_INSNS);
final List<InsnNode> candidateInsns = insnMap.get(finallyInsn); List<InsnNode> candidateInsns = insnMap.get(finallyInsn);
for (final InsnNode candidateInsn : candidateInsns) { for (InsnNode candidateInsn : candidateInsns) {
copyCodeVars(finallyInsn, candidateInsn); copyCodeVars(finallyInsn, candidateInsn);
candidateInsn.add(AFlag.DONT_GENERATE); candidateInsn.add(AFlag.DONT_GENERATE);
} }
} }
for (BlockNode finallyBlock : tryInfo.completeFinallyBlocks) {
for (final BlockNode finallyBlock : tryInfo.completeFinallyBlocks) {
if (ListUtils.anyMatch(finallyBlock.getInstructions(), ignoredFinallyInsns::contains)) { if (ListUtils.anyMatch(finallyBlock.getInstructions(), ignoredFinallyInsns::contains)) {
// If this block contains an instruction which was not found in all try edges, // If this block contains an instruction which was not found in all try edges,
// don't mark it as a finally block. // don't mark it as a finally block.
@@ -341,7 +323,7 @@ public class MarkFinallyVisitor extends AbstractVisitor {
} }
finallyBlock.add(AFlag.FINALLY_INSNS); finallyBlock.add(AFlag.FINALLY_INSNS);
} }
for (final BlockNode candidateBlock : tryInfo.completeCandidateBlocks) { for (BlockNode candidateBlock : tryInfo.completeCandidateBlocks) {
if (ListUtils.anyMatch(candidateBlock.getInstructions(), ignoredCandidateInsns::contains)) { if (ListUtils.anyMatch(candidateBlock.getInstructions(), ignoredCandidateInsns::contains)) {
// If this block contains an instruction which was found to "duplicate" a finally // If this block contains an instruction which was found to "duplicate" a finally
// instruction which was not found in all try edges, don't mark it as a duplicated // instruction which was not found in all try edges, don't mark it as a duplicated
@@ -353,15 +335,14 @@ public class MarkFinallyVisitor extends AbstractVisitor {
// If any scope has been merged with the fallthrough case of the try catch, don't merge inner trys. // If any scope has been merged with the fallthrough case of the try catch, don't merge inner trys.
// Otherwise, merge inner trys. // Otherwise, merge inner trys.
final boolean mergedFallthroughScope = boolean mergedFallthroughScope =
ListUtils.anyMatch(tryInfo.scopeGroups.getMergedScopes(), scopePair -> scopePair.getFirst().isNotHandlerExit()); ListUtils.anyMatch(tryInfo.scopeGroups.getMergedScopes(), scopePair -> scopePair.getFirst().isNotHandlerExit());
final boolean mergeInnerTryBlocks = hasInnerBlocks && !mergedFallthroughScope; boolean mergeInnerTryBlocks = hasInnerBlocks && !mergedFallthroughScope;
tryInfo.finallyHandler.setFinally(true); tryInfo.finallyHandler.setFinally(true);
if (mergeInnerTryBlocks) { if (mergeInnerTryBlocks) {
final List<TryCatchBlockAttr> innerTryBlocks = tryInfo.tryBlock.getInnerTryBlocks(); List<TryCatchBlockAttr> innerTryBlocks = tryInfo.tryBlock.getInnerTryBlocks();
for (final TryCatchBlockAttr innerTryBlock : innerTryBlocks) { for (TryCatchBlockAttr innerTryBlock : innerTryBlocks) {
tryInfo.tryBlock.getHandlers().addAll(innerTryBlock.getHandlers()); tryInfo.tryBlock.getHandlers().addAll(innerTryBlock.getHandlers());
tryInfo.tryBlock.getBlocks().addAll(innerTryBlock.getBlocks()); tryInfo.tryBlock.getBlocks().addAll(innerTryBlock.getBlocks());
innerTryBlock.setMerged(true); innerTryBlock.setMerged(true);
@@ -369,7 +350,6 @@ public class MarkFinallyVisitor extends AbstractVisitor {
tryInfo.tryBlock.setBlocks(ListUtils.distinctList(tryInfo.tryBlock.getBlocks())); tryInfo.tryBlock.setBlocks(ListUtils.distinctList(tryInfo.tryBlock.getBlocks()));
innerTryBlocks.clear(); innerTryBlocks.clear();
} }
return true; return true;
} }
@@ -380,13 +360,13 @@ public class MarkFinallyVisitor extends AbstractVisitor {
* @param tryBlock The source try block to get the list of exception handlers for * @param tryBlock The source try block to get the list of exception handlers for
* @return The list of exception handlers. * @return The list of exception handlers.
*/ */
private static List<ExceptionHandler> getHandlersForTryCatch(final TryCatchBlockAttr tryBlock) { private static List<ExceptionHandler> getHandlersForTryCatch(TryCatchBlockAttr tryBlock) {
final boolean hasInnerBlocks = !tryBlock.getInnerTryBlocks().isEmpty(); boolean hasInnerBlocks = !tryBlock.getInnerTryBlocks().isEmpty();
final List<ExceptionHandler> handlers; List<ExceptionHandler> handlers;
if (hasInnerBlocks) { if (hasInnerBlocks) {
// collect handlers from this and all inner blocks // collect handlers from this and all inner blocks
handlers = new ArrayList<>(tryBlock.getHandlers()); handlers = new ArrayList<>(tryBlock.getHandlers());
for (final TryCatchBlockAttr innerTryBlock : tryBlock.getInnerTryBlocks()) { for (TryCatchBlockAttr innerTryBlock : tryBlock.getInnerTryBlocks()) {
handlers.addAll(getHandlersForTryCatch(innerTryBlock)); handlers.addAll(getHandlersForTryCatch(innerTryBlock));
} }
} else { } else {
@@ -395,21 +375,21 @@ public class MarkFinallyVisitor extends AbstractVisitor {
return handlers; return handlers;
} }
@Nullable private static @Nullable Map<InsnNode, List<InsnNode>> findCommonInsns(MethodNode mth, TryExtractInfo tryInfo) {
private static Map<InsnNode, List<InsnNode>> findCommonInsns(final MethodNode mth, final TryExtractInfo tryInfo) { List<BlockNode> allHandlerBlocks = tryInfo.handlerScopes.getBlocksForHandler(tryInfo.finallyHandler);
final List<BlockNode> allHandlerBlocks = tryInfo.handlerScopes.getBlocksForHandler(tryInfo.finallyHandler); BlockNode finallyScopeTerminus = getTerminusForHandler(tryInfo.finallyHandler, tryInfo);
final BlockNode finallyScopeTerminus = getTerminusForHandler(tryInfo.finallyHandler, tryInfo); if (finallyScopeTerminus == null) {
return null;
final Map<InsnNode, List<InsnNode>> matchingInsns = new HashMap<>(); }
for (final TryEdge edge : tryInfo.handlerScopes.keySet()) { Map<InsnNode, List<InsnNode>> matchingInsns = new HashMap<>();
for (TryEdge edge : tryInfo.handlerScopes.keySet()) {
if (edge.isHandlerExit() && edge.getExceptionHandler() == tryInfo.finallyHandler) { if (edge.isHandlerExit() && edge.getExceptionHandler() == tryInfo.finallyHandler) {
continue; continue;
} }
List<BlockNode> handlerBlocks = tryInfo.handlerScopes.get(edge);
final List<BlockNode> handlerBlocks = tryInfo.handlerScopes.get(edge);
BlockNode scopeTerminus = null; BlockNode scopeTerminus = null;
for (final BlockNode edgeTerminusBlock : tryInfo.scopeTerminusGroups.keySet()) { for (BlockNode edgeTerminusBlock : tryInfo.scopeTerminusGroups.keySet()) {
final List<TryEdge> edgesWithTerminus = tryInfo.scopeTerminusGroups.get(edgeTerminusBlock); List<TryEdge> edgesWithTerminus = tryInfo.scopeTerminusGroups.get(edgeTerminusBlock);
if (edgesWithTerminus.contains(edge)) { if (edgesWithTerminus.contains(edge)) {
scopeTerminus = edgeTerminusBlock; scopeTerminus = edgeTerminusBlock;
break; break;
@@ -418,26 +398,24 @@ public class MarkFinallyVisitor extends AbstractVisitor {
if (scopeTerminus == null) { if (scopeTerminus == null) {
throw new JadxRuntimeException("Expected to find fallthrough terminus for handler " + edge); throw new JadxRuntimeException("Expected to find fallthrough terminus for handler " + edge);
} }
TraverserActivePathState comparatorState =
final TraverserActivePathState comparatorState =
new TraverserActivePathState(mth, new SameInstructionsStrategyImpl(), finallyScopeTerminus, new TraverserActivePathState(mth, new SameInstructionsStrategyImpl(), finallyScopeTerminus,
scopeTerminus, allHandlerBlocks, handlerBlocks); scopeTerminus, allHandlerBlocks, handlerBlocks);
final TraverserController controller = new TraverserController(); TraverserController controller = new TraverserController();
final List<TraverserActivePathState> pathResults; List<TraverserActivePathState> pathResults;
try { try {
pathResults = controller.process(comparatorState); pathResults = controller.process(comparatorState);
} catch (final TraverserException e) { } catch (TraverserException e) {
LOG.error("Could not search for finally duplicate instructions in path", e); LOG.error("Could not search for finally duplicate instructions in path", e);
return null; return null;
} }
Set<BlockNode> completeFinally = new HashSet<>();
final Set<BlockNode> completeFinally = new HashSet<>(); Set<BlockNode> completeCandidate = new HashSet<>();
final Set<BlockNode> completeCandidate = new HashSet<>(); for (TraverserActivePathState pathResult : pathResults) {
for (final TraverserActivePathState pathResult : pathResults) { for (Pair<InsnNode> matchingInsnPair : pathResult.getMatchedInsns()) {
for (final Pair<InsnNode> matchingInsnPair : pathResult.getMatchedInsns()) { InsnNode finallyInsn = matchingInsnPair.getFirst();
final InsnNode finallyInsn = matchingInsnPair.getFirst(); InsnNode candidateInsn = matchingInsnPair.getSecond();
final InsnNode candidateInsn = matchingInsnPair.getSecond(); List<InsnNode> candidateInsnsList;
final List<InsnNode> candidateInsnsList;
if (!matchingInsns.containsKey(finallyInsn)) { if (!matchingInsns.containsKey(finallyInsn)) {
candidateInsnsList = new LinkedList<>(); candidateInsnsList = new LinkedList<>();
matchingInsns.put(finallyInsn, candidateInsnsList); matchingInsns.put(finallyInsn, candidateInsnsList);
@@ -446,29 +424,25 @@ public class MarkFinallyVisitor extends AbstractVisitor {
} }
candidateInsnsList.add(candidateInsn); candidateInsnsList.add(candidateInsn);
} }
completeFinally.addAll(pathResult.getAllFullyMatchedFinallyBlocks()); completeFinally.addAll(pathResult.getAllFullyMatchedFinallyBlocks());
completeCandidate.addAll(pathResult.getAllFullyMatchedCandidateBlocks()); completeCandidate.addAll(pathResult.getAllFullyMatchedCandidateBlocks());
} }
if (tryInfo.completeFinallyBlocks == null) { if (tryInfo.completeFinallyBlocks == null) {
tryInfo.completeFinallyBlocks = completeFinally; tryInfo.completeFinallyBlocks = completeFinally;
} else { } else {
tryInfo.completeFinallyBlocks.retainAll(completeFinally); tryInfo.completeFinallyBlocks.retainAll(completeFinally);
} }
if (tryInfo.completeCandidateBlocks == null) { if (tryInfo.completeCandidateBlocks == null) {
tryInfo.completeCandidateBlocks = completeCandidate; tryInfo.completeCandidateBlocks = completeCandidate;
} else { } else {
tryInfo.completeCandidateBlocks.addAll(completeCandidate); tryInfo.completeCandidateBlocks.addAll(completeCandidate);
} }
} }
return matchingInsns; return matchingInsns;
} }
private static void removeEmptyUpPath(final List<BlockNode> handlerBlocks, final BlockNode startBlock) { private static void removeEmptyUpPath(List<BlockNode> handlerBlocks, BlockNode startBlock) {
for (final BlockNode pred : startBlock.getPredecessors()) { for (BlockNode pred : startBlock.getPredecessors()) {
if (pred.isEmpty()) { if (pred.isEmpty()) {
if (handlerBlocks.remove(pred) && !BlockUtils.isBackEdge(pred, startBlock)) { if (handlerBlocks.remove(pred) && !BlockUtils.isBackEdge(pred, startBlock)) {
removeEmptyUpPath(handlerBlocks, pred); removeEmptyUpPath(handlerBlocks, pred);
@@ -477,28 +451,28 @@ public class MarkFinallyVisitor extends AbstractVisitor {
} }
} }
private static void copyCodeVars(final InsnNode fromInsn, final InsnNode toInsn) { private static void copyCodeVars(InsnNode fromInsn, InsnNode toInsn) {
copyCodeVars(fromInsn.getResult(), toInsn.getResult()); copyCodeVars(fromInsn.getResult(), toInsn.getResult());
final int argsCount = fromInsn.getArgsCount(); int argsCount = fromInsn.getArgsCount();
for (int i = 0; i < argsCount; i++) { for (int i = 0; i < argsCount; i++) {
copyCodeVars(fromInsn.getArg(i), toInsn.getArg(i)); copyCodeVars(fromInsn.getArg(i), toInsn.getArg(i));
} }
} }
private static void copyCodeVars(final InsnArg fromArg, final InsnArg toArg) { private static void copyCodeVars(InsnArg fromArg, InsnArg toArg) {
if (fromArg == null || toArg == null if (fromArg == null || toArg == null
|| !fromArg.isRegister() || !toArg.isRegister()) { || !fromArg.isRegister() || !toArg.isRegister()) {
return; return;
} }
final SSAVar fromSsaVar = ((RegisterArg) fromArg).getSVar(); SSAVar fromSsaVar = ((RegisterArg) fromArg).getSVar();
final SSAVar toSsaVar = ((RegisterArg) toArg).getSVar(); SSAVar toSsaVar = ((RegisterArg) toArg).getSVar();
toSsaVar.setCodeVar(fromSsaVar.getCodeVar()); toSsaVar.setCodeVar(fromSsaVar.getCodeVar());
} }
/** /**
* Reload method without applying this visitor * Reload method without applying this visitor
*/ */
private static void undoFinallyVisitor(final MethodNode mth) { private static void undoFinallyVisitor(MethodNode mth) {
try { try {
// TODO: make more common and less hacky // TODO: make more common and less hacky
mth.unload(); mth.unload();
@@ -511,16 +485,15 @@ public class MarkFinallyVisitor extends AbstractVisitor {
} }
DepthTraversal.visit(visitor, mth); DepthTraversal.visit(visitor, mth);
} }
} catch (final DecodeException e) { } catch (DecodeException e) {
mth.addError("Undo finally extract failed", e); mth.addError("Undo finally extract failed", e);
} }
} }
@Nullable private static @Nullable BlockNode getTerminusForHandler(ExceptionHandler handler, TryExtractInfo tryInfo) {
private static BlockNode getTerminusForHandler(final ExceptionHandler handler, final TryExtractInfo tryInfo) { for (BlockNode terminus : tryInfo.scopeTerminusGroups.keySet()) {
for (final BlockNode terminus : tryInfo.scopeTerminusGroups.keySet()) { List<TryEdge> edgesWithTerminus = tryInfo.scopeTerminusGroups.get(terminus);
final List<TryEdge> edgesWithTerminus = tryInfo.scopeTerminusGroups.get(terminus); for (TryEdge edge : edgesWithTerminus) {
for (final TryEdge edge : edgesWithTerminus) {
if (edge.isNotHandlerExit()) { if (edge.isNotHandlerExit()) {
continue; continue;
} }
@@ -10,18 +10,18 @@ import jadx.core.dex.nodes.InsnNode;
public final class SameInstructionsStrategyImpl extends SameInstructionsStrategy { public final class SameInstructionsStrategyImpl extends SameInstructionsStrategy {
private static boolean sameDebugInfo(final RegisterArg dupReg, final RegisterArg fReg) { private static boolean sameDebugInfo(RegisterArg dupReg, RegisterArg fReg) {
final RegDebugInfoAttr fDbgInfo = fReg.get(AType.REG_DEBUG_INFO); RegDebugInfoAttr fDbgInfo = fReg.get(AType.REG_DEBUG_INFO);
final RegDebugInfoAttr dupDbgInfo = dupReg.get(AType.REG_DEBUG_INFO); RegDebugInfoAttr dupDbgInfo = dupReg.get(AType.REG_DEBUG_INFO);
if (fDbgInfo == null || dupDbgInfo == null) { if (fDbgInfo == null || dupDbgInfo == null) {
return false; return false;
} }
return dupDbgInfo.equals(fDbgInfo); return dupDbgInfo.equals(fDbgInfo);
} }
private static boolean assignInsnDifferent(final RegisterArg dupReg, final RegisterArg fReg) { private static boolean assignInsnDifferent(RegisterArg dupReg, RegisterArg fReg) {
final InsnNode assignInsn = fReg.getAssignInsn(); InsnNode assignInsn = fReg.getAssignInsn();
final InsnNode dupAssign = dupReg.getAssignInsn(); InsnNode dupAssign = dupReg.getAssignInsn();
if (assignInsn == null || dupAssign == null) { if (assignInsn == null || dupAssign == null) {
return true; return true;
} }
@@ -31,48 +31,45 @@ public final class SameInstructionsStrategyImpl extends SameInstructionsStrategy
if (assignInsn.isConstInsn() && dupAssign.isConstInsn()) { if (assignInsn.isConstInsn() && dupAssign.isConstInsn()) {
// Do this and not deep equals since we already know that the result is the same and that the insn // Do this and not deep equals since we already know that the result is the same and that the insn
// type is the same // type is the same
return !(Objects.equals(assignInsn.getArguments(), assignInsn.getArguments())); return !Objects.equals(assignInsn.getArguments(), assignInsn.getArguments());
} }
return false; return false;
} }
@Override @Override
public final boolean sameInsns(final InsnNode dupInsn, final InsnNode fInsn) { public boolean sameInsns(InsnNode dupInsn, InsnNode fInsn) {
if (!dupInsn.isSame(fInsn)) { if (!dupInsn.isSame(fInsn)) {
return false; return false;
} }
for (int i = 0; i < dupInsn.getArgsCount(); i++) { for (int i = 0; i < dupInsn.getArgsCount(); i++) {
final InsnArg dupArg = dupInsn.getArg(i); InsnArg dupArg = dupInsn.getArg(i);
final InsnArg fArg = fInsn.getArg(i); InsnArg fArg = fInsn.getArg(i);
if (!isSameArgs(dupArg, fArg)) { if (!isSameArgs(dupArg, fArg)) {
return false; return false;
} }
} }
return true; return true;
} }
@Override @Override
public final boolean isSameArgs(final InsnArg dupArg, final InsnArg fArg) { public boolean isSameArgs(InsnArg dupArg, InsnArg fArg) {
if (dupArg == null) { if (dupArg == null) {
return false; return false;
} }
final boolean isReg = dupArg.isRegister(); boolean isReg = dupArg.isRegister();
if (isReg != fArg.isRegister()) { if (isReg != fArg.isRegister()) {
return false; return false;
} }
if (isReg) { if (isReg) {
final RegisterArg dupReg = (RegisterArg) dupArg; RegisterArg dupReg = (RegisterArg) dupArg;
final RegisterArg fReg = (RegisterArg) fArg; RegisterArg fReg = (RegisterArg) fArg;
if (!dupReg.sameCodeVar(fReg) if (!dupReg.sameCodeVar(fReg)
&& !sameDebugInfo(dupReg, fReg) && !sameDebugInfo(dupReg, fReg)
&& assignInsnDifferent(dupReg, fReg)) { && assignInsnDifferent(dupReg, fReg)) {
return false; return false;
} }
} }
final boolean remConst = dupArg.isConst(); boolean remConst = dupArg.isConst();
if (remConst != fArg.isConst()) { if (remConst != fArg.isConst()) {
return false; return false;
} }
@@ -27,15 +27,14 @@ import jadx.core.utils.ListUtils;
*/ */
public final class TryCatchEdgeBlockMap implements Map<TryEdge, List<BlockNode>> { public final class TryCatchEdgeBlockMap implements Map<TryEdge, List<BlockNode>> {
public static boolean anyBlockHasNonImplicitTry(final List<BlockNode> blocks) { public static boolean anyBlockHasNonImplicitTry(List<BlockNode> blocks) {
final List<BlockNode> blocksWithTries = ListUtils.filter(blocks, blk -> blk.contains(AFlag.EXC_TOP_SPLITTER)); List<BlockNode> blocksWithTries = ListUtils.filter(blocks, blk -> blk.contains(AFlag.EXC_TOP_SPLITTER));
if (blocksWithTries.isEmpty()) { if (blocksWithTries.isEmpty()) {
return false; return false;
} }
for (BlockNode topSplitter : blocksWithTries) {
for (final BlockNode topSplitter : blocksWithTries) {
TryCatchBlockAttr block = null; TryCatchBlockAttr block = null;
for (final BlockNode topSplitterSuccessor : topSplitter.getCleanSuccessors()) { for (BlockNode topSplitterSuccessor : topSplitter.getCleanSuccessors()) {
if (topSplitterSuccessor.contains(AType.TRY_BLOCK)) { if (topSplitterSuccessor.contains(AType.TRY_BLOCK)) {
block = topSplitterSuccessor.get(AType.TRY_BLOCK); block = topSplitterSuccessor.get(AType.TRY_BLOCK);
} }
@@ -50,47 +49,42 @@ public final class TryCatchEdgeBlockMap implements Map<TryEdge, List<BlockNode>>
return false; return false;
} }
public static TryCatchEdgeBlockMap getAllInScope(final MethodNode mth, final TryCatchBlockAttr tryCatch, public static TryCatchEdgeBlockMap getAllInScope(MethodNode mth, TryCatchBlockAttr tryCatch,
final TryEdgeScopeGroupMap scopeGroups, final ExceptionHandler finallyHandler, TryEdgeScopeGroupMap scopeGroups, ExceptionHandler finallyHandler,
final Map<BlockNode, List<TryEdge>> scopeTerminusGroups) { Map<BlockNode, List<TryEdge>> scopeTerminusGroups) {
final Map<TryEdge, BlockNode> edgeBlocks = tryCatch.getEdgeBlockMap(mth); Map<TryEdge, BlockNode> edgeBlocks = tryCatch.getEdgeBlockMap();
TryCatchEdgeBlockMap result = new TryCatchEdgeBlockMap();
final TryCatchEdgeBlockMap result = new TryCatchEdgeBlockMap(); for (BlockNode scopeTerminus : scopeTerminusGroups.keySet()) {
for (final BlockNode scopeTerminus : scopeTerminusGroups.keySet()) { List<TryEdge> sourceEdges = scopeTerminusGroups.get(scopeTerminus);
final List<TryEdge> sourceEdges = scopeTerminusGroups.get(scopeTerminus); for (TryEdge sourceEdge : sourceEdges) {
for (final TryEdge sourceEdge : sourceEdges) { BlockNode edgeBlock = edgeBlocks.get(sourceEdge);
final BlockNode edgeBlock = edgeBlocks.get(sourceEdge); boolean useClean = !(sourceEdge.isNotHandlerExit()
final boolean useClean = !(sourceEdge.isNotHandlerExit()
&& ListUtils.anyMatch(scopeGroups.getMergedScopes(), pair -> pair.getSecond().isNotHandlerExit())); && ListUtils.anyMatch(scopeGroups.getMergedScopes(), pair -> pair.getSecond().isNotHandlerExit()));
List<BlockNode> allBlocks = List<BlockNode> allBlocks = BlockUtils.collectAllSuccessorsUntil(mth, edgeBlock, useClean, block -> block == scopeTerminus);
BlockUtils.collectAllSuccessorsUntil(mth, edgeBlock, useClean, (block) -> block == scopeTerminus); boolean anyBlockHasTry = anyBlockHasNonImplicitTry(allBlocks);
final boolean anyBlockHasTry = anyBlockHasNonImplicitTry(allBlocks);
if (anyBlockHasTry && useClean) { if (anyBlockHasTry && useClean) {
// If there's a try edge in the found blocks, collect all successors, not just clean successors. // If there's a try edge in the found blocks, collect all successors, not just clean successors.
allBlocks = BlockUtils.collectAllSuccessorsUntil(mth, edgeBlock, false, (block) -> block == scopeTerminus); allBlocks = BlockUtils.collectAllSuccessorsUntil(mth, edgeBlock, false, block -> block == scopeTerminus);
} }
if (sourceEdge.isNotHandlerExit()) { if (sourceEdge.isNotHandlerExit()) {
// If source edge is a fallthrough case, add the try body. // If source edge is a fallthrough case, add the try body.
allBlocks = new ArrayList<>(allBlocks); allBlocks = new ArrayList<>(allBlocks);
allBlocks.addAll(tryCatch.getBlocks()); allBlocks.addAll(tryCatch.getBlocks());
} }
result.put(sourceEdge, allBlocks); result.put(sourceEdge, allBlocks);
} }
} }
final List<BlockNode> finallyBlocks = result.getBlocksForHandler(finallyHandler); List<BlockNode> finallyBlocks = result.getBlocksForHandler(finallyHandler);
for (final TryEdge edge : result.keySet()) { if (finallyBlocks != null) {
if (edge.isHandlerExit() && edge.getExceptionHandler() == finallyHandler) { for (TryEdge edge : result.keySet()) {
continue; if (edge.isHandlerExit() && edge.getExceptionHandler() == finallyHandler) {
continue;
}
List<BlockNode> blocks = result.get(edge);
blocks.removeAll(finallyBlocks);
} }
final List<BlockNode> blocks = result.get(edge);
blocks.removeAll(finallyBlocks);
} }
return result; return result;
} }
@@ -101,82 +95,79 @@ public final class TryCatchEdgeBlockMap implements Map<TryEdge, List<BlockNode>>
} }
@Override @Override
public final void clear() { public void clear() {
underlying.clear(); underlying.clear();
} }
@Override @Override
public final boolean containsKey(Object key) { public boolean containsKey(Object key) {
return underlying.containsKey(key); return underlying.containsKey(key);
} }
@Override @Override
public final boolean containsValue(Object value) { public boolean containsValue(Object value) {
if (!(value instanceof TryEdge)) { if (!(value instanceof TryEdge)) {
return false; return false;
} }
TryEdge edge = (TryEdge) value;
final TryEdge edge = (TryEdge) value;
return underlying.containsKey(edge); return underlying.containsKey(edge);
} }
@Override @Override
public final Set<Entry<TryEdge, List<BlockNode>>> entrySet() { public Set<Entry<TryEdge, List<BlockNode>>> entrySet() {
return underlying.entrySet(); return underlying.entrySet();
} }
@Override @Override
public final List<BlockNode> get(Object key) { public List<BlockNode> get(Object key) {
return underlying.get(key); return underlying.get(key);
} }
@Override @Override
public final boolean isEmpty() { public boolean isEmpty() {
return underlying.isEmpty(); return underlying.isEmpty();
} }
@Override @Override
public final Set<TryEdge> keySet() { public Set<TryEdge> keySet() {
return underlying.keySet(); return underlying.keySet();
} }
@Override @Override
public final List<BlockNode> put(TryEdge key, List<BlockNode> value) { public List<BlockNode> put(TryEdge key, List<BlockNode> value) {
return underlying.put(key, value); return underlying.put(key, value);
} }
@Override @Override
public final void putAll(Map<? extends TryEdge, ? extends List<BlockNode>> otherMap) { public void putAll(Map<? extends TryEdge, ? extends List<BlockNode>> otherMap) {
underlying.putAll(otherMap); underlying.putAll(otherMap);
} }
@Override @Override
public final List<BlockNode> remove(Object key) { public List<BlockNode> remove(Object key) {
return underlying.remove(key); return underlying.remove(key);
} }
@Override @Override
public final int size() { public int size() {
return underlying.size(); return underlying.size();
} }
@Override @Override
public final Collection<List<BlockNode>> values() { public Collection<List<BlockNode>> values() {
return underlying.values(); return underlying.values();
} }
@Nullable @Nullable
public final List<BlockNode> getBlocksForHandler(final ExceptionHandler handler) { public List<BlockNode> getBlocksForHandler(ExceptionHandler handler) {
TryEdge edgeWithHandler = null; TryEdge edgeWithHandler = null;
for (final TryEdge edge : keySet()) { for (TryEdge edge : keySet()) {
if (edge.isNotHandlerExit()) { if (edge.isNotHandlerExit()) {
continue; continue;
} }
if (!edge.getExceptionHandler().equals(handler)) { if (!edge.getExceptionHandler().equals(handler)) {
continue; continue;
} }
edgeWithHandler = edge; edgeWithHandler = edge;
break; break;
} }
@@ -186,9 +177,9 @@ public final class TryCatchEdgeBlockMap implements Map<TryEdge, List<BlockNode>>
return get(edgeWithHandler); return get(edgeWithHandler);
} }
public final List<BlockNode> getBlocksForAllFallthroughs() { public List<BlockNode> getBlocksForAllFallthroughs() {
final List<BlockNode> blks = new ArrayList<>(); List<BlockNode> blks = new ArrayList<>();
for (final TryEdge edge : keySet()) { for (TryEdge edge : keySet()) {
if (edge.isHandlerExit()) { if (edge.isHandlerExit()) {
continue; continue;
} }
@@ -12,15 +12,15 @@ public final class GlobalTraverserSourceState {
private final Set<BlockNode> containedBlocks; private final Set<BlockNode> containedBlocks;
public GlobalTraverserSourceState(final Set<BlockNode> containedBlocks) { public GlobalTraverserSourceState(Set<BlockNode> containedBlocks) {
this.containedBlocks = containedBlocks; this.containedBlocks = containedBlocks;
} }
public final boolean isBlockContained(final BlockNode block) { public boolean isBlockContained(BlockNode block) {
return containedBlocks.contains(block); return containedBlocks.contains(block);
} }
public final Set<BlockNode> getContainedBlocks() { public Set<BlockNode> getContainedBlocks() {
return containedBlocks; return containedBlocks;
} }
} }
@@ -34,8 +34,8 @@ import jadx.core.utils.exceptions.JadxRuntimeException;
*/ */
public final class TraverserController { public final class TraverserController {
private static List<TraverserActivePathState> processHandlerImplementations(final TraverserActivePathState state, private static List<TraverserActivePathState> processHandlerImplementations(TraverserActivePathState state,
final AbstractBlockTraverserHandler handler) throws TraverserException { AbstractBlockTraverserHandler handler) throws TraverserException {
if (handler instanceof AbstractBlockPathTraverserHandler) { if (handler instanceof AbstractBlockPathTraverserHandler) {
((AbstractBlockPathTraverserHandler) handler).process(); ((AbstractBlockPathTraverserHandler) handler).process();
return List.of(state); return List.of(state);
@@ -53,7 +53,7 @@ public final class TraverserController {
this(null); this(null);
} }
public TraverserController(final @Nullable Function<TraverserState, Boolean> stateAbortCondition) { public TraverserController(@Nullable Function<TraverserState, Boolean> stateAbortCondition) {
this.stateAbortCondition = stateAbortCondition; this.stateAbortCondition = stateAbortCondition;
} }
@@ -70,30 +70,25 @@ public final class TraverserController {
* </ul> * </ul>
* This function will return a list of all of the different paths taken at the point of * This function will return a list of all of the different paths taken at the point of
* termination of each individual branch. * termination of each individual branch.
*
* @param state
* @return
*/ */
public final List<TraverserActivePathState> process(final TraverserActivePathState state) throws TraverserException { public List<TraverserActivePathState> process(TraverserActivePathState state) throws TraverserException {
TraverserActivePathState nextState = state; TraverserActivePathState nextState = state;
final AtomicReference<TraverserState> previousFinallyState = new AtomicReference<>(null); AtomicReference<TraverserState> previousFinallyState = new AtomicReference<>(null);
final AtomicReference<TraverserState> previousCandidateState = new AtomicReference<>(null); AtomicReference<TraverserState> previousCandidateState = new AtomicReference<>(null);
while (true) { while (true) {
final List<TraverserActivePathState> advancedStates = advance(nextState, previousFinallyState, previousCandidateState); List<TraverserActivePathState> advancedStates = advance(nextState, previousFinallyState, previousCandidateState);
if (advancedStates == null || advancedStates.isEmpty()) { if (advancedStates == null || advancedStates.isEmpty()) {
break; break;
} }
if (advancedStates.size() != 1) { if (advancedStates.size() != 1) {
final TraverserController nextController = new TraverserController(stateAbortCondition); TraverserController nextController = new TraverserController(stateAbortCondition);
final List<TraverserActivePathState> returnStates = new ArrayList<>(); List<TraverserActivePathState> returnStates = new ArrayList<>();
for (final TraverserActivePathState advancedState : advancedStates) { for (TraverserActivePathState advancedState : advancedStates) {
final List<TraverserActivePathState> childStates = nextController.process(advancedState); List<TraverserActivePathState> childStates = nextController.process(advancedState);
returnStates.addAll(childStates); returnStates.addAll(childStates);
} }
return returnStates; return returnStates;
} }
nextState = advancedStates.get(0); nextState = advancedStates.get(0);
} }
return List.of(nextState); return List.of(nextState);
@@ -101,42 +96,33 @@ public final class TraverserController {
/** /**
* Processes a singular traverser state once. * Processes a singular traverser state once.
*
* @param state
* @param previousFinallyState
* @param previousCandidateState
* @return
*/ */
public final List<TraverserActivePathState> advance(final TraverserActivePathState state, public List<TraverserActivePathState> advance(TraverserActivePathState state,
final AtomicReference<TraverserState> previousFinallyState, AtomicReference<TraverserState> previousFinallyState,
final AtomicReference<TraverserState> previousCandidateState) throws TraverserException { AtomicReference<TraverserState> previousCandidateState) throws TraverserException {
final TraverserGlobalCommonState commonState = state.getGlobalCommonState(); TraverserGlobalCommonState commonState = state.getGlobalCommonState();
final TraverserState finallyState = state.getFinallyState(); TraverserState finallyState = state.getFinallyState();
final TraverserState candidateState = state.getCandidateState(); TraverserState candidateState = state.getCandidateState();
if (previousFinallyState.get() == finallyState && previousCandidateState.get() == candidateState) { if (previousFinallyState.get() == finallyState && previousCandidateState.get() == candidateState) {
final TraverserStateFactory<TerminalTraverserState> finallyStateProducer = TraverserStateFactory<TerminalTraverserState> finallyStateProducer =
TerminalTraverserState.getFactory(TerminalTraverserState.TerminationReason.UNRESOLVABLE_STATES); TerminalTraverserState.getFactory(TerminalTraverserState.TerminationReason.UNRESOLVABLE_STATES);
final TraverserStateFactory<TerminalTraverserState> candidateStateProducer = TraverserStateFactory<TerminalTraverserState> candidateStateProducer =
TerminalTraverserState.getFactory(TerminalTraverserState.TerminationReason.UNRESOLVABLE_STATES); TerminalTraverserState.getFactory(TerminalTraverserState.TerminationReason.UNRESOLVABLE_STATES);
return List.of(TraverserActivePathState.produceFromFactories(state, finallyStateProducer, candidateStateProducer)); return List.of(TraverserActivePathState.produceFromFactories(state, finallyStateProducer, candidateStateProducer));
} }
previousFinallyState.set(finallyState); previousFinallyState.set(finallyState);
previousCandidateState.set(candidateState); previousCandidateState.set(candidateState);
if (finallyState.isTerminal() || candidateState.isTerminal()) { if (finallyState.isTerminal() || candidateState.isTerminal()) {
return null; return null;
} }
if (finallyState.getClass().equals(candidateState.getClass()) if (finallyState.getClass().equals(candidateState.getClass())
&& finallyState.getCompareState() == TraverserState.ComparisonState.READY_TO_COMPARE && finallyState.getCompareState() == TraverserState.ComparisonState.READY_TO_COMPARE
&& candidateState.getCompareState() == TraverserState.ComparisonState.READY_TO_COMPARE) { && candidateState.getCompareState() == TraverserState.ComparisonState.READY_TO_COMPARE) {
BlockNode finallyBlock;
final BlockNode finallyBlock; BlockNode candidateBlock;
final BlockNode candidateBlock; TraverserBlockInfo finallyBlockInfo = finallyState.getBlockInsnInfo();
final TraverserBlockInfo finallyBlockInfo = finallyState.getBlockInsnInfo(); TraverserBlockInfo candidateBlockInfo = candidateState.getBlockInsnInfo();
final TraverserBlockInfo candidateBlockInfo = candidateState.getBlockInsnInfo();
if (finallyBlockInfo != null && candidateBlockInfo != null) { if (finallyBlockInfo != null && candidateBlockInfo != null) {
finallyBlock = finallyBlockInfo.getBlock(); finallyBlock = finallyBlockInfo.getBlock();
candidateBlock = candidateBlockInfo.getBlock(); candidateBlock = candidateBlockInfo.getBlock();
@@ -144,46 +130,38 @@ public final class TraverserController {
finallyBlock = null; finallyBlock = null;
candidateBlock = null; candidateBlock = null;
} }
boolean isCached;
final boolean isCached;
if (finallyBlock != null && candidateBlock != null) { if (finallyBlock != null && candidateBlock != null) {
isCached = commonState.hasBlocksBeenCached(finallyBlock, candidateBlock); isCached = commonState.hasBlocksBeenCached(finallyBlock, candidateBlock);
} else { } else {
isCached = false; isCached = false;
} }
if (isCached) { if (isCached) {
final List<TraverserActivePathState> dupStates = commonState.getCachedStateFor(finallyBlock, candidateBlock); List<TraverserActivePathState> dupStates = commonState.getCachedStateFor(finallyBlock, candidateBlock);
final List<TraverserActivePathState> recoveredFromCacheStates = new ArrayList<>(dupStates.size()); List<TraverserActivePathState> recoveredFromCacheStates = new ArrayList<>(dupStates.size());
for (final TraverserActivePathState dupState : dupStates) { for (TraverserActivePathState dupState : dupStates) {
final TraverserState reusedFinallyState = dupState.getFinallyState(); TraverserState reusedFinallyState = dupState.getFinallyState();
final TraverserState reusedCandidateState = dupState.getCandidateState(); TraverserState reusedCandidateState = dupState.getCandidateState();
final TraverserStateFactory<?> finallyStateProducer = RecoveredFromCacheTraverserState.getFactory(reusedFinallyState); TraverserStateFactory<?> finallyStateProducer = RecoveredFromCacheTraverserState.getFactory(reusedFinallyState);
final TraverserStateFactory<?> candidateStateProducer = TraverserStateFactory<?> candidateStateProducer =
RecoveredFromCacheTraverserState.getFactory(reusedCandidateState); RecoveredFromCacheTraverserState.getFactory(reusedCandidateState);
final TraverserActivePathState recoveredFromCacheState = TraverserActivePathState recoveredFromCacheState =
TraverserActivePathState.produceFromFactories(state, finallyStateProducer, candidateStateProducer); TraverserActivePathState.produceFromFactories(state, finallyStateProducer, candidateStateProducer);
recoveredFromCacheState.mergeWith(dupStates); recoveredFromCacheState.mergeWith(dupStates);
recoveredFromCacheStates.add(recoveredFromCacheState); recoveredFromCacheStates.add(recoveredFromCacheState);
} }
return recoveredFromCacheStates; return recoveredFromCacheStates;
} }
AbstractBlockTraverserHandler handler = candidateState.getNextHandler();
final AbstractBlockTraverserHandler handler = candidateState.getNextHandler(); return processHandlerImplementations(state, handler);
final List<TraverserActivePathState> resultingStates = processHandlerImplementations(state, handler);
return resultingStates;
} }
boolean hasReadyToCompare = finallyState.getCompareState() == TraverserState.ComparisonState.READY_TO_COMPARE
final boolean hasReadyToCompare = finallyState.getCompareState() == TraverserState.ComparisonState.READY_TO_COMPARE
|| candidateState.getCompareState() == TraverserState.ComparisonState.READY_TO_COMPARE; || candidateState.getCompareState() == TraverserState.ComparisonState.READY_TO_COMPARE;
boolean finallyStateAborted = advanceSingleState(state, finallyState, hasReadyToCompare);
final boolean finallyStateAborted = advanceSingleState(state, finallyState, hasReadyToCompare); boolean candidateStateAborted = advanceSingleState(state, candidateState, hasReadyToCompare);
final boolean candidateStateAborted = advanceSingleState(state, candidateState, hasReadyToCompare);
if (finallyStateAborted && candidateStateAborted) { if (finallyStateAborted && candidateStateAborted) {
return null; return null;
} }
return List.of(state); return List.of(state);
} }
@@ -192,15 +170,15 @@ public final class TraverserController {
* *
* @return Whether this state has been aborted by the state abort function. * @return Whether this state has been aborted by the state abort function.
*/ */
private boolean advanceSingleState(final TraverserActivePathState activePathState, final TraverserState singleState, private boolean advanceSingleState(TraverserActivePathState activePathState, TraverserState singleState,
final boolean hasReadyToCompare) throws TraverserException { boolean hasReadyToCompare) throws TraverserException {
final boolean stateAborted = stateAbortCondition != null && stateAbortCondition.apply(singleState); boolean stateAborted = stateAbortCondition != null && stateAbortCondition.apply(singleState);
if (stateAbortCondition == null || !stateAborted) { if (stateAbortCondition == null || !stateAborted) {
if (singleState.getCompareState() == TraverserState.ComparisonState.NOT_READY if (singleState.getCompareState() == TraverserState.ComparisonState.NOT_READY
|| (singleState.getCompareState() == TraverserState.ComparisonState.AWAITING_OPTIONAL_PREDECESSOR_MERGE || singleState.getCompareState() == TraverserState.ComparisonState.AWAITING_OPTIONAL_PREDECESSOR_MERGE
&& hasReadyToCompare)) { && hasReadyToCompare) {
final AbstractBlockTraverserHandler handler = singleState.getNextHandler(); AbstractBlockTraverserHandler handler = singleState.getNextHandler();
final List<TraverserActivePathState> results = processHandlerImplementations(activePathState, handler); List<TraverserActivePathState> results = processHandlerImplementations(activePathState, handler);
if (results.size() != 1 || results.get(0) != activePathState) { if (results.size() != 1 || results.get(0) != activePathState) {
throw new JadxRuntimeException("A traverser handler which was not expected to change path states actually did"); throw new JadxRuntimeException("A traverser handler which was not expected to change path states actually did");
} }
@@ -2,7 +2,7 @@ package jadx.core.dex.visitors.finaly.traverser;
public class TraverserException extends Exception { public class TraverserException extends Exception {
public TraverserException(final String msg) { public TraverserException(String msg) {
super(msg); super(msg);
} }
} }
@@ -5,17 +5,17 @@ import jadx.core.dex.visitors.finaly.traverser.state.TraverserState;
import jadx.core.utils.exceptions.JadxRuntimeException; import jadx.core.utils.exceptions.JadxRuntimeException;
public final class DuplicatedTraverserStateFactory<T extends TraverserState> extends TraverserStateFactory<T> { public final class DuplicatedTraverserStateFactory<T extends TraverserState> extends TraverserStateFactory<T> {
private final T baseState; private final T baseState;
public DuplicatedTraverserStateFactory(final T baseState) { public DuplicatedTraverserStateFactory(T baseState) {
this.baseState = baseState; this.baseState = baseState;
} }
@SuppressWarnings("unchecked")
@Override @Override
public final T generateInternalState(final TraverserActivePathState state) { public T generateInternalState(TraverserActivePathState state) {
final Class<? extends T> baseStateClass = (Class<? extends T>) baseState.getClass(); Class<? extends T> baseStateClass = (Class<? extends T>) baseState.getClass();
final TraverserState duplicated = baseState.duplicate(state); TraverserState duplicated = baseState.duplicate(state);
if (!baseStateClass.isInstance(duplicated)) { if (!baseStateClass.isInstance(duplicated)) {
throw new JadxRuntimeException( throw new JadxRuntimeException(
"A state of class " + baseState.getClass() + " has duplicated to produce a class of " + duplicated.getClass()); "A state of class " + baseState.getClass() + " has duplicated to produce a class of " + duplicated.getClass());
@@ -5,10 +5,9 @@ import jadx.core.dex.visitors.finaly.traverser.state.TraverserState;
public abstract class TraverserStateFactory<T extends TraverserState> { public abstract class TraverserStateFactory<T extends TraverserState> {
protected abstract T generateInternalState(final TraverserActivePathState state); protected abstract T generateInternalState(TraverserActivePathState state);
public final T generateState(final TraverserActivePathState state) { public final T generateState(TraverserActivePathState state) {
final T generatedState = generateInternalState(state); return generateInternalState(state);
return generatedState;
} }
} }
@@ -9,7 +9,7 @@ public abstract class AbstractActivePathTraverserHandler extends AbstractBlockTr
private final TraverserActivePathState comparatorState; private final TraverserActivePathState comparatorState;
public AbstractActivePathTraverserHandler(final TraverserActivePathState comparatorState) { public AbstractActivePathTraverserHandler(TraverserActivePathState comparatorState) {
this.comparatorState = comparatorState; this.comparatorState = comparatorState;
} }
@@ -14,11 +14,11 @@ public abstract class AbstractBlockPathTraverserHandler extends AbstractBlockTra
private final AtomicReference<? extends TraverserState> stateRef; private final AtomicReference<? extends TraverserState> stateRef;
public AbstractBlockPathTraverserHandler(final TraverserState initialState) { public AbstractBlockPathTraverserHandler(TraverserState initialState) {
this.stateRef = new AtomicReference<>(initialState); this.stateRef = new AtomicReference<>(initialState);
} }
public AbstractBlockPathTraverserHandler(final AtomicReference<? extends TraverserState> initialStateRef) { public AbstractBlockPathTraverserHandler(AtomicReference<? extends TraverserState> initialStateRef) {
this.stateRef = initialStateRef; this.stateRef = initialStateRef;
} }
@@ -12,34 +12,30 @@ import jadx.core.utils.exceptions.JadxRuntimeException;
public class BaseBlockTraverserHandler extends AbstractBlockPathTraverserHandler { public class BaseBlockTraverserHandler extends AbstractBlockPathTraverserHandler {
public BaseBlockTraverserHandler(final TraverserState initialState) { public BaseBlockTraverserHandler(TraverserState initialState) {
super(initialState); super(initialState);
} }
public BaseBlockTraverserHandler(final AtomicReference<TraverserState> initialStateRef) { public BaseBlockTraverserHandler(AtomicReference<TraverserState> initialStateRef) {
super(initialStateRef); super(initialStateRef);
} }
@Override @Override
protected void handle() { protected void handle() {
final TraverserBlockInfo blockInsnInfo = getState().getBlockInsnInfo(); TraverserBlockInfo blockInsnInfo = getState().getBlockInsnInfo();
if (blockInsnInfo == null) { if (blockInsnInfo == null) {
throw new JadxRuntimeException("Expected to find block info within " + getClass().getSimpleName()); throw new JadxRuntimeException("Expected to find block info within " + getClass().getSimpleName());
} }
TraverserActivePathState comparator = getState().getComparatorState();
final TraverserActivePathState comparator = getState().getComparatorState(); AtomicReference<TraverserState> stateRef = comparator.getReferenceForState(getState());
final AtomicReference<TraverserState> stateRef = comparator.getReferenceForState(getState());
if (stateRef == null) { if (stateRef == null) {
throw new JadxRuntimeException("Orphaned traverser state"); throw new JadxRuntimeException("Orphaned traverser state");
} }
BlockNode block = blockInsnInfo.getBlock();
final BlockNode block = blockInsnInfo.getBlock(); ImplicitInsnBlockTraverserVisitor implicitVisitor = new ImplicitInsnBlockTraverserVisitor(getState());
TraverserState stateAfterImplicit = implicitVisitor.visit(block);
final ImplicitInsnBlockTraverserVisitor implicitVisitor = new ImplicitInsnBlockTraverserVisitor(getState()); PathEndBlockTraverserVisitor pathEndVisitor = new PathEndBlockTraverserVisitor(stateAfterImplicit);
final TraverserState stateAfterImplicit = implicitVisitor.visit(block); TraverserState nextState = pathEndVisitor.visit(block);
final PathEndBlockTraverserVisitor pathEndVisitor = new PathEndBlockTraverserVisitor(stateAfterImplicit);
final TraverserState nextState = pathEndVisitor.visit(block);
stateRef.set(nextState); stateRef.set(nextState);
} }
@@ -13,36 +13,34 @@ import jadx.core.dex.visitors.finaly.traverser.visitors.comparator.InstructionBl
public final class InstructionActivePathTraverserHandler extends AbstractActivePathTraverserHandler { public final class InstructionActivePathTraverserHandler extends AbstractActivePathTraverserHandler {
public static final class UnresolvableBlockException extends TraverserException { public static final class UnresolvableBlockException extends TraverserException {
public UnresolvableBlockException(final BlockNode block, final String reason) { public UnresolvableBlockException(BlockNode block, String reason) {
super("A block, " + block.toString() + ", could not have instructions compared.\n\t" + reason); super("A block, " + block.toString() + ", could not have instructions compared.\n\t" + reason);
} }
} }
public InstructionActivePathTraverserHandler(final TraverserActivePathState state) { public InstructionActivePathTraverserHandler(TraverserActivePathState state) {
super(state); super(state);
} }
@Override @Override
protected List<TraverserActivePathState> handle() throws TraverserException { protected List<TraverserActivePathState> handle() throws TraverserException {
final TraverserActivePathState comparator = getComparator(); TraverserActivePathState comparator = getComparator();
final TraverserGlobalCommonState commonState = comparator.getGlobalCommonState(); TraverserGlobalCommonState commonState = comparator.getGlobalCommonState();
final TraverserState finallyState = comparator.getFinallyState(); TraverserState finallyState = comparator.getFinallyState();
final TraverserState candidateState = comparator.getCandidateState(); TraverserState candidateState = comparator.getCandidateState();
final TraverserBlockInfo finallyBlockInfo = finallyState.getBlockInsnInfo(); TraverserBlockInfo finallyBlockInfo = finallyState.getBlockInsnInfo();
final TraverserBlockInfo candidateBlockInfo = candidateState.getBlockInsnInfo(); TraverserBlockInfo candidateBlockInfo = candidateState.getBlockInsnInfo();
final BlockNode finallyBlock = finallyBlockInfo.getBlock(); BlockNode finallyBlock = finallyBlockInfo.getBlock();
final BlockNode candidateBlock = candidateBlockInfo.getBlock(); BlockNode candidateBlock = candidateBlockInfo.getBlock();
final InstructionBlockComparatorTraverserVisitor visitor = new InstructionBlockComparatorTraverserVisitor(); InstructionBlockComparatorTraverserVisitor visitor = new InstructionBlockComparatorTraverserVisitor();
final TraverserActivePathState newState = visitor.visit(comparator); TraverserActivePathState newState = visitor.visit(comparator);
if (finallyBlock != null && candidateBlock != null) { if (finallyBlock != null && candidateBlock != null) {
commonState.addCachedStateFor(finallyBlock, candidateBlock, List.of(newState)); commonState.addCachedStateFor(finallyBlock, candidateBlock, List.of(newState));
} }
return List.of(newState); return List.of(newState);
} }
} }
@@ -27,17 +27,17 @@ import jadx.core.utils.exceptions.JadxRuntimeException;
public final class MergePathActivePathTraverserHandler extends AbstractActivePathTraverserHandler { public final class MergePathActivePathTraverserHandler extends AbstractActivePathTraverserHandler {
private static TraverserActivePathState createNonMatchingTerminator(final TraverserActivePathState state) { private static TraverserActivePathState createNonMatchingTerminator(TraverserActivePathState state) {
final TraverserStateFactory<TerminalTraverserState> finallyStateFactory = TraverserStateFactory<TerminalTraverserState> finallyStateFactory =
TerminalTraverserState.getFactory(TerminalTraverserState.TerminationReason.NON_MATCHING_PATHS); TerminalTraverserState.getFactory(TerminalTraverserState.TerminationReason.NON_MATCHING_PATHS);
final TraverserStateFactory<TerminalTraverserState> candidateStateFactory = TraverserStateFactory<TerminalTraverserState> candidateStateFactory =
TerminalTraverserState.getFactory(TerminalTraverserState.TerminationReason.NON_MATCHING_PATHS); TerminalTraverserState.getFactory(TerminalTraverserState.TerminationReason.NON_MATCHING_PATHS);
return TraverserActivePathState.produceFromFactories(state, finallyStateFactory, candidateStateFactory); return TraverserActivePathState.produceFromFactories(state, finallyStateFactory, candidateStateFactory);
} }
private static boolean isStateOnTerminus(final TraverserState state, final BlockNode terminus) { private static boolean isStateOnTerminus(TraverserState state, BlockNode terminus) {
final TraverserBlockInfo blockInfo = state.getBlockInsnInfo(); TraverserBlockInfo blockInfo = state.getBlockInsnInfo();
if (blockInfo == null) { if (blockInfo == null) {
return false; return false;
} }
@@ -45,12 +45,12 @@ public final class MergePathActivePathTraverserHandler extends AbstractActivePat
} }
private static Function<TraverserState, Boolean> getStateAbortOnTerminusFunction( private static Function<TraverserState, Boolean> getStateAbortOnTerminusFunction(
final IdentifiedScopeWithTerminatorTraverserState finallyState, IdentifiedScopeWithTerminatorTraverserState finallyState,
final IdentifiedScopeWithTerminatorTraverserState candidateState) { IdentifiedScopeWithTerminatorTraverserState candidateState) {
final BlockNode finallyTerminus = finallyState.getTerminus(); BlockNode finallyTerminus = finallyState.getTerminus();
final BlockNode candidateTerminus = candidateState.getTerminus(); BlockNode candidateTerminus = candidateState.getTerminus();
final GlobalTraverserSourceState finallyGlobalState = finallyState.getGlobalState(); GlobalTraverserSourceState finallyGlobalState = finallyState.getGlobalState();
final GlobalTraverserSourceState candidateGlobalState = candidateState.getGlobalState(); GlobalTraverserSourceState candidateGlobalState = candidateState.getGlobalState();
return (final TraverserState state) -> { return (final TraverserState state) -> {
if (state.getGlobalState() == finallyGlobalState) { if (state.getGlobalState() == finallyGlobalState) {
@@ -63,25 +63,24 @@ public final class MergePathActivePathTraverserHandler extends AbstractActivePat
}; };
} }
private static PostMergeStatus getScopeSplitPostMergeStatus(final List<TraverserActivePathState> pathsTaken) { private static PostMergeStatus getScopeSplitPostMergeStatus(List<TraverserActivePathState> pathsTaken) {
// If the scope split is the same, all branches must not end in a terminator. // If the scope split is the same, all branches must not end in a terminator.
PostMergeStatus status = new PostMergeStatus();
final PostMergeStatus status = new PostMergeStatus(); for (TraverserActivePathState path : pathsTaken) {
for (final TraverserActivePathState path : pathsTaken) { TraverserState finallyState;
final TraverserState finallyState; TraverserState candidateState;
final TraverserState candidateState;
if (path.getFinallyState().isTerminal() || path.getCandidateState().isTerminal()) { if (path.getFinallyState().isTerminal() || path.getCandidateState().isTerminal()) {
final TraverserState rawFinallyState = path.getFinallyState(); TraverserState rawFinallyState = path.getFinallyState();
final TraverserState rawCandidateState = path.getCandidateState(); TraverserState rawCandidateState = path.getCandidateState();
final boolean finallyIsCached = rawFinallyState instanceof RecoveredFromCacheTraverserState; boolean finallyIsCached = rawFinallyState instanceof RecoveredFromCacheTraverserState;
final boolean candidateIsCached = rawCandidateState instanceof RecoveredFromCacheTraverserState; boolean candidateIsCached = rawCandidateState instanceof RecoveredFromCacheTraverserState;
if (!(finallyIsCached && candidateIsCached)) { if (!(finallyIsCached && candidateIsCached)) {
status.perfectMatch = false; status.perfectMatch = false;
continue; continue;
} }
final RecoveredFromCacheTraverserState finallyCachedState = (RecoveredFromCacheTraverserState) rawFinallyState; RecoveredFromCacheTraverserState finallyCachedState = (RecoveredFromCacheTraverserState) rawFinallyState;
final RecoveredFromCacheTraverserState candidateCachedState = (RecoveredFromCacheTraverserState) rawCandidateState; RecoveredFromCacheTraverserState candidateCachedState = (RecoveredFromCacheTraverserState) rawCandidateState;
if (finallyCachedState.canContinue() || candidateCachedState.canContinue()) { if (finallyCachedState.canContinue() || candidateCachedState.canContinue()) {
status.perfectMatch = false; status.perfectMatch = false;
continue; continue;
@@ -92,20 +91,17 @@ public final class MergePathActivePathTraverserHandler extends AbstractActivePat
finallyState = path.getFinallyState(); finallyState = path.getFinallyState();
candidateState = path.getCandidateState(); candidateState = path.getCandidateState();
} }
CentralityState finallyCentralityState = finallyState.getCentralityState();
final CentralityState finallyCentralityState = finallyState.getCentralityState(); CentralityState candidateCentralityState = candidateState.getCentralityState();
final CentralityState candidateCentralityState = candidateState.getCentralityState();
status.finallyAllowsCentral &= finallyCentralityState.getAllowsCentral(); status.finallyAllowsCentral &= finallyCentralityState.getAllowsCentral();
status.candidateAllowsCentral &= candidateCentralityState.getAllowsCentral(); status.candidateAllowsCentral &= candidateCentralityState.getAllowsCentral();
status.finallyAllowableOutputs.addAll(finallyCentralityState.getAllowableOutputArguments()); status.finallyAllowableOutputs.addAll(finallyCentralityState.getAllowableOutputArguments());
status.candidateAllowableOutputs.addAll(candidateCentralityState.getAllowableOutputArguments()); status.candidateAllowableOutputs.addAll(candidateCentralityState.getAllowableOutputArguments());
} }
return status; return status;
} }
private static final class PostMergeStatus { private static final class PostMergeStatus {
public final Set<RegisterArg> finallyAllowableOutputs = new HashSet<>(); public final Set<RegisterArg> finallyAllowableOutputs = new HashSet<>();
public final Set<RegisterArg> candidateAllowableOutputs = new HashSet<>(); public final Set<RegisterArg> candidateAllowableOutputs = new HashSet<>();
public boolean finallyAllowsCentral; public boolean finallyAllowsCentral;
@@ -113,54 +109,54 @@ public final class MergePathActivePathTraverserHandler extends AbstractActivePat
public boolean perfectMatch = true; public boolean perfectMatch = true;
} }
public MergePathActivePathTraverserHandler(final TraverserActivePathState comparatorState) { public MergePathActivePathTraverserHandler(TraverserActivePathState comparatorState) {
super(comparatorState); super(comparatorState);
} }
@Override @Override
protected final List<TraverserActivePathState> handle() { protected List<TraverserActivePathState> handle() {
final TraverserActivePathState comparator = getComparator().duplicate(); TraverserActivePathState comparator = getComparator().duplicate();
final TraverserGlobalCommonState commonState = comparator.getGlobalCommonState(); TraverserGlobalCommonState commonState = comparator.getGlobalCommonState();
final IdentifiedScopeWithTerminatorTraverserState finallyState = IdentifiedScopeWithTerminatorTraverserState finallyState =
(IdentifiedScopeWithTerminatorTraverserState) comparator.getFinallyState(); (IdentifiedScopeWithTerminatorTraverserState) comparator.getFinallyState();
final IdentifiedScopeWithTerminatorTraverserState candidateState = IdentifiedScopeWithTerminatorTraverserState candidateState =
(IdentifiedScopeWithTerminatorTraverserState) comparator.getCandidateState(); (IdentifiedScopeWithTerminatorTraverserState) comparator.getCandidateState();
final BlockNode finallyTerminus = finallyState.getTerminus(); BlockNode finallyTerminus = finallyState.getTerminus();
final BlockNode candidateTerminus = candidateState.getTerminus(); BlockNode candidateTerminus = candidateState.getTerminus();
final Function<TraverserState, Boolean> abortFunction = getStateAbortOnTerminusFunction(finallyState, candidateState); Function<TraverserState, Boolean> abortFunction = getStateAbortOnTerminusFunction(finallyState, candidateState);
final List<BlockNode[]> allPermutationsPaths = getAllPermutationsOfCollection(candidateState.getRoots()); List<BlockNode[]> allPermutationsPaths = getAllPermutationsOfCollection(candidateState.getRoots());
List<TraverserActivePathState> paths = null; List<TraverserActivePathState> paths = null;
PostMergeStatus postMerge = null; PostMergeStatus postMerge = null;
for (final BlockNode[] candidateRootsPermutation : allPermutationsPaths) { for (BlockNode[] candidateRootsPermutation : allPermutationsPaths) {
final List<TraverserActivePathState> traversalPaths = new ArrayList<>(); List<TraverserActivePathState> traversalPaths = new ArrayList<>();
for (int i = 0; i < finallyState.getRoots().size(); i++) { for (int i = 0; i < finallyState.getRoots().size(); i++) {
final var finallyRoot = finallyState.getRoots().get(i); var finallyRoot = finallyState.getRoots().get(i);
final var candidateRoot = candidateRootsPermutation[i]; var candidateRoot = candidateRootsPermutation[i];
final var finallyCentrality = finallyState.getCentralityState().duplicate(); var finallyCentrality = finallyState.getCentralityState().duplicate();
final var candidateCentrality = candidateState.getCentralityState().duplicate(); var candidateCentrality = candidateState.getCentralityState().duplicate();
final var finallyBlockInfo = new TraverserBlockInfo(finallyRoot); var finallyBlockInfo = new TraverserBlockInfo(finallyRoot);
final var candidateBlockInfo = new TraverserBlockInfo(candidateRoot); var candidateBlockInfo = new TraverserBlockInfo(candidateRoot);
final var finallyStateFactory = NewBlockTraverserState.getFactory(finallyCentrality, finallyBlockInfo); var finallyStateFactory = NewBlockTraverserState.getFactory(finallyCentrality, finallyBlockInfo);
final var candidateStateFactory = NewBlockTraverserState.getFactory(candidateCentrality, candidateBlockInfo); var candidateStateFactory = NewBlockTraverserState.getFactory(candidateCentrality, candidateBlockInfo);
final var newState = TraverserActivePathState.produceFromFactories(comparator, finallyStateFactory, candidateStateFactory); var newState = TraverserActivePathState.produceFromFactories(comparator, finallyStateFactory, candidateStateFactory);
traversalPaths.add(newState); traversalPaths.add(newState);
} }
final List<TraverserActivePathState> currentPaths = new ArrayList<>(); List<TraverserActivePathState> currentPaths = new ArrayList<>();
boolean errorOccurred = false; boolean errorOccurred = false;
for (final TraverserActivePathState pathState : traversalPaths) { for (TraverserActivePathState pathState : traversalPaths) {
final TraverserController branchController = new TraverserController(abortFunction); TraverserController branchController = new TraverserController(abortFunction);
final List<TraverserActivePathState> out; List<TraverserActivePathState> out;
try { try {
out = branchController.process(pathState); out = branchController.process(pathState);
} catch (final TraverserException e) { } catch (TraverserException e) {
errorOccurred = true; errorOccurred = true;
break; break;
} }
@@ -175,57 +171,53 @@ public final class MergePathActivePathTraverserHandler extends AbstractActivePat
// If the finally terminus and candidate terminus have been cached at this stage, it means that a // If the finally terminus and candidate terminus have been cached at this stage, it means that a
// path that we searched evaluated the two termini. At this point, we can ignore a non-perfect // path that we searched evaluated the two termini. At this point, we can ignore a non-perfect
// match if the path could continue from the point of the termini. // match if the path could continue from the point of the termini.
final boolean hasTerminusBeenEvaluatedInPaths = commonState.hasBlocksBeenCached(finallyTerminus, candidateTerminus); boolean hasTerminusBeenEvaluatedInPaths = commonState.hasBlocksBeenCached(finallyTerminus, candidateTerminus);
final PostMergeStatus currentPostMerge = getScopeSplitPostMergeStatus(currentPaths); PostMergeStatus currentPostMerge = getScopeSplitPostMergeStatus(currentPaths);
if (!currentPostMerge.perfectMatch && !hasTerminusBeenEvaluatedInPaths) { if (!currentPostMerge.perfectMatch && !hasTerminusBeenEvaluatedInPaths) {
// No match // No match
continue; continue;
} }
paths = currentPaths; paths = currentPaths;
postMerge = currentPostMerge; postMerge = currentPostMerge;
break; break;
} }
if (paths == null || postMerge == null) { if (paths == null || postMerge == null) {
final TraverserActivePathState nonMatchingState = createNonMatchingTerminator(comparator); TraverserActivePathState nonMatchingState = createNonMatchingTerminator(comparator);
return List.of(nonMatchingState); return List.of(nonMatchingState);
} }
CentralityState newFinallyCentralityState = finallyState.getCentralityState().duplicate();
final CentralityState newFinallyCentralityState = finallyState.getCentralityState().duplicate();
newFinallyCentralityState.setAllowsCentral(postMerge.finallyAllowsCentral); newFinallyCentralityState.setAllowsCentral(postMerge.finallyAllowsCentral);
newFinallyCentralityState.addAllowableOutputs(postMerge.finallyAllowableOutputs); newFinallyCentralityState.addAllowableOutputs(postMerge.finallyAllowableOutputs);
final CentralityState newCandidateCentralityState = candidateState.getCentralityState().duplicate(); CentralityState newCandidateCentralityState = candidateState.getCentralityState().duplicate();
newCandidateCentralityState.setAllowsCentral(postMerge.candidateAllowsCentral); newCandidateCentralityState.setAllowsCentral(postMerge.candidateAllowsCentral);
newCandidateCentralityState.addAllowableOutputs(postMerge.candidateAllowableOutputs); newCandidateCentralityState.addAllowableOutputs(postMerge.candidateAllowableOutputs);
final TraverserBlockInfo finallyTerminusBlockInfo = new TraverserBlockInfo(finallyState.getTerminus()); TraverserBlockInfo finallyTerminusBlockInfo = new TraverserBlockInfo(finallyState.getTerminus());
final TraverserBlockInfo candidateTerminusBlockInfo = new TraverserBlockInfo(candidateState.getTerminus()); TraverserBlockInfo candidateTerminusBlockInfo = new TraverserBlockInfo(candidateState.getTerminus());
final TraverserStateFactory<NewBlockTraverserState> finallyStateFactory = TraverserStateFactory<NewBlockTraverserState> finallyStateFactory =
NewBlockTraverserState.getFactory(newFinallyCentralityState, finallyTerminusBlockInfo); NewBlockTraverserState.getFactory(newFinallyCentralityState, finallyTerminusBlockInfo);
final TraverserStateFactory<NewBlockTraverserState> candidateStateFactory = TraverserStateFactory<NewBlockTraverserState> candidateStateFactory =
NewBlockTraverserState.getFactory(newCandidateCentralityState, candidateTerminusBlockInfo); NewBlockTraverserState.getFactory(newCandidateCentralityState, candidateTerminusBlockInfo);
final TraverserActivePathState nextState = TraverserActivePathState nextState =
TraverserActivePathState.produceFromFactories(comparator, finallyStateFactory, candidateStateFactory); TraverserActivePathState.produceFromFactories(comparator, finallyStateFactory, candidateStateFactory);
nextState.mergeWith(paths); nextState.mergeWith(paths);
return List.of(nextState); return List.of(nextState);
} }
public static List<BlockNode[]> getAllPermutationsOfCollection(final Collection<BlockNode> elements) { public static List<BlockNode[]> getAllPermutationsOfCollection(Collection<BlockNode> elements) {
final Stack<BlockNode> permutationStack = new Stack<>(); Stack<BlockNode> permutationStack = new Stack<>();
final List<BlockNode[]> permutations = new ArrayList<>(); List<BlockNode[]> permutations = new ArrayList<>();
permutations(permutations, elements, permutationStack, elements.size()); permutations(permutations, elements, permutationStack, elements.size());
return permutations; return permutations;
} }
public static void permutations(final List<BlockNode[]> permutations, final Collection<BlockNode> elements, public static void permutations(List<BlockNode[]> permutations, Collection<BlockNode> elements,
final Stack<BlockNode> permutationStack, final int size) { Stack<BlockNode> permutationStack, int size) {
if (permutationStack.size() == size) { if (permutationStack.size() == size) {
permutations.add(permutationStack.toArray(BlockNode[]::new)); permutations.add(permutationStack.toArray(BlockNode[]::new));
} }
BlockNode[] availableItems = elements.toArray(BlockNode[]::new); BlockNode[] availableItems = elements.toArray(BlockNode[]::new);
for (BlockNode i : availableItems) { for (BlockNode i : availableItems) {
permutationStack.push(i); permutationStack.push(i);
@@ -12,36 +12,30 @@ import jadx.core.utils.exceptions.JadxRuntimeException;
public final class PredecessorBlockPathTraverserHandler<T extends TraverserState & ISourceBlockState> public final class PredecessorBlockPathTraverserHandler<T extends TraverserState & ISourceBlockState>
extends AbstractBlockPathTraverserHandler { extends AbstractBlockPathTraverserHandler {
private final ISourceBlockState sourceBlockState; private final ISourceBlockState sourceBlockState;
public PredecessorBlockPathTraverserHandler(final T initialState) { public PredecessorBlockPathTraverserHandler(T initialState) {
super(initialState); super(initialState);
this.sourceBlockState = initialState; this.sourceBlockState = initialState;
} }
public PredecessorBlockPathTraverserHandler(final AtomicReference<T> initialStateRef) { public PredecessorBlockPathTraverserHandler(AtomicReference<T> initialStateRef) {
super(initialStateRef); super(initialStateRef);
this.sourceBlockState = initialStateRef.get(); this.sourceBlockState = initialStateRef.get();
} }
@Override @Override
protected final void handle() { protected void handle() {
final TraverserState baseState = getState(); TraverserState baseState = getState();
final TraverserActivePathState comparator = baseState.getComparatorState(); TraverserActivePathState comparator = baseState.getComparatorState();
final AtomicReference<TraverserState> stateRef = comparator.getReferenceForState(baseState); AtomicReference<TraverserState> stateRef = comparator.getReferenceForState(baseState);
if (stateRef == null) { if (stateRef == null) {
throw new JadxRuntimeException("Orphaned traverser state"); throw new JadxRuntimeException("Orphaned traverser state");
} }
BlockNode sourceBlock = sourceBlockState.getSourceBlock();
final BlockNode sourceBlock = sourceBlockState.getSourceBlock(); AbstractBlockTraverserVisitor visitor = new PredecessorBlockTraverserVisitor(baseState);
final AbstractBlockTraverserVisitor visitor = new PredecessorBlockTraverserVisitor(baseState); TraverserState nextState = visitor.visit(sourceBlock);
final TraverserState nextState = visitor.visit(sourceBlock);
stateRef.set(nextState); stateRef.set(nextState);
} }
} }
@@ -21,11 +21,9 @@ import jadx.core.utils.BlockUtils;
public final class PredecessorMergeActivePathTraverserHandler extends AbstractActivePathTraverserHandler { public final class PredecessorMergeActivePathTraverserHandler extends AbstractActivePathTraverserHandler {
private static List<BlockNode> orderBlocks(final List<BlockNode> blocks) { private static List<BlockNode> orderBlocks(List<BlockNode> blocks) {
final List<BlockNode> dup = new ArrayList<>(blocks); List<BlockNode> dup = new ArrayList<>(blocks);
// Collections.sort(dup, (blk1, blk2) -> Integer.compare(blk1.getCId(), blk2.getCId())); // Collections.sort(dup, (blk1, blk2) -> Integer.compare(blk1.getCId(), blk2.getCId()));
return dup; return dup;
} }
@@ -34,25 +32,24 @@ public final class PredecessorMergeActivePathTraverserHandler extends AbstractAc
} }
@Override @Override
protected final List<TraverserActivePathState> handle() throws TraverserException { protected List<TraverserActivePathState> handle() throws TraverserException {
// At this point, we expect the handler to contain the block state of the path which is // At this point, we expect the handler to contain the block state of the path which is
// requesting a predecessor merge. If the other handler also requests a predecessor merge, // requesting a predecessor merge. If the other handler also requests a predecessor merge,
// we can merge the two. If not, we'll split the active handler to support the multiple // we can merge the two. If not, we'll split the active handler to support the multiple
// paths. // paths.
TraverserActivePathState comparator = getComparator();
TraverserState finallyState = comparator.getFinallyState();
TraverserState candidateState = comparator.getCandidateState();
final TraverserActivePathState comparator = getComparator(); boolean finallyNeedsDuplicate = finallyState.getCompareState() == TraverserState.ComparisonState.READY_TO_COMPARE;
final TraverserState finallyState = comparator.getFinallyState(); boolean candidateNeedsDuplicate = candidateState.getCompareState() == TraverserState.ComparisonState.READY_TO_COMPARE;
final TraverserState candidateState = comparator.getCandidateState(); boolean shouldMerge = finallyNeedsDuplicate && candidateNeedsDuplicate;
final boolean finallyNeedsDuplicate = finallyState.getCompareState() == TraverserState.ComparisonState.READY_TO_COMPARE;
final boolean candidateNeedsDuplicate = candidateState.getCompareState() == TraverserState.ComparisonState.READY_TO_COMPARE;
final boolean shouldMerge = finallyNeedsDuplicate && candidateNeedsDuplicate;
if (shouldMerge) { if (shouldMerge) {
return mergeScopes((UnknownAdvanceStrategyTraverserState) finallyState, (UnknownAdvanceStrategyTraverserState) candidateState); return mergeScopes((UnknownAdvanceStrategyTraverserState) finallyState, (UnknownAdvanceStrategyTraverserState) candidateState);
} else { } else {
final UnknownAdvanceStrategyTraverserState advancingState; UnknownAdvanceStrategyTraverserState advancingState;
final TraverserState otherState; TraverserState otherState;
if (finallyNeedsDuplicate) { if (finallyNeedsDuplicate) {
advancingState = (UnknownAdvanceStrategyTraverserState) finallyState; advancingState = (UnknownAdvanceStrategyTraverserState) finallyState;
otherState = candidateState; otherState = candidateState;
@@ -64,57 +61,57 @@ public final class PredecessorMergeActivePathTraverserHandler extends AbstractAc
} }
} }
private List<TraverserActivePathState> mergeScopes(final UnknownAdvanceStrategyTraverserState finallyState, private List<TraverserActivePathState> mergeScopes(UnknownAdvanceStrategyTraverserState finallyState,
final UnknownAdvanceStrategyTraverserState candidateState) throws TraverserException { UnknownAdvanceStrategyTraverserState candidateState) throws TraverserException {
final List<BlockNode> finallyBlocks = finallyState.getNextBlocks(); List<BlockNode> finallyBlocks = finallyState.getNextBlocks();
final List<BlockNode> candidateBlocks = candidateState.getNextBlocks(); List<BlockNode> candidateBlocks = candidateState.getNextBlocks();
final int finallyBlocksSize = finallyBlocks.size(); int finallyBlocksSize = finallyBlocks.size();
final int candidateBlocksSize = candidateBlocks.size(); int candidateBlocksSize = candidateBlocks.size();
final List<TraverserActivePathState> states; List<TraverserActivePathState> states;
if (candidateBlocksSize % finallyBlocksSize == 0 && candidateBlocksSize == finallyBlocksSize) { if (candidateBlocksSize % finallyBlocksSize == 0 && candidateBlocksSize == finallyBlocksSize) {
final List<BlockNode> finallyBlocksOrdered = orderBlocks(finallyBlocks); List<BlockNode> finallyBlocksOrdered = orderBlocks(finallyBlocks);
final List<BlockNode> candidateBlocksOrdered = orderBlocks(candidateBlocks); List<BlockNode> candidateBlocksOrdered = orderBlocks(candidateBlocks);
final int duplicationCount = candidateBlocksSize / finallyBlocksSize; int duplicationCount = candidateBlocksSize / finallyBlocksSize;
states = new ArrayList<>(duplicationCount); states = new ArrayList<>(duplicationCount);
for (int i = 0; i < duplicationCount; i++) { for (int i = 0; i < duplicationCount; i++) {
final List<BlockNode> candidateBlocksSubset = new ArrayList<>(finallyBlocksSize); List<BlockNode> candidateBlocksSubset = new ArrayList<>(finallyBlocksSize);
for (int j = 0; j < finallyBlocksSize; j++) { for (int j = 0; j < finallyBlocksSize; j++) {
candidateBlocksSubset.add(candidateBlocksOrdered.get(i * finallyBlocksSize + j)); candidateBlocksSubset.add(candidateBlocksOrdered.get(i * finallyBlocksSize + j));
} }
final TraverserActivePathState comparatorState = getScopeForBlocks(finallyBlocksOrdered, candidateBlocksSubset); TraverserActivePathState comparatorState = getScopeForBlocks(finallyBlocksOrdered, candidateBlocksSubset);
states.add(comparatorState); states.add(comparatorState);
} }
} else { } else {
final TraverserStateFactory<TerminalTraverserState> finallyStateFactory = TraverserStateFactory<TerminalTraverserState> finallyStateFactory =
TerminalTraverserState.getFactory(TerminalTraverserState.TerminationReason.UNMERGEABLE_STATE); TerminalTraverserState.getFactory(TerminalTraverserState.TerminationReason.UNMERGEABLE_STATE);
final TraverserStateFactory<TerminalTraverserState> candidateStateFactory = TraverserStateFactory<TerminalTraverserState> candidateStateFactory =
TerminalTraverserState.getFactory(TerminalTraverserState.TerminationReason.UNMERGEABLE_STATE); TerminalTraverserState.getFactory(TerminalTraverserState.TerminationReason.UNMERGEABLE_STATE);
final TraverserActivePathState newState = TraverserActivePathState newState =
TraverserActivePathState.produceFromFactories(getComparator(), finallyStateFactory, candidateStateFactory); TraverserActivePathState.produceFromFactories(getComparator(), finallyStateFactory, candidateStateFactory);
states = List.of(newState); states = List.of(newState);
} }
return states; return states;
} }
private List<TraverserActivePathState> duplicateForPaths(final TraverserActivePathState comparator, private List<TraverserActivePathState> duplicateForPaths(TraverserActivePathState comparator,
final UnknownAdvanceStrategyTraverserState advancingState, final TraverserState otherState, UnknownAdvanceStrategyTraverserState advancingState, TraverserState otherState,
final boolean duplicateIsFromFinally) { boolean duplicateIsFromFinally) {
final List<BlockNode> nextPredecessors = advancingState.getNextBlocks(); List<BlockNode> nextPredecessors = advancingState.getNextBlocks();
final List<TraverserActivePathState> newPaths = new ArrayList<>(nextPredecessors.size()); List<TraverserActivePathState> newPaths = new ArrayList<>(nextPredecessors.size());
for (final BlockNode predecessor : nextPredecessors) { for (BlockNode predecessor : nextPredecessors) {
final CentralityState centralityState = advancingState.getCentralityState(); CentralityState centralityState = advancingState.getCentralityState();
final TraverserBlockInfo duplicatePathBlockInfo = new TraverserBlockInfo(predecessor); TraverserBlockInfo duplicatePathBlockInfo = new TraverserBlockInfo(predecessor);
final TraverserStateFactory<NewBlockTraverserState> duplicatePathStateFactory = TraverserStateFactory<NewBlockTraverserState> duplicatePathStateFactory =
NewBlockTraverserState.getFactory(centralityState, duplicatePathBlockInfo); NewBlockTraverserState.getFactory(centralityState, duplicatePathBlockInfo);
final TraverserStateFactory<?> otherStateFactory = new DuplicatedTraverserStateFactory<>(otherState); TraverserStateFactory<?> otherStateFactory = new DuplicatedTraverserStateFactory<>(otherState);
final TraverserActivePathState comparatorDuplicated = comparator.duplicate(); TraverserActivePathState comparatorDuplicated = comparator.duplicate();
final TraverserActivePathState newPathState; TraverserActivePathState newPathState;
if (duplicateIsFromFinally) { if (duplicateIsFromFinally) {
newPathState = newPathState =
TraverserActivePathState.produceFromFactories(comparatorDuplicated, duplicatePathStateFactory, otherStateFactory); TraverserActivePathState.produceFromFactories(comparatorDuplicated, duplicatePathStateFactory, otherStateFactory);
@@ -124,29 +121,28 @@ public final class PredecessorMergeActivePathTraverserHandler extends AbstractAc
} }
newPaths.add(newPathState); newPaths.add(newPathState);
} }
return newPaths; return newPaths;
} }
private TraverserActivePathState getScopeForBlocks(final List<BlockNode> finallyBlocks, final List<BlockNode> candidateBlocks) { private TraverserActivePathState getScopeForBlocks(List<BlockNode> finallyBlocks, List<BlockNode> candidateBlocks) {
final TraverserActivePathState comparator = getComparator(); TraverserActivePathState comparator = getComparator();
final MethodNode mth = getComparator().getGlobalCommonState().getMethodNode(); MethodNode mth = getComparator().getGlobalCommonState().getMethodNode();
final TraverserState finallyState = comparator.getFinallyState(); TraverserState finallyState = comparator.getFinallyState();
final TraverserState candidateState = comparator.getCandidateState(); TraverserState candidateState = comparator.getCandidateState();
final GlobalTraverserSourceState finallyGlobalState = comparator.getGlobalStateFor(finallyState); GlobalTraverserSourceState finallyGlobalState = comparator.getGlobalStateFor(finallyState);
final CentralityState finallyCentralityState = finallyState.getCentralityState(); CentralityState finallyCentralityState = finallyState.getCentralityState();
final BlockNode finallyTerminator = BlockNode finallyTerminator =
BlockUtils.getBottomCommonPredecessor(mth, finallyBlocks, finallyGlobalState.getContainedBlocks()); BlockUtils.getBottomCommonPredecessor(mth, finallyBlocks, finallyGlobalState.getContainedBlocks());
final TraverserStateFactory<IdentifiedScopeWithTerminatorTraverserState> finallyStateFactory = TraverserStateFactory<IdentifiedScopeWithTerminatorTraverserState> finallyStateFactory =
IdentifiedScopeWithTerminatorTraverserState.getFactory(finallyCentralityState, finallyBlocks, finallyTerminator); IdentifiedScopeWithTerminatorTraverserState.getFactory(finallyCentralityState, finallyBlocks, finallyTerminator);
final GlobalTraverserSourceState candidateGlobalState = comparator.getGlobalStateFor(candidateState); GlobalTraverserSourceState candidateGlobalState = comparator.getGlobalStateFor(candidateState);
final CentralityState candidateCentralityState = candidateState.getCentralityState(); CentralityState candidateCentralityState = candidateState.getCentralityState();
final BlockNode candidateTerminator = BlockNode candidateTerminator =
BlockUtils.getBottomCommonPredecessor(mth, candidateBlocks, candidateGlobalState.getContainedBlocks()); BlockUtils.getBottomCommonPredecessor(mth, candidateBlocks, candidateGlobalState.getContainedBlocks());
final TraverserStateFactory<IdentifiedScopeWithTerminatorTraverserState> candidateStateFactory = TraverserStateFactory<IdentifiedScopeWithTerminatorTraverserState> candidateStateFactory =
IdentifiedScopeWithTerminatorTraverserState.getFactory(candidateCentralityState, candidateBlocks, candidateTerminator); IdentifiedScopeWithTerminatorTraverserState.getFactory(candidateCentralityState, candidateBlocks, candidateTerminator);
return TraverserActivePathState.produceFromFactories(comparator, finallyStateFactory, candidateStateFactory); return TraverserActivePathState.produceFromFactories(comparator, finallyStateFactory, candidateStateFactory);
@@ -7,50 +7,45 @@ import jadx.core.dex.visitors.finaly.traverser.handlers.AbstractBlockTraverserHa
import jadx.core.dex.visitors.finaly.traverser.handlers.InstructionActivePathTraverserHandler; import jadx.core.dex.visitors.finaly.traverser.handlers.InstructionActivePathTraverserHandler;
public final class AwaitingInsnCompareTraverserState extends TraverserState { public final class AwaitingInsnCompareTraverserState extends TraverserState {
private final CentralityState centralityState; private final CentralityState centralityState;
private final @Nullable TraverserBlockInfo blockInsnInfo; private final @Nullable TraverserBlockInfo blockInsnInfo;
public AwaitingInsnCompareTraverserState(final TraverserActivePathState state, final CentralityState centralityState, public AwaitingInsnCompareTraverserState(TraverserActivePathState state, CentralityState centralityState,
final TraverserBlockInfo blockInsnInfo) { TraverserBlockInfo blockInsnInfo) {
super(state); super(state);
this.centralityState = centralityState; this.centralityState = centralityState;
this.blockInsnInfo = blockInsnInfo; this.blockInsnInfo = blockInsnInfo;
} }
@Override @Override
public final @Nullable AbstractBlockTraverserHandler getNextHandler() { public @Nullable AbstractBlockTraverserHandler getNextHandler() {
return new InstructionActivePathTraverserHandler(getComparatorState()); return new InstructionActivePathTraverserHandler(getComparatorState());
} }
@Override @Override
public final ComparisonState getCompareState() { public ComparisonState getCompareState() {
return ComparisonState.READY_TO_COMPARE; return ComparisonState.READY_TO_COMPARE;
} }
@Override @Override
public final boolean isTerminal() { public boolean isTerminal() {
return false; return false;
} }
@Override @Override
protected final @Nullable CentralityState getUnderlyingCentralityState() { protected @Nullable CentralityState getUnderlyingCentralityState() {
return centralityState; return centralityState;
} }
@Override @Override
protected final @Nullable TraverserBlockInfo getUnderlyingBlockInsnInfo() { protected @Nullable TraverserBlockInfo getUnderlyingBlockInsnInfo() {
return blockInsnInfo; return blockInsnInfo;
} }
@Override @Override
protected final TraverserState duplicateInternalState(final TraverserActivePathState comparatorState) { protected TraverserState duplicateInternalState(TraverserActivePathState comparatorState) {
final CentralityState dCentralityState = centralityState.duplicate(); CentralityState dCentralityState = centralityState.duplicate();
final TraverserBlockInfo dBlockInsnInfo = blockInsnInfo.duplicate(); TraverserBlockInfo dBlockInsnInfo = blockInsnInfo.duplicate();
return new AwaitingInsnCompareTraverserState(comparatorState, dCentralityState, dBlockInsnInfo);
final TraverserState duplicated = new AwaitingInsnCompareTraverserState(comparatorState, dCentralityState, dBlockInsnInfo);
return duplicated;
} }
} }
@@ -4,5 +4,5 @@ import jadx.core.dex.nodes.BlockNode;
public interface ISourceBlockState { public interface ISourceBlockState {
public abstract BlockNode getSourceBlock(); BlockNode getSourceBlock();
} }
@@ -12,8 +12,8 @@ import jadx.core.dex.visitors.finaly.traverser.handlers.MergePathActivePathTrave
public final class IdentifiedScopeWithTerminatorTraverserState extends TraverserState { public final class IdentifiedScopeWithTerminatorTraverserState extends TraverserState {
public static TraverserStateFactory<IdentifiedScopeWithTerminatorTraverserState> getFactory(final CentralityState centralityState, public static TraverserStateFactory<IdentifiedScopeWithTerminatorTraverserState> getFactory(CentralityState centralityState,
final List<BlockNode> roots, final BlockNode scopeTerminator) { List<BlockNode> roots, BlockNode scopeTerminator) {
return new IdentifiedScopeWithTerminatorStateFactory(centralityState, roots, scopeTerminator); return new IdentifiedScopeWithTerminatorStateFactory(centralityState, roots, scopeTerminator);
} }
@@ -24,15 +24,15 @@ public final class IdentifiedScopeWithTerminatorTraverserState extends Traverser
private final List<BlockNode> roots; private final List<BlockNode> roots;
private final BlockNode scopeTerminator; private final BlockNode scopeTerminator;
public IdentifiedScopeWithTerminatorStateFactory(final CentralityState centralityState, final List<BlockNode> roots, public IdentifiedScopeWithTerminatorStateFactory(CentralityState centralityState, List<BlockNode> roots,
final BlockNode scopeTerminator) { BlockNode scopeTerminator) {
this.centralityState = centralityState; this.centralityState = centralityState;
this.roots = roots; this.roots = roots;
this.scopeTerminator = scopeTerminator; this.scopeTerminator = scopeTerminator;
} }
@Override @Override
public final IdentifiedScopeWithTerminatorTraverserState generateInternalState(final TraverserActivePathState state) { public IdentifiedScopeWithTerminatorTraverserState generateInternalState(TraverserActivePathState state) {
return new IdentifiedScopeWithTerminatorTraverserState(state, centralityState, roots, scopeTerminator); return new IdentifiedScopeWithTerminatorTraverserState(state, centralityState, roots, scopeTerminator);
} }
} }
@@ -41,8 +41,8 @@ public final class IdentifiedScopeWithTerminatorTraverserState extends Traverser
private final List<BlockNode> roots; private final List<BlockNode> roots;
private final BlockNode scopeTerminator; private final BlockNode scopeTerminator;
public IdentifiedScopeWithTerminatorTraverserState(final TraverserActivePathState state, final CentralityState centralityState, public IdentifiedScopeWithTerminatorTraverserState(TraverserActivePathState state, CentralityState centralityState,
final List<BlockNode> roots, final BlockNode scopeTerminator) { List<BlockNode> roots, BlockNode scopeTerminator) {
super(state); super(state);
this.roots = roots; this.roots = roots;
this.scopeTerminator = scopeTerminator; this.scopeTerminator = scopeTerminator;
@@ -50,40 +50,40 @@ public final class IdentifiedScopeWithTerminatorTraverserState extends Traverser
} }
@Override @Override
public final @Nullable AbstractBlockTraverserHandler getNextHandler() { public @Nullable AbstractBlockTraverserHandler getNextHandler() {
return new MergePathActivePathTraverserHandler(getComparatorState()); return new MergePathActivePathTraverserHandler(getComparatorState());
} }
@Override @Override
public final ComparisonState getCompareState() { public ComparisonState getCompareState() {
return ComparisonState.READY_TO_COMPARE; return ComparisonState.READY_TO_COMPARE;
} }
@Override @Override
public final boolean isTerminal() { public boolean isTerminal() {
return false; return false;
} }
@Override @Override
protected final @Nullable CentralityState getUnderlyingCentralityState() { protected @Nullable CentralityState getUnderlyingCentralityState() {
return centralityState; return centralityState;
} }
@Override @Override
protected final @Nullable TraverserBlockInfo getUnderlyingBlockInsnInfo() { protected @Nullable TraverserBlockInfo getUnderlyingBlockInsnInfo() {
return null; return null;
} }
@Override @Override
protected final TraverserState duplicateInternalState(final TraverserActivePathState comparatorState) { protected TraverserState duplicateInternalState(TraverserActivePathState comparatorState) {
return new IdentifiedScopeWithTerminatorTraverserState(comparatorState, centralityState, roots, scopeTerminator); return new IdentifiedScopeWithTerminatorTraverserState(comparatorState, centralityState, roots, scopeTerminator);
} }
public final BlockNode getTerminus() { public BlockNode getTerminus() {
return scopeTerminator; return scopeTerminator;
} }
public final List<BlockNode> getRoots() { public List<BlockNode> getRoots() {
return roots; return roots;
} }
} }
@@ -9,23 +9,22 @@ import jadx.core.dex.visitors.finaly.traverser.handlers.BaseBlockTraverserHandle
public final class NewBlockTraverserState extends TraverserState { public final class NewBlockTraverserState extends TraverserState {
public static final TraverserStateFactory<NewBlockTraverserState> getFactory(final CentralityState centralityState, public static TraverserStateFactory<NewBlockTraverserState> getFactory(CentralityState centralityState,
final TraverserBlockInfo blockInsnInfo) { TraverserBlockInfo blockInsnInfo) {
return new NewBlockStateFactory(centralityState, blockInsnInfo); return new NewBlockStateFactory(centralityState, blockInsnInfo);
} }
private static class NewBlockStateFactory extends TraverserStateFactory<NewBlockTraverserState> { private static class NewBlockStateFactory extends TraverserStateFactory<NewBlockTraverserState> {
private final CentralityState centralityState; private final CentralityState centralityState;
private final TraverserBlockInfo blockInsnInfo; private final TraverserBlockInfo blockInsnInfo;
public NewBlockStateFactory(final CentralityState centralityState, final TraverserBlockInfo blockInsnInfo) { public NewBlockStateFactory(CentralityState centralityState, TraverserBlockInfo blockInsnInfo) {
this.centralityState = centralityState; this.centralityState = centralityState;
this.blockInsnInfo = blockInsnInfo; this.blockInsnInfo = blockInsnInfo;
} }
@Override @Override
public NewBlockTraverserState generateInternalState(final TraverserActivePathState state) { public NewBlockTraverserState generateInternalState(TraverserActivePathState state) {
return new NewBlockTraverserState(state, centralityState, blockInsnInfo); return new NewBlockTraverserState(state, centralityState, blockInsnInfo);
} }
} }
@@ -33,20 +32,19 @@ public final class NewBlockTraverserState extends TraverserState {
private final CentralityState centralityState; private final CentralityState centralityState;
private final @Nullable TraverserBlockInfo blockInsnInfo; private final @Nullable TraverserBlockInfo blockInsnInfo;
public NewBlockTraverserState(final TraverserActivePathState state, final CentralityState centralityState, public NewBlockTraverserState(TraverserActivePathState state, CentralityState centralityState, TraverserBlockInfo blockInsnInfo) {
final TraverserBlockInfo blockInsnInfo) {
super(state); super(state);
this.centralityState = centralityState; this.centralityState = centralityState;
this.blockInsnInfo = blockInsnInfo; this.blockInsnInfo = blockInsnInfo;
} }
@Override @Override
public final ComparisonState getCompareState() { public ComparisonState getCompareState() {
return ComparisonState.NOT_READY; return ComparisonState.NOT_READY;
} }
@Override @Override
public final boolean isTerminal() { public boolean isTerminal() {
return false; return false;
} }
@@ -68,12 +66,9 @@ public final class NewBlockTraverserState extends TraverserState {
} }
@Override @Override
protected final TraverserState duplicateInternalState(final TraverserActivePathState comparatorState) { protected TraverserState duplicateInternalState(TraverserActivePathState comparatorState) {
final CentralityState dCentralityState = centralityState.duplicate(); CentralityState dCentralityState = centralityState.duplicate();
final TraverserBlockInfo dBlockInsnInfo = blockInsnInfo.duplicate(); TraverserBlockInfo dBlockInsnInfo = blockInsnInfo.duplicate();
return new NewBlockTraverserState(comparatorState, dCentralityState, dBlockInsnInfo);
final TraverserState duplicated = new NewBlockTraverserState(comparatorState, dCentralityState, dBlockInsnInfo);
return duplicated;
} }
} }
@@ -10,23 +10,21 @@ import jadx.core.dex.visitors.finaly.traverser.handlers.PredecessorBlockPathTrav
public final class NoBlockTraverserState extends TraverserState implements ISourceBlockState { public final class NoBlockTraverserState extends TraverserState implements ISourceBlockState {
public static TraverserStateFactory<NoBlockTraverserState> getFactory(final CentralityState centralityState, public static TraverserStateFactory<NoBlockTraverserState> getFactory(CentralityState centralityState, BlockNode sourceBlock) {
final BlockNode sourceBlock) {
return new NoBlockStateFactory(centralityState, sourceBlock); return new NoBlockStateFactory(centralityState, sourceBlock);
} }
private static class NoBlockStateFactory extends TraverserStateFactory<NoBlockTraverserState> { private static class NoBlockStateFactory extends TraverserStateFactory<NoBlockTraverserState> {
private final CentralityState centralityState; private final CentralityState centralityState;
private final BlockNode sourceBlock; private final BlockNode sourceBlock;
public NoBlockStateFactory(final CentralityState centralityState, final BlockNode sourceBlock) { public NoBlockStateFactory(CentralityState centralityState, BlockNode sourceBlock) {
this.centralityState = centralityState; this.centralityState = centralityState;
this.sourceBlock = sourceBlock; this.sourceBlock = sourceBlock;
} }
@Override @Override
public NoBlockTraverserState generateInternalState(final TraverserActivePathState state) { public NoBlockTraverserState generateInternalState(TraverserActivePathState state) {
return new NoBlockTraverserState(state, centralityState, sourceBlock); return new NoBlockTraverserState(state, centralityState, sourceBlock);
} }
} }
@@ -34,45 +32,45 @@ public final class NoBlockTraverserState extends TraverserState implements ISour
private final BlockNode sourceBlock; private final BlockNode sourceBlock;
private final CentralityState centralityState; private final CentralityState centralityState;
public NoBlockTraverserState(final TraverserActivePathState state, final CentralityState centralityState, final BlockNode sourceBlock) { public NoBlockTraverserState(TraverserActivePathState state, CentralityState centralityState, BlockNode sourceBlock) {
super(state); super(state);
this.sourceBlock = sourceBlock; this.sourceBlock = sourceBlock;
this.centralityState = centralityState; this.centralityState = centralityState;
} }
@Override @Override
public final @Nullable AbstractBlockPathTraverserHandler getNextHandler() { public @Nullable AbstractBlockPathTraverserHandler getNextHandler() {
return new PredecessorBlockPathTraverserHandler<>(this); return new PredecessorBlockPathTraverserHandler<>(this);
} }
@Override @Override
public final ComparisonState getCompareState() { public ComparisonState getCompareState() {
return ComparisonState.NOT_READY; return ComparisonState.NOT_READY;
} }
@Override @Override
public final boolean isTerminal() { public boolean isTerminal() {
return false; return false;
} }
@Override @Override
protected final @Nullable CentralityState getUnderlyingCentralityState() { protected @Nullable CentralityState getUnderlyingCentralityState() {
return centralityState; return centralityState;
} }
@Override @Override
protected final @Nullable TraverserBlockInfo getUnderlyingBlockInsnInfo() { protected @Nullable TraverserBlockInfo getUnderlyingBlockInsnInfo() {
return null; return null;
} }
@Override @Override
public final BlockNode getSourceBlock() { public BlockNode getSourceBlock() {
return sourceBlock; return sourceBlock;
} }
@Override @Override
protected final TraverserState duplicateInternalState(final TraverserActivePathState comparatorState) { protected TraverserState duplicateInternalState(TraverserActivePathState comparatorState) {
final CentralityState dCentralityState = centralityState.duplicate(); CentralityState dCentralityState = centralityState.duplicate();
return new NoBlockTraverserState(comparatorState, dCentralityState, sourceBlock); return new NoBlockTraverserState(comparatorState, dCentralityState, sourceBlock);
} }
} }
@@ -8,20 +8,19 @@ import jadx.core.dex.visitors.finaly.traverser.handlers.AbstractBlockTraverserHa
public final class RecoveredFromCacheTraverserState extends TraverserState { public final class RecoveredFromCacheTraverserState extends TraverserState {
public static TraverserStateFactory<RecoveredFromCacheTraverserState> getFactory(final TraverserState underlying) { public static TraverserStateFactory<RecoveredFromCacheTraverserState> getFactory(TraverserState underlying) {
return new RecoveredFromCacheStateFactory(underlying); return new RecoveredFromCacheStateFactory(underlying);
} }
private static final class RecoveredFromCacheStateFactory extends TraverserStateFactory<RecoveredFromCacheTraverserState> { private static final class RecoveredFromCacheStateFactory extends TraverserStateFactory<RecoveredFromCacheTraverserState> {
private final TraverserState underlying; private final TraverserState underlying;
private RecoveredFromCacheStateFactory(final TraverserState underlying) { private RecoveredFromCacheStateFactory(TraverserState underlying) {
this.underlying = underlying; this.underlying = underlying;
} }
@Override @Override
protected final RecoveredFromCacheTraverserState generateInternalState(final TraverserActivePathState state) { protected RecoveredFromCacheTraverserState generateInternalState(TraverserActivePathState state) {
return new RecoveredFromCacheTraverserState(underlying); return new RecoveredFromCacheTraverserState(underlying);
} }
@@ -29,47 +28,46 @@ public final class RecoveredFromCacheTraverserState extends TraverserState {
private final TraverserState underlying; private final TraverserState underlying;
public RecoveredFromCacheTraverserState(final TraverserState underlying) { public RecoveredFromCacheTraverserState(TraverserState underlying) {
super(underlying.getComparatorState()); super(underlying.getComparatorState());
this.underlying = underlying; this.underlying = underlying;
} }
@Override @Override
public final @Nullable AbstractBlockTraverserHandler getNextHandler() { public @Nullable AbstractBlockTraverserHandler getNextHandler() {
return null; return null;
} }
@Override @Override
public final ComparisonState getCompareState() { public ComparisonState getCompareState() {
return ComparisonState.NOT_READY; return ComparisonState.NOT_READY;
} }
@Override @Override
public final boolean isTerminal() { public boolean isTerminal() {
return true; return true;
} }
@Override @Override
protected final @Nullable CentralityState getUnderlyingCentralityState() { protected @Nullable CentralityState getUnderlyingCentralityState() {
return null; return null;
} }
@Override @Override
protected final @Nullable TraverserBlockInfo getUnderlyingBlockInsnInfo() { protected @Nullable TraverserBlockInfo getUnderlyingBlockInsnInfo() {
return null; return null;
} }
@Override @Override
protected final TraverserState duplicateInternalState(final TraverserActivePathState comparatorState) { protected TraverserState duplicateInternalState(TraverserActivePathState comparatorState) {
return new RecoveredFromCacheTraverserState(underlying); return new RecoveredFromCacheTraverserState(underlying);
} }
public final TraverserState getUnderlying() { public TraverserState getUnderlying() {
return underlying; return underlying;
} }
public final boolean canContinue() { public boolean canContinue() {
return underlying.isTerminal(); return underlying.isTerminal();
} }
} }
@@ -8,11 +8,11 @@ import jadx.core.dex.visitors.finaly.traverser.handlers.AbstractBlockPathTravers
public final class TerminalTraverserState extends TraverserState { public final class TerminalTraverserState extends TraverserState {
public static TraverserStateFactory<TerminalTraverserState> getFactory(final TerminationReason terminationReason) { public static TraverserStateFactory<TerminalTraverserState> getFactory(TerminationReason terminationReason) {
return new TerminalStateFactory(terminationReason); return new TerminalStateFactory(terminationReason);
} }
public static enum TerminationReason { public enum TerminationReason {
/** /**
* When comparing instructions within a finally and candidate block, non-matching * When comparing instructions within a finally and candidate block, non-matching
* instructions were found calling for the termination of the Traverser. * instructions were found calling for the termination of the Traverser.
@@ -38,10 +38,9 @@ public final class TerminalTraverserState extends TraverserState {
} }
private static class TerminalStateFactory extends TraverserStateFactory<TerminalTraverserState> { private static class TerminalStateFactory extends TraverserStateFactory<TerminalTraverserState> {
private final TerminationReason terminationReason; private final TerminationReason terminationReason;
public TerminalStateFactory(final TerminationReason terminationReason) { public TerminalStateFactory(TerminationReason terminationReason) {
this.terminationReason = terminationReason; this.terminationReason = terminationReason;
} }
@@ -53,45 +52,42 @@ public final class TerminalTraverserState extends TraverserState {
private final TerminationReason terminationReason; private final TerminationReason terminationReason;
public TerminalTraverserState(final TraverserActivePathState state, final TerminationReason terminationReason) { public TerminalTraverserState(TraverserActivePathState state, TerminationReason terminationReason) {
super(state); super(state);
this.terminationReason = terminationReason; this.terminationReason = terminationReason;
} }
@Override @Override
public final boolean isTerminal() { public boolean isTerminal() {
return true; return true;
} }
@Override @Override
@Nullable public @Nullable AbstractBlockPathTraverserHandler getNextHandler() {
public final AbstractBlockPathTraverserHandler getNextHandler() {
return null; return null;
} }
public final TerminationReason getTerminationReason() { public TerminationReason getTerminationReason() {
return terminationReason; return terminationReason;
} }
@Override @Override
public final ComparisonState getCompareState() { public ComparisonState getCompareState() {
return ComparisonState.NOT_READY; return ComparisonState.NOT_READY;
} }
@Override @Override
protected final @Nullable CentralityState getUnderlyingCentralityState() { protected @Nullable CentralityState getUnderlyingCentralityState() {
return null; return null;
} }
@Override @Override
protected final @Nullable TraverserBlockInfo getUnderlyingBlockInsnInfo() { protected @Nullable TraverserBlockInfo getUnderlyingBlockInsnInfo() {
return null; return null;
} }
@Override @Override
protected final TraverserState duplicateInternalState(final TraverserActivePathState comparatorState) { protected TraverserState duplicateInternalState(TraverserActivePathState comparatorState) {
final TraverserState duplicated = new TerminalTraverserState(comparatorState, terminationReason); return new TerminalTraverserState(comparatorState, terminationReason);
return duplicated;
} }
} }
@@ -41,20 +41,18 @@ public class TraverserActivePathState {
* be held by the resulting active path state. * be held by the resulting active path state.
* @return The cloned active path state. * @return The cloned active path state.
*/ */
public static TraverserActivePathState produceFromFactories(final TraverserActivePathState previousTraverserState, public static TraverserActivePathState produceFromFactories(TraverserActivePathState previousTraverserState,
final TraverserStateFactory<?> finallyStateProducer, final TraverserStateFactory<?> candidateStateProducer) { TraverserStateFactory<?> finallyStateProducer, TraverserStateFactory<?> candidateStateProducer) {
final TraverserActivePathState dState = TraverserActivePathState dState =
new TraverserActivePathState(previousTraverserState.matchedInsns, previousTraverserState.finallyCompletionMonitor, new TraverserActivePathState(previousTraverserState.matchedInsns, previousTraverserState.finallyCompletionMonitor,
previousTraverserState.candidateCompletionMonitor, previousTraverserState.commonGlobalState, previousTraverserState.candidateCompletionMonitor, previousTraverserState.commonGlobalState,
previousTraverserState.finallyGlobalState, previousTraverserState.finallyGlobalState,
previousTraverserState.candidateGlobalState); previousTraverserState.candidateGlobalState);
final TraverserState dFinallyState = finallyStateProducer.generateState(dState); TraverserState dFinallyState = finallyStateProducer.generateState(dState);
final TraverserState dCandidateState = candidateStateProducer.generateState(dState); TraverserState dCandidateState = candidateStateProducer.generateState(dState);
dState.candidateStateRef.set(dCandidateState); dState.candidateStateRef.set(dCandidateState);
dState.finallyStateRef.set(dFinallyState); dState.finallyStateRef.set(dFinallyState);
return dState; return dState;
} }
@@ -63,33 +61,29 @@ public class TraverserActivePathState {
* i.e. how many of the instructions have been compared in the Traversal. * i.e. how many of the instructions have been compared in the Traversal.
*/ */
private static final class BlockCompletionMonitor { private static final class BlockCompletionMonitor {
private final BlockNode block; private final BlockNode block;
private final Set<Integer> matchedIndices; private final Set<Integer> matchedIndices;
private final int insnCount;
private BlockCompletionMonitor(final BlockNode block) { private BlockCompletionMonitor(BlockNode block) {
this.block = block; this.block = block;
this.insnCount = block.getInstructions().size(); int insnCount = block.getInstructions().size();
this.matchedIndices = new HashSet<>(insnCount); this.matchedIndices = new HashSet<>(insnCount);
for (int i = 0; i < insnCount; i++) { for (int i = 0; i < insnCount; i++) {
matchedIndices.add(i); matchedIndices.add(i);
} }
} }
private void registerWithBlockInfo(final TraverserBlockInfo info, final int numberMatched) { private void registerWithBlockInfo(TraverserBlockInfo info, int numberMatched) {
if (info.getBlock() != block) { if (info.getBlock() != block) {
return; return;
} }
int botPointer = info.getBottomOffset();
final int botPointer = info.getBottomOffset();
for (int i = 0; i < numberMatched; i++) { for (int i = 0; i < numberMatched; i++) {
final int indexMatched = botPointer + i; int indexMatched = botPointer + i;
matchedIndices.remove(indexMatched); matchedIndices.remove(indexMatched);
} }
int bottomImplicitCount = info.getBottomImplicitCount();
final int bottomImplicitCount = info.getBottomImplicitCount(); boolean noPathEndInsns = botPointer - bottomImplicitCount == 0;
final boolean noPathEndInsns = botPointer - bottomImplicitCount == 0;
if (noPathEndInsns) { if (noPathEndInsns) {
for (int i = 0; i < bottomImplicitCount; i++) { for (int i = 0; i < bottomImplicitCount; i++) {
matchedIndices.remove(i); matchedIndices.remove(i);
@@ -98,16 +92,15 @@ public class TraverserActivePathState {
} }
private BlockCompletionMonitor duplicate() { private BlockCompletionMonitor duplicate() {
final BlockCompletionMonitor dup = new BlockCompletionMonitor(block); BlockCompletionMonitor dup = new BlockCompletionMonitor(block);
dup.matchedIndices.retainAll(matchedIndices); dup.matchedIndices.retainAll(matchedIndices);
return dup; return dup;
} }
private void mergeWith(final BlockCompletionMonitor other) { private void mergeWith(BlockCompletionMonitor other) {
if (other.block != block) { if (other.block != block) {
return; return;
} }
matchedIndices.retainAll(other.matchedIndices); matchedIndices.retainAll(other.matchedIndices);
} }
@@ -125,101 +118,100 @@ public class TraverserActivePathState {
} }
@Override @Override
public final void clear() { public void clear() {
underlying.clear(); underlying.clear();
} }
@Override @Override
public final boolean containsKey(Object key) { public boolean containsKey(Object key) {
return underlying.containsKey(key); return underlying.containsKey(key);
} }
@Override @Override
public final boolean containsValue(Object value) { public boolean containsValue(Object value) {
if (!(value instanceof BlockNode)) { if (!(value instanceof BlockNode)) {
return false; return false;
} }
BlockNode edge = (BlockNode) value;
final BlockNode edge = (BlockNode) value;
return underlying.containsKey(edge); return underlying.containsKey(edge);
} }
@Override @Override
public final Set<Entry<BlockNode, BlockCompletionMonitor>> entrySet() { public Set<Entry<BlockNode, BlockCompletionMonitor>> entrySet() {
return underlying.entrySet(); return underlying.entrySet();
} }
@Override @Override
public final BlockCompletionMonitor get(Object key) { public BlockCompletionMonitor get(Object key) {
return underlying.get(key); return underlying.get(key);
} }
@Override @Override
public final boolean isEmpty() { public boolean isEmpty() {
return underlying.isEmpty(); return underlying.isEmpty();
} }
@Override @Override
public final Set<BlockNode> keySet() { public Set<BlockNode> keySet() {
return underlying.keySet(); return underlying.keySet();
} }
@Override @Override
public final BlockCompletionMonitor put(BlockNode key, BlockCompletionMonitor value) { public BlockCompletionMonitor put(BlockNode key, BlockCompletionMonitor value) {
return underlying.put(key, value); return underlying.put(key, value);
} }
@Override @Override
public final void putAll(Map<? extends BlockNode, ? extends BlockCompletionMonitor> otherMap) { public void putAll(Map<? extends BlockNode, ? extends BlockCompletionMonitor> otherMap) {
underlying.putAll(otherMap); underlying.putAll(otherMap);
} }
@Override @Override
public final BlockCompletionMonitor remove(Object key) { public BlockCompletionMonitor remove(Object key) {
return underlying.remove(key); return underlying.remove(key);
} }
@Override @Override
public final int size() { public int size() {
return underlying.size(); return underlying.size();
} }
@Override @Override
public final Collection<BlockCompletionMonitor> values() { public Collection<BlockCompletionMonitor> values() {
return underlying.values(); return underlying.values();
} }
private void registerWithBlockInfo(final TraverserBlockInfo info, final int numberMatched) { private void registerWithBlockInfo(TraverserBlockInfo info, int numberMatched) {
final BlockNode block = info.getBlock(); BlockNode block = info.getBlock();
if (containsKey(block)) { if (containsKey(block)) {
get(block).registerWithBlockInfo(info, numberMatched); get(block).registerWithBlockInfo(info, numberMatched);
} else { } else {
final BlockCompletionMonitor monitor = new BlockCompletionMonitor(block); BlockCompletionMonitor monitor = new BlockCompletionMonitor(block);
monitor.registerWithBlockInfo(info, numberMatched); monitor.registerWithBlockInfo(info, numberMatched);
put(block, monitor); put(block, monitor);
} }
} }
private void mergeEntry(final BlockCompletionMonitor other) { private void mergeEntry(BlockCompletionMonitor other) {
final BlockNode block = other.block; BlockNode block = other.block;
if (containsKey(block)) { if (containsKey(block)) {
get(block).mergeWith(other); get(block).mergeWith(other);
} else { } else {
final BlockCompletionMonitor monitor = other.duplicate(); BlockCompletionMonitor monitor = other.duplicate();
put(block, monitor); put(block, monitor);
} }
} }
private void mergeMap(final BlockCompletionMonitorMap other) { private void mergeMap(BlockCompletionMonitorMap other) {
for (final BlockCompletionMonitor monitor : other.values()) { for (BlockCompletionMonitor monitor : other.values()) {
mergeEntry(monitor); mergeEntry(monitor);
} }
} }
private BlockCompletionMonitorMap duplicate() { private BlockCompletionMonitorMap duplicate() {
final BlockCompletionMonitorMap dup = new BlockCompletionMonitorMap(); BlockCompletionMonitorMap dup = new BlockCompletionMonitorMap();
for (final BlockNode sourceBlock : keySet()) { for (BlockNode sourceBlock : keySet()) {
final BlockCompletionMonitor monitor = get(sourceBlock); BlockCompletionMonitor monitor = get(sourceBlock);
dup.put(sourceBlock, monitor.duplicate()); dup.put(sourceBlock, monitor.duplicate());
} }
return dup; return dup;
@@ -239,27 +231,20 @@ public class TraverserActivePathState {
/** /**
* Creates a new instance of a traversal active path. This constructor is used to create a new * Creates a new instance of a traversal active path. This constructor is used to create a new
* path to be used by the traverser controller to begin a new traversal. * path to be used by the traverser controller to begin a new traversal.
*
* @param mth
* @param sameInstructionsStrategy
* @param finallyBlockTerminus
* @param candidateBlockTerminus
* @param finallyBlocks
* @param candidateBlocks
*/ */
public TraverserActivePathState(final MethodNode mth, final SameInstructionsStrategy sameInstructionsStrategy, public TraverserActivePathState(MethodNode mth, SameInstructionsStrategy sameInstructionsStrategy,
final BlockNode finallyBlockTerminus, final BlockNode candidateBlockTerminus, final List<BlockNode> finallyBlocks, BlockNode finallyBlockTerminus, BlockNode candidateBlockTerminus, List<BlockNode> finallyBlocks,
final List<BlockNode> candidateBlocks) { List<BlockNode> candidateBlocks) {
final boolean shouldFinallyAllowFirstBlockSkip = !finallyBlockTerminus.getInstructions().isEmpty(); boolean shouldFinallyAllowFirstBlockSkip = !finallyBlockTerminus.getInstructions().isEmpty();
final boolean shouldCandidateAllowFirstBlockSkip = !candidateBlockTerminus.getInstructions().isEmpty(); boolean shouldCandidateAllowFirstBlockSkip = !candidateBlockTerminus.getInstructions().isEmpty();
final CentralityState finallyCentralityState = new CentralityState(sameInstructionsStrategy, shouldFinallyAllowFirstBlockSkip); CentralityState finallyCentralityState = new CentralityState(sameInstructionsStrategy, shouldFinallyAllowFirstBlockSkip);
final CentralityState candidateCentralityState = new CentralityState(sameInstructionsStrategy, shouldCandidateAllowFirstBlockSkip); CentralityState candidateCentralityState = new CentralityState(sameInstructionsStrategy, shouldCandidateAllowFirstBlockSkip);
final TraverserBlockInfo finallyBlockInfo = new TraverserBlockInfo(finallyBlockTerminus); TraverserBlockInfo finallyBlockInfo = new TraverserBlockInfo(finallyBlockTerminus);
final TraverserBlockInfo candidateBlockInfo = new TraverserBlockInfo(candidateBlockTerminus); TraverserBlockInfo candidateBlockInfo = new TraverserBlockInfo(candidateBlockTerminus);
final TraverserState finallyState = new NewBlockTraverserState(this, finallyCentralityState, finallyBlockInfo); TraverserState finallyState = new NewBlockTraverserState(this, finallyCentralityState, finallyBlockInfo);
final TraverserState candidateState = new NewBlockTraverserState(this, candidateCentralityState, candidateBlockInfo); TraverserState candidateState = new NewBlockTraverserState(this, candidateCentralityState, candidateBlockInfo);
this.finallyGlobalState = new GlobalTraverserSourceState(new HashSet<>(finallyBlocks)); this.finallyGlobalState = new GlobalTraverserSourceState(new HashSet<>(finallyBlocks));
this.candidateGlobalState = new GlobalTraverserSourceState(new HashSet<>(candidateBlocks)); this.candidateGlobalState = new GlobalTraverserSourceState(new HashSet<>(candidateBlocks));
@@ -276,17 +261,10 @@ public class TraverserActivePathState {
* Creates a new instance of a traversal active path. This constructor is used to duplicate a * Creates a new instance of a traversal active path. This constructor is used to duplicate a
* state between a previous traverser controller and is a liaison for initialising non-null * state between a previous traverser controller and is a liaison for initialising non-null
* final fields for the {@link TraverserActivePathState#produceFromFactories} function. * final fields for the {@link TraverserActivePathState#produceFromFactories} function.
*
* @param matchedInsns
* @param finallyCompletionMonitor
* @param candidateCompletionMonitor
* @param commonGlobalState
* @param finallyGlobalState
* @param candidateGlobalState
*/ */
private TraverserActivePathState(final Set<Pair<InsnNode>> matchedInsns, final BlockCompletionMonitorMap finallyCompletionMonitor, private TraverserActivePathState(Set<Pair<InsnNode>> matchedInsns, BlockCompletionMonitorMap finallyCompletionMonitor,
final BlockCompletionMonitorMap candidateCompletionMonitor, final TraverserGlobalCommonState commonGlobalState, BlockCompletionMonitorMap candidateCompletionMonitor, TraverserGlobalCommonState commonGlobalState,
final GlobalTraverserSourceState finallyGlobalState, final GlobalTraverserSourceState candidateGlobalState) { GlobalTraverserSourceState finallyGlobalState, GlobalTraverserSourceState candidateGlobalState) {
this.finallyStateRef = new AtomicReference<>(); this.finallyStateRef = new AtomicReference<>();
this.candidateStateRef = new AtomicReference<>(); this.candidateStateRef = new AtomicReference<>();
this.matchedInsns = matchedInsns; this.matchedInsns = matchedInsns;
@@ -298,19 +276,17 @@ public class TraverserActivePathState {
} }
public final TraverserActivePathState duplicate() { public final TraverserActivePathState duplicate() {
final Set<Pair<InsnNode>> dMatchedInsns = new HashSet<>(matchedInsns); Set<Pair<InsnNode>> dMatchedInsns = new HashSet<>(matchedInsns);
final BlockCompletionMonitorMap dFinallyCompletionMonitor = finallyCompletionMonitor.duplicate(); BlockCompletionMonitorMap dFinallyCompletionMonitor = finallyCompletionMonitor.duplicate();
final BlockCompletionMonitorMap dCandidateCompletionMonitor = candidateCompletionMonitor.duplicate(); BlockCompletionMonitorMap dCandidateCompletionMonitor = candidateCompletionMonitor.duplicate();
final TraverserActivePathState dState = TraverserActivePathState dState =
new TraverserActivePathState(dMatchedInsns, dFinallyCompletionMonitor, dCandidateCompletionMonitor, new TraverserActivePathState(dMatchedInsns, dFinallyCompletionMonitor, dCandidateCompletionMonitor,
commonGlobalState, finallyGlobalState, candidateGlobalState); commonGlobalState, finallyGlobalState, candidateGlobalState);
final TraverserState dFinallyState = getFinallyState().duplicate(dState); TraverserState dFinallyState = getFinallyState().duplicate(dState);
final TraverserState dCandidateState = getCandidateState().duplicate(dState); TraverserState dCandidateState = getCandidateState().duplicate(dState);
dState.candidateStateRef.set(dCandidateState); dState.candidateStateRef.set(dCandidateState);
dState.finallyStateRef.set(dFinallyState); dState.finallyStateRef.set(dFinallyState);
return dState; return dState;
} }
@@ -335,24 +311,24 @@ public class TraverserActivePathState {
} }
@Nullable @Nullable
public final AtomicReference<TraverserState> getReferenceForState(final TraverserState state) { public final AtomicReference<TraverserState> getReferenceForState(TraverserState state) {
if (finallyStateRef.get() == state) { if (finallyStateRef.get() == state) {
return finallyStateRef; return finallyStateRef;
} else if (candidateStateRef.get() == state) {
return candidateStateRef;
} else {
return null;
} }
if (candidateStateRef.get() == state) {
return candidateStateRef;
}
return null;
} }
public final GlobalTraverserSourceState getGlobalStateFor(final TraverserState state) { public final GlobalTraverserSourceState getGlobalStateFor(TraverserState state) {
if (finallyStateRef.get() == state) { if (finallyStateRef.get() == state) {
return finallyGlobalState; return finallyGlobalState;
} else if (candidateStateRef.get() == state) {
return candidateGlobalState;
} else {
throw new JadxRuntimeException("Orphaned TraverserState node");
} }
if (candidateStateRef.get() == state) {
return candidateGlobalState;
}
throw new JadxRuntimeException("Orphaned TraverserState node");
} }
public final GlobalTraverserSourceState getFinallyGlobalState() { public final GlobalTraverserSourceState getFinallyGlobalState() {
@@ -367,8 +343,8 @@ public class TraverserActivePathState {
return commonGlobalState; return commonGlobalState;
} }
public final void mergeWith(final List<TraverserActivePathState> otherStates) { public final void mergeWith(List<TraverserActivePathState> otherStates) {
for (final TraverserActivePathState otherState : otherStates) { for (TraverserActivePathState otherState : otherStates) {
matchedInsns.addAll(otherState.getMatchedInsns()); matchedInsns.addAll(otherState.getMatchedInsns());
finallyCompletionMonitor.mergeMap(otherState.finallyCompletionMonitor); finallyCompletionMonitor.mergeMap(otherState.finallyCompletionMonitor);
@@ -376,10 +352,10 @@ public class TraverserActivePathState {
} }
} }
public final void registerWithBlockInfo(final TraverserBlockInfo info, final int numberMatched) { public final void registerWithBlockInfo(TraverserBlockInfo info, int numberMatched) {
final BlockNode block = info.getBlock(); BlockNode block = info.getBlock();
final boolean isFinallyBlock = finallyGlobalState.isBlockContained(block); boolean isFinallyBlock = finallyGlobalState.isBlockContained(block);
final BlockCompletionMonitorMap monitorMap; BlockCompletionMonitorMap monitorMap;
if (isFinallyBlock) { if (isFinallyBlock) {
monitorMap = finallyCompletionMonitor; monitorMap = finallyCompletionMonitor;
} else { } else {
@@ -396,17 +372,14 @@ public class TraverserActivePathState {
return getAllFullyMatchedBlocks(candidateCompletionMonitor); return getAllFullyMatchedBlocks(candidateCompletionMonitor);
} }
private Set<BlockNode> getAllFullyMatchedBlocks(final BlockCompletionMonitorMap monitorMap) { private Set<BlockNode> getAllFullyMatchedBlocks(BlockCompletionMonitorMap monitorMap) {
final Set<BlockNode> matches = new HashSet<>(); Set<BlockNode> matches = new HashSet<>();
for (BlockCompletionMonitor monitor : monitorMap.values()) {
for (final BlockCompletionMonitor monitor : monitorMap.values()) {
if (!monitor.isEntireBlock()) { if (!monitor.isEntireBlock()) {
continue; continue;
} }
matches.add(monitor.block); matches.add(monitor.block);
} }
return matches; return matches;
} }
} }
@@ -6,7 +6,6 @@ import jadx.core.dex.nodes.BlockNode;
import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.InsnNode;
public final class TraverserBlockInfo { public final class TraverserBlockInfo {
private final BlockNode block; private final BlockNode block;
// These offsets are the instruction indices NOT an instruction size. // These offsets are the instruction indices NOT an instruction size.
@@ -14,11 +13,11 @@ public final class TraverserBlockInfo {
private int topOffset; private int topOffset;
private int bottomImplicitCount; private int bottomImplicitCount;
public TraverserBlockInfo(final BlockNode block) { public TraverserBlockInfo(BlockNode block) {
this(block, 0, 0, 0); this(block, 0, 0, 0);
} }
public TraverserBlockInfo(final BlockNode block, final int bottomOffset, final int topOffset, final int bottomImplicitCount) { public TraverserBlockInfo(BlockNode block, int bottomOffset, int topOffset, int bottomImplicitCount) {
this.bottomOffset = bottomOffset; this.bottomOffset = bottomOffset;
this.topOffset = topOffset; this.topOffset = topOffset;
this.block = block; this.block = block;
@@ -26,67 +25,62 @@ public final class TraverserBlockInfo {
} }
@Override @Override
public final String toString() { public String toString() {
return toString(""); return toString("");
} }
public final String toString(final String indent) { public String toString(String indent) {
final StringBuilder sb = new StringBuilder("BlockInsnInfo - "); StringBuilder sb = new StringBuilder("BlockInsnInfo - ");
sb.append(block.toString()); sb.append(block.toString());
sb.append(" [↑ "); sb.append(" [↑ ");
sb.append(bottomOffset); sb.append(bottomOffset);
sb.append("] [↓ "); sb.append("] [↓ ");
sb.append(topOffset); sb.append(topOffset);
sb.append("] "); sb.append("] ");
return sb.toString(); return sb.toString();
} }
public final TraverserBlockInfo duplicate() { public TraverserBlockInfo duplicate() {
return new TraverserBlockInfo(block, bottomOffset, topOffset, bottomImplicitCount); return new TraverserBlockInfo(block, bottomOffset, topOffset, bottomImplicitCount);
} }
public final BlockNode getBlock() { public BlockNode getBlock() {
return block; return block;
} }
public final int getTopOffset() { public int getTopOffset() {
return topOffset; return topOffset;
} }
public final void setTopOffset(final int topOffset) { public void setTopOffset(int topOffset) {
this.topOffset = topOffset; this.topOffset = topOffset;
} }
public final int getBottomOffset() { public int getBottomOffset() {
return bottomOffset; return bottomOffset;
} }
public final void setBottomOffset(final int bottomOffset) { public void setBottomOffset(int bottomOffset) {
this.bottomOffset = bottomOffset; this.bottomOffset = bottomOffset;
} }
public final int getBottomImplicitCount() { public int getBottomImplicitCount() {
return bottomImplicitCount; return bottomImplicitCount;
} }
public final void setBottomImplicitOffset(final int bottomImplicitCount) { public void setBottomImplicitOffset(int bottomImplicitCount) {
this.bottomImplicitCount = bottomImplicitCount; this.bottomImplicitCount = bottomImplicitCount;
} }
public final List<InsnNode> getInsnsSlice() { public List<InsnNode> getInsnsSlice() {
final List<InsnNode> insns = block.getInstructions(); List<InsnNode> insns = block.getInstructions();
int totalSkippedCount = bottomOffset + topOffset;
final int totalSkippedCount = bottomOffset + topOffset;
if (totalSkippedCount > insns.size()) { if (totalSkippedCount > insns.size()) {
throw new IndexOutOfBoundsException("Attempted to get instructions slice of block " + block.toString() + " with " throw new IndexOutOfBoundsException("Attempted to get instructions slice of block " + block.toString() + " with "
+ totalSkippedCount + " skipped instructions whilst only having " + insns.size() + " instructions in block."); + totalSkippedCount + " skipped instructions whilst only having " + insns.size() + " instructions in block.");
} }
int startIndex = topOffset;
final int startIndex = topOffset; int endIndex = insns.size() - bottomOffset;
final int endIndex = insns.size() - bottomOffset;
return insns.subList(startIndex, endIndex); return insns.subList(startIndex, endIndex);
} }
} }
@@ -11,33 +11,31 @@ import jadx.core.dex.nodes.MethodNode;
import jadx.core.utils.Pair; import jadx.core.utils.Pair;
public final class TraverserGlobalCommonState { public final class TraverserGlobalCommonState {
private final MethodNode mth; private final MethodNode mth;
private final Map<Pair<BlockNode>, List<TraverserActivePathState>> searchedStates; private final Map<Pair<BlockNode>, List<TraverserActivePathState>> searchedStates;
public TraverserGlobalCommonState(final MethodNode mth) { public TraverserGlobalCommonState(MethodNode mth) {
this.mth = mth; this.mth = mth;
this.searchedStates = new HashMap<>(); this.searchedStates = new HashMap<>();
} }
public final void addCachedStateFor(final BlockNode finallyBlock, final BlockNode candidateBlock, public void addCachedStateFor(BlockNode finallyBlock, BlockNode candidateBlock, List<TraverserActivePathState> state) {
final List<TraverserActivePathState> state) { Pair<BlockNode> blocks = new Pair<>(finallyBlock, candidateBlock);
final Pair<BlockNode> blocks = new Pair<>(finallyBlock, candidateBlock);
searchedStates.put(blocks, state); searchedStates.put(blocks, state);
} }
@Nullable @Nullable
public final List<TraverserActivePathState> getCachedStateFor(final BlockNode finallyBlock, final BlockNode candidateBlock) { public List<TraverserActivePathState> getCachedStateFor(BlockNode finallyBlock, BlockNode candidateBlock) {
final Pair<BlockNode> blocks = new Pair<>(finallyBlock, candidateBlock); Pair<BlockNode> blocks = new Pair<>(finallyBlock, candidateBlock);
return searchedStates.get(blocks); return searchedStates.get(blocks);
} }
public final boolean hasBlocksBeenCached(final BlockNode finallyBlock, final BlockNode candidateBlock) { public boolean hasBlocksBeenCached(BlockNode finallyBlock, BlockNode candidateBlock) {
final Pair<BlockNode> blocks = new Pair<>(finallyBlock, candidateBlock); Pair<BlockNode> blocks = new Pair<>(finallyBlock, candidateBlock);
return searchedStates.containsKey(blocks); return searchedStates.containsKey(blocks);
} }
public final MethodNode getMethodNode() { public MethodNode getMethodNode() {
return mth; return mth;
} }
} }
@@ -8,7 +8,7 @@ import jadx.core.dex.visitors.finaly.traverser.handlers.AbstractBlockTraverserHa
public abstract class TraverserState { public abstract class TraverserState {
public static enum ComparisonState { public enum ComparisonState {
NOT_READY, NOT_READY,
AWAITING_OPTIONAL_PREDECESSOR_MERGE, AWAITING_OPTIONAL_PREDECESSOR_MERGE,
READY_TO_COMPARE READY_TO_COMPARE
@@ -16,12 +16,11 @@ public abstract class TraverserState {
private final TraverserActivePathState comparatorState; private final TraverserActivePathState comparatorState;
public TraverserState(final TraverserActivePathState comparatorState) { public TraverserState(TraverserActivePathState comparatorState) {
this.comparatorState = comparatorState; this.comparatorState = comparatorState;
} }
@Nullable public abstract @Nullable AbstractBlockTraverserHandler getNextHandler();
public abstract AbstractBlockTraverserHandler getNextHandler();
public abstract ComparisonState getCompareState(); public abstract ComparisonState getCompareState();
@@ -36,40 +35,37 @@ public abstract class TraverserState {
* *
* @return The deep cloned duplication of this Traverser state. * @return The deep cloned duplication of this Traverser state.
*/ */
protected abstract TraverserState duplicateInternalState(final TraverserActivePathState comparatorState); protected abstract TraverserState duplicateInternalState(TraverserActivePathState comparatorState);
@Override @Override
public final String toString() { public final String toString() {
return toString(0); return toString(0);
} }
public final TraverserState duplicate(final TraverserActivePathState comparatorState) { public final TraverserState duplicate(TraverserActivePathState comparatorState) {
final TraverserState duplicatedState = duplicateInternalState(comparatorState); return duplicateInternalState(comparatorState);
return duplicatedState;
} }
public final TraverserActivePathState getComparatorState() { public final TraverserActivePathState getComparatorState() {
return comparatorState; return comparatorState;
} }
public final String toString(final int indentAmount) { public final String toString(int indentAmount) {
final String baseIndent = " ".repeat(indentAmount); String baseIndent = " ".repeat(indentAmount);
final String secondIndent = " ".repeat(indentAmount + 2); String secondIndent = " ".repeat(indentAmount + 2);
final StringBuilder sb = new StringBuilder(baseIndent); StringBuilder sb = new StringBuilder(baseIndent);
sb.append(getClass().getSimpleName()); sb.append(getClass().getSimpleName());
sb.append(' '); sb.append(' ');
if (isTerminal()) { if (isTerminal()) {
sb.append("TERMINAL "); sb.append("TERMINAL ");
} }
sb.append(" {"); sb.append(" {");
sb.append(System.lineSeparator()); sb.append(System.lineSeparator());
sb.append(secondIndent); sb.append(secondIndent);
sb.append("centrality: "); sb.append("centrality: ");
final CentralityState centralityState = getUnderlyingCentralityState(); CentralityState centralityState = getUnderlyingCentralityState();
if (centralityState == null) { if (centralityState == null) {
sb.append("none"); sb.append("none");
} else { } else {
@@ -82,7 +78,7 @@ public abstract class TraverserState {
sb.append(System.lineSeparator()); sb.append(System.lineSeparator());
sb.append(secondIndent); sb.append(secondIndent);
final TraverserBlockInfo blockInsnInfo = getBlockInsnInfo(); TraverserBlockInfo blockInsnInfo = getBlockInsnInfo();
if (blockInsnInfo != null) { if (blockInsnInfo != null) {
sb.append(blockInsnInfo.toString(secondIndent)); sb.append(blockInsnInfo.toString(secondIndent));
} else { } else {
@@ -92,12 +88,11 @@ public abstract class TraverserState {
sb.append(baseIndent); sb.append(baseIndent);
sb.append("}"); sb.append("}");
return sb.toString(); return sb.toString();
} }
public final CentralityState getCentralityState() { public final CentralityState getCentralityState() {
final CentralityState underlying = getUnderlyingCentralityState(); CentralityState underlying = getUnderlyingCentralityState();
if (underlying == null) { if (underlying == null) {
throw new UnsupportedOperationException("Centrality state is not supported for " + getClass().getName()); throw new UnsupportedOperationException("Centrality state is not supported for " + getClass().getName());
} }
@@ -11,54 +11,49 @@ import jadx.core.dex.visitors.finaly.traverser.handlers.AbstractActivePathTraver
import jadx.core.dex.visitors.finaly.traverser.handlers.PredecessorMergeActivePathTraverserHandler; import jadx.core.dex.visitors.finaly.traverser.handlers.PredecessorMergeActivePathTraverserHandler;
public final class UnknownAdvanceStrategyTraverserState extends TraverserState { public final class UnknownAdvanceStrategyTraverserState extends TraverserState {
private final CentralityState centralityState; private final CentralityState centralityState;
private final List<BlockNode> nextBlocks; private final List<BlockNode> nextBlocks;
public UnknownAdvanceStrategyTraverserState(final TraverserActivePathState state, final CentralityState centralityState, public UnknownAdvanceStrategyTraverserState(TraverserActivePathState state, CentralityState centralityState,
final List<BlockNode> nextBlocks) { List<BlockNode> nextBlocks) {
super(state); super(state);
this.centralityState = centralityState; this.centralityState = centralityState;
this.nextBlocks = nextBlocks; this.nextBlocks = nextBlocks;
} }
@Override @Override
public final @Nullable AbstractActivePathTraverserHandler getNextHandler() { public @Nullable AbstractActivePathTraverserHandler getNextHandler() {
return new PredecessorMergeActivePathTraverserHandler(getComparatorState()); return new PredecessorMergeActivePathTraverserHandler(getComparatorState());
} }
@Override @Override
public final ComparisonState getCompareState() { public ComparisonState getCompareState() {
return ComparisonState.READY_TO_COMPARE; return ComparisonState.READY_TO_COMPARE;
} }
@Override @Override
public final boolean isTerminal() { public boolean isTerminal() {
return false; return false;
} }
@Override @Override
protected final @Nullable CentralityState getUnderlyingCentralityState() { protected @Nullable CentralityState getUnderlyingCentralityState() {
return centralityState; return centralityState;
} }
@Override @Override
protected final @Nullable TraverserBlockInfo getUnderlyingBlockInsnInfo() { protected @Nullable TraverserBlockInfo getUnderlyingBlockInsnInfo() {
return null; return null;
} }
@Override @Override
protected final TraverserState duplicateInternalState(final TraverserActivePathState comparatorState) { protected TraverserState duplicateInternalState(TraverserActivePathState comparatorState) {
final CentralityState dCentralityState = centralityState.duplicate(); CentralityState dCentralityState = centralityState.duplicate();
final List<BlockNode> dNextBlocks = new ArrayList<>(nextBlocks); List<BlockNode> dNextBlocks = new ArrayList<>(nextBlocks);
return new UnknownAdvanceStrategyTraverserState(comparatorState, dCentralityState, dNextBlocks);
final TraverserState duplicated = new UnknownAdvanceStrategyTraverserState(comparatorState, dCentralityState, dNextBlocks);
return duplicated;
} }
public final List<BlockNode> getNextBlocks() { public List<BlockNode> getNextBlocks() {
return nextBlocks; return nextBlocks;
} }
} }
@@ -11,44 +11,34 @@ import jadx.core.dex.visitors.finaly.traverser.state.TraverserState;
public final class ImplicitInsnBlockTraverserVisitor extends AbstractBlockTraverserVisitor { public final class ImplicitInsnBlockTraverserVisitor extends AbstractBlockTraverserVisitor {
public static boolean isInstructionImplicit(final InsnNode node) { public static boolean isInstructionImplicit(InsnNode node) {
// An instruction is implicit if it can be safely skipped for comparison when traversing in reverse // An instruction is implicit if it can be safely skipped for comparison when traversing in reverse
// order. // order.
// The presence of a GOTO should be reflected in the structure of a block graph. // The presence of a GOTO should be reflected in the structure of a block graph.
// i.e. a GOTO should be the last instruction of a block with a single successor. // i.e. a GOTO should be the last instruction of a block with a single successor.
// Another example might be NOP. // Another example might be NOP.
final InsnType type = node.getType(); return node.getType() == InsnType.GOTO;
switch (type) {
case GOTO:
return true;
default:
return false;
}
} }
public ImplicitInsnBlockTraverserVisitor(final TraverserState state) { public ImplicitInsnBlockTraverserVisitor(TraverserState state) {
super(state); super(state);
} }
@Override @Override
public final TraverserState visit(final BlockNode block) { public TraverserState visit(BlockNode block) {
final TraverserBlockInfo insnInfo = getState().getBlockInsnInfo(); TraverserBlockInfo insnInfo = getState().getBlockInsnInfo();
List<InsnNode> insns = insnInfo.getInsnsSlice();
ListIterator<InsnNode> insnsIterator = insns.listIterator(insns.size());
final List<InsnNode> insns = insnInfo.getInsnsSlice(); // The number of instructions that have been identified as "implicit" instructions.
final ListIterator<InsnNode> insnsIterator = insns.listIterator(insns.size());
/**
* The number of instructions that have been identified as "implicit" instructions.
*/
int bottomDelta = 0; int bottomDelta = 0;
while (insnsIterator.hasPrevious()) { while (insnsIterator.hasPrevious()) {
final InsnNode insn = insnsIterator.previous(); InsnNode insn = insnsIterator.previous();
if (!isInstructionImplicit(insn)) { if (!isInstructionImplicit(insn)) {
break; break;
} }
bottomDelta++; bottomDelta++;
} }
insnInfo.setBottomOffset(insnInfo.getBottomOffset() + bottomDelta); insnInfo.setBottomOffset(insnInfo.getBottomOffset() + bottomDelta);
insnInfo.setBottomImplicitOffset(insnInfo.getBottomImplicitCount() + bottomDelta); insnInfo.setBottomImplicitOffset(insnInfo.getBottomImplicitCount() + bottomDelta);
return getState(); return getState();
@@ -16,8 +16,8 @@ import jadx.core.dex.visitors.finaly.traverser.state.TraverserState;
public final class PathEndBlockTraverserVisitor extends AbstractBlockTraverserVisitor { public final class PathEndBlockTraverserVisitor extends AbstractBlockTraverserVisitor {
public static boolean isInstructionPathEnd(final InsnNode insn) { public static boolean isInstructionPathEnd(InsnNode insn) {
final InsnType type = insn.getType(); InsnType type = insn.getType();
switch (type) { switch (type) {
case RETURN: case RETURN:
@@ -28,29 +28,24 @@ public final class PathEndBlockTraverserVisitor extends AbstractBlockTraverserVi
} }
} }
public PathEndBlockTraverserVisitor(final TraverserState state) { public PathEndBlockTraverserVisitor(TraverserState state) {
super(state); super(state);
} }
@Override @Override
public final TraverserState visit(final BlockNode block) { public TraverserState visit(BlockNode block) {
final CentralityState centralityState = getState().getCentralityState(); CentralityState centralityState = getState().getCentralityState();
TraverserBlockInfo insnInfo = getState().getBlockInsnInfo();
final TraverserBlockInfo insnInfo = getState().getBlockInsnInfo();
if (!centralityState.getAllowsCentral()) { if (!centralityState.getAllowsCentral()) {
return new AwaitingInsnCompareTraverserState(getComparator(), centralityState, insnInfo); return new AwaitingInsnCompareTraverserState(getComparator(), centralityState, insnInfo);
} }
List<InsnNode> insns = insnInfo.getInsnsSlice();
ListIterator<InsnNode> insnsIterator = insns.listIterator(insns.size());
final List<InsnNode> insns = insnInfo.getInsnsSlice(); // The number of instructions that have been identified as "path end" instructions.
final ListIterator<InsnNode> insnsIterator = insns.listIterator(insns.size());
/**
* The number of instructions that have been identified as "path end" instructions.
*/
int bottomDelta = 0; int bottomDelta = 0;
while (insnsIterator.hasPrevious()) { while (insnsIterator.hasPrevious()) {
final InsnNode insn = insnsIterator.previous(); InsnNode insn = insnsIterator.previous();
// Check if we should ignore the instruction due to it being a "path end" instruction. // Check if we should ignore the instruction due to it being a "path end" instruction.
if (isInstructionPathEnd(insn)) { if (isInstructionPathEnd(insn)) {
@@ -68,7 +63,7 @@ public final class PathEndBlockTraverserVisitor extends AbstractBlockTraverserVi
// RETURN r2 <-- A path end instruction // RETURN r2 <-- A path end instruction
if (insn.getArgsCount() != 0) { if (insn.getArgsCount() != 0) {
final InsnArg handlerExitArg = insn.getArg(0); InsnArg handlerExitArg = insn.getArg(0);
// Returned values from instructions can only be register args so we check that the input to the // Returned values from instructions can only be register args so we check that the input to the
// path end insn is a register arg // path end insn is a register arg
if (handlerExitArg instanceof RegisterArg) { if (handlerExitArg instanceof RegisterArg) {
@@ -89,8 +84,8 @@ public final class PathEndBlockTraverserVisitor extends AbstractBlockTraverserVi
insnInfo.setBottomOffset(insnInfo.getBottomOffset() + bottomDelta); insnInfo.setBottomOffset(insnInfo.getBottomOffset() + bottomDelta);
final BlockNode sourceBlock = insnInfo.getBlock(); BlockNode sourceBlock = insnInfo.getBlock();
final boolean noInstructionsLeft = insnInfo.getBottomOffset() >= sourceBlock.getInstructions().size(); boolean noInstructionsLeft = insnInfo.getBottomOffset() >= sourceBlock.getInstructions().size();
if (noInstructionsLeft) { if (noInstructionsLeft) {
// Mark the state to request finding predecessors to search for duplicate instructions for // Mark the state to request finding predecessors to search for duplicate instructions for
return new NoBlockTraverserState(getComparator(), centralityState, sourceBlock); return new NoBlockTraverserState(getComparator(), centralityState, sourceBlock);
@@ -14,31 +14,28 @@ import jadx.core.utils.ListUtils;
public final class PredecessorBlockTraverserVisitor extends AbstractBlockTraverserVisitor { public final class PredecessorBlockTraverserVisitor extends AbstractBlockTraverserVisitor {
public PredecessorBlockTraverserVisitor(final TraverserState state) { public PredecessorBlockTraverserVisitor(TraverserState state) {
super(state); super(state);
} }
@Override @Override
public final TraverserState visit(final BlockNode block) { public TraverserState visit(BlockNode block) {
TraverserState currentState = getState();
final TraverserState currentState = getState(); CentralityState centralityState = currentState.getCentralityState();
final CentralityState centralityState = currentState.getCentralityState(); GlobalTraverserSourceState globalState = currentState.getGlobalState();
final GlobalTraverserSourceState globalState = currentState.getGlobalState();
final List<BlockNode> predecessors = block.getPredecessors();
final List<BlockNode> containedPredecessors = ListUtils.filter(predecessors, globalState::isBlockContained);
final int predecessorsCount = containedPredecessors.size();
List<BlockNode> predecessors = block.getPredecessors();
List<BlockNode> containedPredecessors = ListUtils.filter(predecessors, globalState::isBlockContained);
int predecessorsCount = containedPredecessors.size();
switch (predecessorsCount) { switch (predecessorsCount) {
case 0: case 0:
return new TerminalTraverserState(getComparator(), TerminalTraverserState.TerminationReason.END_OF_PATH); return new TerminalTraverserState(getComparator(), TerminalTraverserState.TerminationReason.END_OF_PATH);
case 1: case 1:
final BlockNode nextBlock = containedPredecessors.get(0); BlockNode nextBlock = containedPredecessors.get(0);
final TraverserBlockInfo blockInfo = new TraverserBlockInfo(nextBlock); TraverserBlockInfo blockInfo = new TraverserBlockInfo(nextBlock);
return new NewBlockTraverserState(getComparator(), centralityState, blockInfo); return new NewBlockTraverserState(getComparator(), centralityState, blockInfo);
default: default:
return new UnknownAdvanceStrategyTraverserState(getComparator(), centralityState, containedPredecessors); return new UnknownAdvanceStrategyTraverserState(getComparator(), centralityState, containedPredecessors);
} }
} }
} }
@@ -4,5 +4,5 @@ import jadx.core.dex.visitors.finaly.traverser.state.TraverserActivePathState;
public abstract class AbstractTraverserComparatorVisitor { public abstract class AbstractTraverserComparatorVisitor {
public abstract TraverserActivePathState visit(final TraverserActivePathState state); public abstract TraverserActivePathState visit(TraverserActivePathState state);
} }
@@ -19,42 +19,42 @@ import jadx.core.utils.Pair;
public final class InstructionBlockComparatorTraverserVisitor extends AbstractTraverserComparatorVisitor { public final class InstructionBlockComparatorTraverserVisitor extends AbstractTraverserComparatorVisitor {
private static TraverserActivePathState createStateForPerfectMatch(final TraverserActivePathState previousState, private static TraverserActivePathState createStateForPerfectMatch(TraverserActivePathState previousState,
final BlockNode finallyBlock, BlockNode finallyBlock,
final BlockNode candidateBlock) { BlockNode candidateBlock) {
final CentralityState finallyCentralityState = previousState.getFinallyState().getCentralityState().duplicate(); CentralityState finallyCentralityState = previousState.getFinallyState().getCentralityState().duplicate();
final CentralityState candidateCentralityState = previousState.getCandidateState().getCentralityState().duplicate(); CentralityState candidateCentralityState = previousState.getCandidateState().getCentralityState().duplicate();
finallyCentralityState.setAllowsCentral(false); finallyCentralityState.setAllowsCentral(false);
candidateCentralityState.setAllowsCentral(false); candidateCentralityState.setAllowsCentral(false);
finallyCentralityState.setAllowsNonStartingNode(false); finallyCentralityState.setAllowsNonStartingNode(false);
candidateCentralityState.setAllowsNonStartingNode(false); candidateCentralityState.setAllowsNonStartingNode(false);
final TraverserStateFactory<NoBlockTraverserState> finallyStateProducer = TraverserStateFactory<NoBlockTraverserState> finallyStateProducer =
NoBlockTraverserState.getFactory(finallyCentralityState, finallyBlock); NoBlockTraverserState.getFactory(finallyCentralityState, finallyBlock);
final TraverserStateFactory<NoBlockTraverserState> candidateStateProducer = TraverserStateFactory<NoBlockTraverserState> candidateStateProducer =
NoBlockTraverserState.getFactory(candidateCentralityState, candidateBlock); NoBlockTraverserState.getFactory(candidateCentralityState, candidateBlock);
return TraverserActivePathState.produceFromFactories(previousState, finallyStateProducer, candidateStateProducer); return TraverserActivePathState.produceFromFactories(previousState, finallyStateProducer, candidateStateProducer);
} }
private static TraverserActivePathState createStateForUnevenMatch(final TraverserActivePathState previousState, private static TraverserActivePathState createStateForUnevenMatch(TraverserActivePathState previousState,
final TraverserState finallyState, TraverserState finallyState,
final TraverserState candidateState, final BlockNode finallyBlock, final BlockNode candidateBlock, final int finallyInsnsSize, TraverserState candidateState, BlockNode finallyBlock, BlockNode candidateBlock, int finallyInsnsSize,
final int candidateInsnsSize) { int candidateInsnsSize) {
final int maxIterateCount = Math.max(finallyInsnsSize, candidateInsnsSize); int maxIterateCount = Math.max(finallyInsnsSize, candidateInsnsSize);
final boolean finallyOverruns = finallyInsnsSize > candidateInsnsSize; boolean finallyOverruns = finallyInsnsSize > candidateInsnsSize;
final int insnsDelta; int insnsDelta;
final TraverserStateFactory<?> newFinallyStateProducer; TraverserStateFactory<?> newFinallyStateProducer;
final TraverserStateFactory<?> newCandidateStateProducer; TraverserStateFactory<?> newCandidateStateProducer;
final TraverserBlockInfo adjustedBlockInfo; TraverserBlockInfo adjustedBlockInfo;
if (finallyOverruns) { if (finallyOverruns) {
// More finally instructions than candidate instructions // More finally instructions than candidate instructions
final CentralityState candidateCentralityState = candidateState.getCentralityState().duplicate(); CentralityState candidateCentralityState = candidateState.getCentralityState().duplicate();
candidateCentralityState.setAllowsCentral(false); candidateCentralityState.setAllowsCentral(false);
candidateCentralityState.setAllowsNonStartingNode(false); candidateCentralityState.setAllowsNonStartingNode(false);
final CentralityState finallyCentralityState = finallyState.getCentralityState(); CentralityState finallyCentralityState = finallyState.getCentralityState();
finallyCentralityState.setAllowsCentral(false); finallyCentralityState.setAllowsCentral(false);
finallyCentralityState.setAllowsNonStartingNode(false); finallyCentralityState.setAllowsNonStartingNode(false);
@@ -64,10 +64,10 @@ public final class InstructionBlockComparatorTraverserVisitor extends AbstractTr
newCandidateStateProducer = NoBlockTraverserState.getFactory(candidateCentralityState, candidateBlock); newCandidateStateProducer = NoBlockTraverserState.getFactory(candidateCentralityState, candidateBlock);
} else { } else {
// More candidate instructions than finally instructions // More candidate instructions than finally instructions
final CentralityState finallyCentralityState = finallyState.getCentralityState().duplicate(); CentralityState finallyCentralityState = finallyState.getCentralityState().duplicate();
finallyCentralityState.setAllowsCentral(false); finallyCentralityState.setAllowsCentral(false);
finallyCentralityState.setAllowsNonStartingNode(false); finallyCentralityState.setAllowsNonStartingNode(false);
final CentralityState candidateCentralityState = candidateState.getCentralityState(); CentralityState candidateCentralityState = candidateState.getCentralityState();
candidateCentralityState.setAllowsCentral(false); candidateCentralityState.setAllowsCentral(false);
candidateCentralityState.setAllowsNonStartingNode(false); candidateCentralityState.setAllowsNonStartingNode(false);
@@ -82,11 +82,11 @@ public final class InstructionBlockComparatorTraverserVisitor extends AbstractTr
return TraverserActivePathState.produceFromFactories(previousState, newFinallyStateProducer, newCandidateStateProducer); return TraverserActivePathState.produceFromFactories(previousState, newFinallyStateProducer, newCandidateStateProducer);
} }
private static TraverserActivePathState createStateForBlockSkip(final TraverserActivePathState previousState, private static TraverserActivePathState createStateForBlockSkip(TraverserActivePathState previousState,
final TraverserState finallyState, TraverserState finallyState,
final TraverserState candidateState, final BlockNode finallyBlock, final BlockNode candidateBlock) { TraverserState candidateState, BlockNode finallyBlock, BlockNode candidateBlock) {
final CentralityState finallyCentralityState = finallyState.getCentralityState(); CentralityState finallyCentralityState = finallyState.getCentralityState();
final CentralityState candidateCentralityState = candidateState.getCentralityState(); CentralityState candidateCentralityState = candidateState.getCentralityState();
// TODO: Maybe replace this with controller logic so that we can determine if we need to use these // TODO: Maybe replace this with controller logic so that we can determine if we need to use these
// as path ends and then merge above path? // as path ends and then merge above path?
@@ -95,23 +95,23 @@ public final class InstructionBlockComparatorTraverserVisitor extends AbstractTr
// later iteration. // later iteration.
if (finallyCentralityState.getAllowsNonStartingNode()) { if (finallyCentralityState.getAllowsNonStartingNode()) {
finallyCentralityState.setAllowsNonStartingNode(false); finallyCentralityState.setAllowsNonStartingNode(false);
final TraverserStateFactory<NoBlockTraverserState> newFinallyStateProducer = TraverserStateFactory<NoBlockTraverserState> newFinallyStateProducer =
NoBlockTraverserState.getFactory(finallyCentralityState, finallyBlock); NoBlockTraverserState.getFactory(finallyCentralityState, finallyBlock);
final TraverserStateFactory<?> newCandidateStateProducer = new DuplicatedTraverserStateFactory<>(candidateState); TraverserStateFactory<?> newCandidateStateProducer = new DuplicatedTraverserStateFactory<>(candidateState);
return TraverserActivePathState.produceFromFactories(previousState, newFinallyStateProducer, newCandidateStateProducer); return TraverserActivePathState.produceFromFactories(previousState, newFinallyStateProducer, newCandidateStateProducer);
} else { } else {
candidateCentralityState.setAllowsNonStartingNode(false); candidateCentralityState.setAllowsNonStartingNode(false);
final TraverserStateFactory<NoBlockTraverserState> newCandidateStateProducer = TraverserStateFactory<NoBlockTraverserState> newCandidateStateProducer =
NoBlockTraverserState.getFactory(candidateCentralityState, candidateBlock); NoBlockTraverserState.getFactory(candidateCentralityState, candidateBlock);
final TraverserStateFactory<?> newFinallyStateProducer = new DuplicatedTraverserStateFactory<>(finallyState); TraverserStateFactory<?> newFinallyStateProducer = new DuplicatedTraverserStateFactory<>(finallyState);
return TraverserActivePathState.produceFromFactories(previousState, newFinallyStateProducer, newCandidateStateProducer); return TraverserActivePathState.produceFromFactories(previousState, newFinallyStateProducer, newCandidateStateProducer);
} }
} }
private static TraverserActivePathState createStateForTerminatorState(final TraverserActivePathState previousState) { private static TraverserActivePathState createStateForTerminatorState(TraverserActivePathState previousState) {
final TraverserStateFactory<TerminalTraverserState> finallyStateProducer = TraverserStateFactory<TerminalTraverserState> finallyStateProducer =
TerminalTraverserState.getFactory(TerminalTraverserState.TerminationReason.NON_MATCHING_INSTRUCTIONS); TerminalTraverserState.getFactory(TerminalTraverserState.TerminationReason.NON_MATCHING_INSTRUCTIONS);
final TraverserStateFactory<TerminalTraverserState> candidateStateProducer = TraverserStateFactory<TerminalTraverserState> candidateStateProducer =
TerminalTraverserState.getFactory(TerminalTraverserState.TerminationReason.NON_MATCHING_INSTRUCTIONS); TerminalTraverserState.getFactory(TerminalTraverserState.TerminationReason.NON_MATCHING_INSTRUCTIONS);
return TraverserActivePathState.produceFromFactories(previousState, finallyStateProducer, candidateStateProducer); return TraverserActivePathState.produceFromFactories(previousState, finallyStateProducer, candidateStateProducer);
@@ -120,57 +120,57 @@ public final class InstructionBlockComparatorTraverserVisitor extends AbstractTr
private final SameInstructionsStrategy sameInstructionsStrategy = new SameInstructionsStrategyImpl(); private final SameInstructionsStrategy sameInstructionsStrategy = new SameInstructionsStrategyImpl();
@Override @Override
public final TraverserActivePathState visit(final TraverserActivePathState state) { public TraverserActivePathState visit(TraverserActivePathState state) {
final TraverserState finallyState = state.getFinallyState(); TraverserState finallyState = state.getFinallyState();
final TraverserState candidateState = state.getCandidateState(); TraverserState candidateState = state.getCandidateState();
final TraverserBlockInfo finallyBlockInfo = finallyState.getBlockInsnInfo(); TraverserBlockInfo finallyBlockInfo = finallyState.getBlockInsnInfo();
final TraverserBlockInfo candidateBlockInfo = candidateState.getBlockInsnInfo(); TraverserBlockInfo candidateBlockInfo = candidateState.getBlockInsnInfo();
if (finallyBlockInfo == null || candidateBlockInfo == null) { if (finallyBlockInfo == null || candidateBlockInfo == null) {
throw new UnsupportedOperationException( throw new UnsupportedOperationException(
"The instruction comparator handler has received a state which does not support block insn info"); "The instruction comparator handler has received a state which does not support block insn info");
} }
final BlockNode finallyBlock = finallyBlockInfo.getBlock(); BlockNode finallyBlock = finallyBlockInfo.getBlock();
final BlockNode candidateBlock = candidateBlockInfo.getBlock(); BlockNode candidateBlock = candidateBlockInfo.getBlock();
final List<InsnNode> finallyInsns = finallyBlockInfo.getInsnsSlice(); List<InsnNode> finallyInsns = finallyBlockInfo.getInsnsSlice();
final List<InsnNode> candidateInsns = candidateBlockInfo.getInsnsSlice(); List<InsnNode> candidateInsns = candidateBlockInfo.getInsnsSlice();
final int finallyInsnsSize = finallyInsns.size(); int finallyInsnsSize = finallyInsns.size();
final int candidateInsnsSize = candidateInsns.size(); int candidateInsnsSize = candidateInsns.size();
final int maxIterateCount = Math.min(finallyInsnsSize, candidateInsnsSize); int maxIterateCount = Math.min(finallyInsnsSize, candidateInsnsSize);
final List<Pair<InsnNode>> matchingInsns = new ArrayList<>(maxIterateCount); List<Pair<InsnNode>> matchingInsns = new ArrayList<>(maxIterateCount);
// Search through each instruction in reverse and see how many match // Search through each instruction in reverse and see how many match
for (int i = 0; i < maxIterateCount; i++) { for (int i = 0; i < maxIterateCount; i++) {
final InsnNode candidateInsn = candidateInsns.get(candidateInsnsSize - i - 1); InsnNode candidateInsn = candidateInsns.get(candidateInsnsSize - i - 1);
final InsnNode finallyInsn = finallyInsns.get(finallyInsnsSize - i - 1); InsnNode finallyInsn = finallyInsns.get(finallyInsnsSize - i - 1);
if (!sameInstructionsStrategy.sameInsns(candidateInsn, finallyInsn)) { if (!sameInstructionsStrategy.sameInsns(candidateInsn, finallyInsn)) {
break; break;
} }
final Pair<InsnNode> match = new Pair<>(finallyInsn, candidateInsn); Pair<InsnNode> match = new Pair<>(finallyInsn, candidateInsn);
matchingInsns.add(match); matchingInsns.add(match);
} }
final int matchedInsnsCount = matchingInsns.size(); int matchedInsnsCount = matchingInsns.size();
state.registerWithBlockInfo(finallyBlockInfo, matchedInsnsCount); state.registerWithBlockInfo(finallyBlockInfo, matchedInsnsCount);
state.registerWithBlockInfo(candidateBlockInfo, matchedInsnsCount); state.registerWithBlockInfo(candidateBlockInfo, matchedInsnsCount);
final boolean finallyOverruns = finallyInsnsSize > candidateInsnsSize; boolean finallyOverruns = finallyInsnsSize > candidateInsnsSize;
final boolean candidateOverruns = finallyInsnsSize < candidateInsnsSize; boolean candidateOverruns = finallyInsnsSize < candidateInsnsSize;
final boolean sameSizedSlices = !finallyOverruns && !candidateOverruns; boolean sameSizedSlices = !finallyOverruns && !candidateOverruns;
final boolean allMatched = matchedInsnsCount == maxIterateCount; boolean allMatched = matchedInsnsCount == maxIterateCount;
final boolean noneMatched = matchedInsnsCount == 0; boolean noneMatched = matchedInsnsCount == 0;
state.getMatchedInsns().addAll(matchingInsns); state.getMatchedInsns().addAll(matchingInsns);
final TraverserActivePathState newState; TraverserActivePathState newState;
if (allMatched) { if (allMatched) {
if (sameSizedSlices) { if (sameSizedSlices) {
// All instructions matched and there are no more instructions to match in either // All instructions matched and there are no more instructions to match in either
@@ -195,9 +195,9 @@ public final class InstructionBlockComparatorTraverserVisitor extends AbstractTr
return newState; return newState;
} }
private boolean eitherStateAllowsBlockSkip(final TraverserState finallyState, final TraverserState candidateState) { private boolean eitherStateAllowsBlockSkip(TraverserState finallyState, TraverserState candidateState) {
final CentralityState finallyCentralityState = finallyState.getCentralityState(); CentralityState finallyCentralityState = finallyState.getCentralityState();
final CentralityState candidateCentralityState = candidateState.getCentralityState(); CentralityState candidateCentralityState = candidateState.getCentralityState();
return finallyCentralityState.getAllowsNonStartingNode() || candidateCentralityState.getAllowsNonStartingNode(); return finallyCentralityState.getAllowsNonStartingNode() || candidateCentralityState.getAllowsNonStartingNode();
} }
@@ -30,7 +30,7 @@ public class SourceFileRename extends AbstractVisitor {
@Override @Override
public void init(RootNode root) throws JadxException { public void init(RootNode root) throws JadxException {
final var useSourceName = root.getArgs().getUseSourceNameAsClassNameAlias(); var useSourceName = root.getArgs().getUseSourceNameAsClassNameAlias();
if (useSourceName == UseSourceNameAsClassNameAlias.NEVER) { if (useSourceName == UseSourceNameAsClassNameAlias.NEVER) {
return; return;
} }
@@ -323,8 +323,8 @@ public class TypeCompare {
return WIDER; return WIDER;
} }
final int type1Width = getTypeWidth(type1); int type1Width = getTypeWidth(type1);
final int type2Width = getTypeWidth(type2); int type2Width = getTypeWidth(type2);
if (type1Width > type2Width) { if (type1Width > type2Width) {
return WIDER; return WIDER;
} else if (type1Width < type2Width) { } else if (type1Width < type2Width) {
@@ -44,12 +44,12 @@ public class BetterName {
: secondName; : secondName;
} }
final var firstResult = analyze(firstName); var firstResult = analyze(firstName);
final var secondResult = analyze(secondName); var secondResult = analyze(secondName);
if (firstResult.digitCount != 0 || secondResult.digitCount != 0) { if (firstResult.digitCount != 0 || secondResult.digitCount != 0) {
final var firstRatio = (float) firstResult.digitCount / firstResult.length; var firstRatio = (float) firstResult.digitCount / firstResult.length;
final var secondRatio = (float) secondResult.digitCount / secondResult.length; var secondRatio = (float) secondResult.digitCount / secondResult.length;
if (Math.abs(secondRatio - firstRatio) >= TOLERANCE) { if (Math.abs(secondRatio - firstRatio) >= TOLERANCE) {
return firstRatio <= secondRatio return firstRatio <= secondRatio
@@ -64,7 +64,7 @@ public class BetterName {
} }
private static AnalyzeResult analyze(String name) { private static AnalyzeResult analyze(String name) {
final var result = new AnalyzeResult(); var result = new AnalyzeResult();
StringUtils.visitCodePoints(name, cp -> { StringUtils.visitCodePoints(name, cp -> {
if (Character.isDigit(cp)) { if (Character.isDigit(cp)) {
@@ -40,11 +40,11 @@ public class ListUtils {
if (list1.size() != list2.size()) { if (list1.size() != list2.size()) {
return false; return false;
} }
final Iterator<T> iter1 = list1.iterator(); Iterator<T> iter1 = list1.iterator();
final Iterator<U> iter2 = list2.iterator(); Iterator<U> iter2 = list2.iterator();
while (iter1.hasNext() && iter2.hasNext()) { while (iter1.hasNext() && iter2.hasNext()) {
final T item1 = iter1.next(); T item1 = iter1.next();
final U item2 = iter2.next(); U item2 = iter2.next();
if (!comparer.test(item1, item2)) { if (!comparer.test(item1, item2)) {
return false; return false;
} }
@@ -26,7 +26,7 @@ class ResNameUtils {
return postfix; return postfix;
} }
final StringBuilder sb = new StringBuilder(name.length() + 1); StringBuilder sb = new StringBuilder(name.length() + 1);
boolean nameChanged = false; boolean nameChanged = false;
int cp = name.codePointAt(0); int cp = name.codePointAt(0);
@@ -51,7 +51,7 @@ class ResNameUtils {
} }
} }
final String sanitizedName = sb.toString(); String sanitizedName = sb.toString();
if (NameMapper.isReserved(sanitizedName)) { if (NameMapper.isReserved(sanitizedName)) {
nameChanged = true; nameChanged = true;
} }
@@ -483,7 +483,7 @@ public class ResTableBinaryParser extends CommonBinaryParser implements IResTabl
} }
if (constField != null) { if (constField != null) {
final String newFieldName = ResNameUtils.convertToRFieldName(newResName); String newFieldName = ResNameUtils.convertToRFieldName(newResName);
constField.rename(newFieldName); constField.rename(newFieldName);
constField.add(AFlag.DONT_RENAME); constField.add(AFlag.DONT_RENAME);
} }
@@ -500,7 +500,7 @@ public class ResTableBinaryParser extends CommonBinaryParser implements IResTabl
} }
if (root.getArgs().isRenameValid()) { if (root.getArgs().isRenameValid()) {
final boolean allowNonPrintable = !root.getArgs().isRenamePrintable(); boolean allowNonPrintable = !root.getArgs().isRenamePrintable();
newResName = ResNameUtils.sanitizeAsResourceName(newResName, String.format("_res_0x%08x", resRef), allowNonPrintable); newResName = ResNameUtils.sanitizeAsResourceName(newResName, String.format("_res_0x%08x", resRef), allowNonPrintable);
} }
@@ -225,7 +225,7 @@ public class ResXmlGen {
} }
break; break;
case "plurals": case "plurals":
final String quantity = PLURALS_MAP.get(value.getNameRef()); String quantity = PLURALS_MAP.get(value.getNameRef());
addSimpleValue(cw, typeName, itemTag, "quantity", quantity, valueStr); addSimpleValue(cw, typeName, itemTag, "quantity", quantity, valueStr);
break; break;
default: default:
@@ -36,7 +36,7 @@ public class StringFormattedCheck {
return false; return false;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Duo<T1, T2> other = (Duo<T1, T2>) obj; Duo<T1, T2> other = (Duo<T1, T2>) obj;
if (!Objects.equals(this.m1, other.m1)) { if (!Objects.equals(this.m1, other.m1)) {
return false; return false;
} }
@@ -907,7 +907,7 @@ public class XMLChar {
* @return true if name is a valid Name * @return true if name is a valid Name
*/ */
public static boolean isValidName(String name) { public static boolean isValidName(String name) {
final int length = name.length(); int length = name.length();
if (length == 0) { if (length == 0) {
return false; return false;
} }
@@ -937,7 +937,7 @@ public class XMLChar {
* @return true if name is a valid NCName * @return true if name is a valid NCName
*/ */
public static boolean isValidNCName(String ncName) { public static boolean isValidNCName(String ncName) {
final int length = ncName.length(); int length = ncName.length();
if (length == 0) { if (length == 0) {
return false; return false;
} }
@@ -966,7 +966,7 @@ public class XMLChar {
* @return true if nmtoken is a valid Nmtoken * @return true if nmtoken is a valid Nmtoken
*/ */
public static boolean isValidNmtoken(String nmtoken) { public static boolean isValidNmtoken(String nmtoken) {
final int length = nmtoken.length(); int length = nmtoken.length();
if (length == 0) { if (length == 0) {
return false; return false;
} }
@@ -1049,7 +1049,7 @@ public class XMLChar {
public static String trim(String value) { public static String trim(String value) {
int start; int start;
int end; int end;
final int lengthMinusOne = value.length() - 1; int lengthMinusOne = value.length() - 1;
for (start = 0; start <= lengthMinusOne; ++start) { for (start = 0; start <= lengthMinusOne; ++start) {
if (!isSpace(value.charAt(start))) { if (!isSpace(value.charAt(start))) {
break; break;
@@ -488,7 +488,7 @@ public class EntryConfig {
if (getClass() != obj.getClass()) { if (getClass() != obj.getClass()) {
return false; return false;
} }
final EntryConfig other = (EntryConfig) obj; EntryConfig other = (EntryConfig) obj;
return this.mQualifiers.equals(other.mQualifiers); return this.mQualifiers.equals(other.mQualifiers);
} }