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