From 3d679b76bce04a9bfbb7c0a2f769d5838d2c3bf9 Mon Sep 17 00:00:00 2001 From: Leonid Startsev Date: Thu, 21 Apr 2022 15:26:05 +0300 Subject: [PATCH] Use Metadata directly instead of KotlinClassHeader; rewrite latter to Java so Java clients can obtain Metadata instance from it. --- .../jvm/api/kotlinx-metadata-jvm.api | 39 ++-- .../kotlinx-metadata/jvm/build.gradle.kts | 4 + .../metadata/jvm/KotlinClassHeader.java | 173 +++++++++++++++++ .../kotlinx/metadata/jvm/KotlinClassHeader.kt | 179 ------------------ .../metadata/jvm/KotlinClassMetadata.kt | 125 ++++++++---- .../metadata/jvm/KotlinModuleMetadata.kt | 9 +- .../kotlinx/metadata/jvm/jvmMetadataUtil.kt | 24 +++ .../kotlinx/metadata/test/JavaUsageTest.java | 26 +++ .../metadata/test/MetadataSmokeTest.kt | 18 +- .../kotlin/kotlinp/readKotlinClassHeader.kt | 10 +- 10 files changed, 347 insertions(+), 260 deletions(-) create mode 100644 libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinClassHeader.java delete mode 100644 libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinClassHeader.kt create mode 100644 libraries/kotlinx-metadata/jvm/test/kotlinx/metadata/test/JavaUsageTest.java diff --git a/libraries/kotlinx-metadata/jvm/api/kotlinx-metadata-jvm.api b/libraries/kotlinx-metadata/jvm/api/kotlinx-metadata-jvm.api index beecdd25002..7aee2962c77 100644 --- a/libraries/kotlinx-metadata/jvm/api/kotlinx-metadata-jvm.api +++ b/libraries/kotlinx-metadata/jvm/api/kotlinx-metadata-jvm.api @@ -231,35 +231,36 @@ public final class kotlinx/metadata/jvm/KmPackageParts { public final fun getMultiFileClassParts ()Ljava/util/Map; } -public final class kotlinx/metadata/jvm/KotlinClassHeader { +public final class kotlinx/metadata/jvm/KotlinClassHeader : kotlin/Metadata { public static final field CLASS_KIND I - public static final field COMPATIBLE_BYTECODE_VERSION [I public static final field COMPATIBLE_METADATA_VERSION [I - public static final field Companion Lkotlinx/metadata/jvm/KotlinClassHeader$Companion; public static final field FILE_FACADE_KIND I public static final field MULTI_FILE_CLASS_FACADE_KIND I public static final field MULTI_FILE_CLASS_PART_KIND I public static final field SYNTHETIC_CLASS_KIND I - public fun (Ljava/lang/Integer;[I[I[Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;)V public fun (Ljava/lang/Integer;[I[Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;)V - public final fun getBytecodeVersion ()[I - public final fun getData1 ()[Ljava/lang/String; - public final fun getData2 ()[Ljava/lang/String; - public final fun getExtraInt ()I - public final fun getExtraString ()Ljava/lang/String; - public final fun getKind ()I - public final fun getMetadataVersion ()[I - public final fun getPackageName ()Ljava/lang/String; -} - -public final class kotlinx/metadata/jvm/KotlinClassHeader$Companion { + public fun annotationType ()Ljava/lang/Class; + public fun bv ()[I + public fun d1 ()[Ljava/lang/String; + public fun d2 ()[Ljava/lang/String; + public fun k ()I + public fun mv ()[I + public fun pn ()Ljava/lang/String; + public fun xi ()I + public fun xs ()Ljava/lang/String; } public abstract class kotlinx/metadata/jvm/KotlinClassMetadata { + public static final field CLASS_KIND I + public static final field COMPATIBLE_METADATA_VERSION [I public static final field Companion Lkotlinx/metadata/jvm/KotlinClassMetadata$Companion; - public synthetic fun (Lkotlinx/metadata/jvm/KotlinClassHeader;Lkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun getHeader ()Lkotlinx/metadata/jvm/KotlinClassHeader; - public static final fun read (Lkotlinx/metadata/jvm/KotlinClassHeader;)Lkotlinx/metadata/jvm/KotlinClassMetadata; + public static final field FILE_FACADE_KIND I + public static final field MULTI_FILE_CLASS_FACADE_KIND I + public static final field MULTI_FILE_CLASS_PART_KIND I + public static final field SYNTHETIC_CLASS_KIND I + public synthetic fun (Lkotlin/Metadata;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun getHeader ()Lkotlin/Metadata; + public static final fun read (Lkotlin/Metadata;)Lkotlinx/metadata/jvm/KotlinClassMetadata; } public final class kotlinx/metadata/jvm/KotlinClassMetadata$Class : kotlinx/metadata/jvm/KotlinClassMetadata { @@ -276,7 +277,7 @@ public final class kotlinx/metadata/jvm/KotlinClassMetadata$Class$Writer : kotli } public final class kotlinx/metadata/jvm/KotlinClassMetadata$Companion { - public final fun read (Lkotlinx/metadata/jvm/KotlinClassHeader;)Lkotlinx/metadata/jvm/KotlinClassMetadata; + public final fun read (Lkotlin/Metadata;)Lkotlinx/metadata/jvm/KotlinClassMetadata; public final fun writeClass (Lkotlinx/metadata/KmClass;[II)Lkotlinx/metadata/jvm/KotlinClassMetadata$Class; public static synthetic fun writeClass$default (Lkotlinx/metadata/jvm/KotlinClassMetadata$Companion;Lkotlinx/metadata/KmClass;[IIILjava/lang/Object;)Lkotlinx/metadata/jvm/KotlinClassMetadata$Class; public final fun writeFileFacade (Lkotlinx/metadata/KmPackage;[II)Lkotlinx/metadata/jvm/KotlinClassMetadata$FileFacade; diff --git a/libraries/kotlinx-metadata/jvm/build.gradle.kts b/libraries/kotlinx-metadata/jvm/build.gradle.kts index f97028309af..1a5c7c76053 100644 --- a/libraries/kotlinx-metadata/jvm/build.gradle.kts +++ b/libraries/kotlinx-metadata/jvm/build.gradle.kts @@ -23,6 +23,10 @@ group = "org.jetbrains.kotlinx" val deployVersion = findProperty("kotlinxMetadataDeployVersion") as String? version = deployVersion ?: "0.1-SNAPSHOT" +//kotlin { +// explicitApiWarning() +//} + sourceSets { "main" { projectDefault() } "test" { projectDefault() } diff --git a/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinClassHeader.java b/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinClassHeader.java new file mode 100644 index 00000000000..fab843ec55d --- /dev/null +++ b/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinClassHeader.java @@ -0,0 +1,173 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package kotlinx.metadata.jvm; + +import kotlin.DeprecationLevel; +import kotlin.Metadata; +import kotlin.ReplaceWith; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion; + +import java.lang.annotation.Annotation; +import java.util.Arrays; + + +/** + * An implementation of the {@link Metadata} annotation on a JVM class file, containing the metadata of Kotlin declarations declared in the class file. + * This class is intended to be used by Java clients to be able to pass a {@link Metadata} instance to various methods in the library if it cannot be obtained via reflection. + * Kotlin clients should be able to instantiate annotation directly and therefore should not use this class. + */ +@SuppressWarnings("ClassExplicitlyAnnotation") +@kotlin.Deprecated(message = "Kotlin clients should instantiate Metadata annotation directly", replaceWith = @ReplaceWith(expression = "Metadata", imports = {}), level = DeprecationLevel.ERROR) +public final class KotlinClassHeader implements Metadata { + private final int k; + @NotNull private final int[] mv; + @NotNull private final String[] d1; + @NotNull private final String[] d2; + @NotNull private final String xs; + @NotNull private final String pn; + private final int xi; + + /** + * @param kind see {@link Metadata#k()} + * @param metadataVersion see {@link Metadata#mv()} + * @param data1 see {@link Metadata#d1()} + * @param data2 see {@link Metadata#d2()} + * @param extraString see {@link Metadata#xs()} + * @param packageName see {@link Metadata#pn()} + * @param extraInt see {@link Metadata#xi()} + */ + @SuppressWarnings("SSBasedInspection") + public KotlinClassHeader( + @Nullable Integer kind, + @Nullable int[] metadataVersion, + @Nullable String[] data1, + @Nullable String[] data2, + @Nullable String extraString, + @Nullable String packageName, + @Nullable Integer extraInt + ) { + if (kind != null) this.k = kind; else this.k = 1; + if (metadataVersion != null) this.mv = metadataVersion; else this.mv = new int[0]; + if (data1 != null) this.d1 = data1; else this.d1 = new String[0]; + if (data2 != null) this.d2 = data2; else this.d2 = new String[0]; + if (extraString != null) this.xs = extraString; else this.xs = ""; + if (packageName != null) this.pn = packageName; else this.pn = ""; + if (extraInt != null) this.xi = extraInt; else this.xi = 0; + } + + /** + * {@inheritdoc} + */ + @Override + public int k() { + return k; + } + + /** + * {@inheritdoc} + */ + @Override + public int[] mv() { + return mv; + } + + /** + * {@inheritdoc} + */ + @Override + @Deprecated + public int[] bv() { + return new int[] {1, 0, 3}; + } + + /** + * {@inheritdoc} + */ + @Override + public String[] d1() { + return d1; + } + + /** + * {@inheritdoc} + */ + @Override + public String[] d2() { + return d2; + } + + /** + * {@inheritdoc} + */ + @Override + public String xs() { + return xs; + } + + /** + * {@inheritdoc} + */ + @Override + public String pn() { + return pn; + } + + /** + * {@inheritdoc} + */ + @Override + public int xi() { + return xi; + } + + /** + * {@inheritdoc} + */ + @Override + public Class annotationType() { + return Metadata.class; + } + + // TODO: equals, hashCode, etc as in Java Annotation contract? + + /** + * Use {@link KotlinClassMetadata#CLASS_KIND} instead + */ + @Deprecated + public static final int CLASS_KIND = 1; + + /** + * Use {@link KotlinClassMetadata#FILE_FACADE_KIND} instead + */ + @Deprecated + public static final int FILE_FACADE_KIND = 2; + + /** + * Use {@link KotlinClassMetadata#SYNTHETIC_CLASS_KIND} instead + */ + @Deprecated + public static final int SYNTHETIC_CLASS_KIND = 3; + + /** + * Use {@link KotlinClassMetadata#MULTI_FILE_CLASS_FACADE_KIND} instead + */ + @Deprecated + public static final int MULTI_FILE_CLASS_FACADE_KIND = 4; + + /** + * Use {@link KotlinClassMetadata#MULTI_FILE_CLASS_PART_KIND} instead + */ + @Deprecated + public static final int MULTI_FILE_CLASS_PART_KIND = 5; + + /** + * Use {@link KotlinClassMetadata#COMPATIBLE_METADATA_VERSION} instead + */ + @Deprecated + public static final int[] COMPATIBLE_METADATA_VERSION = Arrays.copyOf(JvmMetadataVersion.INSTANCE.toArray(), 3); +} diff --git a/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinClassHeader.kt b/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinClassHeader.kt deleted file mode 100644 index cb48be85dd1..00000000000 --- a/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinClassHeader.kt +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright 2000-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. - * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. - */ - -@file:Suppress("MemberVisibilityCanBePrivate") - -package kotlinx.metadata.jvm - -import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion - -/** - * A mirror to the [Metadata] annotation on a JVM class file, containing the metadata of Kotlin declarations declared in the class file. - * Properties of this class correspond 1:1 to the properties of [Metadata]. - * - * @param kind see [kind] - * @param metadataVersion see [metadataVersion] - * @param bytecodeVersion see [bytecodeVersion] - * @param data1 see [data1] - * @param data2 see [data2] - * @param extraString see [extraString] - * @param packageName see [packageName] - * @param extraInt see [extraInt] - */ -class KotlinClassHeader -@Deprecated( - "Use another constructor, which doesn't take bytecodeVersion as a parameter.", - ReplaceWith("KotlinClassHeader(kind, metadataVersion, data1, data2, extraString, packageName, extraInt)"), - DeprecationLevel.ERROR -) -constructor( - kind: Int?, - metadataVersion: IntArray?, - bytecodeVersion: IntArray?, - data1: Array?, - data2: Array?, - extraString: String?, - packageName: String?, - extraInt: Int? -) { - @Suppress("DEPRECATION_ERROR") - constructor( - kind: Int?, - metadataVersion: IntArray?, - data1: Array?, - data2: Array?, - extraString: String?, - packageName: String?, - extraInt: Int? - ) : this(kind, metadataVersion, null, data1, data2, extraString, packageName, extraInt) - - /** - * A kind of the metadata this header encodes. - * - * @see Metadata.kind - * @see CLASS_KIND - * @see FILE_FACADE_KIND - * @see SYNTHETIC_CLASS_KIND - * @see MULTI_FILE_CLASS_FACADE_KIND - * @see MULTI_FILE_CLASS_PART_KIND - */ - val kind: Int = kind ?: 1 - - /** - * The version of the metadata provided in other properties of this header. - * - * @see Metadata.metadataVersion - * @see COMPATIBLE_METADATA_VERSION - */ - val metadataVersion: IntArray = metadataVersion ?: intArrayOf() - - /** - * The version of the bytecode interface (naming conventions, signatures) of the corresponding class file. - * - * @see Metadata.bytecodeVersion - * @see COMPATIBLE_BYTECODE_VERSION - */ - @Deprecated( - "Bytecode version had no significant use in Kotlin metadata and it will be removed in a future version.", - level = DeprecationLevel.ERROR - ) - val bytecodeVersion: IntArray = bytecodeVersion ?: intArrayOf() - - /** - * The first array of strings used to encode the metadata. - * - * @see Metadata.data1 - */ - val data1: Array = data1 ?: emptyArray() - - /** - * The second array of strings used to encode the metadata. - * - * @see Metadata.data2 - */ - val data2: Array = data2 ?: emptyArray() - - /** - * An extra string field for the metadata. - * - * @see Metadata.extraString - */ - val extraString: String = extraString ?: "" - - /** - * Fully qualified name of the Kotlin package of the corresponding class, in case [JvmPackageName] was used. - * - * @see Metadata.packageName - */ - val packageName: String = packageName ?: "" - - /** - * An extra int field for the metadata. - * - * @see Metadata.extraInt - */ - val extraInt: Int = extraInt ?: 0 - - companion object { - /** - * A class file kind signifying that the corresponding class file contains a declaration of a Kotlin class. - * - * @see kind - */ - const val CLASS_KIND = 1 - - /** - * A class file kind signifying that the corresponding class file is a compiled Kotlin file facade. - * - * @see kind - */ - const val FILE_FACADE_KIND = 2 - - /** - * A class file kind signifying that the corresponding class file is synthetic, e.g. it's a class for lambda, `$DefaultImpls` class - * for interface method implementations, `$WhenMappings` class for optimized `when` over enums, etc. - * - * @see kind - */ - const val SYNTHETIC_CLASS_KIND = 3 - - /** - * A class file kind signifying that the corresponding class file is a compiled multi-file class facade. - * - * @see kind - * @see JvmMultifileClass - */ - const val MULTI_FILE_CLASS_FACADE_KIND = 4 - - /** - * A class file kind signifying that the corresponding class file is a compiled multi-file class part, i.e. an internal class - * with method bodies and their metadata, accessed only from the corresponding facade. - * - * @see kind - * @see JvmMultifileClass - */ - const val MULTI_FILE_CLASS_PART_KIND = 5 - - /** - * The latest metadata version supported by this version of the library. - * - * @see metadataVersion - */ - @JvmField - val COMPATIBLE_METADATA_VERSION = JvmMetadataVersion.INSTANCE.toArray().copyOf() - - /** - * The latest bytecode version supported by this version of the library. - * - * @see bytecodeVersion - */ - @Deprecated( - "Bytecode version had no significant use in Kotlin metadata and it will be removed in a future version.", - level = DeprecationLevel.ERROR - ) - @JvmField - val COMPATIBLE_BYTECODE_VERSION = intArrayOf(1, 0, 3) - } -} diff --git a/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinClassMetadata.kt b/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinClassMetadata.kt index 844b70d585c..fe5b5c726cb 100644 --- a/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinClassMetadata.kt +++ b/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinClassMetadata.kt @@ -14,6 +14,7 @@ import kotlinx.metadata.jvm.impl.writeProtoBufData import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil import org.jetbrains.kotlin.metadata.jvm.serialization.JvmStringTable +import java.util.* import kotlin.LazyThreadSafetyMode.PUBLICATION /** @@ -22,11 +23,11 @@ import kotlin.LazyThreadSafetyMode.PUBLICATION * To create an instance of [KotlinClassMetadata], first obtain a [KotlinClassHeader] instance by loading the contents * of the [Metadata] annotation on a class file, and then call [KotlinClassMetadata.read]. */ -sealed class KotlinClassMetadata(val header: KotlinClassHeader) { +sealed class KotlinClassMetadata(val header: Metadata) { /** * Represents metadata of a class file containing a declaration of a Kotlin class. */ - class Class internal constructor(header: KotlinClassHeader) : KotlinClassMetadata(header) { + class Class internal constructor(header: Metadata) : KotlinClassMetadata(header) { private val classData by lazy(PUBLICATION) { val data1 = (header.data1.takeIf(Array<*>::isNotEmpty) ?: throw InconsistentKotlinMetadataException("data1 must not be empty")) @@ -54,7 +55,6 @@ sealed class KotlinClassMetadata(val header: KotlinClassHeader) { /** * A [KmClassVisitor] that generates the metadata of a Kotlin class. */ -// @Suppress("INVISIBLE_REFERENCE") FIXME: maybe make supertype internal? @OptIn(DeprecatedVisitor::class) @Deprecated( "Writer API is deprecated. Please use KotlinClassMetadata.writeClass(kmClass, metadataVersion, extraInt)", @@ -65,7 +65,7 @@ sealed class KotlinClassMetadata(val header: KotlinClassHeader) { * Returns the metadata of the class that was written with this writer. * * @param metadataVersion metadata version to be written to the metadata (see [KotlinClassHeader.metadataVersion]), - * [KotlinClassHeader.COMPATIBLE_METADATA_VERSION] by default. Cannot be less (lexicographically) than `[1, 4]` + * [KotlinClassMetadata.COMPATIBLE_METADATA_VERSION] by default. Cannot be less (lexicographically) than `[1, 4]` * @param extraInt the value of the class-level flags to be written to the metadata (see [KotlinClassHeader.extraInt]), * 0 by default */ @@ -75,12 +75,12 @@ sealed class KotlinClassMetadata(val header: KotlinClassHeader) { level = DeprecationLevel.ERROR ) fun write( - metadataVersion: IntArray = KotlinClassHeader.COMPATIBLE_METADATA_VERSION, + metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION, extraInt: Int = 0 ): Class { checkMetadataVersion(metadataVersion) val (d1, d2) = writeProtoBufData(t.build(), c) - val metadata = KotlinClassHeader(KotlinClassHeader.CLASS_KIND, metadataVersion, d1, d2, null, null, extraInt) + val metadata = Metadata(CLASS_KIND, metadataVersion, d1, d2, extraInt = extraInt) return Class(metadata) } } @@ -89,7 +89,7 @@ sealed class KotlinClassMetadata(val header: KotlinClassHeader) { /** * Represents metadata of a class file containing a compiled Kotlin file facade. */ - class FileFacade internal constructor(header: KotlinClassHeader) : KotlinClassMetadata(header) { + class FileFacade internal constructor(header: Metadata) : KotlinClassMetadata(header) { private val packageData by lazy(PUBLICATION) { val data1 = (header.data1.takeIf(Array<*>::isNotEmpty) ?: throw InconsistentKotlinMetadataException("data1 must not be empty")) @@ -127,7 +127,7 @@ sealed class KotlinClassMetadata(val header: KotlinClassHeader) { * Returns the metadata of the file facade that was written with this writer. * * @param metadataVersion metadata version to be written to the metadata (see [KotlinClassHeader.metadataVersion]), - * [KotlinClassHeader.COMPATIBLE_METADATA_VERSION] by default. Cannot be less (lexicographically) than `[1, 4]` + * [KotlinClassMetadata.COMPATIBLE_METADATA_VERSION] by default. Cannot be less (lexicographically) than `[1, 4]` * @param extraInt the value of the class-level flags to be written to the metadata (see [KotlinClassHeader.extraInt]), * 0 by default */ @@ -137,12 +137,12 @@ sealed class KotlinClassMetadata(val header: KotlinClassHeader) { level = DeprecationLevel.ERROR ) fun write( - metadataVersion: IntArray = KotlinClassHeader.COMPATIBLE_METADATA_VERSION, + metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION, extraInt: Int = 0 ): FileFacade { checkMetadataVersion(metadataVersion) val (d1, d2) = writeProtoBufData(t.build(), c) - val metadata = KotlinClassHeader(KotlinClassHeader.FILE_FACADE_KIND, metadataVersion, d1, d2, null, null, extraInt) + val metadata = Metadata(FILE_FACADE_KIND, metadataVersion, d1, d2, extraInt = extraInt) return FileFacade(metadata) } } @@ -152,7 +152,7 @@ sealed class KotlinClassMetadata(val header: KotlinClassHeader) { * Represents metadata of a class file containing a synthetic class, e.g. a class for lambda, `$DefaultImpls` class for interface * method implementations, `$WhenMappings` class for optimized `when` over enums, etc. */ - class SyntheticClass internal constructor(header: KotlinClassHeader) : KotlinClassMetadata(header) { + class SyntheticClass internal constructor(header: Metadata) : KotlinClassMetadata(header) { private val functionData by lazy(PUBLICATION) { header.data1.takeIf(Array<*>::isNotEmpty)?.let { data1 -> JvmProtoBufUtil.readFunctionDataFrom(data1, header.data2) @@ -208,7 +208,7 @@ sealed class KotlinClassMetadata(val header: KotlinClassHeader) { * Returns the metadata of the synthetic class that was written with this writer. * * @param metadataVersion metadata version to be written to the metadata (see [KotlinClassHeader.metadataVersion]), - * [KotlinClassHeader.COMPATIBLE_METADATA_VERSION] by default. Cannot be less (lexicographically) than `[1, 4]` + * [KotlinClassMetadata.COMPATIBLE_METADATA_VERSION] by default. Cannot be less (lexicographically) than `[1, 4]` * @param extraInt the value of the class-level flags to be written to the metadata (see [KotlinClassHeader.extraInt]), * 0 by default */ @@ -219,7 +219,7 @@ sealed class KotlinClassMetadata(val header: KotlinClassHeader) { ) @JvmOverloads fun write( - metadataVersion: IntArray = KotlinClassHeader.COMPATIBLE_METADATA_VERSION, + metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION, extraInt: Int = 0 ): SyntheticClass { checkMetadataVersion(metadataVersion) @@ -227,7 +227,7 @@ sealed class KotlinClassMetadata(val header: KotlinClassHeader) { val (d1, d2) = if (proto != null) writeProtoBufData(proto, c) else Pair(emptyArray(), emptyArray()) - val metadata = KotlinClassHeader(KotlinClassHeader.SYNTHETIC_CLASS_KIND, metadataVersion, d1, d2, null, null, extraInt) + val metadata = Metadata(SYNTHETIC_CLASS_KIND, metadataVersion, d1, d2, extraInt = extraInt) return SyntheticClass(metadata) } } @@ -238,7 +238,7 @@ sealed class KotlinClassMetadata(val header: KotlinClassHeader) { * * @see JvmMultifileClass */ - class MultiFileClassFacade internal constructor(header: KotlinClassHeader) : KotlinClassMetadata(header) { + class MultiFileClassFacade internal constructor(header: Metadata) : KotlinClassMetadata(header) { /** * JVM internal names of the part classes which this multi-file class combines. */ @@ -257,7 +257,7 @@ sealed class KotlinClassMetadata(val header: KotlinClassHeader) { * * @param partClassNames JVM internal names of the part classes which this multi-file class combines * @param metadataVersion metadata version to be written to the metadata (see [KotlinClassHeader.metadataVersion]), - * [KotlinClassHeader.COMPATIBLE_METADATA_VERSION] by default. Cannot be less (lexicographically) than `[1, 4]` + * [KotlinClassMetadata.COMPATIBLE_METADATA_VERSION] by default. Cannot be less (lexicographically) than `[1, 4]` * @param extraInt the value of the class-level flags to be written to the metadata (see [KotlinClassHeader.extraInt]), * 0 by default */ @@ -269,13 +269,13 @@ sealed class KotlinClassMetadata(val header: KotlinClassHeader) { @JvmOverloads fun write( partClassNames: List, - metadataVersion: IntArray = KotlinClassHeader.COMPATIBLE_METADATA_VERSION, + metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION, extraInt: Int = 0 ): MultiFileClassFacade { checkMetadataVersion(metadataVersion) - val metadata = KotlinClassHeader( - KotlinClassHeader.MULTI_FILE_CLASS_FACADE_KIND, metadataVersion, - partClassNames.toTypedArray(), null, null, null, extraInt + val metadata = Metadata( + MULTI_FILE_CLASS_FACADE_KIND, metadataVersion, + partClassNames.toTypedArray(), extraInt = extraInt ) return MultiFileClassFacade(metadata) } @@ -288,7 +288,7 @@ sealed class KotlinClassMetadata(val header: KotlinClassHeader) { * * @see JvmMultifileClass */ - class MultiFileClassPart internal constructor(header: KotlinClassHeader) : KotlinClassMetadata(header) { + class MultiFileClassPart internal constructor(header: Metadata) : KotlinClassMetadata(header) { private val packageData by lazy(PUBLICATION) { val data1 = (header.data1.takeIf(Array<*>::isNotEmpty) ?: throw InconsistentKotlinMetadataException("data1 must not be empty")) @@ -333,7 +333,7 @@ sealed class KotlinClassMetadata(val header: KotlinClassHeader) { * * @param facadeClassName JVM internal name of the corresponding multi-file class facade * @param metadataVersion metadata version to be written to the metadata (see [KotlinClassHeader.metadataVersion]), - * [KotlinClassHeader.COMPATIBLE_METADATA_VERSION] by default. Cannot be less (lexicographically) than `[1, 4]` + * [KotlinClassMetadata.COMPATIBLE_METADATA_VERSION] by default. Cannot be less (lexicographically) than `[1, 4]` * @param extraInt the value of the class-level flags to be written to the metadata (see [KotlinClassHeader.extraInt]), * 0 by default */ @@ -344,13 +344,13 @@ sealed class KotlinClassMetadata(val header: KotlinClassHeader) { @JvmOverloads fun write( facadeClassName: String, - metadataVersion: IntArray = KotlinClassHeader.COMPATIBLE_METADATA_VERSION, + metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION, extraInt: Int = 0 ): MultiFileClassPart { checkMetadataVersion(metadataVersion) val (d1, d2) = writeProtoBufData(t.build(), c) - val metadata = KotlinClassHeader( - KotlinClassHeader.MULTI_FILE_CLASS_PART_KIND, metadataVersion, d1, d2, facadeClassName, null, extraInt + val metadata = Metadata( + MULTI_FILE_CLASS_PART_KIND, metadataVersion, d1, d2, facadeClassName, extraInt = extraInt ) return MultiFileClassPart(metadata) } @@ -361,7 +361,7 @@ sealed class KotlinClassMetadata(val header: KotlinClassHeader) { * 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. */ - class Unknown internal constructor(header: KotlinClassHeader) : KotlinClassMetadata(header) + class Unknown internal constructor(header: Metadata) : KotlinClassMetadata(header) companion object { // TODO: docs @@ -369,7 +369,7 @@ sealed class KotlinClassMetadata(val header: KotlinClassHeader) { @Suppress("DEPRECATION_ERROR") fun writeClass( kmClass: KmClass, - metadataVersion: IntArray = KotlinClassHeader.COMPATIBLE_METADATA_VERSION, + metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION, extraInt: Int = 0 ): Class = KotlinClassMetadata.Class.Writer().also { kmClass.accept(it) }.write(metadataVersion, extraInt) @@ -378,7 +378,7 @@ sealed class KotlinClassMetadata(val header: KotlinClassHeader) { @Suppress("DEPRECATION_ERROR") fun writeFileFacade( kmPackage: KmPackage, - metadataVersion: IntArray = KotlinClassHeader.COMPATIBLE_METADATA_VERSION, + metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION, extraInt: Int = 0 ): FileFacade = FileFacade.Writer().also { kmPackage.accept(it) }.write(metadataVersion, extraInt) @@ -386,20 +386,20 @@ sealed class KotlinClassMetadata(val header: KotlinClassHeader) { @Suppress("DEPRECATION_ERROR") fun writeLambda( kmLambda: KmLambda, - metadataVersion: IntArray = KotlinClassHeader.COMPATIBLE_METADATA_VERSION, + metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION, extraInt: Int = 0 ): SyntheticClass = SyntheticClass.Writer().also { kmLambda.accept(it) }.write(metadataVersion, extraInt) @OptIn(DeprecatedVisitor::class) @Suppress("DEPRECATION_ERROR") fun writeSyntheticClass( - metadataVersion: IntArray = KotlinClassHeader.COMPATIBLE_METADATA_VERSION, + metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION, extraInt: Int = 0 ): SyntheticClass = SyntheticClass.Writer().write(metadataVersion, extraInt) @Suppress("DEPRECATION_ERROR") fun writeMultiFileClassFacade( - partClassNames: List, metadataVersion: IntArray = KotlinClassHeader.COMPATIBLE_METADATA_VERSION, + partClassNames: List, metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION, extraInt: Int = 0 ): MultiFileClassFacade = MultiFileClassFacade.Writer().write(partClassNames, metadataVersion, extraInt) @@ -408,7 +408,7 @@ sealed class KotlinClassMetadata(val header: KotlinClassHeader) { fun writeMultiFileClassPart( kmPackage: KmPackage, facadeClassName: String, - metadataVersion: IntArray = KotlinClassHeader.COMPATIBLE_METADATA_VERSION, + metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION, extraInt: Int = 0 ): MultiFileClassPart = MultiFileClassPart.Writer().also { kmPackage.accept(it) }.write(facadeClassName, metadataVersion, extraInt) @@ -423,7 +423,7 @@ sealed class KotlinClassMetadata(val header: KotlinClassHeader) { * @param header the header of a Kotlin JVM class file to be parsed */ @JvmStatic - fun read(header: KotlinClassHeader): KotlinClassMetadata? { + fun read(header: Metadata): KotlinClassMetadata? { if (!JvmMetadataVersion( header.metadataVersion, (header.extraInt and (1 shl 3)/* see JvmAnnotationNames.METADATA_STRICT_VERSION_SEMANTICS_FLAG */) != 0 @@ -432,11 +432,11 @@ sealed class KotlinClassMetadata(val header: KotlinClassHeader) { return try { when (header.kind) { - KotlinClassHeader.CLASS_KIND -> Class(header) - KotlinClassHeader.FILE_FACADE_KIND -> FileFacade(header) - KotlinClassHeader.SYNTHETIC_CLASS_KIND -> SyntheticClass(header) - KotlinClassHeader.MULTI_FILE_CLASS_FACADE_KIND -> MultiFileClassFacade(header) - KotlinClassHeader.MULTI_FILE_CLASS_PART_KIND -> MultiFileClassPart(header) + CLASS_KIND -> Class(header) + FILE_FACADE_KIND -> FileFacade(header) + SYNTHETIC_CLASS_KIND -> SyntheticClass(header) + MULTI_FILE_CLASS_FACADE_KIND -> MultiFileClassFacade(header) + MULTI_FILE_CLASS_PART_KIND -> MultiFileClassPart(header) else -> Unknown(header) } } catch (e: InconsistentKotlinMetadataException) { @@ -452,5 +452,54 @@ sealed class KotlinClassMetadata(val header: KotlinClassHeader) { "Please change the version from ${version.toList()} to at least [1, 4]." } } + + /** + * A class file kind signifying that the corresponding class file contains a declaration of a Kotlin class. + * + * @see Metadata.kind + */ + const val CLASS_KIND = 1 + + /** + * A class file kind signifying that the corresponding class file is a compiled Kotlin file facade. + * + * @see Metadata.kind + */ + const val FILE_FACADE_KIND = 2 + + /** + * A class file kind signifying that the corresponding class file is synthetic, e.g. it's a class for lambda, `$DefaultImpls` class + * for interface method implementations, `$WhenMappings` class for optimized `when` over enums, etc. + * + * @see Metadata.kind + */ + const val SYNTHETIC_CLASS_KIND = 3 + + /** + * A class file kind signifying that the corresponding class file is a compiled multi-file class facade. + * + * @see Metadata.kind + * + * @see JvmMultifileClass + */ + const val MULTI_FILE_CLASS_FACADE_KIND = 4 + + /** + * A class file kind signifying that the corresponding class file is a compiled multi-file class part, i.e. an internal class + * with method bodies and their metadata, accessed only from the corresponding facade. + * + * @see Metadata.kind + * + * @see JvmMultifileClass + */ + const val MULTI_FILE_CLASS_PART_KIND = 5 + + /** + * The latest metadata version supported by this version of the library. + * + * @see Metadata.metadataVersion + */ + @JvmField // TODO: move it somewhere since it is also used in KotlinModuleMetadata? + val COMPATIBLE_METADATA_VERSION = JvmMetadataVersion.INSTANCE.toArray().copyOf() } } diff --git a/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinModuleMetadata.kt b/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinModuleMetadata.kt index b9f446ac237..3763c86b8c6 100644 --- a/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinModuleMetadata.kt +++ b/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinModuleMetadata.kt @@ -9,6 +9,7 @@ package kotlinx.metadata.jvm import kotlinx.metadata.* import kotlinx.metadata.impl.accept +import kotlinx.metadata.jvm.KotlinClassMetadata.Companion.COMPATIBLE_METADATA_VERSION import org.jetbrains.kotlin.metadata.jvm.JvmModuleProtoBuf import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion import org.jetbrains.kotlin.metadata.jvm.deserialization.ModuleMapping @@ -83,11 +84,11 @@ class KotlinModuleMetadata(@Suppress("CanBeParameter", "MemberVisibilityCanBePri /** * Returns the metadata of the module file that was written with this writer. * - * @param metadataVersion metadata version to be written to the metadata (see [KotlinClassHeader.metadataVersion]), - * [KotlinClassHeader.COMPATIBLE_METADATA_VERSION] by default + * @param metadataVersion metadata version to be written to the metadata (see [Metadata.metadataVersion]), + * [KotlinClassMetadata.COMPATIBLE_METADATA_VERSION] by default */ @Deprecated("Writer class API is deprecated. Please use KotlinModuleMetadata.write(kmModule, metadataVersion)", level = DeprecationLevel.ERROR) - fun write(metadataVersion: IntArray = KotlinClassHeader.COMPATIBLE_METADATA_VERSION): KotlinModuleMetadata = + fun write(metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION): KotlinModuleMetadata = KotlinModuleMetadata(b.build().serializeToByteArray(JvmMetadataVersion(*metadataVersion), 0)) } @@ -144,7 +145,7 @@ class KotlinModuleMetadata(@Suppress("CanBeParameter", "MemberVisibilityCanBePri @OptIn(DeprecatedVisitor::class) @Suppress("DEPRECATION_ERROR") - fun write(kmModule: KmModule, metadataVersion: IntArray = KotlinClassHeader.COMPATIBLE_METADATA_VERSION): KotlinModuleMetadata = Writer().also { kmModule.accept(it) }.write(metadataVersion) + fun write(kmModule: KmModule, metadataVersion: IntArray = COMPATIBLE_METADATA_VERSION): KotlinModuleMetadata = Writer().also { kmModule.accept(it) }.write(metadataVersion) } } diff --git a/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/jvmMetadataUtil.kt b/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/jvmMetadataUtil.kt index 4a70f123154..54a3625dd5f 100644 --- a/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/jvmMetadataUtil.kt +++ b/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/jvmMetadataUtil.kt @@ -18,3 +18,27 @@ val ClassName.jvmInternalName: String get() = if (this.isLocal) substring(1) else replace('.', '$') + +/** + * Helper function to instantiate [Metadata]. + * In contrary to direct constructor call, this one accept nullable parameters to substitute nulls with default values. + * Also, this one does not accept [Metadata.bytecodeVersion] as it is deprecated. + */ +fun Metadata( + kind: Int? = null, + metadataVersion: IntArray? = null, + data1: Array? = null, + data2: Array? = null, + extraString: String? = null, + packageName: String? = null, + extraInt: Int? = null +): Metadata = Metadata( + kind ?: 1, + metadataVersion ?: intArrayOf(), + intArrayOf(1, 0, 3), + data1 ?: emptyArray(), + data2 ?: emptyArray(), + extraString ?: "", + packageName ?: "", + extraInt ?: 0 +) \ No newline at end of file diff --git a/libraries/kotlinx-metadata/jvm/test/kotlinx/metadata/test/JavaUsageTest.java b/libraries/kotlinx-metadata/jvm/test/kotlinx/metadata/test/JavaUsageTest.java new file mode 100644 index 00000000000..653f8d7e2db --- /dev/null +++ b/libraries/kotlinx-metadata/jvm/test/kotlinx/metadata/test/JavaUsageTest.java @@ -0,0 +1,26 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package kotlinx.metadata.test; + +import kotlin.Metadata; +import kotlinx.metadata.KmClass; +import kotlinx.metadata.jvm.KotlinClassHeader; +import kotlinx.metadata.jvm.KotlinClassMetadata; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class JavaUsageTest { + @SuppressWarnings("ConstantConditions") + @Test + public void testKotlinClassHeader() { + Metadata m = MetadataSmokeTest.class.getAnnotation(Metadata.class); + KmClass clazz1 = ((KotlinClassMetadata.Class) KotlinClassMetadata.read(m)).toKmClass(); + KotlinClassHeader kh = new KotlinClassHeader(m.k(), m.mv(),m.d1(),m.d2(), m.xs(), m.pn(), m.xi()); + KmClass clazz2 = ((KotlinClassMetadata.Class) KotlinClassMetadata.read(kh)).toKmClass(); + assertEquals(clazz1.getName(), clazz2.getName()); + } +} diff --git a/libraries/kotlinx-metadata/jvm/test/kotlinx/metadata/test/MetadataSmokeTest.kt b/libraries/kotlinx-metadata/jvm/test/kotlinx/metadata/test/MetadataSmokeTest.kt index 3e591fc2576..0ad7cbe233a 100644 --- a/libraries/kotlinx-metadata/jvm/test/kotlinx/metadata/test/MetadataSmokeTest.kt +++ b/libraries/kotlinx-metadata/jvm/test/kotlinx/metadata/test/MetadataSmokeTest.kt @@ -17,10 +17,8 @@ import kotlin.test.* @OptIn(DeprecatedVisitor::class) class MetadataSmokeTest { - private fun Class<*>.readMetadata(): KotlinClassHeader { - return getAnnotation(Metadata::class.java).run { - KotlinClassHeader(kind, metadataVersion, data1, data2, extraString, packageName, extraInt) - } + private fun Class<*>.readMetadata(): Metadata { + return getAnnotation(Metadata::class.java) } @Test @@ -155,17 +153,7 @@ class MetadataSmokeTest { fun lambdaVersionRequirement() { val x: suspend Int.(String, String) -> Unit = { _, _ -> } val annotation = x::class.java.getAnnotation(Metadata::class.java)!! - val metadata = KotlinClassMetadata.read( - KotlinClassHeader( - kind = annotation.kind, - metadataVersion = annotation.metadataVersion, - data1 = annotation.data1, - data2 = annotation.data2, - extraInt = annotation.extraInt, - extraString = annotation.extraString, - packageName = annotation.packageName - ) - ) as KotlinClassMetadata.SyntheticClass + val metadata = KotlinClassMetadata.read(annotation) as KotlinClassMetadata.SyntheticClass metadata.accept(KmLambda()) } diff --git a/libraries/tools/kotlinp/src/org/jetbrains/kotlin/kotlinp/readKotlinClassHeader.kt b/libraries/tools/kotlinp/src/org/jetbrains/kotlin/kotlinp/readKotlinClassHeader.kt index 893220be7b5..a485f2b08cd 100644 --- a/libraries/tools/kotlinp/src/org/jetbrains/kotlin/kotlinp/readKotlinClassHeader.kt +++ b/libraries/tools/kotlinp/src/org/jetbrains/kotlin/kotlinp/readKotlinClassHeader.kt @@ -5,13 +5,13 @@ package org.jetbrains.kotlin.kotlinp -import kotlinx.metadata.jvm.KotlinClassHeader +import kotlinx.metadata.jvm.Metadata import org.jetbrains.org.objectweb.asm.* import java.io.File import java.io.FileInputStream -internal fun File.readKotlinClassHeader(): KotlinClassHeader? { - var header: KotlinClassHeader? = null +internal fun File.readKotlinClassHeader(): Metadata? { + var header: Metadata? = null try { val metadataDesc = Type.getDescriptor(Metadata::class.java) @@ -27,7 +27,7 @@ internal fun File.readKotlinClassHeader(): KotlinClassHeader? { return header } -private fun readMetadataVisitor(output: (KotlinClassHeader) -> Unit): AnnotationVisitor = +private fun readMetadataVisitor(output: (Metadata) -> Unit): AnnotationVisitor = object : AnnotationVisitor(Opcodes.API_VERSION) { var kind: Int? = null var metadataVersion: IntArray? = null @@ -69,6 +69,6 @@ private fun readMetadataVisitor(output: (KotlinClassHeader) -> Unit): Annotation } override fun visitEnd() { - output(KotlinClassHeader(kind, metadataVersion, data1, data2, extraString, packageName, extraInt)) + output(Metadata(kind, metadataVersion, data1, data2, extraString, packageName, extraInt)) } }