refactor: move type with outer generic to different class
This commit is contained in:
@@ -458,6 +458,14 @@ public class ClassGen {
|
||||
}
|
||||
|
||||
public void useClass(CodeWriter code, ArgType type) {
|
||||
ArgType outerType = type.getOuterType();
|
||||
if (outerType != null) {
|
||||
useClass(code, outerType);
|
||||
code.add('.');
|
||||
useClass(code, type.getInnerType());
|
||||
return;
|
||||
}
|
||||
|
||||
useClass(code, ClassInfo.fromType(cls.root(), type));
|
||||
ArgType[] generics = type.getGenericTypes();
|
||||
if (generics != null) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package jadx.core.dex.instructions.args;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import jadx.core.Consts;
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
@@ -89,8 +90,8 @@ public abstract class ArgType {
|
||||
return new GenericObject(obj, generics);
|
||||
}
|
||||
|
||||
public static ArgType genericInner(ArgType genericType, String innerName, ArgType[] generics) {
|
||||
return new GenericObject((GenericObject) genericType, innerName, generics);
|
||||
public static ArgType outerGeneric(ArgType genericOuterType, ArgType innerType) {
|
||||
return new OuterGenericObject((GenericObject) genericOuterType, (ObjectType) innerType);
|
||||
}
|
||||
|
||||
public static ArgType array(ArgType vtype) {
|
||||
@@ -244,8 +245,8 @@ public abstract class ArgType {
|
||||
|
||||
public WildcardType(ArgType obj, WildcardBound bound) {
|
||||
super(OBJECT.getObject());
|
||||
this.type = obj;
|
||||
this.bound = bound;
|
||||
this.type = Objects.requireNonNull(obj);
|
||||
this.bound = Objects.requireNonNull(bound);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -281,19 +282,10 @@ public abstract class ArgType {
|
||||
|
||||
private static class GenericObject extends ObjectType {
|
||||
private final ArgType[] generics;
|
||||
private final GenericObject outerType;
|
||||
|
||||
public GenericObject(String obj, ArgType[] generics) {
|
||||
super(obj);
|
||||
this.outerType = null;
|
||||
this.generics = generics;
|
||||
this.hash = calcHash();
|
||||
}
|
||||
|
||||
public GenericObject(GenericObject outerType, String innerName, ArgType[] generics) {
|
||||
super(outerType.getObject() + '$' + innerName);
|
||||
this.outerType = outerType;
|
||||
this.generics = generics;
|
||||
this.generics = Objects.requireNonNull(generics);
|
||||
this.hash = calcHash();
|
||||
}
|
||||
|
||||
@@ -311,11 +303,6 @@ public abstract class ArgType {
|
||||
return generics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArgType getOuterType() {
|
||||
return outerType;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean internalEquals(Object obj) {
|
||||
return super.internalEquals(obj)
|
||||
@@ -328,6 +315,54 @@ public abstract class ArgType {
|
||||
}
|
||||
}
|
||||
|
||||
private static class OuterGenericObject extends ObjectType {
|
||||
private final GenericObject outerType;
|
||||
private final ObjectType innerType;
|
||||
|
||||
public OuterGenericObject(GenericObject outerType, ObjectType innerType) {
|
||||
super(outerType.getObject() + '$' + innerType.getObject());
|
||||
this.outerType = outerType;
|
||||
this.innerType = innerType;
|
||||
this.hash = calcHash();
|
||||
}
|
||||
|
||||
private int calcHash() {
|
||||
return objName.hashCode() + 31 * (outerType.hashCode() + 31 * innerType.hashCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGeneric() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArgType[] getGenericTypes() {
|
||||
return innerType.getGenericTypes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArgType getOuterType() {
|
||||
return outerType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArgType getInnerType() {
|
||||
return innerType;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean internalEquals(Object obj) {
|
||||
return super.internalEquals(obj)
|
||||
&& Objects.equals(outerType, ((OuterGenericObject) obj).outerType)
|
||||
&& Objects.equals(innerType, ((OuterGenericObject) obj).innerType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return outerType.toString() + '$' + innerType.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ArrayArg extends KnownType {
|
||||
private static final PrimitiveType[] ARRAY_POSSIBLES = new PrimitiveType[] { PrimitiveType.ARRAY };
|
||||
private final ArgType arrayElement;
|
||||
@@ -494,6 +529,10 @@ public abstract class ArgType {
|
||||
return null;
|
||||
}
|
||||
|
||||
public ArgType getInnerType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isArray() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ public class SignatureParser {
|
||||
if (inner == null) {
|
||||
throw new JadxRuntimeException("No inner type found: " + debugString());
|
||||
}
|
||||
return ArgType.genericInner(genericType, inner.getObject(), inner.getGenericTypes());
|
||||
return ArgType.outerGeneric(genericType, inner);
|
||||
} else {
|
||||
consume(';');
|
||||
return genericType;
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
package jadx.core.dex.instructions.args;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class ArgTypeTest {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ArgTypeTest.class);
|
||||
|
||||
@Test
|
||||
public void testEqualsOfGenericTypes() {
|
||||
ArgType first = ArgType.generic("java.lang.List", ArgType.STRING);
|
||||
@@ -22,6 +28,19 @@ class ArgTypeTest {
|
||||
|
||||
ArgType type = ArgType.generic("java.lang.List", wildcard);
|
||||
assertTrue(type.containsGenericType());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInnerGeneric() {
|
||||
ArgType[] genericTypes = new ArgType[] { ArgType.genericType("K"), ArgType.genericType("V") };
|
||||
ArgType base = ArgType.generic("java.util.Map", genericTypes);
|
||||
|
||||
ArgType genericInner = ArgType.outerGeneric(base, ArgType.generic("Entry", genericTypes));
|
||||
LOG.debug("genericInner : {}", genericInner);
|
||||
|
||||
ArgType genericInner2 = ArgType.outerGeneric(base, ArgType.object("Entry"));
|
||||
LOG.debug("genericInner2: {}", genericInner2);
|
||||
|
||||
assertThat(genericInner.toString(), is("java.util.Map<K, V>$Entry<K, V>"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,9 +14,9 @@ import static jadx.core.dex.instructions.args.ArgType.INT;
|
||||
import static jadx.core.dex.instructions.args.ArgType.OBJECT;
|
||||
import static jadx.core.dex.instructions.args.ArgType.array;
|
||||
import static jadx.core.dex.instructions.args.ArgType.generic;
|
||||
import static jadx.core.dex.instructions.args.ArgType.genericInner;
|
||||
import static jadx.core.dex.instructions.args.ArgType.genericType;
|
||||
import static jadx.core.dex.instructions.args.ArgType.object;
|
||||
import static jadx.core.dex.instructions.args.ArgType.outerGeneric;
|
||||
import static jadx.core.dex.instructions.args.ArgType.wildcard;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.singletonList;
|
||||
@@ -46,9 +46,9 @@ class SignatureParserTest {
|
||||
checkType("La<TV;Lb;>;", generic("La;", genericType("V"), object("b")));
|
||||
checkType("La<Lb<Lc;>;>;", generic("La;", generic("Lb;", object("Lc;"))));
|
||||
checkType("La/b/C<Ld/E<Lf/G;>;>;", generic("La/b/C;", generic("Ld/E;", object("Lf/G;"))));
|
||||
checkType("La<TD;>.c;", genericInner(generic("La;", genericType("D")), "c", null));
|
||||
checkType("La<TD;>.c/d;", genericInner(generic("La;", genericType("D")), "c.d", null));
|
||||
checkType("La<Lb;>.c<TV;>;", genericInner(generic("La;", object("Lb;")), "c", new ArgType[] { genericType("V") }));
|
||||
checkType("La<TD;>.c;", outerGeneric(generic("La;", genericType("D")), ArgType.object("c")));
|
||||
checkType("La<TD;>.c/d;", outerGeneric(generic("La;", genericType("D")), ArgType.object("c.d")));
|
||||
checkType("La<Lb;>.c<TV;>;", outerGeneric(generic("La;", object("Lb;")), ArgType.generic("c", genericType("V"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -117,7 +117,7 @@ class SignatureParserTest {
|
||||
assertThat(argTypes, hasSize(1));
|
||||
ArgType argType = argTypes.get(0);
|
||||
assertThat(argType.getObject().indexOf('/'), is(-1));
|
||||
assertThat(argType, is(genericInner(generic("La/b/C;", genericType("T")), "d.E", null)));
|
||||
assertThat(argType, is(outerGeneric(generic("La/b/C;", genericType("T")), object("d.E"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package jadx.tests.integration.generics;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jadx.NotYetImplemented;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.tests.api.IntegrationTest;
|
||||
|
||||
import static jadx.tests.api.utils.JadxMatchers.containsOne;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
public class TestOuterGeneric extends IntegrationTest {
|
||||
|
||||
public static class TestCls {
|
||||
public static class A<T> {
|
||||
public class B<V> {
|
||||
}
|
||||
|
||||
public class C {
|
||||
}
|
||||
}
|
||||
|
||||
public static class D {
|
||||
public class E {
|
||||
}
|
||||
}
|
||||
|
||||
public void test1() {
|
||||
A<String> a = new A<>();
|
||||
use(a);
|
||||
A<String>.B<Exception> b = a.new B<Exception>();
|
||||
use(b);
|
||||
use(b);
|
||||
A<String>.C c = a.new C();
|
||||
use(c);
|
||||
use(c);
|
||||
|
||||
use(new A<Set<String>>().new C());
|
||||
}
|
||||
|
||||
public void test2() {
|
||||
D d = new D();
|
||||
D.E e = d.new E();
|
||||
use(e);
|
||||
use(e);
|
||||
}
|
||||
|
||||
public void test3() {
|
||||
use(A.class);
|
||||
use(A.B.class);
|
||||
use(A.C.class);
|
||||
}
|
||||
|
||||
private void use(Object obj) {
|
||||
}
|
||||
}
|
||||
|
||||
@NotYetImplemented("Instance constructor for inner classes")
|
||||
@Test
|
||||
public void test() {
|
||||
ClassNode cls = getClassNode(TestCls.class);
|
||||
String code = cls.getCode().toString();
|
||||
|
||||
assertThat(code, containsOne("A<String> a = new A<>();"));
|
||||
assertThat(code, containsOne("A<String>.B<Exception> b = a.new B<Exception>();"));
|
||||
assertThat(code, containsOne("A<String>.C c = a.new C();"));
|
||||
assertThat(code, containsOne("use(new A<Set<String>>().new C());"));
|
||||
assertThat(code, containsOne("D.E e = d.new E();"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user