未验证 提交 b66293a2 编写于 作者: S Skylot

fix: handle wildcard in invoke type resolver (#1238)

上级 abcaafa8
......@@ -192,6 +192,11 @@ public class SSAVar {
return usedInPhi;
}
public boolean isAssignInPhi() {
InsnNode assignInsn = getAssignInsn();
return assignInsn != null && assignInsn.getType() == InsnType.PHI;
}
public boolean isUsedInPhi() {
return usedInPhi != null && !usedInPhi.isEmpty();
}
......
......@@ -52,6 +52,13 @@ public class MoveInlineVisitor extends AbstractVisitor {
if (resultArg.sameRegAndSVar(moveArg)) {
return true;
}
if (moveArg.isRegister()) {
RegisterArg moveReg = (RegisterArg) moveArg;
if (moveReg.getSVar().isAssignInPhi()) {
// don't mix already merged variables
return false;
}
}
SSAVar ssaVar = resultArg.getSVar();
if (ssaVar.isUsedInPhi()) {
return deleteMove(mth, move);
......
......@@ -46,6 +46,10 @@ public final class TypeBoundCheckCastAssign implements ITypeBoundDynamic {
return insn.getResult();
}
public IndexInsnNode getInsn() {
return insn;
}
@Override
public String toString() {
return "CHECK_CAST_ASSIGN{(" + insn.getIndex() + ") " + insn.getArg(0).getType() + "}";
......
package jadx.core.dex.visitors.typeinference;
import org.jetbrains.annotations.Nullable;
import jadx.core.dex.instructions.InvokeNode;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.InsnArg;
......@@ -48,12 +50,24 @@ public final class TypeBoundInvokeAssign implements ITypeBoundDynamic {
mthDeclType = instanceType;
}
ArgType resultGeneric = root.getTypeUtils().replaceClassGenerics(instanceType, mthDeclType, genericReturnType);
if (resultGeneric != null && !resultGeneric.isWildcard()) {
return resultGeneric;
ArgType result = processResultType(resultGeneric);
if (result != null) {
return result;
}
return invokeNode.getCallMth().getReturnType();
}
@Nullable
private ArgType processResultType(@Nullable ArgType resultGeneric) {
if (resultGeneric == null) {
return null;
}
if (!resultGeneric.isWildcard()) {
return resultGeneric;
}
return resultGeneric.getWildcardType();
}
private InsnArg getInstanceArg() {
return invokeNode.getArg(0);
}
......
......@@ -57,6 +57,7 @@ import jadx.core.dex.visitors.ssa.SSATransform;
import jadx.core.utils.BlockUtils;
import jadx.core.utils.InsnList;
import jadx.core.utils.InsnUtils;
import jadx.core.utils.ListUtils;
import jadx.core.utils.Utils;
import jadx.core.utils.exceptions.JadxOverflowException;
......@@ -83,6 +84,7 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
this.resolvers = Arrays.asList(
this::initTypeBounds,
this::runTypePropagation,
this::tryRestoreTypeVarCasts,
this::tryInsertCasts,
this::tryDeduceTypes,
this::trySplitConstInsns,
......@@ -520,7 +522,60 @@ public final class TypeInferenceVisitor extends AbstractVisitor {
return false;
}
@SuppressWarnings("ForLoopReplaceableByWhile")
/**
* Fix check casts to type var extend type:
* <br>
* {@code <T extends Comparable> T var = (Comparable) obj; => T var = (T) obj; }
*/
private boolean tryRestoreTypeVarCasts(MethodNode mth) {
int changed = 0;
List<SSAVar> mthSVars = mth.getSVars();
for (SSAVar var : mthSVars) {
changed += restoreTypeVarCasts(var);
}
if (changed == 0) {
return false;
}
if (Consts.DEBUG_TYPE_INFERENCE) {
mth.addDebugComment("Restore " + changed + " type vars casts");
}
initTypeBounds(mth);
return runTypePropagation(mth);
}
private int restoreTypeVarCasts(SSAVar var) {
TypeInfo typeInfo = var.getTypeInfo();
Set<ITypeBound> bounds = typeInfo.getBounds();
if (!ListUtils.anyMatch(bounds, t -> t.getType().isGenericType())) {
return 0;
}
List<ITypeBound> casts = ListUtils.filter(bounds, TypeBoundCheckCastAssign.class::isInstance);
if (casts.isEmpty()) {
return 0;
}
ArgType bestType = selectBestTypeFromBounds(bounds).orElse(ArgType.UNKNOWN);
if (!bestType.isGenericType()) {
return 0;
}
List<ArgType> extendTypes = bestType.getExtendTypes();
if (extendTypes.size() != 1) {
return 0;
}
int fixed = 0;
ArgType extendType = extendTypes.get(0);
for (ITypeBound bound : casts) {
TypeBoundCheckCastAssign cast = (TypeBoundCheckCastAssign) bound;
ArgType castType = cast.getType();
TypeCompareEnum result = typeUpdate.getTypeCompare().compareTypes(extendType, castType);
if (result.isEqual() || result == TypeCompareEnum.NARROW_BY_GENERIC) {
cast.getInsn().updateIndex(bestType);
fixed++;
}
}
return fixed;
}
@SuppressWarnings({ "ForLoopReplaceableByWhile", "ForLoopReplaceableByForEach" })
private boolean tryInsertCasts(MethodNode mth) {
int added = 0;
List<SSAVar> mthSVars = mth.getSVars();
......
......@@ -112,7 +112,7 @@ public class ListUtils {
return list;
}
public static <T> List<T> filter(List<T> list, Predicate<T> filter) {
public static <T> List<T> filter(Collection<T> list, Predicate<T> filter) {
if (list == null || list.isEmpty()) {
return Collections.emptyList();
}
......@@ -148,7 +148,7 @@ public class ListUtils {
return found;
}
public static <T> boolean allMatch(List<T> list, Predicate<T> test) {
public static <T> boolean allMatch(Collection<T> list, Predicate<T> test) {
if (list == null || list.isEmpty()) {
return false;
}
......@@ -160,7 +160,7 @@ public class ListUtils {
return true;
}
public static <T> boolean anyMatch(List<T> list, Predicate<T> test) {
public static <T> boolean anyMatch(Collection<T> list, Predicate<T> test) {
if (list == null || list.isEmpty()) {
return false;
}
......
package jadx.tests.integration.types;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.junit.jupiter.api.Test;
import jadx.tests.api.SmaliTest;
import jadx.tests.api.extensions.profiles.TestProfile;
import jadx.tests.api.extensions.profiles.TestWithProfiles;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
/**
* Issue 1238
*/
public class TestTypeResolver20 extends SmaliTest {
public static class TestCls {
public interface Sequence<T> {
Iterator<T> iterator();
}
public static <T extends Comparable<? super T>> T max(Sequence<? extends T> seq) {
Iterator<? extends T> it = seq.iterator();
if (!it.hasNext()) {
return null;
}
T t = it.next();
while (it.hasNext()) {
T next = it.next();
if (t.compareTo(next) < 0) {
t = next;
}
}
return t;
}
private static class ArraySeq<T> implements Sequence<T> {
private final List<T> list;
@SafeVarargs
public ArraySeq(T... arr) {
this.list = Arrays.asList(arr);
}
@Override
public Iterator<T> iterator() {
return list.iterator();
}
}
public void check() {
assertThat(max(new ArraySeq<>(2, 5, 3, 4))).isEqualTo(5);
}
}
@TestWithProfiles({ TestProfile.DX_J8, TestProfile.JAVA8 })
public void test() {
noDebugInfo();
assertThat(getClassNode(TestCls.class))
.code()
.doesNotContain("next = next;")
.containsOne("T next = it.next();");
}
@Test
public void testSmali() {
assertThat(getClassNodeFromSmaliFiles())
.code()
.containsOne("T next = it.next();")
.containsOne("T next2 = it.next();");
}
}
.class public interface abstract Lkotlin/sequences/Sequence;
.super Ljava/lang/Object;
.source "SourceFile"
.annotation system Ldalvik/annotation/Signature;
value = {
"<T:",
"Ljava/lang/Object;",
">",
"Ljava/lang/Object;"
}
.end annotation
.method public abstract iterator()Ljava/util/Iterator;
.annotation system Ldalvik/annotation/Signature;
value = {
"()",
"Ljava/util/Iterator<",
"TT;>;"
}
.end annotation
.end method
.class public Ltypes/TestTypeResolver20;
.super Ljava/lang/Object;
.source "SourceFile"
.method public static final max(Lkotlin/sequences/Sequence;)Ljava/lang/Comparable;
.registers 4
.annotation system Ldalvik/annotation/Signature;
value = {
"<T::",
"Ljava/lang/Comparable<",
"-TT;>;>(",
"Lkotlin/sequences/Sequence<",
"+TT;>;)TT;"
}
.end annotation
.line 1147
invoke-interface {p0}, Lkotlin/sequences/Sequence;->iterator()Ljava/util/Iterator;
move-result-object p0
.line 1148
invoke-interface {p0}, Ljava/util/Iterator;->hasNext()Z
move-result v0
if-nez v0, :cond_11
const/4 p0, 0x0
return-object p0
.line 1149
:cond_11
invoke-interface {p0}, Ljava/util/Iterator;->next()Ljava/lang/Object;
move-result-object v0
check-cast v0, Ljava/lang/Comparable;
.line 1150
:cond_17
:goto_17
invoke-interface {p0}, Ljava/util/Iterator;->hasNext()Z
move-result v1
if-eqz v1, :cond_2b
.line 1151
invoke-interface {p0}, Ljava/util/Iterator;->next()Ljava/lang/Object;
move-result-object v1
check-cast v1, Ljava/lang/Comparable;
.line 1152
invoke-interface {v0, v1}, Ljava/lang/Comparable;->compareTo(Ljava/lang/Object;)I
move-result v2
if-gez v2, :cond_17
move-object v0, v1
goto :goto_17
:cond_2b
return-object v0
.end method
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册