Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package jadx.core.dex.visitors.typeinference;

import java.util.List;

import jadx.core.dex.info.FieldInfo;
import jadx.core.dex.instructions.IndexInsnNode;
import jadx.core.dex.instructions.args.ArgType;
Expand Down Expand Up @@ -44,7 +46,38 @@ private ArgType getResultType(ArgType instanceType) {
if (resultGeneric != null && !resultGeneric.isWildcard()) {
return resultGeneric;
}
return initType; // TODO: check if this type is allowed in current scope
if (initType.isGenericType() || initType.isArray()) {
// type variable unresolvable from the (raw) instance type: erase it (arrays element-wise)
// to its leftmost bound, like javac, so the result type is valid in the current scope
return eraseTypeVar(initType);
}
return initType;
}

private static ArgType eraseTypeVar(ArgType type) {
if (type.isArray()) {
// erase element, keep rank (T[] -> Object[])
return ArgType.array(eraseTypeVar(type.getArrayElement()));
}
if (type.isGenericType()) {
List<ArgType> bounds = type.getExtendTypes();
if (bounds.isEmpty()) {
return ArgType.OBJECT;
}
ArgType bound = bounds.get(0);
if (bound.isGenericType()) {
// bound is itself a type variable (<S extends T>)
return ArgType.OBJECT;
}
// drop type args so we don't reintroduce an out-of-scope variable (Comparable<T> -> Comparable)
return bound.isGeneric() ? ArgType.object(bound.getObject()) : bound;
}
if (type.isGeneric()) {
// parameterized element (List<T>[]) -> raw
return ArgType.object(type.getObject());
}
// already concrete (array element)
return type;
}

private InsnArg getInstanceArg() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package jadx.tests.integration.generics;

import org.junit.jupiter.api.Test;

import jadx.tests.api.IntegrationTest;

import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;

public class TestRawGenericFieldNullCompare extends IntegrationTest {

public static class TestCls {
public static class NumBox<T extends Number> {
public T value;
}

public static class MultiBox<T extends Number & Comparable<T>> {
public T value;
}

public static class CompBox<T extends Comparable<T>> {
public T value;
}

public static class ArrBox<T extends Number> {
public T[] values;
}

public static boolean isNull(NumBox box) {
return box.value == null;
}

public static int useMulti(MultiBox box) {
if (box.value == null) {
return -1;
}
return box.value.intValue();
}

public static boolean compNull(CompBox box) {
return box.value == null;
}

public static boolean arrNull(ArrBox box) {
return box.values == null;
}
}

@Test
public void test() {
useJavaInput();
noDebugInfo();
assertThat(getClassNode(TestCls.class))
.code()
.contains("== null")
.doesNotContain("== 0")
.doesNotContain("Type inference failed");
}
}