Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -769,6 +770,15 @@ private void writeClassDefinition(ClassNode node, TextBuffer buffer, int indent,
}

GenericClassDescriptor descriptor = cl.getSignature();
if (descriptor != null) {
VarType superclass = new VarType(cl.superClass.getString(), true);
List<VarType> interfaces = Arrays.stream(cl.getInterfaceNames())
.map(s -> new VarType(s, true))
.toList();

descriptor.verifyTypes(superclass, interfaces);
}

if (descriptor != null && !descriptor.fparameters.isEmpty()) {
appendTypeParameters(buffer, descriptor.fparameters, descriptor.fbounds);
}
Expand Down Expand Up @@ -861,6 +871,9 @@ public void writeField(ClassWrapper wrapper, StructClass cl, StructField fd, Tex
Map.Entry<VarType, GenericFieldDescriptor> fieldTypeData = getFieldTypeData(fd);
VarType fieldType = fieldTypeData.getKey();
GenericFieldDescriptor descriptor = fieldTypeData.getValue();
if (descriptor != null) {
descriptor.verifyType(cl.getSignature(), fieldType);
}

if (!isEnum) {
buffer.append(ExprProcessor.getCastTypeName(descriptor == null ? fieldType : descriptor.type));
Expand Down Expand Up @@ -1045,6 +1058,41 @@ public boolean writeMethod(ClassNode node, StructMethod mt, int methodIndex, Tex
}

GenericMethodDescriptor descriptor = mt.getSignature();
if (descriptor != null) {
List<VarType> 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
params.remove(0);
params.remove(0);
}

StructExceptionsAttribute attr = mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_EXCEPTIONS);
List<VarType> exceptions = new ArrayList<>();
if (attr != null) {
for (int i = 0; i < attr.getThrowsExceptions().size(); i++) {
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(checker, params, mt.methodDescriptor().ret, exceptions);
}

boolean throwsExceptions = false;
int paramCount = 0;

Expand Down
55 changes: 55 additions & 0 deletions src/org/jetbrains/java/decompiler/main/ClassWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -704,6 +705,15 @@ else if (components != null) {
buffer.appendClass(node.simpleName, true, cl.qualifiedName);

GenericClassDescriptor descriptor = cl.getSignature();
if (descriptor != null) {
VarType superclass = new VarType(cl.superClass.getString(), true);
List<VarType> interfaces = Arrays.stream(cl.getInterfaceNames())
.map(s -> new VarType(s, true))
.toList();

descriptor.verifyTypes(superclass, interfaces);
}

if (descriptor != null && !descriptor.fparameters.isEmpty()) {
appendTypeParameters(buffer, descriptor.fparameters, descriptor.fbounds);
}
Expand Down Expand Up @@ -834,6 +844,9 @@ public void writeField(ClassWrapper wrapper, StructClass cl, StructField fd, Tex
Map.Entry<VarType, GenericFieldDescriptor> fieldTypeData = getFieldTypeData(fd);
VarType fieldType = fieldTypeData.getKey();
GenericFieldDescriptor descriptor = fieldTypeData.getValue();
if (descriptor != null) {
descriptor.verifyType(cl.getSignature(), fieldType);
}

if (!isEnum) {
buffer.appendCastTypeName(descriptor == null ? fieldType : descriptor.type);
Expand Down Expand Up @@ -1120,6 +1133,48 @@ public boolean writeMethod(ClassNode node, StructMethod mt, int methodIndex, Tex
}

GenericMethodDescriptor descriptor = mt.getSignature();
if (descriptor != null) {
List<VarType> params = new ArrayList<>(Arrays.asList(mt.methodDescriptor().params));

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<VarVersionPair> 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<VarType> exceptions = new ArrayList<>();
if (attr != null) {
for (int i = 0; i < attr.getThrowsExceptions().size(); i++) {
exceptions.add(new VarType(attr.getExcClassname(i, node.classStruct.getPool()), true));
}
}

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(checker, params, mt.methodDescriptor().ret, exceptions);
}

boolean throwsExceptions = false;
int paramCount = 0;

Expand Down
6 changes: 4 additions & 2 deletions src/org/jetbrains/java/decompiler/main/RecordHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import org.jetbrains.java.decompiler.code.CodeConstants;
import org.jetbrains.java.decompiler.main.rels.MethodWrapper;
import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
import org.jetbrains.java.decompiler.modules.decompiler.flow.DirectGraph;
import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement;
import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
Expand Down Expand Up @@ -201,7 +200,10 @@ private static void recordComponentToJava(TextBuffer buffer, StructClass cl, Str
VarType fieldType = new VarType(cd.getDescriptor(), false);
GenericFieldDescriptor descriptor = cd.getSignature();

if (descriptor != null) fieldType = descriptor.type;
if (descriptor != null) {
descriptor.verifyType(cl.getSignature(), fieldType);
fieldType = descriptor.type;
}

buffer.appendCastTypeName(varArgComponent ? fieldType.decreaseArrayDim() : fieldType);
if (varArgComponent) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.jetbrains.java.decompiler.struct.gen.CodeType;
import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor;
import org.jetbrains.java.decompiler.struct.gen.VarType;
import org.jetbrains.java.decompiler.struct.gen.generics.GenericFieldDescriptor;
import org.jetbrains.java.decompiler.struct.gen.generics.GenericType;
import org.jetbrains.java.decompiler.struct.match.MatchEngine;
import org.jetbrains.java.decompiler.struct.match.MatchNode;
Expand Down Expand Up @@ -78,31 +79,36 @@ public VarType getInferredExprType(VarType upperBound) {
cl = cl.superClass == null ? null : DecompilerContext.getStructContext().getClass((String)cl.superClass.value);
}

if (ft != null && ft.getSignature() != null) {
VarType ret = ft.getSignature().type.remap(types.getOrDefault(cl.qualifiedName, Collections.emptyMap()));
if (ft != null) {
GenericFieldDescriptor signature = ft.getSignature();
if (signature != null) {
signature.verifyType(cl.getSignature(), descriptor.type);

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

if (instType.isGeneric() && instType.type != CodeType.GENVAR) {
GenericType ginstance = (GenericType)instType;
if (instance != null && cl.getSignature() != null) {
VarType instType = instance.getInferredExprType(null);

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

if (_new != null) {
ret = _new;
} else {
ret = getExprType();
cl = DecompilerContext.getStructContext().getClass(instType.value);
if (cl != null && cl.getSignature() != null) {
Map<VarType, VarType> tempMap = new HashMap<>();
cl.getSignature().genericType.mapGenVarsTo(ginstance, tempMap);
VarType _new = ret.remap(tempMap);

if (_new != null) {
ret = _new;
} else {
ret = getExprType();
}
}
}
}
}

return ret;
return ret;
}
}

return getExprType();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ public VarType getDefinitionVarType() {
if (lvt.getSignature() != null) {
GenericFieldDescriptor descriptor = GenericMain.parseFieldSignature(lvt.getSignature());
if (descriptor != null) {
descriptor.verifyLoosely(lvt.getVarType());
return descriptor.type;
}
}
Expand All @@ -245,28 +246,39 @@ public VarType getDefinitionVarType() {
}
int visibleOffset = bytecode == null ? -1 : bytecode.length();
if (originalIndex != null) {
// first try from signature
// Try loading the type from LVT attributes:
// First try the signature if it's available and matches a known descriptor.
// If signature not available, then try the descriptor.
// If neither are present, fall back to the context-inferred type.

VarType type = null;
StructLocalVariableTableAttribute lvt = method.methodStruct.getLocalVariableAttr();
if (lvt != null) {
String descriptor = lvt.getDescriptor(originalIndex, visibleOffset);
if (descriptor != null) {
type = new VarType(descriptor);
}
}

if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) {
StructLocalVariableTypeTableAttribute attr =
StructLocalVariableTypeTableAttribute lvtSignatures =
method.methodStruct.getAttribute(StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE);
if (attr != null) {
String signature = attr.getSignature(originalIndex, visibleOffset);
if (lvtSignatures != null) {
String signature = lvtSignatures.getSignature(originalIndex, visibleOffset);
if (signature != null) {
GenericFieldDescriptor descriptor = GenericMain.parseFieldSignature(signature);
if (descriptor != null) {
if (type != null) {
descriptor.verifyLoosely(type);
}
return descriptor.type;
}
}
}
}

// then try from descriptor
StructLocalVariableTableAttribute attr = method.methodStruct.getLocalVariableAttr();
if (attr != null) {
String descriptor = attr.getDescriptor(originalIndex, visibleOffset);
if (descriptor != null) {
return new VarType(descriptor);
}
if (type != null) {
return type;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@
package org.jetbrains.java.decompiler.struct.gen.generics;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
import org.jetbrains.java.decompiler.struct.gen.VarType;

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

public final List<List<VarType>> fbounds = new ArrayList<>();

private GenericsChecker checker;

public GenericsChecker getChecker() {
if (checker == null) {
checker = new GenericsChecker(fparameters, fbounds);
}

return checker;
}

public void verifyTypes(VarType actualSuperclass, List<VarType> actualSuperInterfaces) {
GenericsChecker checker = getChecker();

if (superclass != null) {
if (!checker.isProperlyBounded(actualSuperclass, superclass)) {
DecompilerContext.getLogger().writeMessage("Mismatched superclass signature, expected: " + actualSuperclass + ", actual: " + superclass.value, IFernflowerLogger.Severity.WARN);
superclass = actualSuperclass;
}
}

for (int i = 0; i < superinterfaces.size(); i++) {
VarType actualSuperInterface = i < actualSuperInterfaces.size() ? actualSuperInterfaces.get(i) : null;
VarType superInterface = superinterfaces.get(i);

if (actualSuperInterface == null) {
DecompilerContext.getLogger().writeMessage("Actual superinterface count is less than expected", IFernflowerLogger.Severity.WARN);
break;
}

if (!checker.isProperlyBounded(actualSuperInterface, superInterface)) {
DecompilerContext.getLogger().writeMessage("Mismatched superinterface signature, expected: " + actualSuperInterface + ", actual: " + superInterface.value, IFernflowerLogger.Severity.WARN);
superinterfaces.set(i, actualSuperInterface);
}
}
}
}
Loading