Skip to content

Commit

Permalink
Fix J21 inner class synthetic params not being removed
Browse files Browse the repository at this point in the history
  • Loading branch information
jaskarth committed Apr 4, 2024
1 parent 56e942b commit 23f73e1
Show file tree
Hide file tree
Showing 14 changed files with 263 additions and 132 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public boolean isLanguage(StructClass cl) {

for (Key<?> key : ANNOTATION_ATTRIBUTES) {
if (cl.hasAttribute(key)) {
StructAnnotationAttribute attr = cl.getAttribute(key);
StructAnnotationAttribute attr = cl.getAttribute((Key<StructAnnotationAttribute>) key);
for (AnnotationExprent anno : attr.getAnnotations()) {
if (anno.getClassName().equals("kotlin/Metadata")) {
setContextVariables(cl);
Expand All @@ -60,7 +60,7 @@ public static void setContextVariables(StructClass cl) {
loop:
for (Key<?> key : ANNOTATION_ATTRIBUTES) {
if (cl.hasAttribute(key)) {
StructAnnotationAttribute attr = cl.getAttribute(key);
StructAnnotationAttribute attr = cl.getAttribute((Key<StructAnnotationAttribute>) key);
for (AnnotationExprent a : attr.getAnnotations()) {
if (a.getClassName().equals("kotlin/Metadata")) {
anno = a;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ private boolean invokeProcessors(TextBuffer buffer, ClassNode node) {
public void writeClassHeader(StructClass cl, TextBuffer buffer, ImportCollector importCollector) {
if (KotlinDecompilationContext.getCurrentType() == KotlinDecompilationContext.KotlinType.FILE) {
for (Key<?> key : ANNOTATION_ATTRIBUTES) {
StructAnnotationAttribute attr = cl.getAttribute(key);
StructAnnotationAttribute attr = cl.getAttribute((Key<StructAnnotationAttribute>) key);
if (attr != null) {
for (AnnotationExprent expr : attr.getAnnotations()) {
if (expr.getClassName().equals("kotlin/Metadata")) {
Expand Down Expand Up @@ -1408,7 +1408,7 @@ private static Map.Entry<VarType, GenericFieldDescriptor> getFieldTypeData(Struc

private static boolean containsDeprecatedAnnotation(StructMember mb) {
for (Key<?> key : ANNOTATION_ATTRIBUTES) {
StructAnnotationAttribute attribute = (StructAnnotationAttribute) mb.getAttribute(key);
StructAnnotationAttribute attribute = (StructAnnotationAttribute) mb.getAttribute((Key<StructAnnotationAttribute>) key);
if (attribute != null) {
for (AnnotationExprent annotation : attribute.getAnnotations()) {
if (annotation.getClassName().equals("java/lang/Deprecated")) {
Expand Down Expand Up @@ -1507,7 +1507,7 @@ public static void appendAnnotations(TextBuffer buffer, int indent, StructMember
Set<String> filter = new HashSet<>();

for (Key<?> key : ANNOTATION_ATTRIBUTES) {
StructAnnotationAttribute attribute = mb.getAttribute(key);
StructAnnotationAttribute attribute = mb.getAttribute((Key<StructAnnotationAttribute>) key);
if (attribute != null) {
for (AnnotationExprent annotation : attribute.getAnnotations()) {
if (annotation.getClassName().equals("kotlin/Metadata")
Expand Down Expand Up @@ -1588,7 +1588,7 @@ public static void appendJvmAnnotations(TextBuffer buffer, int indent, StructMem

static boolean isNullable(StructMember mb) {
for (Key<?> key : ANNOTATION_ATTRIBUTES) {
StructAnnotationAttribute attribute = (StructAnnotationAttribute) mb.getAttribute(key);
StructAnnotationAttribute attribute = (StructAnnotationAttribute) mb.getAttribute((Key<StructAnnotationAttribute>) key);
if (attribute != null) {
return attribute.getAnnotations().stream().anyMatch(annotation -> annotation.getClassName().equals(NULLABLE_ANN_NAME));
}
Expand Down Expand Up @@ -1647,7 +1647,7 @@ public static boolean processParameterAnnotations(TextBuffer buffer, StructMetho
boolean ret = false;

for (Key<?> key : PARAMETER_ANNOTATION_ATTRIBUTES) {
StructAnnotationParameterAttribute attribute = (StructAnnotationParameterAttribute) mt.getAttribute(key);
StructAnnotationParameterAttribute attribute = (StructAnnotationParameterAttribute) mt.getAttribute((Key<StructAnnotationParameterAttribute>) key);
if (attribute != null) {
List<List<AnnotationExprent>> annotations = attribute.getParamAnnotations();
if (param < annotations.size()) {
Expand All @@ -1672,7 +1672,7 @@ public static boolean processParameterAnnotations(TextBuffer buffer, StructMetho

private static void appendTypeAnnotations(TextBuffer buffer, int indent, StructMember mb, int targetType, int index, Set<String> filter) {
for (Key<?> key : TYPE_ANNOTATION_ATTRIBUTES) {
StructTypeAnnotationAttribute attribute = (StructTypeAnnotationAttribute) mb.getAttribute(key);
StructTypeAnnotationAttribute attribute = (StructTypeAnnotationAttribute) mb.getAttribute((Key<StructTypeAnnotationAttribute>) key);
if (attribute != null) {
for (TypeAnnotation annotation : attribute.getAnnotations()) {
if (annotation.isTopLevel() && annotation.getTargetType() == targetType && (index < 0 || annotation.getIndex() == index)) {
Expand Down
8 changes: 4 additions & 4 deletions src/org/jetbrains/java/decompiler/main/ClassWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -1536,7 +1536,7 @@ private static Map.Entry<VarType, GenericFieldDescriptor> getFieldTypeData(Struc

private static boolean containsDeprecatedAnnotation(StructMember mb) {
for (Key<?> key : ANNOTATION_ATTRIBUTES) {
StructAnnotationAttribute attribute = (StructAnnotationAttribute) mb.getAttribute(key);
StructAnnotationAttribute attribute = mb.getAttribute((Key<StructAnnotationAttribute>) key);
if (attribute != null) {
for (AnnotationExprent annotation : attribute.getAnnotations()) {
if (annotation.getClassName().equals("java/lang/Deprecated")) {
Expand Down Expand Up @@ -1636,7 +1636,7 @@ static void appendAnnotations(TextBuffer buffer, int indent, StructMember mb, in
Set<String> filter = new HashSet<>();

for (Key<?> key : ANNOTATION_ATTRIBUTES) {
StructAnnotationAttribute attribute = (StructAnnotationAttribute)mb.getAttribute(key);
StructAnnotationAttribute attribute = mb.getAttribute((Key<StructAnnotationAttribute>)key);
if (attribute != null) {
for (AnnotationExprent annotation : attribute.getAnnotations()) {
buffer.appendIndent(indent);
Expand Down Expand Up @@ -1706,7 +1706,7 @@ private static void appendParameterAnnotations(TextBuffer buffer, StructMethod m
Set<String> filter = new HashSet<>();

for (Key<?> key : PARAMETER_ANNOTATION_ATTRIBUTES) {
StructAnnotationParameterAttribute attribute = (StructAnnotationParameterAttribute)mt.getAttribute(key);
StructAnnotationParameterAttribute attribute = mt.getAttribute((Key<StructAnnotationParameterAttribute>) key);
if (attribute != null) {
List<List<AnnotationExprent>> annotations = attribute.getParamAnnotations();
if (param < annotations.size()) {
Expand All @@ -1724,7 +1724,7 @@ private static void appendParameterAnnotations(TextBuffer buffer, StructMethod m

private static void appendTypeAnnotations(TextBuffer buffer, int indent, StructMember mb, int targetType, int index, Set<String> filter) {
for (Key<?> key : TYPE_ANNOTATION_ATTRIBUTES) {
StructTypeAnnotationAttribute attribute = (StructTypeAnnotationAttribute)mb.getAttribute(key);
StructTypeAnnotationAttribute attribute = mb.getAttribute((Key<StructTypeAnnotationAttribute>) key);
if (attribute != null) {
for (TypeAnnotation annotation : attribute.getAnnotations()) {
if (annotation.isTopLevel() && annotation.getTargetType() == targetType && (index < 0 || annotation.getIndex() == index)) {
Expand Down
2 changes: 2 additions & 0 deletions src/org/jetbrains/java/decompiler/main/ClassesProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,8 @@ public void processClass(StructClass cl) throws IOException {
}
} catch (CancelationManager.CanceledException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
DecompilerContext.getLogger().endProcessingClass();
}
Expand Down
6 changes: 3 additions & 3 deletions src/org/jetbrains/java/decompiler/main/RecordHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ private static Set<TextBuffer> getRecordComponentAnnotations(StructClass cl, Str

for (StructMember member : members) {
for (Key<?> key : ClassWriter.ANNOTATION_ATTRIBUTES) {
StructAnnotationAttribute attribute = (StructAnnotationAttribute) member.getAttribute(key);
StructAnnotationAttribute attribute = member.getAttribute((Key<StructAnnotationAttribute>) key);
if (attribute == null) continue;
for (AnnotationExprent annotation : attribute.getAnnotations()) {
TextBuffer text = annotation.toJava(-1);
Expand All @@ -158,7 +158,7 @@ private static Set<TextBuffer> getRecordComponentAnnotations(StructClass cl, Str
}

for (Key<?> key : ClassWriter.TYPE_ANNOTATION_ATTRIBUTES) {
StructTypeAnnotationAttribute attribute = (StructTypeAnnotationAttribute) member.getAttribute(key);
StructTypeAnnotationAttribute attribute = member.getAttribute((Key<StructTypeAnnotationAttribute>) key);
if (attribute == null) continue;
for (TypeAnnotation annotation : attribute.getAnnotations()) {
if (!annotation.isTopLevel()) continue;
Expand All @@ -177,7 +177,7 @@ private static Set<TextBuffer> getRecordComponentAnnotations(StructClass cl, Str
if (constr == null) return buffers;

for (Key<?> key : ClassWriter.PARAMETER_ANNOTATION_ATTRIBUTES) {
StructAnnotationParameterAttribute attribute = (StructAnnotationParameterAttribute) constr.getAttribute(key);
StructAnnotationParameterAttribute attribute = constr.getAttribute((Key<StructAnnotationParameterAttribute>)key);
if (attribute == null) continue;
List<List<AnnotationExprent>> paramAnnotations = attribute.getParamAnnotations();
if (param >= paramAnnotations.size()) continue;
Expand Down
161 changes: 45 additions & 116 deletions src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
import org.jetbrains.java.decompiler.struct.attr.StructEnclosingMethodAttribute;
import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute;
import org.jetbrains.java.decompiler.struct.attr.StructLocalVariableTableAttribute.LocalVariable;
import org.jetbrains.java.decompiler.struct.attr.StructMethodParametersAttribute;
import org.jetbrains.java.decompiler.struct.attr.StructNestHostAttribute;
import org.jetbrains.java.decompiler.struct.consts.LinkConstant;
import org.jetbrains.java.decompiler.struct.consts.PooledConstant;
import org.jetbrains.java.decompiler.struct.gen.CodeType;
Expand Down Expand Up @@ -104,118 +106,6 @@ else if (child.type != ClassNode.Type.MEMBER || (child.access & CodeConstants.AC
processClass(root, child);
}
}
/**
* When Java introduced Enums they aded the ability to use them in Switch statements.
* This was done in a purely syntax sugar way using the old switch on int methods.
* The compiler creates a synthetic class with a static int array field.
* To support enums changing post compile, It initializes this field with a length of the current enum length.
* And then for every referenced enum value it adds a mapping in the form of:
* try {
* field[Enum.VALUE.ordinal()] = 1;
* } catch (FieldNotFoundException e) {}
*
* If a class has multiple switches on multiple enums, the compiler adds the init and try list to the BEGINNING of the static initalizer.
* But they add the field to the END of the fields list.
*/
/*
private void gatherEnumSwitchMaps(ClassNode node) {
for (ClassNode child : node.nested) {
gatherEnumSwitchMaps(child);
}
MethodWrapper clinit = node.getWrapper().getMethodWrapper("<clinit>", "()V");
if (clinit == null || clinit.root == null || clinit.root.getFirst().type != Statement.TYPE_SEQUENCE) {
return;
}
final int STATIC_FINAL_SYNTHETIC = CodeConstants.ACC_STATIC | CodeConstants.ACC_STATIC | CodeConstants.ACC_FINAL | CodeConstants.ACC_SYNTHETIC;
Set<String> potentialFields = new HashSet<String>();
for (StructField fd : node.classStruct.getFields()) {
if ((fd.getAccessFlags() & STATIC_FINAL_SYNTHETIC) == STATIC_FINAL_SYNTHETIC && "[I".equals(fd.getDescriptor())) {
potentialFields.add(fd.getName());
}
}
if (potentialFields.size() == 0) {
return;
}
SequenceStatement seq = (SequenceStatement)clinit.root.getFirst();
for (int x = 0; x < seq.getStats().size();) {
Statement stat = seq.getStats().get(x);
if (stat.type != Statement.TYPE_BASICBLOCK || stat.getExprents() == null || stat.getExprents().size() != 1 || stat.getExprents().get(0).type != Exprent.EXPRENT_ASSIGNMENT) {
break;
}
AssignmentExprent ass = (AssignmentExprent)stat.getExprents().get(0);
if (ass.getLeft().type != Exprent.EXPRENT_FIELD || ass.getRight().type != Exprent.EXPRENT_NEW) {
break;
}
FieldExprent mapField = (FieldExprent)ass.getLeft();
NewExprent _new = ((NewExprent)ass.getRight());
if (!mapField.getClassname().equals(node.classStruct.qualifiedName) || !potentialFields.contains(mapField.getName()) ||
_new.getNewType().type != CodeType.INT || _new.getNewType().arrayDim != 1 ||
_new.getLstDims().size() != 1 || _new.getLstDims().get(0).type != Exprent.EXPRENT_FUNCTION) {
break;
}
FunctionExprent func = (FunctionExprent)_new.getLstDims().get(0);
if (func.getFuncType() != FunctionExprent.FUNCTION_ARRAY_LENGTH || func.getLstOperands().size() != 1 || func.getLstOperands().get(0).type != Exprent.EXPRENT_INVOCATION) {
break;
}
InvocationExprent invoc = (InvocationExprent)func.getLstOperands().get(0);
if (!"values".equals(invoc.getName()) || !("()[L" + invoc.getClassname() + ";").equals(invoc.getStringDescriptor())) {
break;
}
String fieldName = mapField.getName();
String enumName = invoc.getClassname();
Map<Integer, String> idToName = new HashMap<Integer, String>();
boolean replace = false;
int y = x;
while (++y < seq.getStats().size()) {
if (seq.getStats().get(y).type != Statement.TYPE_TRYCATCH) {
break;
}
CatchStatement _try = (CatchStatement)seq.getStats().get(y);
Statement first = _try.getFirst();
List<Exprent> exprents = first.getExprents();
if (_try.getVars().size() != 1 || !"java/lang/NoSuchFieldError".equals(_try.getVars().get(0).getVarType().value) ||
first.type != Statement.TYPE_BASICBLOCK || exprents == null || exprents.size() != 1 || exprents.get(0).type != Exprent.EXPRENT_ASSIGNMENT) {
break;
}
ass = (AssignmentExprent)exprents.get(0);
if (ass.getRight().type != Exprent.EXPRENT_CONST || (!(((ConstExprent)ass.getRight()).getValue() instanceof Integer)) ||
ass.getLeft().type != Exprent.EXPRENT_ARRAY){
break;
}
ArrayExprent array = (ArrayExprent)ass.getLeft();
if (array.getArray().type != Exprent.EXPRENT_FIELD || !array.getArray().equals(mapField) || array.getIndex().type != Exprent.EXPRENT_INVOCATION) {
break;
}
invoc = (InvocationExprent)array.getIndex();
if (!enumName.equals(invoc.getClassname()) || !"ordinal".equals(invoc.getName()) || !"()I".equals(invoc.getStringDescriptor()) ||
invoc.getInstance().type != Exprent.EXPRENT_FIELD) {
break;
}
FieldExprent enumField = (FieldExprent)invoc.getInstance();
if (!enumName.equals(enumField.getClassname()) || !enumField.isStatic()) {
break;
}
idToName.put((Integer)((ConstExprent)ass.getRight()).getValue(), enumField.getName());
seq.replaceStatement(_try, getNewEmptyStatement());
replace = true;
}
if (replace) {
seq.replaceStatement(seq.getStats().get(x), getNewEmptyStatement());
node.classStruct.getEnumSwitchMap().put(fieldName, idToName);
node.getWrapper().getHiddenMembers().add(InterpreterUtil.makeUniqueKey(fieldName, "[I"));
}
x = y;
}
}*/

private static void setLambdaVars(ClassNode parent, ClassNode child) {
if (child.lambdaInformation.is_method_reference) { // method reference, no code and no parameters
Expand Down Expand Up @@ -721,7 +611,9 @@ private static void insertLocalVars(ClassNode parent, ClassNode child) {
// hide synthetic field
if (classNode == child) { // fields higher up the chain were already handled with their classes
StructField fd = child.classStruct.getFields().getWithKey(entry.getKey());
child.getWrapper().getHiddenMembers().add(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()));
if (fd != null) { // this can be null if we're in J21 and there is no field. This is fine, just ignore it.
child.getWrapper().getHiddenMembers().add(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()));
}
}
}
}
Expand Down Expand Up @@ -846,7 +738,7 @@ private static Map<String, List<VarFieldPair>> getMaskLocalVars(ClassWrapper wra

int varIndex = 1;
for (int i = 0; i < md.params.length; i++) { // no static methods allowed
String keyField = getEnclosingVarField(cl, method, graph, varIndex);
String keyField = getEnclosingVarField(cl, method, graph, varIndex, i);
fields.add(keyField == null ? null : new VarFieldPair(keyField, new VarVersionPair(-1, 0))); // TODO: null?
varIndex += md.params[i].stackSize;
}
Expand All @@ -859,11 +751,12 @@ private static Map<String, List<VarFieldPair>> getMaskLocalVars(ClassWrapper wra
return mapMasks;
}

private static String getEnclosingVarField(StructClass cl, MethodWrapper method, DirectGraph graph, int index) {
private static String getEnclosingVarField(StructClass cl, MethodWrapper method, DirectGraph graph, int index, int outerIdx) {
String field = "";

// parameter variable final
if (method.varproc.getVarFinal(new VarVersionPair(index, 0)) == FinalType.NON_FINAL) {
VarVersionPair var = new VarVersionPair(index, 0);
if (method.varproc.getVarFinal(var) == FinalType.NON_FINAL) {
return null;
}

Expand Down Expand Up @@ -893,6 +786,37 @@ private static String getEnclosingVarField(StructClass cl, MethodWrapper method,
}
}

if ("".equals(field) && cl.getVersion().major >= 21) {
// Hack for J21 - if the class doesn't capture the outer state, then the synthetic 'this$0' var won't have an assigntment.
// In this case, there's not much info we can gather. Make a best guess based on the MethodParameters struct.
if (method.methodStruct.hasAttribute(StructGeneralAttribute.ATTRIBUTE_METHOD_PARAMETERS)) {
StructMethodParametersAttribute attr = method.methodStruct.getAttribute(StructGeneralAttribute.ATTRIBUTE_METHOD_PARAMETERS);

List<StructMethodParametersAttribute.Entry> entries = attr.getEntries();
if (outerIdx < entries.size() && (entries.get(outerIdx).myAccessFlags & CodeConstants.ACC_MANDATED) == CodeConstants.ACC_MANDATED) {

String name = method.varproc.getVarName(var);
VarType type = method.varproc.getVarType(var);
field = InterpreterUtil.makeUniqueKey(name, type.toString());
}
}
}

// Still didn't work? We might be missing the mandated flag.
// Do an even worse hack to just guess based on the nest host.
if ("".equals(field) && cl.getVersion().major >= 21) {
if (index == 1 && cl.hasAttribute(StructGeneralAttribute.ATTRIBUTE_NEST_HOST)) {
StructNestHostAttribute host = cl.getAttribute(StructGeneralAttribute.ATTRIBUTE_NEST_HOST);
String hostName = host.getHostClass(cl.getPool());

String name = method.varproc.getVarName(var);
VarType type = method.varproc.getVarType(var);
if (hostName.equals(type.value)) {
field = InterpreterUtil.makeUniqueKey(name, type.toString());
}
}
}

return field;
}

Expand Down Expand Up @@ -1216,6 +1140,11 @@ public boolean equals(Object o) {
public int hashCode() {
return fieldKey.hashCode() + varPair.hashCode();
}

@Override
public String toString() {
return varPair + ": " + fieldKey;
}
}

private static interface ExprentIteratorWithReplace {
Expand Down
2 changes: 1 addition & 1 deletion src/org/jetbrains/java/decompiler/struct/StructMember.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public int getAccessFlags() {
return accessFlags;
}

public <T extends StructGeneralAttribute> T getAttribute(Key<?> attribute) {
public <T extends StructGeneralAttribute> T getAttribute(Key<T> attribute) {
@SuppressWarnings("unchecked") T t = (T)attributes.get(attribute.name);
return t;
}
Expand Down
Loading

0 comments on commit 23f73e1

Please sign in to comment.