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