diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/LoopRegionVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/LoopRegionVisitor.java index 834528872796183886a8ce3e39389df0621ac1a0..e75025af785fc29fba9aa6b900e2618337e29505 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/regions/LoopRegionVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/regions/LoopRegionVisitor.java @@ -16,7 +16,6 @@ import jadx.core.dex.instructions.args.LiteralArg; import jadx.core.dex.instructions.args.RegisterArg; import jadx.core.dex.instructions.args.SSAVar; import jadx.core.dex.nodes.BlockNode; -import jadx.core.dex.nodes.DexNode; import jadx.core.dex.nodes.IBlock; import jadx.core.dex.nodes.IRegion; import jadx.core.dex.nodes.InsnNode; @@ -232,14 +231,23 @@ public class LoopRegionVisitor extends AbstractVisitor implements IRegionVisitor } List toSkip = new LinkedList(); RegisterArg iterVar = nextCall.getResult(); + if (iterVar == null) { + return false; + } if (nextCall.contains(AFlag.WRAPPED)) { InsnArg wrapArg = BlockUtils.searchWrappedInsnParent(mth, nextCall); if (wrapArg != null && wrapArg.getParentInsn() != null) { InsnNode parentInsn = wrapArg.getParentInsn(); if (parentInsn.getType() != InsnType.CHECK_CAST) { + if (!fixIterableType(mth, iterableArg, iterVar)) { + return false; + } parentInsn.replaceArg(wrapArg, iterVar); } else { iterVar = parentInsn.getResult(); + if (iterVar == null || !fixIterableType(mth, iterableArg, iterVar)) { + return false; + } InsnArg castArg = BlockUtils.searchWrappedInsnParent(mth, parentInsn); if (castArg != null && castArg.getParentInsn() != null) { castArg.getParentInsn().replaceArg(castArg, iterVar); @@ -255,9 +263,6 @@ public class LoopRegionVisitor extends AbstractVisitor implements IRegionVisitor } else { toSkip.add(nextCall); } - if (iterVar == null || !fixIterableType(mth.dex(), iterableArg, iterVar)) { - return false; - } assignInsn.add(AFlag.SKIP); for (InsnNode insnNode : toSkip) { @@ -267,7 +272,7 @@ public class LoopRegionVisitor extends AbstractVisitor implements IRegionVisitor return true; } - private static boolean fixIterableType(DexNode dex, InsnArg iterableArg, RegisterArg iterVar) { + private static boolean fixIterableType(MethodNode mth, InsnArg iterableArg, RegisterArg iterVar) { ArgType iterableType = iterableArg.getType(); ArgType varType = iterVar.getType(); if (iterableType.isGeneric()) { @@ -283,10 +288,16 @@ public class LoopRegionVisitor extends AbstractVisitor implements IRegionVisitor iterVar.setType(gType); return true; } - if (ArgType.isInstanceOf(dex, gType, varType)) { + if (ArgType.isInstanceOf(mth.dex(), gType, varType)) { + return true; + } + ArgType wildcardType = gType.getWildcardType(); + if (wildcardType != null + && gType.getWildcardBounds() == 1 + && ArgType.isInstanceOf(mth.dex(), wildcardType, varType)) { return true; } - LOG.warn("Generic type differs: {} and {}", gType, varType); + LOG.warn("Generic type differs: '{}' and '{}' in {}", gType, varType, mth); return false; } if (!iterableArg.isRegister()) { diff --git a/jadx-core/src/test/java/jadx/tests/integration/generics/TestGenerics6.java b/jadx-core/src/test/java/jadx/tests/integration/generics/TestGenerics6.java new file mode 100644 index 0000000000000000000000000000000000000000..562e27d69383f83f719cc9a32dbbacc9f390d132 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/generics/TestGenerics6.java @@ -0,0 +1,47 @@ +package jadx.tests.integration.generics; + +import jadx.core.dex.nodes.ClassNode; +import jadx.tests.api.IntegrationTest; + +import java.util.Collection; + +import org.junit.Test; + +import static jadx.tests.api.utils.JadxMatchers.containsOne; +import static org.junit.Assert.assertThat; + +public class TestGenerics6 extends IntegrationTest { + + public static class TestCls { + public void test1(Collection as) { + for (A a : as) { + a.f(); + } + } + + public void test2(Collection is) { + for (I i : is) { + i.f(); + } + } + + private interface I { + void f(); + } + + private class A implements I { + public void f() { + } + } + } + + @Test + public void test() { + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + + assertThat(code, containsOne("for (A a : as) {")); + // TODO: fix iterable arg type (unexpected cast to A in bytecode) +// assertThat(code, containsOne("for (I i : is) {")); + } +}