core: fix loop processing after exception handler remove (fix #59)
This commit is contained in:
@@ -5,6 +5,7 @@ import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.AttrNode;
|
||||
import jadx.core.dex.attributes.nodes.IgnoreEdgeAttr;
|
||||
import jadx.core.dex.attributes.nodes.LoopInfo;
|
||||
import jadx.core.utils.BlockUtils;
|
||||
import jadx.core.utils.EmptyBitSet;
|
||||
import jadx.core.utils.InsnUtils;
|
||||
|
||||
@@ -86,13 +87,8 @@ public class BlockNode extends AttrNode implements IBlock {
|
||||
}
|
||||
List<BlockNode> toRemove = new LinkedList<BlockNode>();
|
||||
for (BlockNode b : sucList) {
|
||||
if (b.contains(AType.EXC_HANDLER)) {
|
||||
if (BlockUtils.isBlockMustBeCleared(b)) {
|
||||
toRemove.add(b);
|
||||
} else if (b.contains(AFlag.SYNTHETIC)) {
|
||||
List<BlockNode> s = b.getSuccessors();
|
||||
if (s.size() == 1 && s.get(0).contains(AType.EXC_HANDLER)) {
|
||||
toRemove.add(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (block.contains(AFlag.LOOP_END)) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import jadx.core.dex.info.ClassInfo;
|
||||
import jadx.core.dex.nodes.BlockNode;
|
||||
import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.utils.BlockUtils;
|
||||
import jadx.core.utils.Utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -63,6 +64,8 @@ public class TryCatchBlock {
|
||||
|
||||
private void unbindHandler(ExceptionHandler handler) {
|
||||
for (BlockNode block : handler.getBlocks()) {
|
||||
// skip synthetic loop exit blocks
|
||||
BlockUtils.skipPredSyntheticPaths(block);
|
||||
block.add(AFlag.SKIP);
|
||||
ExcHandlerAttr excHandlerAttr = block.get(AType.EXC_HANDLER);
|
||||
if (excHandlerAttr != null) {
|
||||
|
||||
@@ -72,16 +72,44 @@ public class BlockUtils {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean isBlockMustBeCleared(BlockNode b) {
|
||||
if (b.contains(AType.EXC_HANDLER) || b.contains(AFlag.SKIP)) {
|
||||
return true;
|
||||
}
|
||||
if (b.contains(AFlag.SYNTHETIC)) {
|
||||
List<BlockNode> s = b.getSuccessors();
|
||||
if (s.size() == 1 && s.get(0).contains(AType.EXC_HANDLER)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove exception handlers from block nodes list
|
||||
*/
|
||||
private static List<BlockNode> cleanBlockList(List<BlockNode> list) {
|
||||
List<BlockNode> ret = new ArrayList<BlockNode>(list.size());
|
||||
for (BlockNode block : list) {
|
||||
if (!block.contains(AType.EXC_HANDLER)) {
|
||||
if (!isBlockMustBeCleared(block)) {
|
||||
ret.add(block);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove exception handlers from block nodes bitset
|
||||
*/
|
||||
public static void cleanBitSet(MethodNode mth, BitSet bs) {
|
||||
for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {
|
||||
BlockNode block = mth.getBasicBlocks().get(i);
|
||||
if (isBlockMustBeCleared(block)) {
|
||||
bs.clear(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return predecessors list without blocks contains 'IGNORE_EDGE' attribute.
|
||||
*
|
||||
@@ -111,18 +139,6 @@ public class BlockUtils {
|
||||
return from.getSuccessors().contains(to);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove exception handlers from block nodes bitset
|
||||
*/
|
||||
public static void cleanBitSet(MethodNode mth, BitSet bs) {
|
||||
for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {
|
||||
BlockNode block = mth.getBasicBlocks().get(i);
|
||||
if (block.contains(AType.EXC_HANDLER)) {
|
||||
bs.clear(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if instruction contains in block (use == for comparison, not equals)
|
||||
*/
|
||||
@@ -461,6 +477,20 @@ public class BlockUtils {
|
||||
return list.isEmpty() ? Collections.<BlockNode>emptyList() : list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 'SKIP' flag for all synthetic predecessors from start block.
|
||||
*/
|
||||
public static void skipPredSyntheticPaths(BlockNode block) {
|
||||
for (BlockNode pred : block.getPredecessors()) {
|
||||
if (pred.contains(AFlag.SYNTHETIC)
|
||||
&& !pred.contains(AType.SPLITTER_BLOCK)
|
||||
&& pred.getInstructions().isEmpty()) {
|
||||
pred.add(AFlag.SKIP);
|
||||
skipPredSyntheticPaths(pred);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if on path from start to end no instructions and no branches.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package jadx.tests.integration.loops;
|
||||
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.tests.api.IntegrationTest;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static jadx.tests.api.utils.JadxMatchers.containsOne;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class TestTryCatchInLoop2 extends IntegrationTest {
|
||||
|
||||
public static class TestCls<T extends String> {
|
||||
private static class MyItem {
|
||||
int idx;
|
||||
String name;
|
||||
}
|
||||
|
||||
private final Map<Integer, MyItem> mCache = new HashMap<Integer, MyItem>();
|
||||
|
||||
void test(MyItem[] items) {
|
||||
synchronized (this.mCache) {
|
||||
for (int i = 0; i < items.length; ++i) {
|
||||
MyItem existingItem = mCache.get(items[i].idx);
|
||||
if (null == existingItem) {
|
||||
mCache.put(items[i].idx, items[i]);
|
||||
} else {
|
||||
existingItem.name = items[i].name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
ClassNode cls = getClassNode(TestCls.class);
|
||||
String code = cls.getCode().toString();
|
||||
|
||||
assertThat(code, containsOne("synchronized (this.mCache) {"));
|
||||
assertThat(code, containsOne("for (int i = 0; i < items.length; i++) {"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user