Skip to content
Draft
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
Binary file modified plugins/kotlin/libs/metadata.jar
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
package org.vineflower.kotlin;

import kotlin.metadata.internal.metadata.ProtoBuf;
import kotlin.metadata.internal.metadata.deserialization.Flags;
import net.fabricmc.fernflower.api.IFabricJavadocProvider;
import org.jetbrains.java.decompiler.api.plugin.StatementWriter;
import org.jetbrains.java.decompiler.code.CodeConstants;
Expand Down Expand Up @@ -32,19 +33,21 @@
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.util.*;
import org.jetbrains.java.decompiler.util.InterpreterUtil;
import org.jetbrains.java.decompiler.util.Key;
import org.jetbrains.java.decompiler.util.TextBuffer;
import org.jetbrains.java.decompiler.util.TextUtil;
import org.jetbrains.java.decompiler.util.collections.VBStyleCollection;
import org.vineflower.kotlin.expr.KAnnotationExprent;
import org.vineflower.kotlin.metadata.MetadataNameResolver;
import org.vineflower.kotlin.struct.*;
import org.vineflower.kotlin.util.KTypes;
import org.vineflower.kotlin.util.KUtils;
import org.vineflower.kotlin.util.ProtobufFlags;

import java.io.IOException;
import java.util.*;

public class KotlinWriter implements StatementWriter {
public class KotlinWriter implements StatementWriter, Flags {
private static final Set<String> ERROR_DUMP_STOP_POINTS = new HashSet<>(Arrays.asList(
"Fernflower.decompileContext",
"MethodProcessor.codeToJava",
Expand Down Expand Up @@ -223,14 +226,14 @@ public void writeClass(ClassNode node, TextBuffer buffer, int indent) {

ProtoBuf.Class proto = KotlinDecompilationContext.getCurrentClass();

ProtobufFlags.Class kotlinFlags;
int kotlinFlags;
if (proto != null) {
kotlinFlags = new ProtobufFlags.Class(proto.getFlags());
kotlinFlags = proto.getFlags();
} else {
if (KotlinDecompilationContext.getCurrentType() == null) {
appendComment(buffer, "Class flags could not be determined", indent);
}
kotlinFlags = new ProtobufFlags.Class(0);
kotlinFlags = 0;
}

if (DecompilerContext.getOption(IFernflowerPreferences.SOURCE_FILE_COMMENTS)) {
Expand All @@ -247,7 +250,7 @@ public void writeClass(ClassNode node, TextBuffer buffer, int indent) {
}
}

if (kotlinFlags.kind == ProtoBuf.Class.Kind.ANNOTATION_CLASS || cl.hasModifier(CodeConstants.ACC_ANNOTATION)) {
if (CLASS_KIND.get(kotlinFlags) == ProtoBuf.Class.Kind.ANNOTATION_CLASS || cl.hasModifier(CodeConstants.ACC_ANNOTATION)) {
// Kotlin's annotation classes are treated quite differently from other classes
writeAnnotationDefinition(node, buffer, indent, propertyData, functions, constructorData);
return;
Expand Down Expand Up @@ -703,7 +706,7 @@ private void writeAnnotationDefinition(ClassNode node, TextBuffer buffer, int in
buffer.appendLineSeparator();
}

private void writeClassDefinition(ClassNode node, TextBuffer buffer, int indent, KConstructor.Data constructorData, ProtobufFlags.Class kotlinFlags) {
private void writeClassDefinition(ClassNode node, TextBuffer buffer, int indent, KConstructor.Data constructorData, int kotlinFlags) {
if (node.type == ClassNode.Type.ANONYMOUS) {
buffer.append(" {").appendLineSeparator();
return;
Expand Down Expand Up @@ -739,31 +742,34 @@ private void writeClassDefinition(ClassNode node, TextBuffer buffer, int indent,

buffer.appendIndent(indent);

if (kotlinFlags.visibility != ProtoBuf.Visibility.PUBLIC || DecompilerContext.getOption(KotlinOptions.SHOW_PUBLIC_VISIBILITY)) {
buffer.append(ProtobufFlags.toString(kotlinFlags.visibility)).append(' ');
if (VISIBILITY.get(kotlinFlags) != ProtoBuf.Visibility.PUBLIC || DecompilerContext.getOption(KotlinOptions.SHOW_PUBLIC_VISIBILITY)) {
KUtils.appendVisibility(buffer, VISIBILITY.get(kotlinFlags));
}

if (kotlinFlags.isExpect) {
if (IS_EXPECT_CLASS.get(kotlinFlags)) {
buffer.append("expect ");
}

if ((!isInterface && kotlinFlags.modality != ProtoBuf.Modality.FINAL) || kotlinFlags.modality == ProtoBuf.Modality.SEALED) {
buffer.append(ProtobufFlags.toString(kotlinFlags.modality)).append(' ');
}
buffer.append(switch (MODALITY.get(kotlinFlags)) {
case FINAL -> isInterface ? "final " : "";
case OPEN -> isInterface ? "" : "open ";
case ABSTRACT -> isInterface ? "" : "abstract ";
case SEALED -> "sealed ";
});

if (kotlinFlags.isExternal) {
if (IS_EXTERNAL_CLASS.get(kotlinFlags)) {
buffer.append("external ");
}
if (kotlinFlags.isInner) {
if (IS_INNER.get(kotlinFlags)) {
buffer.append("inner ");
}
if (kotlinFlags.isFun) {
if (IS_FUN_INTERFACE.get(kotlinFlags)) {
buffer.append("fun ");
}
if (kotlinFlags.isInline) {
buffer.append("inline ");
if (IS_VALUE_CLASS.get(kotlinFlags)) {
buffer.append("value ");
}
if (kotlinFlags.isData) {
if (IS_DATA.get(kotlinFlags)) {
buffer.append("data ");
}

Expand All @@ -773,15 +779,15 @@ private void writeClassDefinition(ClassNode node, TextBuffer buffer, int indent,
buffer.append("interface");
} else if (isAnnotation) {
buffer.append("annotation class");
} else if (kotlinFlags.kind == ProtoBuf.Class.Kind.OBJECT) {
} else if (CLASS_KIND.get(kotlinFlags) == ProtoBuf.Class.Kind.OBJECT) {
buffer.append("object");
} else if (kotlinFlags.kind == ProtoBuf.Class.Kind.COMPANION_OBJECT) {
} else if (CLASS_KIND.get(kotlinFlags) == ProtoBuf.Class.Kind.COMPANION_OBJECT) {
buffer.append("companion object");
} else {
buffer.append("class");
}

if (kotlinFlags.kind != ProtoBuf.Class.Kind.COMPANION_OBJECT || !node.simpleName.equals("Companion")) {
if (CLASS_KIND.get(kotlinFlags) != ProtoBuf.Class.Kind.COMPANION_OBJECT || !node.simpleName.equals("Companion")) {
buffer.append(" ").append(toValidKotlinIdentifier(node.simpleName));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.vineflower.kotlin.struct;

import kotlin.metadata.internal.metadata.deserialization.Flags;
import org.jetbrains.java.decompiler.code.CodeConstants;
import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.main.rels.MethodWrapper;
Expand Down Expand Up @@ -120,7 +121,7 @@ public static DefaultArgsMap from(MethodWrapper defaults, MethodWrapper calling,

if (KUtils.assertionsEnabled()) {
for (KParameter param : params) {
assert map.containsKey(param) == param.flags().declaresDefault : "Parameter " + param.name() + " has default value but no default value was found";
assert map.containsKey(param) == Flags.DECLARES_DEFAULT_VALUE.get(param.flags()) : "Parameter " + param.name() + " has default value but no default value was found";
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.vineflower.kotlin.struct;

import kotlin.metadata.internal.metadata.ProtoBuf;
import kotlin.metadata.internal.metadata.deserialization.Flags;
import kotlin.metadata.internal.metadata.jvm.JvmProtoBuf;
import org.jetbrains.java.decompiler.main.ClassesProcessor;
import org.jetbrains.java.decompiler.main.DecompilerContext;
Expand All @@ -22,21 +23,20 @@
import org.vineflower.kotlin.KotlinWriter;
import org.vineflower.kotlin.metadata.MetadataNameResolver;
import org.vineflower.kotlin.util.KUtils;
import org.vineflower.kotlin.util.ProtobufFlags;

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

public record KConstructor(
KParameter[] parameters,
ProtobufFlags.Constructor flags,
int flags,
MethodWrapper method,
boolean isPrimary,
DefaultArgsMap defaultArgs,
ClassesProcessor.ClassNode node,
ProtobufFlags.Class classFlags
) {
int classFlags
) implements Flags {
private static final VarType DEFAULT_CONSTRUCTOR_MARKER = new VarType("kotlin/jvm/internal/DefaultConstructorMarker", true);

public static Data parse(ClassesProcessor.ClassNode node) {
Expand All @@ -47,8 +47,8 @@ public static Data parse(ClassesProcessor.ClassNode node) {
KotlinDecompilationContext.KotlinType type = KotlinDecompilationContext.getCurrentType();
if (type != KotlinDecompilationContext.KotlinType.CLASS) return null;

ProtobufFlags.Class classFlags = new ProtobufFlags.Class(KotlinDecompilationContext.getCurrentClass().getFlags());
if (classFlags.modality == ProtoBuf.Modality.ABSTRACT) return null;
int classFlags = KotlinDecompilationContext.getCurrentClass().getFlags();
if (MODALITY.get(classFlags) == ProtoBuf.Modality.ABSTRACT) return null;

List<ProtoBuf.Constructor> protoConstructors = KotlinDecompilationContext.getCurrentClass().getConstructorList();
if (protoConstructors.isEmpty()) return null;
Expand All @@ -61,21 +61,21 @@ public static Data parse(ClassesProcessor.ClassNode node) {
for (int i = 0; i < parameters.length; i++) {
ProtoBuf.ValueParameter protoParameter = constructor.getValueParameter(i);
parameters[i] = new KParameter(
new ProtobufFlags.ValueParameter(protoParameter.getFlags()),
protoParameter.getFlags(),
resolver.resolve(protoParameter.getName()),
KType.from(protoParameter.getType(), resolver),
KType.from(protoParameter.getVarargElementType(), resolver),
protoParameter.getTypeId()
);
}

ProtobufFlags.Constructor flags = new ProtobufFlags.Constructor(constructor.getFlags());
int flags = constructor.getFlags();

JvmProtoBuf.JvmMethodSignature signature = constructor.getExtension(JvmProtoBuf.constructorSignature);
String desc = resolver.resolve(signature.getDesc());
MethodWrapper method = wrapper.getMethodWrapper("<init>", desc);
if (method == null) {
if (classFlags.kind == ProtoBuf.Class.Kind.ANNOTATION_CLASS) {
if (CLASS_KIND.get(classFlags) == ProtoBuf.Class.Kind.ANNOTATION_CLASS) {
// Annotation classes are very odd and don't actually have a constructor under the hood
KConstructor kConstructor = new KConstructor(parameters, flags, null, false, null, node, classFlags);
return new Data(null, kConstructor);
Expand All @@ -85,10 +85,10 @@ public static Data parse(ClassesProcessor.ClassNode node) {
continue;
}

boolean isPrimary = !flags.isSecondary;
boolean isPrimary = !IS_SECONDARY.get(flags);

StringBuilder defaultArgsDesc = new StringBuilder("(");
if (classFlags.kind == ProtoBuf.Class.Kind.ENUM_CLASS) {
if (CLASS_KIND.get(classFlags) == ProtoBuf.Class.Kind.ENUM_CLASS) {
// Kotlin drops hidden name/ordinal parameters for enum constructors in its metadata
defaultArgsDesc.append("Ljava/lang/String;").append("I");
}
Expand Down Expand Up @@ -124,15 +124,15 @@ public boolean stringify(TextBuffer buffer, int indent) {
String methodKey = InterpreterUtil.makeUniqueKey(method.methodStruct.getName(), method.methodStruct.getDescriptor());

if (!isPrimary) {
if (flags.hasAnnotations) {
if (HAS_ANNOTATIONS.get(flags)) {
KotlinWriter.appendAnnotations(buf, indent, method.methodStruct, TypeAnnotation.METHOD_RETURN_TYPE);
KotlinWriter.appendJvmAnnotations(buf, indent, method.methodStruct, false, method.classStruct.getPool(), TypeAnnotation.METHOD_RETURN_TYPE);
}

buf.appendIndent(indent);

if (flags.visibility != ProtoBuf.Visibility.PUBLIC || DecompilerContext.getOption(KotlinOptions.SHOW_PUBLIC_VISIBILITY)) {
KUtils.appendVisibility(buf, flags.visibility);
if (VISIBILITY.get(flags) != ProtoBuf.Visibility.PUBLIC || DecompilerContext.getOption(KotlinOptions.SHOW_PUBLIC_VISIBILITY)) {
KUtils.appendVisibility(buf, VISIBILITY.get(flags));
}

buf.append("constructor");
Expand All @@ -149,7 +149,7 @@ public boolean stringify(TextBuffer buffer, int indent) {

parameter.stringify(indent + 1, buf);

if (parameter.flags().declaresDefault) {
if (DECLARES_DEFAULT_VALUE.get(parameter.flags())) {
buf.append(defaultArgs.toJava(parameter, indent + 1), node.classStruct.qualifiedName, methodKey);
}
}
Expand Down Expand Up @@ -213,8 +213,8 @@ public boolean writePrimaryConstructor(TextBuffer buffer, int indent) {

String methodKey = InterpreterUtil.makeUniqueKey(method.methodStruct.getName(), method.methodStruct.getDescriptor());

if (classFlags.kind != ProtoBuf.Class.Kind.OBJECT && classFlags.kind != ProtoBuf.Class.Kind.COMPANION_OBJECT) {
if (flags.hasAnnotations) {
if (CLASS_KIND.get(classFlags) != ProtoBuf.Class.Kind.OBJECT && CLASS_KIND.get(classFlags) != ProtoBuf.Class.Kind.COMPANION_OBJECT) {
if (HAS_ANNOTATIONS.get(flags)) {
buf.append(" ");
// -1 for indent indicates inline
KotlinWriter.appendAnnotations(buf, -1, method.methodStruct, TypeAnnotation.METHOD_RETURN_TYPE);
Expand All @@ -223,11 +223,11 @@ public boolean writePrimaryConstructor(TextBuffer buffer, int indent) {
}

// For cleanliness, public primary constructors are not forced public by the config option
if ((flags.visibility != ProtoBuf.Visibility.PUBLIC || (appended && DecompilerContext.getOption(KotlinOptions.SHOW_PUBLIC_VISIBILITY))) &&
classFlags.kind != ProtoBuf.Class.Kind.ENUM_CLASS // Enum constructors are always private implicitly
if ((VISIBILITY.get(flags) != ProtoBuf.Visibility.PUBLIC || (appended && DecompilerContext.getOption(KotlinOptions.SHOW_PUBLIC_VISIBILITY))) &&
CLASS_KIND.get(classFlags) != ProtoBuf.Class.Kind.ENUM_CLASS // Enum constructors are always private implicitly
) {
buf.append(" ");
KUtils.appendVisibility(buf, flags.visibility);
KUtils.appendVisibility(buf, VISIBILITY.get(flags));
appended = true;
}

Expand All @@ -248,7 +248,7 @@ public boolean writePrimaryConstructor(TextBuffer buffer, int indent) {

parameter.stringify(indent + 1, buf);

if (parameter.flags().declaresDefault) {
if (DECLARES_DEFAULT_VALUE.get(parameter.flags())) {
buf.append(defaultArgs.toJava(parameter, indent + 1), node.classStruct.qualifiedName, methodKey);
}
}
Expand All @@ -271,7 +271,7 @@ public boolean writePrimaryConstructor(TextBuffer buffer, int indent) {
// throw new IllegalStateException("First expression of constructor is not InvocationExprent");
}

if (invocation.getClassname().equals("java/lang/Object") || classFlags.kind == ProtoBuf.Class.Kind.ENUM_CLASS) {
if (invocation.getClassname().equals("java/lang/Object") || CLASS_KIND.get(classFlags) == ProtoBuf.Class.Kind.ENUM_CLASS) {
// No need to declare super constructor call
buffer.append(buf);
return false;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
package org.vineflower.kotlin.struct;

import kotlin.metadata.internal.metadata.ProtoBuf;
import kotlin.metadata.internal.metadata.deserialization.Flags;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.util.TextBuffer;
import org.vineflower.kotlin.KotlinWriter;
import org.vineflower.kotlin.metadata.MetadataNameResolver;
import org.vineflower.kotlin.util.ProtobufFlags;

import java.util.List;
import java.util.stream.Collectors;

public class KContract {
public class KContract implements Flags {
private static final String INVOCATION_KIND = "kotlin.contracts.InvocationKind";
@NotNull
public final List<KEffect> effects;
Expand Down Expand Up @@ -110,18 +110,18 @@ public void stringify(TextBuffer buf, int indent) {
}

public record KExpression(
@NotNull ProtobufFlags.Expression flags,
int flags,
@Nullable KParameter valueParameterReference,
@Nullable ProtoBuf.Expression.ConstantValue constantValue,
@Nullable KType instanceofType,
@NotNull List<KExpression> andArguments,
@NotNull List<KExpression> orArguments
) {
// Placeholder type for receiver type
private static final KParameter THIS_TYPE = new KParameter(new ProtobufFlags.ValueParameter(0), "this", KType.NOTHING, null, 0);
private static final KParameter THIS_TYPE = new KParameter(0, "this", KType.NOTHING, null, 0);

static KExpression from(ProtoBuf.Expression proto, List<KParameter> params, MetadataNameResolver nameResolver) {
ProtobufFlags.Expression flags = new ProtobufFlags.Expression(proto.getFlags());
int flags = proto.getFlags();
KParameter valueParameterReference = null;
if (proto.hasValueParameterReference()) {
int index = proto.getValueParameterReference();
Expand Down Expand Up @@ -158,28 +158,28 @@ public void stringify(TextBuffer buf, int indent, boolean partOfOr) {
if (instanceofType != null) {
buf.append(paramName)
.append(' ')
.append(flags.isNegated ? "!is" : "is")
.append(IS_NEGATED.get(flags) ? "!is" : "is")
.append(' ')
.append(instanceofType.stringify(indent));
} else if (flags.isNullPredicate) {
} else if (IS_NULL_CHECK_PREDICATE.get(flags)) {
buf.append(paramName)
.append(' ')
.append(flags.isNegated ? "!=" : "==")
.append(IS_NEGATED.get(flags) ? "!=" : "==")
.append(' ')
.append("null");
} else if (constantValue != null) {
if (valueParameterReference != null && valueParameterReference.type().isNullable) {
buf.append(paramName)
.append(' ')
.append(flags.isNegated ? "!=" : "==")
.append(IS_NEGATED.get(flags) ? "!=" : "==")
.append(' ')
.append(constantValue.name().toLowerCase());
} else {
String output = valueParameterReference != null && "kotlin/Boolean".equals(valueParameterReference.type().kotlinType)
? paramName
: constantValue.name().toLowerCase();

if (flags.isNegated) {
if (IS_NEGATED.get(flags)) {
buf.append('!');
}

Expand All @@ -189,7 +189,7 @@ public void stringify(TextBuffer buf, int indent, boolean partOfOr) {
if (!valueParameterReference.type().kotlinType.equals("kotlin/Boolean")) {
//TODO figure out why this happens
}
if (flags.isNegated) {
if (IS_NEGATED.get(flags)) {
buf.append('!');
}
buf.append(paramName);
Expand Down
Loading