Skip to content

Commit 3904b93

Browse files
committed
Add mismatched generics catches
1 parent 366afc5 commit 3904b93

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
@@ -769,6 +769,15 @@ private void writeClassDefinition(ClassNode node, TextBuffer buffer, int indent,
769769
}
770770

771771
GenericClassDescriptor descriptor = cl.getSignature();
772+
if (descriptor != null) {
773+
VarType superclass = new VarType(cl.superClass.getString(), true);
774+
List<VarType> interfaces = Arrays.stream(cl.getInterfaceNames())
775+
.map(s -> new VarType(s, true))
776+
.toList();
777+
778+
descriptor.verifyTypes(superclass, interfaces);
779+
}
780+
772781
if (descriptor != null && !descriptor.fparameters.isEmpty()) {
773782
appendTypeParameters(buffer, descriptor.fparameters, descriptor.fbounds);
774783
}
@@ -861,6 +870,9 @@ public void writeField(ClassWrapper wrapper, StructClass cl, StructField fd, Tex
861870
Map.Entry<VarType, GenericFieldDescriptor> fieldTypeData = getFieldTypeData(fd);
862871
VarType fieldType = fieldTypeData.getKey();
863872
GenericFieldDescriptor descriptor = fieldTypeData.getValue();
873+
if (descriptor != null) {
874+
descriptor.verifyType(cl.getSignature(), fieldType);
875+
}
864876

865877
if (!isEnum) {
866878
buffer.append(ExprProcessor.getCastTypeName(descriptor == null ? fieldType : descriptor.type));
@@ -1045,6 +1057,27 @@ public boolean writeMethod(ClassNode node, StructMethod mt, int methodIndex, Tex
10451057
}
10461058

10471059
GenericMethodDescriptor descriptor = mt.getSignature();
1060+
if (descriptor != null) {
1061+
List<VarType> params = new ArrayList<>(Arrays.asList(mt.methodDescriptor().params));
1062+
if ((node.access & CodeConstants.ACC_ENUM) != 0 && init) {
1063+
// Signatures skip enum parameters, the checker must as well
1064+
params.remove(0);
1065+
params.remove(0);
1066+
}
1067+
1068+
StructExceptionsAttribute attr = mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_EXCEPTIONS);
1069+
List<VarType> exceptions = new ArrayList<>();
1070+
if (attr != null) {
1071+
for (int i = 0; i < attr.getThrowsExceptions().size(); i++) {
1072+
exceptions.add(new VarType(attr.getExcClassname(i, node.classStruct.getPool()), true));
1073+
}
1074+
}
1075+
1076+
GenericClassDescriptor classSig = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0 ? node.classStruct.getSignature() : null;
1077+
1078+
descriptor.verifyTypes(classSig, params, mt.methodDescriptor().ret, exceptions);
1079+
}
1080+
10481081
boolean throwsExceptions = false;
10491082
int paramCount = 0;
10501083

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

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

706706
GenericClassDescriptor descriptor = cl.getSignature();
707+
if (descriptor != null) {
708+
VarType superclass = new VarType(cl.superClass.getString(), true);
709+
List<VarType> interfaces = Arrays.stream(cl.getInterfaceNames())
710+
.map(s -> new VarType(s, true))
711+
.toList();
712+
713+
descriptor.verifyTypes(superclass, interfaces);
714+
}
715+
707716
if (descriptor != null && !descriptor.fparameters.isEmpty()) {
708717
appendTypeParameters(buffer, descriptor.fparameters, descriptor.fbounds);
709718
}
@@ -834,6 +843,9 @@ public void writeField(ClassWrapper wrapper, StructClass cl, StructField fd, Tex
834843
Map.Entry<VarType, GenericFieldDescriptor> fieldTypeData = getFieldTypeData(fd);
835844
VarType fieldType = fieldTypeData.getKey();
836845
GenericFieldDescriptor descriptor = fieldTypeData.getValue();
846+
if (descriptor != null) {
847+
descriptor.verifyType(cl.getSignature(), fieldType);
848+
}
837849

838850
if (!isEnum) {
839851
buffer.appendCastTypeName(descriptor == null ? fieldType : descriptor.type);
@@ -1120,6 +1132,27 @@ public boolean writeMethod(ClassNode node, StructMethod mt, int methodIndex, Tex
11201132
}
11211133

11221134
GenericMethodDescriptor descriptor = mt.getSignature();
1135+
if (descriptor != null) {
1136+
List<VarType> params = new ArrayList<>(Arrays.asList(mt.methodDescriptor().params));
1137+
if ((node.access & CodeConstants.ACC_ENUM) != 0 && init) {
1138+
// Signatures skip enum parameters, the checker must as well
1139+
params.remove(0);
1140+
params.remove(0);
1141+
}
1142+
1143+
StructExceptionsAttribute attr = mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_EXCEPTIONS);
1144+
List<VarType> exceptions = new ArrayList<>();
1145+
if (attr != null) {
1146+
for (int i = 0; i < attr.getThrowsExceptions().size(); i++) {
1147+
exceptions.add(new VarType(attr.getExcClassname(i, node.classStruct.getPool()), true));
1148+
}
1149+
}
1150+
1151+
GenericClassDescriptor classSig = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0 ? node.classStruct.getSignature() : null;
1152+
1153+
descriptor.verifyTypes(classSig, params, mt.methodDescriptor().ret, exceptions);
1154+
}
1155+
11231156
boolean throwsExceptions = false;
11241157
int paramCount = 0;
11251158

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)