提交 ba17f7bc 编写于 作者: S Skylot

refactor: move type with outer generic to different class

上级 db2b5373
......@@ -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,24 +282,52 @@ 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.generics = Objects.requireNonNull(generics);
this.hash = calcHash();
}
public GenericObject(GenericObject outerType, String innerName, ArgType[] generics) {
super(outerType.getObject() + '$' + innerName);
private int calcHash() {
return objName.hashCode() + 31 * Arrays.hashCode(generics);
}
@Override
public boolean isGeneric() {
return true;
}
@Override
public ArgType[] getGenericTypes() {
return generics;
}
@Override
boolean internalEquals(Object obj) {
return super.internalEquals(obj)
&& Arrays.equals(generics, ((GenericObject) obj).generics);
}
@Override
public String toString() {
return super.toString() + '<' + Utils.arrayToStr(generics) + '>';
}
}
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.generics = generics;
this.innerType = innerType;
this.hash = calcHash();
}
private int calcHash() {
return objName.hashCode() + 31 * Arrays.hashCode(generics);
return objName.hashCode() + 31 * (outerType.hashCode() + 31 * innerType.hashCode());
}
@Override
......@@ -308,7 +337,7 @@ public abstract class ArgType {
@Override
public ArgType[] getGenericTypes() {
return generics;
return innerType.getGenericTypes();
}
@Override
......@@ -316,15 +345,21 @@ public abstract class ArgType {
return outerType;
}
@Override
public ArgType getInnerType() {
return innerType;
}
@Override
boolean internalEquals(Object obj) {
return super.internalEquals(obj)
&& Arrays.equals(generics, ((GenericObject) obj).generics);
&& Objects.equals(outerType, ((OuterGenericObject) obj).outerType)
&& Objects.equals(innerType, ((OuterGenericObject) obj).innerType);
}
@Override
public String toString() {
return super.toString() + '<' + Utils.arrayToStr(generics) + '>';
return outerType.toString() + '$' + innerType.toString();
}
}
......@@ -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;
......
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
......
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();"));
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册