diff --git a/plugins/kotlin/src/main/java/org/vineflower/kotlin/KotlinWriter.java b/plugins/kotlin/src/main/java/org/vineflower/kotlin/KotlinWriter.java index b4609a762..9d59efd2c 100644 --- a/plugins/kotlin/src/main/java/org/vineflower/kotlin/KotlinWriter.java +++ b/plugins/kotlin/src/main/java/org/vineflower/kotlin/KotlinWriter.java @@ -32,6 +32,7 @@ import org.jetbrains.java.decompiler.struct.gen.generics.GenericClassDescriptor; import org.jetbrains.java.decompiler.struct.gen.generics.GenericFieldDescriptor; import org.jetbrains.java.decompiler.struct.gen.generics.GenericMethodDescriptor; +import org.jetbrains.java.decompiler.struct.gen.generics.GenericsChecker; import org.jetbrains.java.decompiler.util.*; import org.jetbrains.java.decompiler.util.collections.VBStyleCollection; import org.vineflower.kotlin.expr.KAnnotationExprent; @@ -1082,10 +1083,24 @@ public boolean writeMethod(ClassNode node, StructMethod mt, int methodIndex, Tex exceptions.add(new VarType(attr.getExcClassname(i, node.classStruct.getPool()), true)); } } - + GenericClassDescriptor classSig = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0 ? node.classStruct.getSignature() : null; + GenericsChecker checker = null; + if (classSig != null) { + checker = classSig.getChecker(); + + ClassNode currentParent = node.parent; + while (currentParent != null) { + GenericClassDescriptor parentSignature = currentParent.classStruct.getSignature(); + if (parentSignature != null) { + checker = checker.copy(parentSignature.getChecker()); + } + + currentParent = currentParent.parent; + } + } - descriptor.verifyTypes(classSig, params, mt.methodDescriptor().ret, exceptions); + descriptor.verifyTypes(checker, params, mt.methodDescriptor().ret, exceptions); } boolean throwsExceptions = false; diff --git a/src/org/jetbrains/java/decompiler/main/ClassWriter.java b/src/org/jetbrains/java/decompiler/main/ClassWriter.java index 9daa701da..09d4fc3d5 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassWriter.java +++ b/src/org/jetbrains/java/decompiler/main/ClassWriter.java @@ -37,6 +37,7 @@ import org.jetbrains.java.decompiler.struct.gen.generics.GenericClassDescriptor; import org.jetbrains.java.decompiler.struct.gen.generics.GenericFieldDescriptor; import org.jetbrains.java.decompiler.struct.gen.generics.GenericMethodDescriptor; +import org.jetbrains.java.decompiler.struct.gen.generics.GenericsChecker; import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.Key; import org.jetbrains.java.decompiler.util.TextBuffer; @@ -1157,12 +1158,23 @@ public boolean writeMethod(ClassNode node, StructMethod mt, int methodIndex, Tex GenericMethodDescriptor descriptor = mt.getSignature(); if (descriptor != null) { List params = new ArrayList<>(Arrays.asList(mt.methodDescriptor().params)); - if ((node.access & CodeConstants.ACC_ENUM) != 0 && init) { - // Signatures skip enum parameters, the checker must as well + + if (init && node.classStruct.hasModifier(CodeConstants.ACC_ENUM)) { + // Enum name and ordinal parameters need to be explicitly excluded params.remove(0); params.remove(0); } + // Exclude any parameters that the signature itself won't contain + List mask = methodWrapper.synthParameters; + if (mask != null) { + for (int i = 0, j = 0; i < mask.size(); i++, j++) { + if (mask.get(i) != null) { + params.remove(j--); + } + } + } + StructExceptionsAttribute attr = mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_EXCEPTIONS); List exceptions = new ArrayList<>(); if (attr != null) { @@ -1171,9 +1183,19 @@ public boolean writeMethod(ClassNode node, StructMethod mt, int methodIndex, Tex } } - GenericClassDescriptor classSig = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0 ? node.classStruct.getSignature() : null; + GenericsChecker checker = new GenericsChecker(); + + ClassNode currentNode = node; + while (currentNode != null) { + GenericClassDescriptor parentSignature = currentNode.classStruct.getSignature(); + if (parentSignature != null) { + checker = checker.copy(parentSignature.getChecker()); + } + + currentNode = currentNode.parent; + } - descriptor.verifyTypes(classSig, params, mt.methodDescriptor().ret, exceptions); + descriptor.verifyTypes(checker, params, mt.methodDescriptor().ret, exceptions); } boolean throwsExceptions = false; diff --git a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericFieldDescriptor.java b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericFieldDescriptor.java index 3d1273c0f..d53a23e6d 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericFieldDescriptor.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericFieldDescriptor.java @@ -13,18 +13,18 @@ public GenericFieldDescriptor(VarType type) { this.type = type; } - public void verifyType(GenericClassDescriptor containingClassGenerics, VarType actualType) { + public void verifyType(GenericClassDescriptor containingClassGenerics, VarType realType) { if (containingClassGenerics == null) { DecompilerContext.getLogger().writeMessage("Class generics were not found, verifying type loosely", IFernflowerLogger.Severity.INFO); - verifyLoosely(actualType); + verifyLoosely(realType); return; } GenericsChecker checker = containingClassGenerics.getChecker(); - if (!checker.isProperlyBounded(actualType, type)) { - DecompilerContext.getLogger().writeMessage("Mismatched field signature, expected: " + type.value + ", actual: " + actualType.value, IFernflowerLogger.Severity.WARN); - type = actualType; + if (!checker.isProperlyBounded(type, realType)) { + DecompilerContext.getLogger().writeMessage("Mismatched field signature, expected: " + realType.value + ", actual: " + type.value, IFernflowerLogger.Severity.WARN); + type = realType; } } diff --git a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMethodDescriptor.java b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMethodDescriptor.java index e03a9f68a..238225288 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMethodDescriptor.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMethodDescriptor.java @@ -31,22 +31,30 @@ private static List substitute(List list) { return list.isEmpty() ? Collections.emptyList() : list; } - public void verifyTypes(GenericClassDescriptor descriptor, List realParamTypes, VarType realReturnType, List realExceptionTypes) { - GenericsChecker checker = descriptor == null ? new GenericsChecker(typeParameters, typeParameterBounds) : descriptor.getChecker().copy(typeParameters, typeParameterBounds); + public void verifyTypes(GenericsChecker checker, List realParamTypes, VarType realReturnType, List realExceptionTypes) { + checker = checker == null ? new GenericsChecker(typeParameters, typeParameterBounds) : checker.copy(typeParameters, typeParameterBounds); for (int i = 0; i < parameterTypes.size(); i++) { VarType parameterType = parameterTypes.get(i); - VarType actualType = realParamTypes.get(i); + VarType realType = realParamTypes.get(i); - if (!checker.isProperlyBounded(parameterType, actualType)) { - DecompilerContext.getLogger().writeMessage("Mismatched method parameter signature, expected: " + actualType.value + ", actual: " + parameterType.value, IFernflowerLogger.Severity.WARN); - parameterTypes.set(i, actualType); + if (!checker.isProperlyBounded(parameterType, realType)) { + DecompilerContext.getLogger().writeMessage("Mismatched method parameter signature, expected: " + realType.value + ", actual: " + parameterType.value, IFernflowerLogger.Severity.WARN); + parameterTypes.set(i, realType); } } if (!checker.isProperlyBounded(returnType, realReturnType)) { - DecompilerContext.getLogger().writeMessage("Mismatched method parameter signature, expected: " + realReturnType.value + ", actual: " + returnType.value, IFernflowerLogger.Severity.WARN); + DecompilerContext.getLogger().writeMessage("Mismatched method return signature, expected: " + realReturnType.value + ", actual: " + returnType.value, IFernflowerLogger.Severity.WARN); returnType = realReturnType; } + + for (int i = 0; i < exceptionTypes.size(); i++) { + VarType exceptionType = exceptionTypes.get(i); + VarType realType = realExceptionTypes.get(i); + if (!checker.isProperlyBounded(exceptionType, realType)) { + DecompilerContext.getLogger().writeMessage("Mismatched method exception signature, expected: " + realType.value + ", actual: " + exceptionType.value, IFernflowerLogger.Severity.WARN); + } + } } } \ No newline at end of file diff --git a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericsChecker.java b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericsChecker.java index f8a0ea2a4..d6df6ac31 100644 --- a/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericsChecker.java +++ b/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericsChecker.java @@ -3,6 +3,7 @@ import org.jetbrains.java.decompiler.struct.gen.CodeType; import org.jetbrains.java.decompiler.struct.gen.VarType; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -10,6 +11,10 @@ public class GenericsChecker { private final Map> boundsMap; + public GenericsChecker() { + boundsMap = Map.of(); + } + public GenericsChecker(List typeVariables, List> bounds) { boundsMap = new HashMap<>(typeVariables.size(), 1); for (int i = 0; i < typeVariables.size(); i++) { @@ -17,15 +22,28 @@ public GenericsChecker(List typeVariables, List> bounds) { } } - private GenericsChecker(Map> existingBounds, List typeVariables, List> bounds) { - boundsMap = new HashMap<>(existingBounds); + private GenericsChecker(Map> boundsMap) { + this.boundsMap = boundsMap; + } + + public GenericsChecker copy(List typeVariables, List> bounds) { + HashMap> newBounds = new HashMap<>(boundsMap); for (int i = 0; i < typeVariables.size(); i++) { - boundsMap.put(typeVariables.get(i), bounds.get(i)); + newBounds.put(typeVariables.get(i), bounds.get(i)); } + + return new GenericsChecker(newBounds); } - public GenericsChecker copy(List typeVariables, List> bounds) { - return new GenericsChecker(boundsMap, typeVariables, bounds); + public GenericsChecker copy(GenericsChecker parent) { + HashMap> newBoundsMap = new HashMap<>(this.boundsMap); + for (Map.Entry> entry : parent.boundsMap.entrySet()) { + if (!newBoundsMap.containsKey(entry.getKey())) { + newBoundsMap.put(entry.getKey(), new ArrayList<>(entry.getValue())); + } + } + + return new GenericsChecker(newBoundsMap); } public boolean isProperlyBounded(VarType type, VarType bound) { @@ -33,6 +51,9 @@ public boolean isProperlyBounded(VarType type, VarType bound) { return true; } + // Get base type if array + bound = bound.resizeArrayDim(0); + if (type.type == CodeType.GENVAR && type instanceof GenericType genericType) { List typeBounds = boundsMap.get(genericType.value); if (typeBounds != null) { diff --git a/testData/results/TestCorruptedSignatures.dec b/testData/results/TestCorruptedSignatures.dec index dcdccd13c..30e14cb5e 100644 --- a/testData/results/TestCorruptedSignatures.dec +++ b/testData/results/TestCorruptedSignatures.dec @@ -1,19 +1,18 @@ import java.util.ArrayList; import java.util.List; -import java.util.Map; -abstract class Signatures implements Map { - Map field = new ArrayList(); +abstract class Signatures implements List { + List field = new ArrayList(); - void method(Map o) { + void method(List o) { }// 9 } class 'Signatures' { method 'method (Ljava/util/List;)V' { - 0 8 + 0 7 } } Lines mapping: -9 <-> 9 +9 <-> 8