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

fix: restore missing type parameter declarations (#1800)

上级 912c4315
......@@ -13,8 +13,6 @@ import java.util.stream.Collectors;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.api.DecompilationMode;
import jadx.api.ICodeCache;
......@@ -56,8 +54,6 @@ import static jadx.core.dex.nodes.ProcessState.LOADED;
import static jadx.core.dex.nodes.ProcessState.NOT_LOADED;
public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeNode, Comparable<ClassNode> {
private static final Logger LOG = LoggerFactory.getLogger(ClassNode.class);
private final RootNode root;
private final IClassData clsData;
......@@ -174,10 +170,10 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN
return ArgType.object(superType);
}
public void updateGenericClsData(ArgType superClass, List<ArgType> interfaces, List<ArgType> generics) {
public void updateGenericClsData(List<ArgType> generics, ArgType superClass, List<ArgType> interfaces) {
this.generics = generics;
this.superClass = superClass;
this.interfaces = interfaces;
this.generics = generics;
}
private static void processAttributes(ClassNode cls) {
......
......@@ -2,8 +2,12 @@ package jadx.core.dex.visitors;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import org.jetbrains.annotations.Nullable;
import jadx.core.dex.info.MethodInfo;
import jadx.core.dex.instructions.args.ArgType;
......@@ -18,7 +22,6 @@ import jadx.core.utils.Utils;
import jadx.core.utils.exceptions.JadxException;
public class SignatureProcessor extends AbstractVisitor {
private RootNode root;
@Override
......@@ -55,12 +58,54 @@ public class SignatureProcessor extends AbstractVisitor {
break;
}
}
cls.updateGenericClsData(superClass, interfaces, generics);
generics = fixTypeParamDeclarations(cls, generics, superClass, interfaces);
cls.updateGenericClsData(generics, superClass, interfaces);
} catch (Exception e) {
cls.addWarnComment("Failed to parse class signature: " + sp.getSignature(), e);
}
}
/**
* Add missing type parameters from super type and interfaces to make code compilable
*/
private static List<ArgType> fixTypeParamDeclarations(ClassNode cls,
List<ArgType> generics, ArgType superClass, List<ArgType> interfaces) {
if (interfaces.isEmpty() && superClass.equals(ArgType.OBJECT)) {
return generics;
}
Set<String> typeParams = new HashSet<>();
superClass.visitTypes(t -> addGenericType(typeParams, t));
interfaces.forEach(i -> i.visitTypes(t -> addGenericType(typeParams, t)));
if (typeParams.isEmpty()) {
return generics;
}
List<ArgType> knownTypeParams;
if (cls.isInner()) {
knownTypeParams = new ArrayList<>(generics);
cls.visitParentClasses(p -> knownTypeParams.addAll(p.getGenericTypeParameters()));
} else {
knownTypeParams = generics;
}
for (ArgType declTypeParam : knownTypeParams) {
typeParams.remove(declTypeParam.getObject());
}
if (typeParams.isEmpty()) {
return generics;
}
cls.addInfoComment("Add missing generic type declarations: " + typeParams);
List<ArgType> fixedGenerics = new ArrayList<>(generics.size() + typeParams.size());
fixedGenerics.addAll(generics);
typeParams.stream().sorted().map(ArgType::genericType).forEach(fixedGenerics::add);
return fixedGenerics;
}
private static @Nullable Object addGenericType(Set<String> usedTypeParameters, ArgType t) {
if (t.isGenericType()) {
usedTypeParameters.add(t.getObject());
}
return null;
}
private ArgType validateClsType(ClassNode cls, ArgType candidateType, ArgType currentType) {
if (!candidateType.isObject()) {
cls.addWarnComment("Incorrect class signature, class is not object: " + SignatureParser.getSignature(cls));
......
......@@ -224,6 +224,11 @@ public abstract class IntegrationTest extends TestUtils {
return sortedClsNodes;
}
@NotNull
public ClassNode searchTestCls(List<ClassNode> list, String shortClsName) {
return searchCls(list, getTestPkg() + '.' + shortClsName);
}
@NotNull
public ClassNode searchCls(List<ClassNode> list, String clsName) {
for (ClassNode cls : list) {
......
package jadx.tests.integration.inline;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import jadx.NotYetImplemented;
import jadx.core.dex.nodes.ClassNode;
import jadx.tests.api.SmaliTest;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestInstanceLambda extends SmaliTest {
@SuppressWarnings({ "unchecked", "rawtypes", "SameParameterValue" })
public static class TestCls {
public <T> Map<T, T> test(List<? extends T> list) {
return toMap(list, Lambda.INSTANCE);
}
/**
* Smali test missing 'T' definition in 'Lambda<T>'
*/
private static class Lambda<T> implements Function<T, T> {
public static final Lambda INSTANCE = new Lambda();
@Override
public T apply(T t) {
return t;
}
}
private static <T> Map<T, T> toMap(List<? extends T> list, Function<T, T> valueMap) {
return list.stream().collect(Collectors.toMap(k -> k, valueMap));
}
}
@Test
public void test() {
noDebugInfo();
assertThat(getClassNode(TestCls.class))
.code();
}
@Test
public void testSmaliDisableInline() {
args.setInlineAnonymousClasses(false);
List<ClassNode> classNodes = loadFromSmaliFiles();
assertThat(searchTestCls(classNodes, "Lambda"))
.code()
.containsOne("class Lambda<T> implements Function<T, T> {");
assertThat(searchTestCls(classNodes, "TestCls"))
.code()
.containsOne("Lambda.INSTANCE");
}
@NotYetImplemented("Inline lambda by instance field")
@Test
public void testSmali() {
List<ClassNode> classNodes = loadFromSmaliFiles();
assertThat(classNodes)
.describedAs("Expect lambda to be inlined")
.hasSize(1);
assertThat(searchTestCls(classNodes, "TestCls"))
.code()
.doesNotContain("Lambda.INSTANCE");
}
}
.class public Linline/Lambda;
.super Ljava/lang/Object;
.implements Ljava/util/function/Function;
.annotation system Ldalvik/annotation/Signature;
value = {
"Ljava/lang/Object;",
"Ljava/util/function/Function",
"<TT;TT;>;"
}
.end annotation
.field public static final INSTANCE:Linline/Lambda;
.method static constructor <clinit>()V
.registers 1
new-instance v0, Linline/Lambda;
invoke-direct {v0}, Linline/Lambda;-><init>()V
sput-object v0, Linline/Lambda;->INSTANCE:Linline/Lambda;
return-void
.end method
.method private constructor <init>()V
.registers 1
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
return-void
.end method
.method public final apply(Ljava/lang/Object;)Ljava/lang/Object;
.registers 2
.annotation system Ldalvik/annotation/Signature;
value = {
"(TT;)TT;"
}
.end annotation
return-object p1
.end method
.class public Linline/TestCls;
.super Ljava/lang/Object;
.method public test(Ljava/util/List;)Ljava/util/Map;
.registers 3
.annotation system Ldalvik/annotation/Signature;
value = {
"<T:",
"Ljava/lang/Object;",
">(",
"Ljava/util/List",
"<+TT;>;)",
"Ljava/util/Map",
"<TT;TT;>;"
}
.end annotation
sget-object v0, Linline/Lambda;->INSTANCE:Linline/Lambda;
invoke-static {p1, v0}, Linline/TestCls;->toMap(Ljava/util/List;Ljava/util/function/Function;)Ljava/util/Map;
move-result-object v0
return-object v0
.end method
.method private static synthetic lambda$toMap$0(Ljava/lang/Object;)Ljava/lang/Object;
.registers 1
return-object p0
.end method
.method private static toMap(Ljava/util/List;Ljava/util/function/Function;)Ljava/util/Map;
.registers 4
.annotation system Ldalvik/annotation/Signature;
value = {
"<T:",
"Ljava/lang/Object;",
">(",
"Ljava/util/List",
"<+TT;>;",
"Ljava/util/function/Function",
"<TT;TT;>;)",
"Ljava/util/Map",
"<TT;TT;>;"
}
.end annotation
invoke-interface {p0}, Ljava/util/List;->stream()Ljava/util/stream/Stream;
move-result-object v0
invoke-custom {}, call_site_0("apply", ()Ljava/util/function/Function;, (Ljava/lang/Object;)Ljava/lang/Object;, invoke-static@Linline/TestCls;->lambda$toMap$0(Ljava/lang/Object;)Ljava/lang/Object;, (Ljava/lang/Object;)Ljava/lang/Object;)@Ljava/lang/invoke/LambdaMetafactory;->metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
move-result-object v1
invoke-static {v1, p1}, Ljava/util/stream/Collectors;->toMap(Ljava/util/function/Function;Ljava/util/function/Function;)Ljava/util/stream/Collector;
move-result-object v1
invoke-interface {v0, v1}, Ljava/util/stream/Stream;->collect(Ljava/util/stream/Collector;)Ljava/lang/Object;
move-result-object v0
check-cast v0, Ljava/util/Map;
return-object v0
.end method
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册