Skip to content

Commit 1480c4b

Browse files
committed
Add mismatched generics catches
1 parent d6dcb52 commit 1480c4b

File tree

9 files changed

+261
-32
lines changed

9 files changed

+261
-32
lines changed

plugins/kotlin/src/main/java/org/vineflower/kotlin/KotlinWriter.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -786,6 +786,15 @@ private void writeClassDefinition(ClassNode node, TextBuffer buffer, int indent,
786786
}
787787

788788
GenericClassDescriptor descriptor = cl.getSignature();
789+
if (descriptor != null) {
790+
VarType superclass = new VarType(cl.superClass.getString(), true);
791+
List<VarType> interfaces = Arrays.stream(cl.getInterfaceNames())
792+
.map(s -> new VarType(s, true))
793+
.toList();
794+
795+
descriptor.verifyTypes(superclass, interfaces);
796+
}
797+
789798
if (descriptor != null && !descriptor.fparameters.isEmpty()) {
790799
appendTypeParameters(buffer, descriptor.fparameters, descriptor.fbounds);
791800
}
@@ -871,6 +880,9 @@ public void writeField(ClassWrapper wrapper, StructClass cl, StructField fd, Tex
871880
Map.Entry<VarType, GenericFieldDescriptor> fieldTypeData = getFieldTypeData(fd);
872881
VarType fieldType = fieldTypeData.getKey();
873882
GenericFieldDescriptor descriptor = fieldTypeData.getValue();
883+
if (descriptor != null) {
884+
descriptor.verifyType(cl.getSignature(), fieldType);
885+
}
874886

875887
if (!isEnum) {
876888
buffer.append(ExprProcessor.getCastTypeName(descriptor == null ? fieldType : descriptor.type));
@@ -1055,6 +1067,27 @@ public boolean writeMethod(ClassNode node, StructMethod mt, int methodIndex, Tex
10551067
}
10561068

10571069
GenericMethodDescriptor descriptor = mt.getSignature();
1070+
if (descriptor != null) {
1071+
List<VarType> params = new ArrayList<>(Arrays.asList(mt.methodDescriptor().params));
1072+
if ((node.access & CodeConstants.ACC_ENUM) != 0 && init) {
1073+
// Signatures skip enum parameters, the checker must as well
1074+
params.remove(0);
1075+
params.remove(0);
1076+
}
1077+
1078+
StructExceptionsAttribute attr = mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_EXCEPTIONS);
1079+
List<VarType> exceptions = new ArrayList<>();
1080+
if (attr != null) {
1081+
for (int i = 0; i < attr.getThrowsExceptions().size(); i++) {
1082+
exceptions.add(new VarType(attr.getExcClassname(i, node.classStruct.getPool()), true));
1083+
}
1084+
}
1085+
1086+
GenericClassDescriptor classSig = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0 ? node.classStruct.getSignature() : null;
1087+
1088+
descriptor.verifyTypes(classSig, params, mt.methodDescriptor().ret, exceptions);
1089+
}
1090+
10581091
boolean throwsExceptions = false;
10591092
int paramCount = 0;
10601093

src/org/jetbrains/java/decompiler/main/ClassWriter.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,15 @@ else if (components != null) {
734734
buffer.appendClass(node.simpleName, true, cl.qualifiedName);
735735

736736
GenericClassDescriptor descriptor = cl.getSignature();
737+
if (descriptor != null) {
738+
VarType superclass = new VarType(cl.superClass.getString(), true);
739+
List<VarType> interfaces = Arrays.stream(cl.getInterfaceNames())
740+
.map(s -> new VarType(s, true))
741+
.toList();
742+
743+
descriptor.verifyTypes(superclass, interfaces);
744+
}
745+
737746
if (descriptor != null && !descriptor.fparameters.isEmpty()) {
738747
appendTypeParameters(buffer, descriptor.fparameters, descriptor.fbounds);
739748
}
@@ -857,6 +866,9 @@ public void writeField(ClassWrapper wrapper, StructClass cl, StructField fd, Tex
857866
Map.Entry<VarType, GenericFieldDescriptor> fieldTypeData = getFieldTypeData(fd);
858867
VarType fieldType = fieldTypeData.getKey();
859868
GenericFieldDescriptor descriptor = fieldTypeData.getValue();
869+
if (descriptor != null) {
870+
descriptor.verifyType(cl.getSignature(), fieldType);
871+
}
860872

861873
if (!isEnum) {
862874
buffer.appendCastTypeName(descriptor == null ? fieldType : descriptor.type);
@@ -1143,6 +1155,27 @@ public boolean writeMethod(ClassNode node, StructMethod mt, int methodIndex, Tex
11431155
}
11441156

11451157
GenericMethodDescriptor descriptor = mt.getSignature();
1158+
if (descriptor != null) {
1159+
List<VarType> params = new ArrayList<>(Arrays.asList(mt.methodDescriptor().params));
1160+
if ((node.access & CodeConstants.ACC_ENUM) != 0 && init) {
1161+
// Signatures skip enum parameters, the checker must as well
1162+
params.remove(0);
1163+
params.remove(0);
1164+
}
1165+
1166+
StructExceptionsAttribute attr = mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_EXCEPTIONS);
1167+
List<VarType> exceptions = new ArrayList<>();
1168+
if (attr != null) {
1169+
for (int i = 0; i < attr.getThrowsExceptions().size(); i++) {
1170+
exceptions.add(new VarType(attr.getExcClassname(i, node.classStruct.getPool()), true));
1171+
}
1172+
}
1173+
1174+
GenericClassDescriptor classSig = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0 ? node.classStruct.getSignature() : null;
1175+
1176+
descriptor.verifyTypes(classSig, params, mt.methodDescriptor().ret, exceptions);
1177+
}
1178+
11461179
boolean throwsExceptions = false;
11471180
int paramCount = 0;
11481181

src/org/jetbrains/java/decompiler/main/RecordHelper.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import org.jetbrains.java.decompiler.code.CodeConstants;
44
import org.jetbrains.java.decompiler.main.rels.MethodWrapper;
55
import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
6-
import org.jetbrains.java.decompiler.modules.decompiler.flow.DirectGraph;
76
import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement;
87
import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
98
import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
@@ -201,7 +200,10 @@ private static void recordComponentToJava(TextBuffer buffer, StructClass cl, Str
201200
VarType fieldType = new VarType(cd.getDescriptor(), false);
202201
GenericFieldDescriptor descriptor = cd.getSignature();
203202

204-
if (descriptor != null) fieldType = descriptor.type;
203+
if (descriptor != null) {
204+
descriptor.verifyType(cl.getSignature(), fieldType);
205+
fieldType = descriptor.type;
206+
}
205207

206208
buffer.appendCastTypeName(varArgComponent ? fieldType.decreaseArrayDim() : fieldType);
207209
if (varArgComponent) {

src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.jetbrains.java.decompiler.struct.gen.CodeType;
2020
import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor;
2121
import org.jetbrains.java.decompiler.struct.gen.VarType;
22+
import org.jetbrains.java.decompiler.struct.gen.generics.GenericFieldDescriptor;
2223
import org.jetbrains.java.decompiler.struct.gen.generics.GenericType;
2324
import org.jetbrains.java.decompiler.struct.match.MatchEngine;
2425
import org.jetbrains.java.decompiler.struct.match.MatchNode;
@@ -78,31 +79,36 @@ public VarType getInferredExprType(VarType upperBound) {
7879
cl = cl.superClass == null ? null : DecompilerContext.getStructContext().getClass((String)cl.superClass.value);
7980
}
8081

81-
if (ft != null && ft.getSignature() != null) {
82-
VarType ret = ft.getSignature().type.remap(types.getOrDefault(cl.qualifiedName, Collections.emptyMap()));
82+
if (ft != null) {
83+
GenericFieldDescriptor signature = ft.getSignature();
84+
if (signature != null) {
85+
signature.verifyType(cl.getSignature(), descriptor.type);
8386

84-
if (instance != null && cl.getSignature() != null) {
85-
VarType instType = instance.getInferredExprType(null);
87+
VarType ret = signature.type.remap(types.getOrDefault(cl.qualifiedName, Collections.emptyMap()));
8688

87-
if (instType.isGeneric() && instType.type != CodeType.GENVAR) {
88-
GenericType ginstance = (GenericType)instType;
89+
if (instance != null && cl.getSignature() != null) {
90+
VarType instType = instance.getInferredExprType(null);
8991

90-
cl = DecompilerContext.getStructContext().getClass(instType.value);
91-
if (cl != null && cl.getSignature() != null) {
92-
Map<VarType, VarType> tempMap = new HashMap<>();
93-
cl.getSignature().genericType.mapGenVarsTo(ginstance, tempMap);
94-
VarType _new = ret.remap(tempMap);
92+
if (instType.isGeneric() && instType.type != CodeType.GENVAR) {
93+
GenericType ginstance = (GenericType) instType;
9594

96-
if (_new != null) {
97-
ret = _new;
98-
} else {
99-
ret = getExprType();
95+
cl = DecompilerContext.getStructContext().getClass(instType.value);
96+
if (cl != null && cl.getSignature() != null) {
97+
Map<VarType, VarType> tempMap = new HashMap<>();
98+
cl.getSignature().genericType.mapGenVarsTo(ginstance, tempMap);
99+
VarType _new = ret.remap(tempMap);
100+
101+
if (_new != null) {
102+
ret = _new;
103+
} else {
104+
ret = getExprType();
105+
}
100106
}
101107
}
102108
}
103-
}
104109

105-
return ret;
110+
return ret;
111+
}
106112
}
107113

108114
return getExprType();

src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ public VarType getDefinitionVarType() {
230230
if (lvt.getSignature() != null) {
231231
GenericFieldDescriptor descriptor = GenericMain.parseFieldSignature(lvt.getSignature());
232232
if (descriptor != null) {
233+
descriptor.verifyLoosely(lvt.getVarType());
233234
return descriptor.type;
234235
}
235236
}
@@ -245,28 +246,39 @@ public VarType getDefinitionVarType() {
245246
}
246247
int visibleOffset = bytecode == null ? -1 : bytecode.length();
247248
if (originalIndex != null) {
248-
// first try from signature
249+
// Try loading the type from LVT attributes:
250+
// First try the signature if it's available and matches a known descriptor.
251+
// If signature not available, then try the descriptor.
252+
// If neither are present, fall back to the context-inferred type.
253+
254+
VarType type = null;
255+
StructLocalVariableTableAttribute lvt = method.methodStruct.getLocalVariableAttr();
256+
if (lvt != null) {
257+
String descriptor = lvt.getDescriptor(originalIndex, visibleOffset);
258+
if (descriptor != null) {
259+
type = new VarType(descriptor);
260+
}
261+
}
262+
249263
if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) {
250-
StructLocalVariableTypeTableAttribute attr =
264+
StructLocalVariableTypeTableAttribute lvtSignatures =
251265
method.methodStruct.getAttribute(StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE);
252-
if (attr != null) {
253-
String signature = attr.getSignature(originalIndex, visibleOffset);
266+
if (lvtSignatures != null) {
267+
String signature = lvtSignatures.getSignature(originalIndex, visibleOffset);
254268
if (signature != null) {
255269
GenericFieldDescriptor descriptor = GenericMain.parseFieldSignature(signature);
256270
if (descriptor != null) {
271+
if (type != null) {
272+
descriptor.verifyLoosely(type);
273+
}
257274
return descriptor.type;
258275
}
259276
}
260277
}
261278
}
262279

263-
// then try from descriptor
264-
StructLocalVariableTableAttribute attr = method.methodStruct.getLocalVariableAttr();
265-
if (attr != null) {
266-
String descriptor = attr.getDescriptor(originalIndex, visibleOffset);
267-
if (descriptor != null) {
268-
return new VarType(descriptor);
269-
}
280+
if (type != null) {
281+
return type;
270282
}
271283
}
272284
}

src/org/jetbrains/java/decompiler/struct/gen/generics/GenericClassDescriptor.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@
22
package org.jetbrains.java.decompiler.struct.gen.generics;
33

44
import java.util.ArrayList;
5+
import java.util.HashMap;
56
import java.util.List;
7+
import java.util.Map;
68

9+
import org.jetbrains.java.decompiler.main.DecompilerContext;
10+
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
711
import org.jetbrains.java.decompiler.struct.gen.VarType;
812

913
public class GenericClassDescriptor {
@@ -17,4 +21,40 @@ public class GenericClassDescriptor {
1721
public final List<String> fparameters = new ArrayList<>();
1822

1923
public final List<List<VarType>> fbounds = new ArrayList<>();
24+
25+
private GenericsChecker checker;
26+
27+
public GenericsChecker getChecker() {
28+
if (checker == null) {
29+
checker = new GenericsChecker(fparameters, fbounds);
30+
}
31+
32+
return checker;
33+
}
34+
35+
public void verifyTypes(VarType actualSuperclass, List<VarType> actualSuperInterfaces) {
36+
GenericsChecker checker = getChecker();
37+
38+
if (superclass != null) {
39+
if (!checker.isProperlyBounded(actualSuperclass, superclass)) {
40+
DecompilerContext.getLogger().writeMessage("Mismatched superclass signature, expected: " + actualSuperclass + ", actual: " + superclass.value, IFernflowerLogger.Severity.WARN);
41+
superclass = actualSuperclass;
42+
}
43+
}
44+
45+
for (int i = 0; i < superinterfaces.size(); i++) {
46+
VarType actualSuperInterface = i < actualSuperInterfaces.size() ? actualSuperInterfaces.get(i) : null;
47+
VarType superInterface = superinterfaces.get(i);
48+
49+
if (actualSuperInterface == null) {
50+
DecompilerContext.getLogger().writeMessage("Actual superinterface count is less than expected", IFernflowerLogger.Severity.WARN);
51+
break;
52+
}
53+
54+
if (!checker.isProperlyBounded(actualSuperInterface, superInterface)) {
55+
DecompilerContext.getLogger().writeMessage("Mismatched superinterface signature, expected: " + actualSuperInterface + ", actual: " + superInterface.value, IFernflowerLogger.Severity.WARN);
56+
superinterfaces.set(i, actualSuperInterface);
57+
}
58+
}
59+
}
2060
}
Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,45 @@
11
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
22
package org.jetbrains.java.decompiler.struct.gen.generics;
33

4+
import org.jetbrains.java.decompiler.main.DecompilerContext;
5+
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
6+
import org.jetbrains.java.decompiler.struct.gen.CodeType;
47
import org.jetbrains.java.decompiler.struct.gen.VarType;
58

69
public class GenericFieldDescriptor {
7-
public final VarType type;
10+
public VarType type;
811

912
public GenericFieldDescriptor(VarType type) {
1013
this.type = type;
1114
}
15+
16+
public void verifyType(GenericClassDescriptor containingClassGenerics, VarType actualType) {
17+
if (containingClassGenerics == null) {
18+
DecompilerContext.getLogger().writeMessage("Class generics were not found, verifying type loosely", IFernflowerLogger.Severity.INFO);
19+
verifyLoosely(actualType);
20+
return;
21+
}
22+
23+
GenericsChecker checker = containingClassGenerics.getChecker();
24+
25+
if (!checker.isProperlyBounded(actualType, type)) {
26+
DecompilerContext.getLogger().writeMessage("Mismatched field signature, expected: " + type.value + ", actual: " + actualType.value, IFernflowerLogger.Severity.WARN);
27+
type = actualType;
28+
}
29+
}
30+
31+
//FIXME this is necessary because some places don't have other signature information,
32+
// which prevents the ability to check class- or method-provided generic types
33+
public void verifyLoosely(VarType actualType) {
34+
if (actualType.isSuperset(type)) {
35+
return;
36+
}
37+
38+
if (type.type == CodeType.GENVAR) {
39+
return; // Impossible to verify
40+
}
41+
42+
DecompilerContext.getLogger().writeMessage("Mismatched field signature, expected: " + type.value + ", actual: " + actualType.value, IFernflowerLogger.Severity.WARN);
43+
type = actualType;
44+
}
1245
}

src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMethodDescriptor.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44
import java.util.Collections;
55
import java.util.List;
66

7+
import org.jetbrains.java.decompiler.main.DecompilerContext;
8+
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
79
import org.jetbrains.java.decompiler.struct.gen.VarType;
810

911
public class GenericMethodDescriptor {
1012
public final List<String> typeParameters;
1113
public final List<List<VarType>> typeParameterBounds;
1214
public final List<VarType> parameterTypes;
13-
public final VarType returnType;
15+
public VarType returnType;
1416
public final List<VarType> exceptionTypes;
1517

1618
public GenericMethodDescriptor(List<String> typeParameters,
@@ -28,4 +30,23 @@ public GenericMethodDescriptor(List<String> typeParameters,
2830
private static <T> List<T> substitute(List<T> list) {
2931
return list.isEmpty() ? Collections.emptyList() : list;
3032
}
33+
34+
public void verifyTypes(GenericClassDescriptor descriptor, List<VarType> realParamTypes, VarType realReturnType, List<VarType> realExceptionTypes) {
35+
GenericsChecker checker = descriptor == null ? new GenericsChecker(typeParameters, typeParameterBounds) : descriptor.getChecker().copy(typeParameters, typeParameterBounds);
36+
37+
for (int i = 0; i < parameterTypes.size(); i++) {
38+
VarType parameterType = parameterTypes.get(i);
39+
VarType actualType = realParamTypes.get(i);
40+
41+
if (!checker.isProperlyBounded(parameterType, actualType)) {
42+
DecompilerContext.getLogger().writeMessage("Mismatched method parameter signature, expected: " + actualType.value + ", actual: " + parameterType.value, IFernflowerLogger.Severity.WARN);
43+
parameterTypes.set(i, actualType);
44+
}
45+
}
46+
47+
if (!checker.isProperlyBounded(returnType, realReturnType)) {
48+
DecompilerContext.getLogger().writeMessage("Mismatched method parameter signature, expected: " + realReturnType.value + ", actual: " + returnType.value, IFernflowerLogger.Severity.WARN);
49+
returnType = realReturnType;
50+
}
51+
}
3152
}

0 commit comments

Comments
 (0)