src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMain.java (218 lines of code) (raw):
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.java.decompiler.struct.gen.generics;
import org.jetbrains.java.decompiler.code.CodeConstants;
import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
import org.jetbrains.java.decompiler.modules.decompiler.typeann.TypeAnnotationWriteHelper;
import org.jetbrains.java.decompiler.struct.StructTypePathEntry;
import org.jetbrains.java.decompiler.struct.gen.VarType;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public final class GenericMain {
private static final String[] typeNames = {
"byte",
"char",
"double",
"float",
"int",
"long",
"short",
"boolean",
};
public static GenericClassDescriptor parseClassSignature(String qualifiedName, String signature) {
String original = signature;
try {
GenericClassDescriptor descriptor = new GenericClassDescriptor();
signature = parseFormalParameters(signature, descriptor.fparameters, descriptor.fbounds);
String superCl = GenericType.getNextType(signature);
descriptor.superclass = GenericType.parse(superCl);
signature = signature.substring(superCl.length());
while (!signature.isEmpty()) {
String superIf = GenericType.getNextType(signature);
descriptor.superinterfaces.add(GenericType.parse(superIf));
signature = signature.substring(superIf.length());
}
StringBuilder buf = new StringBuilder();
buf.append('L').append(qualifiedName).append('<');
for (String t : descriptor.fparameters) {
buf.append('T').append(t).append(';');
}
buf.append(">;");
descriptor.genericType = (GenericType)GenericType.parse(buf.toString());
return descriptor;
}
catch (RuntimeException e) {
DecompilerContext.getLogger().writeMessage("Invalid signature: " + original, IFernflowerLogger.Severity.WARN);
return null;
}
}
public static GenericFieldDescriptor parseFieldSignature(String signature) {
try {
return new GenericFieldDescriptor(GenericType.parse(signature));
}
catch (RuntimeException e) {
DecompilerContext.getLogger().writeMessage("Invalid signature: " + signature, IFernflowerLogger.Severity.WARN);
return null;
}
}
public static GenericMethodDescriptor parseMethodSignature(String signature) {
String original = signature;
try {
List<String> typeParameters = new ArrayList<>();
List<List<VarType>> typeParameterBounds = new ArrayList<>();
signature = parseFormalParameters(signature, typeParameters, typeParameterBounds);
int to = signature.indexOf(")");
String parameters = signature.substring(1, to);
signature = signature.substring(to + 1);
List<VarType> parameterTypes = new ArrayList<>();
while (!parameters.isEmpty()) {
String par = GenericType.getNextType(parameters);
parameterTypes.add(GenericType.parse(par));
parameters = parameters.substring(par.length());
}
String ret = GenericType.getNextType(signature);
VarType returnType = GenericType.parse(ret);
signature = signature.substring(ret.length());
List<VarType> exceptionTypes = new ArrayList<>();
if (!signature.isEmpty()) {
String[] exceptions = signature.split("\\^");
for (int i = 1; i < exceptions.length; i++) {
exceptionTypes.add(GenericType.parse(exceptions[i]));
}
}
return new GenericMethodDescriptor(typeParameters, typeParameterBounds, parameterTypes, returnType, exceptionTypes);
}
catch (RuntimeException e) {
DecompilerContext.getLogger().writeMessage("Invalid signature: " + original, IFernflowerLogger.Severity.WARN);
return null;
}
}
private static String parseFormalParameters(String signature, List<String> parameters, List<List<VarType>> bounds) {
if (signature.charAt(0) != '<') {
return signature;
}
int counter = 1;
int index = 1;
loop:
while (index < signature.length()) {
switch (signature.charAt(index)) {
case '<' -> counter++;
case '>' -> {
counter--;
if (counter == 0) {
break loop;
}
}
}
index++;
}
String value = signature.substring(1, index);
signature = signature.substring(index + 1);
while (!value.isEmpty()) {
int to = value.indexOf(":");
String param = value.substring(0, to);
value = value.substring(to + 1);
List<VarType> lstBounds = new ArrayList<>();
while (true) {
if (value.charAt(0) == ':') {
// empty superclass, skip
value = value.substring(1);
}
String bound = GenericType.getNextType(value);
lstBounds.add(GenericType.parse(bound));
value = value.substring(bound.length());
if (value.isEmpty() || value.charAt(0) != ':') {
break;
}
else {
value = value.substring(1);
}
}
parameters.add(param);
bounds.add(lstBounds);
}
return signature;
}
public static String getGenericCastTypeName(GenericType type, List<TypeAnnotationWriteHelper> typeAnnWriteHelpers) {
List<TypeAnnotationWriteHelper> arrayTypeAnnWriteHelpers = ExprProcessor.arrayPath(type, typeAnnWriteHelpers);
List<TypeAnnotationWriteHelper> nonArrayTypeAnnWriteHelpers = ExprProcessor.nonArrayPath(type, typeAnnWriteHelpers);
StringBuilder sb = new StringBuilder(getTypeName(type, nonArrayTypeAnnWriteHelpers));
ExprProcessor.writeArray(sb, type.getArrayDim(), arrayTypeAnnWriteHelpers);
return sb.toString();
}
private static String getTypeName(GenericType type, List<TypeAnnotationWriteHelper> typeAnnWriteHelpers) {
int tp = type.getType();
if (tp <= CodeConstants.TYPE_BOOLEAN) {
return typeNames[tp];
}
else if (tp == CodeConstants.TYPE_VOID) {
return "void";
}
else if (tp == CodeConstants.TYPE_GENVAR) {
StringBuilder sb = new StringBuilder();
ExprProcessor.writeTypeAnnotationBeforeType(type, sb, typeAnnWriteHelpers);
sb.append(type.getValue());
return sb.toString();
}
else if (tp == CodeConstants.TYPE_OBJECT) {
StringBuilder sb = new StringBuilder();
type.appendCastName(sb, typeAnnWriteHelpers);
return sb.toString();
}
throw new RuntimeException("Invalid type: " + type);
}
public static List<TypeAnnotationWriteHelper> getGenericTypeAnnotations(
int argIndex,
List<TypeAnnotationWriteHelper> typeAnnWriteHelpers
) {
return typeAnnWriteHelpers.stream().filter(typeAnnWriteHelper -> {
StructTypePathEntry entry = typeAnnWriteHelper.getPaths().peek();
boolean inGeneric = entry != null && entry.getTypeArgumentIndex() == argIndex
&& entry.getTypePathEntryKind() == StructTypePathEntry.Kind.TYPE.getId();
if (inGeneric) typeAnnWriteHelper.getPaths().pop();
return inGeneric;
}).collect(Collectors.toList());
}
public static List<TypeAnnotationWriteHelper> writeTypeAnnotationBeforeWildCard(
StringBuilder sb,
VarType type,
List<TypeAnnotationWriteHelper> typeAnnWriteHelpers
) {
return typeAnnWriteHelpers.stream().filter(typeAnnWriteHelper -> {
StructTypePathEntry path = typeAnnWriteHelper.getPaths().peek();
if ((type instanceof GenericType genericType && genericType.getWildcard() != GenericType.WILDCARD_NO ||
type == null) && path == null) {
typeAnnWriteHelper.writeTo(sb);
return false;
}
if (type != null && type.getArrayDim() == typeAnnWriteHelper.getPaths().size() && type.getArrayDim() == typeAnnWriteHelper.arrayPathCount()) {
typeAnnWriteHelper.writeTo(sb);
return false;
}
return true;
}).collect(Collectors.toList());
}
public static List<TypeAnnotationWriteHelper> writeTypeAnnotationAfterWildCard(
StringBuilder sb,
VarType type,
List<TypeAnnotationWriteHelper> typeAnnWriteHelpers
) {
typeAnnWriteHelpers.forEach(typeAnnWriteHelper -> { // remove all wild card path entries
StructTypePathEntry path = typeAnnWriteHelper.getPaths().peek();
boolean isWildCard = path != null && path.getTypePathEntryKind() == StructTypePathEntry.Kind.TYPE_WILDCARD.getId();
if (isWildCard) typeAnnWriteHelper.getPaths().pop();
});
return typeAnnWriteHelpers.stream().filter(typeAnnWriteHelper -> {
StructTypePathEntry path = typeAnnWriteHelper.getPaths().peek();
if (type.getArrayDim() == 0 && path == null) {
typeAnnWriteHelper.writeTo(sb);
return false;
}
if (type.getArrayDim() == typeAnnWriteHelper.getPaths().size() && type.getArrayDim() == typeAnnWriteHelper.arrayPathCount()) {
typeAnnWriteHelper.writeTo(sb);
return false;
}
return true;
}).collect(Collectors.toList());
}
}