Restructure KotlinClass(Module)Metadata.write/read:

- Write now returns result (Metadata or ByteArray) directly without re-wrapping it;
- annotationData/bytes is private to avoid unsoundness if external client corrupts it;
- .toXxx() do not throw exceptions anymore and deprecated. Exceptions are thrown only from read/write;
- Instead of toXxx(), properties like .kmClass should be used instead. They're initialized eagerly and return a view without copying.
- KotlinClassMetadata.Unknown is now a data object, as no useful information is exposed

#KT-59365 Fixed
This commit is contained in:
Leonid Startsev
2023-06-13 11:39:17 +02:00
committed by Space Team
parent 5bb4f469ef
commit b919a12274
12 changed files with 256 additions and 197 deletions
+6 -25
View File
@@ -62,10 +62,10 @@ when (metadata) {
``` ```
Let's assume we've obtained an instance of `KotlinClassMetadata.Class`; other kinds of classes are handled similarly, except some of them have metadata in a slightly different form. Let's assume we've obtained an instance of `KotlinClassMetadata.Class`; other kinds of classes are handled similarly, except some of them have metadata in a slightly different form.
The main way to make sense of the underlying metadata is to invoke `toKmClass()`, which returns an instance of `KmClass` (`Km` is a shorthand for “Kotlin metadata”): The main way to make sense of the underlying metadata is to access the `kmClass` property, which returns an instance of `KmClass` (`Km` is a shorthand for “Kotlin metadata”):
```kotlin ```kotlin
val klass = metadata.toKmClass() val klass = metadata.kmClass
println(klass.functions.map { it.name }) println(klass.functions.map { it.name })
println(klass.properties.map { it.name }) println(klass.properties.map { it.name })
``` ```
@@ -92,7 +92,7 @@ if (Flag.Function.IS_SUSPEND(function.flags)) {
## Writing metadata ## Writing metadata
To create metadata of a Kotlin class file from scratch, construct an instance of `KmClass`/`KmPackage`/`KmLambda`, fill it with the data and call corresponding `KotlinClassMetadata.write` function. To create metadata of a Kotlin class file from scratch, construct an instance of `KmClass`/`KmPackage`/`KmLambda`, fill it with the data and call corresponding `KotlinClassMetadata.write` function.
Resulting `KotlinClassMetadata.annotationData` can be used to write `kotlin.Metadata` annotation on a class file. Resulting `kotlin.Metadata` annotation can be written to a class file.
When using metadata writers from Kotlin source code, it's very convenient to use Kotlin scoping functions such as `apply` to reduce boilerplate: When using metadata writers from Kotlin source code, it's very convenient to use Kotlin scoping functions such as `apply` to reduce boilerplate:
@@ -113,7 +113,7 @@ val klass = KmClass().apply {
... ...
} }
val annotation = KotlinClassMetadata.writeClass(klass).annotationData val annotation = KotlinClassMetadata.writeClass(klass)
// Write annotation directly or use annotation.kind, annotation.data1, annotation.data2, etc. // Write annotation directly or use annotation.kind, annotation.data1, annotation.data2, etc.
``` ```
@@ -131,29 +131,10 @@ The only difference is that the source for the reader (and the result of the wri
// Read the module metadata // Read the module metadata
val bytes = File("META-INF/main.kotlin_module").readBytes() val bytes = File("META-INF/main.kotlin_module").readBytes()
val metadata = KotlinModuleMetadata.read(bytes) val metadata = KotlinModuleMetadata.read(bytes)
val module = metadata.toKmModule() val module = metadata.kmModule
... ...
// Write the module metadata // Write the module metadata
val bytes = KotlinModuleMetadata.write(module).bytes val bytes = KotlinModuleMetadata.write(module)
File("META-INF/main.kotlin_module").writeBytes(bytes) File("META-INF/main.kotlin_module").writeBytes(bytes)
``` ```
## Laziness
Note that until you load the actual underlying data of a `KotlinClassMetadata` or `KotlinModuleMetadata` instance by invoking one of the `toKm...` methods,
the data is not completely parsed and verified. If you need to check if the data is not horribly corrupted before proceeding, ensure that either of those is called:
```kotlin
val metadata: KotlinClassMetadata.Class = ...
try {
// Guarantees eager parsing of the underlying data
metadata.toKmClass()
} catch (e: Exception) {
System.err.println("Metadata is corrupted!")
}
```
@@ -1254,30 +1254,30 @@ public abstract class kotlinx/metadata/jvm/KotlinClassMetadata {
public static final field MULTI_FILE_CLASS_PART_KIND I public static final field MULTI_FILE_CLASS_PART_KIND I
public static final field SYNTHETIC_CLASS_KIND I public static final field SYNTHETIC_CLASS_KIND I
public synthetic fun <init> (Lkotlin/Metadata;Lkotlin/jvm/internal/DefaultConstructorMarker;)V public synthetic fun <init> (Lkotlin/Metadata;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getAnnotationData ()Lkotlin/Metadata;
public static final fun read (Lkotlin/Metadata;)Lkotlinx/metadata/jvm/KotlinClassMetadata; public static final fun read (Lkotlin/Metadata;)Lkotlinx/metadata/jvm/KotlinClassMetadata;
public static final fun writeClass (Lkotlinx/metadata/KmClass;)Lkotlinx/metadata/jvm/KotlinClassMetadata$Class; public static final fun writeClass (Lkotlinx/metadata/KmClass;)Lkotlin/Metadata;
public static final fun writeClass (Lkotlinx/metadata/KmClass;[I)Lkotlinx/metadata/jvm/KotlinClassMetadata$Class; public static final fun writeClass (Lkotlinx/metadata/KmClass;[I)Lkotlin/Metadata;
public static final fun writeClass (Lkotlinx/metadata/KmClass;[II)Lkotlinx/metadata/jvm/KotlinClassMetadata$Class; public static final fun writeClass (Lkotlinx/metadata/KmClass;[II)Lkotlin/Metadata;
public static final fun writeFileFacade (Lkotlinx/metadata/KmPackage;)Lkotlinx/metadata/jvm/KotlinClassMetadata$FileFacade; public static final fun writeFileFacade (Lkotlinx/metadata/KmPackage;)Lkotlin/Metadata;
public static final fun writeFileFacade (Lkotlinx/metadata/KmPackage;[I)Lkotlinx/metadata/jvm/KotlinClassMetadata$FileFacade; public static final fun writeFileFacade (Lkotlinx/metadata/KmPackage;[I)Lkotlin/Metadata;
public static final fun writeFileFacade (Lkotlinx/metadata/KmPackage;[II)Lkotlinx/metadata/jvm/KotlinClassMetadata$FileFacade; public static final fun writeFileFacade (Lkotlinx/metadata/KmPackage;[II)Lkotlin/Metadata;
public static final fun writeLambda (Lkotlinx/metadata/KmLambda;)Lkotlinx/metadata/jvm/KotlinClassMetadata$SyntheticClass; public static final fun writeLambda (Lkotlinx/metadata/KmLambda;)Lkotlin/Metadata;
public static final fun writeLambda (Lkotlinx/metadata/KmLambda;[I)Lkotlinx/metadata/jvm/KotlinClassMetadata$SyntheticClass; public static final fun writeLambda (Lkotlinx/metadata/KmLambda;[I)Lkotlin/Metadata;
public static final fun writeLambda (Lkotlinx/metadata/KmLambda;[II)Lkotlinx/metadata/jvm/KotlinClassMetadata$SyntheticClass; public static final fun writeLambda (Lkotlinx/metadata/KmLambda;[II)Lkotlin/Metadata;
public static final fun writeMultiFileClassFacade (Ljava/util/List;)Lkotlinx/metadata/jvm/KotlinClassMetadata$MultiFileClassFacade; public static final fun writeMultiFileClassFacade (Ljava/util/List;)Lkotlin/Metadata;
public static final fun writeMultiFileClassFacade (Ljava/util/List;[I)Lkotlinx/metadata/jvm/KotlinClassMetadata$MultiFileClassFacade; public static final fun writeMultiFileClassFacade (Ljava/util/List;[I)Lkotlin/Metadata;
public static final fun writeMultiFileClassFacade (Ljava/util/List;[II)Lkotlinx/metadata/jvm/KotlinClassMetadata$MultiFileClassFacade; public static final fun writeMultiFileClassFacade (Ljava/util/List;[II)Lkotlin/Metadata;
public static final fun writeMultiFileClassPart (Lkotlinx/metadata/KmPackage;Ljava/lang/String;)Lkotlinx/metadata/jvm/KotlinClassMetadata$MultiFileClassPart; public static final fun writeMultiFileClassPart (Lkotlinx/metadata/KmPackage;Ljava/lang/String;)Lkotlin/Metadata;
public static final fun writeMultiFileClassPart (Lkotlinx/metadata/KmPackage;Ljava/lang/String;[I)Lkotlinx/metadata/jvm/KotlinClassMetadata$MultiFileClassPart; public static final fun writeMultiFileClassPart (Lkotlinx/metadata/KmPackage;Ljava/lang/String;[I)Lkotlin/Metadata;
public static final fun writeMultiFileClassPart (Lkotlinx/metadata/KmPackage;Ljava/lang/String;[II)Lkotlinx/metadata/jvm/KotlinClassMetadata$MultiFileClassPart; public static final fun writeMultiFileClassPart (Lkotlinx/metadata/KmPackage;Ljava/lang/String;[II)Lkotlin/Metadata;
public static final fun writeSyntheticClass ()Lkotlinx/metadata/jvm/KotlinClassMetadata$SyntheticClass; public static final fun writeSyntheticClass ()Lkotlin/Metadata;
public static final fun writeSyntheticClass ([I)Lkotlinx/metadata/jvm/KotlinClassMetadata$SyntheticClass; public static final fun writeSyntheticClass ([I)Lkotlin/Metadata;
public static final fun writeSyntheticClass ([II)Lkotlinx/metadata/jvm/KotlinClassMetadata$SyntheticClass; public static final fun writeSyntheticClass ([II)Lkotlin/Metadata;
} }
public final class kotlinx/metadata/jvm/KotlinClassMetadata$Class : kotlinx/metadata/jvm/KotlinClassMetadata { public final class kotlinx/metadata/jvm/KotlinClassMetadata$Class : kotlinx/metadata/jvm/KotlinClassMetadata {
public final fun accept (Lkotlinx/metadata/KmClassVisitor;)V public final fun accept (Lkotlinx/metadata/KmClassVisitor;)V
public final fun getKmClass ()Lkotlinx/metadata/KmClass;
public final fun toKmClass ()Lkotlinx/metadata/KmClass; public final fun toKmClass ()Lkotlinx/metadata/KmClass;
} }
@@ -1291,34 +1291,35 @@ public final class kotlinx/metadata/jvm/KotlinClassMetadata$Class$Writer : kotli
public final class kotlinx/metadata/jvm/KotlinClassMetadata$Companion { public final class kotlinx/metadata/jvm/KotlinClassMetadata$Companion {
public final fun read (Lkotlin/Metadata;)Lkotlinx/metadata/jvm/KotlinClassMetadata; public final fun read (Lkotlin/Metadata;)Lkotlinx/metadata/jvm/KotlinClassMetadata;
public final fun writeClass (Lkotlinx/metadata/KmClass;)Lkotlinx/metadata/jvm/KotlinClassMetadata$Class; public final fun writeClass (Lkotlinx/metadata/KmClass;)Lkotlin/Metadata;
public final fun writeClass (Lkotlinx/metadata/KmClass;[I)Lkotlinx/metadata/jvm/KotlinClassMetadata$Class; public final fun writeClass (Lkotlinx/metadata/KmClass;[I)Lkotlin/Metadata;
public final fun writeClass (Lkotlinx/metadata/KmClass;[II)Lkotlinx/metadata/jvm/KotlinClassMetadata$Class; public final fun writeClass (Lkotlinx/metadata/KmClass;[II)Lkotlin/Metadata;
public static synthetic fun writeClass$default (Lkotlinx/metadata/jvm/KotlinClassMetadata$Companion;Lkotlinx/metadata/KmClass;[IIILjava/lang/Object;)Lkotlinx/metadata/jvm/KotlinClassMetadata$Class; public static synthetic fun writeClass$default (Lkotlinx/metadata/jvm/KotlinClassMetadata$Companion;Lkotlinx/metadata/KmClass;[IIILjava/lang/Object;)Lkotlin/Metadata;
public final fun writeFileFacade (Lkotlinx/metadata/KmPackage;)Lkotlinx/metadata/jvm/KotlinClassMetadata$FileFacade; public final fun writeFileFacade (Lkotlinx/metadata/KmPackage;)Lkotlin/Metadata;
public final fun writeFileFacade (Lkotlinx/metadata/KmPackage;[I)Lkotlinx/metadata/jvm/KotlinClassMetadata$FileFacade; public final fun writeFileFacade (Lkotlinx/metadata/KmPackage;[I)Lkotlin/Metadata;
public final fun writeFileFacade (Lkotlinx/metadata/KmPackage;[II)Lkotlinx/metadata/jvm/KotlinClassMetadata$FileFacade; public final fun writeFileFacade (Lkotlinx/metadata/KmPackage;[II)Lkotlin/Metadata;
public static synthetic fun writeFileFacade$default (Lkotlinx/metadata/jvm/KotlinClassMetadata$Companion;Lkotlinx/metadata/KmPackage;[IIILjava/lang/Object;)Lkotlinx/metadata/jvm/KotlinClassMetadata$FileFacade; public static synthetic fun writeFileFacade$default (Lkotlinx/metadata/jvm/KotlinClassMetadata$Companion;Lkotlinx/metadata/KmPackage;[IIILjava/lang/Object;)Lkotlin/Metadata;
public final fun writeLambda (Lkotlinx/metadata/KmLambda;)Lkotlinx/metadata/jvm/KotlinClassMetadata$SyntheticClass; public final fun writeLambda (Lkotlinx/metadata/KmLambda;)Lkotlin/Metadata;
public final fun writeLambda (Lkotlinx/metadata/KmLambda;[I)Lkotlinx/metadata/jvm/KotlinClassMetadata$SyntheticClass; public final fun writeLambda (Lkotlinx/metadata/KmLambda;[I)Lkotlin/Metadata;
public final fun writeLambda (Lkotlinx/metadata/KmLambda;[II)Lkotlinx/metadata/jvm/KotlinClassMetadata$SyntheticClass; public final fun writeLambda (Lkotlinx/metadata/KmLambda;[II)Lkotlin/Metadata;
public static synthetic fun writeLambda$default (Lkotlinx/metadata/jvm/KotlinClassMetadata$Companion;Lkotlinx/metadata/KmLambda;[IIILjava/lang/Object;)Lkotlinx/metadata/jvm/KotlinClassMetadata$SyntheticClass; public static synthetic fun writeLambda$default (Lkotlinx/metadata/jvm/KotlinClassMetadata$Companion;Lkotlinx/metadata/KmLambda;[IIILjava/lang/Object;)Lkotlin/Metadata;
public final fun writeMultiFileClassFacade (Ljava/util/List;)Lkotlinx/metadata/jvm/KotlinClassMetadata$MultiFileClassFacade; public final fun writeMultiFileClassFacade (Ljava/util/List;)Lkotlin/Metadata;
public final fun writeMultiFileClassFacade (Ljava/util/List;[I)Lkotlinx/metadata/jvm/KotlinClassMetadata$MultiFileClassFacade; public final fun writeMultiFileClassFacade (Ljava/util/List;[I)Lkotlin/Metadata;
public final fun writeMultiFileClassFacade (Ljava/util/List;[II)Lkotlinx/metadata/jvm/KotlinClassMetadata$MultiFileClassFacade; public final fun writeMultiFileClassFacade (Ljava/util/List;[II)Lkotlin/Metadata;
public static synthetic fun writeMultiFileClassFacade$default (Lkotlinx/metadata/jvm/KotlinClassMetadata$Companion;Ljava/util/List;[IIILjava/lang/Object;)Lkotlinx/metadata/jvm/KotlinClassMetadata$MultiFileClassFacade; public static synthetic fun writeMultiFileClassFacade$default (Lkotlinx/metadata/jvm/KotlinClassMetadata$Companion;Ljava/util/List;[IIILjava/lang/Object;)Lkotlin/Metadata;
public final fun writeMultiFileClassPart (Lkotlinx/metadata/KmPackage;Ljava/lang/String;)Lkotlinx/metadata/jvm/KotlinClassMetadata$MultiFileClassPart; public final fun writeMultiFileClassPart (Lkotlinx/metadata/KmPackage;Ljava/lang/String;)Lkotlin/Metadata;
public final fun writeMultiFileClassPart (Lkotlinx/metadata/KmPackage;Ljava/lang/String;[I)Lkotlinx/metadata/jvm/KotlinClassMetadata$MultiFileClassPart; public final fun writeMultiFileClassPart (Lkotlinx/metadata/KmPackage;Ljava/lang/String;[I)Lkotlin/Metadata;
public final fun writeMultiFileClassPart (Lkotlinx/metadata/KmPackage;Ljava/lang/String;[II)Lkotlinx/metadata/jvm/KotlinClassMetadata$MultiFileClassPart; public final fun writeMultiFileClassPart (Lkotlinx/metadata/KmPackage;Ljava/lang/String;[II)Lkotlin/Metadata;
public static synthetic fun writeMultiFileClassPart$default (Lkotlinx/metadata/jvm/KotlinClassMetadata$Companion;Lkotlinx/metadata/KmPackage;Ljava/lang/String;[IIILjava/lang/Object;)Lkotlinx/metadata/jvm/KotlinClassMetadata$MultiFileClassPart; public static synthetic fun writeMultiFileClassPart$default (Lkotlinx/metadata/jvm/KotlinClassMetadata$Companion;Lkotlinx/metadata/KmPackage;Ljava/lang/String;[IIILjava/lang/Object;)Lkotlin/Metadata;
public final fun writeSyntheticClass ()Lkotlinx/metadata/jvm/KotlinClassMetadata$SyntheticClass; public final fun writeSyntheticClass ()Lkotlin/Metadata;
public final fun writeSyntheticClass ([I)Lkotlinx/metadata/jvm/KotlinClassMetadata$SyntheticClass; public final fun writeSyntheticClass ([I)Lkotlin/Metadata;
public final fun writeSyntheticClass ([II)Lkotlinx/metadata/jvm/KotlinClassMetadata$SyntheticClass; public final fun writeSyntheticClass ([II)Lkotlin/Metadata;
public static synthetic fun writeSyntheticClass$default (Lkotlinx/metadata/jvm/KotlinClassMetadata$Companion;[IIILjava/lang/Object;)Lkotlinx/metadata/jvm/KotlinClassMetadata$SyntheticClass; public static synthetic fun writeSyntheticClass$default (Lkotlinx/metadata/jvm/KotlinClassMetadata$Companion;[IIILjava/lang/Object;)Lkotlin/Metadata;
} }
public final class kotlinx/metadata/jvm/KotlinClassMetadata$FileFacade : kotlinx/metadata/jvm/KotlinClassMetadata { public final class kotlinx/metadata/jvm/KotlinClassMetadata$FileFacade : kotlinx/metadata/jvm/KotlinClassMetadata {
public final fun accept (Lkotlinx/metadata/KmPackageVisitor;)V public final fun accept (Lkotlinx/metadata/KmPackageVisitor;)V
public final fun getKmPackage ()Lkotlinx/metadata/KmPackage;
public final fun toKmPackage ()Lkotlinx/metadata/KmPackage; public final fun toKmPackage ()Lkotlinx/metadata/KmPackage;
} }
@@ -1345,6 +1346,7 @@ public final class kotlinx/metadata/jvm/KotlinClassMetadata$MultiFileClassFacade
public final class kotlinx/metadata/jvm/KotlinClassMetadata$MultiFileClassPart : kotlinx/metadata/jvm/KotlinClassMetadata { public final class kotlinx/metadata/jvm/KotlinClassMetadata$MultiFileClassPart : kotlinx/metadata/jvm/KotlinClassMetadata {
public final fun accept (Lkotlinx/metadata/KmPackageVisitor;)V public final fun accept (Lkotlinx/metadata/KmPackageVisitor;)V
public final fun getFacadeClassName ()Ljava/lang/String; public final fun getFacadeClassName ()Ljava/lang/String;
public final fun getKmPackage ()Lkotlinx/metadata/KmPackage;
public final fun toKmPackage ()Lkotlinx/metadata/KmPackage; public final fun toKmPackage ()Lkotlinx/metadata/KmPackage;
} }
@@ -1358,6 +1360,7 @@ public final class kotlinx/metadata/jvm/KotlinClassMetadata$MultiFileClassPart$W
public final class kotlinx/metadata/jvm/KotlinClassMetadata$SyntheticClass : kotlinx/metadata/jvm/KotlinClassMetadata { public final class kotlinx/metadata/jvm/KotlinClassMetadata$SyntheticClass : kotlinx/metadata/jvm/KotlinClassMetadata {
public final fun accept (Lkotlinx/metadata/KmLambdaVisitor;)V public final fun accept (Lkotlinx/metadata/KmLambdaVisitor;)V
public final fun getKmLambda ()Lkotlinx/metadata/KmLambda;
public final fun isLambda ()Z public final fun isLambda ()Z
public final fun toKmLambda ()Lkotlinx/metadata/KmLambda; public final fun toKmLambda ()Lkotlinx/metadata/KmLambda;
} }
@@ -1371,24 +1374,28 @@ public final class kotlinx/metadata/jvm/KotlinClassMetadata$SyntheticClass$Write
} }
public final class kotlinx/metadata/jvm/KotlinClassMetadata$Unknown : kotlinx/metadata/jvm/KotlinClassMetadata { public final class kotlinx/metadata/jvm/KotlinClassMetadata$Unknown : kotlinx/metadata/jvm/KotlinClassMetadata {
public static final field INSTANCE Lkotlinx/metadata/jvm/KotlinClassMetadata$Unknown;
public fun equals (Ljava/lang/Object;)Z
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
} }
public final class kotlinx/metadata/jvm/KotlinModuleMetadata { public final class kotlinx/metadata/jvm/KotlinModuleMetadata {
public static final field Companion Lkotlinx/metadata/jvm/KotlinModuleMetadata$Companion; public static final field Companion Lkotlinx/metadata/jvm/KotlinModuleMetadata$Companion;
public synthetic fun <init> ([BLkotlinx/metadata/internal/metadata/jvm/deserialization/ModuleMapping;Lkotlin/jvm/internal/DefaultConstructorMarker;)V public synthetic fun <init> ([BLkotlinx/metadata/internal/metadata/jvm/deserialization/ModuleMapping;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun accept (Lkotlinx/metadata/jvm/KmModuleVisitor;)V public final fun accept (Lkotlinx/metadata/jvm/KmModuleVisitor;)V
public final fun getBytes ()[B public final fun getKmModule ()Lkotlinx/metadata/jvm/KmModule;
public static final fun read ([B)Lkotlinx/metadata/jvm/KotlinModuleMetadata; public static final fun read ([B)Lkotlinx/metadata/jvm/KotlinModuleMetadata;
public final fun toKmModule ()Lkotlinx/metadata/jvm/KmModule; public final fun toKmModule ()Lkotlinx/metadata/jvm/KmModule;
public static final fun write (Lkotlinx/metadata/jvm/KmModule;)Lkotlinx/metadata/jvm/KotlinModuleMetadata; public static final fun write (Lkotlinx/metadata/jvm/KmModule;)[B
public static final fun write (Lkotlinx/metadata/jvm/KmModule;[I)Lkotlinx/metadata/jvm/KotlinModuleMetadata; public static final fun write (Lkotlinx/metadata/jvm/KmModule;[I)[B
} }
public final class kotlinx/metadata/jvm/KotlinModuleMetadata$Companion { public final class kotlinx/metadata/jvm/KotlinModuleMetadata$Companion {
public final fun read ([B)Lkotlinx/metadata/jvm/KotlinModuleMetadata; public final fun read ([B)Lkotlinx/metadata/jvm/KotlinModuleMetadata;
public final fun write (Lkotlinx/metadata/jvm/KmModule;)Lkotlinx/metadata/jvm/KotlinModuleMetadata; public final fun write (Lkotlinx/metadata/jvm/KmModule;)[B
public final fun write (Lkotlinx/metadata/jvm/KmModule;[I)Lkotlinx/metadata/jvm/KotlinModuleMetadata; public final fun write (Lkotlinx/metadata/jvm/KmModule;[I)[B
public static synthetic fun write$default (Lkotlinx/metadata/jvm/KotlinModuleMetadata$Companion;Lkotlinx/metadata/jvm/KmModule;[IILjava/lang/Object;)Lkotlinx/metadata/jvm/KotlinModuleMetadata; public static synthetic fun write$default (Lkotlinx/metadata/jvm/KotlinModuleMetadata$Companion;Lkotlinx/metadata/jvm/KmModule;[IILjava/lang/Object;)[B
} }
public final class kotlinx/metadata/jvm/KotlinModuleMetadata$Writer : kotlinx/metadata/jvm/KmModuleVisitor { public final class kotlinx/metadata/jvm/KotlinModuleMetadata$Writer : kotlinx/metadata/jvm/KmModuleVisitor {
@@ -1396,8 +1403,8 @@ public final class kotlinx/metadata/jvm/KotlinModuleMetadata$Writer : kotlinx/me
public fun visitAnnotation (Lkotlinx/metadata/KmAnnotation;)V public fun visitAnnotation (Lkotlinx/metadata/KmAnnotation;)V
public fun visitOptionalAnnotationClass ()Lkotlinx/metadata/KmClassVisitor; public fun visitOptionalAnnotationClass ()Lkotlinx/metadata/KmClassVisitor;
public fun visitPackageParts (Ljava/lang/String;Ljava/util/List;Ljava/util/Map;)V public fun visitPackageParts (Ljava/lang/String;Ljava/util/List;Ljava/util/Map;)V
public final fun write ([I)Lkotlinx/metadata/jvm/KotlinModuleMetadata; public final fun write ([I)[B
public static synthetic fun write$default (Lkotlinx/metadata/jvm/KotlinModuleMetadata$Writer;[IILjava/lang/Object;)Lkotlinx/metadata/jvm/KotlinModuleMetadata; public static synthetic fun write$default (Lkotlinx/metadata/jvm/KotlinModuleMetadata$Writer;[IILjava/lang/Object;)[B
} }
public abstract interface annotation class kotlinx/metadata/jvm/UnstableMetadataApi : java/lang/annotation/Annotation { public abstract interface annotation class kotlinx/metadata/jvm/UnstableMetadataApi : java/lang/annotation/Annotation {
@@ -27,14 +27,24 @@ import kotlin.LazyThreadSafetyMode.PUBLICATION
* trying to cover as much as possible. * trying to cover as much as possible.
* Normally, one would need at least a [Class] and a [FileFacade], as these are two most common kinds. * Normally, one would need at least a [Class] and a [FileFacade], as these are two most common kinds.
* *
* Most of the subclasses offer a conversion method to transform metadata into a Km data structure — for example, [KotlinClassMetadata.Class.toKmClass]. * Most of the subclasses declare a property to view metadata as a Km data structure — for example, [KotlinClassMetadata.Class.kmClass].
* Some of them also can contain additional properties, e.g. [KotlinClassMetadata.MultiFileClassPart.facadeClassName].
* Km data structures represent Kotlin declarations and offer a variety of properties to introspect and alter them. * Km data structures represent Kotlin declarations and offer a variety of properties to introspect and alter them.
* Note that parsing may be lazy, depending on the implementation; therefore, one may not see an [IllegalArgumentException] indicating malformed metadata * After desired changes are made, it is possible to get a new raw metadata instance with a corresponding `write` function, such as [KotlinClassMetadata.writeClass].
* until one calls `toKmClass` or a similar method.
* *
* @property annotationData Raw contents of the metadata represented by [Metadata] annotation in the class file. * Here is an example of reading a content name of some metadata:
* ```
* fun displayName(metadata: Metadata): String = when (val kcm = KotlinClassMetadata.read(metadata)) {
* is KotlinClassMetadata.Class -> "Class ${kcm.kmClass.name}"
* is KotlinClassMetadata.FileFacade -> "File facade with functions: ${kcm.kmPackage.functions.joinToString { it.name }}"
* is KotlinClassMetadata.SyntheticClass -> kcm.kmLambda?.function?.name?.let { "Lambda $it" } ?: "Synthetic class"
* is KotlinClassMetadata.MultiFileClassFacade -> "Multifile class facade with parts: ${kcm.partClassNames.joinToString()}"
* is KotlinClassMetadata.MultiFileClassPart -> "Multifile class part ${kcm.facadeClassName}"
* is KotlinClassMetadata.Unknown -> "Unknown metadata"
* }
* ```
*/ */
sealed class KotlinClassMetadata(val annotationData: Metadata) { sealed class KotlinClassMetadata(internal val annotationData: Metadata) {
/** /**
* Represents metadata of a class file containing a declaration of a Kotlin class. * Represents metadata of a class file containing a declaration of a Kotlin class.
@@ -43,18 +53,26 @@ sealed class KotlinClassMetadata(val annotationData: Metadata) {
* [Class] metadata, even if such declaration was in the same source file. See [FileFacade] for details. * [Class] metadata, even if such declaration was in the same source file. See [FileFacade] for details.
*/ */
class Class internal constructor(annotationData: Metadata) : KotlinClassMetadata(annotationData) { class Class internal constructor(annotationData: Metadata) : KotlinClassMetadata(annotationData) {
private val classData by lazy(PUBLICATION) {
JvmProtoBufUtil.readClassDataFrom(annotationData.requireNotEmpty(), annotationData.data2) /**
* Returns the [KmClass] representation of this metadata.
*
* Returns the same (mutable) [KmClass] instance every time.
*/
public val kmClass: KmClass = KmClass().apply {
val (strings, proto) = JvmProtoBufUtil.readClassDataFrom(annotationData.requireNotEmpty(), annotationData.data2)
proto.accept(this, strings)
} }
/** /**
* Returns a new [KmClass] instance created from this class metadata. * Returns a new [KmClass] instance created from this class metadata.
*
* @throws IllegalArgumentException if metadata is malformed or inconsistent and cannot be transformed into [KmClass].
*/ */
fun toKmClass(): KmClass = wrapIntoMetadataExceptionWhenNeeded { @Deprecated(
KmClass().apply(this::accept) "To avoid excessive copying, use .kmClass property instead. Note that it returns a view and not a copy.",
} ReplaceWith("kmClass"),
DeprecationLevel.WARNING
)
fun toKmClass(): KmClass = KmClass().also { newKm -> kmClass.accept(newKm) } // defensive copy
/** /**
* Makes the given visitor visit the metadata of this class. * Makes the given visitor visit the metadata of this class.
@@ -62,10 +80,7 @@ sealed class KotlinClassMetadata(val annotationData: Metadata) {
* @param v the visitor that must visit this class * @param v the visitor that must visit this class
*/ */
@Deprecated(VISITOR_API_MESSAGE) @Deprecated(VISITOR_API_MESSAGE)
fun accept(v: KmClassVisitor) { fun accept(v: KmClassVisitor) = kmClass.accept(v)
val (strings, proto) = classData
proto.accept(v, strings)
}
/** /**
* A [KmClassVisitor] that generates the metadata of a Kotlin class. * A [KmClassVisitor] that generates the metadata of a Kotlin class.
@@ -107,18 +122,26 @@ sealed class KotlinClassMetadata(val annotationData: Metadata) {
* Classes would have their own JVM classfiles and their own metadata of [Class] kind. * Classes would have their own JVM classfiles and their own metadata of [Class] kind.
*/ */
class FileFacade internal constructor(annotationData: Metadata) : KotlinClassMetadata(annotationData) { class FileFacade internal constructor(annotationData: Metadata) : KotlinClassMetadata(annotationData) {
private val packageData by lazy(PUBLICATION) {
JvmProtoBufUtil.readPackageDataFrom(annotationData.requireNotEmpty(), annotationData.data2) /**
* Returns the [KmPackage] representation of this metadata.
*
* Returns the same (mutable) [KmPackage] instance every time.
*/
public val kmPackage: KmPackage = KmPackage().apply {
val (strings, proto) = JvmProtoBufUtil.readPackageDataFrom(annotationData.requireNotEmpty(), annotationData.data2)
proto.accept(this, strings)
} }
/** /**
* Creates a new [KmPackage] instance from this file facade metadata. * Creates a new [KmPackage] instance from this file facade metadata.
*
* @throws IllegalArgumentException if metadata is malformed or inconsistent and cannot be transformed into [KmPackage].
*/ */
fun toKmPackage(): KmPackage = wrapIntoMetadataExceptionWhenNeeded { @Deprecated(
KmPackage().apply(this::accept) "To avoid excessive copying, use .kmPackage property instead. Note that it returns a view and not a copy.",
} ReplaceWith("kmPackage"),
DeprecationLevel.WARNING
)
fun toKmPackage(): KmPackage = KmPackage().also { newPkg -> kmPackage.accept(newPkg) }
/** /**
* Makes the given visitor visit metadata of this file facade. * Makes the given visitor visit metadata of this file facade.
@@ -126,10 +149,7 @@ sealed class KotlinClassMetadata(val annotationData: Metadata) {
* @param v the visitor that must visit this file facade * @param v the visitor that must visit this file facade
*/ */
@Deprecated(VISITOR_API_MESSAGE) @Deprecated(VISITOR_API_MESSAGE)
fun accept(v: KmPackageVisitor) { fun accept(v: KmPackageVisitor) = kmPackage.accept(v)
val (strings, proto) = packageData
proto.accept(v, strings)
}
/** /**
* A [KmPackageVisitor] that generates the metadata of a Kotlin file facade. * A [KmPackageVisitor] that generates the metadata of a Kotlin file facade.
@@ -167,21 +187,10 @@ sealed class KotlinClassMetadata(val annotationData: Metadata) {
* method implementations, `$WhenMappings` class for optimized `when` over enums, etc. * method implementations, `$WhenMappings` class for optimized `when` over enums, etc.
*/ */
class SyntheticClass internal constructor(annotationData: Metadata) : KotlinClassMetadata(annotationData) { class SyntheticClass internal constructor(annotationData: Metadata) : KotlinClassMetadata(annotationData) {
private val functionData by lazy(PUBLICATION) { private val functionData =
annotationData.data1.takeIf(Array<*>::isNotEmpty)?.let { data1 -> annotationData.data1.takeIf(Array<*>::isNotEmpty)?.let { data1 ->
JvmProtoBufUtil.readFunctionDataFrom(data1, annotationData.data2) JvmProtoBufUtil.readFunctionDataFrom(data1, annotationData.data2)
} }
}
/**
* Creates a new [KmLambda] instance from this synthetic class metadata.
* Returns `null` if this synthetic class does not represent a lambda.
*
* @throws IllegalArgumentException if metadata is malformed or inconsistent and cannot be transformed into [KmLambda].
*/
fun toKmLambda(): KmLambda? = wrapIntoMetadataExceptionWhenNeeded {
if (isLambda) KmLambda().apply(this::accept) else null
}
/** /**
* Returns `true` if this synthetic class is a class file compiled for a Kotlin lambda. * Returns `true` if this synthetic class is a class file compiled for a Kotlin lambda.
@@ -189,6 +198,28 @@ sealed class KotlinClassMetadata(val annotationData: Metadata) {
val isLambda: Boolean val isLambda: Boolean
get() = annotationData.data1.isNotEmpty() get() = annotationData.data1.isNotEmpty()
/**
* Returns the [KmLambda] representation of this metadata, or `null` if this synthetic class does not represent a lambda.
*
* Returns the same (mutable) [KmLambda] instance every time.
*/
public val kmLambda: KmLambda? = if (!isLambda) null else KmLambda().apply {
val (strings, proto) = functionData!!
proto.accept(this, strings)
}
/**
* Creates a new [KmLambda] instance from this synthetic class metadata.
* Returns `null` if this synthetic class does not represent a lambda.
*/
@Deprecated(
"To avoid excessive copying, use .kmLambda property instead. Note that it returns a view and not a copy.",
ReplaceWith("kmLambda"),
DeprecationLevel.WARNING
)
fun toKmLambda(): KmLambda? = if (isLambda) KmLambda().apply(this::accept) else null
/** /**
* Makes the given visitor visit metadata of this file facade if this synthetic class represents a Kotlin lambda * Makes the given visitor visit metadata of this file facade if this synthetic class represents a Kotlin lambda
* (`isLambda` == true). * (`isLambda` == true).
@@ -203,8 +234,7 @@ sealed class KotlinClassMetadata(val annotationData: Metadata) {
"accept(KmLambdaVisitor) is only possible for synthetic classes which are lambdas (isLambda = true)" "accept(KmLambdaVisitor) is only possible for synthetic classes which are lambdas (isLambda = true)"
) )
val (strings, proto) = functionData!! kmLambda!!.accept(v)
proto.accept(v, strings)
} }
/** /**
@@ -329,8 +359,14 @@ sealed class KotlinClassMetadata(val annotationData: Metadata) {
* @see JvmMultifileClass * @see JvmMultifileClass
*/ */
class MultiFileClassPart internal constructor(annotationData: Metadata) : KotlinClassMetadata(annotationData) { class MultiFileClassPart internal constructor(annotationData: Metadata) : KotlinClassMetadata(annotationData) {
private val packageData by lazy(PUBLICATION) { /**
JvmProtoBufUtil.readPackageDataFrom(annotationData.requireNotEmpty(), annotationData.data2) * Returns the [KmPackage] representation of this metadata.
*
* Returns the same (mutable) [KmPackage] instance every time.
*/
public val kmPackage: KmPackage = KmPackage().apply {
val (strings, proto) = JvmProtoBufUtil.readPackageDataFrom(annotationData.requireNotEmpty(), annotationData.data2)
proto.accept(this, strings)
} }
/** /**
@@ -341,12 +377,13 @@ sealed class KotlinClassMetadata(val annotationData: Metadata) {
/** /**
* Creates a new [KmPackage] instance from this multi-file class part metadata. * Creates a new [KmPackage] instance from this multi-file class part metadata.
*
* @throws IllegalArgumentException if metadata is malformed or inconsistent and cannot be transformed into [KmPackage].
*/ */
fun toKmPackage(): KmPackage = wrapIntoMetadataExceptionWhenNeeded { @Deprecated(
KmPackage().apply(this::accept) "To avoid excessive copying, use .kmPackage property instead. Note that it returns a view and not a copy.",
} ReplaceWith("kmPackage"),
DeprecationLevel.WARNING
)
fun toKmPackage(): KmPackage = KmPackage().also { newKmp -> kmPackage.accept(newKmp) }
/** /**
* Makes the given visitor visit metadata of this multi-file class part. * Makes the given visitor visit metadata of this multi-file class part.
@@ -355,8 +392,7 @@ sealed class KotlinClassMetadata(val annotationData: Metadata) {
*/ */
@Deprecated(VISITOR_API_MESSAGE) @Deprecated(VISITOR_API_MESSAGE)
fun accept(v: KmPackageVisitor) { fun accept(v: KmPackageVisitor) {
val (strings, proto) = packageData kmPackage.accept(v)
proto.accept(v, strings)
} }
/** /**
@@ -398,7 +434,7 @@ sealed class KotlinClassMetadata(val annotationData: Metadata) {
* Represents metadata of an unknown class file. This class is used if an old version of this library is used against a new kind * Represents metadata of an unknown class file. This class is used if an old version of this library is used against a new kind
* of class files generated by the Kotlin compiler, unsupported by this library. * of class files generated by the Kotlin compiler, unsupported by this library.
*/ */
class Unknown internal constructor(annotationData: Metadata) : KotlinClassMetadata(annotationData) data object Unknown : KotlinClassMetadata(Metadata())
/** /**
* Collection of methods for reading and writing [KotlinClassMetadata], * Collection of methods for reading and writing [KotlinClassMetadata],
@@ -420,9 +456,9 @@ sealed class KotlinClassMetadata(val annotationData: Metadata) {
fun writeClass( fun writeClass(
kmClass: KmClass, kmClass: KmClass,
metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION, metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION,
extraInt: Int = 0 extraInt: Int = 0,
): Class = wrapWriteIntoIAE { ): Metadata = wrapWriteIntoIAE {
Class.Writer().also { kmClass.accept(it) }.write(metadataVersion, extraInt) Class.Writer().also { kmClass.accept(it) }.write(metadataVersion, extraInt).annotationData
} }
/** /**
@@ -440,9 +476,9 @@ sealed class KotlinClassMetadata(val annotationData: Metadata) {
fun writeFileFacade( fun writeFileFacade(
kmPackage: KmPackage, kmPackage: KmPackage,
metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION, metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION,
extraInt: Int = 0 extraInt: Int = 0,
): FileFacade = wrapWriteIntoIAE { ): Metadata = wrapWriteIntoIAE {
FileFacade.Writer().also { kmPackage.accept(it) }.write(metadataVersion, extraInt) FileFacade.Writer().also { kmPackage.accept(it) }.write(metadataVersion, extraInt).annotationData
} }
/** /**
@@ -460,9 +496,9 @@ sealed class KotlinClassMetadata(val annotationData: Metadata) {
fun writeLambda( fun writeLambda(
kmLambda: KmLambda, kmLambda: KmLambda,
metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION, metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION,
extraInt: Int = 0 extraInt: Int = 0,
): SyntheticClass = wrapWriteIntoIAE { ): Metadata = wrapWriteIntoIAE {
SyntheticClass.Writer().also { kmLambda.accept(it) }.write(metadataVersion, extraInt) SyntheticClass.Writer().also { kmLambda.accept(it) }.write(metadataVersion, extraInt).annotationData
} }
/** /**
@@ -479,8 +515,8 @@ sealed class KotlinClassMetadata(val annotationData: Metadata) {
@JvmOverloads @JvmOverloads
fun writeSyntheticClass( fun writeSyntheticClass(
metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION, metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION,
extraInt: Int = 0 extraInt: Int = 0,
): SyntheticClass = SyntheticClass.Writer().write(metadataVersion, extraInt) ): Metadata = SyntheticClass.Writer().write(metadataVersion, extraInt).annotationData
/** /**
* Writes metadata of the multi-file class facade. * Writes metadata of the multi-file class facade.
@@ -497,8 +533,8 @@ sealed class KotlinClassMetadata(val annotationData: Metadata) {
@JvmOverloads @JvmOverloads
fun writeMultiFileClassFacade( fun writeMultiFileClassFacade(
partClassNames: List<String>, metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION, partClassNames: List<String>, metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION,
extraInt: Int = 0 extraInt: Int = 0,
): MultiFileClassFacade = MultiFileClassFacade.Writer().write(partClassNames, metadataVersion, extraInt) ): Metadata = MultiFileClassFacade.Writer().write(partClassNames, metadataVersion, extraInt).annotationData
/** /**
* Writes the metadata of the multi-file class part. * Writes the metadata of the multi-file class part.
@@ -517,9 +553,9 @@ sealed class KotlinClassMetadata(val annotationData: Metadata) {
kmPackage: KmPackage, kmPackage: KmPackage,
facadeClassName: String, facadeClassName: String,
metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION, metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION,
extraInt: Int = 0 extraInt: Int = 0,
): MultiFileClassPart = wrapWriteIntoIAE { ): Metadata = wrapWriteIntoIAE {
MultiFileClassPart.Writer().also { kmPackage.accept(it) }.write(facadeClassName, metadataVersion, extraInt) MultiFileClassPart.Writer().also { kmPackage.accept(it) }.write(facadeClassName, metadataVersion, extraInt).annotationData
} }
/** /**
@@ -542,14 +578,15 @@ sealed class KotlinClassMetadata(val annotationData: Metadata) {
fun read(annotationData: Metadata): KotlinClassMetadata { fun read(annotationData: Metadata): KotlinClassMetadata {
checkMetadataVersionForRead(annotationData) checkMetadataVersionForRead(annotationData)
// All data is loaded lazily, no exceptions here to handle return wrapIntoMetadataExceptionWhenNeeded {
return when (annotationData.kind) { when (annotationData.kind) {
CLASS_KIND -> Class(annotationData) CLASS_KIND -> Class(annotationData)
FILE_FACADE_KIND -> FileFacade(annotationData) FILE_FACADE_KIND -> FileFacade(annotationData)
SYNTHETIC_CLASS_KIND -> SyntheticClass(annotationData) SYNTHETIC_CLASS_KIND -> SyntheticClass(annotationData)
MULTI_FILE_CLASS_FACADE_KIND -> MultiFileClassFacade(annotationData) MULTI_FILE_CLASS_FACADE_KIND -> MultiFileClassFacade(annotationData)
MULTI_FILE_CLASS_PART_KIND -> MultiFileClassPart(annotationData) MULTI_FILE_CLASS_PART_KIND -> MultiFileClassPart(annotationData)
else -> Unknown(annotationData) else -> Unknown
}
} }
} }
@@ -38,17 +38,27 @@ import org.jetbrains.kotlin.metadata.jvm.deserialization.serializeToByteArray
*/ */
@UnstableMetadataApi @UnstableMetadataApi
class KotlinModuleMetadata private constructor( class KotlinModuleMetadata private constructor(
@Suppress("MemberVisibilityCanBePrivate") val bytes: ByteArray, private val bytes: ByteArray,
@get:IgnoreInApiDump internal val data: ModuleMapping private val data: ModuleMapping,
) { ) {
/**
* Returns the [KmModule] representation of this metadata.
*
* Returns the same (mutable) [KmModule] instance every time.
*/
public val kmModule: KmModule = KmModule().apply(this::accept)
/** /**
* Visits metadata of this module with a new [KmModule] instance and returns that instance. * Visits metadata of this module with a new [KmModule] instance and returns that instance.
*
* @throws IllegalArgumentException if parsed metadata is inconsistent and can't be transformed into [KmModule].
*/ */
fun toKmModule(): KmModule = wrapIntoMetadataExceptionWhenNeeded { @Deprecated(
KmModule().apply(this::accept) "To avoid excessive copying, use .kmModule property instead. Note that it returns a view and not a copy.",
} ReplaceWith("kmModule"),
DeprecationLevel.WARNING
)
fun toKmModule(): KmModule = KmModule().apply(kmModule::accept)
/** /**
* A [KmModuleVisitor] that generates the metadata of a Kotlin JVM module file. * A [KmModuleVisitor] that generates the metadata of a Kotlin JVM module file.
@@ -97,9 +107,8 @@ class KotlinModuleMetadata private constructor(
* [KotlinClassMetadata.COMPATIBLE_METADATA_VERSION] by default * [KotlinClassMetadata.COMPATIBLE_METADATA_VERSION] by default
*/ */
@Deprecated("Writer API is deprecated as excessive and cumbersome. Please use KotlinModuleMetadata.write(kmModule, metadataVersion)") @Deprecated("Writer API is deprecated as excessive and cumbersome. Please use KotlinModuleMetadata.write(kmModule, metadataVersion)")
fun write(metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION): KotlinModuleMetadata { fun write(metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION): ByteArray {
val bytes = b.build().serializeToByteArray(JvmMetadataVersion(*metadataVersion), 0) return b.build().serializeToByteArray(JvmMetadataVersion(*metadataVersion), 0)
return KotlinModuleMetadata(bytes, dataFromBytes(bytes))
} }
} }
@@ -158,12 +167,12 @@ class KotlinModuleMetadata private constructor(
* @param metadataVersion metadata version to be written to the metadata (see [Metadata.metadataVersion]), * @param metadataVersion metadata version to be written to the metadata (see [Metadata.metadataVersion]),
* [KotlinClassMetadata.COMPATIBLE_METADATA_VERSION] by default * [KotlinClassMetadata.COMPATIBLE_METADATA_VERSION] by default
* *
* @throws IllegalArgumentException if [kmModule] is not correct and can't be written or if [metadataVersion] is not supported for writing. * @throws IllegalArgumentException if [kmModule] is not correct and cannot be written or if [metadataVersion] is not supported for writing.
*/ */
@UnstableMetadataApi @UnstableMetadataApi
@JvmStatic @JvmStatic
@JvmOverloads @JvmOverloads
fun write(kmModule: KmModule, metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION): KotlinModuleMetadata = wrapWriteIntoIAE { fun write(kmModule: KmModule, metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION): ByteArray = wrapWriteIntoIAE {
Writer().also { kmModule.accept(it) }.write(metadataVersion) Writer().also { kmModule.accept(it) }.write(metadataVersion)
} }
@@ -21,17 +21,17 @@ public class JavaUsageTest {
@Test @Test
public void testKotlinClassHeader() { public void testKotlinClassHeader() {
Metadata m = MetadataSmokeTest.class.getAnnotation(Metadata.class); Metadata m = MetadataSmokeTest.class.getAnnotation(Metadata.class);
KmClass clazz1 = ((KotlinClassMetadata.Class) Objects.requireNonNull(KotlinClassMetadata.read(m))).toKmClass(); KmClass clazz1 = ((KotlinClassMetadata.Class) Objects.requireNonNull(KotlinClassMetadata.read(m))).getKmClass();
KotlinClassHeader kh = new KotlinClassHeader(m.k(), m.mv(), m.d1(), m.d2(), m.xs(), m.pn(), m.xi()); KotlinClassHeader kh = new KotlinClassHeader(m.k(), m.mv(), m.d1(), m.d2(), m.xs(), m.pn(), m.xi());
KmClass clazz2 = ((KotlinClassMetadata.Class) Objects.requireNonNull(KotlinClassMetadata.read(kh))).toKmClass(); KmClass clazz2 = ((KotlinClassMetadata.Class) Objects.requireNonNull(KotlinClassMetadata.read(kh))).getKmClass();
assertEquals(clazz1.getName(), clazz2.getName()); assertEquals(clazz1.getName(), clazz2.getName());
} }
@Test @Test
public void testWritingBackWithDefaults() { public void testWritingBackWithDefaults() {
Metadata m = MetadataSmokeTest.class.getAnnotation(Metadata.class); Metadata m = MetadataSmokeTest.class.getAnnotation(Metadata.class);
KmClass clazz1 = ((KotlinClassMetadata.Class) Objects.requireNonNull(KotlinClassMetadata.read(m))).toKmClass(); KmClass clazz1 = ((KotlinClassMetadata.Class) Objects.requireNonNull(KotlinClassMetadata.read(m))).getKmClass();
Metadata written = KotlinClassMetadata.writeClass(clazz1).getAnnotationData(); Metadata written = KotlinClassMetadata.writeClass(clazz1);
assertArrayEquals(written.mv(), KotlinClassMetadata.COMPATIBLE_METADATA_VERSION); assertArrayEquals(written.mv(), KotlinClassMetadata.COMPATIBLE_METADATA_VERSION);
assertEquals(0, written.xi()); assertEquals(0, written.xi());
} }
@@ -20,7 +20,7 @@ class MetadataExceptionsTest {
val malformedMetadata = val malformedMetadata =
Metadata(KotlinClassMetadata.CLASS_KIND, KotlinClassMetadata.COMPATIBLE_METADATA_VERSION, arrayOf(malformedInput)) Metadata(KotlinClassMetadata.CLASS_KIND, KotlinClassMetadata.COMPATIBLE_METADATA_VERSION, arrayOf(malformedInput))
val e = assertFailsWith<IllegalArgumentException> { val e = assertFailsWith<IllegalArgumentException> {
(KotlinClassMetadata.read(malformedMetadata) as KotlinClassMetadata.Class).toKmClass() (KotlinClassMetadata.read(malformedMetadata) as KotlinClassMetadata.Class)
} }
assertIs<InvalidProtocolBufferException>(e.cause) assertIs<InvalidProtocolBufferException>(e.cause)
} }
@@ -60,7 +60,7 @@ class MetadataSmokeTest {
} }
} }
val annotationData = KotlinClassMetadata.writeClass(klass).annotationData val annotationData = KotlinClassMetadata.writeClass(klass)
// Then, produce the bytecode of a .class file with ASM // Then, produce the bytecode of a .class file with ASM
@@ -180,7 +180,7 @@ class MetadataSmokeTest {
} }
) )
val classWithUnstableParameterNames = newMetadata.toKmClass() val classWithUnstableParameterNames = newMetadata.readAsKmClass()
classWithUnstableParameterNames.constructors.forEach { assertTrue(Flag.Constructor.HAS_NON_STABLE_PARAMETER_NAMES(it.flags)) } classWithUnstableParameterNames.constructors.forEach { assertTrue(Flag.Constructor.HAS_NON_STABLE_PARAMETER_NAMES(it.flags)) }
classWithUnstableParameterNames.functions.forEach { assertTrue(Flag.Function.HAS_NON_STABLE_PARAMETER_NAMES(it.flags)) } classWithUnstableParameterNames.functions.forEach { assertTrue(Flag.Function.HAS_NON_STABLE_PARAMETER_NAMES(it.flags)) }
@@ -217,7 +217,27 @@ class MetadataSmokeTest {
val kmClassCopy = KotlinClassMetadata val kmClassCopy = KotlinClassMetadata
.writeClass(kmClass, metadata.metadataVersion, metadata.extraInt) .writeClass(kmClass, metadata.metadataVersion, metadata.extraInt)
.toKmClass() .readAsKmClass()
assertEquals(kmClassCopy.jvmFlags, jvmClassFlags) assertEquals(kmClassCopy.jvmFlags, jvmClassFlags)
} }
@Test
fun testDisplayNameSample() {
class A {}
val b: (Int) -> Int = fun(x: Int) = x
assertEquals("Class .kotlinx/metadata/test/MetadataSmokeTest\$testDisplayNameSample\$A", displayName(A::class.java.getMetadata()))
assertEquals("Lambda <no name provided>", displayName(b::class.java.getMetadata()))
}
fun displayName(metadata: Metadata): String = when (val kcm = KotlinClassMetadata.read(metadata)) {
is KotlinClassMetadata.Class -> "Class ${kcm.kmClass.name}"
is KotlinClassMetadata.FileFacade -> "File facade with functions: ${kcm.kmPackage.functions.joinToString { it.name }}"
is KotlinClassMetadata.SyntheticClass -> kcm.kmLambda?.function?.name?.let { "Lambda $it" } ?: "Synthetic class"
is KotlinClassMetadata.MultiFileClassFacade -> "Multifile class facade with parts: ${kcm.partClassNames.joinToString()}"
is KotlinClassMetadata.MultiFileClassPart -> "Multifile class part ${kcm.facadeClassName}"
is KotlinClassMetadata.Unknown -> "Unknown metadata"
}
} }
@@ -14,7 +14,7 @@ internal fun Class<*>.getMetadata(): Metadata {
internal fun Metadata.readAsKmClass(): KmClass { internal fun Metadata.readAsKmClass(): KmClass {
val clazz = KotlinClassMetadata.read(this) as? KotlinClassMetadata.Class val clazz = KotlinClassMetadata.read(this) as? KotlinClassMetadata.Class
return clazz?.toKmClass() ?: error("Not a KotlinClassMetadata.Class: $clazz") return clazz?.kmClass ?: error("Not a KotlinClassMetadata.Class: $clazz")
} }
internal fun Class<*>.readMetadataAsKmClass(): KmClass = getMetadata().readAsKmClass() internal fun Class<*>.readMetadataAsKmClass(): KmClass = getMetadata().readAsKmClass()
@@ -11,7 +11,7 @@ import kotlinx.metadata.jvm.UnstableMetadataApi
import java.io.File import java.io.File
class Kotlinp(private val settings: KotlinpSettings) { class Kotlinp(private val settings: KotlinpSettings) {
internal fun renderClassFile(classFile: KotlinClassMetadata?): String = internal fun renderClassFile(classFile: KotlinClassMetadata): String =
when (classFile) { when (classFile) {
is KotlinClassMetadata.Class -> ClassPrinter(settings).print(classFile) is KotlinClassMetadata.Class -> ClassPrinter(settings).print(classFile)
is KotlinClassMetadata.FileFacade -> FileFacadePrinter(settings).print(classFile) is KotlinClassMetadata.FileFacade -> FileFacadePrinter(settings).print(classFile)
@@ -21,14 +21,17 @@ class Kotlinp(private val settings: KotlinpSettings) {
} }
is KotlinClassMetadata.MultiFileClassFacade -> MultiFileClassFacadePrinter().print(classFile) is KotlinClassMetadata.MultiFileClassFacade -> MultiFileClassFacadePrinter().print(classFile)
is KotlinClassMetadata.MultiFileClassPart -> MultiFileClassPartPrinter(settings).print(classFile) is KotlinClassMetadata.MultiFileClassPart -> MultiFileClassPartPrinter(settings).print(classFile)
is KotlinClassMetadata.Unknown -> buildString { appendLine("unknown file (k=${classFile.annotationData.kind})") } is KotlinClassMetadata.Unknown -> buildString { appendLine("unknown file") }
null -> buildString { appendLine("unsupported file") }
} }
internal fun readClassFile(file: File): KotlinClassMetadata? {
val header = file.readKotlinClassHeader() ?: throw KotlinpException("file is not a Kotlin class file: $file") internal fun readClassFile(file: File): Metadata {
return file.readKotlinClassHeader() ?: throw KotlinpException("file is not a Kotlin class file: $file")
}
internal fun readMetadata(metadata: Metadata): KotlinClassMetadata {
return try { return try {
KotlinClassMetadata.read(header) KotlinClassMetadata.read(metadata)
} catch (e: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
throw KotlinpException("inconsistent Kotlin metadata: ${e.message}") throw KotlinpException("inconsistent Kotlin metadata: ${e.message}")
} }
@@ -43,7 +43,7 @@ object Main {
val text = try { val text = try {
when (file.extension) { when (file.extension) {
"class" -> kotlinp.renderClassFile(kotlinp.readClassFile(file)) "class" -> kotlinp.renderClassFile(kotlinp.readMetadata(kotlinp.readClassFile(file)))
"kotlin_module" -> @OptIn(UnstableMetadataApi::class) kotlinp.renderModuleFile(kotlinp.readModuleFile(file)) "kotlin_module" -> @OptIn(UnstableMetadataApi::class) kotlinp.renderModuleFile(kotlinp.readModuleFile(file))
else -> throw KotlinpException("only .class and .kotlin_module files are supported") else -> throw KotlinpException("only .class and .kotlin_module files are supported")
} }
@@ -567,7 +567,7 @@ class ClassPrinter(private val settings: KotlinpSettings) : AbstractPrinter<Kotl
} }
} }
override fun print(klass: KotlinClassMetadata.Class): String = print(klass.toKmClass()) override fun print(klass: KotlinClassMetadata.Class): String = print(klass.kmClass)
@OptIn(ExperimentalContextReceivers::class) @OptIn(ExperimentalContextReceivers::class)
fun print(kmClass: KmClass): String { fun print(kmClass: KmClass): String {
@@ -625,7 +625,7 @@ abstract class PackagePrinter(private val settings: KotlinpSettings) {
class FileFacadePrinter(settings: KotlinpSettings) : PackagePrinter(settings), AbstractPrinter<KotlinClassMetadata.FileFacade> { class FileFacadePrinter(settings: KotlinpSettings) : PackagePrinter(settings), AbstractPrinter<KotlinClassMetadata.FileFacade> {
override fun print(klass: KotlinClassMetadata.FileFacade): String { override fun print(klass: KotlinClassMetadata.FileFacade): String {
print(klass.toKmPackage()) print(klass.kmPackage)
return sb.toString() return sb.toString()
} }
} }
@@ -635,7 +635,7 @@ class LambdaPrinter(private val settings: KotlinpSettings) : AbstractPrinter<Kot
val sb = StringBuilder().apply { val sb = StringBuilder().apply {
appendLine("lambda {") appendLine("lambda {")
} }
val kLambda = klass.toKmLambda() ?: throw KotlinpException("Synthetic class $klass is not a lambda") val kLambda = klass.kmLambda ?: throw KotlinpException("Synthetic class $klass is not a lambda")
visitFunction(kLambda.function, settings, sb) visitFunction(kLambda.function, settings, sb)
sb.appendLine("}") sb.appendLine("}")
return sb.toString() return sb.toString()
@@ -647,7 +647,7 @@ class MultiFileClassPartPrinter(
) : PackagePrinter(settings), AbstractPrinter<KotlinClassMetadata.MultiFileClassPart> { ) : PackagePrinter(settings), AbstractPrinter<KotlinClassMetadata.MultiFileClassPart> {
override fun print(klass: KotlinClassMetadata.MultiFileClassPart): String { override fun print(klass: KotlinClassMetadata.MultiFileClassPart): String {
sb.appendLine(" // facade: ${klass.facadeClassName}") sb.appendLine(" // facade: ${klass.facadeClassName}")
print(klass.toKmPackage()) print(klass.kmPackage)
return sb.toString() return sb.toString()
} }
} }
@@ -696,7 +696,7 @@ class ModuleFilePrinter(private val settings: KotlinpSettings) {
@UnstableMetadataApi @UnstableMetadataApi
fun print(metadata: KotlinModuleMetadata): String { fun print(metadata: KotlinModuleMetadata): String {
val kmModule = metadata.toKmModule() val kmModule = metadata.kmModule
kmModule.packageParts.forEach { (fqName, kmPackageParts) -> kmModule.packageParts.forEach { (fqName, kmPackageParts) ->
visitPackageParts(fqName, kmPackageParts.fileFacades, kmPackageParts.multiFileClassParts) visitPackageParts(fqName, kmPackageParts.fileFacades, kmPackageParts.multiFileClassParts)
} }
@@ -49,8 +49,8 @@ fun compileAndPrintAllFiles(
when (outputFile.extension) { when (outputFile.extension) {
"kotlin_module" -> { "kotlin_module" -> {
val moduleFile = kotlinp.readModuleFile(outputFile)!! val moduleFile = kotlinp.readModuleFile(outputFile)!!
val transformedWithVisitors = transformModuleFileWithReadWriteVisitors(moduleFile) val transformedWithVisitors = KotlinModuleMetadata.read(transformModuleFileWithReadWriteVisitors(moduleFile))
val transformedWithNodes = transformModuleFileWithNodes(moduleFile) val transformedWithNodes = KotlinModuleMetadata.read(transformModuleFileWithNodes(moduleFile))
for ((sb, moduleFileToRender) in listOf( for ((sb, moduleFileToRender) in listOf(
main to moduleFile, afterVisitors to transformedWithVisitors, afterNodes to transformedWithNodes main to moduleFile, afterVisitors to transformedWithVisitors, afterNodes to transformedWithNodes
@@ -60,9 +60,10 @@ fun compileAndPrintAllFiles(
} }
} }
"class" -> { "class" -> {
val classFile = kotlinp.readClassFile(outputFile)!! val metadata = kotlinp.readClassFile(outputFile)
val classFile = kotlinp.readMetadata(metadata)
val classFile2 = transformClassFileWithReadWriteVisitors(classFile) val classFile2 = transformClassFileWithReadWriteVisitors(classFile)
val classFile3 = transformClassFileWithNodes(classFile) val classFile3 = KotlinClassMetadata.read(transformClassFileWithNodes(metadata, classFile))
for ((sb, classFileToRender) in listOf( for ((sb, classFileToRender) in listOf(
main to classFile, afterVisitors to classFile2, afterNodes to classFile3 main to classFile, afterVisitors to classFile2, afterNodes to classFile3
@@ -130,24 +131,25 @@ private fun transformClassFileWithReadWriteVisitors(classFile: KotlinClassMetada
} }
// Reads the class file and writes it back with KmClass/KmFunction/... elements. // Reads the class file and writes it back with KmClass/KmFunction/... elements.
private fun transformClassFileWithNodes(classFile: KotlinClassMetadata): KotlinClassMetadata = private fun transformClassFileWithNodes(metadata: Metadata, classFile: KotlinClassMetadata): Metadata =
when (classFile) { when (classFile) {
is KotlinClassMetadata.Class -> is KotlinClassMetadata.Class ->
KotlinClassMetadata.writeClass(classFile.toKmClass()) KotlinClassMetadata.writeClass(classFile.kmClass)
is KotlinClassMetadata.FileFacade -> is KotlinClassMetadata.FileFacade ->
KotlinClassMetadata.writeFileFacade(classFile.toKmPackage()) KotlinClassMetadata.writeFileFacade(classFile.kmPackage)
is KotlinClassMetadata.SyntheticClass -> is KotlinClassMetadata.SyntheticClass ->
classFile.toKmLambda()?.let { KotlinClassMetadata.writeLambda(it) } ?: KotlinClassMetadata.writeSyntheticClass() classFile.kmLambda?.let { KotlinClassMetadata.writeLambda(it) } ?: KotlinClassMetadata.writeSyntheticClass()
is KotlinClassMetadata.MultiFileClassPart -> is KotlinClassMetadata.MultiFileClassPart ->
KotlinClassMetadata.writeMultiFileClassPart(classFile.toKmPackage(), classFile.facadeClassName) KotlinClassMetadata.writeMultiFileClassPart(classFile.kmPackage, classFile.facadeClassName)
else -> classFile is KotlinClassMetadata.MultiFileClassFacade -> KotlinClassMetadata.writeMultiFileClassFacade(classFile.partClassNames)
is KotlinClassMetadata.Unknown -> metadata
} }
@Suppress("DEPRECATION") // We're testing that reading/writing with KmNodes is identical to direct @Suppress("DEPRECATION") // We're testing that reading/writing with KmNodes is identical to direct
@OptIn(UnstableMetadataApi::class) @OptIn(UnstableMetadataApi::class)
private fun transformModuleFileWithReadWriteVisitors(moduleFile: KotlinModuleMetadata): KotlinModuleMetadata = private fun transformModuleFileWithReadWriteVisitors(moduleFile: KotlinModuleMetadata): ByteArray =
KotlinModuleMetadata.Writer().apply(moduleFile::accept).write() KotlinModuleMetadata.Writer().apply(moduleFile::accept).write()
@OptIn(UnstableMetadataApi::class) @OptIn(UnstableMetadataApi::class)
private fun transformModuleFileWithNodes(moduleFile: KotlinModuleMetadata): KotlinModuleMetadata = private fun transformModuleFileWithNodes(moduleFile: KotlinModuleMetadata): ByteArray =
KotlinModuleMetadata.write(moduleFile.toKmModule()) KotlinModuleMetadata.write(moduleFile.kmModule)