fix: additional checks for class signature (#2272)
Some checks failed
Build Artifacts / build-win-bundle (push) Waiting to run
Build Test / tests (windows-latest) (push) Waiting to run
Build Artifacts / build (push) Failing after 0s
Build Test / tests (ubuntu-latest) (push) Failing after 0s
Validate Gradle Wrapper / Validation (push) Failing after 0s
CodeQL / Analyze (java) (push) Failing after 1s

This commit is contained in:
Skylot 2024-09-12 20:26:48 +01:00
parent fd80e03809
commit 889a945cf1
No known key found for this signature in database
GPG Key ID: 47A4975761262B6A
4 changed files with 118 additions and 20 deletions

View File

@ -169,6 +169,24 @@ public class SignatureParser {
throw new JadxRuntimeException("Can't parse type: " + debugString() + ", unexpected: " + ch);
}
public List<ArgType> consumeTypeList() {
List<ArgType> list = null;
while (true) {
ArgType type = consumeType();
if (type == null) {
break;
}
if (list == null) {
list = new ArrayList<>();
}
list.add(type);
}
if (list == null) {
return Collections.emptyList();
}
return list;
}
private ArgType consumeObjectType(boolean innerType) {
mark();
int ch;

View File

@ -48,23 +48,49 @@ public class SignatureProcessor extends AbstractVisitor {
}
try {
List<ArgType> generics = sp.consumeGenericTypeParameters();
ArgType superClass = validateClsType(cls, sp.consumeType(), cls.getSuperClass());
List<ArgType> interfaces = cls.getInterfaces();
for (int i = 0; i < interfaces.size(); i++) {
ArgType type = sp.consumeType();
if (type != null) {
interfaces.set(i, validateClsType(cls, type, interfaces.get(i)));
} else {
break;
}
}
generics = fixTypeParamDeclarations(cls, generics, superClass, interfaces);
cls.updateGenericClsData(generics, superClass, interfaces);
ArgType superClass = processSuperType(cls, sp.consumeType());
List<ArgType> interfaces = processInterfaces(cls, sp.consumeTypeList());
List<ArgType> resultGenerics = fixTypeParamDeclarations(cls, generics, superClass, interfaces);
cls.updateGenericClsData(resultGenerics, superClass, interfaces);
} catch (Exception e) {
cls.addWarnComment("Failed to parse class signature: " + sp.getSignature(), e);
}
}
private ArgType processSuperType(ClassNode cls, ArgType parsedType) {
ArgType superType = cls.getSuperClass();
if (Objects.equals(parsedType.getObject(), cls.getClassInfo().getType().getObject())) {
cls.addWarnComment("Incorrect class signature: super class is equals to this class");
return superType;
}
return bestClsType(cls, parsedType, superType);
}
/**
* Parse, validate and update class interfaces types.
*/
private List<ArgType> processInterfaces(ClassNode cls, List<ArgType> parsedTypes) {
List<ArgType> interfaces = cls.getInterfaces();
if (parsedTypes.isEmpty()) {
return interfaces;
}
int parsedCount = parsedTypes.size();
int interfacesCount = interfaces.size();
List<ArgType> result = new ArrayList<>(interfacesCount);
int count = Math.min(interfacesCount, parsedCount);
for (int i = 0; i < interfacesCount; i++) {
if (i < count) {
result.add(bestClsType(cls, parsedTypes.get(i), interfaces.get(i)));
} else {
result.add(interfaces.get(i));
}
}
if (interfacesCount < parsedCount) {
cls.addWarnComment("Unexpected interfaces in signature: " + parsedTypes.subList(interfacesCount, parsedCount));
}
return result;
}
/**
* Add missing type parameters from super type and interfaces to make code compilable
*/
@ -106,16 +132,22 @@ public class SignatureProcessor extends AbstractVisitor {
return null;
}
private ArgType validateClsType(ClassNode cls, ArgType candidateType, ArgType currentType) {
private ArgType bestClsType(ClassNode cls, ArgType candidateType, ArgType currentType) {
if (validateClsType(cls, candidateType)) {
return candidateType;
}
return currentType;
}
private boolean validateClsType(ClassNode cls, ArgType candidateType) {
if (candidateType == null) {
return false;
}
if (!candidateType.isObject()) {
cls.addWarnComment("Incorrect class signature, class is not object: " + SignatureParser.getSignature(cls));
return currentType;
cls.addWarnComment("Incorrect class signature, class is not an object: " + candidateType);
return false;
}
if (Objects.equals(candidateType.getObject(), cls.getClassInfo().getType().getObject())) {
cls.addWarnComment("Incorrect class signature, class is equals to this class: " + SignatureParser.getSignature(cls));
return currentType;
}
return candidateType;
return true;
}
private void parseFieldSignature(FieldNode field) {

View File

@ -0,0 +1,32 @@
package jadx.tests.integration.others;
import org.junit.jupiter.api.Test;
import jadx.tests.api.RaungTest;
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
public class TestClassImplementsSignature extends RaungTest {
public static class TestCls {
public abstract static class A<T> implements Comparable<A<T>> {
T value;
}
}
@Test
public void test() {
assertThat(getClassNode(TestCls.class))
.code()
.containsOne("public static abstract class A<T> implements Comparable<A<T>> {");
}
@Test
public void testRaung() {
allowWarnInCode();
assertThat(getClassNodeFromRaung())
.code()
.containsOne("public class TestClassImplementsSignature<T> {")
.containsOne("Unexpected interfaces in signature");
}
}

View File

@ -0,0 +1,16 @@
.version 52 # Java 8
.class public super others/TestClassImplementsSignature
.signature <T:Ljava/lang/Object;>Ljava/lang/Object;Ljava/lang/Comparable<Lothers/TestClassImplementsSignature<LT;>;>;
.field value Ljava/lang/Object;
.signature TT;
.end field
.method public <init>()V
.max stack 1
.max locals 1
aload 0
invokespecial java/lang/Object <init> ()V
return
.end method