Skip to content

Commit 56c497c

Browse files
committed
Function reference support
1 parent 76559ed commit 56c497c

File tree

13 files changed

+601
-357
lines changed

13 files changed

+601
-357
lines changed

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,16 +83,23 @@ public static void parseMetadataFor(StructClass cl) {
8383
return;
8484
}
8585

86+
int k = (int) ((ConstExprent) anno.getParValues().get(kIndex)).getValue();
87+
8688
if (d1Index == -1) {
87-
DecompilerContext.getLogger().writeMessage("No d1 attribute (data) in class metadata for class " + cl.qualifiedName + ", cannot continue Kotlin parsing", IFernflowerLogger.Severity.WARN);
89+
boolean functionRef = cl.superClass.getString().equals("kotlin/jvm/internal/FunctionReferenceImpl");
90+
if (functionRef) {
91+
cl.getAttributes().put(KotlinMetadata.KEY, new KotlinMetadata(new KotlinMetadata.FunctionReference(cl), null));
92+
}
93+
94+
String resolution = functionRef ? "assuming function reference" : "cannot continue Kotlin parsing";
95+
DecompilerContext.getLogger().writeMessage("No d1 attribute (data) in class metadata for class " + cl.qualifiedName + ", " + resolution, IFernflowerLogger.Severity.WARN);
8896
return;
8997
}
9098

9199
if (d2Index == -1) {
92100
DecompilerContext.getLogger().writeMessage("No d2 attribute (strings) in class metadata for class " + cl.qualifiedName, IFernflowerLogger.Severity.WARN);
93101
}
94102

95-
int k = (int) ((ConstExprent) anno.getParValues().get(kIndex)).getValue();
96103
Exprent d1 = anno.getParValues().get(d1Index);
97104
Exprent d2 = d2Index != -1 ? anno.getParValues().get(d2Index) : null;
98105

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

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,11 @@ public void writeClass(ClassNode node, TextBuffer buffer, int indent) {
260260
return;
261261
}
262262

263+
if (ktData != null && ktData.metadata instanceof KotlinMetadata.SyntheticClass) {
264+
writeLambda(node, buffer, indent, ktData);
265+
return;
266+
}
267+
263268
Optional<ClassNode> companion;
264269
if (ktData != null && ktData.metadata instanceof KotlinMetadata.Class cls && cls.proto().hasCompanionObjectName()) {
265270
String name = ktData.nameResolver.resolve(cls.proto().getCompanionObjectName());
@@ -560,6 +565,55 @@ private void writeLambda(ClassNode node, TextBuffer buffer, int indent, KotlinMe
560565
buffer.appendIndent(indent).append("}");
561566
}
562567

568+
public static TextBuffer stringifyReference(int indent, ClassesProcessor.ClassNode node, BitSet bytecode, Exprent receiver) {
569+
// Attempt to extract the real reference from <init>
570+
MethodWrapper init = node.getWrapper().getMethodWrapper("<init>", receiver != null ? "(Ljava/lang/Object;)V" : "()V");
571+
if (init == null) {
572+
return null;
573+
}
574+
575+
List<Exprent> exprents = init.root.getFirst().getExprents();
576+
if (exprents == null || exprents.size() != 1) {
577+
return null;
578+
}
579+
580+
TextBuffer buf = new TextBuffer();
581+
buf.addBytecodeMapping(bytecode);
582+
583+
Exprent superCall = exprents.get(0);
584+
List<Exprent> parameters = ((InvocationExprent) superCall).getLstParameters();
585+
586+
int parameterIndex = receiver == null ? 1 : 2;
587+
String classRef = ((ConstExprent) parameters.get(parameterIndex++)).getValue().toString();
588+
String methodRef = ((ConstExprent) parameters.get(parameterIndex++)).getValue().toString();
589+
String methodAndDesc = ((ConstExprent) parameters.get(parameterIndex++)).getValue().toString();
590+
591+
StructClass cl = DecompilerContext.getStructContext().getClass(classRef);
592+
593+
String desc = methodAndDesc.substring(methodRef.length());
594+
595+
if (methodRef.equals("<init>")) {
596+
buf.append("::");
597+
VarType classType = new VarType(classRef, true);
598+
buf.appendMethod(KTypes.getKotlinType(classType), false, classRef, methodRef, desc);
599+
return buf;
600+
}
601+
602+
if (receiver != null) {
603+
buf.append(receiver.toJava(indent));
604+
} else {
605+
// Add class name, unless it is top-level
606+
if (cl == null || !cl.hasAttribute(KotlinMetadata.KEY) || !(cl.getAttribute(KotlinMetadata.KEY).metadata instanceof KotlinMetadata.File)) {
607+
VarType classType = new VarType(classRef, true);
608+
buf.appendTypeName(KTypes.getKotlinType(classType), classType);
609+
}
610+
}
611+
612+
buf.append("::");
613+
buf.appendMethod(methodRef, false, classRef, methodRef, desc);
614+
return buf;
615+
}
616+
563617
private void writeKotlinFile(ClassNode node, TextBuffer buffer, int indent, KotlinMetadata ktData) {
564618
ClassWrapper wrapper = node.getWrapper();
565619
StructClass cl = wrapper.getClassStruct();

plugins/kotlin/src/main/java/org/vineflower/kotlin/expr/KFieldExprent.java

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
package org.vineflower.kotlin.expr;
22

33
import org.jetbrains.java.decompiler.main.DecompilerContext;
4+
import org.jetbrains.java.decompiler.main.rels.ClassWrapper;
5+
import org.jetbrains.java.decompiler.main.rels.MethodWrapper;
46
import org.jetbrains.java.decompiler.modules.decompiler.exps.ExprUtil;
57
import org.jetbrains.java.decompiler.modules.decompiler.exps.FieldExprent;
8+
import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
69
import org.jetbrains.java.decompiler.struct.StructClass;
710
import org.jetbrains.java.decompiler.struct.gen.VarType;
11+
import org.jetbrains.java.decompiler.util.InterpreterUtil;
812
import org.jetbrains.java.decompiler.util.TextBuffer;
13+
import org.vineflower.kotlin.KotlinChooser;
14+
import org.vineflower.kotlin.KotlinWriter;
915
import org.vineflower.kotlin.metadata.KotlinMetadata;
16+
import org.vineflower.kotlin.struct.KFunction;
17+
import org.vineflower.kotlin.struct.KParameter;
1018
import org.vineflower.kotlin.util.KTypes;
1119

1220
public class KFieldExprent extends FieldExprent implements KExprent {
@@ -19,6 +27,7 @@ public TextBuffer toJava(int indent) {
1927
TextBuffer buf = new TextBuffer();
2028

2129
if (getName().equals("TYPE") && ExprUtil.PRIMITIVE_TYPES.containsKey(getClassname())) {
30+
buf.addBytecodeMapping(bytecode);
2231
VarType type = new VarType(getClassname(), true);
2332
buf.append(KTypes.getKotlinType(type));
2433
buf.append("::class.javaPrimitiveType");
@@ -30,6 +39,8 @@ public TextBuffer toJava(int indent) {
3039
return super.toJava(indent);
3140
}
3241

42+
KotlinChooser.parseMetadataFor(cl);
43+
3344
KotlinMetadata ktData = cl.getAttribute(KotlinMetadata.KEY);
3445
if (ktData == null) {
3546
return super.toJava(indent);
@@ -39,8 +50,60 @@ public TextBuffer toJava(int indent) {
3950
if (cls.proto().hasCompanionObjectName()) {
4051
String name = ktData.nameResolver == null ? cl.qualifiedName : ktData.nameResolver.resolve(cls.proto().getCompanionObjectName());
4152
buf.appendClass(DecompilerContext.getImportCollector().getShortName(cl.qualifiedName), false, name);
53+
buf.addBytecodeMapping(bytecode);
4254
return buf;
4355
}
56+
} else if (ktData.metadata instanceof KotlinMetadata.SyntheticClass) {
57+
KFunction function = ktData.getFunctions().values().iterator().next();
58+
ClassWrapper wrapper = DecompilerContext.getClassProcessor().getMapRootClasses().get(getClassname()).getWrapper();
59+
MethodWrapper method = function.methodSupplier().apply(wrapper);
60+
61+
buf.append("{");
62+
63+
int index = 0;
64+
for (KParameter param : function.parameters()) {
65+
if (index > 0) {
66+
buf.append(", ");
67+
} else {
68+
buf.append(" ");
69+
}
70+
buf.appendVariable(param.name(), true, true, function.classStruct().qualifiedName, function.methodStruct().getName(), function.methodStruct().getDescriptor(), index, param.name());
71+
index += param.type().stackSize;
72+
}
73+
74+
if (function.parameters().length > 0) {
75+
buf.append(" ->");
76+
}
77+
78+
buf.appendLineSeparator();
79+
80+
MethodWrapper outerMethod = DecompilerContext.getContextProperty(DecompilerContext.CURRENT_METHOD_WRAPPER);
81+
try {
82+
if (method.decompileError == null) {
83+
RootStatement root = method.root;
84+
if (root != null) {
85+
DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, method);
86+
TextBuffer body = root.toJava(indent + 1);
87+
body.addBytecodeMapping(root.getDummyExit().bytecode);
88+
buf.append(body, cl.qualifiedName, InterpreterUtil.makeUniqueKey(method.methodStruct.getName(), method.methodStruct.getDescriptor()));
89+
}
90+
}
91+
} finally {
92+
DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, outerMethod);
93+
}
94+
95+
if (method.decompileError != null) {
96+
KotlinWriter.dumpError(buf, method, indent + 1);
97+
}
98+
99+
buf.appendIndent(indent).append("}");
100+
buf.addBytecodeMapping(bytecode);
101+
return buf;
102+
} else if (ktData.metadata instanceof KotlinMetadata.FunctionReference) {
103+
TextBuffer buffer = KotlinWriter.stringifyReference(indent, DecompilerContext.getClassProcessor().getMapRootClasses().get(getClassname()), bytecode, null);
104+
if (buffer != null) {
105+
return buffer;
106+
}
44107
}
45108
}
46109
return super.toJava(indent);

plugins/kotlin/src/main/java/org/vineflower/kotlin/expr/KInvocationExprent.java

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,38 @@
22

33
import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
44
import org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent;
5+
import org.jetbrains.java.decompiler.struct.gen.VarType;
6+
import org.jetbrains.java.decompiler.util.TextBuffer;
7+
import org.vineflower.kotlin.util.KTypes;
58

69
public class KInvocationExprent extends InvocationExprent implements KExprent {
7-
private boolean shadowStaticBase = false;
8-
910
public KInvocationExprent(InvocationExprent expr) {
1011
super(expr);
1112
}
1213

13-
public boolean isShadowStaticBase() {
14-
return shadowStaticBase;
15-
}
14+
@Override
15+
public TextBuffer toJava(int indent) {
16+
if (KTypes.isFunctionType(new VarType(getClassname(), true))) {
17+
TextBuffer buf = new TextBuffer();
18+
TextBuffer instanceBuf = getInstance().toJava(indent);
19+
if (getInstance().getPrecedence() > getPrecedence()) {
20+
instanceBuf.enclose("(", ")");
21+
}
22+
buf.append(instanceBuf);
23+
if (getLstParameters().isEmpty()) {
24+
buf.appendMethod("()", false, getClassname(), getName(), getDescriptor());
25+
buf.addBytecodeMapping(bytecode);
26+
return buf;
27+
}
28+
29+
buf.appendMethod("(", false, getClassname(), getName(), getDescriptor());
30+
buf.append(appendParamList(indent));
31+
buf.appendMethod(")", false, getClassname(), getName(), getDescriptor());
32+
buf.addBytecodeMapping(bytecode);
33+
return buf;
34+
}
1635

17-
public void setShadowStaticBase(boolean shadowStaticBase) {
18-
this.shadowStaticBase = shadowStaticBase;
36+
return super.toJava(indent);
1937
}
2038

2139
@Override

plugins/kotlin/src/main/java/org/vineflower/kotlin/expr/KNewExprent.java

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,25 @@
88
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
99
import org.jetbrains.java.decompiler.main.rels.MethodWrapper;
1010
import org.jetbrains.java.decompiler.modules.decompiler.StatEdge;
11-
import org.jetbrains.java.decompiler.modules.decompiler.exps.ExitExprent;
12-
import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
13-
import org.jetbrains.java.decompiler.modules.decompiler.exps.NewExprent;
11+
import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
1412
import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
1513
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;
1614
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
1715
import org.jetbrains.java.decompiler.struct.gen.VarType;
1816
import org.jetbrains.java.decompiler.util.InterpreterUtil;
1917
import org.jetbrains.java.decompiler.util.TextBuffer;
18+
import org.vineflower.kotlin.KotlinChooser;
2019
import org.vineflower.kotlin.KotlinWriter;
20+
import org.vineflower.kotlin.metadata.KotlinMetadata;
2121
import org.vineflower.kotlin.util.KTypes;
2222

2323
import java.util.ArrayList;
24+
import java.util.BitSet;
2425
import java.util.List;
2526

2627
public class KNewExprent extends NewExprent implements KExprent {
28+
private static final String REFERENCE_CTOR_DESC = "(Ljava/lang/Object;)V";
29+
2730
public KNewExprent(NewExprent expr) {
2831
super(expr.getNewType(), expr.getLstDims(), expr.bytecode);
2932
setConstructor(expr.getConstructor());
@@ -43,10 +46,10 @@ public int getPrecedence() {
4346

4447
@Override
4548
public TextBuffer toJava(int indent) {
49+
ClassesProcessor.ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(getNewType().value);
4650
if (isLambda()) {
4751
MethodWrapper outerWrapper = DecompilerContext.getContextProperty(DecompilerContext.CURRENT_METHOD_WRAPPER);
4852
try {
49-
ClassesProcessor.ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(getNewType().value);
5053
ClassesProcessor.ClassNode.LambdaInformation lambdaInfo = node.lambdaInformation;
5154
String name = lambdaInfo.content_method_name;
5255
if (name.contains("$lambda$")) {
@@ -144,10 +147,20 @@ public TextBuffer toJava(int indent) {
144147
DecompilerContext.setProperty(DecompilerContext.CURRENT_METHOD_WRAPPER, outerWrapper);
145148
}
146149
} else if (isAnonymous()) {
147-
ClassesProcessor.ClassNode node = DecompilerContext.getClassProcessor().getMapRootClasses().get(getNewType().value);
148150
TextBuffer buf = new TextBuffer();
149151
new KotlinWriter().writeClass(node, buf, indent);
150152
return buf;
153+
} else if (node != null && new KotlinChooser().isLanguage(node.classStruct)) {
154+
KotlinMetadata ktData = node.classStruct.getAttribute(KotlinMetadata.KEY);
155+
if (!(ktData.metadata instanceof KotlinMetadata.FunctionReference)) {
156+
return super.toJava(indent);
157+
}
158+
159+
Exprent receiver = getConstructor().getLstParameters().get(0);
160+
TextBuffer buf = KotlinWriter.stringifyReference(indent, node, bytecode, receiver);
161+
if (buf != null) {
162+
return buf;
163+
}
151164
}
152165

153166
return super.toJava(indent);

plugins/kotlin/src/main/java/org/vineflower/kotlin/metadata/KotlinMetadata.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public record Class(@NotNull StructClass classStruct, @NotNull ProtoBuf.Class pr
2222
public record File(@NotNull StructClass classStruct, @NotNull ProtoBuf.Package proto) implements Metadata {}
2323
public record SyntheticClass(@NotNull StructClass classStruct, @NotNull ProtoBuf.Function proto) implements Metadata {}
2424
public record MultifileClass(@NotNull StructClass classStruct, @NotNull ProtoBuf.Package proto) implements Metadata {}
25+
public record FunctionReference(@NotNull StructClass classStruct) implements Metadata {}
2526

2627
public final @NotNull Metadata metadata;
2728
public final @Nullable MetadataNameResolver nameResolver;
@@ -42,7 +43,7 @@ public KotlinMetadata(@NotNull Metadata metadata, @Nullable MetadataNameResolver
4243
}
4344

4445
List<ProtoBuf.Property> protoProperties;
45-
if (metadata instanceof SyntheticClass) { // Check first for a quick return
46+
if (metadata instanceof SyntheticClass || metadata instanceof FunctionReference) { // Check first for a quick return
4647
return null;
4748
} else if (metadata instanceof Class cls) {
4849
protoProperties = cls.proto().getPropertyList();
@@ -66,7 +67,9 @@ public KotlinMetadata(@NotNull Metadata metadata, @Nullable MetadataNameResolver
6667
}
6768

6869
List<ProtoBuf.Function> protoFunctions;
69-
if (metadata instanceof Class cls) {
70+
if (metadata instanceof FunctionReference) {
71+
return null;
72+
} else if (metadata instanceof Class cls) {
7073
protoFunctions = cls.proto().getFunctionList();
7174
} else if (metadata instanceof File file) {
7275
protoFunctions = file.proto().getFunctionList();

0 commit comments

Comments
 (0)